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
| Property | Type | Default value | Description | Overrides | Inherited from | Defined in |
|---|---|---|---|---|---|---|
appVersion | string | "0.0.1" | The current application version string. | Stack.appVersion | - | packages/client/src/core/stack.ts:230 |
authSession? | AuthSessionProof | undefined | The current authenticated user session, if any. Contains session details, derived key, and document encryption key. | Stack.authSession | - | packages/client/src/core/stack.ts:357 |
cache | object | undefined | In-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 |
connection | string | undefined | The connection string used to create this stack. | Stack.connection | - | packages/client/src/core/stack.ts:226 |
cryptoEngine | CryptoEngine | undefined | Engine for field-level encryption and decryption. Handles key derivation (PBKDF2) and AES-GCM encryption. | - | - | packages/client/src/core/stack.ts:346 |
db | Database<{ }> | undefined | The 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 |
jobEngine | JobEngine | undefined | Engine 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 |
jobScheduler | JobScheduler | undefined | Decides 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 |
lastDocId | number | undefined | - | Stack.lastDocId | - | packages/client/src/core/stack.ts:224 |
listeners | ChangesSubscription[] | [] | Every live changes subscription this stack has handed out; released on close. | Stack.listeners | - | packages/client/src/core/stack.ts:272 |
modelWorker | Worker | null | - | Stack.modelWorker | - | packages/client/src/core/stack.ts:296 |
name | string | undefined | The unique name identifier for this stack instance, derived from the connection string. | Stack.name | - | packages/client/src/core/stack.ts:222 |
options? | StackOptions | undefined | Configuration options provided during stack creation. | Stack.options | - | packages/client/src/core/stack.ts:228 |
patchCount | number | undefined | - | Stack.patchCount | - | packages/client/src/core/stack.ts:238 |
releaseListener | (listener?) => void | undefined | Cancels 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.releaseListener | packages/shared/lib/utils/stack/index.d.ts:115 |
schemaVersion | string | undefined | - | Stack.schemaVersion | - | packages/client/src/core/stack.ts:352 |
subscribeClassDocs | (className, target, classObj?) => ChangesSubscription | undefined | Subscribes 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.subscribeClassDocs | packages/shared/lib/utils/stack/index.d.ts:80 |
subscribeDomainDocs | (domainName, target) => ChangesSubscription | undefined | Subscribes 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.subscribeDomainDocs | packages/shared/lib/utils/stack/index.d.ts:93 |
transactionEngine | TransactionEngine | undefined | Named 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
| Parameter | Type |
|---|---|
classObj | Class |
Returns
Promise<ClassModel>
Overrides
Stack.addClass
addClassLock()
addClassLock(className): Promise<boolean>;
Defined in: packages/client/src/core/stack.ts:2429
Parameters
| Parameter | Type |
|---|---|
className | string |
Returns
Promise<boolean>
Overrides
Stack.addClassLock
addDesignDocumentPKs()
addDesignDocumentPKs(
className,
pKs,
temp): Promise<string>;
Defined in: packages/client/src/core/stack.ts:3904
Parameters
| Parameter | Type | Default value |
|---|---|---|
className | string | undefined |
pKs | string[] | undefined |
temp | boolean | false |
Returns
Promise<string>
Overrides
Stack.addDesignDocumentPKs
addDomain()
addDomain(domainObj): Promise<DomainModel>;
Defined in: packages/client/src/core/stack.ts:3882
Parameters
| Parameter | Type |
|---|---|
domainObj | Domain |
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
| Parameter | Type |
|---|---|
count | number |
Returns
Promise<number>
applyPatch()
applyPatch(patch): Promise<string>;
Defined in: packages/client/src/core/stack.ts:2160
Parameters
| Parameter | Type |
|---|---|
patch | Patch |
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:
- Looks up the user by username
- Executes the configured authentication job (e.g., password verification)
- Creates a new session document
- Sets up encryption keys for the session
Parameters
| Parameter | Type | Description |
|---|---|---|
credentials | ClientCredentials | The 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
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
| Parameter | Type | Description |
|---|---|---|
className | string | The 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
| Parameter | Type | Description |
|---|---|---|
options | { olderThanMs?: number; } | - |
options.olderThanMs? | number | Idle 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
| Parameter | Type |
|---|---|
className | string |
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
| Parameter | Type |
|---|---|
t | string | 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
| Parameter | Type | Description |
|---|---|---|
docId | string | The document ID, or null to auto-generate |
type | string | The 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
| Parameter | Type | Description |
|---|---|---|
docs | object[] | Array of document specifications with optional docId and params |
type | string | The 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
| Parameter | Type | Description |
|---|---|---|
docId | string | The relation document ID, or null to auto-generate |
relationName | string | A descriptive name for this relation instance |
domainObj | Domain | The Domain defining the relationship type |
params | { sourceClass: string; sourceId: string; targetClass: string; targetId: string; } | The relation parameters including source and target references |
params.sourceClass | string | - |
params.sourceId | string | - |
params.targetClass | string | - |
params.targetId | string | - |
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
| Parameter | Type | Description |
|---|---|---|
docs | object[] | Array of relation specifications |
relationName | string | A descriptive name for these relations |
domainObj | Domain | The 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
| Parameter | Type | Description |
|---|---|---|
_id | string |
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
| Parameter | Type |
|---|---|
t | string | 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
| Parameter | Type | Description |
|---|---|---|
field | string | The 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
nullwhere values should be (seeallowLossyWhenLocked). - 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
| Parameter | Type | Description |
|---|---|---|
options | ContentExportOptions | Which 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 Parameter | Default type | Description |
|---|---|---|
T extends | Document | RelationDocument | Document | The expected document type |
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
selector | any | undefined | A PouchDB/Mango query selector |
fields | any | undefined | Optional list of fields to return |
skip | any | undefined | Number of documents to skip |
limit | any | undefined | Maximum 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 Parameter | Default type | Description |
|---|---|---|
T extends | Document | RelationDocument | Document | The expected document type |
Parameters
| Parameter | Type | Description |
|---|---|---|
selector | { [key: string]: any; } | A PouchDB/Mango query selector |
fields? | string[] | Optional list of fields to return |
skip? | number | Number of documents to skip (for pagination) |
limit? | number | Maximum 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 Parameter | Default type |
|---|---|
T extends | Document | RelationDocument | Document |
Parameters
| Parameter | Type | Description |
|---|---|---|
selector | { [key: string]: any; } | A Mango selector; active: true is injected unless present. |
options | { batchSize?: number; fields?: string[]; } | - |
options.batchSize? | number | Documents 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
| Parameter | Type | Description |
|---|---|---|
type | string | The 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
| Parameter | Type |
|---|---|
scopeId | string |
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
| Parameter | Type | Default value | Description |
|---|---|---|---|
className | string | undefined | The name or ID of the class to retrieve |
fresh | boolean | false | If 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
| Parameter | Type |
|---|---|
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
| Parameter | Type |
|---|---|
className | string |
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
| Parameter | Type |
|---|---|
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
destroyedlisteners they hold.
The returned instance emits no doc events and needs no close().
Parameters
| Parameter | Type | Description |
|---|---|---|
className | string | The 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
| Parameter | Type |
|---|---|
docId | string |
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 Parameter | Description |
|---|---|
T extends Document | The expected document type |
Parameters
| Parameter | Type | Description |
|---|---|---|
docId | string | The 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
| Parameter | Type | Default value |
|---|---|---|
domainName | string | undefined |
fresh | boolean | false |
Returns
Promise<Domain>
Overrides
Stack.getDomain
getDomainModel()
getDomainModel(domainName): Promise<DomainModel>;
Defined in: packages/client/src/core/stack.ts:3548
Parameters
| Parameter | Type |
|---|---|
domainName | string |
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
| Parameter | Type |
|---|---|
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
| Parameter | Type |
|---|---|
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
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
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
| Parameter | Type | Description |
|---|---|---|
payload | ContentExport | An envelope from exportContent. |
options | ContentImportOptions | How 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
| Parameter | Type | Description |
|---|---|---|
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
| Parameter | Type |
|---|---|
scopeId | string |
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
| Parameter | Type | Description |
|---|---|---|
className | unknown | The ~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
| Parameter | Type | Default value | Description |
|---|---|---|---|
className | string | undefined | The 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
| Parameter | Type |
|---|---|
className | string |
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
| Parameter | Type | Description |
|---|---|---|
event | CustomEvent<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
| Parameter | Type | Description |
|---|---|---|
event | CustomEvent<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
| Parameter | Type | Description |
|---|---|---|
doc | Document | The document from change.doc. |
classObj? | Class | The 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
| Parameter | Type |
|---|---|
_id | string |
type | string |
params | { [key: string]: string | number | boolean; } |
metaKey | "~class" |
Returns
Call Signature
prepareDoc(
_id,
type,
params,
metaKey): RelationDocument;
Defined in: packages/client/src/core/stack.ts:3970
Parameters
| Parameter | Type |
|---|---|
_id | string |
type | string |
params | { [key: string]: string | number | boolean; } |
metaKey | "~domain" |
Returns
query()
query(sql, ...params): Promise<{
ast: (
| SelectAST
| UnionAST)[];
rows: any;
}>;
Defined in: packages/client/src/core/stack.ts:4493
Parameters
| Parameter | Type |
|---|---|
sql | string |
...params | any[] |
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
| Parameter | Type | Description |
|---|---|---|
sql | string | The SQL SELECT statement. |
...params | any[] | 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
| Parameter | Type |
|---|---|
doc | unknown |
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
| Parameter | Type | Description |
|---|---|---|
proof | AuthSessionProof | The 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
| Parameter | Type | Description |
|---|---|---|
options | StackSyncOptions | See 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
| Parameter | Type | Description |
|---|---|---|
documentKey | string | The 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
| Parameter | Type |
|---|---|
attributeKey | string |
Returns
Promise<{
locked: string[];
unlocked: string[];
}>
updateClass()
updateClass(classObj): Promise<Document>;
Defined in: packages/client/src/core/stack.ts:3897
Parameters
| Parameter | Type |
|---|---|
classObj | Class |
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
| Parameter | Type | Description |
|---|---|---|
input | { cekHex?: string; pk: string; policyString: string; scopeId: string; version?: number; } | - |
input.cekHex? | string | 32-byte CEK as hex; minted when absent. |
input.pk | string | The authority public key (@docstack/abe setup().pk). |
input.policyString | string | - |
input.scopeId | string | - |
input.version? | number | - |
Returns
Promise<AccessScopeModel & object>
clear()
static clear(conn): Promise<unknown>;
Defined in: packages/client/src/core/stack.ts:3826
Parameters
| Parameter | Type |
|---|---|
conn | string |
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
| Parameter | Type | Description |
|---|---|---|
conn | string | The connection string or database name |
options? | StackOptions | Optional 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]
});