@noy-db/hub 0.7.0-pre.11 → 0.7.0-pre.13
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/CHANGELOG.md +142 -0
- package/dist/as/index.js +3 -2
- package/dist/as/index.js.map +1 -1
- package/dist/{backup-VASRVLDZ.js → backup-R6A4VZRX.js} +4 -4
- package/dist/cargo/index.js +2 -2
- package/dist/chunk-66TQOPFM.js +37 -0
- package/dist/chunk-66TQOPFM.js.map +1 -0
- package/dist/{chunk-OEA5NVSU.js → chunk-AJQY44CE.js} +86 -6
- package/dist/chunk-AJQY44CE.js.map +1 -0
- package/dist/{chunk-S5AMPTNE.js → chunk-BMELYFHU.js} +2 -2
- package/dist/{chunk-T6PSWLAR.js → chunk-BUE2D7PX.js} +25 -2
- package/dist/chunk-BUE2D7PX.js.map +1 -0
- package/dist/chunk-BWXGSSFX.js +1589 -0
- package/dist/chunk-BWXGSSFX.js.map +1 -0
- package/dist/{chunk-HN4FNA4X.js → chunk-COTPDJR5.js} +8 -8
- package/dist/{chunk-E3EBMC4G.js → chunk-E2MYJGSG.js} +2 -2
- package/dist/{chunk-JJR2JIN4.js → chunk-FFBYRWDJ.js} +2 -2
- package/dist/{chunk-WQF3QABE.js → chunk-IHFXR2VQ.js} +9 -79
- package/dist/chunk-IHFXR2VQ.js.map +1 -0
- package/dist/chunk-MGOXJIBV.js +176 -0
- package/dist/chunk-MGOXJIBV.js.map +1 -0
- package/dist/chunk-PDD75NUZ.js +87 -0
- package/dist/chunk-PDD75NUZ.js.map +1 -0
- package/dist/chunk-RTBJ5IOO.js +87 -0
- package/dist/chunk-RTBJ5IOO.js.map +1 -0
- package/dist/chunk-RXLYKVTF.js +17 -0
- package/dist/chunk-RXLYKVTF.js.map +1 -0
- package/dist/{chunk-ESVREVWN.js → chunk-SXCYYMVS.js} +2 -2
- package/dist/chunk-TFMM5DBB.js +20 -0
- package/dist/chunk-TFMM5DBB.js.map +1 -0
- package/dist/chunk-TPHAGLUH.js +30 -0
- package/dist/chunk-TPHAGLUH.js.map +1 -0
- package/dist/{chunk-THNJ3IKU.js → chunk-ULUMAZ5S.js} +2 -2
- package/dist/{chunk-NO272T7Q.js → chunk-V4PXLYWM.js} +2 -31
- package/dist/chunk-V4PXLYWM.js.map +1 -0
- package/dist/{chunk-A5WZEPXJ.js → chunk-VGICZPVE.js} +5 -3
- package/dist/{chunk-A5WZEPXJ.js.map → chunk-VGICZPVE.js.map} +1 -1
- package/dist/{chunk-KHKKD62N.js → chunk-VYYM3MEN.js} +2 -16
- package/dist/chunk-VYYM3MEN.js.map +1 -0
- package/dist/{chunk-SWVNZQVR.js → chunk-YPXNAP72.js} +2 -169
- package/dist/chunk-YPXNAP72.js.map +1 -0
- package/dist/{chunk-I6DGKPTY.js → chunk-ZVCC36XQ.js} +101 -1273
- package/dist/chunk-ZVCC36XQ.js.map +1 -0
- package/dist/{collection-facade-MGGHQ3HU.js → collection-facade-4ACKIDDY.js} +4 -3
- package/dist/derivations/index.js +21 -6
- package/dist/dispatch-MSTR5VBI.js +255 -0
- package/dist/dispatch-MSTR5VBI.js.map +1 -0
- package/dist/{dispatch-44J4TL72.js → dispatch-PDP5TA73.js} +5 -5
- package/dist/{executor-KSPGNVDO.js → executor-YDDC5XGY.js} +4 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +39 -35
- package/dist/index.js.map +1 -1
- package/dist/introspection/index.js +3 -2
- package/dist/kernel/collection.d.ts +11 -6
- package/dist/kernel/match-pairs.d.ts +30 -0
- package/dist/lazy/index.js +3 -2
- package/dist/materialized-views/index.js +4 -3
- package/dist/{noydb-QJFZAH34.js → noydb-2C6QQJ5S.js} +17 -12
- package/dist/pod/index.js +17 -12
- package/dist/registry-D2YXKRYC.js +33 -0
- package/dist/search/index.js +1 -1
- package/dist/{stale-KSPG6LFX.js → stale-2CHKZTMK.js} +2 -2
- package/dist/{walk-G3CZIL4L.js → walk-P3E6QSNC.js} +4 -3
- package/dist/with-formula/derivations/dispatch.d.ts +16 -1
- package/dist/with-formula/derivations/registry.d.ts +20 -6
- package/dist/with-formula/derivations/trigger-match.d.ts +52 -0
- package/dist/with-formula/derivations/types.d.ts +23 -0
- package/dist/with-shape/introspection/behaviors.d.ts +7 -0
- package/dist/with-shape/introspection/describe.d.ts +30 -0
- package/package.json +3 -3
- package/dist/chunk-I6DGKPTY.js.map +0 -1
- package/dist/chunk-KHKKD62N.js.map +0 -1
- package/dist/chunk-NO272T7Q.js.map +0 -1
- package/dist/chunk-OEA5NVSU.js.map +0 -1
- package/dist/chunk-SWVNZQVR.js.map +0 -1
- package/dist/chunk-T6PSWLAR.js.map +0 -1
- package/dist/chunk-UB5CRTFF.js +0 -344
- package/dist/chunk-UB5CRTFF.js.map +0 -1
- package/dist/chunk-WQF3QABE.js.map +0 -1
- package/dist/chunk-YPMW26SL.js +0 -81
- package/dist/chunk-YPMW26SL.js.map +0 -1
- package/dist/dispatch-IM52HDXT.js +0 -194
- package/dist/dispatch-IM52HDXT.js.map +0 -1
- package/dist/registry-FZXPQRAB.js +0 -18
- /package/dist/{backup-VASRVLDZ.js.map → backup-R6A4VZRX.js.map} +0 -0
- /package/dist/{chunk-S5AMPTNE.js.map → chunk-BMELYFHU.js.map} +0 -0
- /package/dist/{chunk-HN4FNA4X.js.map → chunk-COTPDJR5.js.map} +0 -0
- /package/dist/{chunk-E3EBMC4G.js.map → chunk-E2MYJGSG.js.map} +0 -0
- /package/dist/{chunk-JJR2JIN4.js.map → chunk-FFBYRWDJ.js.map} +0 -0
- /package/dist/{chunk-ESVREVWN.js.map → chunk-SXCYYMVS.js.map} +0 -0
- /package/dist/{chunk-THNJ3IKU.js.map → chunk-ULUMAZ5S.js.map} +0 -0
- /package/dist/{collection-facade-MGGHQ3HU.js.map → collection-facade-4ACKIDDY.js.map} +0 -0
- /package/dist/{dispatch-44J4TL72.js.map → dispatch-PDP5TA73.js.map} +0 -0
- /package/dist/{executor-KSPGNVDO.js.map → executor-YDDC5XGY.js.map} +0 -0
- /package/dist/{noydb-QJFZAH34.js.map → noydb-2C6QQJ5S.js.map} +0 -0
- /package/dist/{registry-FZXPQRAB.js.map → registry-D2YXKRYC.js.map} +0 -0
- /package/dist/{stale-KSPG6LFX.js.map → stale-2CHKZTMK.js.map} +0 -0
- /package/dist/{walk-G3CZIL4L.js.map → walk-P3E6QSNC.js.map} +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dumpVaultSchema
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-VGICZPVE.js";
|
|
4
|
+
import "./chunk-RXLYKVTF.js";
|
|
5
|
+
import "./chunk-VYYM3MEN.js";
|
|
5
6
|
import "./chunk-URHRIAV6.js";
|
|
6
7
|
import "./chunk-Y2NBBT6K.js";
|
|
7
8
|
import "./chunk-WWNYSV7S.js";
|
|
@@ -18,4 +19,4 @@ import "./chunk-PZ5AY32C.js";
|
|
|
18
19
|
export {
|
|
19
20
|
dumpVaultSchema
|
|
20
21
|
};
|
|
21
|
-
//# sourceMappingURL=walk-
|
|
22
|
+
//# sourceMappingURL=walk-P3E6QSNC.js.map
|
|
@@ -94,5 +94,20 @@ export interface DerivationDispatchCtx extends DerivationDeleteCtx {
|
|
|
94
94
|
* by the executor — a write to the child recomputes the parent aggregate, and
|
|
95
95
|
* a write to the parent recomputes its own.
|
|
96
96
|
*/
|
|
97
|
-
export declare function dispatchDerivations(ctx: DerivationDispatchCtx, id: string, record: Record<string, unknown>, version: number, wave?: WaveContext
|
|
97
|
+
export declare function dispatchDerivations(ctx: DerivationDispatchCtx, id: string, record: Record<string, unknown>, version: number, wave?: WaveContext,
|
|
98
|
+
/** The pre-write record at `id` in THIS collection (undefined = not
|
|
99
|
+
* captured — no field-match trigger, or a sync-applied wave; null =
|
|
100
|
+
* captured and absent, i.e. a create). Only a `Record` re-fires the OLD
|
|
101
|
+
* matched set alongside the new one (spec §7). */
|
|
102
|
+
prior?: Record<string, unknown> | null): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Trigger fan-out for a DELETED parent record (#1249, spec §8). Distinct from
|
|
105
|
+
* the "record-shape derivations not dispatched on delete" rule — that is
|
|
106
|
+
* about deleting a SOURCE record; this fires when a TRIGGER collection's
|
|
107
|
+
* record is deleted, re-deriving source records that still exist. Pairs
|
|
108
|
+
* evaluate against the tombstoned record's values; matched sources re-derive
|
|
109
|
+
* through the normal executor (their derive() reads the now-absent parent
|
|
110
|
+
* and decides what that means — the engine never cascades deletes).
|
|
111
|
+
*/
|
|
112
|
+
export declare function dispatchTriggerDerivationsOnDelete(ctx: DerivationDispatchCtx, id: string, deleted: Record<string, unknown>): Promise<void>;
|
|
98
113
|
export {};
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { ViaGraph, type FieldRef, type EdgeKind, type Grain } from '../../kernel/via/graph.js';
|
|
2
|
+
import { type NormalizedTrigger } from './trigger-match.js';
|
|
2
3
|
import type { DerivationSpec } from './types.js';
|
|
3
4
|
interface RegisteredStrategy {
|
|
4
5
|
spec: DerivationSpec<any, any>;
|
|
5
6
|
strategyHash: string;
|
|
7
|
+
readonly triggers: ReadonlyArray<NormalizedTrigger>;
|
|
6
8
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Vault-internal registry of derivation strategies. Owned by `Vault`;
|
|
9
|
-
* not exported.
|
|
10
|
-
*
|
|
11
|
-
* @internal
|
|
12
|
-
*/
|
|
13
9
|
export declare class DerivationRegistry {
|
|
14
10
|
private readonly _bySource;
|
|
15
11
|
private readonly _byOutput;
|
|
@@ -22,6 +18,24 @@ export declare class DerivationRegistry {
|
|
|
22
18
|
register(spec: DerivationSpec<any, any>): Promise<void>;
|
|
23
19
|
strategiesForSource(source: string): ReadonlyArray<RegisteredStrategy>;
|
|
24
20
|
strategiesProducingOutput(collection: string): ReadonlyArray<RegisteredStrategy>;
|
|
21
|
+
/**
|
|
22
|
+
* The #1253-pattern typo guard for match fields (#1249): a misspelt
|
|
23
|
+
* `to`/`from` silently matches nothing forever, so validate against the
|
|
24
|
+
* collection's enumerable field set at the earliest point it exists.
|
|
25
|
+
* `schemaFieldKeys(schema) === undefined` (TS-generic collection,
|
|
26
|
+
* unreadable validator) is DELIBERATELY silent — those fields are real
|
|
27
|
+
* and unenumerable. `configKeys` folds in the collection's other
|
|
28
|
+
* non-schema field declarations (fieldMeta/moneyFields/dictKeyFields/
|
|
29
|
+
* refs/computed); `denormExempt` is derived here from this registry's
|
|
30
|
+
* own strategies rather than taken as a parameter — a field this
|
|
31
|
+
* collection's own derivations write via `denorm` is never a typo.
|
|
32
|
+
*/
|
|
33
|
+
validateFieldsFor(collectionName: string, schema: unknown, configKeys: ReadonlyArray<string>, viaSources?: {
|
|
34
|
+
readonly computed?: Readonly<Record<string, unknown>> | undefined;
|
|
35
|
+
readonly viaFields?: Readonly<Record<string, unknown>> | undefined;
|
|
36
|
+
} | undefined): void;
|
|
37
|
+
/** #1266 — one message shape for both match sides; see {@link matchTargetFieldNames}. */
|
|
38
|
+
private _virtualMatchMessage;
|
|
25
39
|
/**
|
|
26
40
|
* All registered strategies as a flat, deduplicated array.
|
|
27
41
|
* Each strategy is indexed once per source (not once per output key),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers behind composite `triggerBy` (#1249).
|
|
3
|
+
*
|
|
4
|
+
* A trigger entry is a CONJUNCTION of equality pairs: a source record
|
|
5
|
+
* matches when EVERY pair satisfies String(source[to]) === String(written[from]).
|
|
6
|
+
* `from: 'id'` reads the written record's id (winning over any stored field
|
|
7
|
+
* named `id`, matching dispatch's `{ ...incoming, id }` convention). The
|
|
8
|
+
* legacy `on` form normalizes to `[{ from: 'id', to: on }]` so everything
|
|
9
|
+
* downstream has ONE shape.
|
|
10
|
+
*
|
|
11
|
+
* Scalar coercion is verbatim from `_findMatchingIds`: both sides must be
|
|
12
|
+
* string | number; anything else fails the pair (never throws).
|
|
13
|
+
* Spec: docs/superpowers/specs/2026-08-29-composite-triggerby-design.md §4-§5.
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
export interface MatchPair {
|
|
17
|
+
readonly from: string;
|
|
18
|
+
readonly to: string;
|
|
19
|
+
}
|
|
20
|
+
export interface NormalizedTrigger {
|
|
21
|
+
readonly collection: string;
|
|
22
|
+
readonly match: ReadonlyArray<MatchPair>;
|
|
23
|
+
readonly maxFanout?: number;
|
|
24
|
+
}
|
|
25
|
+
interface RawTrigger {
|
|
26
|
+
readonly collection: string;
|
|
27
|
+
readonly on?: string;
|
|
28
|
+
readonly match?: ReadonlyArray<MatchPair>;
|
|
29
|
+
readonly maxFanout?: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function normalizeTriggerBy(triggerBy: ReadonlyArray<RawTrigger> | undefined): NormalizedTrigger[];
|
|
32
|
+
/**
|
|
33
|
+
* The value tuple a written record presents to one trigger entry.
|
|
34
|
+
* `null` means "this record cannot address any source" (a from-field is
|
|
35
|
+
* absent or non-scalar) — a legitimate no-match, not an error.
|
|
36
|
+
*/
|
|
37
|
+
export declare function tupleFromWritten(match: ReadonlyArray<MatchPair>, writtenId: string, record: Record<string, unknown> | null): Array<{
|
|
38
|
+
field: string;
|
|
39
|
+
value: string;
|
|
40
|
+
}> | null;
|
|
41
|
+
/** Any single component differing means NOT the same tuple (spec §7). */
|
|
42
|
+
export declare function sameTuple(a: Array<{
|
|
43
|
+
field: string;
|
|
44
|
+
value: string;
|
|
45
|
+
}> | null, b: Array<{
|
|
46
|
+
field: string;
|
|
47
|
+
value: string;
|
|
48
|
+
}> | null): boolean;
|
|
49
|
+
/** Moved to the kernel (#1249) so `collection.ts` can import statically without
|
|
50
|
+
* dragging derivations code into the floor bundle; re-exported here for
|
|
51
|
+
* compatibility with existing consumers of this module. */
|
|
52
|
+
export { recordMatchesPairs, findMatchingIdsByPairs } from '../../kernel/match-pairs.js';
|
|
@@ -183,6 +183,29 @@ export interface DerivationSpec<TSource extends Record<string, unknown>, TOutput
|
|
|
183
183
|
collection: string;
|
|
184
184
|
on: string;
|
|
185
185
|
maxFanout?: number;
|
|
186
|
+
} | {
|
|
187
|
+
/**
|
|
188
|
+
* Multi-field trigger (#1249): a source record matches when EVERY
|
|
189
|
+
* pair satisfies `String(source[to]) === String(written[from])`.
|
|
190
|
+
* `from: 'id'` reads the written record's id; any other `from`
|
|
191
|
+
* reads the written record's field. Exactly one of `on` | `match`
|
|
192
|
+
* per entry. On a LOCAL UPDATE that changes any matched `from`
|
|
193
|
+
* field, fan-out runs on old-match ∪ new-match (any single
|
|
194
|
+
* component changing counts) — the prior record is threaded only
|
|
195
|
+
* from the local write path, so a sync-applied wave write or a
|
|
196
|
+
* tiers restore fans out on the new tuple only. Parent DELETES fan
|
|
197
|
+
* out using the tombstoned record's values — for BOTH forms. A
|
|
198
|
+
* missing/non-scalar `from` value matches nothing. Match fields
|
|
199
|
+
* are validated against the collections' enumerable field sets
|
|
200
|
+
* where possible (silent for TS-generic collections — see the
|
|
201
|
+
* registration guard).
|
|
202
|
+
*/
|
|
203
|
+
collection: string;
|
|
204
|
+
match: ReadonlyArray<{
|
|
205
|
+
from: string;
|
|
206
|
+
to: string;
|
|
207
|
+
}>;
|
|
208
|
+
maxFanout?: number;
|
|
186
209
|
}>;
|
|
187
210
|
/**
|
|
188
211
|
* @internal — set by `withRollup()`. Marks this strategy as
|
|
@@ -46,6 +46,13 @@ export interface DerivationBehaviorEntry {
|
|
|
46
46
|
readonly collection: string;
|
|
47
47
|
readonly on: string;
|
|
48
48
|
readonly maxFanout?: number;
|
|
49
|
+
} | {
|
|
50
|
+
readonly collection: string;
|
|
51
|
+
readonly match: ReadonlyArray<{
|
|
52
|
+
readonly from: string;
|
|
53
|
+
readonly to: string;
|
|
54
|
+
}>;
|
|
55
|
+
readonly maxFanout?: number;
|
|
49
56
|
}>;
|
|
50
57
|
readonly rollup?: {
|
|
51
58
|
readonly from: string;
|
|
@@ -206,6 +206,36 @@ export interface ZodFieldSlot {
|
|
|
206
206
|
* A duck-typed probe, not a Zod dependency: an unrecognised validator returns
|
|
207
207
|
* `undefined` and the caller stays silent rather than guessing, so a validator
|
|
208
208
|
* hub cannot read never produces a false "unknown field" error.
|
|
209
|
+
*
|
|
210
|
+
* Wrappers (reported by the pilot on #1249's guard, and it equally affected
|
|
211
|
+
* #1253's): a wrapped object's `.shape` is undefined, so "has a schema" and
|
|
212
|
+
* "hub can enumerate its fields" silently diverged for exactly the schemas
|
|
213
|
+
* most worth guarding — validated ones.
|
|
214
|
+
*
|
|
215
|
+
* ONE RULE UNWRAPS ALL OF THEM: FOLLOW THE OUTPUT SIDE. These keys describe
|
|
216
|
+
* the PARSED record, so the only question a wrapper raises is whether it
|
|
217
|
+
* changes the parsed shape.
|
|
218
|
+
*
|
|
219
|
+
* Zod 3 `ZodEffects` — `.refine()`/`.superRefine()` (`effect.type` is
|
|
220
|
+
* `'refinement'`) and `z.preprocess()` (`'preprocess'`) both parse WITH
|
|
221
|
+
* the inner schema, so the output is the inner object: follow
|
|
222
|
+
* `_def.schema`. `.transform()` REPLACES the output, so the inner keys
|
|
223
|
+
* would be a lie: stay `undefined`, deliberately.
|
|
224
|
+
* Zod 4 `ZodPipe` — `z.preprocess()` is `pipe(transform -> object)` and
|
|
225
|
+
* `.transform()` is `pipe(object -> transform)`. Following `_def.out`
|
|
226
|
+
* resolves both correctly with no effect-kind test: preprocess reaches the
|
|
227
|
+
* object, transform reaches a `ZodTransform` that has no shape and no
|
|
228
|
+
* inner schema, so the loop returns `undefined` on its own.
|
|
229
|
+
*
|
|
230
|
+
* `preprocess` was the pilot's second finding (#1262): the first fix followed
|
|
231
|
+
* refinements only, which left four of their registered collections unguarded
|
|
232
|
+
* — including the one carrying their only `fieldMeta` PII declaration, i.e.
|
|
233
|
+
* precisely the collection #1253 was written for. It is one string away from
|
|
234
|
+
* `transform` and means the opposite thing, so a test pins the two apart.
|
|
235
|
+
*
|
|
236
|
+
* A duck-typed probe throughout: an unrecognised validator returns `undefined`
|
|
237
|
+
* and the caller stays silent rather than guessing. Bounded depth, so a cyclic
|
|
238
|
+
* duck cannot hang us.
|
|
209
239
|
*/
|
|
210
240
|
export declare function schemaFieldKeys(schema: unknown): readonly string[] | undefined;
|
|
211
241
|
export declare function deriveZodFields(schema: unknown): Promise<Record<string, ZodFieldSlot>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noy-db/hub",
|
|
3
|
-
"version": "0.7.0-pre.
|
|
3
|
+
"version": "0.7.0-pre.13",
|
|
4
4
|
"description": "Zero-knowledge, offline-first, encrypted document store — core library with AES-256-GCM, PBKDF2, multi-user keyring, and sync engine",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vLannaAi <vicio@lanna.ai>",
|
|
@@ -233,14 +233,14 @@
|
|
|
233
233
|
"node": ">=22.0.0"
|
|
234
234
|
},
|
|
235
235
|
"dependencies": {
|
|
236
|
-
"@noy-db/attestation": "0.7.0-pre.
|
|
236
|
+
"@noy-db/attestation": "0.7.0-pre.13"
|
|
237
237
|
},
|
|
238
238
|
"devDependencies": {
|
|
239
239
|
"@types/node": "^22.0.0",
|
|
240
240
|
"esbuild": "^0.25.0",
|
|
241
241
|
"zod": "^4.0.0",
|
|
242
242
|
"zod-to-json-schema": "^3.25.2",
|
|
243
|
-
"@noy-db/on-shamir": "0.7.0-pre.
|
|
243
|
+
"@noy-db/on-shamir": "0.7.0-pre.13"
|
|
244
244
|
},
|
|
245
245
|
"peerDependencies": {
|
|
246
246
|
"zod-to-json-schema": "^3.25.0"
|