ccs_files_user_client changelog
Versions follow semver. For the package itself see ccs_files_user_client.
1.3.0 (2026-10-25)
Section titled “1.3.0 (2026-10-25)”Default endpoint. Source-compatible with 1.2.0.
FilesClient(apiUrl: ...)is optional: omitted, the production File Service,FilesEndpoints.production(https://files.centralcodestation.com/api/v1).FilesEndpoints.stagingandFilesEndpoints.local(http://localhost:50600/api/v1) are the other environments, the same origins asFILES_ORIGINSof the Node SDK@central-code-station/ccs-files-client.FilesClient.fromTokenRouteis unchanged.FilesEnvironment(production,staging,local):FilesEnvironment.parse(String.fromEnvironment('CCS_ENVIRONMENT'))reads a build-time--dart-define=CCS_ENVIRONMENT=...(empty is production; another value throwsFilesConfigurationException),.apiUrlits API base. The example app readsCCS_ENVIRONMENT(FILES_API_URLstill wins).
1.2.0 (2026-10-24)
Section titled “1.2.0 (2026-10-24)”Application-managed folders, as the File Service now serves them. Source-compatible with 1.1.0.
ManagedBy(clientId,name,managedFolderId,managedAt,toJson) and, onFileRecordandFolderRecord(everyFilesItem),managed(falsewhen absent) andmanagedBy(null when not managed): a folder a tenant’s application manages and everything below it are read-only for people.FilesItem.isReadOnlyis true whenmanaged; decide the editing controls from it, not frommyActions.folders.releaseManagement(folderId, reason:)(POST P/folders/:id/release-management) answers theFolderRecord: the tenant owner’s emergency release of a mark, audited with the reason. Never retried.folder_managed(403) documented onFilesForbiddenException, with the newisFolderManagedandmanagedFolderId(the folder that carries the mark, frombody['managed_folder_id']);tenant_owner_required(403) answersreleaseManagementfor anyone but the tenant owner.
1.1.0 (2026-10-23)
Section titled “1.1.0 (2026-10-23)”Legal hold, and search documented as the File Service now answers it. Source-compatible with 1.0.0.
FileHold(reason,placedBy,placedAt,until,inForce([now]),toJson) andFileRecord.hold(nullwhen the file is not held).files.hold.place(fileId, reason:, until:)(PUT P/files/:id/hold) andfiles.hold.release(fileId, reason:)(DELETE P/files/:id/hold, the reason only when given), each answering theFileRecord;holds.list(placedBy:, after:, limit:)(GET P/holds), aPage<FileRecord>newest hold first. They need thefiles.managepermission; both writes are retried like other idempotent writes.file_on_hold(409, a purge or a version delete of a held file) documented onFilesConflictException, with the newFilesConflictException.holdreading the hold from the error body.
Changed
Section titled “Changed”- Search wording:
SearchField.namematches file names that start with the query, case-insensitively (a prefix, not a substring);SearchField.keywordsmatches whole words (name, title, description, tags, category, department, custom values); with the default fields a query the words find nothing for falls back to the name prefix.SearchResult.totalis exact up to 10,000 andnullabove.
1.0.0 (2026-10-23)
Section titled “1.0.0 (2026-10-23)”First release: the Dart/Flutter user client of the CCS File Service, the counterpart of
@central-code-station/ccs-files-user-client. Generic: it knows nothing about any record of the app.
FilesClientfor one (tenant, application) context, from a token route (FilesClient.fromTokenRoute) or a token callback:folders(root,create,get,children,path,update,move,copy,delete,restore,usage),files(get,byExternalRef,update,move,copy,delete,restore,downloadUrl,versions,renditions,myActions),uploads(the protocol step by step),permissions(get,replace,grant,revoke,roles),shares(create,list,mine,revoke),trash.list,settings.public,preview,urls,invalidate,search(withSearchField.content),tags,capabilities,signOut,close.- A 401 asks the token provider for a fresh token and replays once; reads and idempotent writes are retried twice
after network errors and 5xx; creating writes carry an
Idempotency-Keykept across retries;If-Matchon updates. Preview and inline URLs are cached until a minute before they expire;urlsasks only for what is not cached. TokenProvider(tokenUrl, headers): caches until a minute before expiry, one in-flight request,clear(), configuration from the route’s optionalapi_url,tenant_idandapplication_id.UploadController:start,pause,resume,cancel,progress(aStream<UploadProgress>),checkpoints,done,toJsonandUploadController.fromJson(resume after a restart, from the first part the store does not have); sourcesUploadSource.file,.path,.stream(with a known length),.bytes, or your own. Single PUT with exactly the ticket’s headers; multipart with 4 parts in flight, 5 retries per part with full-jitter backoff, part URLs renewed through the API on a 403 or when older than 50 minutes; a lost completion answer is recovered from the ticket status; cancel aborts the ticket.UploadController.fromTicket(ticket, source): the engine on a ticket of the app’s backend, with no Files token;donegives the parts and theirETags (completionBody()), optionalrefreshPartUrls.FilesUploadQueue: bounded concurrency, persisted throughFilesUploadStore(JsonFileUploadStore,MemoryUploadStore),restore()at startup, interrupted uploads resumed when the app returns to the foreground, optionalpauseInBackground.- Typed
FileRecord,FolderRecord,UploadTicket,UploadRequest,Preview,SearchQuery,SearchResult,AccessEntry,ShareLink,PublicSettings,Capabilities; the error classes of the Node SDK as Dart exceptions (FilesApiExceptionand its subclasses by status,FilesUploadException,FilesUploadSourceMismatchException,FilesContextException, …). The SDK never logs. - Uploads run in the foreground isolate; background transfers (iOS background
URLSession) are out of scope. - Tests: unit tests against a fake service and store (including a 1 GiB upload resumed after a simulated kill) and an integration test against the service started locally. Not yet run on a device.