@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.
- package/dist/adapter-utils.cjs +1 -1
- package/dist/adapter-utils.d.cts +2 -2
- package/dist/adapter-utils.d.ts +2 -2
- package/dist/adapter-utils.js +1 -1
- package/dist/adapter-utils.js.map +1 -1
- package/dist/audit-ledger-Dc6hAXam.d.cts +378 -0
- package/dist/audit-ledger-dxvslGi3.d.ts +378 -0
- package/dist/chunk-2FF6QGOA.js +2 -0
- package/dist/chunk-2FF6QGOA.js.map +1 -0
- package/dist/chunk-4MNQDXH7.cjs +3 -0
- package/dist/chunk-4MNQDXH7.cjs.map +1 -0
- package/dist/chunk-644QZVTT.js +16 -0
- package/dist/{chunk-26Z5VNPZ.js.map → chunk-644QZVTT.js.map} +1 -1
- package/dist/chunk-ENZEHIL7.cjs +3 -0
- package/dist/chunk-ENZEHIL7.cjs.map +1 -0
- package/dist/chunk-I722BZA5.js +7 -0
- package/dist/chunk-I722BZA5.js.map +1 -0
- package/dist/chunk-IXRS4LM4.cjs +2 -0
- package/dist/chunk-IXRS4LM4.cjs.map +1 -0
- package/dist/chunk-NPX5EKPP.cjs +16 -0
- package/dist/{chunk-EX3XG667.cjs.map → chunk-NPX5EKPP.cjs.map} +1 -1
- package/dist/chunk-PA6VC32N.js +2 -0
- package/dist/chunk-PA6VC32N.js.map +1 -0
- package/dist/chunk-PXRV64PA.js +3 -0
- package/dist/chunk-PXRV64PA.js.map +1 -0
- package/dist/chunk-R2GHSCTR.js +3 -0
- package/dist/chunk-R2GHSCTR.js.map +1 -0
- package/dist/chunk-T4TRJEJN.cjs +2 -0
- package/dist/chunk-T4TRJEJN.cjs.map +1 -0
- package/dist/chunk-X7G7UBXU.cjs +7 -0
- package/dist/chunk-X7G7UBXU.cjs.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +214 -391
- package/dist/index.d.ts +214 -391
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +1 -1
- package/dist/internals.d.cts +5 -5
- package/dist/internals.d.ts +5 -5
- package/dist/internals.js +1 -1
- package/dist/plugins/index.cjs +2 -2
- package/dist/plugins/index.cjs.map +1 -1
- package/dist/plugins/index.d.cts +2 -2
- package/dist/plugins/index.d.ts +2 -2
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/{plugins-Ykl_sAPE.d.ts → plugins-BIzXaYbg.d.cts} +15 -1
- package/dist/{plugins-Ykl_sAPE.d.cts → plugins-BIzXaYbg.d.ts} +15 -1
- package/dist/predicate-Bnx3LN7P.d.cts +655 -0
- package/dist/predicate-BxQVf0ug.d.ts +655 -0
- package/dist/system-A6VYKLVF.js +2 -0
- package/dist/{system-VZWB6WXX.js.map → system-A6VYKLVF.js.map} +1 -1
- package/dist/system-CDJMD5O5.cjs +2 -0
- package/dist/{system-GK3NSFQH.cjs.map → system-CDJMD5O5.cjs.map} +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/{utils-BnQajqPu.d.cts → utils-Mg55IerF.d.cts} +27 -1
- package/dist/{utils-BnQajqPu.d.ts → utils-Mg55IerF.d.ts} +27 -1
- package/dist/worker.cjs +1 -1
- package/dist/worker.d.cts +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +1 -1
- package/package.json +1 -1
- package/dist/audit-ledger-9IElAHH9.d.ts +0 -205
- package/dist/audit-ledger-qMjEBqiP.d.cts +0 -205
- package/dist/chunk-26Z5VNPZ.js +0 -16
- package/dist/chunk-4VZOZWXM.cjs +0 -2
- package/dist/chunk-4VZOZWXM.cjs.map +0 -1
- package/dist/chunk-7NMXRATK.cjs +0 -3
- package/dist/chunk-7NMXRATK.cjs.map +0 -1
- package/dist/chunk-7TSYQEN3.js +0 -2
- package/dist/chunk-7TSYQEN3.js.map +0 -1
- package/dist/chunk-EOLY64E6.cjs +0 -3
- package/dist/chunk-EOLY64E6.cjs.map +0 -1
- package/dist/chunk-EX3XG667.cjs +0 -16
- package/dist/chunk-N4KTCKOI.cjs +0 -7
- package/dist/chunk-N4KTCKOI.cjs.map +0 -1
- package/dist/chunk-T6IJUWYR.js +0 -3
- package/dist/chunk-T6IJUWYR.js.map +0 -1
- package/dist/chunk-TPOKS4RY.js +0 -3
- package/dist/chunk-TPOKS4RY.js.map +0 -1
- package/dist/chunk-TZHC4E6S.js +0 -7
- package/dist/chunk-TZHC4E6S.js.map +0 -1
- package/dist/helpers-D2pfb6vT.d.ts +0 -235
- package/dist/helpers-hh6UanB1.d.cts +0 -235
- package/dist/system-GK3NSFQH.cjs +0 -2
- 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 };
|