@sanity/workflow-engine-test 0.10.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # @sanity/workflow-engine-test
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 092a0d4: **BREAKING:** runtime-supplied refs written into field state are gated on the deployment's declared resource surface.
8
+ - A `doc.ref` / `doc.refs` / `release.ref` value a caller or effect handler supplies — start `initialFields` input, an action's param-sourced op values, `editField` values, effect-completion ops — must target a declared resource: the `workflowResource` itself or a resource the `resourceClients` resolver serves. An off-surface ref throws the new `RefResourceUndeclaredError` and the commit aborts, instead of writing cleanly and failing later when something dereferences it. The check is advisory like every engine check; stored documents are unchanged.
9
+ - Definition-authored refs are exempt: literal `initialValue` seeds, `type: 'query'` texts, spawn `with` projections, and op values that bind no action param were validated (and alias-expanded) at deploy, so they materialize into field state without re-checking.
10
+ - Resource aliases stay a deploy-time abstraction: `deployDefinitions({definitions, resourceAliases?})` expands every `@<alias>:` reference to its physical GDR before fingerprinting, and nothing alias-shaped survives past deploy — the runtime holds no alias map, and an alias-shaped ref supplied to a runtime verb is rejected as malformed like any other non-GDR string.
11
+ - `createBench` takes `serveResources` (dataset siblings served — and thereby declared — straight from the bench's own store) plus a raw `resourceClients` resolver for anything beyond same-store siblings. The engine also exports `sameResource` alongside the existing GDR helpers.
12
+ - The CLI wires no `resourceClients`, so refs supplied at runtime through it (`start --field`, `fire-action` ref params) admit the workflow resource only.
13
+
14
+ - 5a1a9fe: New `startedInstanceFixture(bench)` — a real, minimal started instance built through the bench's actual verbs, for consumers that need a persisted-shape-valid instance document without caring about its workflow (hand-crafted instance stubs do not pass the engine's read parse).
15
+ - 1321ba5: The bench exposes `evaluateStart` — the engine's pre-flight of `startInstance`'s gates (the `start.allowed` verdict with its insight, plus the still-missing required inputs), delegating to the verb with the bench clock so `$now` reads the frozen instant.
16
+
17
+ ### Patch Changes
18
+
19
+ - e683875: The bench `queryInScope` doc now describes the scope the engine actually binds: the caller-free rendered scope (every always-bound condition var, `$fields` with the open stage's overlay merged, `$assigned` at its caller-free `false`, and the author's pre-evaluated `$<predicate>` booleans) — the previous enumeration listed a `$subject` param that was never bound.
20
+ - Updated dependencies [f9389e5]
21
+ - Updated dependencies [d0c62ea]
22
+ - Updated dependencies [c3eed2e]
23
+ - Updated dependencies [df4bd80]
24
+ - Updated dependencies [092a0d4]
25
+ - Updated dependencies [5a1a9fe]
26
+ - Updated dependencies [e3a7ba2]
27
+ - Updated dependencies [30fed9e]
28
+ - Updated dependencies [1321ba5]
29
+ - Updated dependencies [e683875]
30
+ - Updated dependencies [a8ace4d]
31
+ - @sanity/workflow-engine@0.17.0
32
+
33
+ ## 0.11.0
34
+
35
+ ### Minor Changes
36
+
37
+ - 4e2b5bb: **BREAKING:** the bench now mirrors the engine's read/write surface exactly instead of paraphrasing it.
38
+ - `editDocument` performs the real write for every action it accepts, so guards scoped to that action are genuinely consulted: `publish` promotes the existing draft onto the published id and `unpublish` retracts it (both through the fake client's document actions, vetted by the guard seam under that action); previously both silently landed as a plain update and a guard scoped to `actions: ['publish']` was never consulted. `create` is no longer accepted — create through the client, which the same seam gates. The return type is now honest: `StoreDocument | undefined` (`undefined` after `delete`/`unpublish`, or when the default identity can't read the written doc) instead of a cast that crashed at runtime.
39
+ - Read helpers sharing an engine verb's name now delegate to that verb with the engine's args shape: `getInstance`/`listPendingEffects` (was `pendingEffects`)/`findPendingEffects` (new)/`children`/`instancesForDocument`/`definitionsForDocument` all take the engine's args objects, and missing/foreign-tag instances throw the engine's typed `InstanceNotFoundError` instead of a bench-local `Error`.
40
+ - `instancesByStage` speaks the engine's filter vocabulary: `definition` (was `workflowName`) and `includeCompleted` (was `openOnly` — polarity AND default flip: in-flight only unless opted in).
41
+ - `deleteDefinition` joins the engine-call wrappers with the same `actor:`/`grants:` identity shortcuts as every other write verb — no more routing around the bench via `createBenchEngine`.
42
+
43
+ `@sanity-labs/client-fake-for-test` ^0.8.0 (the document publish/unpublish guard-interception seam) is now a regular dependency — the bench has always imported it at runtime, but it was previously declared only as a devDependency, so an external install got a broken package.
44
+
45
+ - 6a46db1: **BREAKING:** the definition root's `applicableWhen` moved to `start.filter`, inside a new `start` block — `{kind?: 'interactive' | 'autonomous', filter?: GROQ}` — describing how standalone runs begin. No alias, no dual-read: definitions authored with `applicableWhen` fail the strict parse until rewritten.
46
+ - `start.kind` classifies who initiates a run — a person from a start surface (`'interactive'`, the default; `startKindOf(def)` is the one reading rule) or a system reacting to a document (`'autonomous'`, hidden from human start pickers). Classification only: `startInstance` stays one verb and one code path for every caller and kind — an interactive start of an autonomous workflow is legal. Deploy invariants: a spawn-only (`lifecycle: 'child'`) definition may not declare `start`; an `'autonomous'` kind requires every required input entry to be subject-style (`doc.ref`/`doc.refs` — the shared `isSubjectEntry` rule); a `start.filter` may read only declared **input-sourced** entries via `$fields` (a typo or a query/literal entry is a silent never-pass); and a root-reading filter (`_type == …`, `@`, or a `^` escaping a scan) requires a subject entry, since a read surface with no subject never binds a root.
47
+ - `start.filter` is a **read-side visibility rule, not a gate** — `startInstance` never evaluates it and never throws on it; it starts whenever the caller supplies the required inputs. The filter decides what appears in / is chosen from Start lists: `definitionsForDocument`/applicability and the Studio start control evaluate it in a widened, named start-filter context (`START_FILTER_VARS`) — the candidate document as root, `$tag`/`$definition`/`$now`/`$fields` bound (`$fields` = the caller's input entries as GDR envelopes, so `$fields.<entry>.id` is the GDR URI), and `*[...]` = the workflow resource's dataset. Filters that never scan keep the cheap pure evaluation (`analyzeCondition(...).readsDataset` decides); scanning filters evaluate over a fetched slice of EVERY instance in the tag (completed included — `*` carries no hidden predicate; filters qualify in-flight themselves with `!defined(completedAt)`), so a filter can bound what surfaces (WIP limits, one-run-per-subject). Absent bindings evaluate GROQ-null and fail closed.
48
+
49
+ - e5f5b77: **BREAKING:** actions become the only payload mechanism — every op, effect, and spawn in the system lives on an action; structure never does.
50
+ - Actions gain `when` (cascade-fired trigger: the engine fires it the moment it's true, at most once per stage visit, level-triggered; never fireAction-able) and `spawn` (the subworkflow block, unlocking caller-triggered and conditional spawn). Fires-on-entry is spelled `when: 'true'`.
51
+ - Deleted from the model: `activation`, activity-level `ops`/`effects`/`subworkflows`, `completeWhen`/`failWhen`, `kind`, and transition `ops`/`effects`. Transitions are pure edges `{name, when, to}` (the condition key renamed from `filter` — trigger semantics). `target` survives as the off-system marker; `kind` is now always derived, plus a new derived executor classification (autonomous / interactive / off-system / hybrid) on activity evaluations.
52
+ - Activities are `active` from stage entry (no `pending` status, no activation moment, no machine-step implicit); a new deploy invariant requires every activity to have a reachable path to a terminal status.
53
+ - A cascade hop is ONE transaction: the current stage's triggered actions run to in-memory fixpoint, then transition selection, then a single CAS commit; chain hops stay separate commits. The per-visit `firedActions` ledger on the activity entry re-arms on re-entry (a stage resets on re-entry). The cascading token executes triggered actions; `roles` on a triggered action pins which identities may execute it (an incapable token leaves it armed).
54
+ - The context bag split: `$effects` now carries completed effects' outputs ONLY (derived from `effectHistory`), and the new `$context` carries the `startInstance` seed plus a parent's `spawn.context` handoff (the persisted `effectsContext` field renamed `context`; `startInstance`'s seed arg renamed accordingly). `$effectStatus` is unchanged.
55
+ - Spawn registry rows key per (activity entry, spawning action), so two spawn actions on one activity can never cross-adopt each other's children.
56
+ - `fireAction` rejects a `when` action (`cascade-fired` verdict); evaluation projects triggered actions as narratable (`triggered: true` + `whenInsight`), never as buttons.
57
+
58
+ ### Patch Changes
59
+
60
+ - Updated dependencies [11e2d6f]
61
+ - Updated dependencies [7510fd5]
62
+ - Updated dependencies [01e8042]
63
+ - Updated dependencies [79c54db]
64
+ - Updated dependencies [6a46db1]
65
+ - Updated dependencies [35b9b85]
66
+ - Updated dependencies [e5f5b77]
67
+ - Updated dependencies [01e8042]
68
+ - @sanity/workflow-engine@0.16.0
69
+
3
70
  ## 0.10.0
4
71
 
5
72
  ### Minor Changes
package/README.md CHANGED
@@ -18,17 +18,22 @@ npm install -D @sanity/workflow-engine-test
18
18
  `createBench()` builds an isolated bench: the real engine, a fake in-memory
19
19
  Sanity client, an all-access default actor, and a bench-owned clock. Every
20
20
  wrapped verb injects the bench's client, tag, workflow resource, access
21
- default, and clock, so a test reads as the workflow it exercises:
21
+ default, clock, and cross-resource resolver (`serveResources` sugar for
22
+ same-store dataset siblings, raw `resourceClients` for anything else), so a
23
+ test reads as the workflow it exercises:
22
24
 
23
- - **Runtime verbs** — `deployDefinitions`, `startInstance`, `fireAction`,
24
- `editField`, `completeEffect` (and `completePendingEffect`, addressed by
25
- effect name), `tick`, `evaluate`.
25
+ - **Runtime verbs** — `deployDefinitions`, `deleteDefinition`, `startInstance`,
26
+ `fireAction`, `editField`, `completeEffect` (and `completePendingEffect`,
27
+ addressed by effect name), `tick`, `evaluate`.
26
28
  - **Instance-admin overrides** — `setStage`, `abortInstance`. Same injection,
27
29
  so a forced move into a deadline-gated stage evaluates `$now` at bench time,
28
30
  not wall-clock.
29
31
  - **Reads and probes** — `getInstance`, `currentStage`, `activityStatus`,
30
- `pendingEffects`, `children`, `instancesForSubject`, `instancesByStage`,
31
- `instancesForDocument`, `query`, plus the guard helpers below.
32
+ `listPendingEffects`, `findPendingEffects`, `children`, `instancesForSubject`,
33
+ `instancesByStage`, `instancesForDocument`, `definitionsForDocument`,
34
+ `evaluateStart`, `query`, plus the guard helpers below. Helpers sharing an
35
+ engine verb's name take the engine's own args objects and throw its typed
36
+ errors — bench reads and engine reads cannot drift.
32
37
 
33
38
  ```ts
34
39
  const bench = createBench({now: '2026-01-01T00:00:00Z'})
@@ -59,7 +64,7 @@ ticking — never by waiting.
59
64
  ## Out-of-band engines
60
65
 
61
66
  Some tests need an engine of their own — custom effect handlers, racing two
62
- drainers, a `deleteDefinition` call. `createBenchEngine(bench, overrides?)`
67
+ drainers, a custom clock. `createBenchEngine(bench, overrides?)`
63
68
  builds one on the bench's client, inheriting its tag, workflow resource, and
64
69
  clock (`overrides` win). The bench exposes `tag` and `workflowResource` for
65
70
  raw `workflow.*` calls, and the default tag ships as the `BENCH_TAG` constant.
@@ -80,7 +85,12 @@ bench gives you a helper for each:
80
85
  writes through the bench client (`patch` is a `{set?, unset?, insert?}` spec),
81
86
  and a write a deployed guard denies throws `MutationGuardDeniedError` — the
82
87
  rejection the lake is designed to apply to _any_ client once guard
83
- enforcement ships; the bench reproduces it on the client it mints.
88
+ enforcement ships; the bench reproduces it on the client it mints. Every
89
+ guard action is a real write: `action: 'delete'` deletes, `'publish'`
90
+ promotes the existing `drafts.<id>` draft onto the published id, and
91
+ `'unpublish'` retracts it — so a guard scoped to `actions: ['publish']` is
92
+ genuinely consulted. The helper resolves to the post-write document, or
93
+ `undefined` once a delete/unpublish removed it.
84
94
  - **Read-time preflight** — `bench.activeGuardsForDocument(id)` answers "_would_
85
95
  a write be denied right now?" _without_ writing. This is the check a UI uses
86
96
  to disable a button or show a lock ahead of time. It's a pure read over the