Pyric
Navigate

API reference

@pyric/ui/storage

76 published symbols from @pyric/ui

Generated from the TypeScript declarations shipped at this import path.

Interfaces

CustomMetadataEntry

One customMetadata row. id is a stable render key — keys are user-editable, so they can’t key the rows themselves.

Properties

PropertyTypeDescription
error?stringValidation error ('Key is required' / 'Duplicate key').
idstring-
keystring-
valuestring-

DeleteSelectionWithConfirmProps

Properties

PropertyTypeDescription
body?ReactNodeConfirm-dialog body. Default lists the selected paths.
className?stringClass forwarded to the default trigger button.
confirmLabel?string-
entriesStorageSelectionEntry[]What to delete — useStorageSelection().selected (or any {kind, fullPath} rows). Folders delete recursively.
gate?Pick<UseStorageRulesGateResult, "verdictFor">Rules-aware affordance — pass useStorageRulesGate(storage). When ANY selected entry’s DELETE verdict denies, the trigger disables with the reason (default trigger: data-pyric-denied + data-pyric-denied-reason + title; renderTrigger receives deniedReason). For folder entries the verdict evaluates the folder path itself — an approximation of the recursive walk (descendants matched by {allPaths=**} rules share the verdict).
impl?StorageRecursiveDeleteImplFolder-walk impl override (default: the listAll-driven one).
list?Pick<UseStorageListResult, "insertItem" | "removeItem">Optimistic seam from useStorageList.
onDeleted?(outcome: StorageDeleteOutcome) => voidFired after a run with NO failures (e.g. clear the selection + refresh the list).
onFailed?(outcome: StorageDeleteOutcome) => voidFired after a run with failures (the toast already showed).
renderTrigger?(props: { deniedReason?: string; disabled: boolean; isRunning: boolean; onClick: () => void; progress: number; }) => ReactNodeRender override for the trigger button.
storageFirebaseStorageThe package’s single Storage handle prop.
title?stringConfirm-dialog title. Default derives from the entry count.

DroppedFile

One dropped file, flattened from the drop’s file/folder tree.

Properties

PropertyTypeDescription
fileFile-
relativePathstringPath relative to the drop — 'a.txt' for a plain file drop, 'photos/cat.png' for a file inside a dropped folder. Feed straight into useObjectUpload: upload(files.map((f) => ({ path: f.relativePath, data: f.file }))).

MetadataEditorState

Properties

PropertyTypeDescription
draftMetadataDraft-
errorCountnumber-
initialMetadataDraftSnapshot for isDirty / reset — same reference-compare semantics as the document editor’s tree !== initial.

ObjectBrowserProps

Properties

PropertyTypeDescription
className?string-
emptyState?ReactNode-
entriesStorageListEntry[]The folders-first row model from useStorageList.
error?ErrorRenders a role="alert" container with the message. Pair with the hook’s typed StorageError for code-driven copy.
gate?Pick<UseStorageRulesGateResult, "verdictFor">Rules-aware affordances — pass useStorageRulesGate(storage). Rows whose READ verdict denies are stamped data-pyric-denied (with the evaluator’s reason trace on data-pyric-denied-reason). A denied folder row would throw storage/unauthorized on listAll; the stamp warns BEFORE the click. Rows stay clickable — the affordance is advisory and the sandbox enforcement layer remains authoritative.
onNavigate?(path: string) => voidFolder row click — fired with the prefix’s fullPath. Wire to usePathState.enter (or setPath).
onSelect?(ref: StorageReference) => voidObject row click — fired with the object’s reference.
renderEntry?(entry: StorageListEntry) => ReactNodeRow-label slot. Default renders the entry name. The row button, its click wiring, and the data-* states stay with the component — the slot only owns the label content.
renderRowAction?(entry: StorageListEntry) => ReactNodeOptional per-row action rendered as a sibling of the navigation/select button. Use this for independent controls such as selection checkboxes.
rowHeight?number | (index: number) => numberEstimated row height when virtualizing. Default 36.
selectedPath?stringMarks the matching object row data-pyric-selected + aria-selected.
status?StorageListStatusDrives the loading state ('loading' with no rows yet) and the idle short-circuit. Default 'success' for static usage.
virtualizedHeight?string | numberScroll-container height when virtualized. Default '60vh'.
virtualizeThreshold?numberAbove this row count, the list switches to virtualization via <VirtualList>listAll has no pagination, so a big prefix arrives as one flat result and virtualization is the only defense. Default 100 (same as <DocumentList>). Infinity disables.

ObjectInspectorProps

Properties

PropertyTypeDescription
children?ReactNodeExtra content below the preview (metadata editor, delete button, …).
className?string-
pathstringObject path to inspect. null renders the idle shell — keep the inspector mounted and swap paths as the user selects rows.
previews?StoragePreview[]Consumer previews, tried BEFORE the built-ins (first match wins) — the extension channel of the preview registry.
renderMetadata?(metadata: FullMetadata) => ReactNodeMetadata-section slot. Default renders the standard field list. The header, preview, and state wiring stay with the component.
storageFirebaseStorageThe package’s sandbox Storage handle.

PathBreadcrumbProps

Properties

PropertyTypeDescription
className?string-
onNavigate?(path: string) => voidFired with the absolute path of the clicked crumb ('' for root). Wire to usePathState.setPath.
pathstringCurrent path. '' renders just the root crumb.
rootLabel?ReactNodeLabel for the root crumb. Default '/' — pass the bucket name for a gs://bucket feel.
separator?ReactNodeRendered between crumbs. Default '/'.

StorageDeleteFailure

One entry’s failure in a bulk run. error is the typed StorageError (.code e.g. storage/unauthorized).

Properties

PropertyType
errorError
fullPathstring

StorageDeleteOutcome

Properties

PropertyTypeDescription
deletedstring[]fullPaths of entries fully deleted.
failedStorageDeleteFailure[]-

StorageDeleteProgress

Properties

PropertyTypeDescription
deletedCountnumberObjects deleted so far in this folder walk.
donebooleanTrue for the final emission.

StorageGateVerdict

Per-path verdict. Upload and delete are evaluated with their granular Storage request methods so request.method policies and missing incoming payload semantics remain truthful. write retains its historical upload meaning; callers use delete for delete affordances.

Properties

PropertyTypeDescription
deletebooleanGranular delete verdict (request.method == 'delete').
readboolean-
reasons{ read: string[]; write: string[]; }Evaluator reason traces for DENIED verbs ("no rule matches…" / "match /… : condition false"); empty arrays when allowed. Feed into disabled-state tooltips and data-*-reason attributes.
reasons.readstring[]-
reasons.writestring[]-
uploadbooleanGranular create/upload verdict (request.method == 'create').
writeboolean-

StorageListEntry

One row of the merged folder/object model, the prefix→folder synthesis ported (as an idea, not code) from the emulator UI’s useStorageFiles: listAll’s prefixes become kind: 'folder' rows, its items become kind: 'object' rows, folders first.

Properties

PropertyTypeDescription
fullPathstringBucket-rooted path (no trailing slash, even for folders).
kind"object" | "folder"-
namestringLast path segment, display name.
refStorageReference-

StoragePreview

One entry in the content-type preview registry — the storage counterpart of the Firestore field-editor registry, keyed by a match predicate instead of a type name because content types are open-ended. First match wins; consumer previews run BEFORE the built-ins, so overriding image/* is just shipping your own matcher.

Properties

PropertyTypeDescription
idstringDiagnostic id — also stamped on the preview container as data-pyric-preview="<id>".
match(metadata: FullMetadata) => boolean-
maxBytes?numberSkip the preview (and the blob download) for objects larger than this — the inspector renders its data-pyric-preview-too-large fallback instead. undefined = no cap.
needsBlob?booleanAsk the inspector to loadBlob() before rendering. Default false (metadata-only previews render immediately).
render(ctx: StoragePreviewContext) => ReactNode-

StoragePreviewContext

What a preview’s render receives. blob/blobUrl are only populated for previews that declared needsBlob.

Properties

PropertyType
blobBlob
blobUrlstring
metadataFullMetadata

StorageRecursiveDeleteImpl

Recursive folder delete implementation — the same injection seam as the Firestore half’s RecursiveDeleteImpl. Unlike Firestore (where tree-walking needs sandbox introspection or a Cloud Function), the public storage surface CAN walk a prefix, so the package ships createListAllDeleteImpl as the default; inject your own for server-driven deletes.

Properties

PropertyType
start(target: StorageReference) => AsyncIterableIterator<StorageDeleteProgress>

StorageSelectionEntry

What the selection tracks per row — a structural subset of StorageListEntry, so useStorageList’s entries pass straight in. The kind decides the delete verb later (object → deleteObject, folder → recursive).

Properties

PropertyType
fullPathstring
kind"object" | "folder"

UploadDropzoneProps

Properties

PropertyTypeDescription
children?ReactNodeSlot — the dropzone chrome (“Drop files here…”, a browse <input type="file">, anything). The component owns only the drag wiring + data-* states.
className?string-
disabled?booleanIgnore drops + suppress the dragging state.
disabledReason?stringWhy the dropzone is disabled — stamped on data-disabled-reason (and only while disabled) so the chrome/styling can surface it. The canonical source is the rules gate: disabled={!gate.verdictFor(path).upload} + disabledReason={gate.verdictFor(path).reasons.write.join('; ')}.
onFiles(files: DroppedFile[]) => voidFired once per drop with the flattened file list (folder drops are traversed recursively via webkitGetAsEntry; empty folders yield nothing — wire useObjectUpload.createFolder to your own “new folder” affordance instead). Not fired for empty drops.

UploadEntry

Explicit-path upload input. path is relative to the hook’s path option (the destination folder).

Properties

PropertyType
dataArrayBuffer | Uint8Array<ArrayBufferLike> | Blob
metadata?SettableMetadata
pathstring

UploadTask

One file’s upload, TASK-SHAPED for resumable forward-compat: the byte counters and the onProgress callback are in the type NOW so a future uploadBytesResumable-backed implementation emits real intermediate snapshots without a breaking change. Today (pyric/storage has no resumable uploads — COMPAT) a task completes in one tick: onProgress fires once at 0 bytes and once at totalBytes.

Properties

PropertyTypeDescription
bytesTransferrednumber-
error?ErrorPopulated on 'error' — a typed StorageError from the sandbox (.code is storage/<code>, e.g. storage/unauthorized for a rules-denied write).
fullPathstringBucket-rooted destination path.
idstringStable id — key task rows on this, not on fullPath (two uploads can target the same path).
metadata?FullMetadataPopulated on 'success'.
statusUploadTaskStatus-
totalBytesnumber-

UseMetadataEditorOptions

Properties

PropertyTypeDescription
initial?SettableMetadataThe metadata being edited — the same shape useStorageObject’s metadata carries. Read once on mount (the editor is a stateful workspace, like the document editor); reset() + remount to re-initialize.

UseMetadataEditorResult

Properties

PropertyTypeDescription
addCustomEntry(key?: string, value?: string) => void-
cacheControlstring-
contentTypestring-
customCustomMetadataEntry[]-
dispatch(action: MetadataEditorAction) => voidRaw dispatch — prefer the named helpers.
errorCountnumber-
isDirtybooleantrue once any modifying action fired since the last reset/successful save. Reference-compare semantics — manual re-entry of the original values does NOT clear it.
isSavingboolean-
isValidbooleanConvenience: errorCount === 0.
removeCustomEntry(id: string) => void-
reset() => voidRestore the initial values. Clears isDirty.
save() => Promise<FullMetadata>updateMetadata(ref(storage, path), toPatch()). Errors surface via saveError (typed StorageError), not throws — resolves undefined on failure or when the draft is invalid. On success the draft becomes the new baseline (isDirty clears) and the fresh FullMetadata is returned.
saveErrorError-
setCacheControl(value: string) => void-
setContentType(value: string) => void-
setCustomKey(id: string, key: string) => void-
setCustomValue(id: string, value: string) => void-
toPatch() => SettableMetadataSerialize the draft to an updateMetadata patch. Empty contentType/cacheControl become undefined — which LEAVES the previous value (the sandbox doesn’t model null-clears; see pyric/storage’s updateMetadata doc). customMetadata is always included and replaces wholesale, so row removal works.

UseObjectUploadOptions

Properties

PropertyTypeDescription
list?Pick<UseStorageListResult, "insertItem" | "removeItem">Optimistic seam from useStorageList: each upload inserts its path immediately and rolls back via removeItem on failure. Caveat: rolling back an upload that was OVERWRITING an existing object drops that object’s row locally (the seam can’t tell an optimistic row from a listed one) — refresh() restores server truth.
onComplete?(task: UploadTask) => voidFired once per task reaching 'success'.
onError?(task: UploadTask) => voidFired once per task reaching 'error'.
onProgress?(task: UploadTask) => voidTask-shaped progress callback (see UploadTask).
path?stringDestination folder, bucket-rooted. Default '' (root). Wire to usePathState().path so uploads land in the browsed folder.

UseObjectUploadResult

Properties

PropertyTypeDescription
clearCompleted() => voidDrop settled (success/error) tasks from tasks.
createFolder(name: string) => Promise<void>Create an empty folder under the hook’s path: writes the GCS placeholder convention — a zero-byte object named <path>/ (trailing slash). listAll hides the placeholder from items at every level (it only surfaces as a prefix), so the folder appears in the browser with no phantom file inside. ref() normalizes the trailing slash away, so the placeholder is written through a structural value-object reference the sandbox accepts. Throws the underlying error after rolling back the optimistic prefix insert. ALTERNATIVE: when the store must stay free of placeholder objects (Pyric Studio’s choice), use the client-side pending-prefix mechanism instead — see pendingPrefixes.ts for the reducer and the recorded tradeoff.
isUploadingbooleantrue while any task is 'running'.
tasksUploadTask[]Every task started by this hook instance, oldest first.
upload(input: UploadInput | UploadInput[]) => Promise<UploadTask[]>Upload one or many files. Tasks run concurrently; the promise resolves with the settled tasks once ALL finish and never rejects — per-file failures land on task.error (and onError), so one bad file doesn’t mask the others.

UsePathStateOptions

Properties

PropertyTypeDescription
defaultPath?stringUncontrolled initial value. Default '' (bucket root).
onPathChange?(path: string) => voidFired with the normalized next path on every navigation. Called in both modes.
path?stringControlled value. When provided, the hook derives everything from it and navigation calls only fire onPathChange — the owner owns the state (e.g. a router binding ?path=).

UsePathStateResult

Properties

PropertyTypeDescription
enter(nameOrPath: string) => voidDescend into a child folder — accepts a bare name ('sub') or an absolute path ('docs/sub', e.g. a prefix’s fullPath).
navigateToIndex(index: number) => voidJump to the ancestor ending at segments[index] — the breadcrumb click. navigateToIndex(-1) (or any negative) is the root.
pathstringCurrent normalized path. '' is the bucket root.
segmentsstring[]Path split into segments. [] at root.
setPath(path: string) => voidJump to an absolute path (normalized).
up() => voidAscend one level. No-op at root.

UseStorageDeleteOptions

Properties

PropertyTypeDescription
impl?StorageRecursiveDeleteImplFolder-walk implementation. Default createListAllDeleteImpl.
list?Pick<UseStorageListResult, "insertItem" | "removeItem">Optimistic seam from useStorageList: entries vanish from the local list immediately and roll back (object → item, folder → trailing-slash prefix insert) on failure.

UseStorageDeleteResult

Properties

PropertyTypeDescription
deleteEntries(entries: StorageSelectionEntry[]) => Promise<StorageDeleteOutcome>Delete a mixed object/folder selection (objects via deleteObject, folders via the recursive impl), sequentially in selection order. Resolves with the outcome and never rejects — per-entry failures land in outcome.failed (and error keeps the first one for simple renders).
errorErrorFirst failure of the current/last run. Cleared on the next call.
isRunningboolean-
progressnumberObjects deleted in the current/last run (folder walks included).

UseStorageListResult

Properties

PropertyTypeDescription
entriesStorageListEntry[]Folders-first merged row model. Derived from prefixes + items.
errorErrorStorageError (with a typed storage/<code> on .code) from the sandbox. A denied list is error.code === 'storage/unauthorized' (ST-B2).
insertItem(fullPath: string) => voidOptimistic seam (consumed by M3 upload / M6 bulk ops, exposed now so those hooks layer on without reshaping this one). Inserts fullPath into the local list immediately, applying the same prefix→folder synthesis listAll would: a direct child becomes an item, a deeper descendant surfaces as its first-segment folder. A trailing slash declares a folder (the GCS placeholder convention useObjectUpload.createFolder writes): a direct trailing-slash child inserts a prefix, not an item. No-op for paths outside the listed path, duplicates, or when status is 'idle'. What each call ACTUALLY inserted is recorded (keyed by the given fullPath) so removeItem can reverse it precisely. Rollback = removeItem or refresh.
itemsStorageReference[]Direct child objects under path. Sorted by fullPath.
prefixesStorageReference[]Synthetic folder prefixes under path. Sorted by fullPath.
refresh() => voidRe-run listAll for the current path.
removeItem(fullPath: string) => voidOptimistic counterpart. When fullPath was previously given to insertItem, this reverses EXACTLY what that call inserted: a deep upload that synthesized a first-segment folder row removes that folder row, and an insert that was a no-op (the row already existed — e.g. a real, listed folder) removes NOTHING, so a failed upload can never delete server-truth rows. For paths never seen by insertItem it removes the matching item/folder row directly (the optimistic-delete use). Rollback = refresh.
statusStorageListStatus'idle' only when storage is null/undefined.

UseStorageObjectResult

Properties

PropertyTypeDescription
blobBlob-
blobErrorError-
blobStatusStorageObjectStatusBlob read state. Stays 'idle' until loadBlob(), the blob is LAZY; metadata alone never downloads bytes.
blobUrlstringURL.createObjectURL handle for the loaded blob, used as the local preview channel. Revoked automatically when the blob is replaced, the path changes, or the hook unmounts.
errorErrorTyped StorageError (storage/object-not-found, storage/unauthorized, …).
loadBlob() => voidFetch the bytes via getBlob. Subsequent calls re-fetch.
metadataFullMetadata-
refresh() => voidRe-read the metadata (also resets the blob, the object may have been overwritten).
statusStorageObjectStatusMetadata read state. 'idle' when storage or path is null.

UseStorageRulesGateOptions

Properties

PropertyTypeDescription
identity?StorageAuthIdentity override. null is anonymous; OMIT the field to use the handle’s own identity (the sandbox context’s auth).
paths?string | readonly string[]Path (or paths) to pre-evaluate into verdicts, keyed by the normalized path. Ad-hoc paths (e.g. browser rows) go through verdictFor instead — the two are the same evaluation.
rules?string | StorageRulesExplicit rules source — raw rules text (parsed here; a malformed string surfaces as status: 'error') or a pre-parsed StorageRules handle. Overrides the sandbox’s deployed ruleset when both exist.
writeResource?{ contentType?: string; size: number; }The about-to-write payload bound to request.resource for the CREATE evaluation — pass { size, contentType } when gating a specific upload so size/contentType-conditioned rules evaluate truthfully. When omitted, request.resource is unset, which is absent, upload/write remain a conservative deny for payload-dependent policies. Delete is evaluated independently and never receives this value.
writeResource.contentType?string-
writeResource.sizenumber-

UseStorageRulesGateResult

Properties

PropertyTypeDescription
advisorybooleanAlways false: pyric/storage handles are sandbox mirrors.
errorErrorRules-resolution failure (e.g. a malformed rules string).
identityStorageAuthThe identity the verdicts evaluate under.
sourceStorageRulesSourceWhere the active ruleset came from.
statusStorageRulesGateStatus'idle' only when storage is null/undefined.
verdictFor(path: string) => StorageGateVerdictEvaluate an arbitrary path under the current ruleset + identity. Pure and synchronous once status is 'ready'; before that (and whenever no rules are reachable) it returns the allow-all verdict — the gate FAILS OPEN, because affordances are advisory and the real enforcement (sandbox throw / server denial) stays authoritative.
verdictsRecord<string, StorageGateVerdict>Pre-evaluated verdicts for options.paths, keyed by normalized path.

UseStorageSelectionResult

Properties

PropertyTypeDescription
clear() => void-
deselect(fullPath: string) => void-
isSelected(fullPath: string) => boolean-
select(entry: StorageSelectionEntry) => void-
selectAll(entries: StorageSelectionEntry[]) => voidReplace the selection (e.g. a “select all” over list.entries).
selectedStorageSelectionEntry[]Selected entries in selection order.
sizenumber-
toggle(entry: StorageSelectionEntry) => voidAdd/remove — the checkbox verb.

Type Aliases

MetadataEditorAction

type MetadataEditorAction =
  | {
  type: "setContentType";
  value: string;
}
  | {
  type: "setCacheControl";
  value: string;
}
  | {
  id: string;
  key: string;
  type: "setCustomKey";
}
  | {
  id: string;
  type: "setCustomValue";
  value: string;
}
  | {
  key?: string;
  type: "addCustomEntry";
  value?: string;
}
  | {
  id: string;
  type: "removeCustomEntry";
}
  | {
  type: "reset";
}
  | {
  type: "commit";
};

Type Declaration

{
  type: "setContentType";
  value: string;
}
type
type: "setContentType";
value
value: string;
{
  type: "setCacheControl";
  value: string;
}
type
type: "setCacheControl";
value
value: string;
{
  id: string;
  key: string;
  type: "setCustomKey";
}
id
id: string;
key
key: string;
type
type: "setCustomKey";
{
  id: string;
  type: "setCustomValue";
  value: string;
}
id
id: string;
type
type: "setCustomValue";
value
value: string;
{
  key?: string;
  type: "addCustomEntry";
  value?: string;
}
key?
optional key: string;
type
type: "addCustomEntry";
value?
optional value: string;
{
  id: string;
  type: "removeCustomEntry";
}
id
id: string;
type
type: "removeCustomEntry";
{
  type: "reset";
}
type
type: "reset";
{
  type: "commit";
}
type
type: "commit";

Internal — a successful save makes the draft the new baseline.


PendingPrefixAction

type PendingPrefixAction =
  | {
  path: string;
  type: "create";
}
  | {
  path: string;
  type: "materialize";
}
  | {
  path: string;
  type: "discard";
}
  | {
  type: "clear";
};

Type Declaration

{
  path: string;
  type: "create";
}
path
path: string;
type
type: "create";

Create a folder at path (absolute, bucket-rooted; nested paths allowed) — adds the full ancestor chain.

{
  path: string;
  type: "materialize";
}
path
path: string;
type
type: "materialize";

An object now exists directly under path: drop path and its ancestors from pending (they are real prefixes now).

{
  path: string;
  type: "discard";
}
path
path: string;
type
type: "discard";

Remove a session-only folder and every pending descendant beneath it.

{
  type: "clear";
}
type
type: "clear";

PendingPrefixState

type PendingPrefixState = readonly string[];

Sorted, deduped, normalized pending prefix paths.


StorageApi

type StorageApi = Pick<pyric-storage-reference-api,
  | "ref"
  | "listAll"
  | "getMetadata"
  | "getBlob"
  | "uploadBytes"
  | "uploadBytesResumable"
| "deleteObject">;

The modular Storage fns the browse/inspect hooks call, as an INJECTABLE bundle (same pattern as @pyric/ui’s FirestoreApi / AuthApi).

Default = in-process pyric/storage, so existing consumers are unchanged. Pyric Studio served mode injects the SharedWorker client bundle so the Storage surface browses the live worker object store. These ops are already async, so no sync/async wrinkle (unlike auth listUsers); the worker handles/refs are runtime-compatible at the surface the hooks use (.fullPath / .name).

uploadBytes rides the same seam so useObjectUpload follows the injected backend: in-process writes are uncapped; the worker client’s uploadBytes (base64 storage.putBytes over the MessagePort) enforces an 8 MiB payload cap on both ends — an over-cap upload fails that file’s task with the typed storage/... too-large error and the rest of the batch proceeds.

NOTE the rules gate (useStorageRulesGate) is NOT here: it reads in-process rules internals and no-ops on a handle without them (worker handles), which is the correct degrade (the worker enforces read rules on listAll server-side).


StorageListStatus

type StorageListStatus = "idle" | "loading" | "success" | "error";

StorageObjectStatus

type StorageObjectStatus = "idle" | "loading" | "success" | "error";

StorageRulesGateStatus

type StorageRulesGateStatus = "idle" | "loading" | "ready" | "error";

StorageRulesSource

type StorageRulesSource = "option" | "sandbox" | "none";

Where the active ruleset came from:

  • 'option' — the explicit rules option (string or pre-parsed).
  • 'sandbox' — the ruleset deployed on the sandbox handle (getStorageSandbox(ctx, { rules })), read off the handle’s StorageService.
  • 'none' — no rules reachable. Every verdict allows (open-by-default, the same semantics pyric/storage’s enforcement layer applies when no rules are configured).

UploadInput

type UploadInput = File | UploadEntry;

upload() accepts plain Files (destination = the file’s webkitRelativePath when present — folder drops keep their structure — else its name) or explicit UploadEntrys.


UploadTaskStatus

type UploadTaskStatus = "running" | "success" | "error";

Variables

defaultStoragePreviews

const defaultStoragePreviews: ReadonlyArray<StoragePreview>;

The section 3 defaults: image, text/json; everything else is metadata-only (the inspector’s data-pyric-preview-none state).


imagePreview

const imagePreview: StoragePreview;

image/* → blob-URL <img>.


initialPendingPrefixes

const initialPendingPrefixes: PendingPrefixState;

TEXT_PREVIEW_MAX_BYTES

const TEXT_PREVIEW_MAX_BYTES: number;

256KB — the section 3 default cap for the text-family preview.


textPreview

const textPreview: StoragePreview;

text/* + application/json → text panel, 256KB cap (bigger objects fall through to the too-large fallback). JSON is pretty-printed when parseable.

Functions

createListAllDeleteImpl()

function createListAllDeleteImpl(api?: Pick<StorageApi, "listAll" | "deleteObject">): StorageRecursiveDeleteImpl;

The default, listAll-driven impl: walks the prefix tree, deleteObjects every item (yielding progress per object), then sweeps each visited folder’s <path>/ placeholder so emptied create-folder folders disappear too (listAll hides placeholders, so the walk alone would leave ghost folders). Placeholder sweeps are best-effort — deletedCount counts listed objects only.

Parameters

ParameterType
api?Pick<StorageApi, "listAll" | "deleteObject">

Returns

StorageRecursiveDeleteImpl


DeleteSelectionWithConfirm()

function DeleteSelectionWithConfirm(__namedParameters: DeleteSelectionWithConfirmProps): Element;

Bulk delete behind the confirm-dialog primitive, with toasts on outcome — wires useConfirm + useStorageDelete + useToast the way <DeleteWithConfirm> wires the Firestore trio. Requires <ConfirmProvider> AND <ToastProvider> ancestors.

Outcome toasts: all-success → one success toast with the count; any failure → an error toast listing each failed path with its typed StorageError.code.

The default trigger styles via [data-pyric-ui="delete-selection"] (+ [data-pyric-destructive], [data-pyric-running], [data-pyric-denied] with the reason on data-pyric-denied-reason/title); it disables while running, when entries is empty, or when the rules gate denies the selection.

Parameters

ParameterType
__namedParametersDeleteSelectionWithConfirmProps

Returns

Element


expandPathChain()

function expandPathChain(path: string): string[];

'a/b/c'['a', 'a/b', 'a/b/c']; ''[].

Parameters

ParameterType
pathstring

Returns

string[]


folderInputError()

function folderInputError(input: string): string;

Validate a create-folder input (relative to the current folder; nested a/b/c allowed — VS Code semantics). Returns an error message or null when valid. Normalization tolerates stray/repeat slashes; ./.. segments are rejected (GCS object names have no dot-segment semantics — accepting them would create unreachable names).

Parameters

ParameterType
inputstring

Returns

string


initMetadataEditorState()

function initMetadataEditorState(initial: SettableMetadata): MetadataEditorState;

Build the edit state from the metadata a getMetadata / useStorageObject read returned.

Parameters

ParameterType
initialSettableMetadata

Returns

MetadataEditorState


isPendingPrefix()

function isPendingPrefix(state: PendingPrefixState, path: string): boolean;

Whether path itself is a pending (session-only) folder.

Parameters

ParameterType
statePendingPrefixState
pathstring

Returns

boolean


metadataEditorReducer()

function metadataEditorReducer(state: MetadataEditorState, action: MetadataEditorAction): MetadataEditorState;

Pure reducer — exported (with initMetadataEditorState) so the edit state is testable without React, mirroring the document editor’s reducer/hook split.

Parameters

Returns

MetadataEditorState


normalizeStoragePath()

function normalizeStoragePath(path: string): string;

Strip leading/trailing slashes and collapse repeats — mirrors pyric/storage’s reference normalization so usePathState and useStorageList always agree on what a path is.

Parameters

ParameterType
pathstring

Returns

string


ObjectBrowser()

function ObjectBrowser(__namedParameters: ObjectBrowserProps): Element;

Headless storage browser shell — renders useStorageList’s merged folder/object rows. Folder rows navigate (onNavigate with the prefix path), object rows select (onSelect with the ref). Below virtualizeThreshold renders a plain <ul>; above it, composes the package’s <VirtualList>.

Ships no visual styling. Consumers style via:

  • [data-pyric-ui="object-browser"] — the root (stamps data-size)
  • …[data-pyric-loading] / [data-pyric-empty] / [data-pyric-error]
  • …[data-pyric-virtualized] — virtualized mode
  • [data-pyric-object-browser-items] — the <ul> in plain mode
  • [data-pyric-storage-entry] — each row
  • [data-pyric-storage-entry][data-pyric-entry-kind="folder"|"object"]
  • [data-pyric-storage-entry][data-pyric-entry-path="docs/a.txt"]
  • [data-pyric-storage-entry][data-pyric-denied] — read-denied row (rules gate; reason on data-pyric-denied-reason)
  • [data-pyric-entry-select] — the row button
  • [data-pyric-entry-select][data-pyric-selected] — the selected object
  • [data-pyric-storage-action] — optional sibling row action

Parameters

ParameterType
__namedParametersObjectBrowserProps

Returns

Element


ObjectInspector()

function ObjectInspector(__namedParameters: ObjectInspectorProps): Element;

Headless inspector for one storage object: metadata + a content-type-driven preview. Previews come from the registry (image/* and text/* + application/json built in; extend via previews). Blob bytes load lazily and ONLY when the matched preview asks (needsBlob) and the object is within the preview’s maxBytes cap; the blob URL is revoked on unmount/path change (see useStorageObject).

Ships no visual styling. Consumers style via:

  • [data-pyric-ui="object-inspector"] — root (stamps data-size)
  • …[data-pyric-idle] / [data-pyric-loading] / [data-pyric-error]
  • [data-pyric-object-name] / [data-pyric-object-metadata]
  • [data-pyric-metadata-field="<field>"] — each metadata row
  • [data-pyric-object-preview] — the preview container, stamping data-pyric-preview="<id>" for the matched registry entry
  • …[data-pyric-preview-loading] — blob in flight
  • …[data-pyric-preview-error] — blob load failed
  • …[data-pyric-preview-too-large] — over the preview’s cap
  • …[data-pyric-preview-none] — no registry match (metadata-only)

Parameters

ParameterType
__namedParametersObjectInspectorProps

Returns

Element


parseCopyCounter()

function parseCopyCounter(stem: string): {
  base: string;
  counter: number;
};

Trailing (n) counter: photo (3) → base photo, counter 3. counter: null when the stem carries none. A counter beyond Number.MAX_SAFE_INTEGER is treated as plain text (no counter): incrementing it would be lossy — n + 1 === n in float land, which turns resolveCollision’s probe loop into a hang — and the candidate would render in scientific notation anyway.

Parameters

ParameterType
stemstring

Returns

{
  base: string;
  counter: number;
}
base
base: string;
counter
counter: number;

PathBreadcrumb()

function PathBreadcrumb(__namedParameters: PathBreadcrumbProps): Element;

Headless breadcrumb for storage paths. Every crumb (including the current one) is a real <button> — clicking the current crumb is a cheap “refresh this level” affordance for consumers that wire onNavigate to a path-keyed loader.

Ships no visual styling. Consumers style via:

  • [data-pyric-ui="path-breadcrumb"] — the <nav> root
  • [data-pyric-breadcrumb-item] — each <li>
  • [data-pyric-breadcrumb-link] — each crumb button
  • [data-pyric-breadcrumb-link][data-pyric-current] — the current level
  • [data-pyric-breadcrumb-root] — the root crumb’s button
  • [data-pyric-breadcrumb-separator] — the separators

Parameters

ParameterType
__namedParametersPathBreadcrumbProps

Returns

Element


pendingChildFolders()

function pendingChildFolders(state: PendingPrefixState, parentPath: string): string[];

Direct-child folder NAMES pending under parentPath ('' = root), sorted. The chain expansion guarantees every level is present, so a simple parent match is exact.

Parameters

ParameterType
statePendingPrefixState
parentPathstring

Returns

string[]


pendingPrefixReducer()

function pendingPrefixReducer(state: PendingPrefixState, action: PendingPrefixAction): PendingPrefixState;

Parameters

ParameterType
statePendingPrefixState
actionPendingPrefixAction

Returns

PendingPrefixState


planBatchNames()

function planBatchNames(relativePaths: readonly string[], taken: ReadonlySet<string>): string[];

Resolve a whole drop/pick batch against the destination folder’s existing names, with OS drop semantics: collisions are detected and renamed at the batch’s TOP LEVEL only (the names the OS drop “creates” in the destination — a plain file’s name, or a dropped folder’s root segment). Files inside a dropped folder ride their folder’s rename and keep their inner structure untouched — exactly like dropping photos/ next to an existing photos/ yields photos (1)/… with the contents intact.

Within one batch:

  • all paths sharing a top-level FOLDER segment share its resolution (one dropped folder = one rename), and
  • top-level FILES resolve individually in order, each claiming its resolved name, so two same-named files in one batch get successive counters.

Only the destination’s DIRECT children can be checked — that is all the drop target (one listAll level) knows. Deeper paths follow GCS overwrite semantics, which the folder-level rename already shields in practice (a colliding folder is renamed wholesale).

Returns resolved paths in input order.

Parameters

ParameterType
relativePathsreadonly string[]
takenReadonlySet<string>

Returns

string[]


resolveCollision()

function resolveCollision(name: string, taken: ReadonlySet<string>): string;

Resolve one name against a set of taken sibling names. Returns the name unchanged when free; otherwise the first (n) candidate that is free, per the module rule above.

Parameters

ParameterType
namestring
takenReadonlySet<string>

Returns

string


selectStoragePreview()

function selectStoragePreview(metadata: FullMetadata, consumerPreviews: readonly StoragePreview[]): StoragePreview;

Pick the preview for metadata: consumer previews first (override channel), then the built-ins, first match wins. undefined means metadata-only.

Parameters

ParameterType
metadataFullMetadata
consumerPreviewsreadonly StoragePreview[]

Returns

StoragePreview


splitStorageName()

function splitStorageName(name: string): {
  ext: string;
  stem: string;
};

name split as the rule defines: ext includes the leading dot, or is '' when the name has no extension (dotfiles, trailing dots, extensionless names).

Parameters

ParameterType
namestring

Returns

{
  ext: string;
  stem: string;
}
ext
ext: string;
stem
stem: string;

StorageApiProvider()

function StorageApiProvider(__namedParameters: {
  children: ReactNode;
  value: StorageApi;
}): FunctionComponentElement<ProviderProps<StorageApi>>;

Provide a Storage API bundle to the subtree (Studio’s worker client).

Parameters

ParameterType
__namedParameters{ children: ReactNode; value: StorageApi; }
__namedParameters.childrenReactNode
__namedParameters.valueStorageApi

Returns

FunctionComponentElement<ProviderProps<StorageApi>>


UploadDropzone()

function UploadDropzone(__namedParameters: UploadDropzoneProps): Element;

Headless drop target for file + folder uploads. Slot-based: the children render the chrome; the component owns drag wiring and stamps data-dragging while a drag hovers (a counter tracks enter/leave pairs so crossing child elements doesn’t flicker).

Ships no visual styling. Consumers style via:

  • [data-pyric-ui="upload-dropzone"] — the root
  • …[data-dragging] — a drag is hovering
  • …[data-disabled]
  • …[data-disabled-reason="…"] — why (e.g. a denied write verdict)

Parameters

ParameterType
__namedParametersUploadDropzoneProps

Returns

Element


useMetadataEditor()

function useMetadataEditor(
   storage: FirebaseStorage,
   path: string,
   options?: UseMetadataEditorOptions): UseMetadataEditorResult;

Headless metadata editor — the useDocumentEditor reducer pattern over updateMetadata: a pure reducer owns the draft (contentType, cacheControl, customMetadata k/v rows with stable ids + empty/duplicate-key validation); the hook adds named dispatch helpers and the save half.

Parameters

ParameterType
storageFirebaseStorage
pathstring
options?UseMetadataEditorOptions

Returns

UseMetadataEditorResult


useObjectUpload()

function useObjectUpload(storage: FirebaseStorage, options?: UseObjectUploadOptions): UseObjectUploadResult;

Multi-file upload over the package’s single Storage handle prop. Headless: returns task state; render it however you like (the <UploadDropzone> component is one producer of upload() calls).

Optimistic-with-rollback: with the list seam wired, each upload’s row appears in useStorageList immediately and disappears again if the write fails (typed StorageError on task.error).

Parameters

ParameterType
storageFirebaseStorage
options?UseObjectUploadOptions

Returns

UseObjectUploadResult


usePathState()

function usePathState(options?: UsePathStateOptions): UsePathStateResult;

Path navigation state for the storage browser. Controlled when path is provided (the owner re-renders with the next value), uncontrolled otherwise — standard React value/defaultValue semantics. All emitted paths are normalized (normalizeStoragePath).

Parameters

ParameterType
options?UsePathStateOptions

Returns

UsePathStateResult


useStorageApi()

function useStorageApi(): StorageApi;

Read the active Storage API bundle (defaults to in-process pyric/storage).

Returns

StorageApi


useStorageDelete()

function useStorageDelete(storage: FirebaseStorage, options?: UseStorageDeleteOptions): UseStorageDeleteResult;

Drive bulk + recursive deletes from a React component — the storage counterpart of useRecursiveDelete (same progress / isRunning / error shape, same stale-run generation token), bulk because storage selections are flat multi-row affairs.

Parameters

ParameterType
storageFirebaseStorage
options?UseStorageDeleteOptions

Returns

UseStorageDeleteResult


useStorageList()

function useStorageList(storage: FirebaseStorage, path: string): UseStorageListResult;

List the objects + synthetic folders directly under path : listAll over the package’s sandbox Storage handle. Read-via-get, not realtime: the list updates on refresh, path change, or the optimistic seam. Pass '' (or the result of usePathState) for the bucket root.

listAll has no pagination; a very large prefix arrives as one flat result (virtualize the rendering, which <ObjectBrowser> does).

Parameters

ParameterType
storageFirebaseStorage
pathstring

Returns

UseStorageListResult


useStorageObject()

function useStorageObject(storage: FirebaseStorage, path: string): UseStorageObjectResult;

One object’s metadata + lazily-loaded bytes, the data source for <ObjectInspector>. Read-via-get like the rest of the storage half: updates on refresh, path change, or loadBlob.

Parameters

ParameterType
storageFirebaseStorage
pathstring

Returns

UseStorageObjectResult


useStorageRulesGate()

function useStorageRulesGate(storage: FirebaseStorage, options?: UseStorageRulesGateOptions): UseStorageRulesGateResult;

Pre-flight rules evaluation — the M7 differentiator. Evaluates the current identity against paths BEFORE the click, so components can annotate denied affordances (data-pyric-denied, disabled-with- reason) instead of letting the user discover a denial via a thrown storage/unauthorized.

Rules discovery: a sandbox handle carries its deployed ruleset (getStorageSandbox(ctx, { rules }) parses it into the handle’s StorageService) — the hook reads it through the handle’s target, so sandbox callers pass nothing. Identity likewise defaults to the handle’s SandboxContext.auth. An explicit rules or identity option overrides the handle when evaluating a what-if scenario.

Evaluation contract (mirrors pyric/storage’s own enforcement): resource (the existing object) is bound as null — the gate pre-evaluates without fetching per-path metadata, matching how the sandbox enforces listAll. Rules conditioned on existing-object state (resource.*) evaluate as if the object doesn’t exist; the common identity/path/payload-shaped rules evaluate exactly.

Parameters

ParameterType
storageFirebaseStorage
options?UseStorageRulesGateOptions

Returns

UseStorageRulesGateResult


useStorageSelection()

function useStorageSelection(): UseStorageSelectionResult;

Multi-select state over storage rows, keyed by fullPath. Deliberately dumb: it doesn’t watch the list, so clear it on path change (or after a bulk op via the delete hook’s outcome) — the consumer owns that policy.

Returns

UseStorageSelectionResult