@docstack/client 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -0
- package/README.md +1 -1
- package/lib/core/attribute.d.ts +11 -2
- package/lib/core/class.d.ts +40 -6
- package/lib/core/content-transfer.d.ts +178 -0
- package/lib/core/crypto-engine/index.d.ts +82 -1
- package/lib/core/crypto-engine/utils.d.ts +36 -1
- package/lib/core/datamodel/index.d.ts +16 -0
- package/lib/core/domain.d.ts +1 -1
- package/lib/core/guarded-db.d.ts +52 -0
- package/lib/core/index.d.ts +106 -1
- package/lib/core/policy-engine/index.d.ts +35 -0
- package/lib/core/query-engine/classes.d.ts +24 -0
- package/lib/core/query-engine/executor.d.ts +11 -0
- package/lib/core/query-engine/index.d.ts +2 -1
- package/lib/core/query-engine/planner.d.ts +19 -0
- package/lib/core/stack.d.ts +676 -7
- package/lib/core/sync/class-filter.d.ts +106 -0
- package/lib/core/sync/filter-identity.d.ts +53 -0
- package/lib/core/sync/index.d.ts +334 -0
- package/lib/core/sync/internal-docs.d.ts +159 -0
- package/lib/index.d.ts +27 -1
- package/lib/index.js +12686 -4
- package/lib/index.umd.js +5017 -620
- package/lib/plugins/pouchdb.d.ts +43 -3
- package/lib/utils/logger/index.d.ts +28 -4
- package/lib/utils/logger/transport.d.ts +52 -11
- package/package.json +18 -10
- package/lib/core/attribute.js +0 -406
- package/lib/core/attribute.js.map +0 -1
- package/lib/core/class.js +0 -761
- package/lib/core/class.js.map +0 -1
- package/lib/core/crypto-engine/index.js +0 -229
- package/lib/core/crypto-engine/index.js.map +0 -1
- package/lib/core/crypto-engine/utils.js +0 -88
- package/lib/core/crypto-engine/utils.js.map +0 -1
- package/lib/core/datamodel/index.js +0 -1308
- package/lib/core/datamodel/index.js.map +0 -1
- package/lib/core/domain.js +0 -423
- package/lib/core/domain.js.map +0 -1
- package/lib/core/index.js +0 -532
- package/lib/core/index.js.map +0 -1
- package/lib/core/job-engine/index.js +0 -220
- package/lib/core/job-engine/index.js.map +0 -1
- package/lib/core/policy-engine/index.js +0 -232
- package/lib/core/policy-engine/index.js.map +0 -1
- package/lib/core/query-engine/accumulators.js +0 -258
- package/lib/core/query-engine/accumulators.js.map +0 -1
- package/lib/core/query-engine/evaluator.js +0 -179
- package/lib/core/query-engine/evaluator.js.map +0 -1
- package/lib/core/query-engine/executor.js +0 -405
- package/lib/core/query-engine/executor.js.map +0 -1
- package/lib/core/query-engine/index.js +0 -4
- package/lib/core/query-engine/index.js.map +0 -1
- package/lib/core/query-engine/parser.js +0 -515
- package/lib/core/query-engine/parser.js.map +0 -1
- package/lib/core/query-engine/planner.js +0 -330
- package/lib/core/query-engine/planner.js.map +0 -1
- package/lib/core/stack.js +0 -1826
- package/lib/core/stack.js.map +0 -1
- package/lib/core/test-utils/docstack.js +0 -222
- package/lib/core/test-utils/docstack.js.map +0 -1
- package/lib/core/trigger/index.js +0 -81
- package/lib/core/trigger/index.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/plugins/pouchdb.js +0 -412
- package/lib/plugins/pouchdb.js.map +0 -1
- package/lib/utils/crypto/index.js +0 -34
- package/lib/utils/crypto/index.js.map +0 -1
- package/lib/utils/index.js +0 -58
- package/lib/utils/index.js.map +0 -1
- package/lib/utils/logger/index.js +0 -20
- package/lib/utils/logger/index.js.map +0 -1
- package/lib/utils/logger/transport.js +0 -28
- package/lib/utils/logger/transport.js.map +0 -1
- package/lib/workers/dataModel.js +0 -48
- package/lib/workers/dataModel.js.map +0 -1
package/lib/core/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { Trigger } from "./trigger/index.js";
|
|
|
5
5
|
import { JobEngine } from "./job-engine/index.js";
|
|
6
6
|
import Attribute from './attribute.js';
|
|
7
7
|
import { AttributeType, ClientCredentials, StackConfig } from "@docstack/shared";
|
|
8
|
+
import { DocStackSyncHandle } from './sync/index.js';
|
|
9
|
+
import type { DocStackSyncOptions } from './sync/index.js';
|
|
8
10
|
/**
|
|
9
11
|
* The main entry point for the DocStack client library.
|
|
10
12
|
*
|
|
@@ -43,8 +45,94 @@ declare class DocStack extends EventTarget {
|
|
|
43
45
|
private store;
|
|
44
46
|
/** Array of all initialized ClientStack instances. */
|
|
45
47
|
stacks: ClientStack[];
|
|
48
|
+
/**
|
|
49
|
+
* Stacks currently being opened, by name.
|
|
50
|
+
*
|
|
51
|
+
* `stacks` only gains an entry once `ClientStack.create` resolves, so it cannot answer
|
|
52
|
+
* "is this one already on its way?". Without that, {@link addStack}'s guard is a check
|
|
53
|
+
* followed by an `await` - not atomic - and two concurrent callers each open a handle
|
|
54
|
+
* on the same database. See ADR-0022.
|
|
55
|
+
*/
|
|
56
|
+
private pendingStacks;
|
|
57
|
+
/** The handle from the last {@link sync} call. */
|
|
58
|
+
private syncHandle?;
|
|
46
59
|
private logger;
|
|
47
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Splits a {@link StackConfig} into the connection string and the options a stack
|
|
62
|
+
* is created with.
|
|
63
|
+
*
|
|
64
|
+
* Everything the caller passed is forwarded - including `adapter` and any
|
|
65
|
+
* adapter-specific keys - so a stack can be opened on a transport other than the
|
|
66
|
+
* default one.
|
|
67
|
+
*
|
|
68
|
+
* @param config - A stack name, or a full configuration object.
|
|
69
|
+
* @returns The connection string and the resolved {@link StackOptions}.
|
|
70
|
+
*/
|
|
71
|
+
private static resolveStackConfig;
|
|
72
|
+
/**
|
|
73
|
+
* Opens a stack and adds it to this instance.
|
|
74
|
+
*
|
|
75
|
+
* Public because an application's set of databases is not always known at startup:
|
|
76
|
+
* a workspace joined at runtime needs its own database, and its own replication
|
|
77
|
+
* pair, without tearing down the stacks already open. Adding a stack that is
|
|
78
|
+
* already open returns the existing instance rather than opening a second handle
|
|
79
|
+
* on the same database.
|
|
80
|
+
*
|
|
81
|
+
* Dispatches `stack-added` with the new stack in `detail`.
|
|
82
|
+
*
|
|
83
|
+
* @param config - A stack name, or a full configuration object.
|
|
84
|
+
* @returns The stack, initialized and ready.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const stack = await docstack.addStack({ name: `ws-${workspace.slug}`, patches });
|
|
89
|
+
* await stack.sync({ remote: () => driveFor(workspace) });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
addStack: (config: StackConfig) => Promise<ClientStack>;
|
|
93
|
+
/**
|
|
94
|
+
* Closes a stack and drops it from this instance.
|
|
95
|
+
*
|
|
96
|
+
* Closing cancels the stack's replication and releases its listeners; the data on
|
|
97
|
+
* disk is left alone unless `destroy` is set, which is what leaving a workspace for
|
|
98
|
+
* good looks like.
|
|
99
|
+
*
|
|
100
|
+
* Dispatches `stack-removed` with the stack's name in `detail`.
|
|
101
|
+
*
|
|
102
|
+
* @param name - The stack name or connection string.
|
|
103
|
+
* @param options - `destroy: true` also deletes the underlying database.
|
|
104
|
+
* @returns `true` if a stack was removed, `false` if there was none by that name.
|
|
105
|
+
*/
|
|
106
|
+
removeStack: (name: string, options?: {
|
|
107
|
+
destroy?: boolean;
|
|
108
|
+
}) => Promise<boolean>;
|
|
109
|
+
/**
|
|
110
|
+
* Starts replication for every open stack against one transport.
|
|
111
|
+
*
|
|
112
|
+
* The `remote` resolver is called once per stack, which is what makes a
|
|
113
|
+
* database-per-workspace application a single call rather than a loop the
|
|
114
|
+
* application has to keep in step with its own stack list.
|
|
115
|
+
*
|
|
116
|
+
* @param options - See {@link DocStackSyncOptions}. `stacks` narrows it to a subset.
|
|
117
|
+
* @returns A handle holding every stack's replication.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const sync = await docstack.sync({
|
|
122
|
+
* remote: (stack) => new PouchDB(stack.name, { adapter: 'googledrive', accessToken }),
|
|
123
|
+
* });
|
|
124
|
+
* sync.addEventListener('status', () => render(sync.getStatus()));
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
sync: (options: DocStackSyncOptions) => Promise<DocStackSyncHandle>;
|
|
128
|
+
/**
|
|
129
|
+
* Returns the handle from the last {@link sync} call, or `null`.
|
|
130
|
+
*/
|
|
131
|
+
getSyncHandle: () => DocStackSyncHandle | null;
|
|
132
|
+
/**
|
|
133
|
+
* Stops replication on every stack.
|
|
134
|
+
*/
|
|
135
|
+
cancelSync: () => void;
|
|
48
136
|
private initStacks;
|
|
49
137
|
/**
|
|
50
138
|
* Resets all initialized stacks.
|
|
@@ -185,4 +273,21 @@ declare class DocStack extends EventTarget {
|
|
|
185
273
|
* @module @docstack/client
|
|
186
274
|
*/
|
|
187
275
|
export { ClientStack, Trigger, Class, Attribute, Domain, JobEngine };
|
|
276
|
+
export { StackSyncHandle, DocStackSyncHandle, SyncSchemaMismatchError, SYNC_META_DOC_ID, readRemoteSchemaVersion, publishSchemaVersion, createReplicationFilter, isInternalDoc, resolveInternalClasses, createClassFilter, hasClassRules, DATA_MODEL_CLASSES, withFilterIdentity, describeFilter, INTERNAL_DOC_IDS, INTERNAL_DOC_ID_PREFIXES, INTERNAL_DOC_CLASSES, OPTIONAL_INTERNAL_DOC_CLASSES, } from "./sync/index.js";
|
|
277
|
+
export type { SyncDirection, SyncState, SyncStatus, StackSyncOptions, DocStackSyncOptions, RemoteResolver, SyncMetaDoc, InternalDocFilterOptions, ClassFilterOptions, } from "./sync/index.js";
|
|
278
|
+
export type { ClassBuildOptions } from "./class.js";
|
|
279
|
+
export { CONTENT_EXPORT_FORMAT, META_CLASSES, isContentClassName, isContentDocument, isContentRelation, stripTransientFields, } from "./content-transfer.js";
|
|
280
|
+
export type { ContentExport, ContentExportOptions, ContentImportOptions, ContentImportReport, ContentImportIssue, } from "./content-transfer.js";
|
|
281
|
+
export { SYSTEM_SEEDED_DOC_IDS } from "./datamodel/index.js";
|
|
282
|
+
export { collectQueryClasses } from "./query-engine/index.js";
|
|
283
|
+
export { StackWriteGuardError } from "./guarded-db.js";
|
|
284
|
+
export { StackLockedError } from "../plugins/pouchdb.js";
|
|
285
|
+
/**
|
|
286
|
+
* Key-identity helpers, for applications that re-key a database.
|
|
287
|
+
*
|
|
288
|
+
* `deriveKeyId` names a key the same way stored payloads do, so an application can tell
|
|
289
|
+
* which of its keys a field belongs to without holding any of them.
|
|
290
|
+
*/
|
|
291
|
+
export { deriveKeyId, isEncryptedPayload } from "./crypto-engine/index.js";
|
|
292
|
+
export type { EncryptedPayload } from "./crypto-engine/index.js";
|
|
188
293
|
export { DocStack };
|
|
@@ -28,11 +28,35 @@ export type PolicyOperation = "read" | "write";
|
|
|
28
28
|
export declare class PolicyEngine {
|
|
29
29
|
/** Reference to the parent stack for database and session access. */
|
|
30
30
|
private readonly stack;
|
|
31
|
+
/**
|
|
32
|
+
* Every `~Policy` document, loaded once and reused across evaluations.
|
|
33
|
+
*
|
|
34
|
+
* `isReadableDocument` runs once per document a read returns, and it used to re-fetch
|
|
35
|
+
* the policy list from the database each time - the dominant cost of every read path.
|
|
36
|
+
* The list is invalidated on any write that touches a `~Policy` document: the write
|
|
37
|
+
* path calls {@link invalidatePolicyCache} synchronously (see StackPlugin), and the
|
|
38
|
+
* stack's shared changes feed calls it again for out-of-band writes such as another
|
|
39
|
+
* tab's. `null` means not loaded.
|
|
40
|
+
*/
|
|
41
|
+
private allPoliciesCache;
|
|
42
|
+
/**
|
|
43
|
+
* Compiled policy rules, keyed by their source text.
|
|
44
|
+
*
|
|
45
|
+
* A rule is evaluated once per policy per document, and `new Function` is a full
|
|
46
|
+
* compile each time. The source text is the key - not the policy id - so two policies
|
|
47
|
+
* sharing a rule share the compilation, and an edited rule is simply a new key.
|
|
48
|
+
*/
|
|
49
|
+
private compiledRules;
|
|
31
50
|
/**
|
|
32
51
|
* Creates a new PolicyEngine instance.
|
|
33
52
|
* @param stack - The parent ClientStack instance
|
|
34
53
|
*/
|
|
35
54
|
constructor(stack: ClientStack);
|
|
55
|
+
/**
|
|
56
|
+
* Drops the cached policy list so the next evaluation re-reads it.
|
|
57
|
+
* Called by the write path and the changes feed whenever a `~Policy` document lands.
|
|
58
|
+
*/
|
|
59
|
+
invalidatePolicyCache(): void;
|
|
36
60
|
/**
|
|
37
61
|
* Gets the current authentication session proof.
|
|
38
62
|
* @returns The session proof, or undefined if not authenticated
|
|
@@ -61,6 +85,17 @@ export declare class PolicyEngine {
|
|
|
61
85
|
private evaluateRule;
|
|
62
86
|
private filterPoliciesForSession;
|
|
63
87
|
private authorize;
|
|
88
|
+
/**
|
|
89
|
+
* Whether any policy applies to a class - i.e. whether reads of it are filtered.
|
|
90
|
+
*
|
|
91
|
+
* Lets the query engine know when a database-level LIMIT is safe: with no
|
|
92
|
+
* applicable policies, no row fetched within the limit can be dropped afterwards.
|
|
93
|
+
* Cheap once the policy list is cached.
|
|
94
|
+
*
|
|
95
|
+
* @param targetClass - The class name or id.
|
|
96
|
+
* @returns `true` if at least one policy targets the class.
|
|
97
|
+
*/
|
|
98
|
+
hasPoliciesFor(targetClass: string): Promise<boolean>;
|
|
64
99
|
/**
|
|
65
100
|
* Ensures write access is allowed for a document of the given class.
|
|
66
101
|
* Throws an error if no policy permits the write operation.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SelectAST, UnionAST } from "@docstack/shared";
|
|
2
|
+
/**
|
|
3
|
+
* Collects the class names a parsed query reads.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately generic: rather than enumerating the node types that can hold a nested
|
|
6
|
+
* query - `scalar_subquery`, `exists_expr`, the right-hand side of `IN` - it walks the
|
|
7
|
+
* whole graph and harvests every `select` node it finds. A new subquery form is then
|
|
8
|
+
* covered on the day it is added, rather than on the day someone notices a stale list.
|
|
9
|
+
*
|
|
10
|
+
* @param ast - The `ast` from {@link ClientStack.query}.
|
|
11
|
+
* @returns The class names read, or `null` when the query reads a source that is not a
|
|
12
|
+
* named class. That distinction is the contract: `[]` means *this query reads nothing* -
|
|
13
|
+
* `SELECT 1` - and it is safe to subscribe to nothing; `null` means *I cannot account for
|
|
14
|
+
* this*, and the caller should fail open rather than watch an incomplete set.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const { rows, ast } = await stack.query("SELECT * FROM Task JOIN Project ON …");
|
|
19
|
+
* collectQueryClasses(ast); // ["Task", "Project"]
|
|
20
|
+
* collectQueryClasses([]); // [] - reads nothing
|
|
21
|
+
* collectQueryClasses(null); // null - unknown, fail open
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const collectQueryClasses: (ast: (SelectAST | UnionAST)[] | null | undefined) => string[] | null;
|
|
@@ -12,3 +12,14 @@ import ClientStack from '../stack.js';
|
|
|
12
12
|
* @throws {Error} If a table specified in the query is not found.
|
|
13
13
|
*/
|
|
14
14
|
export declare function executePlan(stack: ClientStack, plan: any, params: any, outerRow?: any, outerAliases?: any): Promise<any>;
|
|
15
|
+
/**
|
|
16
|
+
* Executes a plan as an async stream of rows.
|
|
17
|
+
*
|
|
18
|
+
* A single-table plan with no aggregation, DISTINCT, ORDER BY, or subqueries streams
|
|
19
|
+
* for real: rows ride `findDocumentsIterator`'s keyset pages, get the WHERE re-check
|
|
20
|
+
* and projection per row, and honor OFFSET/LIMIT by counting - peak memory is one
|
|
21
|
+
* batch regardless of result size, and a LIMIT stops the underlying scan early. Any
|
|
22
|
+
* other plan falls back to `executePlan` and yields from the materialized result, so
|
|
23
|
+
* the API is uniform even where streaming isn't.
|
|
24
|
+
*/
|
|
25
|
+
export declare function executePlanStream(stack: ClientStack, plan: any, params: any): AsyncGenerator<any, void, unknown>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { parse } from "./parser.js";
|
|
2
2
|
export type { SelectAST, UnionAST } from "@docstack/shared";
|
|
3
3
|
export { createPlan } from "./planner.js";
|
|
4
|
-
export { executePlan } from "./executor.js";
|
|
4
|
+
export { executePlan, executePlanStream } from "./executor.js";
|
|
5
|
+
export { collectQueryClasses } from "./classes.js";
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a column name is safe to hand to Mango - as a selector key, a sort field,
|
|
3
|
+
* or a projection field. See {@link PUSHABLE_COLUMN_RE} and {@link UNPUSHABLE_COLUMNS}.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isPushableColumnName(name: any): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a predicate is a simple binary expression that can be converted
|
|
8
|
+
* directly into a Mango selector by `buildSelector`.
|
|
9
|
+
*
|
|
10
|
+
* This is the single authority on pushability - `buildSelector` imports it and refuses
|
|
11
|
+
* predicates that fail it, so the planner and the executor cannot drift apart and a
|
|
12
|
+
* predicate classified as pushed can never be silently dropped.
|
|
13
|
+
*
|
|
14
|
+
* @param {object} predicate The predicate AST node.
|
|
15
|
+
* @returns {boolean} True if the predicate is simple enough for pushdown.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isPushablePredicate(predicate: any): boolean;
|
|
1
18
|
/**
|
|
2
19
|
* Creates a logical query plan from a list of Abstract Syntax Trees (ASTs) for a UNION query.
|
|
3
20
|
* @param {Array<object>} astList The list of ASTs generated by the parser.
|
|
@@ -18,10 +35,12 @@ export declare function createPlan(astList: any): {
|
|
|
18
35
|
aggregation: any;
|
|
19
36
|
orderBy: any;
|
|
20
37
|
limit: any;
|
|
38
|
+
offset: any;
|
|
21
39
|
} | {
|
|
22
40
|
type: string;
|
|
23
41
|
selectPlans: any[];
|
|
24
42
|
unionOps: any[];
|
|
25
43
|
orderBy: any;
|
|
26
44
|
limit: any;
|
|
45
|
+
offset: any;
|
|
27
46
|
};
|