@directive-run/core 1.4.0 → 1.5.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 (71) hide show
  1. package/dist/adapter-utils.cjs +1 -1
  2. package/dist/adapter-utils.d.cts +1 -1
  3. package/dist/adapter-utils.d.ts +1 -1
  4. package/dist/adapter-utils.js +1 -1
  5. package/dist/{chunk-FK7BD7XT.js → chunk-EH2Q754B.js} +3 -3
  6. package/dist/chunk-EH2Q754B.js.map +1 -0
  7. package/dist/chunk-HAF5JCET.js +16 -0
  8. package/dist/chunk-HAF5JCET.js.map +1 -0
  9. package/dist/{chunk-4CMO5OVZ.js → chunk-M5KZXNZX.js} +2 -2
  10. package/dist/chunk-M5KZXNZX.js.map +1 -0
  11. package/dist/chunk-PGUTGWUI.cjs +3 -0
  12. package/dist/chunk-PGUTGWUI.cjs.map +1 -0
  13. package/dist/{chunk-DDUARSUH.cjs → chunk-S3CFYDIB.cjs} +3 -3
  14. package/dist/chunk-S3CFYDIB.cjs.map +1 -0
  15. package/dist/chunk-SQVKCJHE.cjs +16 -0
  16. package/dist/chunk-SQVKCJHE.cjs.map +1 -0
  17. package/dist/chunk-YCCQ73C6.js +3 -0
  18. package/dist/chunk-YCCQ73C6.js.map +1 -0
  19. package/dist/{chunk-BEJ6ICA7.cjs → chunk-ZHS3EW2Z.cjs} +2 -2
  20. package/dist/chunk-ZHS3EW2Z.cjs.map +1 -0
  21. package/dist/{helpers-D6LcRum7.d.ts → helpers-B1MiHave.d.cts} +12 -2
  22. package/dist/{helpers-BUY1lYCX.d.cts → helpers-h9PR2JSJ.d.ts} +12 -2
  23. package/dist/index.cjs +1 -1
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +183 -5
  26. package/dist/index.d.ts +183 -5
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/internals.cjs +1 -1
  30. package/dist/internals.d.cts +49 -39
  31. package/dist/internals.d.ts +49 -39
  32. package/dist/internals.js +1 -1
  33. package/dist/plugins/index.cjs +1 -1
  34. package/dist/plugins/index.cjs.map +1 -1
  35. package/dist/plugins/index.d.cts +11 -8
  36. package/dist/plugins/index.d.ts +11 -8
  37. package/dist/plugins/index.js +1 -1
  38. package/dist/plugins/index.js.map +1 -1
  39. package/dist/{plugins-Dy1C8GtT.d.cts → plugins-Bakr7js6.d.cts} +369 -69
  40. package/dist/{plugins-Dy1C8GtT.d.ts → plugins-Bakr7js6.d.ts} +369 -69
  41. package/dist/system-744ZPPES.js +2 -0
  42. package/dist/{system-JIO36ALC.js.map → system-744ZPPES.js.map} +1 -1
  43. package/dist/system-CK3SHMXZ.cjs +2 -0
  44. package/dist/{system-2THXJBFM.cjs.map → system-CK3SHMXZ.cjs.map} +1 -1
  45. package/dist/testing.cjs +1 -1
  46. package/dist/testing.cjs.map +1 -1
  47. package/dist/testing.d.cts +1 -1
  48. package/dist/testing.d.ts +1 -1
  49. package/dist/testing.js +1 -1
  50. package/dist/testing.js.map +1 -1
  51. package/dist/worker.cjs +1 -1
  52. package/dist/worker.cjs.map +1 -1
  53. package/dist/worker.d.cts +1 -1
  54. package/dist/worker.d.ts +1 -1
  55. package/dist/worker.js +1 -1
  56. package/dist/worker.js.map +1 -1
  57. package/package.json +1 -1
  58. package/dist/chunk-4CMO5OVZ.js.map +0 -1
  59. package/dist/chunk-BEJ6ICA7.cjs.map +0 -1
  60. package/dist/chunk-DDUARSUH.cjs.map +0 -1
  61. package/dist/chunk-E2WETPLH.js +0 -3
  62. package/dist/chunk-E2WETPLH.js.map +0 -1
  63. package/dist/chunk-FK7BD7XT.js.map +0 -1
  64. package/dist/chunk-LFMRWCIG.js +0 -16
  65. package/dist/chunk-LFMRWCIG.js.map +0 -1
  66. package/dist/chunk-TUS5WDVE.cjs +0 -3
  67. package/dist/chunk-TUS5WDVE.cjs.map +0 -1
  68. package/dist/chunk-VSHSYVSY.cjs +0 -16
  69. package/dist/chunk-VSHSYVSY.cjs.map +0 -1
  70. package/dist/system-2THXJBFM.cjs +0 -2
  71. package/dist/system-JIO36ALC.js +0 -2
@@ -226,6 +226,260 @@ interface DefinitionMeta {
226
226
  [key: string]: unknown;
227
227
  }
228
228
 
229
+ /**
230
+ * Data-configuration predicates and templates.
231
+ *
232
+ * A {@link FactPredicate} is a declarative, serializable boolean spec over a
233
+ * module's fact + derivation namespace — the data form of a constraint
234
+ * `when`, an effect `on`, or a boolean derivation. A {@link FactTemplate} is
235
+ * the value-producing counterpart: a fact-interpolating string.
236
+ *
237
+ * Convention: `$` marks an operator/expression node inside a predicate or
238
+ * template body (`$eq`, `$gte`, `$all`, `$template`, `$set`, `$ref`).
239
+ * Definition arms (`compute`, `handler`, `patch`) stay sigil-free.
240
+ *
241
+ * Operators are `$`-prefixed so they can never collide with a fact key —
242
+ * schema keys starting with `$` are rejected at registration.
243
+ */
244
+ /** Comparison operator names — the `$`-prefixed keys inside an operator object. */
245
+ type PredicateOp = "$eq" | "$ne" | "$in" | "$nin" | "$exists" | "$gt" | "$gte" | "$lt" | "$lte" | "$between" | "$matches" | "$startsWith" | "$endsWith" | "$contains" | "$changed";
246
+ /** Combinator node keys. */
247
+ type PredicateCombinatorKey = "$all" | "$any" | "$not";
248
+ /**
249
+ * `true` when `V` supports relational operators (`$gt` … `$between`).
250
+ * `[V]` tuple-wrapping suppresses distribution over union-typed facts.
251
+ */
252
+ type IsOrderable<V> = [V] extends [number | bigint | Date] ? true : [V] extends [string] ? true : false;
253
+ /**
254
+ * The operator object permitted for a fact of type `V`. Built as a
255
+ * **per-operator union** (one operator per member) rather than an
256
+ * intersection — a typo'd operator (`$eqq`) then matches no member and is a
257
+ * compile error, and a relational operator on a non-orderable fact resolves
258
+ * to `never`.
259
+ *
260
+ * One operator per object — for two operators on the same fact, write the
261
+ * array form or `$all`. This is by design (the type is the source of truth).
262
+ *
263
+ * `$matches` accepts a `RegExp` only — string operands cannot express flags
264
+ * (case-insensitivity, dotall, etc.), so the type rejects them. Pass a real
265
+ * `RegExp` instance for flag control. (The runtime still accepts a string
266
+ * operand for one cycle for back-compat, but emits a dev-warn.)
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * const op1: OperatorObject<number> = { $gte: 30 };
271
+ * const op2: OperatorObject<string> = { $matches: /^J/i };
272
+ * const op3: OperatorObject<string> = { $in: ["red", "yellow"] };
273
+ * const op4: OperatorObject<string> = { $startsWith: "Ada" };
274
+ * const op5: OperatorObject<string> = { $endsWith: ".com" };
275
+ * ```
276
+ */
277
+ type OperatorObject<V> = {
278
+ $eq: V;
279
+ } | {
280
+ $ne: V;
281
+ } | {
282
+ $in: readonly V[];
283
+ } | {
284
+ $nin: readonly V[];
285
+ } | {
286
+ $exists: boolean;
287
+ } | {
288
+ $changed: true;
289
+ } | (IsOrderable<V> extends true ? {
290
+ $gt: V;
291
+ } | {
292
+ $gte: V;
293
+ } | {
294
+ $lt: V;
295
+ } | {
296
+ $lte: V;
297
+ } | {
298
+ $between: readonly [V, V];
299
+ } : never) | ([V] extends [string] ? {
300
+ $matches: RegExp;
301
+ } | {
302
+ $contains: string;
303
+ } | {
304
+ $startsWith: string;
305
+ } | {
306
+ $endsWith: string;
307
+ } : never) | ([V] extends [readonly (infer E)[]] ? {
308
+ $contains: E;
309
+ } : never);
310
+ /**
311
+ * The spec for a single fact key: a bare value (equality), an operator
312
+ * object, or — for an object-typed fact — a nested predicate (partial match).
313
+ */
314
+ type PredicateField<V> = V | OperatorObject<V> | ([V] extends [readonly unknown[]] ? never : [V] extends [object] ? PredicateObject<V> : never);
315
+ /**
316
+ * Object form — every key is a fact/derivation name, every value a
317
+ * {@link PredicateField}. Multiple keys are AND-ed. A nested object value
318
+ * recurses (partial match), which is how cross-module namespaced predicates
319
+ * (`{ self: { phase: "red" }, auth: { token: { $exists: true } } }`) work.
320
+ */
321
+ type PredicateObject<F> = {
322
+ [K in keyof F]?: PredicateField<F[K]>;
323
+ };
324
+ /** Array form — explicit clauses, AND-ed. The codegen/devtools-friendly form. */
325
+ type PredicateClause<F> = {
326
+ [K in keyof F]: {
327
+ readonly fact: K;
328
+ readonly op: PredicateOp;
329
+ readonly value: unknown;
330
+ };
331
+ }[keyof F];
332
+ /** Combinator node — exactly one of `$all` / `$any` / `$not`. */
333
+ type PredicateCombinator<F> = {
334
+ $all: readonly FactPredicate<F>[];
335
+ $any?: never;
336
+ $not?: never;
337
+ } | {
338
+ $any: readonly FactPredicate<F>[];
339
+ $all?: never;
340
+ $not?: never;
341
+ } | {
342
+ $not: FactPredicate<F>;
343
+ $all?: never;
344
+ $any?: never;
345
+ };
346
+ /**
347
+ * A declarative boolean spec over a fact namespace `F`. The data form of a
348
+ * constraint `when`, an effect `on`, or a boolean derivation. Accepts an
349
+ * object form, an array-of-clauses form, or a combinator node.
350
+ *
351
+ * Keys are **fact names only** — derivations are not addressable from inside
352
+ * a predicate. To gate on a derivation, either reference the underlying fact
353
+ * the derivation reads, or fall back to the function form of `when` / `on`.
354
+ *
355
+ * @example
356
+ * ```ts
357
+ * // Object form (the common case)
358
+ * const p1: FactPredicate<{ phase: string; elapsed: number }> = {
359
+ * phase: "red",
360
+ * elapsed: { $gte: 30 },
361
+ * };
362
+ *
363
+ * // Combinator form
364
+ * const p2: FactPredicate<{ phase: string }> = {
365
+ * $any: [{ phase: "red" }, { phase: "yellow" }],
366
+ * };
367
+ * ```
368
+ */
369
+ type FactPredicate<F> = PredicateObject<F> | readonly PredicateClause<F>[] | PredicateCombinator<F>;
370
+ /**
371
+ * A fact-interpolating string expression. `${key}` placeholders are replaced
372
+ * with the named fact's value; `$${` emits a literal `${`. The value-producing
373
+ * counterpart to {@link FactPredicate} — usable as a string derivation, a
374
+ * constraint `require` field value, or an event `patch` value.
375
+ *
376
+ * @example { $template: "Phase ${phase} for ${elapsed}s" }
377
+ */
378
+ interface FactTemplate {
379
+ readonly $template: string;
380
+ }
381
+ /**
382
+ * A resolver dedup key written as data: an ordered list of requirement-payload
383
+ * field names. `key: ["type", "to"]` dedupes requirements by those fields.
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * resolvers: {
388
+ * fetch: {
389
+ * requirement: "FETCH",
390
+ * key: ["url", "method"] satisfies KeySelector<{ url: string; method: string }>,
391
+ * resolve: doFetch,
392
+ * },
393
+ * }
394
+ * ```
395
+ */
396
+ type KeySelector<R> = readonly (keyof R & string)[];
397
+ /**
398
+ * A typed single-field copy from an event payload. Lives in the patch-spec
399
+ * namespace — used inside a {@link PatchSpec} `$set` value.
400
+ *
401
+ * @example
402
+ * ```ts
403
+ * patch: { $set: { userId: { $ref: "id" } satisfies PayloadRef<{ id: string }> } }
404
+ * ```
405
+ */
406
+ interface PayloadRef<P> {
407
+ readonly $ref: keyof P & string;
408
+ }
409
+ /**
410
+ * A patch value: a literal, a typed payload copy, or (for string facts) a
411
+ * template. Lives in the patch-spec namespace — used inside a {@link PatchSpec}
412
+ * `$set` block.
413
+ *
414
+ * @example
415
+ * ```ts
416
+ * const v1: PatchValue<boolean, { active: boolean }> = true;
417
+ * const v2: PatchValue<string, { name: string }> = { $ref: "name" };
418
+ * const v3: PatchValue<string, { name: string }> = { $template: "user ${name}" };
419
+ * ```
420
+ */
421
+ type PatchValue<V, P> = V | PayloadRef<P> | ([V] extends [string] ? FactTemplate : never);
422
+ /**
423
+ * An event handler written as data: assigns facts from literals, payload
424
+ * fields (`$ref`), or interpolated strings (`$template`).
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * const spec: PatchSpec<{ status: string; label: string }, { name: string }> = {
429
+ * $set: {
430
+ * status: "active",
431
+ * label: { $template: "user ${name}" },
432
+ * },
433
+ * };
434
+ * ```
435
+ */
436
+ interface PatchSpec<F, P> {
437
+ readonly $set: {
438
+ [K in keyof F]?: PatchValue<F[K], P>;
439
+ };
440
+ }
441
+ /**
442
+ * The per-clause result of an explained predicate evaluation. One entry per
443
+ * leaf operator (`$eq`, `$gte`, …); combinator nodes (`$all`, `$any`, `$not`)
444
+ * may also appear as headers when the runtime emits them — hence the union
445
+ * over {@link PredicateCombinatorKey}.
446
+ *
447
+ * @example
448
+ * ```ts
449
+ * const result: ClauseResult = {
450
+ * path: "elapsed",
451
+ * op: "$gte",
452
+ * expected: 30,
453
+ * actual: 20,
454
+ * pass: false,
455
+ * };
456
+ * ```
457
+ */
458
+ interface ClauseResult {
459
+ /** Dotted path to the fact (`elapsed`, `auth.token`). */
460
+ readonly path: string;
461
+ /** The operator applied (`$gte`, `$eq`, …) — `$eq` for a bare value. */
462
+ readonly op: PredicateOp | PredicateCombinatorKey;
463
+ /**
464
+ * The value the predicate expected. For combinator clauses (`$all`,
465
+ * `$any`, `$not`) this is the child count.
466
+ */
467
+ readonly expected: unknown;
468
+ /**
469
+ * The actual fact value at evaluation time. For combinator clauses this
470
+ * is the number of child clauses that passed.
471
+ */
472
+ readonly actual: unknown;
473
+ /** Whether this clause passed. */
474
+ readonly pass: boolean;
475
+ /**
476
+ * Children of a combinator clause (`$all`, `$any`, `$not`). Preserves the
477
+ * tree shape of the original predicate so renderers (devtools,
478
+ * `system.explain()`) can indent nested clauses.
479
+ */
480
+ readonly children?: ClauseResult[];
481
+ }
482
+
229
483
  /**
230
484
  * Requirement Types - Type definitions for requirements and constraints
231
485
  */
@@ -268,47 +522,23 @@ type RequirementsSchema = Record<string, RequirementPayloadSchema>;
268
522
  * - No requirements: `null` or `[]`
269
523
  */
270
524
  type RequirementOutput$1<R extends Requirement = Requirement> = R | R[] | null;
271
- /**
272
- * Resolver-to-constraint binding mode (RFC-1: Resolver Constraint-Binding).
273
- *
274
- * Controls whether fact writes from a resolver are *bound* to the constraint
275
- * that triggered them. When the binding is `'auto'`, every write performed by
276
- * the resolver re-evaluates the constraint's `when()` predicate against the
277
- * latest facts; if `when()` no longer holds, the write is dropped, the
278
- * resolver's {@link AbortController} is aborted, and `ctx.signal.aborted`
279
- * becomes `true` so the resolver can early-exit on its next checkpoint.
280
- *
281
- * Binding is **one-shot per resolver invocation**: once `when()` flips to
282
- * `false`, the binding stays deactivated even if `when()` would later flip
283
- * back to `true` mid-resolver. This prevents a resolver from "resurrecting"
284
- * a stale intent after the user has moved past it.
285
- *
286
- * - `'none'` (default): Current behavior. Every fact write succeeds.
287
- * - `'auto'`: Writes are gated by the constraint's `when()` predicate. The
288
- * predicate **must be synchronous** for `bind: 'auto'`; async constraints
289
- * cannot be bound (their `when()` cannot be re-evaluated cheaply on every
290
- * set).
291
- *
292
- * @example
293
- * ```ts
294
- * constraints: {
295
- * leaveParty: {
296
- * when: (f) => f.status === 'mutating',
297
- * require: { type: 'EXECUTE_ACTION' },
298
- * bind: 'auto', // resolver tail won't clobber `status = 'left'`
299
- * },
300
- * }
301
- * ```
302
- */
303
- type ConstraintBindMode = "none" | "auto";
304
525
  /** Constraint definition */
305
526
  interface ConstraintDef<S extends Schema, R extends Requirement = Requirement> {
306
527
  /** Priority for ordering (higher runs first) */
307
528
  priority?: number;
308
529
  /** Mark this constraint as async (avoids runtime detection) */
309
530
  async?: boolean;
310
- /** Condition function (sync or async) */
311
- when: (facts: Facts<S>) => boolean | Promise<boolean>;
531
+ /**
532
+ * Condition the constraint requires. Either:
533
+ * - a function (sync or async) `(facts) => boolean`, or
534
+ * - a declarative {@link FactPredicate} spec — serializable, inspectable,
535
+ * always synchronous; e.g. `{ phase: "red", elapsed: { $gte: 30 } }`.
536
+ *
537
+ * A data `when` is normalized to a wrapper function at registration; the
538
+ * wrapper still reads through the tracked facts proxy, so auto-tracking
539
+ * captures both fact and derivation deps correctly.
540
+ */
541
+ when: ((facts: Facts<S>) => boolean | Promise<boolean>) | FactPredicate<InferSchema<S>>;
312
542
  /**
313
543
  * Requirement(s) to produce when condition is met.
314
544
  * - Single requirement: `{ type: "RESTOCK", sku: "ABC" }`
@@ -320,21 +550,23 @@ interface ConstraintDef<S extends Schema, R extends Requirement = Requirement> {
320
550
  /** Timeout for async constraints (ms) */
321
551
  timeout?: number;
322
552
  /**
323
- * Resolver-to-constraint binding mode (RFC-1).
324
- *
325
- * When `'auto'`, fact writes from the resolver triggered by this
326
- * constraint are dropped (and the resolver is aborted) once `when()`
327
- * flips to false. Defaults to `'none'` (current behavior preserved).
553
+ * Names the facts the resolver dispatched by this constraint *owns*:
554
+ * a write from that resolver to an
555
+ * owned fact is dropped — and the resolver aborted if the fact was
556
+ * changed by anything else since the resolver last wrote it. Writes to
557
+ * facts not listed always land; `when()` is not consulted. Omit for no
558
+ * binding (default). Ignored on async constraints.
328
559
  *
329
- * **Forbidden on async constraints** (`async: true`): the binding
330
- * checker re-evaluates `when()` synchronously on every fact write,
331
- * which is incompatible with async predicates. Setting `bind: 'auto'`
332
- * on an async constraint logs a dev-mode warning and is treated as
333
- * `'none'`.
334
- *
335
- * @see {@link ConstraintBindMode}
560
+ * @example
561
+ * ```ts
562
+ * executeAction: {
563
+ * when: (f) => f.status === 'mutating',
564
+ * require: { type: 'EXECUTE_ACTION' },
565
+ * owns: ['status'], // the resolver owns `status`
566
+ * }
567
+ * ```
336
568
  */
337
- bind?: ConstraintBindMode;
569
+ owns?: readonly string[];
338
570
  /**
339
571
  * Constraint IDs whose resolvers must complete before this constraint is evaluated.
340
572
  * If a dependency's `when()` returns false (no requirements), this constraint proceeds.
@@ -435,6 +667,15 @@ interface EffectDef<S extends Schema> {
435
667
  run(facts: Facts<S>, prev: InferSchema<S> | null): void | EffectCleanup | Promise<void | EffectCleanup>;
436
668
  /** Optional explicit dependencies for optimization */
437
669
  deps?: Array<keyof InferSchema<S>>;
670
+ /**
671
+ * Optional declarative trigger — a {@link FactPredicate} that gates whether
672
+ * `run()` fires: even when a dependency changes, the effect runs only if
673
+ * the predicate currently holds. Mutually exclusive with `deps` — the
674
+ * predicate supplies its own deps via static extraction.
675
+ *
676
+ * @example on: { phase: "red", elapsed: { $gte: 30 } }
677
+ */
678
+ on?: FactPredicate<InferSchema<S>>;
438
679
  /** Optional metadata for debugging and devtools (never read on hot path). */
439
680
  meta?: DefinitionMeta;
440
681
  }
@@ -620,8 +861,13 @@ interface ResolverDef<S extends Schema, R extends Requirement = Requirement> {
620
861
  * - Function: type guard predicate (e.g., `requirement: (req) => req.type === "FETCH_USER"`)
621
862
  */
622
863
  requirement: string | ((req: Requirement) => req is R);
623
- /** Custom key function for deduplication */
624
- key?: RequirementKeyFn<R>;
864
+ /**
865
+ * Custom dedup key for the resolver. Either:
866
+ * - a `RequirementKeyFn<R>` function, or
867
+ * - a {@link KeySelector} array of requirement-payload field names:
868
+ * `key: ["type", "to"]` builds a stable dedup key from those fields.
869
+ */
870
+ key?: RequirementKeyFn<R> | KeySelector<R>;
625
871
  /** Retry policy */
626
872
  retry?: RetryPolicy;
627
873
  /** Timeout for resolver execution (ms) */
@@ -1388,9 +1634,10 @@ type TypedDerivationFn<M extends ModuleSchema, K extends keyof GetDerivationsSch
1388
1634
  * Typed derivations definition using the module schema.
1389
1635
  * Each derivation key must match schema.derivations and return the declared type.
1390
1636
  */
1637
+ type TypedDerivationT<M extends ModuleSchema, K extends keyof GetDerivationsSchema<M>> = InferSchemaType<GetDerivationsSchema<M>[K]>;
1391
1638
  type TypedDerivationsDef<M extends ModuleSchema> = {
1392
1639
  [K in keyof GetDerivationsSchema<M>]: TypedDerivationFn<M, K> | {
1393
- compute: TypedDerivationFn<M, K>;
1640
+ compute: TypedDerivationFn<M, K> | ([TypedDerivationT<M, K>] extends [boolean] ? FactPredicate<InferFacts<M>> : never) | ([TypedDerivationT<M, K>] extends [string] ? FactTemplate : never);
1394
1641
  meta?: DefinitionMeta;
1395
1642
  };
1396
1643
  };
@@ -1407,6 +1654,15 @@ type TypedEventsDef<M extends ModuleSchema> = {
1407
1654
  [K in keyof GetEventsSchema<M>]: TypedEventHandlerFn<M, K> | {
1408
1655
  handler: TypedEventHandlerFn<M, K>;
1409
1656
  meta?: DefinitionMeta;
1657
+ } | {
1658
+ /**
1659
+ * Declarative event body: assigns facts from literals, payload
1660
+ * fields ({@link KeySelector}), or interpolated strings
1661
+ * ({@link FactTemplate}). Use instead of `handler` for simple
1662
+ * "set facts from event payload" events.
1663
+ */
1664
+ patch: PatchSpec<InferFacts<M>, InferEventPayloadFromSchema<GetEventsSchema<M>[K]>>;
1665
+ meta?: DefinitionMeta;
1410
1666
  };
1411
1667
  };
1412
1668
  /**
@@ -1421,8 +1677,12 @@ interface TypedConstraintDef<M extends ModuleSchema> {
1421
1677
  priority?: number;
1422
1678
  /** Mark this constraint as async */
1423
1679
  async?: boolean;
1424
- /** Condition function */
1425
- when: (facts: Facts<M["facts"]>) => boolean | Promise<boolean>;
1680
+ /**
1681
+ * Condition the constraint requires. Either a function (sync or async)
1682
+ * `(facts) => boolean`, or a declarative {@link FactPredicate} spec
1683
+ * (e.g. `{ phase: "red", elapsed: { $gte: 30 } }`).
1684
+ */
1685
+ when: ((facts: Facts<M["facts"]>) => boolean | Promise<boolean>) | FactPredicate<InferFacts<M>>;
1426
1686
  /**
1427
1687
  * Requirement(s) to produce when condition is met.
1428
1688
  */
@@ -1430,10 +1690,11 @@ interface TypedConstraintDef<M extends ModuleSchema> {
1430
1690
  /** Timeout for async constraints (ms) */
1431
1691
  timeout?: number;
1432
1692
  /**
1433
- * Resolver-to-constraint binding mode (RFC-1).
1434
- * Defaults to `'none'`. See {@link ConstraintBindMode}.
1693
+ * Names the facts the resolver dispatched by this constraint owns an
1694
+ * external clobber of one of them drops the resolver's write. Omit for no
1695
+ * binding (default). Ignored on async constraints.
1435
1696
  */
1436
- bind?: ConstraintBindMode;
1697
+ owns?: readonly string[];
1437
1698
  /**
1438
1699
  * Constraint IDs whose resolvers must complete before this constraint is evaluated.
1439
1700
  * If a dependency's `when()` returns false (no requirements), this constraint proceeds.
@@ -1466,8 +1727,12 @@ interface CrossModuleConstraintDef<M extends ModuleSchema, Deps extends CrossMod
1466
1727
  priority?: number;
1467
1728
  /** Mark this constraint as async */
1468
1729
  async?: boolean;
1469
- /** Condition function with cross-module facts access */
1470
- when: (facts: CrossModuleFactsWithSelf<M, Deps>) => boolean | Promise<boolean>;
1730
+ /**
1731
+ * Condition the constraint requires. Either a function (sync or async)
1732
+ * with cross-module facts access, or a nested {@link FactPredicate}:
1733
+ * `{ self: { phase: "red" }, auth: { token: { $exists: true } } }`.
1734
+ */
1735
+ when: ((facts: CrossModuleFactsWithSelf<M, Deps>) => boolean | Promise<boolean>) | FactPredicate<CrossModuleFactsWithSelf<M, Deps>>;
1471
1736
  /**
1472
1737
  * Requirement(s) to produce when condition is met.
1473
1738
  */
@@ -1475,10 +1740,11 @@ interface CrossModuleConstraintDef<M extends ModuleSchema, Deps extends CrossMod
1475
1740
  /** Timeout for async constraints (ms) */
1476
1741
  timeout?: number;
1477
1742
  /**
1478
- * Resolver-to-constraint binding mode (RFC-1).
1479
- * Defaults to `'none'`. See {@link ConstraintBindMode}.
1743
+ * Names the facts the resolver dispatched by this constraint owns
1744
+ * an external clobber of one of them drops the resolver's write. Omit
1745
+ * for no binding (default). Ignored on async constraints.
1480
1746
  */
1481
- bind?: ConstraintBindMode;
1747
+ owns?: readonly string[];
1482
1748
  /**
1483
1749
  * Constraint IDs whose resolvers must complete before this constraint is evaluated.
1484
1750
  * If a dependency's `when()` returns false (no requirements), this constraint proceeds.
@@ -1511,6 +1777,11 @@ interface CrossModuleEffectDef<M extends ModuleSchema, Deps extends CrossModuleD
1511
1777
  run: (facts: CrossModuleFactsWithSelf<M, Deps>, prev: CrossModuleFactsWithSelf<M, Deps> | undefined) => void | EffectCleanup | Promise<void | EffectCleanup>;
1512
1778
  /** Optional dependency keys to filter when effect runs */
1513
1779
  deps?: string[];
1780
+ /**
1781
+ * Optional declarative trigger — a {@link FactPredicate} that gates whether
1782
+ * `run()` fires. Mutually exclusive with `deps`.
1783
+ */
1784
+ on?: FactPredicate<CrossModuleFactsWithSelf<M, Deps>>;
1514
1785
  /** Optional metadata for debugging and devtools (never read on hot path). */
1515
1786
  meta?: DefinitionMeta;
1516
1787
  }
@@ -1532,7 +1803,7 @@ type CrossModuleDerivationFn<M extends ModuleSchema, Deps extends CrossModuleDep
1532
1803
  */
1533
1804
  type CrossModuleDerivationsDef<M extends ModuleSchema, Deps extends CrossModuleDeps> = {
1534
1805
  [K in keyof GetDerivationsSchema<M>]: CrossModuleDerivationFn<M, Deps, K> | {
1535
- compute: CrossModuleDerivationFn<M, Deps, K>;
1806
+ compute: CrossModuleDerivationFn<M, Deps, K> | ([TypedDerivationT<M, K>] extends [boolean] ? FactPredicate<CrossModuleFactsWithSelf<M, Deps>> : never) | ([TypedDerivationT<M, K>] extends [string] ? FactTemplate : never);
1536
1807
  meta?: DefinitionMeta;
1537
1808
  };
1538
1809
  };
@@ -1583,8 +1854,12 @@ type ExtractRequirement<M extends ModuleSchema, T extends keyof GetRequirementsS
1583
1854
  interface TypedResolverDef<M extends ModuleSchema, T extends keyof GetRequirementsSchema<M> & string> {
1584
1855
  /** Requirement type to handle */
1585
1856
  requirement: T;
1586
- /** Custom key function for deduplication */
1587
- key?: (req: ExtractRequirement<M, T>) => string;
1857
+ /**
1858
+ * Custom dedup key. Either a `(req) => string` function, or a
1859
+ * {@link KeySelector} array of requirement-payload field names
1860
+ * (`["type", "to"]`) that builds a stable key from those fields.
1861
+ */
1862
+ key?: ((req: ExtractRequirement<M, T>) => string) | KeySelector<ExtractRequirement<M, T>>;
1588
1863
  /** Retry policy */
1589
1864
  retry?: RetryPolicy;
1590
1865
  /** Timeout for resolver execution (ms) */
@@ -1845,6 +2120,11 @@ interface SystemInspection {
1845
2120
  hitCount: number;
1846
2121
  lastActiveAt: number | null;
1847
2122
  meta?: DefinitionMeta;
2123
+ /**
2124
+ * The data-form predicate spec (when the constraint's `when` is declarative),
2125
+ * exposed for devtools and `explain()` rendering.
2126
+ */
2127
+ whenSpec?: FactPredicate<Record<string, unknown>>;
1848
2128
  }>;
1849
2129
  resolvers: Record<string, ResolverStatus>;
1850
2130
  /** All defined resolver names and their requirement types */
@@ -2095,7 +2375,7 @@ interface ResolversControl<M extends ModuleSchema = ModuleSchema> {
2095
2375
  interface DynamicConstraintDef<M extends ModuleSchema = ModuleSchema> {
2096
2376
  priority?: number;
2097
2377
  async?: boolean;
2098
- when: (facts: Readonly<InferSchema<M["facts"]>>) => boolean | Promise<boolean>;
2378
+ when: ((facts: Readonly<InferSchema<M["facts"]>>) => boolean | Promise<boolean>) | FactPredicate<InferSchema<M["facts"]>>;
2099
2379
  require: {
2100
2380
  type: string;
2101
2381
  [key: string]: unknown;
@@ -2118,15 +2398,27 @@ interface DynamicConstraintDef<M extends ModuleSchema = ModuleSchema> {
2118
2398
  interface DynamicEffectDef<M extends ModuleSchema = ModuleSchema> {
2119
2399
  run: (facts: Readonly<InferSchema<M["facts"]>>, prev: InferSchema<M["facts"]> | null) => void | (() => void) | Promise<void | (() => void)>;
2120
2400
  deps?: Array<string & keyof InferSchema<M["facts"]>>;
2401
+ /**
2402
+ * Optional declarative trigger — a {@link FactPredicate} that gates whether
2403
+ * `run()` fires. Mutually exclusive with `deps`.
2404
+ */
2405
+ on?: FactPredicate<InferSchema<M["facts"]>>;
2121
2406
  meta?: DefinitionMeta;
2122
2407
  }
2123
2408
  /** Resolver definition for dynamic registration — typed context.facts, relaxed requirement */
2124
2409
  interface DynamicResolverDef<M extends ModuleSchema = ModuleSchema> {
2125
2410
  requirement: string;
2126
- key?: (req: {
2411
+ /**
2412
+ * Custom dedup key. Either a `(req) => string` function, or a
2413
+ * {@link KeySelector} array of requirement-payload field names.
2414
+ */
2415
+ key?: ((req: {
2416
+ type: string;
2417
+ [key: string]: unknown;
2418
+ }) => string) | KeySelector<{
2127
2419
  type: string;
2128
2420
  [key: string]: unknown;
2129
- }) => string;
2421
+ }>;
2130
2422
  retry?: RetryPolicy;
2131
2423
  timeout?: number;
2132
2424
  batch?: BatchConfig;
@@ -2185,6 +2477,11 @@ type ObservationEvent = {
2185
2477
  type: "constraint.evaluate";
2186
2478
  id: string;
2187
2479
  active: boolean;
2480
+ /**
2481
+ * Per-clause breakdown of a data-form predicate evaluation. Present
2482
+ * only when the constraint's `when` is a {@link FactPredicate}.
2483
+ */
2484
+ whenExplain?: ClauseResult[];
2188
2485
  } | {
2189
2486
  type: "constraint.error";
2190
2487
  id: string;
@@ -2517,8 +2814,11 @@ interface Plugin<M extends ModuleSchema = ModuleSchema> {
2517
2814
  * Called after a constraint's `when` function is evaluated.
2518
2815
  * @param id - The constraint ID
2519
2816
  * @param active - Whether the constraint is active (when returned true)
2817
+ * @param whenExplain - For data-form constraints, the per-clause breakdown
2818
+ * (which clauses passed, which failed, against what fact values). Omitted
2819
+ * for function-form `when`.
2520
2820
  */
2521
- onConstraintEvaluate?: (id: string, active: boolean) => void;
2821
+ onConstraintEvaluate?: (id: string, active: boolean, whenExplain?: ClauseResult[]) => void;
2522
2822
  /**
2523
2823
  * Called when a constraint's `when` function throws an error.
2524
2824
  * @param id - The constraint ID
@@ -2643,4 +2943,4 @@ interface Plugin<M extends ModuleSchema = ModuleSchema> {
2643
2943
  onTraceComplete?: (entry: TraceEntry) => void;
2644
2944
  }
2645
2945
 
2646
- export { type SystemInspection as $, type AnySystem as A, type BatchConfig as B, type CrossModuleDeps as C, type DefinitionMeta as D, type EffectsDef as E, type Facts as F, type InferFacts as G, type HistoryAPI as H, type InferDerivations as I, type InferRequirementTypes as J, type InferRequirements as K, type InferSchemaType as L, type ModuleSchema as M, type NamespacedSystem as N, type InferSelectorState as O, type Plugin as P, type MetaAccessor as Q, type RequirementWithId as R, type SchemaType as S, type TypedDerivationsDef as T, type MetaMatch as U, type ObservationEvent as V, type RetryPolicy as W, type Schema as X, type Snapshot as Y, type System as Z, type SystemConfig as _, type TypedEventsDef as a, type SystemMode as a0, type SystemSnapshot as a1, type TraceEntry as a2, isNamespacedSystem as a3, isSingleModuleSystem as a4, type FactsStore as a5, type ConstraintsDef as a6, type ConstraintState as a7, type ResolversDef as a8, type ConstraintBindMode as a9, type FactKeys as aA, type FactReturnType as aB, type FlexibleEventHandler as aC, type HistoryConfig as aD, type InferEventPayloadFromSchema as aE, type InferRequirementPayloadFromSchema as aF, type InferSchema as aG, type MutableNamespacedFacts as aH, type NamespacedDerivations as aI, type NamespacedEventsAccessor as aJ, type NamespacedFacts as aK, type ObservableKeys as aL, type RequirementExplanation as aM, type RequirementOutput as aN, type RequirementPayloadSchema$1 as aO, type RequirementsSchema as aP, type ResolverContext as aQ, type ResolversControl as aR, type SnapshotMeta as aS, type SystemEvent as aT, type TraceConfig as aU, type TypedConstraintDef as aV, type TypedResolverContext as aW, type TypedResolverDef as aX, type UnionEvents as aY, type RequirementOutput$1 as aZ, type ResolverStatus as aa, type FactChange as ab, type ReconcileResult as ac, type RecoveryStrategy as ad, type ErrorSource as ae, type RetryLaterConfig as af, type BatchItemResult as ag, type BatchResolveResults as ah, type ConstraintsControl as ai, type CrossModuleConstraintDef as aj, type CrossModuleDerivationFn as ak, type CrossModuleEffectDef as al, type CrossModuleFactsWithSelf as am, type DerivationKeys as an, type DerivationReturnType as ao, type DerivationsControl as ap, type DerivationsSchema as aq, type DeriveAccessor as ar, type DispatchEventsFromSchema as as, type EffectCleanup as at, type EffectsControl as au, type EventPayloadSchema as av, type EventsAccessor as aw, type EventsAccessorFromSchema as ax, type EventsDef as ay, type EventsSchema as az, type TypedConstraintsDef as b, type TypedResolversDef as c, type ModuleHooks as d, type CrossModuleDerivationsDef as e, type CrossModuleEffectsDef as f, type CrossModuleConstraintsDef as g, type ModuleDef as h, type CreateSystemOptionsSingle as i, type SingleModuleSystem as j, type ModulesMap as k, type CreateSystemOptionsNamed as l, type TraceOption as m, type ErrorBoundaryConfig as n, type Requirement as o, type RequirementKeyFn as p, DirectiveError as q, type DistributableSnapshot as r, type DistributableSnapshotOptions as s, type DynamicConstraintDef as t, type DynamicEffectDef as u, type DynamicResolverDef as v, type FactsSnapshot as w, type HistoryOption as x, type HistoryState as y, type InferEvents as z };
2946
+ export { type OperatorObject as $, type AnySystem as A, type BatchConfig as B, type ClauseResult as C, type DefinitionMeta as D, type EffectsDef as E, type FactTemplate as F, type FactsSnapshot as G, type HistoryAPI as H, type HistoryOption as I, type HistoryState as J, type InferDerivations as K, type InferEvents as L, type ModuleSchema as M, type NamespacedSystem as N, type InferFacts as O, type PatchSpec as P, type InferRequirementTypes as Q, type RequirementWithId as R, type SchemaType as S, type TypedDerivationsDef as T, type InferRequirements as U, type InferSchemaType as V, type InferSelectorState as W, type KeySelector as X, type MetaAccessor as Y, type MetaMatch as Z, type ObservationEvent as _, type Facts as a, type RequirementsSchema as a$, type PatchValue as a0, type PayloadRef as a1, type PredicateClause as a2, type PredicateCombinator as a3, type PredicateCombinatorKey as a4, type PredicateObject as a5, type PredicateOp as a6, type RetryPolicy as a7, type Schema as a8, type Snapshot as a9, type DerivationReturnType as aA, type DerivationsControl as aB, type DerivationsSchema as aC, type DeriveAccessor as aD, type DispatchEventsFromSchema as aE, type EffectCleanup as aF, type EffectsControl as aG, type EventPayloadSchema as aH, type EventsAccessor as aI, type EventsAccessorFromSchema as aJ, type EventsDef as aK, type EventsSchema as aL, type FactKeys as aM, type FactReturnType as aN, type FlexibleEventHandler as aO, type HistoryConfig as aP, type InferEventPayloadFromSchema as aQ, type InferRequirementPayloadFromSchema as aR, type InferSchema as aS, type MutableNamespacedFacts as aT, type NamespacedDerivations as aU, type NamespacedEventsAccessor as aV, type NamespacedFacts as aW, type ObservableKeys as aX, type RequirementExplanation as aY, type RequirementOutput as aZ, type RequirementPayloadSchema$1 as a_, type System as aa, type SystemConfig as ab, type SystemInspection as ac, type SystemMode as ad, type SystemSnapshot as ae, type TraceEntry as af, isNamespacedSystem as ag, isSingleModuleSystem as ah, type FactsStore as ai, type ConstraintsDef as aj, type ConstraintState as ak, type ResolversDef as al, type ResolverStatus as am, type FactChange as an, type ReconcileResult as ao, type RecoveryStrategy as ap, type ErrorSource as aq, type RetryLaterConfig as ar, type BatchItemResult as as, type BatchResolveResults as at, type ConstraintsControl as au, type CrossModuleConstraintDef as av, type CrossModuleDerivationFn as aw, type CrossModuleEffectDef as ax, type CrossModuleFactsWithSelf as ay, type DerivationKeys as az, type TypedEventsDef as b, type ResolverContext as b0, type ResolversControl as b1, type SnapshotMeta as b2, type SystemEvent as b3, type TraceConfig as b4, type TypedConstraintDef as b5, type TypedResolverContext as b6, type TypedResolverDef as b7, type UnionEvents as b8, type RequirementOutput$1 as b9, type TypedConstraintsDef as c, type TypedResolversDef as d, type ModuleHooks as e, type CrossModuleDeps as f, type CrossModuleDerivationsDef as g, type CrossModuleEffectsDef as h, type CrossModuleConstraintsDef as i, type ModuleDef as j, type CreateSystemOptionsSingle as k, type SingleModuleSystem as l, type ModulesMap as m, type CreateSystemOptionsNamed as n, type Plugin as o, type TraceOption as p, type ErrorBoundaryConfig as q, type Requirement as r, type RequirementKeyFn as s, DirectiveError as t, type DistributableSnapshot as u, type DistributableSnapshotOptions as v, type DynamicConstraintDef as w, type DynamicEffectDef as x, type DynamicResolverDef as y, type FactPredicate as z };
@@ -0,0 +1,2 @@
1
+ export{a as createSystem}from'./chunk-HAF5JCET.js';import'./chunk-YCCQ73C6.js';import'./chunk-EH2Q754B.js';import'./chunk-M5KZXNZX.js';//# sourceMappingURL=system-744ZPPES.js.map
2
+ //# sourceMappingURL=system-744ZPPES.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"system-JIO36ALC.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"system-744ZPPES.js"}
@@ -0,0 +1,2 @@
1
+ 'use strict';var chunkSQVKCJHE_cjs=require('./chunk-SQVKCJHE.cjs');require('./chunk-PGUTGWUI.cjs'),require('./chunk-S3CFYDIB.cjs'),require('./chunk-ZHS3EW2Z.cjs');Object.defineProperty(exports,"createSystem",{enumerable:true,get:function(){return chunkSQVKCJHE_cjs.a}});//# sourceMappingURL=system-CK3SHMXZ.cjs.map
2
+ //# sourceMappingURL=system-CK3SHMXZ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"system-2THXJBFM.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"system-CK3SHMXZ.cjs"}