@noy-db/hub 0.1.0-pre.3
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 +21 -0
- package/README.md +197 -0
- package/dist/aggregate/index.cjs +476 -0
- package/dist/aggregate/index.cjs.map +1 -0
- package/dist/aggregate/index.d.cts +38 -0
- package/dist/aggregate/index.d.ts +38 -0
- package/dist/aggregate/index.js +53 -0
- package/dist/aggregate/index.js.map +1 -0
- package/dist/blobs/index.cjs +1480 -0
- package/dist/blobs/index.cjs.map +1 -0
- package/dist/blobs/index.d.cts +45 -0
- package/dist/blobs/index.d.ts +45 -0
- package/dist/blobs/index.js +48 -0
- package/dist/blobs/index.js.map +1 -0
- package/dist/bundle/index.cjs +436 -0
- package/dist/bundle/index.cjs.map +1 -0
- package/dist/bundle/index.d.cts +7 -0
- package/dist/bundle/index.d.ts +7 -0
- package/dist/bundle/index.js +40 -0
- package/dist/bundle/index.js.map +1 -0
- package/dist/chunk-2QR2PQTT.js +217 -0
- package/dist/chunk-2QR2PQTT.js.map +1 -0
- package/dist/chunk-4OWFYIDQ.js +79 -0
- package/dist/chunk-4OWFYIDQ.js.map +1 -0
- package/dist/chunk-5AATM2M2.js +90 -0
- package/dist/chunk-5AATM2M2.js.map +1 -0
- package/dist/chunk-ACLDOTNQ.js +543 -0
- package/dist/chunk-ACLDOTNQ.js.map +1 -0
- package/dist/chunk-BTDCBVJW.js +160 -0
- package/dist/chunk-BTDCBVJW.js.map +1 -0
- package/dist/chunk-CIMZBAZB.js +72 -0
- package/dist/chunk-CIMZBAZB.js.map +1 -0
- package/dist/chunk-E445ICYI.js +365 -0
- package/dist/chunk-E445ICYI.js.map +1 -0
- package/dist/chunk-EXQRC2L4.js +722 -0
- package/dist/chunk-EXQRC2L4.js.map +1 -0
- package/dist/chunk-FZU343FL.js +32 -0
- package/dist/chunk-FZU343FL.js.map +1 -0
- package/dist/chunk-GJILMRPO.js +354 -0
- package/dist/chunk-GJILMRPO.js.map +1 -0
- package/dist/chunk-GOUT6DND.js +1285 -0
- package/dist/chunk-GOUT6DND.js.map +1 -0
- package/dist/chunk-J66GRPNH.js +111 -0
- package/dist/chunk-J66GRPNH.js.map +1 -0
- package/dist/chunk-M2F2JAWB.js +464 -0
- package/dist/chunk-M2F2JAWB.js.map +1 -0
- package/dist/chunk-M5INGEFC.js +84 -0
- package/dist/chunk-M5INGEFC.js.map +1 -0
- package/dist/chunk-M62XNWRA.js +72 -0
- package/dist/chunk-M62XNWRA.js.map +1 -0
- package/dist/chunk-MR4424N3.js +275 -0
- package/dist/chunk-MR4424N3.js.map +1 -0
- package/dist/chunk-NPC4LFV5.js +132 -0
- package/dist/chunk-NPC4LFV5.js.map +1 -0
- package/dist/chunk-NXFEYLVG.js +311 -0
- package/dist/chunk-NXFEYLVG.js.map +1 -0
- package/dist/chunk-R36SIKES.js +79 -0
- package/dist/chunk-R36SIKES.js.map +1 -0
- package/dist/chunk-TDR6T5CJ.js +381 -0
- package/dist/chunk-TDR6T5CJ.js.map +1 -0
- package/dist/chunk-UF3BUNQZ.js +1 -0
- package/dist/chunk-UF3BUNQZ.js.map +1 -0
- package/dist/chunk-UQFSPSWG.js +1109 -0
- package/dist/chunk-UQFSPSWG.js.map +1 -0
- package/dist/chunk-USKYUS74.js +793 -0
- package/dist/chunk-USKYUS74.js.map +1 -0
- package/dist/chunk-XCL3WP6J.js +121 -0
- package/dist/chunk-XCL3WP6J.js.map +1 -0
- package/dist/chunk-XHFOENR2.js +680 -0
- package/dist/chunk-XHFOENR2.js.map +1 -0
- package/dist/chunk-ZFKD4QMV.js +430 -0
- package/dist/chunk-ZFKD4QMV.js.map +1 -0
- package/dist/chunk-ZLMV3TUA.js +490 -0
- package/dist/chunk-ZLMV3TUA.js.map +1 -0
- package/dist/chunk-ZRG4V3F5.js +17 -0
- package/dist/chunk-ZRG4V3F5.js.map +1 -0
- package/dist/consent/index.cjs +204 -0
- package/dist/consent/index.cjs.map +1 -0
- package/dist/consent/index.d.cts +24 -0
- package/dist/consent/index.d.ts +24 -0
- package/dist/consent/index.js +23 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/crdt/index.cjs +152 -0
- package/dist/crdt/index.cjs.map +1 -0
- package/dist/crdt/index.d.cts +30 -0
- package/dist/crdt/index.d.ts +30 -0
- package/dist/crdt/index.js +24 -0
- package/dist/crdt/index.js.map +1 -0
- package/dist/crypto-IVKU7YTT.js +44 -0
- package/dist/crypto-IVKU7YTT.js.map +1 -0
- package/dist/delegation-XDJCBTI2.js +16 -0
- package/dist/delegation-XDJCBTI2.js.map +1 -0
- package/dist/dev-unlock-CeXic1xC.d.cts +263 -0
- package/dist/dev-unlock-KrKkcqD3.d.ts +263 -0
- package/dist/hash-9KO1BGxh.d.cts +63 -0
- package/dist/hash-ChfJjRjQ.d.ts +63 -0
- package/dist/history/index.cjs +1215 -0
- package/dist/history/index.cjs.map +1 -0
- package/dist/history/index.d.cts +62 -0
- package/dist/history/index.d.ts +62 -0
- package/dist/history/index.js +79 -0
- package/dist/history/index.js.map +1 -0
- package/dist/i18n/index.cjs +746 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.cts +38 -0
- package/dist/i18n/index.d.ts +38 -0
- package/dist/i18n/index.js +55 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index-BRHBCmLt.d.ts +1940 -0
- package/dist/index-C8kQtmOk.d.ts +380 -0
- package/dist/index-DN-J-5wT.d.cts +1940 -0
- package/dist/index-DhjMjz7L.d.cts +380 -0
- package/dist/index.cjs +14756 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +269 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.js +6085 -0
- package/dist/index.js.map +1 -0
- package/dist/indexing/index.cjs +736 -0
- package/dist/indexing/index.cjs.map +1 -0
- package/dist/indexing/index.d.cts +36 -0
- package/dist/indexing/index.d.ts +36 -0
- package/dist/indexing/index.js +77 -0
- package/dist/indexing/index.js.map +1 -0
- package/dist/lazy-builder-BwEoBQZ9.d.ts +304 -0
- package/dist/lazy-builder-CZVLKh0Z.d.cts +304 -0
- package/dist/ledger-2NX4L7PN.js +33 -0
- package/dist/ledger-2NX4L7PN.js.map +1 -0
- package/dist/mime-magic-CBBSOkjm.d.cts +50 -0
- package/dist/mime-magic-CBBSOkjm.d.ts +50 -0
- package/dist/periods/index.cjs +1035 -0
- package/dist/periods/index.cjs.map +1 -0
- package/dist/periods/index.d.cts +21 -0
- package/dist/periods/index.d.ts +21 -0
- package/dist/periods/index.js +25 -0
- package/dist/periods/index.js.map +1 -0
- package/dist/predicate-SBHmi6D0.d.cts +161 -0
- package/dist/predicate-SBHmi6D0.d.ts +161 -0
- package/dist/query/index.cjs +1957 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.cts +3 -0
- package/dist/query/index.d.ts +3 -0
- package/dist/query/index.js +62 -0
- package/dist/query/index.js.map +1 -0
- package/dist/session/index.cjs +487 -0
- package/dist/session/index.cjs.map +1 -0
- package/dist/session/index.d.cts +45 -0
- package/dist/session/index.d.ts +45 -0
- package/dist/session/index.js +44 -0
- package/dist/session/index.js.map +1 -0
- package/dist/shadow/index.cjs +133 -0
- package/dist/shadow/index.cjs.map +1 -0
- package/dist/shadow/index.d.cts +16 -0
- package/dist/shadow/index.d.ts +16 -0
- package/dist/shadow/index.js +20 -0
- package/dist/shadow/index.js.map +1 -0
- package/dist/store/index.cjs +1069 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +491 -0
- package/dist/store/index.d.ts +491 -0
- package/dist/store/index.js +34 -0
- package/dist/store/index.js.map +1 -0
- package/dist/strategy-BSxFXGzb.d.cts +110 -0
- package/dist/strategy-BSxFXGzb.d.ts +110 -0
- package/dist/strategy-D-SrOLCl.d.cts +548 -0
- package/dist/strategy-D-SrOLCl.d.ts +548 -0
- package/dist/sync/index.cjs +1062 -0
- package/dist/sync/index.cjs.map +1 -0
- package/dist/sync/index.d.cts +42 -0
- package/dist/sync/index.d.ts +42 -0
- package/dist/sync/index.js +28 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/team/index.cjs +1233 -0
- package/dist/team/index.cjs.map +1 -0
- package/dist/team/index.d.cts +117 -0
- package/dist/team/index.d.ts +117 -0
- package/dist/team/index.js +39 -0
- package/dist/team/index.js.map +1 -0
- package/dist/tx/index.cjs +212 -0
- package/dist/tx/index.cjs.map +1 -0
- package/dist/tx/index.d.cts +20 -0
- package/dist/tx/index.d.ts +20 -0
- package/dist/tx/index.js +20 -0
- package/dist/tx/index.js.map +1 -0
- package/dist/types-BZpCZB8N.d.ts +7526 -0
- package/dist/types-Bfs0qr5F.d.cts +7526 -0
- package/dist/ulid-COREQ2RQ.js +9 -0
- package/dist/ulid-COREQ2RQ.js.map +1 -0
- package/dist/util/index.cjs +230 -0
- package/dist/util/index.cjs.map +1 -0
- package/dist/util/index.d.cts +77 -0
- package/dist/util/index.d.ts +77 -0
- package/dist/util/index.js +190 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +244 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PERIODS_COLLECTION,
|
|
3
|
+
appendPeriodLedgerEntry,
|
|
4
|
+
assertTsWritable,
|
|
5
|
+
chainAnchor,
|
|
6
|
+
loadPeriods,
|
|
7
|
+
validatePeriodName,
|
|
8
|
+
withPeriods
|
|
9
|
+
} from "../chunk-5AATM2M2.js";
|
|
10
|
+
import "../chunk-UF3BUNQZ.js";
|
|
11
|
+
import "../chunk-XHFOENR2.js";
|
|
12
|
+
import "../chunk-CIMZBAZB.js";
|
|
13
|
+
import "../chunk-ZRG4V3F5.js";
|
|
14
|
+
import "../chunk-MR4424N3.js";
|
|
15
|
+
import "../chunk-ACLDOTNQ.js";
|
|
16
|
+
export {
|
|
17
|
+
PERIODS_COLLECTION,
|
|
18
|
+
appendPeriodLedgerEntry,
|
|
19
|
+
assertTsWritable,
|
|
20
|
+
chainAnchor,
|
|
21
|
+
loadPeriods,
|
|
22
|
+
validatePeriodName,
|
|
23
|
+
withPeriods
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary indexes for the query DSL.
|
|
3
|
+
*
|
|
4
|
+
* ships **in-memory hash indexes**:
|
|
5
|
+
* - Built during `Collection.ensureHydrated()` from the decrypted cache
|
|
6
|
+
* - Maintained incrementally on `put` and `delete`
|
|
7
|
+
* - Consulted by the query executor for `==` and `in` operators on
|
|
8
|
+
* indexed fields, falling back to a linear scan otherwise
|
|
9
|
+
* - Live entirely in memory — no adapter writes for the index itself
|
|
10
|
+
*
|
|
11
|
+
* Persistent encrypted index blobs (the spec's "store as a separate
|
|
12
|
+
* AES-256-GCM blob" note) are deferred to a follow-up issue. The reasons
|
|
13
|
+
* are documented in the PR body — short version: at the target
|
|
14
|
+
* scale of 1K–50K records, building the index during hydrate is free,
|
|
15
|
+
* so persistence buys nothing measurable.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Index declaration accepted by `Collection`'s constructor.
|
|
19
|
+
*
|
|
20
|
+
* Accepts:
|
|
21
|
+
* - `string` — a single-field hash index (`'clientId'`)
|
|
22
|
+
* - `{ fields: [...] }` or `readonly string[]` — a composite index
|
|
23
|
+
* over an ordered field tuple. Only lazy-mode
|
|
24
|
+
* collections consume composite declarations today; eager mode
|
|
25
|
+
* silently treats a composite as equivalent to declaring each
|
|
26
|
+
* component field as its own single-field index.
|
|
27
|
+
*
|
|
28
|
+
* Additive variants (unique constraints, partial indexes) will land as
|
|
29
|
+
* further union members without breaking existing declarations.
|
|
30
|
+
*/
|
|
31
|
+
type IndexDef = string | {
|
|
32
|
+
readonly fields: readonly string[];
|
|
33
|
+
} | readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Internal representation of a built hash index.
|
|
36
|
+
*
|
|
37
|
+
* Maps stringified field values to the set of record ids whose value
|
|
38
|
+
* for that field matches. Stringification keeps the index simple and
|
|
39
|
+
* works uniformly for primitives (`'open'`, `'42'`, `'true'`).
|
|
40
|
+
*
|
|
41
|
+
* Records whose indexed field is `undefined` or `null` are NOT inserted
|
|
42
|
+
* — `query().where('field', '==', undefined)` falls back to a linear
|
|
43
|
+
* scan, which is the conservative behavior.
|
|
44
|
+
*/
|
|
45
|
+
interface HashIndex {
|
|
46
|
+
readonly field: string;
|
|
47
|
+
readonly buckets: Map<string, Set<string>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Container for all indexes on a single collection.
|
|
51
|
+
*
|
|
52
|
+
* Methods are pure with respect to the in-memory `buckets` Map — they
|
|
53
|
+
* never touch the adapter or the keyring. The Collection class owns
|
|
54
|
+
* lifecycle (build on hydrate, maintain on put/delete).
|
|
55
|
+
*/
|
|
56
|
+
declare class CollectionIndexes {
|
|
57
|
+
private readonly indexes;
|
|
58
|
+
/**
|
|
59
|
+
* Declare an index. Subsequent record additions are tracked under it.
|
|
60
|
+
* Calling this twice for the same field is a no-op (idempotent).
|
|
61
|
+
*/
|
|
62
|
+
declare(field: string): void;
|
|
63
|
+
/** True if the given field has a declared index. */
|
|
64
|
+
has(field: string): boolean;
|
|
65
|
+
/** All declared field names, in declaration order. */
|
|
66
|
+
fields(): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Build all declared indexes from a snapshot of records.
|
|
69
|
+
* Called once per hydration. O(N × indexes.size).
|
|
70
|
+
*/
|
|
71
|
+
build<T>(records: ReadonlyArray<{
|
|
72
|
+
id: string;
|
|
73
|
+
record: T;
|
|
74
|
+
}>): void;
|
|
75
|
+
/**
|
|
76
|
+
* Insert or update a single record across all indexes.
|
|
77
|
+
* Called by `Collection.put()` after the encrypted write succeeds.
|
|
78
|
+
*
|
|
79
|
+
* If `previousRecord` is provided, the record is removed from any old
|
|
80
|
+
* buckets first — this is the update path. Pass `null` for fresh adds.
|
|
81
|
+
*/
|
|
82
|
+
upsert<T>(id: string, newRecord: T, previousRecord: T | null): void;
|
|
83
|
+
/**
|
|
84
|
+
* Remove a record from all indexes. Called by `Collection.delete()`
|
|
85
|
+
* (and as the first half of `upsert` for the update path).
|
|
86
|
+
*/
|
|
87
|
+
remove<T>(id: string, record: T): void;
|
|
88
|
+
/** Drop all index data. Called when the collection is invalidated. */
|
|
89
|
+
clear(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Equality lookup: return the set of record ids whose `field` matches
|
|
92
|
+
* the given value. Returns `null` if no index covers the field — the
|
|
93
|
+
* caller should fall back to a linear scan.
|
|
94
|
+
*
|
|
95
|
+
* The returned Set is a reference to the index's internal storage —
|
|
96
|
+
* callers must NOT mutate it.
|
|
97
|
+
*/
|
|
98
|
+
lookupEqual(field: string, value: unknown): ReadonlySet<string> | null;
|
|
99
|
+
/**
|
|
100
|
+
* Set lookup: return the union of record ids whose `field` matches any
|
|
101
|
+
* of the given values. Returns `null` if no index covers the field.
|
|
102
|
+
*/
|
|
103
|
+
lookupIn(field: string, values: readonly unknown[]): ReadonlySet<string> | null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Operator implementations for the query DSL.
|
|
108
|
+
*
|
|
109
|
+
* All predicates run client-side, AFTER decryption — they never see ciphertext.
|
|
110
|
+
* This file is dependency-free and tree-shakeable.
|
|
111
|
+
*/
|
|
112
|
+
/** Comparison operators supported by the where() builder. */
|
|
113
|
+
type Operator = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in' | 'contains' | 'startsWith' | 'between';
|
|
114
|
+
/**
|
|
115
|
+
* A single field comparison clause inside a query plan.
|
|
116
|
+
* Plans are JSON-serializable, so this type uses primitives only.
|
|
117
|
+
*/
|
|
118
|
+
interface FieldClause {
|
|
119
|
+
readonly type: 'field';
|
|
120
|
+
readonly field: string;
|
|
121
|
+
readonly op: Operator;
|
|
122
|
+
readonly value: unknown;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A user-supplied predicate function escape hatch. Not serializable.
|
|
126
|
+
*
|
|
127
|
+
* The predicate accepts `unknown` at the type level so the surrounding
|
|
128
|
+
* Clause type can stay non-parametric — this keeps Collection<T> covariant
|
|
129
|
+
* in T at the public API surface. Builder methods cast user predicates
|
|
130
|
+
* (typed `(record: T) => boolean`) into this shape on the way in.
|
|
131
|
+
*/
|
|
132
|
+
interface FilterClause {
|
|
133
|
+
readonly type: 'filter';
|
|
134
|
+
readonly fn: (record: unknown) => boolean;
|
|
135
|
+
}
|
|
136
|
+
/** A logical group of clauses combined by AND or OR. */
|
|
137
|
+
interface GroupClause {
|
|
138
|
+
readonly type: 'group';
|
|
139
|
+
readonly op: 'and' | 'or';
|
|
140
|
+
readonly clauses: readonly Clause[];
|
|
141
|
+
}
|
|
142
|
+
type Clause = FieldClause | FilterClause | GroupClause;
|
|
143
|
+
/**
|
|
144
|
+
* Read a possibly nested field path like "address.city" from a record.
|
|
145
|
+
* Returns undefined if any segment is missing.
|
|
146
|
+
*/
|
|
147
|
+
declare function readPath(record: unknown, path: string): unknown;
|
|
148
|
+
/**
|
|
149
|
+
* Evaluate a single field clause against a record.
|
|
150
|
+
* Returns false on type mismatches rather than throwing — query results
|
|
151
|
+
* exclude non-matching records by definition.
|
|
152
|
+
*/
|
|
153
|
+
declare function evaluateFieldClause(record: unknown, clause: FieldClause): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Evaluate any clause (field / filter / group) against a record.
|
|
156
|
+
* The recursion depth is bounded by the user's query expression — no risk of
|
|
157
|
+
* blowing the stack on a 50K-record collection.
|
|
158
|
+
*/
|
|
159
|
+
declare function evaluateClause(record: unknown, clause: Clause): boolean;
|
|
160
|
+
|
|
161
|
+
export { CollectionIndexes as C, type FieldClause as F, type GroupClause as G, type HashIndex as H, type IndexDef as I, type Operator as O, type Clause as a, type FilterClause as b, evaluateFieldClause as c, evaluateClause as e, readPath as r };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary indexes for the query DSL.
|
|
3
|
+
*
|
|
4
|
+
* ships **in-memory hash indexes**:
|
|
5
|
+
* - Built during `Collection.ensureHydrated()` from the decrypted cache
|
|
6
|
+
* - Maintained incrementally on `put` and `delete`
|
|
7
|
+
* - Consulted by the query executor for `==` and `in` operators on
|
|
8
|
+
* indexed fields, falling back to a linear scan otherwise
|
|
9
|
+
* - Live entirely in memory — no adapter writes for the index itself
|
|
10
|
+
*
|
|
11
|
+
* Persistent encrypted index blobs (the spec's "store as a separate
|
|
12
|
+
* AES-256-GCM blob" note) are deferred to a follow-up issue. The reasons
|
|
13
|
+
* are documented in the PR body — short version: at the target
|
|
14
|
+
* scale of 1K–50K records, building the index during hydrate is free,
|
|
15
|
+
* so persistence buys nothing measurable.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Index declaration accepted by `Collection`'s constructor.
|
|
19
|
+
*
|
|
20
|
+
* Accepts:
|
|
21
|
+
* - `string` — a single-field hash index (`'clientId'`)
|
|
22
|
+
* - `{ fields: [...] }` or `readonly string[]` — a composite index
|
|
23
|
+
* over an ordered field tuple. Only lazy-mode
|
|
24
|
+
* collections consume composite declarations today; eager mode
|
|
25
|
+
* silently treats a composite as equivalent to declaring each
|
|
26
|
+
* component field as its own single-field index.
|
|
27
|
+
*
|
|
28
|
+
* Additive variants (unique constraints, partial indexes) will land as
|
|
29
|
+
* further union members without breaking existing declarations.
|
|
30
|
+
*/
|
|
31
|
+
type IndexDef = string | {
|
|
32
|
+
readonly fields: readonly string[];
|
|
33
|
+
} | readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Internal representation of a built hash index.
|
|
36
|
+
*
|
|
37
|
+
* Maps stringified field values to the set of record ids whose value
|
|
38
|
+
* for that field matches. Stringification keeps the index simple and
|
|
39
|
+
* works uniformly for primitives (`'open'`, `'42'`, `'true'`).
|
|
40
|
+
*
|
|
41
|
+
* Records whose indexed field is `undefined` or `null` are NOT inserted
|
|
42
|
+
* — `query().where('field', '==', undefined)` falls back to a linear
|
|
43
|
+
* scan, which is the conservative behavior.
|
|
44
|
+
*/
|
|
45
|
+
interface HashIndex {
|
|
46
|
+
readonly field: string;
|
|
47
|
+
readonly buckets: Map<string, Set<string>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Container for all indexes on a single collection.
|
|
51
|
+
*
|
|
52
|
+
* Methods are pure with respect to the in-memory `buckets` Map — they
|
|
53
|
+
* never touch the adapter or the keyring. The Collection class owns
|
|
54
|
+
* lifecycle (build on hydrate, maintain on put/delete).
|
|
55
|
+
*/
|
|
56
|
+
declare class CollectionIndexes {
|
|
57
|
+
private readonly indexes;
|
|
58
|
+
/**
|
|
59
|
+
* Declare an index. Subsequent record additions are tracked under it.
|
|
60
|
+
* Calling this twice for the same field is a no-op (idempotent).
|
|
61
|
+
*/
|
|
62
|
+
declare(field: string): void;
|
|
63
|
+
/** True if the given field has a declared index. */
|
|
64
|
+
has(field: string): boolean;
|
|
65
|
+
/** All declared field names, in declaration order. */
|
|
66
|
+
fields(): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Build all declared indexes from a snapshot of records.
|
|
69
|
+
* Called once per hydration. O(N × indexes.size).
|
|
70
|
+
*/
|
|
71
|
+
build<T>(records: ReadonlyArray<{
|
|
72
|
+
id: string;
|
|
73
|
+
record: T;
|
|
74
|
+
}>): void;
|
|
75
|
+
/**
|
|
76
|
+
* Insert or update a single record across all indexes.
|
|
77
|
+
* Called by `Collection.put()` after the encrypted write succeeds.
|
|
78
|
+
*
|
|
79
|
+
* If `previousRecord` is provided, the record is removed from any old
|
|
80
|
+
* buckets first — this is the update path. Pass `null` for fresh adds.
|
|
81
|
+
*/
|
|
82
|
+
upsert<T>(id: string, newRecord: T, previousRecord: T | null): void;
|
|
83
|
+
/**
|
|
84
|
+
* Remove a record from all indexes. Called by `Collection.delete()`
|
|
85
|
+
* (and as the first half of `upsert` for the update path).
|
|
86
|
+
*/
|
|
87
|
+
remove<T>(id: string, record: T): void;
|
|
88
|
+
/** Drop all index data. Called when the collection is invalidated. */
|
|
89
|
+
clear(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Equality lookup: return the set of record ids whose `field` matches
|
|
92
|
+
* the given value. Returns `null` if no index covers the field — the
|
|
93
|
+
* caller should fall back to a linear scan.
|
|
94
|
+
*
|
|
95
|
+
* The returned Set is a reference to the index's internal storage —
|
|
96
|
+
* callers must NOT mutate it.
|
|
97
|
+
*/
|
|
98
|
+
lookupEqual(field: string, value: unknown): ReadonlySet<string> | null;
|
|
99
|
+
/**
|
|
100
|
+
* Set lookup: return the union of record ids whose `field` matches any
|
|
101
|
+
* of the given values. Returns `null` if no index covers the field.
|
|
102
|
+
*/
|
|
103
|
+
lookupIn(field: string, values: readonly unknown[]): ReadonlySet<string> | null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Operator implementations for the query DSL.
|
|
108
|
+
*
|
|
109
|
+
* All predicates run client-side, AFTER decryption — they never see ciphertext.
|
|
110
|
+
* This file is dependency-free and tree-shakeable.
|
|
111
|
+
*/
|
|
112
|
+
/** Comparison operators supported by the where() builder. */
|
|
113
|
+
type Operator = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in' | 'contains' | 'startsWith' | 'between';
|
|
114
|
+
/**
|
|
115
|
+
* A single field comparison clause inside a query plan.
|
|
116
|
+
* Plans are JSON-serializable, so this type uses primitives only.
|
|
117
|
+
*/
|
|
118
|
+
interface FieldClause {
|
|
119
|
+
readonly type: 'field';
|
|
120
|
+
readonly field: string;
|
|
121
|
+
readonly op: Operator;
|
|
122
|
+
readonly value: unknown;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A user-supplied predicate function escape hatch. Not serializable.
|
|
126
|
+
*
|
|
127
|
+
* The predicate accepts `unknown` at the type level so the surrounding
|
|
128
|
+
* Clause type can stay non-parametric — this keeps Collection<T> covariant
|
|
129
|
+
* in T at the public API surface. Builder methods cast user predicates
|
|
130
|
+
* (typed `(record: T) => boolean`) into this shape on the way in.
|
|
131
|
+
*/
|
|
132
|
+
interface FilterClause {
|
|
133
|
+
readonly type: 'filter';
|
|
134
|
+
readonly fn: (record: unknown) => boolean;
|
|
135
|
+
}
|
|
136
|
+
/** A logical group of clauses combined by AND or OR. */
|
|
137
|
+
interface GroupClause {
|
|
138
|
+
readonly type: 'group';
|
|
139
|
+
readonly op: 'and' | 'or';
|
|
140
|
+
readonly clauses: readonly Clause[];
|
|
141
|
+
}
|
|
142
|
+
type Clause = FieldClause | FilterClause | GroupClause;
|
|
143
|
+
/**
|
|
144
|
+
* Read a possibly nested field path like "address.city" from a record.
|
|
145
|
+
* Returns undefined if any segment is missing.
|
|
146
|
+
*/
|
|
147
|
+
declare function readPath(record: unknown, path: string): unknown;
|
|
148
|
+
/**
|
|
149
|
+
* Evaluate a single field clause against a record.
|
|
150
|
+
* Returns false on type mismatches rather than throwing — query results
|
|
151
|
+
* exclude non-matching records by definition.
|
|
152
|
+
*/
|
|
153
|
+
declare function evaluateFieldClause(record: unknown, clause: FieldClause): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Evaluate any clause (field / filter / group) against a record.
|
|
156
|
+
* The recursion depth is bounded by the user's query expression — no risk of
|
|
157
|
+
* blowing the stack on a 50K-record collection.
|
|
158
|
+
*/
|
|
159
|
+
declare function evaluateClause(record: unknown, clause: Clause): boolean;
|
|
160
|
+
|
|
161
|
+
export { CollectionIndexes as C, type FieldClause as F, type GroupClause as G, type HashIndex as H, type IndexDef as I, type Operator as O, type Clause as a, type FilterClause as b, evaluateFieldClause as c, evaluateClause as e, readPath as r };
|