@sanity/workflow-engine-test 0.8.0 → 0.9.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/README.md CHANGED
@@ -17,19 +17,24 @@ npm install -D @sanity/workflow-engine-test
17
17
 
18
18
  A workflow stage can deploy **mutation guards** — `temp.system.guard` documents
19
19
  that lock the documents it governs while the stage is active, and lift when it
20
- exits. There are two distinct things you test, and the bench gives you a helper
21
- for each:
22
-
23
- - **Write-time enforcement** `bench.editDocument({documentId, patch})` writes through the
24
- bench client, which **simulates the lake's server-side guard rejection**: a
25
- write a deployed guard denies throws `MutationGuardDeniedError`. In production
26
- the lake enforces this for _any_ client; the bench reproduces it on the
20
+ exits. The lake does not enforce this doc type yet: in production, guard
21
+ enforcement is the engine's own **optimistic** evaluation (advisory verdicts
22
+ and engine-side write denials), and the lake ACL is the only hard boundary.
23
+ The bench installs the _intended_ lake-side contract at its client's write
24
+ seam, so tests exercise the rejection semantics guards are designed to have
25
+ once the lake enforces them. There are two distinct things you test, and the
26
+ bench gives you a helper for each:
27
+
28
+ - **Write-time rejection (simulated)** — `bench.editDocument({documentId, patch})`
29
+ writes through the bench client, and a write a deployed guard denies throws
30
+ `MutationGuardDeniedError` — the rejection the lake is designed to apply to
31
+ _any_ client once guard enforcement ships; the bench reproduces it on the
27
32
  client it mints.
28
33
  - **Read-time preflight** — `bench.activeGuardsForDocument(id)` answers "_would_
29
34
  a write be denied right now?" _without_ writing. This is the check a UI uses
30
35
  to disable a button or show a lock ahead of time. It's a pure read over the
31
- same evaluation path as enforcement, so its verdict matches what a write would
32
- actually do — and it works against any client.
36
+ same evaluation path as the bench's write rejection, so its verdict matches
37
+ what a bench write would actually do — and it works against any client.
33
38
 
34
39
  `bench.listGuards()` returns the deployed guard docs for inspection.
35
40
 
@@ -40,15 +45,15 @@ const bench = createBench({documents: [{_id: 'doc-1', _type: 'article', body: 'b
40
45
  // Preflight: is the doc locked for this user right now?
41
46
  const active = await bench.activeGuardsForDocument('doc-1')
42
47
 
43
- // Enforcement: a guard-violating write is rejected.
48
+ // Rejection: a guard-violating write throws (bench-simulated lake contract).
44
49
  await expect(bench.editDocument({documentId: 'doc-1', patch: {body: 'edited'}})).rejects.toThrow(
45
50
  MutationGuardDeniedError,
46
51
  )
47
52
  ```
48
53
 
49
- > To share one store across benches with enforcement intact, pass another
50
- > bench's already-wired client: `createBench({client: other.client, tags})`. The
51
- > `mutationGuard` rides along, so the second bench enforces the same locks. Only
54
+ > To share one store across benches with the write seam intact, pass another
55
+ > bench's already-wired client: `createBench({client: other.client, tag})`. The
56
+ > `mutationGuard` rides along, so the second bench denies the same locks. Only
52
57
  > a bare hand-built `createTestClient()` opts out of write rejection — the
53
58
  > read-time preflight needs no wiring either way.
54
59
 
package/dist/index.cjs CHANGED
@@ -5014,7 +5014,7 @@ const DEFAULT_WORKFLOW_RESOURCE = {
5014
5014
  type: "dataset",
5015
5015
  id: "test.test"
5016
5016
  }, ALL_ACCESS_USER = {
5017
- kind: "user",
5017
+ kind: "person",
5018
5018
  id: "bench-user",
5019
5019
  roles: ["*"]
5020
5020
  }, WILDCARD_GRANTS = [
@@ -5185,17 +5185,38 @@ function createReadHelpers(scope) {
5185
5185
  )
5186
5186
  ),
5187
5187
  instancesByStage: async ({ workflowName, stage, openOnly }) => {
5188
- const filter = openOnly ? " && completedAt == null" : "";
5189
- return client.fetch(
5190
- `*[_type == "${workflowEngine.WORKFLOW_INSTANCE_TYPE}" && definition == $wf && currentStage == $stage && ${workflowEngine.tagScopeFilter()}${filter}]
5191
- | order(startedAt asc)`,
5192
- { wf: workflowName, stage, tag }
5193
- );
5188
+ const { query, params } = workflowEngine.instancesQuery({
5189
+ tag,
5190
+ filter: { definition: workflowName, stage, includeCompleted: !openOnly }
5191
+ });
5192
+ return client.fetch(query, params);
5194
5193
  },
5195
5194
  instancesForDocument: async (document2) => workflowEngine.workflow.instancesForDocument({ ...scope, document: document2 }),
5196
5195
  snapshot: () => client.snapshot()
5197
5196
  };
5198
5197
  }
5198
+ function subjectField(documentId, opts) {
5199
+ return {
5200
+ type: "doc.ref",
5201
+ name: opts?.name ?? "subject",
5202
+ value: workflowEngine.gdrRef({
5203
+ res: DEFAULT_WORKFLOW_RESOURCE,
5204
+ documentId,
5205
+ type: opts?.type ?? "document"
5206
+ })
5207
+ };
5208
+ }
5209
+ function releaseField(releaseName, opts) {
5210
+ return {
5211
+ type: "release.ref",
5212
+ name: opts?.name ?? "release",
5213
+ value: {
5214
+ id: workflowEngine.gdrFromResource(DEFAULT_WORKFLOW_RESOURCE, `_.releases.${releaseName}`),
5215
+ type: "system.release",
5216
+ releaseName
5217
+ }
5218
+ };
5219
+ }
5199
5220
  function createBench(options = {}) {
5200
5221
  const tag = options.tag ?? "bench";
5201
5222
  workflowEngine.validateTag(tag);
@@ -5205,7 +5226,7 @@ function createBench(options = {}) {
5205
5226
  workflowResource
5206
5227
  }, access = resolveBenchAccess(options);
5207
5228
  let frozenNow = options.now !== void 0 ? assertIsoInstant(options.now) : void 0;
5208
- const clock = () => frozenNow ?? (/* @__PURE__ */ new Date()).toISOString();
5229
+ const clock = () => frozenNow ?? (/* @__PURE__ */ new Date()).toISOString(), engine = createEngineWrappers({ scope, access, clock }), reads = createReadHelpers(scope);
5209
5230
  return {
5210
5231
  client,
5211
5232
  access,
@@ -5221,10 +5242,16 @@ function createBench(options = {}) {
5221
5242
  throw new Error(`bench.advance: ms must be a finite number, got ${ms2}`);
5222
5243
  frozenNow = new Date(Date.parse(frozenNow ?? (/* @__PURE__ */ new Date()).toISOString()) + ms2).toISOString();
5223
5244
  },
5224
- ...createEngineWrappers({ scope, access, clock }),
5225
- ...createReadHelpers(scope),
5245
+ ...engine,
5246
+ ...reads,
5226
5247
  ...createGuardHelpers(client),
5227
- ...createQueryHelpers(scope)
5248
+ ...createQueryHelpers(scope),
5249
+ completePendingEffect: async ({ effect, ...rest }) => {
5250
+ const match = (await reads.pendingEffects(rest.instanceId)).find((e) => e.name === effect);
5251
+ if (match === void 0)
5252
+ throw new Error(`completePendingEffect: no pending effect named "${effect}"`);
5253
+ return engine.completeEffect({ ...rest, effectKey: match._key });
5254
+ }
5228
5255
  };
5229
5256
  }
5230
5257
  function assertIsoInstant(iso) {
@@ -5238,4 +5265,6 @@ exports.ALL_ACCESS_USER = ALL_ACCESS_USER;
5238
5265
  exports.DEFAULT_WORKFLOW_RESOURCE = DEFAULT_WORKFLOW_RESOURCE;
5239
5266
  exports.WILDCARD_GRANTS = WILDCARD_GRANTS;
5240
5267
  exports.createBench = createBench;
5268
+ exports.releaseField = releaseField;
5269
+ exports.subjectField = subjectField;
5241
5270
  //# sourceMappingURL=index.cjs.map