Files
Document-level file/asset manager (ADR 014). The bytes of a file live in an AssetStore, the document only holds
nabu-asset:sha256-<hex> references and a small assetIndex Loro map (hash → { mime, name, size, url? }).
Lazy: Files.get(nabu).
Import
import { Files } from '@aionbuilders/nabu';Constructor
new Files(nabu: Nabu)| Parameter | Type | Description |
|---|---|---|
nabu | Nabu | n/a |
Static members
static get()
get(nabu: Nabu): Files| Parameter | Type | Description |
|---|---|---|
nabu | Nabu | n/a |
Properties
assets
assets: LoroMap<Record<string, unknown>>Beta format: bytes inside the document. Read-only legacy, emptied by the migration.
index
index: LoroMap<Record<string, unknown>>ADR 014 §3.2: hash → { mime, name, size, url? }, metadata only (never bytes).
revokeGraceMs
revokeGraceMs: numberMilliseconds an unused objectURL is kept before being revoked (a block that moves unmounts and remounts at once).
store
store: AssetStoreThe local store of the bytes.
config readonly
readonly config: FilesConfighosted readonly
readonly hosted: booleanWhether a host is configured (files.onBeforePersist): files are then uploaded in the background.
maxFileSize readonly
readonly maxFileSize: number | nullfiles.maxFileSize, or null (no limit).
maxInlineSize readonly
readonly maxInlineSize: numberMethods
adoptAssets()
adoptAssets(blocks: any[], embedded: Record<string, EmbeddedAsset> | null | undefined): any[]PASTE / import: make every nabu-asset: media of blocks valid in THIS document (ADR 014 §3.4, §3.8). Synchronous:
the refs are content hashes, so a block keeps its ref and the storage happens in the background (see Files#whenIdle).
- a hash ref whose bytes the fragment carries: they are verified (the hash must match, else the file is treated as
missing — reason
corrupt— andnabu.warnsays so) and written to the store, unless it already has them; overfiles.maxFileSizethe block becomesrejected(no ref, a readable reason) instead; - a hash ref the document already indexes: reused as is;
- a hash ref with no bytes and no index entry: kept, then flagged
status: 'missing'unless the local store has the file (reasonnot-embeddedwhen the copy said it left the bytes out, elseunavailable); - a beta
nabu-asset:<uuid>ref (older exports / clipboards) with its bytes: re-created throughFiles#ingestand given its hash ref when done (the block ispendingwith a local preview meanwhile); without bytes:missing; - with a host hook, a file the host does not have yet makes the block
pending: the upload starts in the background (the block keeps its hash ref; the host location lands inassetIndex[hash].url). No commit; the caller commits. Never throws.
| Parameter | Type | Description |
|---|---|---|
blocks | any[] | PasteBlocks (already sanitized) |
embedded | Record<string, EmbeddedAsset> | null | undefined | the |
cancel()
cancel(ref: string): booleanAbort the running upload of the file ref designates (the host's signal fires). The blocks showing it become error with
the reason cancelled; the local copy stays. Uploads are per file: every block with the same ref is affected.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
Returns boolean
whether an upload was running
checkSize()
checkSize(size: number): FileReason | nullCheck a file size against files.maxFileSize.
| Parameter | Type | Description |
|---|---|---|
size | number | bytes |
Returns FileReason | null
the reason it is refused, null when accepted
dispose()
dispose(): voidStop the automatic upload retries (timers, online listener): the persistence of the editor was disposed.
embedAssets()
embedAssets(blocks: any[]): Record<string, EmbeddedAsset> | undefinedCOPY: the bytes of every nabu-asset: media of blocks, for the assets map of the fragment (keyed by hash id, ADR 014
§3.8). A file whose bytes cannot go along (total over maxInlineSize, or not in the in-memory cache — evicted, or not
read yet) is NOT silently dropped: it gets an entry { mime, name, size, omitted: true } so the receiver shows the block
as missing with the reason not-embedded (unless its own store has the file), and nabu.warn says so. The blocks keep
their ref either way.
| Parameter | Type | Description |
|---|---|---|
blocks | any[] | PasteBlocks about to be copied |
Returns Record<string, EmbeddedAsset> | undefined
undefined when there is nothing to carry
externalize()
externalize(blocks: any[], options?: object): any[]COPY, text/html and text/plain: a copy of blocks where no nabu-asset: ref is left (it means nothing outside
Nabu). With inline (HTML), a media of at most HTML_INLINE_LIMIT bytes whose type sanitizeUrl(kind:'media')
accepts (image / video) becomes a data: URL; any other asset gets an empty ref, which the block serializers render
as a label (alt / file name) rather than a broken src.
| Parameter | Type | Default | Description |
|---|---|---|---|
blocks | any[] | n/a | n/a |
optionsoptional | object | {} | n/a |
options.inlineoptional | boolean | false | n/a |
getAsset()
getAsset(ref: string): {
data: Uint8Array;
id: string;
name: string;
type: string;
} | nullThe bytes behind a nabu-asset: ref (or bare id), when they are at hand SYNCHRONOUSLY (a copy / export cannot wait):
the files of at most maxInlineSize that were added or displayed in this session. null otherwise.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
hold()
hold(ref: string): () => voidA block displaying ref is mounted: its objectURL stays alive until the returned function is called (the media
components call this from an effect). An objectURL no mounted block uses any more is revoked after a short grace period.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
hostUrls()
hostUrls(hashes: Iterable<string, any, any>): Map<string, string>The host location (assetIndex[hash].url) of each of hashes that has one (lot 3: what onAssetReleased receives).
| Parameter | Type | Description |
|---|---|---|
hashes | Iterable<string, any, any> | n/a |
importRemote()
importRemote(blocks: any[]): any[]PASTE (HTML from outside): with config.importRemote, every block of blocks whose ref is a remote http(s) URL is
fetched and stored in the local store (Files#ingest). The block shows the original URL at once, as pending, and gets its
nabu-asset: ref in the background (a silent commit, not an undo step; still pending while a host uploads it); when the
fetch or the storage fails (or the file is over maxFileSize) it keeps the original URL. Without the option, blocks come back untouched. No commit; never throws.
| Parameter | Type | Description |
|---|---|---|
blocks | any[] | PasteBlocks (already sanitized) |
indexedHashes()
indexedHashes(): string[]Ids (sha256-<hex>) of every file the document knows (its assetIndex): what a persisted document references.
ingest()
ingest(file: File): Promise<{ id: string; meta: AssetMeta; ref: string }>Hash a File and write it to the local store (deduplicated: the same bytes give the same id and one entry), then
make sure the document's assetIndex knows it. This is step 1 of the lifecycle (ADR 014 §3.4).
Throws FileRejectedError for a file over files.maxFileSize.
| Parameter | Type | Description |
|---|---|---|
file | File | n/a |
initialStatus()
initialStatus(id: string): 'pending' | 'ready'The status a block showing the file id should start with: pending when a host still has to get it, else ready.
| Parameter | Type | Description |
|---|---|---|
id | string | n/a |
isUploading()
isUploading(ref: string): booleanWhether the upload of the file ref designates is running. Reactive.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
pendingUploads()
pendingUploads(): Promise<number>How many uploads are still to do or running (the outbox of the document, plus any running job). A compaction refuses to run while this is not 0 (ADR 014 §3.6): the local copy is the only copy of such a file. Waits for the resume pass first.
persist()
persist(file: File): Promise<string>Store a File and hand it to the host: Files#ingest, then (with onBeforePersist) the upload starts in the
background. Resolves with the nabu-asset:sha256-… ref as soon as the local copy is written — it never changes, the
host location ends up in assetIndex[hash].url. Blocks do not call this: they use ingest and become pending, the
upload queue does the rest. For programmatic use.
| Parameter | Type | Description |
|---|---|---|
file | File | n/a |
progress()
progress(ref: string): number | nullProgress (0–1) of the running upload of the file ref designates; null when none runs. Reactive.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
reconcileUploads()
reconcileUploads(): Promise<void>Run an upload pass now (tests, or after changing the host config at runtime).
releaseIndex()
releaseIndex(hashes: Iterable<string, any, any>): Promise<void>Remove the index entries of files a compaction released (silent commit, not an undo step). The entries stay readable in the history that was archived / retained; only the live index forgets them.
| Parameter | Type | Description |
|---|---|---|
hashes | Iterable<string, any, any> | n/a |
resolve()
resolve(ref: string): stringResolve a ref to a displayable URL. nabu-asset: refs → objectURL made from the local store (loaded in the
background: '' until it is there, reactive), else the url of the document index (through config.resolve);
external URLs → config.resolve or identity.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
retry()
retry(ref: string): Promise<boolean>Run the upload of the file ref designates again, from its local copy. Resolves true when the file ended up at the host.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
stage()
stage(file: File): Promise<{
asset: EmbeddedAsset | null;
id: string;
props?: Record<string, any>;
ref: string;
}>PASTE FILES: read a pasted File and prepare it for Files#adoptAssets. The file is hashed and written to the local
store right here (no base64 round trip), whatever the mode: the fragment only carries its hash ref, as one undo step with
the blocks. props is a patch for the block: status: 'pending' when a host still has to get the file (the upload starts
once the block is in the document), or status: 'rejected' + reason (and an empty ref) when it is over files.maxFileSize.
| Parameter | Type | Description |
|---|---|---|
file | File | n/a |
state()
state(ref: string): 'ready' | 'loading' | 'missing'What displaying ref would give right now: loading (being read from the store), missing (neither the local store
nor the document index has it) or ready. Reactive. Refs that are not nabu-asset: are always ready.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
warm()
warm(ref: string): voidRead the bytes of a stored file into the embed cache, so that a later copy / export (synchronous) can carry them. Cheap no-op for big files, which are never embedded.
| Parameter | Type | Description |
|---|---|---|
ref | string | n/a |
whenIdle()
whenIdle(): Promise<void>Resolves when the background storage work started by a paste / import / migration is done (bytes verified and written
to the store, index completed, missing files flagged). Uploads to a host (onBeforePersist) are NOT waited for.
whenResumed()
whenResumed(): Promise<void>Resolves when the first pass over the document (resuming the uploads of a reopened document) is done.
Source: src/lib/files/files.svelte.js:82