@directive-run/core 1.12.0 → 1.13.0

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.
Files changed (92) hide show
  1. package/dist/adapter-utils.cjs +1 -1
  2. package/dist/adapter-utils.d.cts +2 -2
  3. package/dist/adapter-utils.d.ts +2 -2
  4. package/dist/adapter-utils.js +1 -1
  5. package/dist/adapter-utils.js.map +1 -1
  6. package/dist/audit-ledger-Dc6hAXam.d.cts +378 -0
  7. package/dist/audit-ledger-dxvslGi3.d.ts +378 -0
  8. package/dist/chunk-2FF6QGOA.js +2 -0
  9. package/dist/chunk-2FF6QGOA.js.map +1 -0
  10. package/dist/chunk-4MNQDXH7.cjs +3 -0
  11. package/dist/chunk-4MNQDXH7.cjs.map +1 -0
  12. package/dist/chunk-644QZVTT.js +16 -0
  13. package/dist/{chunk-26Z5VNPZ.js.map → chunk-644QZVTT.js.map} +1 -1
  14. package/dist/chunk-ENZEHIL7.cjs +3 -0
  15. package/dist/chunk-ENZEHIL7.cjs.map +1 -0
  16. package/dist/chunk-I722BZA5.js +7 -0
  17. package/dist/chunk-I722BZA5.js.map +1 -0
  18. package/dist/chunk-IXRS4LM4.cjs +2 -0
  19. package/dist/chunk-IXRS4LM4.cjs.map +1 -0
  20. package/dist/chunk-NPX5EKPP.cjs +16 -0
  21. package/dist/{chunk-EX3XG667.cjs.map → chunk-NPX5EKPP.cjs.map} +1 -1
  22. package/dist/chunk-PA6VC32N.js +2 -0
  23. package/dist/chunk-PA6VC32N.js.map +1 -0
  24. package/dist/chunk-PXRV64PA.js +3 -0
  25. package/dist/chunk-PXRV64PA.js.map +1 -0
  26. package/dist/chunk-R2GHSCTR.js +3 -0
  27. package/dist/chunk-R2GHSCTR.js.map +1 -0
  28. package/dist/chunk-T4TRJEJN.cjs +2 -0
  29. package/dist/chunk-T4TRJEJN.cjs.map +1 -0
  30. package/dist/chunk-X7G7UBXU.cjs +7 -0
  31. package/dist/chunk-X7G7UBXU.cjs.map +1 -0
  32. package/dist/index.cjs +2 -2
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +214 -391
  35. package/dist/index.d.ts +214 -391
  36. package/dist/index.js +2 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/internals.cjs +1 -1
  39. package/dist/internals.d.cts +5 -5
  40. package/dist/internals.d.ts +5 -5
  41. package/dist/internals.js +1 -1
  42. package/dist/plugins/index.cjs +2 -2
  43. package/dist/plugins/index.cjs.map +1 -1
  44. package/dist/plugins/index.d.cts +2 -2
  45. package/dist/plugins/index.d.ts +2 -2
  46. package/dist/plugins/index.js +1 -1
  47. package/dist/plugins/index.js.map +1 -1
  48. package/dist/{plugins-Ykl_sAPE.d.ts → plugins-BIzXaYbg.d.cts} +15 -1
  49. package/dist/{plugins-Ykl_sAPE.d.cts → plugins-BIzXaYbg.d.ts} +15 -1
  50. package/dist/predicate-Bnx3LN7P.d.cts +655 -0
  51. package/dist/predicate-BxQVf0ug.d.ts +655 -0
  52. package/dist/system-A6VYKLVF.js +2 -0
  53. package/dist/{system-VZWB6WXX.js.map → system-A6VYKLVF.js.map} +1 -1
  54. package/dist/system-CDJMD5O5.cjs +2 -0
  55. package/dist/{system-GK3NSFQH.cjs.map → system-CDJMD5O5.cjs.map} +1 -1
  56. package/dist/testing.cjs +1 -1
  57. package/dist/testing.cjs.map +1 -1
  58. package/dist/testing.d.cts +1 -1
  59. package/dist/testing.d.ts +1 -1
  60. package/dist/testing.js +1 -1
  61. package/dist/testing.js.map +1 -1
  62. package/dist/{utils-BnQajqPu.d.cts → utils-Mg55IerF.d.cts} +27 -1
  63. package/dist/{utils-BnQajqPu.d.ts → utils-Mg55IerF.d.ts} +27 -1
  64. package/dist/worker.cjs +1 -1
  65. package/dist/worker.d.cts +1 -1
  66. package/dist/worker.d.ts +1 -1
  67. package/dist/worker.js +1 -1
  68. package/package.json +1 -1
  69. package/dist/audit-ledger-9IElAHH9.d.ts +0 -205
  70. package/dist/audit-ledger-qMjEBqiP.d.cts +0 -205
  71. package/dist/chunk-26Z5VNPZ.js +0 -16
  72. package/dist/chunk-4VZOZWXM.cjs +0 -2
  73. package/dist/chunk-4VZOZWXM.cjs.map +0 -1
  74. package/dist/chunk-7NMXRATK.cjs +0 -3
  75. package/dist/chunk-7NMXRATK.cjs.map +0 -1
  76. package/dist/chunk-7TSYQEN3.js +0 -2
  77. package/dist/chunk-7TSYQEN3.js.map +0 -1
  78. package/dist/chunk-EOLY64E6.cjs +0 -3
  79. package/dist/chunk-EOLY64E6.cjs.map +0 -1
  80. package/dist/chunk-EX3XG667.cjs +0 -16
  81. package/dist/chunk-N4KTCKOI.cjs +0 -7
  82. package/dist/chunk-N4KTCKOI.cjs.map +0 -1
  83. package/dist/chunk-T6IJUWYR.js +0 -3
  84. package/dist/chunk-T6IJUWYR.js.map +0 -1
  85. package/dist/chunk-TPOKS4RY.js +0 -3
  86. package/dist/chunk-TPOKS4RY.js.map +0 -1
  87. package/dist/chunk-TZHC4E6S.js +0 -7
  88. package/dist/chunk-TZHC4E6S.js.map +0 -1
  89. package/dist/helpers-D2pfb6vT.d.ts +0 -235
  90. package/dist/helpers-hh6UanB1.d.cts +0 -235
  91. package/dist/system-GK3NSFQH.cjs +0 -2
  92. package/dist/system-VZWB6WXX.js +0 -2
package/dist/index.d.ts CHANGED
@@ -1,386 +1,9 @@
1
- import { P as PredicateOp, a as PatchSpec, C as ClauseResult, F as FactTemplate, b as FactPredicate, S as SchemaType, D as DefinitionMeta, M as ModuleSchema, c as Facts, T as TypedDerivationsDef, d as TypedEventsDef, E as EffectsDef, e as TypedConstraintsDef, f as TypedResolversDef, g as ModuleHooks, h as CrossModuleDeps, i as CrossModuleDerivationsDef, j as CrossModuleEffectsDef, k as CrossModuleConstraintsDef, l as ModuleDef, m as CreateSystemOptionsSingle, n as SingleModuleSystem, o as ModulesMap, p as CreateSystemOptionsNamed, N as NamespacedSystem, q as Plugin, r as TraceOption, s as ErrorBoundaryConfig, R as RequirementWithId, t as Requirement, u as RequirementKeyFn } from './plugins-Ykl_sAPE.js';
2
- export { A as AnySystem, B as BatchConfig, v as DirectiveError, w as DistributableSnapshot, x as DistributableSnapshotOptions, y as DynamicConstraintDef, z as DynamicEffectDef, G as DynamicResolverDef, H as FactsSnapshot, I as HistoryAPI, J as HistoryOption, K as HistoryState, L as InferDerivations, O as InferEvents, Q as InferFacts, U as InferRequirementTypes, V as InferRequirements, W as InferSchemaType, X as InferSelectorState, Y as KeySelector, Z as MetaAccessor, _ as MetaMatch, $ as ObservationEvent, a0 as OperatorObject, a1 as PatchValue, a2 as PayloadRef, a3 as PredicateClause, a4 as PredicateCombinator, a5 as PredicateCombinatorKey, a6 as PredicateObject, a7 as RetryPolicy, a8 as Schema, a9 as Snapshot, aa as System, ab as SystemConfig, ac as SystemInspection, ad as SystemMode, ae as SystemSnapshot, af as TraceEntry, ag as isNamespacedSystem, ah as isSingleModuleSystem } from './plugins-Ykl_sAPE.js';
3
- export { D as DerivationDefWithMeta, t as typedConstraint, a as typedResolver } from './helpers-D2pfb6vT.js';
4
- export { A as AuditEntry, a as AuditEntryKind, b as AuditLedger, c as AuditLedgerOptions, d as AuditLedgerSink, Q as QueryFilter, e as createAuditLedger, m as memorySink } from './audit-ledger-9IElAHH9.js';
5
- export { D as DistributableSnapshotLike, S as SignedSnapshot, a as SnapshotDiff, b as SnapshotDiffEntry, d as diffSnapshots, i as isSignedSnapshot, c as isSnapshotExpired, s as shallowEqual, e as signSnapshot, v as validateSnapshot, f as verifySnapshotSignature } from './utils-BnQajqPu.js';
6
-
7
- /**
8
- * Schema Introspection
9
- *
10
- * A runtime discriminant for every `t.*()` builder result, so downstream
11
- * consumers (`predicateFromIntent`, `doctor`, future `predicateToZod`)
12
- * can ask "what kind is this fact?" without grepping the source.
13
- *
14
- * The discriminant lives at `_kind?: SchemaKindNode` on `ExtendedSchemaType`.
15
- * Base builders set it; chain mutators (`.nullable()`, `.brand()`, etc.)
16
- * preserve / decorate it. When `_kind` is absent (legacy or third-party
17
- * builder), the parser falls back to reading the freeform `_typeName`
18
- * string. When even that fails: `{ kind: "unknown" }` — graceful close.
19
- *
20
- * This module has zero hot-path cost; it is only invoked when an
21
- * introspecting caller asks for it.
22
- */
23
-
24
- /**
25
- * The closed set of kinds a Directive schema field can be.
26
- *
27
- * Drives operator availability via {@link getOperatorsForKind}: e.g.
28
- * `"number"` gets the orderable operators (`$gte`, `$lte`); `"boolean"`
29
- * does not.
30
- */
31
- type SchemaKind = "number" | "string" | "boolean" | "bigint" | "date" | "array" | "object" | "record" | "tuple" | "enum" | "literal" | "union" | "branded" | "unknown";
32
- /**
33
- * A tree-shaped discriminator for a schema field. Composite kinds
34
- * (array, object, tuple, etc.) carry their element / shape information
35
- * so an LLM-prompt builder can show "cartTotal is a number" AND
36
- * "items is an array of { sku: string, qty: number }".
37
- *
38
- * `nullable` / `hasDefault` flags appear on the inner node (NOT a
39
- * wrapping kind) so operator-lookup on `t.number().nullable()` returns
40
- * the number's operators unchanged — `$gte` works on the non-null arm.
41
- */
42
- type SchemaKindNode = ({
43
- kind: "number" | "string" | "boolean" | "bigint" | "date" | "unknown";
44
- } | {
45
- kind: "literal";
46
- value: string | number | boolean | null;
47
- primitive: "string" | "number" | "boolean" | "null";
48
- } | {
49
- kind: "enum";
50
- values: readonly (string | number)[];
51
- primitive: "string" | "number";
52
- } | {
53
- kind: "array";
54
- element: SchemaKindNode;
55
- } | {
56
- kind: "tuple";
57
- elements: readonly SchemaKindNode[];
58
- } | {
59
- kind: "object";
60
- shape: Record<string, SchemaKindNode>;
61
- } | {
62
- kind: "record";
63
- value: SchemaKindNode;
64
- } | {
65
- kind: "union";
66
- members: readonly SchemaKindNode[];
67
- } | {
68
- kind: "branded";
69
- inner: SchemaKindNode;
70
- }) & {
71
- /** True if the schema accepts `null` (from `.nullable()` / `.optional()`). */
72
- nullable?: boolean;
73
- /** True if the schema has a `.default()`. */
74
- hasDefault?: boolean;
75
- };
76
- /**
77
- * Return the {@link SchemaKindNode} for a schema field. Prefers the
78
- * explicit `_kind` discriminant set by the builder; falls back to
79
- * parsing the freeform `_typeName` string for legacy / third-party
80
- * builders that don't set `_kind`.
81
- *
82
- * @example
83
- * ```ts
84
- * getKind(t.number()) // → { kind: "number" }
85
- * getKind(t.string().nullable()) // → { kind: "string", nullable: true }
86
- * getKind(t.array(t.number())) // → { kind: "array", element: { kind: "number" } }
87
- * ```
88
- */
89
- declare function getKind(schema: unknown): SchemaKindNode;
90
- /**
91
- * Walk the `facts` block of a module schema and emit a flat map from
92
- * dotted path → kind node. Nested `t.object()` shapes flatten using
93
- * `.` as the separator, matching the convention used by
94
- * `OperatorObject<V>`'s nested-path support.
95
- *
96
- * Passing a top-level schema directly (without the `facts:` wrapper)
97
- * also works — anything iterable as `Record<string, ExtendedSchemaType>`
98
- * is acceptable.
99
- */
100
- declare function getSchemaFieldKinds(schema: unknown): Map<string, SchemaKindNode>;
101
- /**
102
- * Return the set of `PredicateOp` strings that are valid against a
103
- * given {@link SchemaKindNode}.
104
- *
105
- * Mirrors the type-level matrix encoded in `OperatorObject<V>` in
106
- * `types/predicate.ts` — drift between the two is enforced by the
107
- * compile-time conformance test
108
- * (`schema-introspection-conformance.test-d.ts`).
109
- *
110
- * - **Common to all kinds:** `$eq`, `$ne`, `$in`, `$nin`, `$exists`
111
- * - **Orderable (`number`/`string`/`bigint`/`date`):** + `$gt`, `$gte`,
112
- * `$lt`, `$lte`, `$between`
113
- * - **String-specific:** + `$matches`, `$startsWith`, `$endsWith`, `$contains`
114
- * - **Array:** + `$contains` (over the element type)
115
- * - **Union:** *intersection* across members (the operand must be valid
116
- * for every branch).
117
- * - **Branded:** delegates to the inner kind.
118
- * - **Literal / Enum:** operators of the primitive, with `$eq` / `$in`
119
- * operand restricted at the LLM-prompt layer (not enforced here).
120
- *
121
- * `nullable` does not change operator availability — `$gte` on a
122
- * nullable number is fine on the non-null arm; `$exists` handles null.
123
- */
124
- declare function getOperatorsForKind(node: SchemaKindNode): readonly PredicateOp[];
125
- /**
126
- * Return all known predicate operators — convenience for prompt builders
127
- * that need to show the LLM the full set.
128
- */
129
- declare function listAllPredicateOperators(): readonly PredicateOp[];
130
-
131
- /**
132
- * Runtime for data-configuration predicates and templates.
133
- *
134
- * Pure module — imports only its own types. Reads facts through whatever
135
- * object it is handed (the reactive `Facts` proxy in production, a plain
136
- * snapshot in tests), so it never depends on the engine, store, or tracking.
137
- */
138
-
139
- /** A readable scope — the `Facts` proxy and a plain snapshot both satisfy it. */
140
- type Scope = Record<string, unknown>;
141
- /**
142
- * True when `v` is a data-form spec (predicate object/array) rather than a
143
- * function. Excludes class instances (Date, RegExp, Map, Set, Promise, etc.)
144
- * — only plain `{}` literals and arrays of plain clause shapes qualify.
145
- *
146
- * @example
147
- * ```ts
148
- * isPredicate({ phase: "red" }); // true
149
- * isPredicate((f) => f.phase === "red"); // false
150
- * isPredicate([{ fact: "phase", op: "$eq", value: "red" }]); // true
151
- * ```
152
- */
153
- declare function isPredicate(v: unknown): boolean;
154
- /**
155
- * True when `v` is a {@link FactTemplate} (`{ $template: string }`).
156
- *
157
- * @example
158
- * ```ts
159
- * isTemplate({ $template: "Hi ${name}" }); // true
160
- * isTemplate({ $set: { name: "x" } }); // false
161
- * ```
162
- */
163
- declare function isTemplate(v: unknown): v is FactTemplate;
164
- /**
165
- * Throw when a predicate spec contains an operand that cannot survive a
166
- * JSON round-trip — i.e. that would silently mis-evaluate if the spec was
167
- * loaded from `JSON.parse`.
168
- *
169
- * Three failure classes are detected:
170
- *
171
- * - **Lost `RegExp` operand.** A `$matches` operand that is not a
172
- * `RegExp` instance. `JSON.parse` reconstructs a serialized regex as
173
- * `{}`, so a `$matches` clause with an empty-object operand is the
174
- * signature of a regex that did not survive serialization. Reify it
175
- * with `new RegExp(pattern, flags)` before installing the predicate.
176
- * - **`bigint` operand.** `JSON.stringify` throws on `bigint`, so a
177
- * `bigint` operand cannot have been produced by a JSON pipeline and
178
- * cannot be persisted by one either.
179
- * - **`Set` / `Map` operand.** Both serialize to `{}` and lose all
180
- * members; a predicate carrying one is not JSON-safe.
181
- *
182
- * This is an opt-in helper — the engine does not call it automatically.
183
- * Users who load predicates from JSON should call it after `JSON.parse`
184
- * to fail loud rather than silently mis-evaluate. See the
185
- * "Serialization" section of RFC-0004.
186
- *
187
- * @example
188
- * ```ts
189
- * validatePredicate({ phase: { $matches: {} } });
190
- * // throws — empty object where a RegExp is required
191
- *
192
- * validatePredicate({ phase: "red", elapsed: { $gte: 30 } });
193
- * // ok — JSON-clean operands
194
- * ```
195
- */
196
- declare function validatePredicate(spec: unknown, path?: string): void;
197
-
198
- interface SchemaValidationError {
199
- /** Dotted path to the failing clause (e.g. `cartTotal`, `auth.token.$gte`). */
200
- readonly path: string;
201
- /** The operator that failed (or the literal-equality marker `$eq`). */
202
- readonly op: string;
203
- /** The kind of the fact at this path, or `undefined` when the fact wasn't in the kind map. */
204
- readonly kind?: SchemaKindNode;
205
- /** The operators that ARE allowed for this fact's kind. */
206
- readonly allowedOps?: readonly string[];
207
- /** Human-readable failure reason (suitable for feeding back to an LLM). */
208
- readonly reason: string;
209
- }
210
- interface SchemaValidationOptions {
211
- /** Reject predicates with more than this many operator clauses (DoS guard). Default unbounded. */
212
- readonly maxOperatorCount?: number;
213
- }
214
- type SchemaValidationResult = {
215
- ok: true;
216
- operatorCount: number;
217
- } | {
218
- ok: false;
219
- errors: readonly SchemaValidationError[];
220
- operatorCount: number;
221
- };
222
- /**
223
- * Cross-check an LLM-emitted (or otherwise externally-sourced) predicate
224
- * against a schema's runtime kind map. Catches errors that
225
- * {@link validatePredicate} cannot — operator-on-wrong-kind (`$gte` on a
226
- * boolean fact), unknown fact paths, and (optionally) operator-count
227
- * exhaustion DoS attempts.
228
- *
229
- * Pair with {@link validatePredicate} (structural / JSON safety) for full
230
- * coverage. Use {@link getSchemaFieldKinds} to derive `kindMap` from a
231
- * module schema.
232
- *
233
- * Designed for the LLM-emit retry loop: returns a list of errors with
234
- * structured `{path, op, kind, allowedOps, reason}` rather than throwing,
235
- * so the caller can feed the errors back to the model.
236
- *
237
- * @example
238
- * ```ts
239
- * const kindMap = getSchemaFieldKinds({ facts: { cartTotal: t.number(), active: t.boolean() } });
240
- * const result = validatePredicateAgainstSchema(
241
- * { cartTotal: { $gte: 50 }, active: { $gte: true } },
242
- * kindMap,
243
- * );
244
- * // → { ok: false, errors: [{ path: "active", op: "$gte", reason: "..." }], operatorCount: 2 }
245
- * ```
246
- */
247
- declare function validatePredicateAgainstSchema(spec: unknown, kindMap: Map<string, SchemaKindNode>, opts?: SchemaValidationOptions): SchemaValidationResult;
248
- /**
249
- * Evaluate a {@link FactPredicate} against a fact scope. `prev` (a previous
250
- * snapshot) is consulted only by the `$changed` operator.
251
- *
252
- * @example
253
- * ```ts
254
- * evaluatePredicate({ phase: "red", elapsed: { $gte: 30 } }, { phase: "red", elapsed: 45 });
255
- * // → true
256
- * evaluatePredicate({ $any: [{ phase: "red" }, { phase: "yellow" }] }, { phase: "green" });
257
- * // → false
258
- * ```
259
- */
260
- declare function evaluatePredicate(spec: unknown, facts: Scope, prev?: Scope, depth?: number): boolean;
261
- /**
262
- * Evaluate a predicate and return a per-clause breakdown — the data feed for
263
- * devtools, `system.explain()`, and `directive explain`.
264
- *
265
- * @example
266
- * ```ts
267
- * evaluatePredicateExplained(
268
- * { phase: "red", elapsed: { $gte: 30 } },
269
- * { phase: "red", elapsed: 20 },
270
- * );
271
- * // → [
272
- * // { path: "phase", op: "$eq", expected: "red", actual: "red", pass: true },
273
- * // { path: "elapsed", op: "$gte", expected: 30, actual: 20, pass: false },
274
- * // ]
275
- * ```
276
- */
277
- declare function evaluatePredicateExplained(spec: unknown, facts: Scope, prev?: Scope, pathPrefix?: string): ClauseResult[];
278
- /**
279
- * Memoize a predicate as a reusable evaluation closure.
280
- *
281
- * The returned function accepts any `facts` scope (the reactive proxy in
282
- * production, a plain object in tests) plus an optional `prev` snapshot for
283
- * `$changed`. The closure is cached **by predicate identity** in a
284
- * `WeakMap`, so passing the same `predicate` reference repeatedly is
285
- * allocation-free; cleanup is automatic once the predicate is no longer
286
- * reachable.
287
- *
288
- * Note: no actual compilation happens — the returned closure re-walks the
289
- * spec on every call via `evaluatePredicate`. The name reflects what the
290
- * function does (closure memoization keyed by predicate identity), not a
291
- * bytecode/AST compile step.
292
- *
293
- * Intended for advanced users who want a stable function reference per
294
- * predicate (custom devtools, batched analyses). Regular module code does
295
- * not need to call this — the engine wraps data-form `when` / `on` specs
296
- * automatically at registration.
297
- *
298
- * @example
299
- * ```ts
300
- * const predicate = { phase: "red", elapsed: { $gte: 30 } };
301
- * const check = memoizePredicate(predicate);
302
- * check({ phase: "red", elapsed: 45 }); // → true
303
- * check({ phase: "red", elapsed: 5 }); // → false
304
- * ```
305
- */
306
- declare function memoizePredicate(predicate: object): (facts: Scope, prev?: Scope) => boolean;
307
- /**
308
- * Collect the fact keys a predicate references. Used for static analysis,
309
- * devtools, and effect `on` dependency wiring. Nested predicates contribute
310
- * dotted keys (`auth.token`).
311
- *
312
- * @example
313
- * ```ts
314
- * extractDeps({ phase: "red", elapsed: { $gte: 30 } });
315
- * // → Set { "phase", "elapsed" }
316
- * extractDeps({ self: { phase: "red" }, auth: { token: { $exists: true } } });
317
- * // → Set { "self.phase", "auth.token" }
318
- * ```
319
- */
320
- declare function extractDeps(spec: unknown, prefix?: string): Set<string>;
321
- /**
322
- * Interpolate a {@link FactTemplate} against a scope. Single-pass character
323
- * scanner: `${ident}` interpolates `scope[ident]`; `$${` emits a literal
324
- * `${`; unknown keys dev-warn and yield an empty string.
325
- *
326
- * @example
327
- * ```ts
328
- * evaluateTemplate({ $template: "Hi ${name}!" }, { name: "Ada" });
329
- * // → "Hi Ada!"
330
- * evaluateTemplate({ $template: "$${price}" }, {});
331
- * // → "${price}"
332
- * ```
333
- */
334
- declare function evaluateTemplate(spec: FactTemplate, scope: Scope): string;
335
- /**
336
- * Collect the placeholder keys referenced by a template. The static-analysis
337
- * counterpart to {@link extractDeps} — useful for devtools, codegen, and
338
- * "which facts does this template read" inspections. Only valid identifier
339
- * placeholders are collected; malformed ones are ignored.
340
- *
341
- * @example
342
- * ```ts
343
- * extractTemplateKeys({ $template: "${firstName} ${lastName}" });
344
- * // → Set { "firstName", "lastName" }
345
- * extractTemplateKeys({ $template: "$${literal}" });
346
- * // → Set {} (escaped — not a placeholder)
347
- * ```
348
- */
349
- declare function extractTemplateKeys(spec: FactTemplate): Set<string>;
350
- /**
351
- * Build a stable dedup key by selecting fields from a requirement payload.
352
- * Order-as-declared; values are stable-stringified (keys sorted recursively)
353
- * so two payloads with the same fields in different orders dedupe to the
354
- * same key.
355
- *
356
- * @example
357
- * ```ts
358
- * evaluateKeySelector(["url", "method"], { url: "/a", method: "GET" });
359
- * // → '"/a"|"GET"'
360
- * evaluateKeySelector(["id"], { id: 42 });
361
- * // → '42'
362
- * ```
363
- */
364
- declare function evaluateKeySelector(selector: readonly string[], source: Record<string, unknown>): string;
365
- /**
366
- * Apply a {@link PatchSpec} — assign facts from literals, payload copies
367
- * (`$ref`), or interpolated strings (`$template`). Mutates through the passed
368
- * `facts` proxy so change-tracking and downstream invalidation fire.
369
- *
370
- * @example
371
- * ```ts
372
- * const spec = {
373
- * $set: {
374
- * active: true,
375
- * userId: { $ref: "id" },
376
- * label: { $template: "user ${name}" },
377
- * },
378
- * };
379
- * applyPatch(spec, facts, { id: "u_1", name: "Ada" });
380
- * // facts.active = true; facts.userId = "u_1"; facts.label = "user Ada"
381
- * ```
382
- */
383
- declare function applyPatch(spec: PatchSpec<Record<string, unknown>, Record<string, unknown>>, facts: Record<string, unknown>, payload: Record<string, unknown>): void;
1
+ import { C as ClauseResult, F as FactPredicate, S as SchemaType, D as DefinitionMeta, M as ModuleSchema, a as Facts, T as TypedDerivationsDef, b as TypedEventsDef, E as EffectsDef, c as TypedConstraintsDef, d as TypedResolversDef, e as ModuleHooks, f as CrossModuleDeps, g as CrossModuleDerivationsDef, h as CrossModuleEffectsDef, i as CrossModuleConstraintsDef, j as ModuleDef, k as CreateSystemOptionsSingle, l as SingleModuleSystem, m as ModulesMap, n as CreateSystemOptionsNamed, N as NamespacedSystem, P as Plugin, o as TraceOption, p as ErrorBoundaryConfig, R as RequirementWithId, q as Requirement, r as RequirementKeyFn } from './plugins-BIzXaYbg.js';
2
+ export { A as AnySystem, B as BatchConfig, s as DirectiveError, t as DistributableSnapshot, u as DistributableSnapshotOptions, v as DynamicConstraintDef, w as DynamicEffectDef, x as DynamicResolverDef, y as FactTemplate, z as FactsSnapshot, H as HistoryAPI, G as HistoryOption, I as HistoryState, J as InferDerivations, K as InferEvents, L as InferFacts, O as InferRequirementTypes, Q as InferRequirements, U as InferSchemaType, V as InferSelectorState, W as KeySelector, X as MetaAccessor, Y as MetaMatch, Z as ObservationEvent, _ as OperatorObject, $ as PatchSpec, a0 as PatchValue, a1 as PayloadRef, a2 as PredicateClause, a3 as PredicateCombinator, a4 as PredicateCombinatorKey, a5 as PredicateObject, a6 as PredicateOp, a7 as RetryPolicy, a8 as Schema, a9 as Snapshot, aa as System, ab as SystemConfig, ac as SystemInspection, ad as SystemMode, ae as SystemSnapshot, af as TraceEntry, ag as isNamespacedSystem, ah as isSingleModuleSystem } from './plugins-BIzXaYbg.js';
3
+ import { S as SchemaKindNode } from './predicate-BxQVf0ug.js';
4
+ export { D as DerivationDefWithMeta, a as SchemaKind, b as SchemaValidationError, c as SchemaValidationOptions, d as SchemaValidationResult, e as applyPatch, f as evaluateKeySelector, g as evaluatePredicate, h as evaluatePredicateExplained, i as evaluateTemplate, j as extractDeps, k as extractTemplateKeys, l as getKind, m as getOperatorsForKind, n as getSchemaFieldKinds, o as isPredicate, p as isTemplate, q as listAllPredicateOperators, r as memoizePredicate, t as typedConstraint, s as typedResolver, v as validatePredicate, u as validatePredicateAgainstSchema } from './predicate-BxQVf0ug.js';
5
+ export { A as AuditEntry, a as AuditEntryKind, b as AuditLedger, c as AuditLedgerOptions, d as AuditLedgerSink, Q as QueryFilter, V as VerifyResult, e as createAuditLedger, m as memorySink } from './audit-ledger-dxvslGi3.js';
6
+ export { D as DistributableSnapshotLike, S as SignedSnapshot, a as SnapshotDiff, b as SnapshotDiffEntry, d as diffSnapshots, i as isSignedSnapshot, c as isSnapshotExpired, s as shallowEqual, e as signSnapshot, v as validateSnapshot, f as verifySnapshotSignature } from './utils-Mg55IerF.js';
384
7
 
385
8
  /**
386
9
  * Predicate backtest — rule-change impact.
@@ -824,6 +447,40 @@ declare function diffRules(options: DiffRulesOptions): RulesDiffReport;
824
447
  */
825
448
  declare function diffClauses(before: unknown, after: unknown): Change[];
826
449
 
450
+ /**
451
+ * `predicateHash` — content-addressed fingerprint for a {@link FactPredicate}.
452
+ *
453
+ * Canonicalized via `stableStringify` so semantically-identical predicates
454
+ * (different key order, different whitespace) produce the same hash.
455
+ * Uses djb2 32-bit (8-character hex). SHA-256 reserved for v2.
456
+ *
457
+ * @module
458
+ */
459
+
460
+ /**
461
+ * Compute a content-addressed hash for a {@link FactPredicate}. Canonicalised
462
+ * via `stableStringify` so two semantically-identical predicates emitted
463
+ * with different key order or whitespace produce the same hash.
464
+ *
465
+ * Suitable for:
466
+ * - Cache keys, dedupe in `sweepUnder`
467
+ * - Audit-ledger entry indexing (the `whenSpec` field has its own hash chain)
468
+ * - Provenance records (`predicateFromIntentWithProvenance.predicateHash`)
469
+ *
470
+ * **NOT suitable for cryptographic use** — 32-bit djb2 is collision-prone
471
+ * against an adversary. SHA-256 is reserved for v2.
472
+ *
473
+ * @example
474
+ * ```ts
475
+ * predicateHash({ cartTotal: { $gte: 50 } });
476
+ * // → "a1b2c3d4" (stable across runs and runtimes)
477
+ *
478
+ * // Two semantically-identical predicates → same hash
479
+ * predicateHash({ a: 1, b: 2 }) === predicateHash({ b: 2, a: 1 }); // true
480
+ * ```
481
+ */
482
+ declare function predicateHash<F = Record<string, unknown>>(spec: FactPredicate<F>): string;
483
+
827
484
  /**
828
485
  * predict() — "would this predicate fire against these facts, and if
829
486
  * not, what facts must change to make it fire?"
@@ -848,7 +505,7 @@ interface PredictMissingChange {
848
505
  /** Human-readable hint suitable for feeding back to an LLM or showing to a user. */
849
506
  readonly suggestion: string;
850
507
  }
851
- interface PredictResult<F> {
508
+ interface PredictResult<_F = unknown> {
852
509
  /** True iff the predicate would fire against `facts`. */
853
510
  readonly wouldFire: boolean;
854
511
  /** Full clause-by-clause result (reuses the whenExplain payload). */
@@ -857,10 +514,12 @@ interface PredictResult<F> {
857
514
  * List of failing clauses, each with a `suggestion` describing the
858
515
  * smallest concrete change to `facts` that would make that clause pass.
859
516
  * Empty when `wouldFire === true`.
517
+ *
518
+ * If the predicate contains a `$changed` clause AND `prev` was not
519
+ * passed, a synthetic warning is included here (the `$changed` clause
520
+ * cannot be evaluated without a `prev` snapshot).
860
521
  */
861
522
  readonly missingChanges: readonly PredictMissingChange[];
862
- /** Reference to the predicate evaluated, for telemetry. */
863
- readonly predicate: FactPredicate<F>;
864
523
  }
865
524
  /**
866
525
  * Run a predicate against the current fact state and report whether it
@@ -869,6 +528,12 @@ interface PredictResult<F> {
869
528
  * Designed for the LLM-iteration loop and for "preview the impact of this
870
529
  * rule" UIs.
871
530
  *
531
+ * **`$changed` and `prev`:** the `$changed` operator compares the current
532
+ * value of a fact against the `prev` snapshot. If the predicate uses
533
+ * `$changed` and `prev` is not supplied, the missing-changes list includes
534
+ * a synthetic warning indicating that the clause cannot be evaluated —
535
+ * pass `predict(predicate, facts, prev)` to get a real result.
536
+ *
872
537
  * @example
873
538
  * ```ts
874
539
  * const predicate = { cartTotal: { $gte: 50 }, region: { $in: ["US", "EU"] } };
@@ -882,7 +547,6 @@ interface PredictResult<F> {
882
547
  * // { path: "cartTotal", op: "$gte", expected: 50, actual: 30,
883
548
  * // suggestion: "set cartTotal to at least 50 (currently 30)" },
884
549
  * // ],
885
- * // predicate: { cartTotal: { $gte: 50 }, region: { $in: ["US", "EU"] } },
886
550
  * // }
887
551
  * ```
888
552
  */
@@ -896,9 +560,11 @@ declare function predict<F = Record<string, unknown>>(predicate: FactPredicate<F
896
560
  *
897
561
  * - **direct contradiction:** same fact path, opposite-direction
898
562
  * comparison (`$gte 50` vs `$lt 50` — they cannot both fire).
563
+ * Reported under `contradictions`.
899
564
  * - **subset:** candidate's range is a strict subset of an existing
900
565
  * constraint's range (candidate `$gte 100` when existing already
901
- * has `$gte 50`; the new rule never fires independently of the old).
566
+ * has `$gte 50`; the new rule is redundant). Reported under
567
+ * `warnings` — a redundant rule is not a contradiction, just noise.
902
568
  * - **overlap:** shares ≥1 fact path with non-trivial intersection —
903
569
  * a warning, not a hard error.
904
570
  *
@@ -914,6 +580,16 @@ declare function predict<F = Record<string, unknown>>(predicate: FactPredicate<F
914
580
  * exists) are NOT acceptable — every reported contradiction must be
915
581
  * defensible.
916
582
  *
583
+ * **v1 scope LIMITATION (M4):** `checkAgainst` operates on predicate
584
+ * logic only. It does NOT consult `bind:` / `owns:` resolver metadata —
585
+ * a candidate that would *write* to a fact owned by another constraint
586
+ * will not be flagged here. Use `doctor.checkOwns()` for that gate.
587
+ *
588
+ * **INVARIANT (M11):** `checkAgainst` depends on `flattenPredicate`
589
+ * emitting `{ path, op, value }` LeafClause shape — if that shape ever
590
+ * changes, every comparison below silently breaks. See
591
+ * `doctor-leaf-shape.contract.test.ts`.
592
+ *
917
593
  * Pure, sync, no engine dependency.
918
594
  */
919
595
 
@@ -939,15 +615,70 @@ interface Contradiction {
939
615
  };
940
616
  }
941
617
  interface CheckAgainstResult {
942
- /** Hard contradictions — candidate never co-fires with existing rule. */
618
+ /**
619
+ * Hard contradictions — candidate never co-fires with existing rule.
620
+ * Includes only `type: "direct"` findings. Redundancy (`type: "subset"`)
621
+ * lives under `warnings` — a redundant rule is noise, not a conflict.
622
+ */
943
623
  readonly contradictions: readonly Contradiction[];
944
- /** Soft overlaps — shared facts with non-trivial intersection. */
624
+ /**
625
+ * Soft findings — shared facts with non-trivial intersection
626
+ * (`type: "overlap"`) AND subset redundancy (`type: "subset"`).
627
+ */
945
628
  readonly warnings: readonly Contradiction[];
946
629
  }
947
630
  /** Shape of `system.inspect().constraints[N]` we care about — accepts any superset. */
948
631
  interface ExistingConstraint {
949
632
  readonly id: string;
950
633
  readonly whenSpec?: unknown;
634
+ /**
635
+ * Fact paths the constraint's resolver(s) write to, if exposed by the
636
+ * inspect API. Consulted by {@link doctor.checkOwns} to flag candidates
637
+ * that would write to fields owned elsewhere. Optional — older inspect
638
+ * payloads omit it.
639
+ */
640
+ readonly owns?: readonly string[];
641
+ /** Same as {@link owns} but for the bind: side of binding metadata. */
642
+ readonly bind?: readonly string[];
643
+ }
644
+ /**
645
+ * Returned by {@link doctor.checkOwns}.
646
+ *
647
+ * `severity` matches the {@link Contradiction} discriminator on
648
+ * {@link CheckAgainstResult} so callers can route both shapes
649
+ * uniformly: by default, owns-collisions are `"warning"` (the engine
650
+ * still has a runtime gate), but a caller can promote them to
651
+ * `"error"` for stricter pre-deploy linting. (M5)
652
+ */
653
+ interface CheckOwnsFinding {
654
+ readonly constraintId: string;
655
+ /** The fact path on the candidate predicate that triggered the finding. */
656
+ readonly candidatePath: string;
657
+ /** The owner side it would collide with: "owns" or "bind". */
658
+ readonly source: "owns" | "bind";
659
+ readonly reason: string;
660
+ /**
661
+ * Severity discriminator — `"warning"` by default (the engine has
662
+ * its own runtime gate), `"error"` reserved for callers that want
663
+ * strict pre-deploy lints. (M5)
664
+ */
665
+ readonly severity?: "error" | "warning";
666
+ }
667
+ /**
668
+ * Result of {@link doctor.checkOwns}.
669
+ *
670
+ * Owns-/bind- collisions are warnings, not contradictions — the engine
671
+ * itself enforces the runtime binding gate, so a pre-deploy doctor pass
672
+ * treats them as advisory. The {@link CheckOwnsFinding.severity}
673
+ * discriminator anticipates v2 promotion of findings to errors. (M5, F-3)
674
+ */
675
+ interface CheckOwnsResult {
676
+ /**
677
+ * Owns-/bind- collisions surfaced by {@link doctor.checkOwns}. Empty
678
+ * when constraints expose no `owns:` / `bind:` metadata or the
679
+ * candidate's fact paths don't overlap.
680
+ */
681
+ readonly warnings: readonly CheckOwnsFinding[];
951
682
  }
952
683
  declare const doctor: {
953
684
  /**
@@ -979,8 +710,100 @@ declare const doctor: {
979
710
  checkAgainst<F = Record<string, unknown>>(candidate: FactPredicate<F>, existing: readonly ExistingConstraint[] | {
980
711
  constraints: readonly ExistingConstraint[];
981
712
  }): CheckAgainstResult;
713
+ /**
714
+ * Flag a candidate predicate whose referenced fact paths overlap with
715
+ * fields *owned* (or `bind:`-bound) by an existing constraint's
716
+ * resolvers. A v1 stub: relies on `inspect()` exposing `owns:` / `bind:`
717
+ * on each constraint snapshot. When that metadata is absent, this
718
+ * returns `{ findings: [] }` — no false positives.
719
+ *
720
+ * **TODO (v2):** wire the engine's resolver-ownership graph through
721
+ * `system.inspect()` so this returns real findings without manual
722
+ * `owns:` annotations on inspect payloads.
723
+ *
724
+ * @example
725
+ * ```ts
726
+ * doctor.checkOwns(
727
+ * { cartTotal: { $gte: 100 } },
728
+ * system.inspect(),
729
+ * );
730
+ * // → { findings: [
731
+ * // { constraintId: "applyDiscount", candidatePath: "cartTotal",
732
+ * // source: "owns",
733
+ * // reason: "Constraint applyDiscount already owns cartTotal." }
734
+ * // ] }
735
+ * ```
736
+ */
737
+ checkOwns<F = Record<string, unknown>>(candidate: FactPredicate<F>, existing: readonly ExistingConstraint[] | {
738
+ constraints: readonly ExistingConstraint[];
739
+ }): CheckOwnsResult;
982
740
  };
983
741
 
742
+ /**
743
+ * `describePredicate` — turn any {@link FactPredicate} into a precise,
744
+ * human-readable English sentence (or algebraic expression).
745
+ *
746
+ * Closes the LLM-emit round-trip:
747
+ *
748
+ * intent (NL) → predicate (AST) → describePredicate (NL) → reprompt
749
+ *
750
+ * Pure tree walker. No side effects. No throws on a valid predicate. On
751
+ * an invalid input (cyclic spec, non-object) returns a sentinel string
752
+ * rather than throwing, so it is safe to call on user/LLM input.
753
+ *
754
+ * Reuses no traversal infrastructure from `walkPredicate` directly because
755
+ * `walkPredicate` is a callback walker (flat) and English rendering needs a
756
+ * recursive return-value walker (tree). The two are isomorphic — same
757
+ * structural rules, same depth cap, same cycle guard. Operator/value
758
+ * formatting mirrors `rules-diff.ts`'s prose style.
759
+ *
760
+ * @module
761
+ */
762
+
763
+ /**
764
+ * Options controlling how {@link describePredicate} renders a predicate.
765
+ */
766
+ interface DescribeOptions {
767
+ /** Locale for number formatting (default: `"en-US"`). */
768
+ locale?: string;
769
+ /** Wrap each clause in parens when there's > 1 clause. Default `true`. */
770
+ parenthesize?: boolean;
771
+ /** Style: `"natural"` (English prose) or `"formal"` (algebraic). Default `"natural"`. */
772
+ style?: "natural" | "formal";
773
+ /**
774
+ * Friendly fact-name renderer. Default: pass-through. Use to map e.g.
775
+ * `cartTotal` → `"cart total"`. Only consulted in `"natural"` style — the
776
+ * formal style preserves the raw dotted path so output stays algebraic.
777
+ */
778
+ factName?: (path: string) => string;
779
+ }
780
+ /**
781
+ * Render a {@link FactPredicate} as a precise human-readable sentence.
782
+ *
783
+ * Renders a `FactPredicate` as English by default (`style: 'natural'`);
784
+ * pass `style: 'formal'` for algebraic notation (`≥`, `∈`, `∧`, etc.).
785
+ *
786
+ * @remarks
787
+ * Exported as `describePredicate` (not `describe`) to avoid collision with
788
+ * vitest's `describe()`.
789
+ *
790
+ * @example
791
+ * ```ts
792
+ * describePredicate({ cartTotal: { $gte: 50 } });
793
+ * // → "cartTotal is at least 50"
794
+ *
795
+ * describePredicate(
796
+ * { $any: [{ region: "US" }, { region: "EU" }] },
797
+ * { factName: (p) => p.replace(/([A-Z])/g, " $1").toLowerCase() },
798
+ * );
799
+ * // → "(region is US) OR (region is E U)"
800
+ *
801
+ * describePredicate({ cartTotal: { $gte: 50 } }, { style: "formal" });
802
+ * // → "cartTotal ≥ 50"
803
+ * ```
804
+ */
805
+ declare function describePredicate<F = Record<string, unknown>>(predicate: FactPredicate<F>, opts?: DescribeOptions): string;
806
+
984
807
  /**
985
808
  * Compile a `FactPredicate` to a parameterized Postgres-style SQL WHERE
986
809
  * clause. The same predicate that gates a constraint on the client can
@@ -2415,4 +2238,4 @@ declare const Backoff: {
2415
2238
  readonly Exponential: "exponential";
2416
2239
  };
2417
2240
 
2418
- export { Backoff, type Branded, type ChainableSchemaType, type Change, type ChangeKind, type CheckAgainstResult, ClauseResult, type ConstraintDiff, type ConstraintStatus, type Contradiction, type ContradictionType, CreateSystemOptionsNamed, CreateSystemOptionsSingle, CrossModuleDeps, DefinitionMeta, type DiffRulesOptions, ErrorBoundaryConfig, type ExistingConstraint, type ExtendedSchemaType, FactPredicate, FactTemplate, Facts, type LeafClause, MAX_REPLAY_FRAMES, MAX_SWEEP_POINTS, type ModuleConfig, type ModuleConfigWithDeps, ModuleDef, ModuleHooks, ModuleSchema, ModulesMap, NamespacedSystem, PatchSpec, Plugin, type PredicateBacktestReport, PredicateOp, type PredicateToMongoOptions, type PredicateToPostgrestOptions, type PredicateToSqlOptions, type PredicateToSqlResult, type PredictMissingChange, type PredictResult, type ReplayDiffSample, type ReplayFrame, type ReplayUnderOptions, Requirement, RequirementSet, type RequirementTypeStatus, RequirementWithId, type RulesDiffReport, type RulesMapInput, type SchemaKind, type SchemaKindNode, SchemaType, type SchemaValidationError, type SchemaValidationOptions, type SchemaValidationResult, type SignalClock, SingleModuleSystem, type SweepHole, type SweepPoint, type SweepReport, type SweepUnderOptions, type TimerFactOpts, type TimerFactState, TraceOption, applyPatch, completeTimer, createModule, createModuleFactory, createRequirementStatusPlugin, createStatusHook, createSystem, createSystemWithStatus, defaultClock, diffClauses, diffRules, doctor, elapsedMs, evaluateKeySelector, evaluatePredicate, evaluatePredicateExplained, evaluateTemplate, extractDeps, extractTemplateKeys, flattenPredicate, forType, framesFromHistory, framesFromSnapshots, generateRequirementId, getKind, getOperatorsForKind, getSchemaFieldKinds, initialTimerState, isPredicate, isRequirementType, isTemplate, listAllPredicateOperators, memoizePredicate, pauseTimer, predicateToMongo, predicateToPostgrest, predicateToSQL, predicateToWhere, predict, realClock, registerRepeat, remainingMs, replayUnder, req, resetTimer, resumeTimer, startTimer, sweepUnder, t, tickTimer, timerOps, toReplayFrames, toRulesMap, validatePredicate, validatePredicateAgainstSchema, virtualClock };
2241
+ export { Backoff, type Branded, type ChainableSchemaType, type Change, type ChangeKind, type CheckAgainstResult, type CheckOwnsFinding, type CheckOwnsResult, ClauseResult, type ConstraintDiff, type ConstraintStatus, type Contradiction, type ContradictionType, CreateSystemOptionsNamed, CreateSystemOptionsSingle, CrossModuleDeps, DefinitionMeta, type DescribeOptions, type DiffRulesOptions, ErrorBoundaryConfig, type ExistingConstraint, type ExtendedSchemaType, FactPredicate, Facts, type LeafClause, MAX_REPLAY_FRAMES, MAX_SWEEP_POINTS, type ModuleConfig, type ModuleConfigWithDeps, ModuleDef, ModuleHooks, ModuleSchema, ModulesMap, NamespacedSystem, Plugin, type PredicateBacktestReport, type PredicateToMongoOptions, type PredicateToPostgrestOptions, type PredicateToSqlOptions, type PredicateToSqlResult, type PredictMissingChange, type PredictResult, type ReplayDiffSample, type ReplayFrame, type ReplayUnderOptions, Requirement, RequirementSet, type RequirementTypeStatus, RequirementWithId, type RulesDiffReport, type RulesMapInput, SchemaKindNode, SchemaType, type SignalClock, SingleModuleSystem, type SweepHole, type SweepPoint, type SweepReport, type SweepUnderOptions, type TimerFactOpts, type TimerFactState, TraceOption, completeTimer, createModule, createModuleFactory, createRequirementStatusPlugin, createStatusHook, createSystem, createSystemWithStatus, defaultClock, describePredicate, diffClauses, diffRules, doctor, elapsedMs, flattenPredicate, forType, framesFromHistory, framesFromSnapshots, generateRequirementId, initialTimerState, isRequirementType, pauseTimer, predicateHash, predicateToMongo, predicateToPostgrest, predicateToSQL, predicateToWhere, predict, realClock, registerRepeat, remainingMs, replayUnder, req, resetTimer, resumeTimer, startTimer, sweepUnder, t, tickTimer, timerOps, toReplayFrames, toRulesMap, virtualClock };