Interfaces
CollectionListProps
Properties
| Property | Type | Description |
|---|---|---|
className? | string | - |
collections | CollectionReference<DocumentData>[] | - |
emptyState? | ReactNode | Optional empty-state node. Rendered when collections.length is 0 and the list isn’t loading. |
error? | Error | - |
isLoading? | boolean | - |
onSelect? | (collection: CollectionReference) => void | Fired when a list item is clicked. Consumer wires navigation. |
DeleteWithConfirmProps
Properties
| Property | Type | Description |
|---|---|---|
body? | ReactNode | Confirm-dialog body. |
className? | string | Class forwarded to the default trigger button. |
confirmLabel? | string | Label on the destructive button. |
impl | RecursiveDeleteImpl | Implementation that walks the tree + deletes. Consumer-supplied. Sandbox-backed apps usually wire pyric/sandbox introspection; production apps usually call a Cloud Function. |
onDeleted? | () => void | Fired after the delete iterator finishes successfully. |
renderTrigger? | (props: { isRunning: boolean; onClick: () => void; progress: number; }) => ReactNode | Optional render override for the trigger button. |
target | | DocumentReference<DocumentData> | CollectionReference<DocumentData> | The doc / collection to delete. |
title? | string | Confirm-dialog title. Defaults to a sensible derivation from the target’s path. |
DocumentEditorRootProps
Properties
| Property | Type | Description |
|---|---|---|
children | ReactNode | - |
className? | string | - |
fieldEditors? | FieldEditorRegistry | Override or extend the built-in field editors. |
initial? | Record<string, unknown> | Initial document data. Built into the editor’s tree on first mount; later changes don’t rebuild — call editor.reset() to re-initialize from a fresh initial. |
onChange? | (state: UseDocumentEditorResult) => void | Called on every state change with the latest editor state. The parent typically watches state.isValid + state.isDirty to enable/disable a Save button. |
DocumentEditorState
Reducer state. tree is the live document under edit; initial
is the snapshot the editor was constructed from (used to
implement reset and isDirty).
Extended by
Properties
| Property | Type | Description |
|---|---|---|
errorCount | number | Count of nodes with an active error. Derived after every action; the reducer keeps it in state to avoid a tree walk on every render. |
initial | EditorTree | Frozen copy of the tree at construction. reset restores from here; isDirty is computed by comparing serializations. |
tree | EditorTree | - |
DocumentListProps
Properties
| Property | Type | Description |
|---|---|---|
className? | string | - |
documents | QueryDocumentSnapshot<DocumentData>[] | - |
emptyState? | ReactNode | - |
error? | Error | - |
hasMore? | boolean | - |
isLoading? | boolean | - |
onLoadMore? | () => void | Fired when the user requests another page. Wire to the hook’s loadMore. The component will not render a Load More button when this is undefined. |
onSelect? | (ref: DocumentReference) => void | - |
renderLabel? | (doc: QueryDocumentSnapshot) => ReactNode | Optional renderer for the row label. Default renders the doc id. Override to show a field value alongside, an icon, etc. |
renderRowAction? | (doc: QueryDocumentSnapshot) => ReactNode | Optional per-row action(s), rendered as a SIBLING of the row’s select button inside the entry (not nested in it — so the action carries its own click handling without an invalid button-in-button). Used for row-level affordances like delete. |
rowHeight? | number | (index: number) => number | Estimated row height when virtualizing. Default 36 — matches a single-line text button at 13px font + ~10px padding. Pass a function for variable sizing (TanStack measures the actual height via ResizeObserver after the first paint anyway). |
updateScope? | string | Stable collection/query identity used to reset update highlighting when the rendered list changes scope. |
virtualizedHeight? | string | number | Pixel height the virtualized scroll container fills. Only applies when documents.length > virtualizeThreshold. Default '60vh' — consumers usually constrain via their layout. |
virtualizeThreshold? | number | Above this row count, the list switches to virtualization via <VirtualList>. Default 100. Set to Infinity to disable (e.g. when measuring layout shifts is more important than scroll perf). |
DocumentPreviewProps
Properties
| Property | Type | Description |
|---|---|---|
className? | string | Forwarded to the root <div>. |
documentRef? | DocumentReference<DocumentData> | The document’s own reference. Required to surface its subcollections — DocumentSnapshot (as typed by pyric/firestore) doesn’t expose .ref, so the consumer threads the ref it already holds from fetching the doc. Without it (or without listSubcollections) the Subcollections section is omitted. |
emptyState? | ReactNode | Content rendered when the snapshot is missing or !exists(). Defaults to null (renders nothing). |
fieldEditors? | FieldEditorRegistry | Override or extend the built-in field editors. Merged on top of defaultFieldEditors — only the keys you provide override. |
firestore? | Firestore | Firestore handle passed to listSubcollections. Required alongside documentRef + listSubcollections to surface the Subcollections section (the same explicit-handle shape ReferencePicker uses). |
listSubcollections? | ListSubcollections | Injected lister for the document’s subcollections. The modular Web SDK has no client-side listCollections; sandbox-backed apps wire pyric/sandbox’s in-process listing, production apps pass a known list or a server proxy. Same shape ReferencePicker / useCollectionList use. Omit to hide the Subcollections section. |
onReferenceClick? | (ref: DocumentReference) => void | Fired when a reference field is clicked. When supplied, the reference Display renders as an interactive <button> (with data-pyric-clickable); when omitted, it stays inert as a <span>. Consumers wire navigation here — the library does not depend on a router. |
onSubcollectionClick? | (collection: CollectionReference) => void | Fired when a subcollection’s drill affordance is activated. Receives the subcollection’s CollectionReference (its .path is the navigate target). Consumers wire navigation here. |
snapshot | DocumentSnapshot<DocumentData> | Snapshot from useFirestoreDoc or getDoc. When null / undefined, renders emptyState. |
EditorTree
Normalized tree of nodes. nodes is the lookup; childIds is the
ordered child list per parent. The root node is itself a map
node (its children are the document’s top-level fields).
Properties
| Property | Type |
|---|---|
childIds | Record<string, string[]> |
nodes | Record<string, FieldNode> |
rootId | string |
FieldDisplayProps
Type Parameters
| Type Parameter | Default type |
|---|---|
V | unknown |
Properties
| Property | Type | Description |
|---|---|---|
fieldEditors? | FieldEditorRegistry | Recursive editors (Map, Array) need the registry to dispatch on their children. Leaf editors (String, Number, …) can ignore this prop. Required-but-optional because the consumer of the component (<FieldRenderer>, <DocumentPreview>) always threads it through. |
path? | string | Dotted/bracketed path from the document root, e.g. users.alice or tags[0]. Forwarded so consumer styles can target nested positions via [data-field-path="users.alice"]. |
value | V | Value to display. The component’s V generic narrows this. |
FieldEditorContract
Contract for one Firestore value type. Display (read-mode) is
required; Edit + validate + defaultValue are required for
leaf types that participate in M3’s editor. Map/array contracts
supply only Display — their edit affordances come from the
<DocumentEditor> compound component.
Type Parameters
| Type Parameter | Default type |
|---|---|
V | unknown |
Properties
| Property | Type |
|---|---|
Display | ComponentType<FieldDisplayProps<V>> |
Edit? | ComponentType<FieldEditProps<V>> |
type | FieldType |
FieldEditProps
Props passed to a per-type Edit component. The leaf editors
(string, number, …) consume this directly. Map/array editing is
handled by the <DocumentEditor> compound component itself, not
by individual editors — Firestore’s container shapes are special
enough that pushing them through the registry costs more than
it’s worth.
Type Parameters
| Type Parameter | Default type |
|---|---|
V | unknown |
Properties
FieldNode
One node in the normalized editor tree. Every Firestore field — leaf or container — is a node with a uuid, a parent pointer, a type, and a value. Containers (map, array) carry no value of their own; their children represent the value.
Properties
| Property | Type | Description |
|---|---|---|
error? | string | Validation error message attached to the node by the reducer after every action. undefined when valid. Computed on EVERY action regardless of touched — errorCount / isValid must reflect the true state so Save stays disabled. touched governs only whether a consumer chooses to DISPLAY the error. |
id | string | Stable uuid. Used as React key and as the action target. |
key | string | Map children carry their key here. Array children carry null (position comes from the parent’s childIds order). The root also carries null (it has no parent). |
parentId | string | Parent uuid; null for the root. |
touched? | boolean | Set once the field has been blurred (or a submit attempt swept the whole tree via touchAll). Consumers gate error display on touched && error so a freshly-added empty row doesn’t show “Field name is required” before the user has interacted with it. |
type | FieldType | Discriminated type. Drives which editor renders. |
value | unknown | Leaf value. undefined for map / array — those carry their value as children. null field type has value === null. |
FieldRendererProps
Properties
| Property | Type | Description |
|---|---|---|
fieldEditors | FieldEditorRegistry | Registry to dispatch through. Pass the merged registry — this component does not fall back to defaults on its own (to avoid a circular import with the editors). <DocumentPreview> is the entry point that merges user overrides into defaults. |
path? | string | - |
value | unknown | - |
ParsedImportDoc
One document to create. id === null means “let Firestore auto-id it”
(only produced by the array shape when no generateId option is given —
a map key is always a chosen id).
Properties
ParseImportOptions
Properties
| Property | Type | Description |
|---|---|---|
generateId? | () => string | When provided, array-shape entries get their auto-id GENERATED AT PARSE TIME (instead of id: null / addDoc-at-write-time). Fixing ids at parse makes a retry after a partial failure idempotent: the same parse’s ids are reused, so re-running the import cannot duplicate already-written docs. Use firestoreAutoId for prod-parity ids. |
ParseImportResult
Properties
| Property | Type | Description |
|---|---|---|
docs | ParsedImportDoc[] | - |
errors | string[] | Human-readable problems found while parsing. A non-empty errors does NOT necessarily mean docs is empty — the parser is per-item tolerant so one bad entry doesn’t block the rest; the caller decides whether to block on any error or proceed with the valid subset. |
QueryBuilderActions
Properties
| Property | Type | Description |
|---|---|---|
addCondition | (c?: Partial<Omit<QueryCondition, "id">>) => void | - |
buildQuery | (base: | Query<DocumentData> | CollectionReference<DocumentData>) => Query | Compose the state into a Firestore Query. Returns the base collection when there are no conditions / orderBy / limit. Conditions with empty field are skipped — the builder UI lets users add a row before they’ve filled it in. |
removeCondition | (id: string) => void | - |
reset | () => void | - |
setLimit | (limit?: number) => void | - |
setOrderBy | (orderBy?: { direction: QueryOrderDirection; field: string; }) => void | - |
updateCondition | (id: string, patch: Partial<Omit<QueryCondition, "id">>) => void | - |
QueryBuilderProps
Properties
| Property | Type | Description |
|---|---|---|
className? | string | - |
initial? | Partial<QueryBuilderState> | Drives the hook used internally. Both the state and the composed Query are exposed via onChange. |
onChange? | (builder: UseQueryBuilderResult) => void | Fired on every state change with the latest builder API. The parent typically calls builder.buildQuery(collection) and feeds the result into useDocumentList / useFirestoreCollection. |
QueryBuilderState
Properties
| Property | Type |
|---|---|
conditions | QueryCondition[] |
limit? | number |
orderBy? | { direction: QueryOrderDirection; field: string; } |
orderBy.direction | QueryOrderDirection |
orderBy.field | string |
QueryCondition
Properties
RecursiveDeleteImpl
Implementation injected by the consumer. The library doesn’t ship
one — sandbox-backed apps usually walk pyric/sandbox’s in-process
tree; production apps usually call a Cloud Function. Either way,
start returns an async iterator emitting progress.
Properties
| Property | Type |
|---|---|
start | (target: | DocumentReference<DocumentData> | CollectionReference<DocumentData>) => AsyncIterableIterator<RecursiveDeleteProgress> |
RecursiveDeleteProgress
Properties
| Property | Type | Description |
|---|---|---|
deletedCount | number | Total nodes deleted so far. |
done | boolean | True for the final emission. |
ReferencePickerProps
Properties
| Property | Type | Description |
|---|---|---|
className? | string | Forwarded to the root. |
firestore | Firestore | - |
initialPath? | string | Initial path text. |
listCollections | (firestore: Firestore, parent: DocumentReference<DocumentData>) => Promise<CollectionReference<DocumentData>[]> | Lister for subcollections. Required — see useReferencePicker docs for the rationale. |
onPick? | (ref: DocumentReference) => void | Fired when the user commits a picked reference (browse pick OR a valid manually-typed path with the Commit button). |
pathLabel? | string | Label for the path text input. Default ‘Document path’. |
SubscriptionState
Type Parameters
| Type Parameter |
|---|
T |
Properties
UseCollectionListOptions
Properties
| Property | Type | Description |
|---|---|---|
firestore | Firestore | - |
listCollections | (firestore: Firestore, parent: DocumentReference<DocumentData>) => Promise<CollectionReference<DocumentData>[]> | Injected collection-listing function. The library doesn’t ship a default — the modular Web SDK doesn’t expose listCollections on the client. Sandbox-backed apps usually wire pyric/sandbox’s in-process listing; production apps either pass a known list (e.g. from a schema) or call a server proxy. |
parent? | DocumentReference<DocumentData> | Parent document, or null/undefined for root collections. |
UseCollectionListResult
Properties
| Property | Type | Description |
|---|---|---|
collections | CollectionReference<DocumentData>[] | - |
createCollection | (collectionId: string, firstDoc: { data: Record<string, unknown>; id: string; }) => Promise<DocumentReference<DocumentData>> | Create a new collection by writing its first document. Firestore collections don’t exist independently of their documents — setDoc on the first child path materializes the collection. |
error | Error | - |
isLoading | boolean | - |
refresh | () => void | Re-run the listing function. |
UseDocumentEditorOptions
Properties
| Property | Type | Description |
|---|---|---|
initial? | Record<string, unknown> | Initial document data — the same shape a DocumentSnapshot.data() call returns. |
UseDocumentEditorResult
Reducer state. tree is the live document under edit; initial
is the snapshot the editor was constructed from (used to
implement reset and isDirty).
Extends
Properties
| Property | Type | Description |
|---|---|---|
addArrayEntry | (parentId: string, childType: FieldType) => void | Append a child to an array. Nested arrays are silently rejected by the reducer (Firestore disallows them). |
addMapEntry | (parentId: string, key: string, childType: FieldType) => void | Append a child to a map. |
dispatch | (action: DocumentEditorAction) => void | Raw dispatch — drops to the reducer-action surface. Prefer the named helpers below. |
errorCount | number | Count of nodes with an active error. Derived after every action; the reducer keeps it in state to avoid a tree walk on every render. |
initial | EditorTree | Frozen copy of the tree at construction. reset restores from here; isDirty is computed by comparing serializations. |
isDirty | boolean | true once any modifying action has fired since the last reset. Cleared by reset. Does NOT clear when the user manually re-enters the original values — checking that would require a full serialization comparison on every dispatch. |
isValid | boolean | Convenience: errorCount === 0. |
remove | (nodeId: string) => void | Remove a node (and all its descendants). Removing the root is a no-op. |
replaceData | (data: Record<string, unknown>) => void | Replace the editor with a newly delivered snapshot and adopt it as the clean baseline. Intended for live document viewers. |
reset | () => void | Restore the tree to its initial state. Clears isDirty. |
setKey | (nodeId: string, key: string) => void | Set a map-child’s key. |
setType | (nodeId: string, newType: FieldType) => void | Switch a node’s type. Map/array nodes drop their children. |
setValue | (nodeId: string, value: unknown) => void | Update a leaf value. |
toData | () => Record<string, unknown> | Serialize the tree back to a Firestore-shaped object suitable for setDoc / updateDoc. |
touch | (nodeId: string) => void | Mark one node touched (dispatch on blur). Gates error display — a freshly-added row stays quiet until the user leaves it. |
touchAll | () => void | Mark every node touched (dispatch on a submit attempt) so any hidden errors surface at once. |
tree | EditorTree | - |
UseDocumentListOptions
Properties
| Property | Type | Description |
|---|---|---|
collection | CollectionReference | - |
mode? | "paged" | "live" | paged preserves the historical get-based cursor behavior. live keeps the currently requested window under an onSnapshot subscription and grows that window when loadMore is requested. Default paged. |
pageSize? | number | Page size for cursor-based pagination. Default 50. |
query? | Query | Optional filter / sort. If omitted, the raw collection is used. |
UseDocumentListResult
Properties
| Property | Type | Description |
|---|---|---|
createDocument | (id: string, data: Record<string, unknown>, opts?: { onExisting?: "overwrite" | "fail"; }) => Promise<DocumentReference<DocumentData>> | Create a document. If id is null, Firestore generates one via addDoc. With onExisting: 'fail' (CREATE semantics, the admin create() analog) an id that already exists rejects with code: 'already-exists' instead of silently overwriting — checked against the BACKEND (a getDoc probe), not any loaded page, so it is honest beyond pagination. Default: ‘overwrite’ (plain setDoc, the historical behavior). |
deleteDocument | (ref: DocumentReference) => Promise<void> | - |
documents | QueryDocumentSnapshot<DocumentData>[] | - |
error | Error | - |
hasMore | boolean | True if there might be another page. The hook tracks this via the last fetch’s length === pageSize. |
isLoading | boolean | - |
loadMore | () => void | Fetch the next page; live mode grows and re-establishes its window. |
refresh | () => void | Re-establish the active read/subscription. Useful after the consumer mutates data outside this hook. |
subscriptionGeneration | number | Identifies the active live subscription. Consumers that diff result snapshots can include this in their scope so a re-subscription (including load-more) establishes a silent baseline instead of looking like writes. |
UseDocumentSubcollectionsOptions
Properties
| Property | Type | Description |
|---|---|---|
documentRef | DocumentReference<DocumentData> | The document whose subcollections to list. When null/undefined the hook stays idle (empty, not loading) — used when the preview has no ref to drill from. |
firestore | Firestore | - |
listSubcollections | ListSubcollections | - |
UseDocumentSubcollectionsResult
Properties
| Property | Type |
|---|---|
error | Error |
isLoading | boolean |
subcollections | CollectionReference<DocumentData>[] |
UseQueryBuilderOptions
Properties
| Property | Type | Description |
|---|---|---|
initial? | Partial<QueryBuilderState> | Pre-populate the builder. |
UseRecursiveDeleteResult
Properties
| Property | Type | Description |
|---|---|---|
delete | (target: | DocumentReference<DocumentData> | CollectionReference<DocumentData>) => Promise<void> | Run the delete. Resolves when the iterator signals done. |
error | Error | Error thrown by the iterator, if any. Cleared at the start of the next call. |
isRunning | boolean | True while an iteration is in flight. |
progress | number | Number of nodes deleted in the current/last run. |
UseReferencePickerOptions
Properties
| Property | Type | Description |
|---|---|---|
firestore | Firestore | - |
initialPath? | string | Initial value to pre-populate the text input + parse. |
listCollections | (firestore: Firestore, parent: DocumentReference<DocumentData>) => Promise<CollectionReference<DocumentData>[]> | Lister for subcollections under a parent (or root when parent == null). The library does not ship a default — see useCollectionList for the same rationale (the modular Web SDK can’t enumerate collections client-side). |
pageSize? | number | Default page size for the document list when browsing inside a collection. Default 20. |
UseReferencePickerResult
Properties
| Property | Type | Description |
|---|---|---|
browseLocation | BrowseLocation | Current browse position in the tree. |
canDrillBack | boolean | Whether drillBack has anywhere to go. |
clear | () => void | Clear the path input + reset browse to root. |
collections | CollectionReference<DocumentData>[] | Collections available at the current browse level. Populated when browseLocation is root or document. |
documents | QueryDocumentSnapshot<DocumentData>[] | First page of documents in the current collection — populated when browseLocation.kind === 'collection'. |
drillBack | () => void | Step back one level. No-op when at root. |
drillIntoCollection | (ref: CollectionReference) => void | Drill into a collection — fetches its first page of documents. |
drillIntoDocument | (ref: DocumentReference) => void | Drill into a document — fetches its subcollections. |
error | string | Parse error, or null when valid. |
isLoading | boolean | True while a fetch is in flight. |
pathInput | string | Current text input value. |
pick | (ref: DocumentReference) => void | Commit a chosen reference. Updates pathInput (and therefore the parsed reference). |
reference | DocumentReference<DocumentData> | Validated DocumentReference parsed from pathInput, or null when the path is empty / invalid. |
setPathInput | (path: string) => void | Set the text-input value. Parses on every change. |
VectorView
Normalized read-side view of a Firestore vector (embedding) value. Editors and the renderer work against this rather than the raw shape so they don’t have to care which backend produced the value.
Properties
Type Aliases
BrowseLocation
type BrowseLocation =
| {
kind: "root";
}
| {
kind: "document";
ref: DocumentReference;
}
| {
kind: "collection";
ref: CollectionReference;
};
DocumentEditorAction
type DocumentEditorAction =
| {
nodeId: string;
type: "setValue";
value: unknown;
}
| {
newType: FieldType;
nodeId: string;
type: "setType";
}
| {
key: string;
nodeId: string;
type: "setKey";
}
| {
childType: FieldType;
key: string;
parentId: string;
type: "addMapEntry";
}
| {
childType: FieldType;
parentId: string;
type: "addArrayEntry";
}
| {
nodeId: string;
type: "remove";
}
| {
type: "reset";
}
| {
data: Record<string, unknown>;
type: "replaceData";
}
| {
nodeId: string;
type: "touch";
}
| {
type: "touchAll";
};
Discriminated union of every action the reducer accepts. Each
action carries a type discriminator plus the data the reducer
needs to apply it.
Type Declaration
{
nodeId: string;
type: "setValue";
value: unknown;
}
nodeId
nodeId: string;
type
type: "setValue";
value
value: unknown;
{
newType: FieldType;
nodeId: string;
type: "setType";
}
newType
newType: FieldType;
nodeId
nodeId: string;
type
type: "setType";
{
key: string;
nodeId: string;
type: "setKey";
}
key
key: string;
nodeId
nodeId: string;
type
type: "setKey";
{
childType: FieldType;
key: string;
parentId: string;
type: "addMapEntry";
}
childType
childType: FieldType;
key
key: string;
parentId
parentId: string;
type
type: "addMapEntry";
{
childType: FieldType;
parentId: string;
type: "addArrayEntry";
}
childType
childType: FieldType;
parentId
parentId: string;
type
type: "addArrayEntry";
{
nodeId: string;
type: "remove";
}
nodeId
nodeId: string;
type
type: "remove";
{
type: "reset";
}
type
type: "reset";
{
data: Record<string, unknown>;
type: "replaceData";
}
data
data: Record<string, unknown>;
type
type: "replaceData";
{
nodeId: string;
type: "touch";
}
nodeId
nodeId: string;
type
type: "touch";
Mark one node touched (dispatched on blur). Doesn’t change any value — only gates error display for consumers that check it.
{
type: "touchAll";
}
type
type: "touchAll";
Mark every node touched (dispatched on a submit attempt), so errors that were hidden pre-interaction all surface at once.
FieldEditorRegistry
type FieldEditorRegistry = Partial<Record<FieldType, FieldEditorContract<any>>>;
Map of field-type to editor contract. Partial<…> so consumers
can override one type without re-supplying the rest — the merge
happens at the <DocumentPreview> boundary.
The stored value type is FieldEditorContract<any> rather than
FieldEditorContract<unknown> because each per-type contract
narrows its generic (e.g., FieldEditorContract<Timestamp> for
timestamp) and TypeScript’s ComponentType is invariant in
props. any at the registry layer means the type-safety lives
at the per-contract definition site, not in the dispatch map.
FieldRenderer narrows back from unknown -> the right contract
via inferType at dispatch time.
FieldType
type FieldType =
| "string"
| "number"
| "boolean"
| "null"
| "timestamp"
| "geopoint"
| "reference"
| "bytes"
| "map"
| "array"
| "vector";
The set of value types @pyric/ui knows how to display + edit.
Maps 1:1 to Firestore’s serializable value shapes; consumers can
extend the registry but the built-in editors cover these.
FirestoreApi
type FirestoreApi = Pick<pyric-firestore-reference-api,
| "addDoc"
| "collection"
| "deleteDoc"
| "doc"
| "getDoc"
| "getDocs"
| "limit"
| "onSnapshot"
| "query"
| "setDoc"
| "startAfter">;
The modular Firestore functions the data hooks call, as an INJECTABLE bundle.
WHY: the hooks default to the in-process pyric/firestore API, but Pyric
Studio’s served mode drives the SAME ops over a SharedWorker via a PARALLEL
modular client (@pyric/cli/serve/worker: its own collection/getDocs/…
over a MessagePort, and a ClientDb that is not a pyric/firestore
Firestore). Statically importing the in-process fns hardwires the hooks to
the in-page sandbox; reading them from this context lets a consumer inject the
worker client’s fns so the hooks operate on the live worker backend without
the hooks (or the components) knowing which backend they hit.
The bundle is typed to the in-process signatures. A worker bundle is adapted
(cast) to this shape at the Studio boundary: the worker handles + snapshots
are runtime-compatible at the surface the hooks use (.id / .data() /
.docs / .ref), which is the contract function-injection relies on.
Default = the real pyric/firestore fns, so every existing consumer (the
dev-seed review build, tests, any app embedding @pyric/ui) is unchanged: no
provider needed unless you are swapping the backend.
ListSubcollections()
type ListSubcollections = (firestore: Firestore, parent: DocumentReference) => Promise<CollectionReference[]>;
Lister for a document’s own subcollections. Same injected-lister
shape as useCollectionList / ReferencePicker use — the modular
Web SDK doesn’t expose a native listCollections on the client, so
the caller wires it (sandbox in-process listing, a server proxy, or
a known schema list).
Parameters
| Parameter | Type |
|---|---|
firestore | Firestore |
parent | DocumentReference |
Returns
Promise<CollectionReference[]>
QueryOp
type QueryOp = WhereFilterOp;
UseQueryBuilderResult
type UseQueryBuilderResult = QueryBuilderState & QueryBuilderActions;
Variables
defaultFieldEditors
const defaultFieldEditors: FieldEditorRegistry;
Default registry. Covers every FieldType. Consumers extend
or override by passing their own (partial) registry into
<DocumentPreview> / <FieldRenderer> — the components merge
overrides into these defaults so a consumer can swap just one
editor without re-declaring the rest.
DocumentEditor
const DocumentEditor: {
Fields: typeof DocumentEditorFields;
Root: typeof DocumentEditorRoot;
};
Compound root export. Consumers pattern-match via dot access:
<DocumentEditor.Root initial={data} onChange={…}> <DocumentEditor.Fields /> </DocumentEditor.Root>
Type Declaration
Fields
Fields: typeof DocumentEditorFields;
Root
Root: typeof DocumentEditorRoot;
MULTI_VALUE_OPS
const MULTI_VALUE_OPS: ReadonlySet<QueryOp>;
Ops that accept an array of values. The value editor in the
bundled
QUERY_OPS
const QUERY_OPS: readonly QueryOp[];
Functions
asVectorView()
function asVectorView(value: unknown): VectorView;
Detect + normalize a Firestore vector value, or return null if the
value isn’t a vector. Vectors reach @pyric/ui in several runtime
shapes depending on the backend the snapshot came from — there is no
single VectorValue class pyric/firestore re-exports, so we match
structurally (the same strategy isDocumentReferenceShape uses
for refs):
- pyric
Vectorwrapper — frozen.value: number[]array plus a.dimensiongetter (sandbox / rules-side reads). - firebase/firestore (web)
VectorValue— exposes.toArray()and nothing else publicly. - firebase-admin
VectorValue— internal._values: number[](also a.toArray()). - wire sentinel —
{ __type__: '__vector__', value: number[] }, the plain-object encoded form a discover crawler / seed emits.
A bare number[] is intentionally NOT a vector — those stay array.
Only the typed/branded shapes above match.
Parameters
| Parameter | Type |
|---|---|
value | unknown |
Returns
CollectionList()
function CollectionList(__namedParameters: CollectionListProps): Element;
Headless collection list. Takes a pre-fetched array of references
(from useCollectionList) plus a select callback. Renders one
row per collection with data-pyric-collection-id for styling
and testing. The library does not own the data fetch — the hook
does — so this component is a thin presentational layer.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | CollectionListProps |
Returns
Element
DeleteWithConfirm()
function DeleteWithConfirm(__namedParameters: DeleteWithConfirmProps): Element;
Composition that wires useConfirm + useRecursiveDelete. Requires
a <ConfirmProvider> ancestor.
The default trigger renders a plain <button> carrying the
destructive intent (the consumer styles via [data-pyric-destructive]).
Consumers wanting different chrome pass renderTrigger.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | DeleteWithConfirmProps |
Returns
Element
detectCollisions()
function detectCollisions(existingIds: readonly string[], docs: readonly ParsedImportDoc[]): string[];
Ids in docs (map-shape entries only — id !== null) that already exist
in existingIds. The UI shows the skip-or-overwrite policy choice ONLY
when this returns a non-empty list.
Parameters
| Parameter | Type |
|---|---|
existingIds | readonly string[] |
docs | readonly ParsedImportDoc[] |
Returns
string[]
DocumentEditorFields()
function DocumentEditorFields(): Element;
Renders the top-level fields of the document. For finer control,
a consumer can call useDocumentEditorContext() and render the
tree themselves.
Returns
Element
DocumentEditorRoot()
function DocumentEditorRoot(__namedParameters: DocumentEditorRootProps): Element;
Wires the useDocumentEditor hook + field-editor registry into a
React context so <DocumentEditor.Fields> and <DocumentEditor.AddField>
can render the tree without prop drilling.
Pattern: hook + compound component. Consumers wanting full control
over the layout call useDocumentEditor directly and render their
own tree; consumers wanting the default rendering use this.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | DocumentEditorRootProps |
Returns
Element
DocumentList()
function DocumentList(__namedParameters: DocumentListProps): Element;
Headless document list. Below virtualizeThreshold, renders a
plain <ul>; above it, switches to TanStack-Virtual via
<VirtualList> so 10k-doc collections don’t bloat the DOM.
The hook owns pagination state; this component just renders. The
Load More button only renders when hasMore is true AND
onLoadMore is provided. Consumers wanting infinite scroll
trigger onLoadMore from a sentinel IntersectionObserver in
their own code — the component doesn’t bake that policy in.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | DocumentListProps |
Returns
Element
DocumentPreview()
function DocumentPreview(__namedParameters: DocumentPreviewProps): Element;
Read-only renderer for a Firestore document. Iterates top-level fields in lexicographic order; each field dispatches through the field-editor registry on its inferred type.
Headless — no shipped CSS. Consumers style via className on the
root and [data-pyric-field-type] / [data-pyric-field-path]
attribute selectors on the per-field nodes.
Editing arrives in M3 (<DocumentEditor>). M2 only displays.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | DocumentPreviewProps |
Returns
Element
FieldRenderer()
function FieldRenderer(__namedParameters: FieldRendererProps): Element;
Dispatches a single value to its registered display component. Recursive editors (Map, Array) re-enter through this component for their children, threading the same registry.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | FieldRendererProps |
Returns
Element
FirestoreApiProvider()
function FirestoreApiProvider(__namedParameters: {
children: ReactNode;
value: FirestoreApi;
}): FunctionComponentElement<ProviderProps<FirestoreApi>>;
Provide a Firestore API bundle to the subtree. Pyric Studio wraps its data
surface with this, supplying the in-process bundle for dev-seed review and the
SharedWorker client bundle under pyric dev --ui.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | { children: ReactNode; value: FirestoreApi; } |
__namedParameters.children | ReactNode |
__namedParameters.value | FirestoreApi |
Returns
FunctionComponentElement<ProviderProps<FirestoreApi>>
firestoreAutoId()
function firestoreAutoId(): string;
A Firestore-style 20-char auto id (same alphabet/length the SDK uses).
Returns
string
firestoreValuesEqual()
function firestoreValuesEqual(previous: unknown, next: unknown): boolean;
Firestore-aware structural equality for values delivered by either the in-process SDK or the SharedWorker serializer.
Parameters
| Parameter | Type |
|---|---|
previous | unknown |
next | unknown |
Returns
boolean
inferType()
function inferType(value: unknown): FieldType;
Runtime-classify a value into one of the FieldTypes.
The discrimination order matters:
nullchecked beforetypeof === 'object'(null is an object)- vector (a typed embedding wrapper) checked before
Array.isArrayand before generic objects — its wire-sentinel shape is a plain object, and a barenumber[]must stayarray, notvector Array.isArraychecked before generic objects- Firestore special types (Timestamp/GeoPoint/Bytes/DocumentRef)
checked before falling through to
map
undefined values aren’t legal Firestore field values; we coerce
them to 'null' rather than throw — the caller can decide whether
to display or filter.
Parameters
| Parameter | Type |
|---|---|
value | unknown |
Returns
initState()
function initState(initial: Record<string, unknown>): DocumentEditorState;
Initial state factory. Builds tree + initial snapshot + error count.
Parameters
| Parameter | Type |
|---|---|
initial | Record<string, unknown> |
Returns
mergeFieldEditors()
function mergeFieldEditors(override: Partial<Record<FieldType, FieldEditorContract<any>>>): FieldEditorRegistry;
Merge a consumer-supplied registry over the defaults. undefined
input returns the defaults as-is. Used internally by
<DocumentPreview> so consumers don’t have to spread manually.
Parameters
| Parameter | Type |
|---|---|
override | Partial<Record<FieldType, FieldEditorContract<any>>> |
Returns
parseImport()
function parseImport(input: string, options?: ParseImportOptions): ParseImportResult;
Parse raw JSON text into the documents it would create. Never throws —
a JSON syntax error or a wrong top-level shape becomes an entry in
errors with an empty docs array.
Parameters
| Parameter | Type |
|---|---|
input | string |
options? | ParseImportOptions |
Returns
QueryBuilder()
function QueryBuilder(__namedParameters: QueryBuilderProps): Element;
Default visible composition over useQueryBuilder. Renders the
condition list + orderBy + limit form. Headless — every node
carries data-pyric-* for styling.
Values are JSON-parsed on input. 42, "text", true, null,
and [1, 2, 3] (for in/not-in/array-contains-any) all
work; raw strings that aren’t JSON-parsable fall through as-is.
For non-JSON Firestore values (Timestamp, GeoPoint, Reference,
Bytes), consumers either use the hook directly with their own
value editors or swap the rendered component out.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | QueryBuilderProps |
Returns
Element
reducer()
function reducer(state: DocumentEditorState, action: DocumentEditorAction): DocumentEditorState;
Pure reducer. Every action returns a fresh state — no in-place
mutation of state.tree. Validation re-runs after the structural
change so errorCount is always current. Container nodes (map /
array) ignore actions that don’t apply to them rather than
throwing; the components only render valid affordances per type.
Parameters
| Parameter | Type |
|---|---|
state | DocumentEditorState |
action | DocumentEditorAction |
Returns
ReferencePicker()
function ReferencePicker(__namedParameters: ReferencePickerProps): Element;
Visible reference picker — text input + browseable panel.
Two ways to commit a reference:
- Type a path, then click Commit (enabled only when the path
parses to a valid
DocumentReference). - Drill into a collection in the panel and click a document row.
Either path fires onPick(ref). Headless — emits structural
data-pyric-* for styling.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | ReferencePickerProps |
Returns
Element
treeFromData()
function treeFromData(data: Record<string, unknown>): EditorTree;
Build a normalized editor tree from a Firestore-shaped object.
The root node is a virtual map that holds the document’s
top-level fields. Order of children mirrors Object.entries (the
renderer is responsible for any sort it wants).
Parameters
| Parameter | Type |
|---|---|
data | Record<string, unknown> |
Returns
treeToData()
function treeToData(tree: EditorTree): Record<string, unknown>;
Serialize a tree back to a Firestore-shaped object. Leaf values
pass through unchanged (so a Timestamp round-trips as the same
Timestamp instance). Maps recurse with their sorted keys; arrays
recurse in childIds order.
Parameters
| Parameter | Type |
|---|---|
tree | EditorTree |
Returns
Record<string, unknown>
truncateVectorsForDisplay()
function truncateVectorsForDisplay(value: unknown): unknown;
Deep-replace any vector-shaped value with a compact preview STRING, so the
result can be JSON.stringify’d / formatted without dumping full embeddings.
Recurses plain objects + arrays; class instances (Timestamp/GeoPoint) and
scalars pass through untouched. Vector instances/sentinels are caught first.
Parameters
| Parameter | Type |
|---|---|
value | unknown |
Returns
unknown
useCollectionList()
function useCollectionList(__namedParameters: UseCollectionListOptions): UseCollectionListResult;
Operational read + create for collections under a parent (or root).
Listing is injected because the modular Web SDK doesn’t expose a
native listCollections on the client — see options docs.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | UseCollectionListOptions |
Returns
useDocumentEditor()
function useDocumentEditor(options?: UseDocumentEditorOptions): UseDocumentEditorResult;
Headless document editor. Owns the entire edit state for one
document via a pure reducer. Consumers either render the bundled
<DocumentEditor> compound component over this hook, or render
their own tree using the returned state.
The hook builds its tree from initial on first mount. Changing
initial later does NOT rebuild the tree; live viewers explicitly call
replaceData() when a newer snapshot should become the clean baseline.
This matches the firebase-tools-ui pattern of treating the editor as a
stateful workspace while still allowing snapshot-driven reconciliation.
Parameters
| Parameter | Type |
|---|---|
options? | UseDocumentEditorOptions |
Returns
useDocumentEditorContext()
function useDocumentEditorContext(): UseDocumentEditorResult;
Read access to the underlying editor state from inside a Root.
Returns
useDocumentList()
function useDocumentList(__namedParameters: UseDocumentListOptions): UseDocumentListResult;
Paginated document list with two acquisition strategies. The default
paged mode uses startAfter and accumulates one-shot reads. live keeps
the requested prefix under one onSnapshot listener; loading more grows
that prefix and establishes a new subscription baseline.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | UseDocumentListOptions |
Returns
useDocumentSubcollections()
function useDocumentSubcollections(__namedParameters: UseDocumentSubcollectionsOptions): UseDocumentSubcollectionsResult;
Read a single document’s subcollection list. A thin specialization of
the useCollectionList pattern, scoped to one parent document and
read-only (no create — that lives in useCollectionList).
Parameters
| Parameter | Type |
|---|---|
__namedParameters | UseDocumentSubcollectionsOptions |
Returns
UseDocumentSubcollectionsResult
useFirestoreApi()
function useFirestoreApi(): FirestoreApi;
Read the active Firestore API bundle (defaults to in-process pyric/firestore).
Returns
useFirestoreCollection()
function useFirestoreCollection(query: Query<DocumentData>): SubscriptionState<QuerySnapshot<DocumentData>>;
Subscribe to a Firestore query (a Query from pyric/firestore’s
modular surface, including any CollectionReference, which extends
Query). Returns { data, error, isLoading }.
Null/undefined query short-circuits to idle. Cleanup is automatic
on unmount or query change. Query objects don’t have a stable
structural identity, so the consumer must memoize at the call site
— pass the same instance across renders to avoid re-subscribing.
Parameters
| Parameter | Type |
|---|---|
query | Query<DocumentData> |
Returns
SubscriptionState<QuerySnapshot<DocumentData>>
useFirestoreDoc()
function useFirestoreDoc(ref: DocumentReference<DocumentData>): SubscriptionState<DocumentSnapshot<DocumentData>>;
Subscribe to a single Firestore document. Returns { data, error, isLoading }. Null/undefined ref short-circuits to an idle state
(data: undefined, error: undefined, isLoading: false) — useful
for conditional rendering before a ref is known.
Cleanup is automatic on unmount or ref change. Memoize the ref at the call site; this hook’s effect re-runs on identity change.
Parameters
| Parameter | Type |
|---|---|
ref | DocumentReference<DocumentData> |
Returns
SubscriptionState<DocumentSnapshot<DocumentData>>
useQueryBuilder()
function useQueryBuilder(options?: UseQueryBuilderOptions): UseQueryBuilderResult;
Headless query-builder state machine. Single-level — no nested
and()/or() groups in v1. Consumers compose the state into a
Firestore Query via buildQuery(base) and feed that into
useDocumentList / useFirestoreCollection.
Parameters
| Parameter | Type |
|---|---|
options? | UseQueryBuilderOptions |
Returns
useRecursiveDelete()
function useRecursiveDelete(impl: RecursiveDeleteImpl): UseRecursiveDeleteResult;
Drive a RecursiveDeleteImpl from a React component. Tracks progress + running state so the consumer can render a progress indicator. Errors are caught and surfaced via the returned state, not thrown.
Stale-run protection: if the component remounts (or the user cancels and starts a new run) before a previous iteration finishes, the older run’s progress updates are dropped via a generation token.
Parameters
| Parameter | Type |
|---|---|
impl | RecursiveDeleteImpl |
Returns
useReferencePicker()
function useReferencePicker(__namedParameters: UseReferencePickerOptions): UseReferencePickerResult;
Picker state machine. Browses a Firestore tree level-by-level
(root → collection → document → collection → …), maintains a
separately-validated text-input path, and commits a chosen
reference via pick.
Headless — consumers compose the resulting state into their own
UI, or use the bundled <ReferencePicker> component.
Parameters
| Parameter | Type |
|---|---|
__namedParameters | UseReferencePickerOptions |
Returns
validateCollectionId()
function validateCollectionId(id: string): string;
Validate a collection id. Returns an error message, or undefined when valid.
Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
string
validateDocumentId()
function validateDocumentId(id: string): string;
Validate a document id. Returns an error message, or undefined when valid.
Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
string
validateLeaf()
function validateLeaf(type: FieldType, value: unknown): string;
Per-type leaf validator. Returns an error message when the value
doesn’t satisfy the type’s constraints, undefined when valid.
map and array aren’t validated here — their integrity comes
from their children + (for maps) sibling-key uniqueness, which
is enforced in validateTree.
Parameters
| Parameter | Type |
|---|---|
type | FieldType |
value | unknown |
Returns
string
validateTree()
function validateTree(tree: EditorTree): {
errorCount: number;
tree: EditorTree;
};
Walk the tree, attach an error to each node, and return the
mutated tree along with the total error count. The function does
not mutate the input; it returns a fresh tree.
Per-leaf errors come from validateLeaf. Map nodes additionally surface duplicate-key + empty-key errors on the offending children (not on the parent).
Parameters
| Parameter | Type |
|---|---|
tree | EditorTree |
Returns
{
errorCount: number;
tree: EditorTree;
}
errorCount
errorCount: number;
tree
tree: EditorTree;
vectorPreview()
function vectorPreview(view: VectorView): string;
Compact, display-safe rendering of a vector: vector · <dim> [a, b, c, …].
So a real embedding never dumps its full array into a diff, a rules trace, or
a debugger panel.
Parameters
| Parameter | Type |
|---|---|
view | VectorView |
Returns
string