@bytetrade/files-core
Framework-free files API: scoped services, pure drive protocols, transport, path helpers and browse sessions. No Vue, Pinia, or Quasar.
Entries
| Import | Contents |
|---|---|
@bytetrade/files-core | Full surface, including scoped services and pure drive protocols |
@bytetrade/files-core/base | Types, config, session, paths, i18n — no service or drive-protocol implementations. Prefer this when the host supplies its own FilesBrowserClient (the folder picker does). |
Configure
import {
configureFiles,
setFilesLocale,
setFilesI18n
} from '@bytetrade/files-core/base';
configureFiles({
baseURL: 'https://…',
token: () => getAccessToken(),
transport: myAxiosLikeTransport,
browser: myFilesBrowserClient
});
setFilesLocale('zh-CN');
// Optional: forward to the host app's i18n so every language is covered.
setFilesI18n((key, params) => i18n.t(key, params));
Built-in i18n
Without setFilesI18n, only English and Chinese catalogs ship in src/i18n.ts. Hosts that support more locales must inject a translator.
FilesBrowserClient
Hosts that drive the UI (folder picker, custom browser) implement FilesBrowserClient and pass it via configureFiles({ browser }). The session (BrowseSession / getBrowseSession(id)) is the navigation authority the UI subscribes to; the client is how the UI asks the host to load menus, open paths, create directories, and go back/forward.
Session-owned browsing (0.2.2)
Configure a loader once for each UI instance, then subscribe to its state:
const session = getBrowseSession(instanceId);
session.configureLoader({
scope: () => `${accountId}|${filesEndpoint}`,
load: async (path, { signal, cursor }) => {
const response = await listDirectory(path, { signal, cursor });
return { listing: response.listing, nextCursor: response.nextCursor };
}
});
await session.open(path);
await session.retry();
await session.loadNextPage();
The loader owns cancellation, stale-response checks and a bounded per-instance
cache. Scope changes clear listing, selection, menus, breadcrumbs and history;
hosts should call syncScope() on account/endpoint changes to clear immediately.
An ignored transport abort still cannot commit a late result. warm(path) only
populates the cache and never changes the visible path or loading status.
status distinguishes idle, loading, successfully loaded (including empty) and
failure; errors are normalized FilesErrors. A page failure leaves existing
items intact in pagination.error. Pagination only runs when the transport
returns a cursor; unpaged backends must not invent one. Refreshes preserve
selection by resource identity and apply the latest session sort.
Navigation, sorting and selection go through session methods. Reactive hosts
project session state into their store; they must not hydrate an older store
listing back into an active request. hydrate remains for host menu/preferences
and externally supplied listings. A specialized repository-shell query can be
supplied via open(path, { load }); retry reuses that query. dispose() cancels
requests and warmups; removeBrowseSession(id) disposes before dropping it.
Host-owned transfer engines (0.2.3)
FilesTransferRuntime provides a framework-free command lifecycle for upload and
download tasks. Hosts supply the byte engine via adapter, persistence, and a
stable scope identifying the account and remote machine. LAN routing and token
rotation should not change that identity. ready is awaited before start or
resume; adapters check context.assertCurrent() after asynchronous preparation
and before handing work to native code.
execute returns accepted, rejected with a normalized FilesError, or superseded
when an owner scope/command has replaced the work. Explicit commands cancel owned
retry timers. Late progress after pause/cancel/completion is ignored; completion
cleanup runs once. reset() aborts an app lifecycle while permitting a new one;
dispose() permanently releases the runtime.
restoreFilesTransfer resets persisted byte jobs according to engine restart
capabilities while retaining backend finalization jobs. FilesTransferLane
serializes and coalesces folder cycles without deadlocking reentrant empty-file
completions. FilesTransferManifest parses incremental UTF-8 NDJSON/SSE lists,
including a final record without a newline, and rejects malformed input.
Directory leaves use entryKind: 'directory'; isFolder remains the queue parent
flag. Optional transferScope, uploadRoot, downloadRoot, and manifestComplete
fields allow hosts to restore intent without changing legacy database enums.
Empty entries go through FilesService.createEmptyFile / ensureDirectory;
resolveDirectory anchors Google directory names under the selected opaque ID.
Raw relative names must be encoded exactly once by the host path adapter.
Account infrastructure
createFilesInfrastructureService(dependencies) groups account-side APIs into
nodes, libraries, cloudAccounts, mounts, and menus. Transport, current
scope, translation, provider support, and independent feature gates are
injected. The services do not read UI selections, routes, stores, dialogs or
persistent storage. All server requests accept cancellation and reject typed
errors.
- Library listings carry immutable
libraryReferencevalues. Rename/delete use those references; creating a library requires an explicit account scope. Shared-list failures do not become an empty list, and failed deletes are not acknowledged as successful. Repository names remain literal at the API boundary. - Cloud account queries use
/api/accounts, filter by available supported types, and attach account ownership. Files pages keep this list separate from the Settings integration list and must discard replies after account/view changes. - Mounts require
createFilesNodeReference(name, scope). SMB/NFS discovery returns{ kind: 'paths', paths }; only{ kind: 'mounted' }means a successful mount. Multi-step dialogs must carry the same node reference through every step. History and unmount calls also use this reference; volume names are encoded once. menus.listGroupcomposes explicit origins using the library/account services, independent Common/Sync gates, and host labels. Hosts own loading, retries, publication and native local-sync status.- Node HTTP reads are pure and reject failed/malformed results. Transitional node cache helpers still seed the host runtime, but check both machine/account cache identity and request scope around async work. Writes are serialized; a late cache read or failed old request cannot overwrite a new account's topology.
Hosts using the shared node cache coordinator should supply
configureFiles({ scope: currentFilesScope }). Legacy helpers delegate to these
services; management notifications belong to the host. NFS, Common and Sync
retain separate feature callbacks instead of assuming a new server minimum.
Share management
createFilesShareService(dependencies) manages internal, public and SMB shares.
Creation and per-file lookup accept a captured FileReference; existing-share
operations accept createShareReference(id, scope). List results carry that
reference so an old row cannot be reused after an account or server switch.
Revocation removes a share record and never calls the file deletion API.
The host supplies transport, current scope, source capabilities and the independent
sharing feature gate. Services check scope and cancellation before dispatch and
completion. Member permissions, including zero, are passed explicitly. Lookup
returns null only for an absent share; transport, business and malformed response
errors reject with FilesError instead of returning empty lists or false success.
createFilesShareAccessService handles password authentication and metadata for
public links under a share principal. It cannot call account-side management.
Hosts must key cached tokens by both server endpoint and share ID, verify metadata
before entering the protected page, and cancel requests when navigation changes.
Legacy commonShareAPI retains only its read-only listing bridge for older
presentation adapters. Its management and public authentication methods are removed.
Resource metadata and content (0.2.4)
createFilesService(dependencies).metadata accepts captured FileReference
objects for getMd5, getPermission, setPermission, usage, readText,
writeText, and resolveMedia. resourceUrl builds scoped raw/preview URLs;
it is a URL descriptor, not an HTTP request. Paths carry raw names and are encoded
once at the URL boundary. Public URLs require the captured share principal and
current token. ID-aware Google reads verify the path still identifies that object
before using the backend's name-based raw endpoint.
Every asynchronous operation accepts an AbortSignal, checks scope before and
after dispatch, and rejects FilesError on failure. Missing metadata never returns
an error sentinel. Hosts own notifications and must cancel view requests when
closing or changing the selected file. Keep the opened reference when saving;
never reconstruct a save destination from a later selection.
Text reads force a text response so JSON-looking content remains literal. Writes
preserve the supplied text, including empty content and surrounding whitespace.
Cloud content writes remain unsupported; independent host feature gates and
read-only permissions still apply. Sync media metadata resolves raw_path
separately from text content. Usage consumes incremental NDJSON and requires a
completion marker, rejecting malformed, negative or truncated totals and stopping
idle readers promptly on cancellation.
Origin.openPreview, formatFileContent, onSaveFile, and uploadEmptyFile are
removed. Legacy presentation URL formatters remain for other host integrations;
Files and LarePass preview/editor flows use the metadata service.
Build
Run these from the repository root so the workspace links resolve:
npm install
npm run build --workspace @bytetrade/files-core
npm test --workspace @bytetrade/files-core
Output lands in assets/ (gitignored / regenerated). Publishing rebuilds it
through the package's prepack hook.
0.2 migration
- Files v1 cloud-account and direct-transfer routes are removed. Cloud accounts
are listed through
GET /api/accounts; S3/Dropbox downloads use/api/raw. Tencent's removed direct-upload/download protocol now raisesunsupported. Its enum and transfer metadata formatting remain available for stored rows. - Independent sharing/Common backend feature gates remain host-provided. They are not an alternate Files protocol.
- Business failures always reject.
noToastcontrols notifications only;inspectBusinessErrorno longer acceptsthrowError. Mount-selection business code 300 is still a successful result. - Catch
FilesErrorand inspectcode,status,businessCode,retryableandcause.ErrorResponsenow extendsFilesErrorand retains its response. Cancellation and account/endpoint changes do not generate user notifications. fetchNodeListrejects failed requests. Background cache refresh handles its own rejection; callers awaiting a fresh topology must handle failure.- New operation contracts accept explicit
FileReferencevalues, not the active browser location or selected indexes. A reference carries a source ID, account or share scope, endpoint and either raw path segments or an opaque provider ID. Google references require an ID.fileReferenceFromLocationexplicitly decodes the current route-orientedFileLocationonce; supplyresourceIdfor Google. - File capabilities are defined in
types/capabilities. Batch-operation contracts represent partial success explicitly. The new contracts provide the foundation for the next per-drive service migration; resource callers use the scoped services; per-drive codecs only format presentation data.
The folder picker must use its matching 0.2 release. It uses the same core types and its standalone build embeds core, even though its UI does not change.
0.2.1 operation services
createFilesService implements the explicit operation contracts for native
Drive/Data/Common, node-scoped Cache/External, Sync, Google/S3/Dropbox and share
resources. Supply a transport, a current-scope resolver and a source resolver.
Copy/move additionally require an explicit scheduling-node resolver. The service
does not read a window location, clipboard, active menu, selected indexes or
progress notifier. Independent backend feature gates remain host capabilities.
const service = createFilesService({
transport,
scope: currentScope,
resolveSource: sourceId => sources.get(sourceId),
nodeForSource: source => source.context.node || masterNode
});
const listing = await service.list({ directory, signal });
await service.createDirectory({ parent: directory, name: 'Documents' });
const result = await service.copy({ files: [listing.entries[0].reference], destination });
// result.pending contains accepted server jobs, not completed copies.
// Register each job with the host's existing task queue; retain result.failed.
deletegroups by source and parent, and preserves per-file success when the backend reports failed dirents. Unknown transport failures do not imply that any member of the failed group succeeded.copy/movereturn accepted jobs inpending, completed work insucceededand errors infailed. Cancellation and account/endpoint changes stay distinct from business failures. Cross-scope references are rejected before dispatch.- Mutation responses use
FileMutationResult. Empty backend write responses do not fabricate size/timestamps, a collision-suffixed name or a new Google ID. Refresh the caller's explicit directory after a mutation;refreshis provided when derivable. - Google references keep their opaque ID, with optional raw
pathSegmentsrequired by v2 path-based writes. ID-only directory listing is supported. Path-based Google mutations verify uniqueness and the captured ID first; ambiguous names fail withconflictrather than choosing an arbitrary object. ID-aware rename passesdriveIdto the backend. - Public shares require a matching share principal and a token on every request. Revoking a share remains a separate share-management operation; it never means deleting the shared source file. Tencent remains unsupported.
Originand its subclasses no longer exposecopy,paste,move,action,createDir,renameItemordeleteItem. The old notification-coupled paste executor is removed. Use the services and let the host own notifications/tasks.
FilesResourceListing.providerData is a transitional presentation bridge for
existing host formatters. It does not change service identity or operation
parameters. Menu/repository/node shells, display formatting and BrowseSession
state are intentionally outside the resource service and migrate separately.
The folder picker uses the matching 0.2.7 release and peer dependency.
Protocol cleanup (0.2.7)
Origin, DriveAPI, dataAPIs, the files/seahub namespaces, and the obsolete CRUD, library, account, sharing-list and usage bridges have been removed. Select createDriveProtocol(driveType, options) with an explicit drive type. Protocols only convert paths, URLs and transfer presentation data; they do not read page state, make requests, choose nodes or touch the DOM. Options capture the endpoint, share token, library names and host mode. Folder-upload metadata receives its parent transfer explicitly.
Use createFilesService for resources and metadata, createFilesShareService for sharing, and createFilesInfrastructureService for account operations. createFilesUploadService accepts a scoped upload target with a captured node/library and handles upload links and resume offsets with cancellation and scope checks. Host code owns file selection, repository navigation, task completion refresh and archive-task coordination. Archive password prompting and task transport remain in ArchiveAPI pending the separate archive-service migration. Independent feature gates are unchanged.
Archive and ACL services (0.2.8)
createFilesService(deps).archive owns compression, extraction, streamed entries and entry text/blob reads. Inputs use FileReference identities, output tasks include their scope and node, and optional node references must match the files' owning node. Native drives are supported; a task cannot span Cache/External nodes. The independent archiveEnabled callback preserves server feature gating. Names are raw resource names; literal percent sequences are not decoded again.
Archive requests never open dialogs, retry passwords or show notifications. The host catches password business codes and retries the same captured operation. Preview consumers accept NDJSON/SSE records, check identity before callbacks and body completion, stop at completion records, and cancel their readers on abort. Passwords travel only in request headers.
createFilesService(deps).acl reads and updates top-level Common/External directory access. It validates target identity, response entries and grants, and shares the resource service's cancellation, scope checks and error contract. The pure isAclTopLevel and isAclManager helpers remain available. Hosts bind cached user lists to the account and endpoint that loaded them.
ArchiveAPI, getAcl, updateAcl, loadAclManager, fetchUserList and the global onNeedPassword config callback have been removed. Application coordinators own password prompts, request lifetimes and task queue persistence.
Empty mount history (0.2.9)
mounts.history accepts the backend's null empty-history response and returns []. Empty arrays remain valid; network failures and malformed history records still reject. The matching [email protected] release aligns the mobile selector with the main app UI.