Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1bbe433
feat(llc)!: rework attachment uploads around AttachmentUploadTask
xsahil03x Aug 31, 2026
d6a9135
fix(llc): scale transport progress back to attachment bytes
xsahil03x Aug 31, 2026
6c52495
docs(llc): keep transport and storage detail out of the upload dartdoc
xsahil03x Aug 31, 2026
0934503
fix(llc): act on review of the upload task API
xsahil03x Aug 31, 2026
16f7722
refactor(llc): hand the private batch constructor its tasks
xsahil03x Aug 31, 2026
fe5d63e
test(llc): pin that a finished batch leaves no upload emitting
xsahil03x Aug 31, 2026
90a0a69
style(llc): name the tasks the batch factory builds
xsahil03x Aug 31, 2026
cf307b2
docs(llc): put the upload contract on the interface that declares it
xsahil03x Aug 31, 2026
4915465
docs(llc): hold the upload docs to Effective Dart, and teach the samples
xsahil03x Aug 31, 2026
64d36c8
revert(llc): drop the AttachmentUploader interface
xsahil03x Aug 31, 2026
f6f0433
style(llc): accumulate the batch's sent bytes in a plain loop
xsahil03x Aug 31, 2026
40494b3
docs(llc): make the samples honour the nullable fraction they teach
xsahil03x Aug 31, 2026
02e86e7
Merge feat/error-layer into feat/attachment-upload-task-api
xsahil03x Aug 31, 2026
c7366d2
docs(llc): drop the mechanism from the upload progress docs
xsahil03x Aug 31, 2026
546c6a4
docs(changelog): hold two upload entries to the policy
xsahil03x Aug 31, 2026
f78d72f
revert(llc): re-export Uint8List, from where the API needs it
xsahil03x Aug 31, 2026
43411b3
fix(llc): export the whole of dart:typed_data, as 0.4.0 did
xsahil03x Aug 31, 2026
fd4611e
fix(llc): leave the typed_data export exactly where main has it
xsahil03x Aug 31, 2026
ca4f472
Merge remote-tracking branch 'origin/feat/error-layer' into feat/atta…
xsahil03x Aug 31, 2026
7ade30e
Merge remote-tracking branch 'origin/feat/error-layer' into feat/atta…
xsahil03x Sep 1, 2026
e4d57c8
fix(llc): report a CDN's own cancellation as it arrived
xsahil03x Sep 1, 2026
f3bd2b5
fix(llc): compare an upload outcome by what it carries
xsahil03x Sep 1, 2026
462f74c
docs(llc): say what a queued upload has not done, and what "completed…
xsahil03x Sep 1, 2026
dec01b4
test(llc): pin the bytes a failed upload had already sent
xsahil03x Sep 1, 2026
9fed6f7
test(llc): split the batch tests along the seams their groups already…
xsahil03x Sep 1, 2026
df3dce1
style(llc): let the pattern narrow the exception it already matched
xsahil03x Sep 1, 2026
cb31c8f
test(llc): split the task tests the same way as the batch ones
xsahil03x Sep 1, 2026
27f5df3
Merge remote-tracking branch 'origin/feat/error-layer' into feat/atta…
xsahil03x Sep 1, 2026
40b1180
refactor(llc): carry an upload's trace beside its failure
xsahil03x Sep 1, 2026
af098cc
Merge remote-tracking branch 'origin/feat/error-layer' into feat/atta…
xsahil03x Sep 1, 2026
5c88b16
test(llc): assert a batch kept a trace, not that two nulls agree
xsahil03x Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/stream_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@
- `Result.getOrElse`, `getOrDefault`, `recover` and `recoverCatching` return the result's own type and no longer take a type parameter. To widen, widen the result (`Result<num> widened = intResult`) or use `fold`
- Replaced the logger: `StreamLogger` is the handle you write with and a `StreamLogHandler` is where records go, so `Priority`, `MessageBuilder`, `Tag`, `IsLoggableValidator` and `Finder` are renamed or gone
- `LoggingInterceptor` writes through the logger rather than printing, so it is silent until an app asks for records. Its `logPrint` is now optional, and it takes a `tag`
- The package no longer re-exports `dart:typed_data`, so code that reached `Uint8List` through `package:stream_core/stream_core.dart` must import `dart:typed_data` itself
- Reworked attachment uploads around `AttachmentUploadTask`: `StreamAttachmentUploader.upload` returns the running task rather than a `Future`, and `uploadBatch` returns an `AttachmentUploadBatch`. `CancelToken` and progress callbacks are gone from the public API
- Removed `StreamAttachment.uploadState`. Where an upload has got to lives on the task running it, not on the attachment
- Replaced the `UploadState*` classes with `UploadQueued`, `UploadPreparing`, `UploadInProgress`, `UploadSuccess`, `UploadFailed` and `UploadCancelled`, constructed directly rather than through named constructors. `UploadInProgress.progress` is an `UploadProgress` in bytes rather than a `double`, and `UploadFailed.error` is a `StreamException`
- Removed `AttachmentUploadException`. A failed upload carries the `StreamException` that stopped it, and a cancelled one a `StreamNetworkException` with `isCancelled` set
- Removed the `OnUploadProgress` and `OnBatchUploadProgress` callbacks along with the `StreamAttachmentUploaderBatch` extension. Progress arrives on `AttachmentUploadTask.state` and `AttachmentUploadBatch.state`, so it can never disagree with the lifecycle
- `uploadBatch`'s `maxConcurrent` now defaults to `3` rather than `5`
- `UploadedAttachment` compares by value rather than by identity

### ✨ Features

Expand All @@ -49,6 +55,8 @@
- Added `ConnectUserDetailsRequest.fromUser`, which builds the details a client may send from a `User`
- Added `StreamWebSocketClient.dispose`, which closes the connection along with `events` and `connectionState`; the client is now `Disposable`, and `connect` throws a `StateError` afterwards
- Added `InFlightCache`, which hands concurrent callers asking for the same key the one call already in flight, and its outcome, success or failure alike
- Added `AttachmentUploadTask`, one upload as an object: `state` carries the whole lifecycle including byte progress, `result` settles once and never throws, and `cancel` calls it off at once
- Added `AttachmentUploadBatch`, which uploads several attachments under a concurrency limit, aggregates byte-weighted progress, and finishes as a sealed `BatchUploadResult` — `BatchUploadCompleted`, `BatchUploadStoppedOnError` or `BatchUploadCancelled` — carrying one outcome per attachment in input order

### 🐛 Bug Fixes

Expand Down
5 changes: 4 additions & 1 deletion packages/stream_core/lib/src/attachment.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
export 'attachment/attachment.dart';
export 'attachment/attachment_file.dart';
export 'attachment/attachment_type.dart';
export 'attachment/attachment_upload_state.dart';
export 'attachment/cdn/cdn_client.dart';
export 'attachment/cdn/uploaded_file.dart';
export 'attachment/uploader/attachment_upload_batch.dart';
export 'attachment/uploader/attachment_upload_state.dart';
export 'attachment/uploader/attachment_upload_task.dart';
export 'attachment/uploader/attachment_uploader.dart';
export 'attachment/uploader/batch_upload_state.dart';
export 'attachment/uploader/uploaded_attachment.dart';
18 changes: 6 additions & 12 deletions packages/stream_core/lib/src/attachment/attachment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import 'package:uuid/uuid.dart';

import 'attachment_file.dart';
import 'attachment_type.dart';
import 'attachment_upload_state.dart';

/// Represents a file attachment with type information and upload state.
/// Represents a file attachment with type information.
///
/// Combines an [AttachmentFile] with its [AttachmentType] and tracks the
/// upload progress through [AttachmentUploadState]. This class provides
/// a complete representation of an attachment throughout its lifecycle.
/// Combines an [AttachmentFile] with its [AttachmentType], and is what an
/// upload is asked for: it carries the local [id] the upload is addressed by,
/// the [file] to send, and the [custom] data handed back on the uploaded
/// attachment. Where the upload has got to lives on the task running it, not
/// here.
///
/// Example usage:
/// ```dart
Expand Down Expand Up @@ -49,14 +50,12 @@ class StreamAttachment {
/// If not provided, a UUID v4 will be automatically generated.
/// The [type] specifies what kind of attachment this is.
/// The [file] contains the actual file data and metadata.
/// The [uploadState] tracks the upload progress, defaulting to preparing.
/// The [custom] allows storing arbitrary key-value pairs for additional
/// metadata specific to your application's needs.
StreamAttachment({
String? id,
required this.type,
required this.file,
this.uploadState = const AttachmentUploadState.preparing(),
this.custom,
}) : id = id ?? const Uuid().v4();

Expand All @@ -72,9 +71,6 @@ class StreamAttachment {
/// The file data and metadata.
final AttachmentFile file;

/// The current upload state of this attachment.
final AttachmentUploadState uploadState;

/// Optional custom data for storing arbitrary key-value pairs.
///
/// This allows applications to attach additional metadata to attachments
Expand All @@ -90,14 +86,12 @@ class StreamAttachment {
StreamAttachment copyWith({
AttachmentType? type,
AttachmentFile? file,
AttachmentUploadState? uploadState,
Map<String, Object?>? custom,
}) {
return StreamAttachment(
id: id, // ID is preserved and cannot be changed
type: type ?? this.type,
file: file ?? this.file,
uploadState: uploadState ?? this.uploadState,
custom: custom ?? this.custom,
);
}
Expand Down

This file was deleted.

Loading
Loading