Skip to main content

Class: ClientStack

Defined in: packages/client/src/core/stack.ts:192

The core database engine for DocStack client applications.

ClientStack provides a complete offline-first datastore built on PouchDB with:

  • Schema validation and class-based document modeling
  • SQL-like querying capabilities
  • Field-level encryption via CryptoEngine
  • Access control via PolicyEngine
  • Background job execution via JobEngine

Example

// Create a new stack instance
const stack = await ClientStack.create('my-app-db');

// Authenticate a user
const session = await stack.authenticate({ username: 'admin', password: 'secret' });

// Query documents using SQL
const { rows } = await stack.query('SELECT * FROM Task WHERE isComplete = false');

Extends

  • Stack

Properties

PropertyTypeDefault valueDescriptionOverridesInherited fromDefined in
appVersionstring"0.0.1"The current application version string.Stack.appVersion-packages/client/src/core/stack.ts:230
authSession?AuthSessionProofundefinedThe current authenticated user session, if any. Contains session details, derived key, and document encryption key.Stack.authSession-packages/client/src/core/stack.ts:357
cacheobjectundefinedIn-memory cache for Class and Domain objects. Items are cached with a 15-minute TTL to improve performance.Stack.cache-packages/client/src/core/stack.ts:235
connectionstringundefinedThe connection string used to create this stack.Stack.connection-packages/client/src/core/stack.ts:226
cryptoEngineCryptoEngineundefinedEngine for field-level encryption and decryption. Handles key derivation (PBKDF2) and AES-GCM encryption.--packages/client/src/core/stack.ts:346
dbDatabase<{ }>undefinedThe underlying PouchDB database instance. Initialized asynchronously during stack creation. Example // Access the raw PouchDB API for advanced operations const allDocs = await stack.db.allDocs({ include_docs: true });Stack.db-packages/client/src/core/stack.ts:204
jobEngineJobEngineundefinedEngine for executing background jobs and scheduled tasks. Jobs are defined as documents and run in a sandboxed environment. Example const run = await stack.jobEngine.executeJob('Job-CleanupOldData'); console.log('Job completed:', run.status);Stack.jobEngine-packages/client/src/core/stack.ts:307
jobSchedulerJobSchedulerundefinedDecides when approved jobs run, and dispatches them. Constructed with the stack but deliberately not started by it: ~Job.content is JavaScript that replicates, so which jobs may run with nobody watching is the application's decision, named at JobScheduler.start. Example stack.jobScheduler.start({ jobs: ['Job-review-campaign'] });--packages/client/src/core/stack.ts:321
lastDocIdnumberundefined-Stack.lastDocId-packages/client/src/core/stack.ts:224
listenersChangesSubscription[][]Every live changes subscription this stack has handed out; released on close.Stack.listeners-packages/client/src/core/stack.ts:272
modelWorkerWorkernull-Stack.modelWorker-packages/client/src/core/stack.ts:296
namestringundefinedThe unique name identifier for this stack instance, derived from the connection string.Stack.name-packages/client/src/core/stack.ts:222
options?StackOptionsundefinedConfiguration options provided during stack creation.Stack.options-packages/client/src/core/stack.ts:228
patchCountnumberundefined-Stack.patchCount-packages/client/src/core/stack.ts:238
releaseListener(listener?) => voidundefinedCancels a subscription and forgets it. The counterpart to subscribeClassDocs: a caller that is done watching must call this rather than dropping the handle, because the subscription keeps the underlying feed - and everything the handler closes over - alive on its own. Safe to call with a handle that is already cancelled, or with nothing at all.-Stack.releaseListenerpackages/shared/lib/utils/stack/index.d.ts:115
schemaVersionstringundefined-Stack.schemaVersion-packages/client/src/core/stack.ts:352
subscribeClassDocs(className, target, classObj?) => ChangesSubscriptionundefinedSubscribes an event target to a class's document changes. Prefer this over wiring onClassDoc directly. It routes every change through prepareChangeDocument, so a subclass that encrypts cannot forget to decrypt on this path, and it serialises the handlers: preparing a document is asynchronous, so two rapid changes to one document could otherwise be dispatched in whichever order their preparation happened to finish. The change's seq still rides along for consumers that want to discard a stale update independently.-Stack.subscribeClassDocspackages/shared/lib/utils/stack/index.d.ts:80
subscribeDomainDocs(domainName, target) => ChangesSubscriptionundefinedSubscribes an event target to a domain's relation documents. The Domain counterpart to subscribeClassDocs, and it needs to be a separate call rather than the same one: a relation document is named by ~domain and has no ~class, so subscribing it as if it were a class matches nothing and the target simply never hears anything.-Stack.subscribeDomainDocspackages/shared/lib/utils/stack/index.d.ts:93
transactionEngineTransactionEngineundefinedNamed write transactions (ADR-0039). Dormant unless the stack was opened with transactions: true; see beginTransaction.--packages/client/src/core/stack.ts:351

Methods

addClass()

addClass(classObj): Promise<ClassModel>;

Defined in: packages/client/src/core/stack.ts:3841

Parameters

ParameterType
classObjClass

Returns

Promise<ClassModel>

Overrides

Stack.addClass

addClassLock()

addClassLock(className): Promise<boolean>;

Defined in: packages/client/src/core/stack.ts:2429

Parameters

ParameterType
classNamestring

Returns

Promise<boolean>

Overrides

Stack.addClassLock

addDesignDocumentPKs()

addDesignDocumentPKs(
className,
pKs,
temp): Promise<string>;

Defined in: packages/client/src/core/stack.ts:3904

Parameters

ParameterTypeDefault value
classNamestringundefined
pKsstring[]undefined
tempbooleanfalse

Returns

Promise<string>

Overrides

Stack.addDesignDocumentPKs

addDomain()

addDomain(domainObj): Promise<DomainModel>;

Defined in: packages/client/src/core/stack.ts:3882

Parameters

ParameterType
domainObjDomain

Returns

Promise<DomainModel>

Overrides

Stack.addDomain

advanceLastDocId()

advanceLastDocId(count): Promise<number>;

Defined in: packages/client/src/core/stack.ts:3780

Advances the document-id counter by count in one database write.

Batch creation hands out count ids from the in-memory counter and commits them here once, instead of a get+put round-trip per document.

Parameters

ParameterType
countnumber

Returns

Promise<number>


applyPatch()

applyPatch(patch): Promise<string>;

Defined in: packages/client/src/core/stack.ts:2160

Parameters

ParameterType
patchPatch

Returns

Promise<string>


authenticate()

authenticate(credentials): Promise<AuthSessionProof>;

Defined in: packages/client/src/core/stack.ts:1934

Authenticates a user and establishes a session.

This method:

  1. Looks up the user by username
  2. Executes the configured authentication job (e.g., password verification)
  3. Creates a new session document
  4. Sets up encryption keys for the session

Parameters

ParameterTypeDescription
credentialsClientCredentialsThe user's login credentials containing username and password

Returns

Promise<AuthSessionProof>

The authentication session proof containing session info and encryption keys

Throws

Error if the user is not found or authentication fails

Example

const proof = await stack.authenticate({
username: 'john.doe',
password: 'securePassword123'
});
console.log('Logged in as:', proof.session.username);

beginTransaction()

beginTransaction(): TransactionHandle;

Defined in: packages/client/src/core/stack.ts:3030

Opens a named write transaction (ADR-0039). Requires the stack to have been opened with transactions: true.

Writes through the handle validate at the call site and stage in memory; reads through it see the staged state overlaid on committed state. Nothing reaches the database - or replication, or any other reader - until commit. stack.db stays live and unchanged next to open transactions: direct writes land immediately, and only touch a transaction by making its commit refuse when they advance a staged document's revision.

Returns

TransactionHandle


canApplyQueryLimitEarly()

canApplyQueryLimitEarly(className): Promise<boolean>;

Defined in: packages/client/src/core/stack.ts:2683

Whether a database-level limit returns the same rows as limiting in memory.

findDocuments drops a document whose visible fields are all sealed - a locked legacy key, or a scope the keyring cannot open. A limit applied before that filter would under-fill. So pushdown is allowed exactly when no row of this class can drop: the class has no encrypted attributes, or every key that might seal one is held (the legacy key, and every declared scope - a document may carry any label). The query engine asks this before pushing a SQL LIMIT into the fetch.

Parameters

ParameterTypeDescription
classNamestringThe class being queried.

Returns

Promise<boolean>


cancelSync()

cancelSync(): void;

Defined in: packages/client/src/core/stack.ts:546

Stops this stack's replication. Idempotent; called automatically by close.

Returns

void


checkSystem()

checkSystem(): Promise<void>;

Defined in: packages/client/src/core/stack.ts:2239

Returns

Promise<void>


cleanupSortIndexes()

cleanupSortIndexes(options): Promise<{
kept: string[];
removed: string[];
}>;

Defined in: packages/client/src/core/stack.ts:2802

Drops sort indexes that have gone unused.

Covers indexes this device created and ones that replicated in as design documents from a peer: every _design/docstack-sort-* doc is considered, and one with no registry entry is adopted with the current time as first-seen, so it gets a full idle period before removal. Runs automatically at init; callable directly for an immediate sweep.

Parameters

ParameterTypeDescription
options{ olderThanMs?: number; }-
options.olderThanMs?numberIdle threshold; defaults to SORT_INDEX_MAX_IDLE_MS.

Returns

Promise<{ kept: string[]; removed: string[]; }>

Which fields were removed and which kept.


clearAuthSession()

clearAuthSession(): void;

Defined in: packages/client/src/core/stack.ts:601

Clears the current authentication session and removes the document encryption key. Call this when a user logs out.

Returns

void


clearClassLock()

clearClassLock(className): Promise<boolean>;

Defined in: packages/client/src/core/stack.ts:2450

Parameters

ParameterType
classNamestring

Returns

Promise<boolean>

Overrides

Stack.clearClassLock

close()

close(): void;

Defined in: packages/client/src/core/stack.ts:3006

Closes the stack and cleans up all resources. Removes event listeners and terminates background workers.

Returns

void

Overrides

Stack.close

commit()

commit(t): Promise<TransactionCommitReport>;

Defined in: packages/client/src/core/stack.ts:3040

Flushes a transaction's staged writes as one batch through the authoring pipeline. On refusal - validation, or a document changed underneath - nothing is persisted and the transaction stays open. The report says what landed and on what storage guarantee (adapter.atomicBatch).

Parameters

ParameterType
tstring | TransactionHandle

Returns

Promise<TransactionCommitReport>


createDoc()

createDoc(
docId,
type,
classObj,
params): Promise<Document>;

Defined in: packages/client/src/core/stack.ts:4019

Creates or updates a single document in the database.

If docId is provided and the document exists, it will be updated. If docId is null, a new ID will be auto-generated in the format {type}-{incrementalId}. Access policies are enforced before writing.

Parameters

ParameterTypeDescription
docIdstringThe document ID, or null to auto-generate
typestringThe class name (e.g., 'Task', 'User')
classObj| Class | { [name: string]: AttributeModel; }The Class instance or schema definition for validation
params{ }The document data to save

Returns

Promise<Document>

The created or updated document

Throws

Error if policy check fails or document type conflicts

Example

// Create with auto-generated ID
const task = await stack.createDoc(null, 'Task', taskClass, {
title: 'New Task',
isComplete: false
});

// Update existing document
await stack.createDoc('Task-123', 'Task', taskClass, {
title: 'Updated Title'
});

Overrides

Stack.createDoc

createDocs()

createDocs(
docs,
type,
classObj): Promise<Document[]>;

Defined in: packages/client/src/core/stack.ts:4138

Creates or updates multiple documents in a single batch operation. More efficient than calling createDoc multiple times.

Parameters

ParameterTypeDescription
docsobject[]Array of document specifications with optional docId and params
typestringThe class name for all documents
classObj| Class | { [name: string]: AttributeModel; }The Class instance or schema definition for validation

Returns

Promise<Document[]>

Array of created or updated documents

Throws

Error if policy check fails for any document

Example

const tasks = await stack.createDocs([
{ docId: null, params: { title: 'Task 1' } },
{ docId: null, params: { title: 'Task 2' } },
{ docId: 'Task-existing', params: { title: 'Updated' } }
], 'Task', taskClass);

Overrides

Stack.createDocs

createRelationDoc()

createRelationDoc(
docId,
relationName,
domainObj,
params): Promise<RelationDocument>;

Defined in: packages/client/src/core/stack.ts:4240

Creates a relation document linking two entities via a Domain. Relation documents represent relationships between documents (e.g., 1:N, N:N).

Parameters

ParameterTypeDescription
docIdstringThe relation document ID, or null to auto-generate
relationNamestringA descriptive name for this relation instance
domainObjDomainThe Domain defining the relationship type
params{ sourceClass: string; sourceId: string; targetClass: string; targetId: string; }The relation parameters including source and target references
params.sourceClassstring-
params.sourceIdstring-
params.targetClassstring-
params.targetIdstring-

Returns

Promise<RelationDocument>

The created relation document, or null on error

Example

const relation = await stack.createRelationDoc(
null,
'ProjectTask',
projectTaskDomain,
{
sourceClass: 'Project',
targetClass: 'Task',
sourceId: 'Project-1',
targetId: 'Task-42'
}
);

Overrides

Stack.createRelationDoc

createRelationDocs()

createRelationDocs(
docs,
relationName,
domainObj): Promise<RelationDocument[]>;

Defined in: packages/client/src/core/stack.ts:4338

Creates multiple relation documents in a single batch operation. More efficient than calling createRelationDoc multiple times.

Parameters

ParameterTypeDescription
docsobject[]Array of relation specifications
relationNamestringA descriptive name for these relations
domainObjDomainThe Domain defining the relationship type

Returns

Promise<RelationDocument[]>

Array of created relation documents

Example

const relations = await stack.createRelationDocs([
{ docId: null, params: { sourceClass: 'Project', targetClass: 'Task', sourceId: 'Project-1', targetId: 'Task-1' } },
{ docId: null, params: { sourceClass: 'Project', targetClass: 'Task', sourceId: 'Project-1', targetId: 'Task-2' } }
], 'ProjectTasks', projectTaskDomain);

Overrides

Stack.createRelationDocs

deleteDocument()

deleteDocument(_id): Promise<boolean>;

Defined in: packages/client/src/core/stack.ts:4420

Sets the active param of a document to false

Parameters

ParameterTypeDescription
_idstring

Returns

Promise<boolean>

Promise

Overrides

Stack.deleteDocument

destroyDb()

destroyDb(): Promise<boolean>;

Defined in: packages/client/src/core/stack.ts:3810

Returns

Promise<boolean>


discardTransaction()

discardTransaction(t): void;

Defined in: packages/client/src/core/stack.ts:3045

Drops a transaction's staged writes. Idempotent.

Parameters

ParameterType
tstring | TransactionHandle

Returns

void


dump()

dump(): Promise<AllDocsResponse<{
}>>;

Defined in: packages/client/src/core/stack.ts:872

Exports all documents from the database. Useful for debugging or creating backups.

Returns

Promise<AllDocsResponse<{ }>>

All documents including their content

Overrides

Stack.dump

ensureSortIndex()

ensureSortIndex(field): Promise<boolean>;

Defined in: packages/client/src/core/stack.ts:2752

Creates (or confirms) a Mango index for sorting by field, and records the use.

Indexes are made on demand by the query engine when an ORDER BY can ride the database, and every index is a standing cost: a view updated on every write from then on. Three things keep that bounded: a cap (MAX_SORT_INDEXES) past which this returns false and the caller sorts in memory; a usage registry (a _local document, per device) stamped on each use; and cleanupSortIndexes, run at init, dropping indexes idle past SORT_INDEX_MAX_IDLE_MS. A dropped index is not an error - the next sorted query recreates it.

Parameters

ParameterTypeDescription
fieldstringThe document field to index for sorting (under ~class).

Returns

Promise<boolean>

true when the index exists and may be used for a sorted query.


exportContent()

exportContent(options): Promise<ContentExport>;

Defined in: packages/client/src/core/stack.ts:1055

Exports this stack's application content, and nothing else.

Deliberately narrower than dump, which returns the database verbatim - class models, patches, users, sessions, policies, design documents, and encrypted attributes as unreadable payloads. That is a backup of this database. This is the portable one: the documents an application put in, ready for importContent to place into a stack whose schema its own patches built and whose document key is its own.

What it does not do:

  • It does not bypass encryption. Documents are read through the decrypting path, so encrypted attributes come out as plaintext. That is what makes the export portable across keys - and what makes the result as sensitive as the data itself. A locked stack cannot decrypt, so the export is refused rather than silently emitting null where values should be (see allowLossyWhenLocked).
  • It does not bypass read policies. Documents the current session may not read are absent, exactly as they are absent from findDocuments.
  • It carries no schema, no patches and no system documents.

Parameters

ParameterTypeDescription
optionsContentExportOptionsWhich classes and domains to cover; see ContentExportOptions.

Returns

Promise<ContentExport>

The portable envelope.

Throws

Error when the stack is locked and an exported class has encrypted attributes, unless allowLossyWhenLocked is set.

Example

const payload = await stack.exportContent({ classes: ["Task", "Project"] });
download(new Blob([JSON.stringify(payload)], { type: "application/json" }));

findDocument()

findDocument<T>(
selector,
fields,
skip,
limit): Promise<T>;

Defined in: packages/client/src/core/stack.ts:3510

Finds a single document matching a selector. Convenience wrapper around findDocuments that returns the first match.

Type Parameters

Type ParameterDefault typeDescription
T extends | Document | RelationDocumentDocumentThe expected document type

Parameters

ParameterTypeDefault valueDescription
selectoranyundefinedA PouchDB/Mango query selector
fieldsanyundefinedOptional list of fields to return
skipanyundefinedNumber of documents to skip
limitanyundefinedMaximum number of documents to check

Returns

Promise<T>

The first matching document, or null if none found


findDocuments()

findDocuments<T>(
selector,
fields?,
skip?,
limit?,
sort?): Promise<{
[key: string]: any;
docs: T[];
}>;

Defined in: packages/client/src/core/stack.ts:3253

Finds multiple documents matching a PouchDB/Mango-style selector. Automatically filters to only active documents and applies access policies.

Type Parameters

Type ParameterDefault typeDescription
T extends | Document | RelationDocumentDocumentThe expected document type

Parameters

ParameterTypeDescription
selector{ [key: string]: any; }A PouchDB/Mango query selector
fields?string[]Optional list of fields to return
skip?numberNumber of documents to skip (for pagination)
limit?numberMaximum number of documents to return
sort?object[]-

Returns

Promise<{ [key: string]: any; docs: T[]; }>

Object containing matching documents array

Example

const result = await stack.findDocuments({
'~class': { $eq: 'Task' },
isComplete: { $eq: false }
});
console.log('Found tasks:', result.docs.length);

Overrides

Stack.findDocuments

findDocumentsIterator()

findDocumentsIterator<T>(selector, options): AsyncGenerator<T, void, void>;

Defined in: packages/client/src/core/stack.ts:3390

Reads documents matching a selector as an async stream, in _id order.

Pages through the database with a keyset cursor on _id (which the primary index serves) instead of materializing the full result: peak memory is one batch, and total work across all pages is the same one scan a single big read would do. Each batch goes through the same policy/decryption pipeline as findDocuments. The cursor advances by the last fetched document, not the last readable one, so pages thinned out by policy filtering cannot stall the iteration.

Type Parameters

Type ParameterDefault type
T extends | Document | RelationDocumentDocument

Parameters

ParameterTypeDescription
selector{ [key: string]: any; }A Mango selector; active: true is injected unless present.
options{ batchSize?: number; fields?: string[]; }-
options.batchSize?numberDocuments fetched per page (default 100).
options.fields?string[]Projection; _id is always included (the cursor needs it).

Returns

AsyncGenerator<T, void, void>

Example

for await (const doc of stack.findDocumentsIterator({ "~class": "Task" })) {
render(doc);
}

generateDocId()

generateDocId(type): string;

Defined in: packages/client/src/core/stack.ts:2051

Mints an identifier for a new document.

Random, not sequential, and that is the whole point. Ids used to be ${type}-${lastDocId + 1}, from a counter that only local writes advance: a document arriving by replication goes through getReplicationHandle, which bypasses that path by design, so the counter stood still while ids were consumed. The next local write then minted an id the database already held, PouchDB resolved the two as revisions of one document, and the new one was gone - with no error, because the conflict was swallowed. Two devices did it to each other from their very first document, both starting at 1.

No counter repair fixes that. Feeding replicated documents back into the counter still leaves two offline devices minting the same id, because a sequence derived from local state cannot be unique across devices that have not met. The identifier has to stop being derived from local state at all. See ADR-0023.

The class prefix stays, so an id still says what it is.

Parameters

ParameterTypeDescription
typestringThe class or domain name, used as the prefix.

Returns

string

An id of the form Task-9f2c..., 96 random bits wide.

Example

stack.generateDocId("Task"); // "Task-3f9a2b7c1d4e5f60a1b2c3d4"

getAccessScopeKids()

getAccessScopeKids(scopeId): Promise<Set<string>>;

Defined in: packages/client/src/core/stack.ts:722

Every key id belonging to a scope, across rotation versions - or null for an unknown scope.

Parameters

ParameterType
scopeIdstring

Returns

Promise<Set<string>>


getClass()

getClass(className, fresh): Promise<Class>;

Defined in: packages/client/src/core/stack.ts:3065

Retrieves a Class instance by name. Results are cached for 15 minutes to improve performance.

Parameters

ParameterTypeDefault valueDescription
classNamestringundefinedThe name or ID of the class to retrieve
freshbooleanfalseIf true, bypasses the cache and fetches from database

Returns

Promise<Class>

The Class instance, or null if not found

Example

const taskClass = await stack.getClass('Task');
if (taskClass) {
const tasks = await taskClass.getCards();
}

Overrides

Stack.getClass

getClasses()

getClasses(conf): Promise<Class[]>;

Defined in: packages/client/src/core/stack.ts:3613

Parameters

ParameterType
conf{ filter?: string[]; search?: string; }
conf.filter?string[]
conf.search?string

Returns

Promise<Class[]>


getClassModel()

getClassModel(className): Promise<ClassModel>;

Defined in: packages/client/src/core/stack.ts:3515

Parameters

ParameterType
classNamestring

Returns

Promise<ClassModel>

Overrides

Stack.getClassModel

getClassModels()

getClassModels(conf): Promise<
| {
list: ClassModel[];
listener?: undefined;
}
| {
list: ClassModel[];
listener: Changes<{
}>;
}>;

Defined in: packages/client/src/core/stack.ts:3568

Parameters

ParameterType
conf{ filter?: string[]; listen?: boolean; search?: string; }
conf.filter?string[]
conf.listen?boolean
conf.search?string

Returns

Promise< | { list: ClassModel[]; listener?: undefined; } | { list: ClassModel[]; listener: Changes<{ }>; }>


getClassNames()

getClassNames(): Promise<string[]>;

Defined in: packages/client/src/core/stack.ts:994

Returns

Promise<string[]>


getClassSnapshot()

getClassSnapshot(className): Promise<Class>;

Defined in: packages/client/src/core/stack.ts:3117

Reads a class's current stored model without subscribing or caching it.

The counterpart to getClass for code that wants a schema rather than a live view: validation, encryption, and anything else that runs per write or per row. Two properties matter and pull in opposite directions in getClass:

  • It is always current. The cache is invalidated by a changes feed, which is asynchronous, so during a burst of schema writes - patch application, most obviously - the cached instance can still be the previous schema. Validating a document against that fails.
  • It does not subscribe. A Class built by Class.get watches its documents until closed, so building one per written document leaves live feeds behind and PouchDB eventually warns about the destroyed listeners they hold.

The returned instance emits no doc events and needs no close().

Parameters

ParameterTypeDescription
classNamestringThe name or ID of the class.

Returns

Promise<Class>

The class, or null if there is no model by that name.

Example

const classObj = await stack.getClassSnapshot(doc["~class"]);
classObj?.getEncryptedAttributes();

getConsumerSchemaVersion()

getConsumerSchemaVersion(): Promise<string>;

Defined in: packages/client/src/core/stack.ts:1419

The highest consumer patch version this device has applied, from the patch ledger - null when no consumer patch has ever applied (or they are all deferred, which for the schema gate is the same thing: the schema those patches install is not here yet). The sync layer folds this into what it publishes and compares, so consumer-schema skew between devices refuses at the gate instead of pulling documents this device's schema cannot describe (ADR-0040).

Returns

Promise<string>


getContentClassNames()

getContentClassNames(): Promise<{
classes: string[];
domains: string[];
}>;

Defined in: packages/client/src/core/stack.ts:999

Returns

Promise<{ classes: string[]; domains: string[]; }>


getDb()

getDb(): Database<{
}>;

Defined in: packages/client/src/core/stack.ts:460

Returns the stack's PouchDB database handle.

The handle is guarded: reads and ordinary writes behave exactly as PouchDB documents them, and put/post/remove/bulkDocs all run the stack's authoring path. The two routes that would skip it - bulkDocs with new_edits: false, and the _-prefixed adapter methods - throw StackWriteGuardError; replicating into a stack goes through sync instead.

Returns

Database<{ }>

The guarded PouchDB database


getDbInfo()

getDbInfo(): Promise<DatabaseInfo>;

Defined in: packages/client/src/core/stack.ts:556

Retrieves information about the database including document count and update sequence.

Returns

Promise<DatabaseInfo>

Database information object


getDbName()

getDbName(): string;

Defined in: packages/client/src/core/stack.ts:564

Returns the name of the underlying PouchDB database.

Returns

string

The database name string


getDocRevision()

getDocRevision(docId): Promise<string>;

Defined in: packages/client/src/core/stack.ts:3221

Parameters

ParameterType
docIdstring

Returns

Promise<string>


getDocument()

getDocument<T>(docId): Promise<ExistingDocument<T>>;

Defined in: packages/client/src/core/stack.ts:3206

Retrieves a single document by its ID.

Type Parameters

Type ParameterDescription
T extends DocumentThe expected document type

Parameters

ParameterTypeDescription
docIdstringThe document ID to retrieve

Returns

Promise<ExistingDocument<T>>

The document, or null if not found

Example

const task = await stack.getDocument<TaskDocument>('Task-123');
if (task) {
console.log(task.title);
}

getDomain()

getDomain(domainName, fresh): Promise<Domain>;

Defined in: packages/client/src/core/stack.ts:3137

Parameters

ParameterTypeDefault value
domainNamestringundefined
freshbooleanfalse

Returns

Promise<Domain>

Overrides

Stack.getDomain

getDomainModel()

getDomainModel(domainName): Promise<DomainModel>;

Defined in: packages/client/src/core/stack.ts:3548

Parameters

ParameterType
domainNamestring

Returns

Promise<DomainModel>

Overrides

Stack.getDomainModel

getDomainModels()

getDomainModels(conf): Promise<
| {
list: DomainModel[];
listener?: undefined;
}
| {
list: DomainModel[];
listener: Changes<{
}>;
}>;

Defined in: packages/client/src/core/stack.ts:3669

Parameters

ParameterType
conf{ filter?: string[]; listen?: boolean; search?: string; }
conf.filter?string[]
conf.listen?boolean
conf.search?string

Returns

Promise< | { list: DomainModel[]; listener?: undefined; } | { list: DomainModel[]; listener: Changes<{ }>; }>


getDomains()

getDomains(conf): Promise<Domain[]>;

Defined in: packages/client/src/core/stack.ts:3716

Parameters

ParameterType
conf{ filter?: string[]; search?: string; }
conf.filter?string[]
conf.search?string

Returns

Promise<Domain[]>


getEphemeralClassNames()

getEphemeralClassNames(): Promise<string[]>;

Defined in: packages/client/src/core/stack.ts:950

Returns

Promise<string[]>


getLastDocId()

getLastDocId(): Promise<number>;

Defined in: packages/client/src/core/stack.ts:2057

Returns

Promise<number>


getSyncHandle()

getSyncHandle(): StackSyncHandle;

Defined in: packages/client/src/core/stack.ts:524

Returns this stack's replication handle, or null if sync was never called.

Returns

StackSyncHandle


getSyncStatus()

getSyncStatus(): SyncStatus;

Defined in: packages/client/src/core/stack.ts:539

Returns where this stack's replication stands, or null if it has none.

Returns

SyncStatus

Example

const status = stack.getSyncStatus();
if (status?.lastConvergedAt) {
ui.setLabel(`Synced ${formatAgo(status.lastConvergedAt)}`);
}

getSystem()

getSystem(): Promise<SystemDoc>;

Defined in: packages/client/src/core/stack.ts:2072

Returns

Promise<SystemDoc>


importContent()

importContent(payload, options): Promise<ContentImportReport>;

Defined in: packages/client/src/core/stack.ts:1175

Imports content produced by exportContent into this stack.

The counterpart, and it is not symmetric: an export is a read, an import is a reconciliation. The payload carries data and no schema, so this stack's datamodel decides what is allowed in.

  • Reconciled against the datamodel. Every document's class must already exist here; a missing one is reported rather than invented, because the export carries no schema to create it from. Attributes the target class does not define are dropped by default.
  • Written through the authoring path, so schema validation, relation checks and triggers all run - and so encrypted attributes are encrypted under this stack's document key, not the one they were exported from.
  • Documents before relations, because a relation is rejected unless both ends already exist.

Not a transaction: a failure part way through leaves what was already written. The report says what landed.

Parameters

ParameterTypeDescription
payloadContentExportAn envelope from exportContent.
optionsContentImportOptionsHow to reconcile; see ContentImportOptions.

Returns

Promise<ContentImportReport>

What was written, skipped, and why.

Throws

Error when the payload is not a recognised export, or when a "fail" option is set and the condition it names occurs.

Example

const report = await stack.importContent(JSON.parse(await file.text()));
report.documents.written; // 128
report.issues; // [{ docId: "Task-9", kind: "missing-class", ... }]

incrementLastDocId()

incrementLastDocId(): Promise<number>;

Defined in: packages/client/src/core/stack.ts:3770

Returns

Promise<number>


initdb()

initdb(): Promise<ClientStack>;

Defined in: packages/client/src/core/stack.ts:2861

Returns

Promise<ClientStack>


initIndex()

initIndex(): Promise<void>;

Defined in: packages/client/src/core/stack.ts:3154

Returns

Promise<void>


invalidateWriteCaches()

invalidateWriteCaches(docs?): void;

Defined in: packages/client/src/core/stack.ts:2652

Evicts derived caches after documents were written.

Called synchronously by StackPlugin after every successful bulkDocs batch (which every local write funnels through, put/post/remove and replication included), and again by the shared changes feed for writes made outside this instance - another tab on the same database, most commonly. The write-path call is what makes a policy or schema write visible to the very next read: the changes feed delivers asynchronously, and a cache invalidated only by the feed would serve stale answers in that window.

Class-model writes clear the model and snapshot caches wholesale rather than by key - a rename leaves the old name keyed to a model that no longer answers to it, and class writes are rare enough that precision buys nothing.

Parameters

ParameterTypeDescription
docs?unknown[]The documents just written; omit to invalidate everything.

Returns

void


isCryptoEngineDisabled()

isCryptoEngineDisabled(): boolean;

Defined in: packages/client/src/core/stack.ts:572

Checks if the crypto engine was disabled during stack initialization.

Returns

boolean

true if encryption is disabled, false otherwise


isLocked()

isLocked(): boolean;

Defined in: packages/client/src/core/stack.ts:619

Whether the stack is operating without its document encryption key.

A locked stack reads everything that needs no key and refuses writes to classes carrying encrypted attributes, rather than storing them in the clear. Patches that would write encrypted data are deferred until unlock. Stacks with the crypto engine disabled are never locked - there is no key to be missing.

Returns

boolean

true when encryption is enabled but no key is held.


isScopeLocked()

isScopeLocked(scopeId): boolean;

Defined in: packages/client/src/core/stack.ts:728

Whether a declared scope's CEK is absent from the keyring - its content is sealed.

Parameters

ParameterType
scopeIdstring

Returns

boolean


isSimpleClass()

isSimpleClass(className): boolean;

Defined in: packages/client/src/core/stack.ts:925

Whether a class stores its documents as given.

See ClassModel.simple. Answers false for a class it has not heard of, which is the safe direction: an unknown class gets the full authoring path.

Parameters

ParameterTypeDescription
classNameunknownThe ~class of a document.

Returns

boolean


lockedScopeIds()

lockedScopeIds(): Promise<string[]>;

Defined in: packages/client/src/core/stack.ts:733

Declared scopes whose CEK the keyring lacks. Loaded scopes only - call after open.

Returns

Promise<string[]>


onClassDoc()

onClassDoc(className, metaKey): ChangesSubscription;

Defined in: packages/client/src/core/stack.ts:2561

Subscribes to changes on the documents of a class, or of a domain.

Returns a handle onto classDocFeed rather than a feed of its own, so the database carries one destroyed listener no matter how many are watched. Cancelling releases only this subscriber; the feed stops once the last one goes.

Prefer subscribeClassDocs / subscribeDomainDocs, which route changes through the decrypting preparation step (ADR-0020). Whichever is used, the handle must be handed to releaseListener when the watcher is done.

Parameters

ParameterTypeDefault valueDescription
classNamestringundefinedThe class or domain whose documents to watch.
metaKey"~class" | "~domain""~class"Which field names the owner: ~class (default) for a class's documents, ~domain for a domain's relation documents. Separate namespaces.

Returns

ChangesSubscription

A cancellable subscription handle.

Overrides

Stack.onClassDoc

onClassLock()

onClassLock(className): Changes<{
}>;

Defined in: packages/client/src/core/stack.ts:2416

Parameters

ParameterType
classNamestring

Returns

Changes<{ }>

Overrides

Stack.onClassLock

onClassModelChanges()

onClassModelChanges(): Changes<{
}>;

Defined in: packages/client/src/core/stack.ts:2386

Returns

Changes<{ }>

PouchDB.Core.Changes<{}>


onClassModelPropagationComplete()

onClassModelPropagationComplete(event): void;

Defined in: packages/client/src/core/stack.ts:2373

Parameters

ParameterTypeDescription
eventCustomEvent<ClassModelPropagationComplete>

Returns

void

Description

When a class model propagation comes to completion remove the corresponding ~lock from the database


onClassModelPropagationStart()

onClassModelPropagationStart(event): void;

Defined in: packages/client/src/core/stack.ts:2358

Parameters

ParameterTypeDescription
eventCustomEvent<ClassModelPropagationStart>

Returns

void

Description

When a class model propagation starts write the ~lock document to the database. It prevents any further modifications on the class data model

Overrides

Stack.onClassModelPropagationStart

prepareChangeDocument()

prepareChangeDocument(doc, classObj?): Promise<Document>;

Defined in: packages/client/src/core/stack.ts:2609

Prepares a document delivered by the changes feed for a listener.

The changes feed is the one read path that does not pass through StackPlugin: decryption lives in the bulkGet wrapper, which is what makes getCards and findDocuments transparent, while include_docs hands back exactly what is stored. Every read decrypted except the one that pushed, so a live view received an EncryptedPayload object where it had just rendered a string.

Parameters

ParameterTypeDescription
docDocumentThe document from change.doc.
classObj?ClassThe class, when known; without it encrypted values are still recognised by shape.

Returns

Promise<Document>

A copy safe to hand to a consumer. Never contains an EncryptedPayload.

Example

const doc = await stack.prepareChangeDocument(change.doc, classObj);
doc.ssn; // plaintext, or null when it cannot be opened

Overrides

Stack.prepareChangeDocument

prepareDoc()

Call Signature

prepareDoc(
_id,
type,
params,
metaKey): Document;

Defined in: packages/client/src/core/stack.ts:3964

Parameters
ParameterType
_idstring
typestring
params{ [key: string]: string | number | boolean; }
metaKey"~class"
Returns

Document

Call Signature

prepareDoc(
_id,
type,
params,
metaKey): RelationDocument;

Defined in: packages/client/src/core/stack.ts:3970

Parameters
ParameterType
_idstring
typestring
params{ [key: string]: string | number | boolean; }
metaKey"~domain"
Returns

RelationDocument


query()

query(sql, ...params): Promise<{
ast: (
| SelectAST
| UnionAST)[];
rows: any;
}>;

Defined in: packages/client/src/core/stack.ts:4493

Parameters

ParameterType
sqlstring
...paramsany[]

Returns

Promise<{ ast: ( | SelectAST | UnionAST)[]; rows: any; }>

Overrides

Stack.query

queryStream()

queryStream(sql, ...params): AsyncGenerator<{
[column: string]: any;
}, void, void>;

Defined in: packages/client/src/core/stack.ts:4556

Executes a SQL query as an async stream of rows.

The streaming counterpart to query: single-table plans without aggregation, DISTINCT, ORDER BY, or subqueries stream row by row on top of findDocumentsIterator - peak memory is one page regardless of result size, and a LIMIT stops the underlying scan early. More complex plans execute normally and yield from the materialized result, so the API is uniform. Row order on the streaming path is _id order.

Parameters

ParameterTypeDescription
sqlstringThe SQL SELECT statement.
...paramsany[]Values for ? placeholders.

Returns

AsyncGenerator<{ [column: string]: any; }, void, void>

Example

for await (const row of stack.queryStream("SELECT t.title FROM Task AS t WHERE t.done = FALSE;")) {
render(row);
}

refreshSimpleClasses()

refreshSimpleClasses(): Promise<string[]>;

Defined in: packages/client/src/core/stack.ts:934

Re-reads which classes are simple.

Returns

Promise<string[]>

The names, for callers that want them.


removeAllListeners()

removeAllListeners(): void;

Defined in: packages/client/src/core/stack.ts:2326

Returns

void

Description

Clears all listeners from the Stack

Overrides

Stack.removeAllListeners

reset()

reset(): Promise<ClientStack>;

Defined in: packages/client/src/core/stack.ts:3799

Returns

Promise<ClientStack>


resolveScopeLabel()

resolveScopeLabel(doc, classModel?): string;

Defined in: packages/client/src/core/stack.ts:714

The scope a document's write seals under: its own ~scope label, else its class's defaultScope (spec 02 §2.2 - the document's value wins).

Parameters

ParameterType
docunknown
classModel?{ defaultScope?: string; }
classModel.defaultScope?string

Returns

string


setAuthSession()

setAuthSession(proof): Promise<void>;

Defined in: packages/client/src/core/stack.ts:581

Sets the current authentication session. Called automatically by authenticate, but can be set manually for custom auth flows.

Parameters

ParameterTypeDescription
proofAuthSessionProofThe authentication session proof containing session and encryption keys

Returns

Promise<void>


setListeners()

setListeners(): void;

Defined in: packages/client/src/core/stack.ts:2281

Returns

void

Overrides

Stack.setListeners

sync()

sync(options): Promise<StackSyncHandle>;

Defined in: packages/client/src/core/stack.ts:513

Starts replicating this stack against a remote.

DocStack owns the lifecycle - the filter that keeps ~system, the crypto marker, design documents, locks, sessions and the patch ledger on this device; the schema gate that refuses a remote written by a newer build; the convergence state a UI renders; and cancellation when the stack closes. It owns nothing about the transport: the remote is whatever PouchDB database the caller hands over, so credentials and adapter configuration stay in the application.

Calling it again replaces the previous replication.

Parameters

ParameterTypeDescription
optionsStackSyncOptionsSee StackSyncOptions.

Returns

Promise<StackSyncHandle>

The handle, once replication is running.

Throws

When the remote was last written by a newer schema.

Example

const sync = await stack.sync({
remote: () => new PouchDB("workspace", { adapter: "googledrive", accessToken }),
direction: "both",
live: true,
retry: true,
});

sync.addEventListener("status", (event) => {
console.log((event as CustomEvent).detail.state);
});

unlock()

unlock(documentKey): Promise<ClientStack>;

Defined in: packages/client/src/core/stack.ts:644

Supplies the document encryption key to a locked stack.

The key is checked against the stack's canary before it is accepted, so passing the wrong one throws instead of quietly producing unreadable writes. On the first unlock of a stack that has none, the canary is minted from the key given - which is what makes every later open verifiable.

Unlocking resumes any bootstrap deferred while locked, then emits unlocked.

Parameters

ParameterTypeDescription
documentKeystringThe hex-encoded document key, from wherever the application provisions it.

Returns

Promise<ClientStack>

Throws

If the stack has encryption disabled, or the key does not match the canary.

Example

const stack = await ClientStack.create('db-app'); // opens locked
await stack.unlock(await myServer.fetchDocumentKey());
stack.isLocked(); // false

unlockScopes()

unlockScopes(attributeKey): Promise<{
locked: string[];
unlocked: string[];
}>;

Defined in: packages/client/src/core/stack.ts:746

Attempts every declared access scope with the session's attribute key (ADR-0045): the ABE decryption either yields a scope's CEK - verified against the scope's canary, then admitted to the keyring - or fails, and the scope stays locked. There is no gate to ask; this IS the access decision. Idempotent: a later call with better material unlocks more. Deferred patches that were waiting on a scope replay after.

Parameters

ParameterType
attributeKeystring

Returns

Promise<{ locked: string[]; unlocked: string[]; }>


updateClass()

updateClass(classObj): Promise<Document>;

Defined in: packages/client/src/core/stack.ts:3897

Parameters

ParameterType
classObjClass

Returns

Promise<Document>

Overrides

Stack.updateClass

buildAccessScope()

static buildAccessScope(input): Promise<AccessScopeModel & object>;

Defined in: packages/client/src/core/stack.ts:804

AUTHORITY-side helper: assembles a complete ~AccessScope document from a fresh (or supplied) CEK - ABE-sealing it under the policy, stamping the kid, minting the per-scope canary. Runs wherever the application controls (its server, an admin ceremony, tests); it needs the authority PUBLIC key only, never the master secret. The document is returned, not written - publishing it (and distributing attribute keys) is the consumer's act.

Parameters

ParameterTypeDescription
input{ cekHex?: string; pk: string; policyString: string; scopeId: string; version?: number; }-
input.cekHex?string32-byte CEK as hex; minted when absent.
input.pkstringThe authority public key (@docstack/abe setup().pk).
input.policyStringstring-
input.scopeIdstring-
input.version?number-

Returns

Promise<AccessScopeModel & object>


clear()

static clear(conn): Promise<unknown>;

Defined in: packages/client/src/core/stack.ts:3826

Parameters

ParameterType
connstring

Returns

Promise<unknown>


create()

static create(conn, options?): Promise<ClientStack>;

Defined in: packages/client/src/core/stack.ts:1351

Creates and initializes a new ClientStack instance. This is the primary way to instantiate a stack - the constructor is private.

Parameters

ParameterTypeDescription
connstringThe connection string or database name
options?StackOptionsOptional configuration including plugins, patches, and credentials

Returns

Promise<ClientStack>

A fully initialized ClientStack instance

Example

// Basic initialization
const stack = await ClientStack.create('my-app-db');

// With authentication
const stack = await ClientStack.create('my-app-db', {
credentials: { username: 'admin', password: 'secret' }
});

// With custom patches
const stack = await ClientStack.create('my-app-db', {
patches: [myCustomPatch]
});