@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
@@ -0,0 +1,655 @@
1
+ import { a8 as Schema, a as Facts, F as FactPredicate, aS as InferSchema, y as FactTemplate, D as DefinitionMeta, q as Requirement, b9 as RequirementOutput, a7 as RetryPolicy, B as BatchConfig, b0 as ResolverContext, a6 as PredicateOp, $ as PatchSpec, C as ClauseResult } from './plugins-BIzXaYbg.cjs';
2
+
3
+ /**
4
+ * Derivation Types - Type definitions for derivations
5
+ */
6
+
7
+ /** Derivation definition function signature. */
8
+ interface DerivationDef<S extends Schema, T, D extends DerivationsDef<S>> {
9
+ (facts: Facts<S>, derived: DerivedValues<S, D>): T;
10
+ }
11
+ /**
12
+ * Derivation definition with metadata (object form).
13
+ * Use this when you want to attach debugging metadata to a derivation.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * derive: {
18
+ * displayName: {
19
+ * compute: (facts) => `${facts.firstName} ${facts.lastName}`,
20
+ * meta: { label: "Display Name", description: "Full name for UI" },
21
+ * },
22
+ * },
23
+ * ```
24
+ */
25
+ interface DerivationDefWithMeta<S extends Schema, T, D extends DerivationsDef<S>> {
26
+ /**
27
+ * The derivation body. Either:
28
+ * - a function `(facts, derived) => T` (original form), or
29
+ * - a {@link FactPredicate} data spec — boolean derivations only, or
30
+ * - a {@link FactTemplate} `{ $template: "..." }` — string derivations only.
31
+ *
32
+ * Data forms are normalized to a wrapper function at registration; the
33
+ * wrapper reads through the facts proxy so existing auto-tracking
34
+ * captures dependencies.
35
+ */
36
+ compute: DerivationDef<S, T, D> | ([T] extends [boolean] ? FactPredicate<InferSchema<S>> : never) | ([T] extends [string] ? FactTemplate : never);
37
+ meta?: DefinitionMeta;
38
+ }
39
+ /** Map of derivation definitions (internal — always bare functions after unwrap). */
40
+ type DerivationsDef<S extends Schema> = Record<string, DerivationDef<S, unknown, DerivationsDef<S>>>;
41
+ /** Computed derived values. */
42
+ type DerivedValues<S extends Schema, D extends DerivationsDef<S>> = {
43
+ readonly [K in keyof D]: ReturnType<D[K]>;
44
+ };
45
+ /** Internal derivation state */
46
+ interface DerivationState<T> {
47
+ id: string;
48
+ compute: () => T;
49
+ cachedValue: T | undefined;
50
+ dependencies: Set<string>;
51
+ isStale: boolean;
52
+ isComputing: boolean;
53
+ /** Consecutive runs producing the same deps (auto-tracked only) */
54
+ stableRunCount: number;
55
+ /** Once true, skip withTracking() overhead until a tracked fact mutates */
56
+ depsStable: boolean;
57
+ }
58
+
59
+ /**
60
+ * Type Helpers - External typed constraint and resolver definitions
61
+ *
62
+ * These types enable defining constraints and resolvers with full type safety
63
+ * outside of module definitions, while maintaining proper type inference.
64
+ */
65
+
66
+ /**
67
+ * External constraint definition with full typing.
68
+ * Use this when defining constraints outside of createModule().
69
+ *
70
+ * @typeParam S - The schema type
71
+ * @typeParam R - The requirement type (defaults to Requirement)
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * // Define a typed constraint factory
76
+ * const createMaxCountConstraint = <S extends Schema>(
77
+ * maxCount: number
78
+ * ): TypedConstraint<S, { type: "RESET_COUNT" }> => ({
79
+ * priority: 10,
80
+ * when: (facts) => (facts as { count: number }).count > maxCount,
81
+ * require: { type: "RESET_COUNT" },
82
+ * });
83
+ *
84
+ * // Use in module
85
+ * const module = createModule("counter", {
86
+ * schema: { count: t.number() },
87
+ * constraints: {
88
+ * maxCount: createMaxCountConstraint(100),
89
+ * },
90
+ * });
91
+ * ```
92
+ */
93
+ interface TypedConstraint<S extends Schema, R extends Requirement = Requirement> {
94
+ /** Priority for ordering (higher runs first) */
95
+ priority?: number;
96
+ /** Mark this constraint as async (avoids runtime detection) */
97
+ async?: boolean;
98
+ /** Condition function (sync or async) */
99
+ when: (facts: Facts<S>) => boolean | Promise<boolean>;
100
+ /**
101
+ * Requirement(s) to produce when condition is met.
102
+ */
103
+ require: RequirementOutput<R> | ((facts: Facts<S>) => RequirementOutput<R>);
104
+ /** Timeout for async constraints (ms) */
105
+ timeout?: number;
106
+ /**
107
+ * Constraint IDs whose resolvers must complete before this constraint is evaluated.
108
+ * - If dependency's `when()` returns false, this constraint proceeds (nothing to wait for)
109
+ * - If dependency's resolver fails, this constraint remains blocked until it succeeds
110
+ * - Cross-module: use the constraint ID as it appears in the merged system
111
+ */
112
+ after?: string[];
113
+ }
114
+ /**
115
+ * External resolver definition with full typing.
116
+ * Use this when defining resolvers outside of createModule().
117
+ *
118
+ * @typeParam S - The schema type
119
+ * @typeParam R - The requirement type (defaults to Requirement)
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // Define a typed resolver factory
124
+ * interface FetchUserReq extends Requirement {
125
+ * type: "FETCH_USER";
126
+ * userId: string;
127
+ * }
128
+ *
129
+ * const createFetchUserResolver = <S extends Schema>(
130
+ * fetchFn: (userId: string) => Promise<User>
131
+ * ): TypedResolver<S, FetchUserReq> => ({
132
+ * requirement: (req): req is FetchUserReq => req.type === "FETCH_USER",
133
+ * key: (req) => `fetch-user-${req.userId}`,
134
+ * retry: { attempts: 3, backoff: "exponential" },
135
+ * resolve: async (req, ctx) => {
136
+ * const user = await fetchFn(req.userId);
137
+ * (ctx.facts as { user: User }).user = user;
138
+ * },
139
+ * });
140
+ * ```
141
+ */
142
+ interface TypedResolver<S extends Schema, R extends Requirement = Requirement> {
143
+ /**
144
+ * Requirement type to handle.
145
+ * - String: matches `req.type` directly (e.g., `requirement: "FETCH_USER"`)
146
+ * - Function: type guard predicate (e.g., `requirement: (req) => req.type === "FETCH_USER"`)
147
+ */
148
+ requirement: R["type"] | ((req: Requirement) => req is R);
149
+ /** Custom key function for deduplication */
150
+ key?: (req: R) => string;
151
+ /** Retry policy */
152
+ retry?: RetryPolicy;
153
+ /** Timeout for resolver execution (ms) */
154
+ timeout?: number;
155
+ /** Batch configuration */
156
+ batch?: BatchConfig;
157
+ /** Resolve function for single requirement */
158
+ resolve?: (req: R, ctx: ResolverContext<S>) => Promise<void>;
159
+ /** Resolve function for batched requirements */
160
+ resolveBatch?: (reqs: R[], ctx: ResolverContext<S>) => Promise<void>;
161
+ }
162
+ /**
163
+ * Create a typed constraint factory for a specific schema.
164
+ * This enables creating reusable constraint definitions with proper typing.
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const schema = { count: t.number(), threshold: t.number() };
169
+ * const factory = createConstraintFactory<typeof schema>();
170
+ *
171
+ * const maxCountConstraint = factory.create({
172
+ * when: (facts) => facts.count > facts.threshold,
173
+ * require: { type: "RESET" },
174
+ * });
175
+ * ```
176
+ */
177
+ declare function createConstraintFactory<S extends Schema>(): {
178
+ /**
179
+ * Create a typed constraint
180
+ */
181
+ create<R extends Requirement = Requirement>(constraint: TypedConstraint<S, R>): TypedConstraint<S, R>;
182
+ };
183
+ /**
184
+ * Create a typed resolver factory for a specific schema.
185
+ * This enables creating reusable resolver definitions with proper typing.
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const schema = { user: t.object<User>() };
190
+ * const factory = createResolverFactory<typeof schema>();
191
+ *
192
+ * const fetchUserResolver = factory.create<FetchUserReq>({
193
+ * requirement: (req): req is FetchUserReq => req.type === "FETCH_USER",
194
+ * resolve: async (req, ctx) => {
195
+ * ctx.facts.user = await fetchUser(req.userId);
196
+ * },
197
+ * });
198
+ * ```
199
+ */
200
+ declare function createResolverFactory<S extends Schema>(): {
201
+ /**
202
+ * Create a typed resolver
203
+ */
204
+ create<R extends Requirement = Requirement>(resolver: TypedResolver<S, R>): TypedResolver<S, R>;
205
+ };
206
+ /**
207
+ * Type-safe constraint creator.
208
+ * Simpler alternative to createConstraintFactory when you don't need a factory pattern.
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * const constraint = typedConstraint<typeof schema, { type: "RESET" }>({
213
+ * when: (facts) => facts.count > 100,
214
+ * require: { type: "RESET" },
215
+ * });
216
+ * ```
217
+ */
218
+ declare function typedConstraint<S extends Schema, R extends Requirement = Requirement>(constraint: TypedConstraint<S, R>): TypedConstraint<S, R>;
219
+ /**
220
+ * Type-safe resolver creator.
221
+ * Simpler alternative to createResolverFactory when you don't need a factory pattern.
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const resolver = typedResolver<typeof schema, FetchUserReq>({
226
+ * requirement: (req): req is FetchUserReq => req.type === "FETCH_USER",
227
+ * resolve: async (req, ctx) => {
228
+ * ctx.facts.user = await fetchUser(req.userId);
229
+ * },
230
+ * });
231
+ * ```
232
+ */
233
+ declare function typedResolver<S extends Schema, R extends Requirement = Requirement>(resolver: TypedResolver<S, R>): TypedResolver<S, R>;
234
+
235
+ /**
236
+ * Schema Introspection
237
+ *
238
+ * A runtime discriminant for every `t.*()` builder result, so downstream
239
+ * consumers (`predicateFromIntent`, `doctor`, future `predicateToZod`)
240
+ * can ask "what kind is this fact?" without grepping the source.
241
+ *
242
+ * The discriminant lives at `_kind?: SchemaKindNode` on `ExtendedSchemaType`.
243
+ * Base builders set it; chain mutators (`.nullable()`, `.brand()`, etc.)
244
+ * preserve / decorate it. When `_kind` is absent (legacy or third-party
245
+ * builder), the parser falls back to reading the freeform `_typeName`
246
+ * string. When even that fails: `{ kind: "unknown" }` — graceful close.
247
+ *
248
+ * This module has zero hot-path cost; it is only invoked when an
249
+ * introspecting caller asks for it.
250
+ */
251
+
252
+ /**
253
+ * The closed set of kinds a Directive schema field can be.
254
+ *
255
+ * Drives operator availability via {@link getOperatorsForKind}: e.g.
256
+ * `"number"` gets the orderable operators (`$gte`, `$lte`); `"boolean"`
257
+ * does not.
258
+ */
259
+ type SchemaKind = "number" | "string" | "boolean" | "bigint" | "date" | "array" | "object" | "record" | "tuple" | "enum" | "literal" | "union" | "branded" | "unknown";
260
+ /**
261
+ * A tree-shaped discriminator for a schema field. Composite kinds
262
+ * (array, object, tuple, etc.) carry their element / shape information
263
+ * so an LLM-prompt builder can show "cartTotal is a number" AND
264
+ * "items is an array of { sku: string, qty: number }".
265
+ *
266
+ * `nullable` / `hasDefault` flags appear on the inner node (NOT a
267
+ * wrapping kind) so operator-lookup on `t.number().nullable()` returns
268
+ * the number's operators unchanged — `$gte` works on the non-null arm.
269
+ */
270
+ type SchemaKindNode = ({
271
+ kind: "number" | "string" | "boolean" | "bigint" | "date" | "unknown";
272
+ } | {
273
+ kind: "literal";
274
+ value: string | number | boolean | null;
275
+ primitive: "string" | "number" | "boolean" | "null";
276
+ } | {
277
+ kind: "enum";
278
+ values: readonly (string | number)[];
279
+ primitive: "string" | "number";
280
+ } | {
281
+ kind: "array";
282
+ element: SchemaKindNode;
283
+ } | {
284
+ kind: "tuple";
285
+ elements: readonly SchemaKindNode[];
286
+ } | {
287
+ kind: "object";
288
+ shape: Record<string, SchemaKindNode>;
289
+ } | {
290
+ kind: "record";
291
+ value: SchemaKindNode;
292
+ } | {
293
+ kind: "union";
294
+ members: readonly SchemaKindNode[];
295
+ } | {
296
+ kind: "branded";
297
+ inner: SchemaKindNode;
298
+ }) & {
299
+ /** True if the schema accepts `null` (from `.nullable()` / `.optional()`). */
300
+ nullable?: boolean;
301
+ /** True if the schema has a `.default()`. */
302
+ hasDefault?: boolean;
303
+ };
304
+ /**
305
+ * Return the {@link SchemaKindNode} for a schema field. Prefers the
306
+ * explicit `_kind` discriminant set by the builder; falls back to
307
+ * parsing the freeform `_typeName` string for legacy / third-party
308
+ * builders that don't set `_kind`.
309
+ *
310
+ * Hostile-getter safe: `_kind` / `_typeName` reads are wrapped — a builder
311
+ * that throws on property access returns `{ kind: "unknown" }` instead of
312
+ * propagating the throw to introspecting callers.
313
+ *
314
+ * In dev mode, a function input (`typeof schema === "function"`) emits a
315
+ * warning — common foot-gun where the caller forgot to invoke a builder
316
+ * factory (e.g. wrote `t.number` instead of `t.number()`).
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * getKind(t.number()) // → { kind: "number" }
321
+ * getKind(t.string().nullable()) // → { kind: "string", nullable: true }
322
+ * getKind(t.array(t.number())) // → { kind: "array", element: { kind: "number" } }
323
+ * ```
324
+ */
325
+ declare function getKind(schema: unknown): SchemaKindNode;
326
+ /**
327
+ * Walk the `facts` block of a module schema and emit a flat map from
328
+ * dotted path → kind node. Nested `t.object()` shapes flatten using
329
+ * `.` as the separator, matching the convention used by
330
+ * `OperatorObject<V>`'s nested-path support.
331
+ *
332
+ * Passing a top-level schema directly (without the `facts:` wrapper)
333
+ * also works — anything iterable as `Record<string, ExtendedSchemaType>`
334
+ * is acceptable.
335
+ *
336
+ * Hostile-getter safe: a builder whose `_kind` / `_typeName` throws is
337
+ * silently skipped (treated as `{ kind: "unknown" }` for that field)
338
+ * rather than aborting the whole walk.
339
+ *
340
+ * In dev mode, a top-level schema that yields an empty map (no
341
+ * introspectable keys) emits a warning — common foot-gun where the
342
+ * caller passed `myModule` instead of `myModule.schema`.
343
+ */
344
+ declare function getSchemaFieldKinds(schema: unknown): Map<string, SchemaKindNode>;
345
+ /**
346
+ * Return the set of `PredicateOp` strings that are valid against a
347
+ * given {@link SchemaKindNode}.
348
+ *
349
+ * Mirrors the type-level matrix encoded in `OperatorObject<V>` in
350
+ * `types/predicate.ts` — drift between the two is enforced by the
351
+ * compile-time conformance test
352
+ * (`schema-introspection-conformance.test-d.ts`).
353
+ *
354
+ * - **Common to all kinds:** `$eq`, `$ne`, `$in`, `$nin`, `$exists`
355
+ * - **Orderable (`number`/`string`/`bigint`/`date`):** + `$gt`, `$gte`,
356
+ * `$lt`, `$lte`, `$between`
357
+ * - **String-specific:** + `$matches`, `$startsWith`, `$endsWith`, `$contains`
358
+ * - **Array:** + `$contains` (over the element type)
359
+ * - **Union:** *intersection* across members (the operand must be valid
360
+ * for every branch).
361
+ * - **Branded:** delegates to the inner kind.
362
+ * - **Literal / Enum:** operators of the primitive, with `$eq` / `$in`
363
+ * operand restricted at the LLM-prompt layer (not enforced here).
364
+ *
365
+ * `nullable` does not change operator availability — `$gte` on a
366
+ * nullable number is fine on the non-null arm; `$exists` handles null.
367
+ */
368
+ declare function getOperatorsForKind(node: SchemaKindNode): readonly PredicateOp[];
369
+ /**
370
+ * Return all known predicate operators — convenience for prompt builders
371
+ * that need to show the LLM the full set.
372
+ */
373
+ declare function listAllPredicateOperators(): readonly PredicateOp[];
374
+
375
+ /**
376
+ * Runtime for data-configuration predicates and templates.
377
+ *
378
+ * Pure module — imports only its own types. Reads facts through whatever
379
+ * object it is handed (the reactive `Facts` proxy in production, a plain
380
+ * snapshot in tests), so it never depends on the engine, store, or tracking.
381
+ */
382
+
383
+ /** A readable scope — the `Facts` proxy and a plain snapshot both satisfy it. */
384
+ type Scope = Record<string, unknown>;
385
+ /**
386
+ * True when `v` is a data-form spec (predicate object/array) rather than a
387
+ * function. Excludes class instances (Date, RegExp, Map, Set, Promise, etc.)
388
+ * — only plain `{}` literals and arrays of plain clause shapes qualify.
389
+ *
390
+ * @example
391
+ * ```ts
392
+ * isPredicate({ phase: "red" }); // true
393
+ * isPredicate((f) => f.phase === "red"); // false
394
+ * isPredicate([{ fact: "phase", op: "$eq", value: "red" }]); // true
395
+ * ```
396
+ */
397
+ declare function isPredicate(v: unknown): boolean;
398
+ /**
399
+ * True when `v` is a {@link FactTemplate} (`{ $template: string }`).
400
+ *
401
+ * @example
402
+ * ```ts
403
+ * isTemplate({ $template: "Hi ${name}" }); // true
404
+ * isTemplate({ $set: { name: "x" } }); // false
405
+ * ```
406
+ */
407
+ declare function isTemplate(v: unknown): v is FactTemplate;
408
+ /**
409
+ * Throw when a predicate spec contains an operand that cannot survive a
410
+ * JSON round-trip — i.e. that would silently mis-evaluate if the spec was
411
+ * loaded from `JSON.parse`.
412
+ *
413
+ * Three failure classes are detected:
414
+ *
415
+ * - **Lost `RegExp` operand.** A `$matches` operand that is not a
416
+ * `RegExp` instance. `JSON.parse` reconstructs a serialized regex as
417
+ * `{}`, so a `$matches` clause with an empty-object operand is the
418
+ * signature of a regex that did not survive serialization. Reify it
419
+ * with `new RegExp(pattern, flags)` before installing the predicate.
420
+ * - **`bigint` operand.** `JSON.stringify` throws on `bigint`, so a
421
+ * `bigint` operand cannot have been produced by a JSON pipeline and
422
+ * cannot be persisted by one either.
423
+ * - **`Set` / `Map` operand.** Both serialize to `{}` and lose all
424
+ * members; a predicate carrying one is not JSON-safe.
425
+ *
426
+ * This is an opt-in helper — the engine does not call it automatically.
427
+ * Users who load predicates from JSON should call it after `JSON.parse`
428
+ * to fail loud rather than silently mis-evaluate. See the
429
+ * "Serialization" section of RFC-0004.
430
+ *
431
+ * @example
432
+ * ```ts
433
+ * validatePredicate({ phase: { $matches: {} } });
434
+ * // throws — empty object where a RegExp is required
435
+ *
436
+ * validatePredicate({ phase: "red", elapsed: { $gte: 30 } });
437
+ * // ok — JSON-clean operands
438
+ * ```
439
+ */
440
+ declare function validatePredicate(spec: unknown, path?: string): void;
441
+
442
+ interface SchemaValidationError {
443
+ /** Dotted path to the failing clause (e.g. `cartTotal`, `auth.token.$gte`). */
444
+ readonly path: string;
445
+ /** The operator that failed (or the literal-equality marker `$eq`). */
446
+ readonly op: string;
447
+ /** The kind of the fact at this path, or `undefined` when the fact wasn't in the kind map. */
448
+ readonly kind?: SchemaKindNode;
449
+ /** The operators that ARE allowed for this fact's kind. */
450
+ readonly allowedOps?: readonly string[];
451
+ /** Human-readable failure reason (suitable for feeding back to an LLM). */
452
+ readonly reason: string;
453
+ }
454
+ interface SchemaValidationOptions {
455
+ /** Reject predicates with more than this many operator clauses (DoS guard). Default unbounded. */
456
+ readonly maxOperatorCount?: number;
457
+ /**
458
+ * Reject `$in` / `$nin` operands that contain more than this many elements
459
+ * (query-planner DoS guard). Default unbounded. A typical safe cap is 1000
460
+ * — beyond that, a downstream query compiler may degrade quadratically.
461
+ */
462
+ readonly maxArrayOperandLength?: number;
463
+ }
464
+ /**
465
+ * Heuristic: flag a regex source string that has obvious nested quantifiers
466
+ * (e.g. `(.+)+`, `(.*)*`, `(\w+)+`, `(a|a)+`) — the classic ReDoS shapes.
467
+ * Not a full ReDoS prover; intentionally conservative so a string-rehydrated
468
+ * `$matches` operand can be rejected before it ever reaches `RegExp.test`.
469
+ *
470
+ * Callers MUST NOT treat a `false` result as "safe" — a determined adversary
471
+ * can craft patterns this heuristic misses. The right answer for untrusted
472
+ * regex is "don't accept untrusted regex"; this helper exists to catch the
473
+ * obvious foot-guns.
474
+ *
475
+ * @example
476
+ * ```ts
477
+ * dangerousRegex("(a+)+"); // → true
478
+ * dangerousRegex("(.*)*"); // → true
479
+ * dangerousRegex("(\\w+)+"); // → true
480
+ * dangerousRegex("^[a-z]+$"); // → false
481
+ * ```
482
+ */
483
+ declare function dangerousRegex(source: string): boolean;
484
+ type SchemaValidationResult = {
485
+ ok: true;
486
+ operatorCount: number;
487
+ } | {
488
+ ok: false;
489
+ errors: readonly SchemaValidationError[];
490
+ operatorCount: number;
491
+ };
492
+ /**
493
+ * Cross-check an LLM-emitted (or otherwise externally-sourced) predicate
494
+ * against a schema's runtime kind map. Catches errors that
495
+ * {@link validatePredicate} cannot — operator-on-wrong-kind (`$gte` on a
496
+ * boolean fact), unknown fact paths, and (optionally) operator-count
497
+ * exhaustion DoS attempts.
498
+ *
499
+ * Pair with {@link validatePredicate} (structural / JSON safety) for full
500
+ * coverage. Use {@link getSchemaFieldKinds} to derive `kindMap` from a
501
+ * module schema.
502
+ *
503
+ * Designed for the LLM-emit retry loop: returns a list of errors with
504
+ * structured `{path, op, kind, allowedOps, reason}` rather than throwing,
505
+ * so the caller can feed the errors back to the model.
506
+ *
507
+ * @example
508
+ * ```ts
509
+ * const kindMap = getSchemaFieldKinds({ facts: { cartTotal: t.number(), active: t.boolean() } });
510
+ * const result = validatePredicateAgainstSchema(
511
+ * { cartTotal: { $gte: 50 }, active: { $gte: true } },
512
+ * kindMap,
513
+ * );
514
+ * // → { ok: false, errors: [{ path: "active", op: "$gte", reason: "..." }], operatorCount: 2 }
515
+ * ```
516
+ */
517
+ declare function validatePredicateAgainstSchema(spec: unknown, kindMap: Map<string, SchemaKindNode>, opts?: SchemaValidationOptions): SchemaValidationResult;
518
+ /**
519
+ * Evaluate a {@link FactPredicate} against a fact scope. `prev` (a previous
520
+ * snapshot) is consulted only by the `$changed` operator.
521
+ *
522
+ * @example
523
+ * ```ts
524
+ * evaluatePredicate({ phase: "red", elapsed: { $gte: 30 } }, { phase: "red", elapsed: 45 });
525
+ * // → true
526
+ * evaluatePredicate({ $any: [{ phase: "red" }, { phase: "yellow" }] }, { phase: "green" });
527
+ * // → false
528
+ * ```
529
+ */
530
+ declare function evaluatePredicate(spec: unknown, facts: Scope, prev?: Scope, depth?: number): boolean;
531
+ /**
532
+ * Evaluate a predicate and return a per-clause breakdown — the data feed for
533
+ * devtools, `system.explain()`, and `directive explain`.
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * evaluatePredicateExplained(
538
+ * { phase: "red", elapsed: { $gte: 30 } },
539
+ * { phase: "red", elapsed: 20 },
540
+ * );
541
+ * // → [
542
+ * // { path: "phase", op: "$eq", expected: "red", actual: "red", pass: true },
543
+ * // { path: "elapsed", op: "$gte", expected: 30, actual: 20, pass: false },
544
+ * // ]
545
+ * ```
546
+ */
547
+ declare function evaluatePredicateExplained(spec: unknown, facts: Scope, prev?: Scope, pathPrefix?: string): ClauseResult[];
548
+ /**
549
+ * Memoize a predicate as a reusable evaluation closure.
550
+ *
551
+ * The returned function accepts any `facts` scope (the reactive proxy in
552
+ * production, a plain object in tests) plus an optional `prev` snapshot for
553
+ * `$changed`. The closure is cached **by predicate identity** in a
554
+ * `WeakMap`, so passing the same `predicate` reference repeatedly is
555
+ * allocation-free; cleanup is automatic once the predicate is no longer
556
+ * reachable.
557
+ *
558
+ * Note: no actual compilation happens — the returned closure re-walks the
559
+ * spec on every call via `evaluatePredicate`. The name reflects what the
560
+ * function does (closure memoization keyed by predicate identity), not a
561
+ * bytecode/AST compile step.
562
+ *
563
+ * Intended for advanced users who want a stable function reference per
564
+ * predicate (custom devtools, batched analyses). Regular module code does
565
+ * not need to call this — the engine wraps data-form `when` / `on` specs
566
+ * automatically at registration.
567
+ *
568
+ * @example
569
+ * ```ts
570
+ * const predicate = { phase: "red", elapsed: { $gte: 30 } };
571
+ * const check = memoizePredicate(predicate);
572
+ * check({ phase: "red", elapsed: 45 }); // → true
573
+ * check({ phase: "red", elapsed: 5 }); // → false
574
+ * ```
575
+ */
576
+ declare function memoizePredicate(predicate: object): (facts: Scope, prev?: Scope) => boolean;
577
+ /**
578
+ * Collect the fact keys a predicate references. Used for static analysis,
579
+ * devtools, and effect `on` dependency wiring. Nested predicates contribute
580
+ * dotted keys (`auth.token`).
581
+ *
582
+ * @example
583
+ * ```ts
584
+ * extractDeps({ phase: "red", elapsed: { $gte: 30 } });
585
+ * // → Set { "phase", "elapsed" }
586
+ * extractDeps({ self: { phase: "red" }, auth: { token: { $exists: true } } });
587
+ * // → Set { "self.phase", "auth.token" }
588
+ * ```
589
+ */
590
+ declare function extractDeps(spec: unknown, prefix?: string): Set<string>;
591
+ /**
592
+ * Interpolate a {@link FactTemplate} against a scope. Single-pass character
593
+ * scanner: `${ident}` interpolates `scope[ident]`; `$${` emits a literal
594
+ * `${`; unknown keys dev-warn and yield an empty string.
595
+ *
596
+ * @example
597
+ * ```ts
598
+ * evaluateTemplate({ $template: "Hi ${name}!" }, { name: "Ada" });
599
+ * // → "Hi Ada!"
600
+ * evaluateTemplate({ $template: "$${price}" }, {});
601
+ * // → "${price}"
602
+ * ```
603
+ */
604
+ declare function evaluateTemplate(spec: FactTemplate, scope: Scope): string;
605
+ /**
606
+ * Collect the placeholder keys referenced by a template. The static-analysis
607
+ * counterpart to {@link extractDeps} — useful for devtools, codegen, and
608
+ * "which facts does this template read" inspections. Only valid identifier
609
+ * placeholders are collected; malformed ones are ignored.
610
+ *
611
+ * @example
612
+ * ```ts
613
+ * extractTemplateKeys({ $template: "${firstName} ${lastName}" });
614
+ * // → Set { "firstName", "lastName" }
615
+ * extractTemplateKeys({ $template: "$${literal}" });
616
+ * // → Set {} (escaped — not a placeholder)
617
+ * ```
618
+ */
619
+ declare function extractTemplateKeys(spec: FactTemplate): Set<string>;
620
+ /**
621
+ * Build a stable dedup key by selecting fields from a requirement payload.
622
+ * Order-as-declared; values are stable-stringified (keys sorted recursively)
623
+ * so two payloads with the same fields in different orders dedupe to the
624
+ * same key.
625
+ *
626
+ * @example
627
+ * ```ts
628
+ * evaluateKeySelector(["url", "method"], { url: "/a", method: "GET" });
629
+ * // → '"/a"|"GET"'
630
+ * evaluateKeySelector(["id"], { id: 42 });
631
+ * // → '42'
632
+ * ```
633
+ */
634
+ declare function evaluateKeySelector(selector: readonly string[], source: Record<string, unknown>): string;
635
+ /**
636
+ * Apply a {@link PatchSpec} — assign facts from literals, payload copies
637
+ * (`$ref`), or interpolated strings (`$template`). Mutates through the passed
638
+ * `facts` proxy so change-tracking and downstream invalidation fire.
639
+ *
640
+ * @example
641
+ * ```ts
642
+ * const spec = {
643
+ * $set: {
644
+ * active: true,
645
+ * userId: { $ref: "id" },
646
+ * label: { $template: "user ${name}" },
647
+ * },
648
+ * };
649
+ * applyPatch(spec, facts, { id: "u_1", name: "Ada" });
650
+ * // facts.active = true; facts.userId = "u_1"; facts.label = "user Ada"
651
+ * ```
652
+ */
653
+ declare function applyPatch(spec: PatchSpec<Record<string, unknown>, Record<string, unknown>>, facts: Record<string, unknown>, payload: Record<string, unknown>): void;
654
+
655
+ export { createConstraintFactory as A, createResolverFactory as B, dangerousRegex as C, type DerivationDefWithMeta as D, type SchemaKindNode as S, type TypedConstraint as T, type SchemaKind as a, type SchemaValidationError as b, type SchemaValidationOptions as c, type SchemaValidationResult as d, applyPatch as e, evaluateKeySelector as f, evaluatePredicate as g, evaluatePredicateExplained as h, evaluateTemplate as i, extractDeps as j, extractTemplateKeys as k, getKind as l, getOperatorsForKind as m, getSchemaFieldKinds as n, isPredicate as o, isTemplate as p, listAllPredicateOperators as q, memoizePredicate as r, typedResolver as s, typedConstraint as t, validatePredicateAgainstSchema as u, validatePredicate as v, type DerivationsDef as w, type DerivedValues as x, type DerivationState as y, type TypedResolver as z };