File records and storage
@alneas/data-engine/integrations/files owns FileProvider, FileNode, and
DataAccessFileProvider. React views, selection, local browser persistence and
provider-specific SDK setup are outside this entry. It has no SDK runtime imports.
Install the optional peers @alneas/data-access@0.1.0 and
@alneas/universal-storage@0.1.0 when composing this integration. Engine core
works without either peer; this integration’s declarations reference their types.
import { DataAccessFileProvider } from '@alneas/data-engine/integrations/files';const files = new DataAccessFileProvider({ dataAccess, storage, collection: 'files', physicalPrefix: 'vault/', searchMode: 'portable', deleteMode: 'metadata-only',});const node = await files.uploadFile('inbox/', new Blob(['hello']), 'report.txt');await files.updateMetadata(node.id, { tags: 'finance,approved' });const matches = await files.searchNodes({ query: 'REPORT', tags: ['finance'] });dataAccess and storage are application-supplied adapter instances; the example
assumes they are configured and authorized. No collection or credentials are
created automatically. Provide columns id, folder, filename, physical_key,
size, content_type, tags, updated_at, kind. Folders omit size/content_type;
allow these columns to be nullable and tags optional. IDs must be application-unique;
the default is Web Crypto randomUUID. Custom createId returns a fresh ID matching
[A-Za-z0-9_-]{1,128}. Use an exclusively owned key namespace. Actual keys returned
by storage are saved; IDs and display names must never be substituted for those keys.
Rename/move update only metadata, leaving the physical key unchanged. Folder
rename/move/delete affect only the folder record, never descendants. Applications
must implement recursive operations explicitly. updateMetadata persists only
tags; it does not promise arbitrary columns or storage object metadata updates.
listNodes(path) uses {folder:path} with ascending kind/filename sort. It has
no pagination/cursor support and returns hasMore:false; the adapter must return
the complete folder listing, or the application must implement a paged provider.
Deletion and partial failures
Section titled “Deletion and partial failures”Default metadata-only deletion preserves existing behavior: delete DB rows only,
sequentially, stopping at the first error. Objects remain and can incur storage
costs. Choose storage-and-metadata explicitly to delete each file’s stored
physical_key first, then its DB row. Missing rows are no-ops; a false storage
delete result requires head(key) === null before proceeding. A storage/read-back
failure retains metadata. A DB failure after deletion can leave a row pointing to
an absent object. Retry/reconcile using FileProviderOperationError.stage,
nodeId, physicalKey, storageDeleted, completedIds and cause. Later IDs in
the batch are untouched. This is not an atomic DB/object transaction. It deletes
the visible key; S3 versions/delete markers, retention and legal erasure are the
application’s responsibility. Authorization, concurrent updates and shared-key
ownership must be enforced by the application.
Failed upload compensation
Section titled “Failed upload compensation”Upload precedes DB creation. The default uploadFailurePolicy:'retain' preserves
the object on a rejected DB create, because the create may have committed but lost
its acknowledgement. Public errors include the actual key and original cause.
For opt-in compensate, provide canCompensateUpload(context). Cleanup runs only
if read-back returns null and this callback returns true, confirming both
definitive non-commit and exclusive ownership of this newly uploaded key. Eventual
consistency or a missing read-back alone is insufficient. A present row is retained;
uncertain read-back/confirmation is retained with compensation:'unconfirmed'.
Successful cleanup reports deleted, failed cleanup failed with
compensationError, and a declined confirmation retained. The original create
still rejects in every case. A rejected storage upload is not automatically
cleaned up because its result is also uncertain. Keep operational reconciliation
outside the provider; no retry or orphan sweep runs automatically.
Search specification
Section titled “Search specification”Portable default search spans all logical folders. query is a literal filename
substring compared using JavaScript toLowerCase; regex metacharacters are literal.
It performs no Unicode normalization or locale-aware collation. Empty query matches
all. Tags are unquoted comma-separated strings, trimmed and compared as exact,
case-sensitive membership; every requested tag must match (AND). Commas inside a
tag are unsupported. Candidate order is preserved; no relevance sorting is added.
The default asks find(collection, {}, {paginateAll:true}) for the full authorized
candidate set and filters locally. Adapters must honor exhaustive retrieval; it
cannot overcome a backend’s result cap. maxSearchRecords (default 10,000) rejects
oversized returned sets after retrieval, so it does not bound network traffic.
For large stores, provide an indexed searchRecords(criteria) that returns the
complete authorized candidate set for those criteria; the same portable predicates
are still applied. Do not return a truncated page or silently omit valid results.
legacy-adapter preserves the former $regex/$options:'i' and $all filters
exactly. Semantics and support then depend on the adapter; CSV tags do not magically
become a DB array. The private facade keeps this legacy default and original DB
create error identity; direct Public imports use portable search and structured
upload errors. Opt into portable search in private callers deliberately.