Skip to content

ccs-files-user-client changelog

Versions follow semver. For the package itself see ccs-files-user-client. The reference is at /sdks/ccs-files-user-client/reference/.

Default endpoint. Source-compatible with 1.2.0.

  • FilesClientOptions.apiUrl is optional (core, createFiles in /angular, useFiles in /react): omitted, the production API, DEFAULT_API_URL (https://files.centralcodestation.com/api/v1, exported from the package root). Still zero runtime dependencies. A browser reads no environment: pass apiUrl for another environment, or keep using FilesClient.fromTokenRoute (unchanged), whose backend sends api_url for the environment it runs in.
  • A blank apiUrl throws “apiUrl must not be blank (omit it for the production API)” instead of “apiUrl is required”.

Application-managed folders, as the File Service now serves them. Source-compatible with 1.1.0.

  • Application-managed folders. File and Folder gain managed (true for a folder a tenant’s application manages and everything below it) and managed_by (ManagedBy: { client_id, name, managed_folder_id, managed_at }, null when not managed; the nearest marked folder with nested marks). For people such items are read-only.
  • isReadOnly(item) (core entry): true when item.managed === true; decide the editing controls from it, not from my_actions.
  • folders.releaseManagement(folderId, { reason }) (POST P/folders/:folder_id/release-management, input type ReleaseManagementInput) resolves with the Folder: the tenant owner’s emergency release of a mark, audited with the reason. Never retried.
  • folder_managed (403, a FilesForbiddenError with body.managed_folder_id): a change people cannot make inside a managed folder (uploads, versions, rename, move, edits, copy in, trash, restore, purge, permissions, share links). FilesForbiddenError.managedFolderId reads the managing folder id (null on other codes) and isFolderManagedError(error) narrows to it.
  • tenant_owner_required (403) answers folders.releaseManagement for anyone but the tenant owner; 400 invalid_request for a folder that does not itself carry the mark.

Legal hold, and the search wording brought in line with the service. Source-compatible with 1.0.0.

  • Legal hold. FileHold ({ reason, placed_by, placed_at, until }); File.hold is FileHold | null (1.0.0 typed it as an untyped record). files.hold.place(fileId, { reason, until? }) (PUT P/files/:file_id/hold: places the hold, or replaces the reason and the end of the one in place) and files.hold.release(fileId, { reason? }) (DELETE P/files/:file_id/hold, the reason kept in the audit log) resolve with the File; holds.list({ placed_by, after, limit }) (GET P/holds) pages the held files, newest hold first. Input types PlaceHoldInput, ReleaseHoldInput, HoldsQuery; resource types HoldResource, HoldsResource. They need the files.manage permission (403 missing_capability otherwise).
  • Search documentation: in: ['name'] matches names that start with the query, case-insensitively (a prefix, not a substring); keywords matches whole words of the title, description, tags, category, department and custom values; the default falls back to the name prefix when no whole word matches. SearchResult.total is exact up to 10,000 and null above.
  • file_on_hold (409, a FilesConflictError with body.hold): purging a held file or deleting one of its versions (files.versions.delete) while the hold is in force.

First release: the web user client of the File Service, for a signed-in user of a tenant’s web app.

  • Token route: createTokenProvider({ url, headers, credentials, method, fetch, expiryMarginSeconds }) (cached until a minute before expiry, one request in flight, clear(), getConfig() for the route’s optional api_url, tenant_id, application_id) and FilesClient.fromTokenRoute(tokens, overrides).
  • Uploads: client.upload(file, input, { concurrency, onProgress, signal }) returns an UploadHandle (id, fileId, uploadId, state, progress with bytes in flight and parts, done, pause(), resume(), cancel(), on('progress' | 'state' | 'error'), serialize()). States creating, uploading, paused, completing, done, failed, cancelled. A single PUT sends exactly the ticket’s headers; multipart sends 4 parts in flight (concurrency), gets part URLs beyond the ticket’s in batches of 100, retries each PUT 5 times with full-jitter backoff (1 s to 30 s) on network errors and 5xx, re-requests an expired part URL once on a 403, and completes with every ETag. A 413 or 422 on the ticket fails the upload before any byte is sent. A failed upload keeps its ticket: resume() continues it.
  • Resume after a reload: client.resumeUpload(snapshot, file) continues an open multipart ticket from the parts the store holds (GET P/uploads/:upload_id), resolves a ticket that was completed meanwhile, and starts over otherwise; another file (size or lastModified) throws UploadFileMismatchError at once. Client snapshots hold no signed URL.
  • Flow A: static FilesClient.uploadFromTicket(ticketOrSnapshot, file, { partUrls, xhrFactory, fetch, concurrency, onProgress, signal }) runs the same engine on a ticket your backend created, with no Files token; done resolves with { upload_id, file_id, method, parts: [{ part_number, etag }] } for the backend to complete.
  • Versions: files.versions.upload(fileId, file, input, options) through the same engine; files.versions list, get, restore, pin, delete; files.renditions list, regenerate.
  • Browse and show: folders (root, create, get, children, path, update, move, copy, delete, restore), files (get, update, move, copy, delete, restore, download({ navigate }), downloadUrl, myPermissions, waitUntilReady), preview(fileId, { version }), urls(fileIds, { rendition, disposition }), invalidate(fileId), search(query) (with in: ['content'] and highlights), tags(query), trash.list, me.capabilities(), settings.public(). Preview and download URLs are cached per file, rendition and disposition until a minute before they expire; playbackSources(preview) lists the ready mp4_1080p and mp4_720p renditions and the original.
  • Sharing: permissions (get, replace, grant, revoke, roles) and shares (create, list, revoke).
  • Lifecycle: signOut() cancels the uploads, clears the caches and the token provider; dispose() pauses the uploads (their tickets stay resumable) and releases the client.
  • Requests: a 401 asks for a fresh token ({ forceRefresh: true }) and replays once; 5xx (not 501) and network errors are retried (maxRetries, default 3) on reads, idempotent writes and the writes that create, which carry an Idempotency-Key fixed before the first attempt; If-Match through { ifMatch }.
  • Errors: FilesApiError (status, code, body, retryAfterSeconds), FilesUnauthorizedError, FilesForbiddenError, FilesNotFoundError, FilesConflictError, FilesPreconditionFailedError, FilesPayloadTooLargeError, FilesUnprocessableError, FilesRateLimitedError, FilesNotImplementedError, FilesContextError, FilesUploadError (partNumber, uploadId, cause), UploadFileMismatchError and UploadCancelledError.
  • Angular (/angular): createFiles(optionsOrClient, destroyRef?) with the uploads() Signal. React (/react): useFiles(options), useUpload(client) ({ start, resume, uploads }), usePreview(client, fileId, { version, refresh }).
  • Zero runtime dependencies; Angular and React are optional peer dependencies. ESM and CommonJS builds with declarations; XMLHttpRequest for upload progress (xhrFactory), fetch where there is none.