@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 +18 -13
- package/dist/index.cjs +40 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -37
- package/dist/index.d.ts +61 -37
- package/dist/index.js +42 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
|
50
|
-
> bench's already-wired client: `createBench({client: other.client,
|
|
51
|
-
> `mutationGuard` rides along, so the second bench
|
|
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: "
|
|
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
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
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
|
-
...
|
|
5225
|
-
...
|
|
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
|