@sanity/workflow-engine-test 0.11.0 → 0.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/CHANGELOG.md +65 -0
- package/README.md +9 -7
- package/dist/index.cjs +77 -8
- package/dist/index.d.cts +96 -16
- package/dist/index.d.ts +96 -16
- package/dist/index.js +76 -9
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# @sanity/workflow-engine-test
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 00c089d: **BREAKING:** Require every definition deployment and workflow deployment configuration to state an `expectedMinReaderModel` literal that exactly matches the installed engine's writer capability.
|
|
8
|
+
|
|
9
|
+
Deploy and definition-diff paths now reject missing or stale acknowledgements before client access or telemetry, with a structured error and readers-first rollout guidance.
|
|
10
|
+
|
|
11
|
+
- 55a54b3: Surfaces understand the new `subject` field kind. Studio plugin: the start dialog's default subject seed lands on a definition's `subject`-kind entry whatever it is named (the doc.ref-named-`subject` convention stays the fallback), subject entries get the document picker / read-only face / state-panel preview / document pill, and mapping validation checks the subject entry's `types` facet. MCP: the subject lookup behind title enrichment prefers the `subject`-kind entry and keeps the name convention as fallback; the authoring guide teaches the kind. Test bench — **BREAKING:** `subjectField` now builds a `subject`-KIND entry value (matching its name and the engine's subject rule); a definition declaring the doc.ref convention needs the new `docRefField(documentId, {name})` instead. `instancesForSubject` matches both single-ref kinds.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 71cd58e: Explain that effect-completion field operations require an explicit workflow or stage target scope when validation rejects a missing scope, and document the requirement on `EffectHandler`.
|
|
16
|
+
- 9126299: **BREAKING:** Delete persisted stage guard documents when their stage exits, and remove the lifted-record `GUARD_LIFTED_PREDICATE` and `isGuardLifted` exports.
|
|
17
|
+
|
|
18
|
+
Guard deletion now uses the observed document revision so stale exit reconciliation cannot delete a guard reactivated by a newer stage visit. Reactive guard streams contain only active persisted guards.
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [239d9f6]
|
|
21
|
+
- Updated dependencies [3af2ca0]
|
|
22
|
+
- Updated dependencies [7276702]
|
|
23
|
+
- Updated dependencies [58f8211]
|
|
24
|
+
- Updated dependencies [00c089d]
|
|
25
|
+
- Updated dependencies [71cd58e]
|
|
26
|
+
- Updated dependencies [cd03973]
|
|
27
|
+
- Updated dependencies [d26cc1a]
|
|
28
|
+
- Updated dependencies [9126299]
|
|
29
|
+
- Updated dependencies [72018af]
|
|
30
|
+
- Updated dependencies [5cd8ffd]
|
|
31
|
+
- Updated dependencies [b82bda4]
|
|
32
|
+
- Updated dependencies [55a54b3]
|
|
33
|
+
- Updated dependencies [b82bda4]
|
|
34
|
+
- Updated dependencies [4490442]
|
|
35
|
+
- Updated dependencies [1540dce]
|
|
36
|
+
- @sanity/workflow-engine@0.18.0
|
|
37
|
+
|
|
38
|
+
## 0.12.0
|
|
39
|
+
|
|
40
|
+
### Minor Changes
|
|
41
|
+
|
|
42
|
+
- 092a0d4: **BREAKING:** runtime-supplied refs written into field state are gated on the deployment's declared resource surface.
|
|
43
|
+
- 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.
|
|
44
|
+
- 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.
|
|
45
|
+
- 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.
|
|
46
|
+
- `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.
|
|
47
|
+
- The CLI wires no `resourceClients`, so refs supplied at runtime through it (`start --field`, `fire-action` ref params) admit the workflow resource only.
|
|
48
|
+
|
|
49
|
+
- 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).
|
|
50
|
+
- 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.
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- 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.
|
|
55
|
+
- Updated dependencies [f9389e5]
|
|
56
|
+
- Updated dependencies [d0c62ea]
|
|
57
|
+
- Updated dependencies [c3eed2e]
|
|
58
|
+
- Updated dependencies [df4bd80]
|
|
59
|
+
- Updated dependencies [092a0d4]
|
|
60
|
+
- Updated dependencies [5a1a9fe]
|
|
61
|
+
- Updated dependencies [e3a7ba2]
|
|
62
|
+
- Updated dependencies [30fed9e]
|
|
63
|
+
- Updated dependencies [1321ba5]
|
|
64
|
+
- Updated dependencies [e683875]
|
|
65
|
+
- Updated dependencies [a8ace4d]
|
|
66
|
+
- @sanity/workflow-engine@0.17.0
|
|
67
|
+
|
|
3
68
|
## 0.11.0
|
|
4
69
|
|
|
5
70
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -18,7 +18,9 @@ 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,
|
|
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
25
|
- **Runtime verbs** — `deployDefinitions`, `deleteDefinition`, `startInstance`,
|
|
24
26
|
`fireAction`, `editField`, `completeEffect` (and `completePendingEffect`,
|
|
@@ -28,14 +30,14 @@ default, and clock, so a test reads as the workflow it exercises:
|
|
|
28
30
|
not wall-clock.
|
|
29
31
|
- **Reads and probes** — `getInstance`, `currentStage`, `activityStatus`,
|
|
30
32
|
`listPendingEffects`, `findPendingEffects`, `children`, `instancesForSubject`,
|
|
31
|
-
`instancesByStage`, `instancesForDocument`, `definitionsForDocument`,
|
|
32
|
-
plus the guard helpers below. Helpers sharing an
|
|
33
|
-
engine's own args objects and throw its typed
|
|
34
|
-
reads cannot drift.
|
|
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.
|
|
35
37
|
|
|
36
38
|
```ts
|
|
37
39
|
const bench = createBench({now: '2026-01-01T00:00:00Z'})
|
|
38
|
-
await bench.deployDefinitions({definitions: [reviewWorkflow]})
|
|
40
|
+
await bench.deployDefinitions({expectedMinReaderModel: 2, definitions: [reviewWorkflow]})
|
|
39
41
|
|
|
40
42
|
const {instance} = await bench.startInstance({
|
|
41
43
|
definition: 'review-workflow',
|
|
@@ -70,7 +72,7 @@ raw `workflow.*` calls, and the default tag ships as the `BENCH_TAG` constant.
|
|
|
70
72
|
## Mutation guards
|
|
71
73
|
|
|
72
74
|
A workflow stage can deploy **mutation guards** — `temp.system.guard` documents
|
|
73
|
-
that lock the documents it governs while the stage is active, and
|
|
75
|
+
that lock the documents it governs while the stage is active, and are deleted when it
|
|
74
76
|
exits. The lake does not enforce this doc type yet: in production, guard
|
|
75
77
|
enforcement is the engine's own **optimistic** evaluation (advisory verdicts
|
|
76
78
|
and engine-side write denials), and the lake ACL is the only hard boundary.
|
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: !0
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
var workflowEngine = require("@sanity/workflow-engine"), clientFakeForTest = require("@sanity-labs/client-fake-for-test");
|
|
7
|
+
var workflowEngine = require("@sanity/workflow-engine"), clientFakeForTest = require("@sanity-labs/client-fake-for-test"), define = require("@sanity/workflow-engine/define");
|
|
8
8
|
|
|
9
9
|
const DEFAULT_WORKFLOW_RESOURCE = {
|
|
10
10
|
type: "dataset",
|
|
@@ -304,7 +304,7 @@ function createReadHelpers(scope) {
|
|
|
304
304
|
...engineScope,
|
|
305
305
|
...args
|
|
306
306
|
}),
|
|
307
|
-
instancesForSubject: async (subjectRef, entryName = "subject") => client.fetch(`*[_type == "${workflowEngine.WORKFLOW_INSTANCE_TYPE}"\n && fields[_type
|
|
307
|
+
instancesForSubject: async (subjectRef, entryName = "subject") => client.fetch(`*[_type == "${workflowEngine.WORKFLOW_INSTANCE_TYPE}"\n && fields[_type in ["doc.ref", "subject"] && name == $entry][0].value.id == $ref\n && ${workflowEngine.tagScopeFilter()}] | order(startedAt asc)`, {
|
|
308
308
|
ref: subjectRef,
|
|
309
309
|
entry: entryName,
|
|
310
310
|
tag: tag
|
|
@@ -327,6 +327,13 @@ function createReadHelpers(scope) {
|
|
|
327
327
|
clock: clock
|
|
328
328
|
} : {}
|
|
329
329
|
}),
|
|
330
|
+
evaluateStart: args => workflowEngine.workflow.evaluateStart({
|
|
331
|
+
...engineScope,
|
|
332
|
+
...args,
|
|
333
|
+
...clock !== void 0 ? {
|
|
334
|
+
clock: clock
|
|
335
|
+
} : {}
|
|
336
|
+
}),
|
|
330
337
|
snapshot: () => client.snapshot()
|
|
331
338
|
};
|
|
332
339
|
}
|
|
@@ -335,6 +342,9 @@ function createBenchEngine(bench, overrides = {}) {
|
|
|
335
342
|
return workflowEngine.createEngine({
|
|
336
343
|
client: bench.client,
|
|
337
344
|
workflowResource: bench.workflowResource,
|
|
345
|
+
...bench.resourceClients !== void 0 ? {
|
|
346
|
+
resourceClients: bench.resourceClients
|
|
347
|
+
} : {},
|
|
338
348
|
tag: bench.tag,
|
|
339
349
|
clock: bench.now,
|
|
340
350
|
executionContext: bench.executionContext,
|
|
@@ -344,7 +354,7 @@ function createBenchEngine(bench, overrides = {}) {
|
|
|
344
354
|
|
|
345
355
|
function subjectField(documentId, opts) {
|
|
346
356
|
return {
|
|
347
|
-
type: "
|
|
357
|
+
type: "subject",
|
|
348
358
|
name: opts?.name ?? "subject",
|
|
349
359
|
value: workflowEngine.gdrRef({
|
|
350
360
|
res: DEFAULT_WORKFLOW_RESOURCE,
|
|
@@ -354,6 +364,18 @@ function subjectField(documentId, opts) {
|
|
|
354
364
|
};
|
|
355
365
|
}
|
|
356
366
|
|
|
367
|
+
function docRefField(documentId, opts) {
|
|
368
|
+
return {
|
|
369
|
+
type: "doc.ref",
|
|
370
|
+
name: opts.name,
|
|
371
|
+
value: workflowEngine.gdrRef({
|
|
372
|
+
res: DEFAULT_WORKFLOW_RESOURCE,
|
|
373
|
+
documentId: documentId,
|
|
374
|
+
type: opts.type ?? "document"
|
|
375
|
+
})
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
357
379
|
function releaseField(releaseName, opts) {
|
|
358
380
|
return {
|
|
359
381
|
type: "release.ref",
|
|
@@ -365,6 +387,35 @@ function releaseField(releaseName, opts) {
|
|
|
365
387
|
};
|
|
366
388
|
}
|
|
367
389
|
|
|
390
|
+
async function startedInstanceFixture(bench, opts) {
|
|
391
|
+
const flow = define.defineWorkflow({
|
|
392
|
+
name: "fixture-flow",
|
|
393
|
+
title: "Fixture flow",
|
|
394
|
+
initialStage: "review",
|
|
395
|
+
stages: [ {
|
|
396
|
+
name: "review",
|
|
397
|
+
transitions: [ {
|
|
398
|
+
name: "go",
|
|
399
|
+
to: "done",
|
|
400
|
+
when: "false"
|
|
401
|
+
} ]
|
|
402
|
+
}, {
|
|
403
|
+
name: "done"
|
|
404
|
+
} ]
|
|
405
|
+
});
|
|
406
|
+
await bench.deployDefinitions({
|
|
407
|
+
expectedMinReaderModel: 2,
|
|
408
|
+
definitions: [ flow ]
|
|
409
|
+
});
|
|
410
|
+
const {instance: instance} = await bench.startInstance({
|
|
411
|
+
definition: flow.name,
|
|
412
|
+
...opts?.instanceId !== void 0 ? {
|
|
413
|
+
instanceId: opts.instanceId
|
|
414
|
+
} : {}
|
|
415
|
+
});
|
|
416
|
+
return instance;
|
|
417
|
+
}
|
|
418
|
+
|
|
368
419
|
function createBench(options = {}) {
|
|
369
420
|
const tag = options.tag ?? BENCH_TAG;
|
|
370
421
|
workflowEngine.validateTag(tag);
|
|
@@ -376,10 +427,16 @@ function createBench(options = {}) {
|
|
|
376
427
|
documents: options.documents
|
|
377
428
|
} : {},
|
|
378
429
|
defaultAccess: access
|
|
379
|
-
}), identities = createIdentityDirectory(client),
|
|
430
|
+
}), identities = createIdentityDirectory(client), resourceClients = benchResourceClients({
|
|
431
|
+
client: client,
|
|
432
|
+
options: options
|
|
433
|
+
}), scope = {
|
|
380
434
|
client: client,
|
|
381
435
|
tag: tag,
|
|
382
436
|
workflowResource: workflowResource,
|
|
437
|
+
...resourceClients !== void 0 ? {
|
|
438
|
+
resourceClients: resourceClients
|
|
439
|
+
} : {},
|
|
383
440
|
executionContext: options.executionContext ?? {
|
|
384
441
|
kind: workflowEngine.EXECUTION_KINDS.test
|
|
385
442
|
}
|
|
@@ -395,10 +452,7 @@ function createBench(options = {}) {
|
|
|
395
452
|
clock: clock
|
|
396
453
|
});
|
|
397
454
|
return {
|
|
398
|
-
|
|
399
|
-
tag: tag,
|
|
400
|
-
workflowResource: workflowResource,
|
|
401
|
-
executionContext: scope.executionContext,
|
|
455
|
+
...scope,
|
|
402
456
|
access: access,
|
|
403
457
|
currentUser: access.actor,
|
|
404
458
|
grants: access.grants ?? [],
|
|
@@ -433,6 +487,17 @@ function createBench(options = {}) {
|
|
|
433
487
|
};
|
|
434
488
|
}
|
|
435
489
|
|
|
490
|
+
function benchResourceClients(args) {
|
|
491
|
+
const {client: client} = args, {serveResources: serveResources, resourceClients: resourceClients} = args.options;
|
|
492
|
+
if (serveResources === void 0 || serveResources.length === 0) return resourceClients;
|
|
493
|
+
const nonDataset = serveResources.find(served => served.type !== "dataset");
|
|
494
|
+
if (nonDataset !== void 0) throw new Error(`createBench: serveResources can only serve dataset siblings of the bench store — got ${nonDataset.type} "${nonDataset.id}". Pass resourceClients for other resource types.`);
|
|
495
|
+
return parsed => {
|
|
496
|
+
const target = workflowEngine.resourceFromParsed(parsed);
|
|
497
|
+
return serveResources.some(served => workflowEngine.sameResource(served, target)) ? client.withConfig(workflowEngine.datasetResourceParts(target.id)) : resourceClients?.(parsed);
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
436
501
|
function assertIsoInstant(iso) {
|
|
437
502
|
const ms = Date.parse(iso);
|
|
438
503
|
if (Number.isNaN(ms)) throw new Error(`bench clock: expected an ISO-8601 instant, got ${JSON.stringify(iso)}`);
|
|
@@ -453,6 +518,10 @@ exports.createBench = createBench;
|
|
|
453
518
|
|
|
454
519
|
exports.createBenchEngine = createBenchEngine;
|
|
455
520
|
|
|
521
|
+
exports.docRefField = docRefField;
|
|
522
|
+
|
|
456
523
|
exports.releaseField = releaseField;
|
|
457
524
|
|
|
525
|
+
exports.startedInstanceFixture = startedInstanceFixture;
|
|
526
|
+
|
|
458
527
|
exports.subjectField = subjectField;
|
package/dist/index.d.cts
CHANGED
|
@@ -15,6 +15,7 @@ import { DeployedDefinition } from "@sanity/workflow-engine";
|
|
|
15
15
|
import type { EditFieldArgs } from "@sanity/workflow-engine";
|
|
16
16
|
import { Engine } from "@sanity/workflow-engine";
|
|
17
17
|
import type { EvaluateArgs } from "@sanity/workflow-engine";
|
|
18
|
+
import { EvaluateStartArgs } from "@sanity/workflow-engine";
|
|
18
19
|
import { FindPendingEffectsArgs } from "@sanity/workflow-engine";
|
|
19
20
|
import type { FireActionArgs } from "@sanity/workflow-engine";
|
|
20
21
|
import type { Grant } from "@sanity/workflow-engine";
|
|
@@ -27,10 +28,13 @@ import { MutationGuardDoc } from "@sanity/workflow-engine";
|
|
|
27
28
|
import type { OperationArgs } from "@sanity/workflow-engine";
|
|
28
29
|
import { OperationResult } from "@sanity/workflow-engine";
|
|
29
30
|
import { PendingEffect } from "@sanity/workflow-engine";
|
|
31
|
+
import type { ResourceClientResolver } from "@sanity/workflow-engine";
|
|
30
32
|
import type { SetStageArgs } from "@sanity/workflow-engine";
|
|
33
|
+
import { StartEvaluation } from "@sanity/workflow-engine";
|
|
31
34
|
import type { StartInstanceArgs } from "@sanity/workflow-engine";
|
|
32
35
|
import type { TestClient } from "@sanity-labs/client-fake-for-test";
|
|
33
36
|
import type { WorkflowAccess } from "@sanity/workflow-engine";
|
|
37
|
+
import { WorkflowDefinition } from "@sanity/workflow-engine";
|
|
34
38
|
import { WorkflowDefinitionInput } from "@sanity/workflow-engine";
|
|
35
39
|
import { WorkflowEvaluation } from "@sanity/workflow-engine";
|
|
36
40
|
import { WorkflowInstance } from "@sanity/workflow-engine";
|
|
@@ -110,6 +114,9 @@ declare interface BenchState {
|
|
|
110
114
|
tag: string;
|
|
111
115
|
/** The workflow resource every wrapped call targets. */
|
|
112
116
|
workflowResource: WorkflowResource;
|
|
117
|
+
/** The cross-resource read routing every wrapped call carries, when the
|
|
118
|
+
* bench declared any — see {@link CreateBenchOptions.resourceClients}. */
|
|
119
|
+
resourceClients?: ResourceClientResolver;
|
|
113
120
|
/** The declared execution context stamped on every wrapped call's history
|
|
114
121
|
* entries (default `{kind: 'test'}`) — inherited by {@link createBenchEngine}. */
|
|
115
122
|
executionContext: DeclaredExecutionContext;
|
|
@@ -234,6 +241,23 @@ export declare interface CreateBenchOptions {
|
|
|
234
241
|
* URIs read as `dataset:test:test:<docId>`.
|
|
235
242
|
*/
|
|
236
243
|
workflowResource?: WorkflowResource;
|
|
244
|
+
/**
|
|
245
|
+
* Sibling datasets the bench serves (and thereby declares) from its own
|
|
246
|
+
* store — the common cross-resource case. Each listed resource routes to
|
|
247
|
+
* a `withConfig`-derived handle over the bench's store, so reads reach it
|
|
248
|
+
* and runtime-supplied refs into it are admitted. Only `dataset`
|
|
249
|
+
* resources can be siblings of the bench's store; anything else needs
|
|
250
|
+
* {@link CreateBenchOptions.resourceClients}.
|
|
251
|
+
*/
|
|
252
|
+
serveResources?: WorkflowResource[];
|
|
253
|
+
/**
|
|
254
|
+
* Cross-resource read routing for the bench engine scope — a served
|
|
255
|
+
* resource is also part of the declared surface for written refs. The
|
|
256
|
+
* escape hatch for resolver logic beyond same-store siblings; prefer
|
|
257
|
+
* {@link CreateBenchOptions.serveResources}, which is consulted first
|
|
258
|
+
* when both are given. Omit for single-resource benches.
|
|
259
|
+
*/
|
|
260
|
+
resourceClients?: ResourceClientResolver;
|
|
237
261
|
/**
|
|
238
262
|
* Freeze the bench clock at this ISO instant from construction, so
|
|
239
263
|
* `$now` and every engine timestamp are deterministic. Equivalent to
|
|
@@ -252,6 +276,19 @@ export declare interface CreateBenchOptions {
|
|
|
252
276
|
/** Default workflow resource for benches that don't pass one. */
|
|
253
277
|
export declare const DEFAULT_WORKFLOW_RESOURCE: WorkflowResource;
|
|
254
278
|
|
|
279
|
+
/**
|
|
280
|
+
* A plain `doc.ref` entry value in the bench's default resource — for
|
|
281
|
+
* document-reference inputs that are NOT the workflow's subject (the
|
|
282
|
+
* `subject` kind is what marks THE subject; see {@link subjectField}).
|
|
283
|
+
*/
|
|
284
|
+
export declare function docRefField(
|
|
285
|
+
documentId: string,
|
|
286
|
+
opts: {
|
|
287
|
+
name: string;
|
|
288
|
+
type?: string;
|
|
289
|
+
},
|
|
290
|
+
): InitialFieldValue;
|
|
291
|
+
|
|
255
292
|
/**
|
|
256
293
|
* The write {@link GuardHelpers.editDocument} performs. Every arm is a real
|
|
257
294
|
* distinct client write the guard seam vets under that action — `create` is
|
|
@@ -281,6 +318,7 @@ declare type EngineScope = {
|
|
|
281
318
|
client: TestClient;
|
|
282
319
|
tag: string;
|
|
283
320
|
workflowResource: WorkflowResource;
|
|
321
|
+
resourceClients?: ResourceClientResolver;
|
|
284
322
|
executionContext: DeclaredExecutionContext;
|
|
285
323
|
};
|
|
286
324
|
|
|
@@ -306,13 +344,28 @@ declare type EngineWrappers = {
|
|
|
306
344
|
guardsForDefinition: (definition: string) => Promise<MutationGuardDoc[]>;
|
|
307
345
|
};
|
|
308
346
|
|
|
347
|
+
/** The slice of a bench {@link startedInstanceFixture} drives — structural,
|
|
348
|
+
* so the fixture module needs no import of the bench itself. */
|
|
349
|
+
declare interface FixtureBench {
|
|
350
|
+
deployDefinitions: (args: {
|
|
351
|
+
expectedMinReaderModel: 2;
|
|
352
|
+
definitions: WorkflowDefinition[];
|
|
353
|
+
}) => Promise<unknown>;
|
|
354
|
+
startInstance: (args: {
|
|
355
|
+
definition: string;
|
|
356
|
+
instanceId?: string;
|
|
357
|
+
}) => Promise<{
|
|
358
|
+
instance: WorkflowInstance;
|
|
359
|
+
}>;
|
|
360
|
+
}
|
|
361
|
+
|
|
309
362
|
declare type GuardHelpers = {
|
|
310
363
|
/** All deployed guard docs ({@link MutationGuardDoc}) in the store. */
|
|
311
364
|
listGuards: () => Promise<MutationGuardDoc[]>;
|
|
312
365
|
/**
|
|
313
366
|
* Guards that would deny an arbitrary edit to `documentId` right now (i.e.
|
|
314
|
-
* active locks). Probes by changing every non-system field; a
|
|
315
|
-
*
|
|
367
|
+
* active locks). Probes by changing every non-system field; a guard whose
|
|
368
|
+
* predicate is `"true"` returns nothing.
|
|
316
369
|
*/
|
|
317
370
|
activeGuardsForDocument: (
|
|
318
371
|
documentId: string,
|
|
@@ -358,11 +411,13 @@ declare type QueryHelpers = {
|
|
|
358
411
|
) => Promise<T>;
|
|
359
412
|
/**
|
|
360
413
|
* Snapshot-aware GROQ for `instanceId` — runs against the same
|
|
361
|
-
* in-memory view the engine's filters see
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
414
|
+
* in-memory view the engine's filters see, with the caller-free
|
|
415
|
+
* rendered scope auto-bound: every always-bound condition var,
|
|
416
|
+
* `$fields` with the open stage's overlay merged, `$assigned` at its
|
|
417
|
+
* caller-free `false`, and the author's pre-evaluated `$<predicate>`
|
|
418
|
+
* booleans, ids in GDR URI form. Use to ask "what does the engine see
|
|
419
|
+
* for this workflow?" — useful for filter debugging, UIs that mirror
|
|
420
|
+
* engine state, etc.
|
|
366
421
|
*/
|
|
367
422
|
queryInScope: <T = unknown>({
|
|
368
423
|
instanceId,
|
|
@@ -406,10 +461,10 @@ declare type ReadHelpers = {
|
|
|
406
461
|
*/
|
|
407
462
|
children: (args: ChildrenArgs) => Promise<WorkflowInstance[]>;
|
|
408
463
|
/**
|
|
409
|
-
* All workflow instances whose
|
|
410
|
-
* `entryName` (default `"subject"`) matches the given doc
|
|
411
|
-
* for "what workflow is running on this document?" questions.
|
|
412
|
-
* `entryName` for workflows whose subject entry uses a different name.
|
|
464
|
+
* All workflow instances whose single-ref field entry (`subject` kind or
|
|
465
|
+
* `doc.ref`) named `entryName` (default `"subject"`) matches the given doc
|
|
466
|
+
* id. Useful for "what workflow is running on this document?" questions.
|
|
467
|
+
* Pass `entryName` for workflows whose subject entry uses a different name.
|
|
413
468
|
*/
|
|
414
469
|
instancesForSubject: (
|
|
415
470
|
subjectRef: string,
|
|
@@ -437,12 +492,20 @@ declare type ReadHelpers = {
|
|
|
437
492
|
/**
|
|
438
493
|
* The startable half of {@link ReadHelpers.instancesForDocument}: the
|
|
439
494
|
* latest deployed version of every definition applicable to the LOADED
|
|
440
|
-
* candidate document — startable,
|
|
441
|
-
* `_type`, `start.filter` passes. All matches, name
|
|
495
|
+
* candidate document — startable, the `subject`-kind entry accepts its
|
|
496
|
+
* `_type`, `start.filter` (browse-time-pure) passes. All matches, name
|
|
497
|
+
* ascending.
|
|
442
498
|
*/
|
|
443
499
|
definitionsForDocument: (
|
|
444
500
|
args: DefinitionsForDocumentArgs,
|
|
445
501
|
) => Promise<DeployedDefinition[]>;
|
|
502
|
+
/**
|
|
503
|
+
* Pre-flight `startInstance`'s gates for a definition + candidate
|
|
504
|
+
* `initialFields`: the `start.allowed` verdict (with its insight) plus the
|
|
505
|
+
* still-missing required inputs — the read a start surface renders before
|
|
506
|
+
* committing.
|
|
507
|
+
*/
|
|
508
|
+
evaluateStart: (args: EvaluateStartArgs) => Promise<StartEvaluation>;
|
|
446
509
|
/** Snapshot of all documents in the store — for inspecting the world. */
|
|
447
510
|
snapshot: () => readonly StoreDocument[];
|
|
448
511
|
};
|
|
@@ -459,6 +522,20 @@ export declare function releaseField(
|
|
|
459
522
|
},
|
|
460
523
|
): InitialFieldValue;
|
|
461
524
|
|
|
525
|
+
/**
|
|
526
|
+
* A REAL, minimal started instance — deployed and started through the bench's
|
|
527
|
+
* actual verbs, parked forever on its first stage. The fixture for consumers
|
|
528
|
+
* (adapter tests, validation tests) that need a persisted-shape-valid
|
|
529
|
+
* instance document without caring about its workflow: hand-crafted instance
|
|
530
|
+
* stubs do not pass the engine's read parse.
|
|
531
|
+
*/
|
|
532
|
+
export declare function startedInstanceFixture(
|
|
533
|
+
bench: FixtureBench,
|
|
534
|
+
opts?: {
|
|
535
|
+
instanceId?: string;
|
|
536
|
+
},
|
|
537
|
+
): Promise<WorkflowInstance>;
|
|
538
|
+
|
|
462
539
|
/** A document as stored in the bench's fake lake. */
|
|
463
540
|
declare type StoreDocument = {
|
|
464
541
|
_id: string;
|
|
@@ -467,9 +544,12 @@ declare type StoreDocument = {
|
|
|
467
544
|
};
|
|
468
545
|
|
|
469
546
|
/**
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
547
|
+
* A workflow-scope `subject`-KIND entry value, keyed by a bare doc id in the
|
|
548
|
+
* bench's default resource — what a definition declaring the first-class
|
|
549
|
+
* `subject` kind expects at start (applicability and document pickers key on
|
|
550
|
+
* that kind). `type` defaults to `'document'`; pass the subject's schema
|
|
551
|
+
* type when a filter or projection reads it. For a plain `doc.ref` input,
|
|
552
|
+
* use {@link docRefField}.
|
|
473
553
|
*/
|
|
474
554
|
export declare function subjectField(
|
|
475
555
|
documentId: string,
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { DeployedDefinition } from "@sanity/workflow-engine";
|
|
|
15
15
|
import type { EditFieldArgs } from "@sanity/workflow-engine";
|
|
16
16
|
import { Engine } from "@sanity/workflow-engine";
|
|
17
17
|
import type { EvaluateArgs } from "@sanity/workflow-engine";
|
|
18
|
+
import { EvaluateStartArgs } from "@sanity/workflow-engine";
|
|
18
19
|
import { FindPendingEffectsArgs } from "@sanity/workflow-engine";
|
|
19
20
|
import type { FireActionArgs } from "@sanity/workflow-engine";
|
|
20
21
|
import type { Grant } from "@sanity/workflow-engine";
|
|
@@ -27,10 +28,13 @@ import { MutationGuardDoc } from "@sanity/workflow-engine";
|
|
|
27
28
|
import type { OperationArgs } from "@sanity/workflow-engine";
|
|
28
29
|
import { OperationResult } from "@sanity/workflow-engine";
|
|
29
30
|
import { PendingEffect } from "@sanity/workflow-engine";
|
|
31
|
+
import type { ResourceClientResolver } from "@sanity/workflow-engine";
|
|
30
32
|
import type { SetStageArgs } from "@sanity/workflow-engine";
|
|
33
|
+
import { StartEvaluation } from "@sanity/workflow-engine";
|
|
31
34
|
import type { StartInstanceArgs } from "@sanity/workflow-engine";
|
|
32
35
|
import type { TestClient } from "@sanity-labs/client-fake-for-test";
|
|
33
36
|
import type { WorkflowAccess } from "@sanity/workflow-engine";
|
|
37
|
+
import { WorkflowDefinition } from "@sanity/workflow-engine";
|
|
34
38
|
import { WorkflowDefinitionInput } from "@sanity/workflow-engine";
|
|
35
39
|
import { WorkflowEvaluation } from "@sanity/workflow-engine";
|
|
36
40
|
import { WorkflowInstance } from "@sanity/workflow-engine";
|
|
@@ -110,6 +114,9 @@ declare interface BenchState {
|
|
|
110
114
|
tag: string;
|
|
111
115
|
/** The workflow resource every wrapped call targets. */
|
|
112
116
|
workflowResource: WorkflowResource;
|
|
117
|
+
/** The cross-resource read routing every wrapped call carries, when the
|
|
118
|
+
* bench declared any — see {@link CreateBenchOptions.resourceClients}. */
|
|
119
|
+
resourceClients?: ResourceClientResolver;
|
|
113
120
|
/** The declared execution context stamped on every wrapped call's history
|
|
114
121
|
* entries (default `{kind: 'test'}`) — inherited by {@link createBenchEngine}. */
|
|
115
122
|
executionContext: DeclaredExecutionContext;
|
|
@@ -234,6 +241,23 @@ export declare interface CreateBenchOptions {
|
|
|
234
241
|
* URIs read as `dataset:test:test:<docId>`.
|
|
235
242
|
*/
|
|
236
243
|
workflowResource?: WorkflowResource;
|
|
244
|
+
/**
|
|
245
|
+
* Sibling datasets the bench serves (and thereby declares) from its own
|
|
246
|
+
* store — the common cross-resource case. Each listed resource routes to
|
|
247
|
+
* a `withConfig`-derived handle over the bench's store, so reads reach it
|
|
248
|
+
* and runtime-supplied refs into it are admitted. Only `dataset`
|
|
249
|
+
* resources can be siblings of the bench's store; anything else needs
|
|
250
|
+
* {@link CreateBenchOptions.resourceClients}.
|
|
251
|
+
*/
|
|
252
|
+
serveResources?: WorkflowResource[];
|
|
253
|
+
/**
|
|
254
|
+
* Cross-resource read routing for the bench engine scope — a served
|
|
255
|
+
* resource is also part of the declared surface for written refs. The
|
|
256
|
+
* escape hatch for resolver logic beyond same-store siblings; prefer
|
|
257
|
+
* {@link CreateBenchOptions.serveResources}, which is consulted first
|
|
258
|
+
* when both are given. Omit for single-resource benches.
|
|
259
|
+
*/
|
|
260
|
+
resourceClients?: ResourceClientResolver;
|
|
237
261
|
/**
|
|
238
262
|
* Freeze the bench clock at this ISO instant from construction, so
|
|
239
263
|
* `$now` and every engine timestamp are deterministic. Equivalent to
|
|
@@ -252,6 +276,19 @@ export declare interface CreateBenchOptions {
|
|
|
252
276
|
/** Default workflow resource for benches that don't pass one. */
|
|
253
277
|
export declare const DEFAULT_WORKFLOW_RESOURCE: WorkflowResource;
|
|
254
278
|
|
|
279
|
+
/**
|
|
280
|
+
* A plain `doc.ref` entry value in the bench's default resource — for
|
|
281
|
+
* document-reference inputs that are NOT the workflow's subject (the
|
|
282
|
+
* `subject` kind is what marks THE subject; see {@link subjectField}).
|
|
283
|
+
*/
|
|
284
|
+
export declare function docRefField(
|
|
285
|
+
documentId: string,
|
|
286
|
+
opts: {
|
|
287
|
+
name: string;
|
|
288
|
+
type?: string;
|
|
289
|
+
},
|
|
290
|
+
): InitialFieldValue;
|
|
291
|
+
|
|
255
292
|
/**
|
|
256
293
|
* The write {@link GuardHelpers.editDocument} performs. Every arm is a real
|
|
257
294
|
* distinct client write the guard seam vets under that action — `create` is
|
|
@@ -281,6 +318,7 @@ declare type EngineScope = {
|
|
|
281
318
|
client: TestClient;
|
|
282
319
|
tag: string;
|
|
283
320
|
workflowResource: WorkflowResource;
|
|
321
|
+
resourceClients?: ResourceClientResolver;
|
|
284
322
|
executionContext: DeclaredExecutionContext;
|
|
285
323
|
};
|
|
286
324
|
|
|
@@ -306,13 +344,28 @@ declare type EngineWrappers = {
|
|
|
306
344
|
guardsForDefinition: (definition: string) => Promise<MutationGuardDoc[]>;
|
|
307
345
|
};
|
|
308
346
|
|
|
347
|
+
/** The slice of a bench {@link startedInstanceFixture} drives — structural,
|
|
348
|
+
* so the fixture module needs no import of the bench itself. */
|
|
349
|
+
declare interface FixtureBench {
|
|
350
|
+
deployDefinitions: (args: {
|
|
351
|
+
expectedMinReaderModel: 2;
|
|
352
|
+
definitions: WorkflowDefinition[];
|
|
353
|
+
}) => Promise<unknown>;
|
|
354
|
+
startInstance: (args: {
|
|
355
|
+
definition: string;
|
|
356
|
+
instanceId?: string;
|
|
357
|
+
}) => Promise<{
|
|
358
|
+
instance: WorkflowInstance;
|
|
359
|
+
}>;
|
|
360
|
+
}
|
|
361
|
+
|
|
309
362
|
declare type GuardHelpers = {
|
|
310
363
|
/** All deployed guard docs ({@link MutationGuardDoc}) in the store. */
|
|
311
364
|
listGuards: () => Promise<MutationGuardDoc[]>;
|
|
312
365
|
/**
|
|
313
366
|
* Guards that would deny an arbitrary edit to `documentId` right now (i.e.
|
|
314
|
-
* active locks). Probes by changing every non-system field; a
|
|
315
|
-
*
|
|
367
|
+
* active locks). Probes by changing every non-system field; a guard whose
|
|
368
|
+
* predicate is `"true"` returns nothing.
|
|
316
369
|
*/
|
|
317
370
|
activeGuardsForDocument: (
|
|
318
371
|
documentId: string,
|
|
@@ -358,11 +411,13 @@ declare type QueryHelpers = {
|
|
|
358
411
|
) => Promise<T>;
|
|
359
412
|
/**
|
|
360
413
|
* Snapshot-aware GROQ for `instanceId` — runs against the same
|
|
361
|
-
* in-memory view the engine's filters see
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
414
|
+
* in-memory view the engine's filters see, with the caller-free
|
|
415
|
+
* rendered scope auto-bound: every always-bound condition var,
|
|
416
|
+
* `$fields` with the open stage's overlay merged, `$assigned` at its
|
|
417
|
+
* caller-free `false`, and the author's pre-evaluated `$<predicate>`
|
|
418
|
+
* booleans, ids in GDR URI form. Use to ask "what does the engine see
|
|
419
|
+
* for this workflow?" — useful for filter debugging, UIs that mirror
|
|
420
|
+
* engine state, etc.
|
|
366
421
|
*/
|
|
367
422
|
queryInScope: <T = unknown>({
|
|
368
423
|
instanceId,
|
|
@@ -406,10 +461,10 @@ declare type ReadHelpers = {
|
|
|
406
461
|
*/
|
|
407
462
|
children: (args: ChildrenArgs) => Promise<WorkflowInstance[]>;
|
|
408
463
|
/**
|
|
409
|
-
* All workflow instances whose
|
|
410
|
-
* `entryName` (default `"subject"`) matches the given doc
|
|
411
|
-
* for "what workflow is running on this document?" questions.
|
|
412
|
-
* `entryName` for workflows whose subject entry uses a different name.
|
|
464
|
+
* All workflow instances whose single-ref field entry (`subject` kind or
|
|
465
|
+
* `doc.ref`) named `entryName` (default `"subject"`) matches the given doc
|
|
466
|
+
* id. Useful for "what workflow is running on this document?" questions.
|
|
467
|
+
* Pass `entryName` for workflows whose subject entry uses a different name.
|
|
413
468
|
*/
|
|
414
469
|
instancesForSubject: (
|
|
415
470
|
subjectRef: string,
|
|
@@ -437,12 +492,20 @@ declare type ReadHelpers = {
|
|
|
437
492
|
/**
|
|
438
493
|
* The startable half of {@link ReadHelpers.instancesForDocument}: the
|
|
439
494
|
* latest deployed version of every definition applicable to the LOADED
|
|
440
|
-
* candidate document — startable,
|
|
441
|
-
* `_type`, `start.filter` passes. All matches, name
|
|
495
|
+
* candidate document — startable, the `subject`-kind entry accepts its
|
|
496
|
+
* `_type`, `start.filter` (browse-time-pure) passes. All matches, name
|
|
497
|
+
* ascending.
|
|
442
498
|
*/
|
|
443
499
|
definitionsForDocument: (
|
|
444
500
|
args: DefinitionsForDocumentArgs,
|
|
445
501
|
) => Promise<DeployedDefinition[]>;
|
|
502
|
+
/**
|
|
503
|
+
* Pre-flight `startInstance`'s gates for a definition + candidate
|
|
504
|
+
* `initialFields`: the `start.allowed` verdict (with its insight) plus the
|
|
505
|
+
* still-missing required inputs — the read a start surface renders before
|
|
506
|
+
* committing.
|
|
507
|
+
*/
|
|
508
|
+
evaluateStart: (args: EvaluateStartArgs) => Promise<StartEvaluation>;
|
|
446
509
|
/** Snapshot of all documents in the store — for inspecting the world. */
|
|
447
510
|
snapshot: () => readonly StoreDocument[];
|
|
448
511
|
};
|
|
@@ -459,6 +522,20 @@ export declare function releaseField(
|
|
|
459
522
|
},
|
|
460
523
|
): InitialFieldValue;
|
|
461
524
|
|
|
525
|
+
/**
|
|
526
|
+
* A REAL, minimal started instance — deployed and started through the bench's
|
|
527
|
+
* actual verbs, parked forever on its first stage. The fixture for consumers
|
|
528
|
+
* (adapter tests, validation tests) that need a persisted-shape-valid
|
|
529
|
+
* instance document without caring about its workflow: hand-crafted instance
|
|
530
|
+
* stubs do not pass the engine's read parse.
|
|
531
|
+
*/
|
|
532
|
+
export declare function startedInstanceFixture(
|
|
533
|
+
bench: FixtureBench,
|
|
534
|
+
opts?: {
|
|
535
|
+
instanceId?: string;
|
|
536
|
+
},
|
|
537
|
+
): Promise<WorkflowInstance>;
|
|
538
|
+
|
|
462
539
|
/** A document as stored in the bench's fake lake. */
|
|
463
540
|
declare type StoreDocument = {
|
|
464
541
|
_id: string;
|
|
@@ -467,9 +544,12 @@ declare type StoreDocument = {
|
|
|
467
544
|
};
|
|
468
545
|
|
|
469
546
|
/**
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
547
|
+
* A workflow-scope `subject`-KIND entry value, keyed by a bare doc id in the
|
|
548
|
+
* bench's default resource — what a definition declaring the first-class
|
|
549
|
+
* `subject` kind expects at start (applicability and document pickers key on
|
|
550
|
+
* that kind). `type` defaults to `'document'`; pass the subject's schema
|
|
551
|
+
* type when a filter or projection reads it. For a plain `doc.ref` input,
|
|
552
|
+
* use {@link docRefField}.
|
|
473
553
|
*/
|
|
474
554
|
export declare function subjectField(
|
|
475
555
|
documentId: string,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { guardsForResource, denyingGuards, MutationGuardDeniedError, workflow, instancesQuery, WORKFLOW_INSTANCE_TYPE, tagScopeFilter, effectOutputsMap, contextMap, createEngine,
|
|
1
|
+
import { guardsForResource, denyingGuards, MutationGuardDeniedError, workflow, instancesQuery, WORKFLOW_INSTANCE_TYPE, tagScopeFilter, effectOutputsMap, contextMap, createEngine, gdrRef, releaseRef, validateTag, EXECUTION_KINDS, resourceFromParsed, sameResource, datasetResourceParts } from "@sanity/workflow-engine";
|
|
2
2
|
|
|
3
3
|
import { createTestClient, defaultAclPath, isDraftId, isVersionId } from "@sanity-labs/client-fake-for-test";
|
|
4
4
|
|
|
5
|
+
import { defineWorkflow } from "@sanity/workflow-engine/define";
|
|
6
|
+
|
|
5
7
|
const DEFAULT_WORKFLOW_RESOURCE = {
|
|
6
8
|
type: "dataset",
|
|
7
9
|
id: "test.test"
|
|
@@ -300,7 +302,7 @@ function createReadHelpers(scope) {
|
|
|
300
302
|
...engineScope,
|
|
301
303
|
...args
|
|
302
304
|
}),
|
|
303
|
-
instancesForSubject: async (subjectRef, entryName = "subject") => client.fetch(`*[_type == "${WORKFLOW_INSTANCE_TYPE}"\n && fields[_type
|
|
305
|
+
instancesForSubject: async (subjectRef, entryName = "subject") => client.fetch(`*[_type == "${WORKFLOW_INSTANCE_TYPE}"\n && fields[_type in ["doc.ref", "subject"] && name == $entry][0].value.id == $ref\n && ${tagScopeFilter()}] | order(startedAt asc)`, {
|
|
304
306
|
ref: subjectRef,
|
|
305
307
|
entry: entryName,
|
|
306
308
|
tag: tag
|
|
@@ -323,6 +325,13 @@ function createReadHelpers(scope) {
|
|
|
323
325
|
clock: clock
|
|
324
326
|
} : {}
|
|
325
327
|
}),
|
|
328
|
+
evaluateStart: args => workflow.evaluateStart({
|
|
329
|
+
...engineScope,
|
|
330
|
+
...args,
|
|
331
|
+
...clock !== void 0 ? {
|
|
332
|
+
clock: clock
|
|
333
|
+
} : {}
|
|
334
|
+
}),
|
|
326
335
|
snapshot: () => client.snapshot()
|
|
327
336
|
};
|
|
328
337
|
}
|
|
@@ -331,6 +340,9 @@ function createBenchEngine(bench, overrides = {}) {
|
|
|
331
340
|
return createEngine({
|
|
332
341
|
client: bench.client,
|
|
333
342
|
workflowResource: bench.workflowResource,
|
|
343
|
+
...bench.resourceClients !== void 0 ? {
|
|
344
|
+
resourceClients: bench.resourceClients
|
|
345
|
+
} : {},
|
|
334
346
|
tag: bench.tag,
|
|
335
347
|
clock: bench.now,
|
|
336
348
|
executionContext: bench.executionContext,
|
|
@@ -340,7 +352,7 @@ function createBenchEngine(bench, overrides = {}) {
|
|
|
340
352
|
|
|
341
353
|
function subjectField(documentId, opts) {
|
|
342
354
|
return {
|
|
343
|
-
type: "
|
|
355
|
+
type: "subject",
|
|
344
356
|
name: opts?.name ?? "subject",
|
|
345
357
|
value: gdrRef({
|
|
346
358
|
res: DEFAULT_WORKFLOW_RESOURCE,
|
|
@@ -350,6 +362,18 @@ function subjectField(documentId, opts) {
|
|
|
350
362
|
};
|
|
351
363
|
}
|
|
352
364
|
|
|
365
|
+
function docRefField(documentId, opts) {
|
|
366
|
+
return {
|
|
367
|
+
type: "doc.ref",
|
|
368
|
+
name: opts.name,
|
|
369
|
+
value: gdrRef({
|
|
370
|
+
res: DEFAULT_WORKFLOW_RESOURCE,
|
|
371
|
+
documentId: documentId,
|
|
372
|
+
type: opts.type ?? "document"
|
|
373
|
+
})
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
353
377
|
function releaseField(releaseName, opts) {
|
|
354
378
|
return {
|
|
355
379
|
type: "release.ref",
|
|
@@ -361,6 +385,35 @@ function releaseField(releaseName, opts) {
|
|
|
361
385
|
};
|
|
362
386
|
}
|
|
363
387
|
|
|
388
|
+
async function startedInstanceFixture(bench, opts) {
|
|
389
|
+
const flow = defineWorkflow({
|
|
390
|
+
name: "fixture-flow",
|
|
391
|
+
title: "Fixture flow",
|
|
392
|
+
initialStage: "review",
|
|
393
|
+
stages: [ {
|
|
394
|
+
name: "review",
|
|
395
|
+
transitions: [ {
|
|
396
|
+
name: "go",
|
|
397
|
+
to: "done",
|
|
398
|
+
when: "false"
|
|
399
|
+
} ]
|
|
400
|
+
}, {
|
|
401
|
+
name: "done"
|
|
402
|
+
} ]
|
|
403
|
+
});
|
|
404
|
+
await bench.deployDefinitions({
|
|
405
|
+
expectedMinReaderModel: 2,
|
|
406
|
+
definitions: [ flow ]
|
|
407
|
+
});
|
|
408
|
+
const {instance: instance} = await bench.startInstance({
|
|
409
|
+
definition: flow.name,
|
|
410
|
+
...opts?.instanceId !== void 0 ? {
|
|
411
|
+
instanceId: opts.instanceId
|
|
412
|
+
} : {}
|
|
413
|
+
});
|
|
414
|
+
return instance;
|
|
415
|
+
}
|
|
416
|
+
|
|
364
417
|
function createBench(options = {}) {
|
|
365
418
|
const tag = options.tag ?? BENCH_TAG;
|
|
366
419
|
validateTag(tag);
|
|
@@ -372,10 +425,16 @@ function createBench(options = {}) {
|
|
|
372
425
|
documents: options.documents
|
|
373
426
|
} : {},
|
|
374
427
|
defaultAccess: access
|
|
375
|
-
}), identities = createIdentityDirectory(client),
|
|
428
|
+
}), identities = createIdentityDirectory(client), resourceClients = benchResourceClients({
|
|
429
|
+
client: client,
|
|
430
|
+
options: options
|
|
431
|
+
}), scope = {
|
|
376
432
|
client: client,
|
|
377
433
|
tag: tag,
|
|
378
434
|
workflowResource: workflowResource,
|
|
435
|
+
...resourceClients !== void 0 ? {
|
|
436
|
+
resourceClients: resourceClients
|
|
437
|
+
} : {},
|
|
379
438
|
executionContext: options.executionContext ?? {
|
|
380
439
|
kind: EXECUTION_KINDS.test
|
|
381
440
|
}
|
|
@@ -391,10 +450,7 @@ function createBench(options = {}) {
|
|
|
391
450
|
clock: clock
|
|
392
451
|
});
|
|
393
452
|
return {
|
|
394
|
-
|
|
395
|
-
tag: tag,
|
|
396
|
-
workflowResource: workflowResource,
|
|
397
|
-
executionContext: scope.executionContext,
|
|
453
|
+
...scope,
|
|
398
454
|
access: access,
|
|
399
455
|
currentUser: access.actor,
|
|
400
456
|
grants: access.grants ?? [],
|
|
@@ -429,10 +485,21 @@ function createBench(options = {}) {
|
|
|
429
485
|
};
|
|
430
486
|
}
|
|
431
487
|
|
|
488
|
+
function benchResourceClients(args) {
|
|
489
|
+
const {client: client} = args, {serveResources: serveResources, resourceClients: resourceClients} = args.options;
|
|
490
|
+
if (serveResources === void 0 || serveResources.length === 0) return resourceClients;
|
|
491
|
+
const nonDataset = serveResources.find(served => served.type !== "dataset");
|
|
492
|
+
if (nonDataset !== void 0) throw new Error(`createBench: serveResources can only serve dataset siblings of the bench store — got ${nonDataset.type} "${nonDataset.id}". Pass resourceClients for other resource types.`);
|
|
493
|
+
return parsed => {
|
|
494
|
+
const target = resourceFromParsed(parsed);
|
|
495
|
+
return serveResources.some(served => sameResource(served, target)) ? client.withConfig(datasetResourceParts(target.id)) : resourceClients?.(parsed);
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
432
499
|
function assertIsoInstant(iso) {
|
|
433
500
|
const ms = Date.parse(iso);
|
|
434
501
|
if (Number.isNaN(ms)) throw new Error(`bench clock: expected an ISO-8601 instant, got ${JSON.stringify(iso)}`);
|
|
435
502
|
return new Date(ms).toISOString();
|
|
436
503
|
}
|
|
437
504
|
|
|
438
|
-
export { ALL_ACCESS, ALL_ACCESS_USER, BENCH_TAG, DEFAULT_WORKFLOW_RESOURCE, WILDCARD_GRANTS, createBench, createBenchEngine, releaseField, subjectField };
|
|
505
|
+
export { ALL_ACCESS, ALL_ACCESS_USER, BENCH_TAG, DEFAULT_WORKFLOW_RESOURCE, WILDCARD_GRANTS, createBench, createBenchEngine, docRefField, releaseField, startedInstanceFixture, subjectField };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-engine-test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "In-memory test bench (createBench) for @sanity/workflow-engine.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@sanity-labs/client-fake-for-test": "^0.8.0",
|
|
46
|
-
"@sanity/workflow-engine": "0.
|
|
46
|
+
"@sanity/workflow-engine": "0.18.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@sanity/client": "^7.22.1",
|