@sanity/workflow-engine-test 0.8.0 → 0.10.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 +494 -0
- package/README.md +73 -17
- package/dist/index.cjs +5534 -4697
- package/dist/index.d.cts +281 -119
- package/dist/index.d.ts +281 -119
- package/dist/index.js +5516 -4703
- package/package.json +6 -5
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,35 +1,51 @@
|
|
|
1
|
+
import type { AbortInstanceArgs } from "@sanity/workflow-engine";
|
|
1
2
|
import { ActivityStatus } from "@sanity/workflow-engine";
|
|
2
3
|
import { Actor } from "@sanity/workflow-engine";
|
|
4
|
+
import { CandidateDocument } from "@sanity/workflow-engine";
|
|
5
|
+
import { Clock } from "@sanity/workflow-engine";
|
|
3
6
|
import type { CompleteEffectArgs } from "@sanity/workflow-engine";
|
|
7
|
+
import { CreateEngineArgs } from "@sanity/workflow-engine";
|
|
8
|
+
import type { DeclaredExecutionContext } from "@sanity/workflow-engine";
|
|
4
9
|
import { DeployDefinitionsArgs } from "@sanity/workflow-engine";
|
|
5
10
|
import { DeployDefinitionsResult } from "@sanity/workflow-engine";
|
|
6
|
-
import {
|
|
11
|
+
import { DeployedDefinition } from "@sanity/workflow-engine";
|
|
7
12
|
import type { EditFieldArgs } from "@sanity/workflow-engine";
|
|
13
|
+
import { Engine } from "@sanity/workflow-engine";
|
|
8
14
|
import type { EvaluateArgs } from "@sanity/workflow-engine";
|
|
9
15
|
import type { FireActionArgs } from "@sanity/workflow-engine";
|
|
10
16
|
import { GdrUri } from "@sanity/workflow-engine";
|
|
11
17
|
import type { Grant } from "@sanity/workflow-engine";
|
|
18
|
+
import { InitialFieldValue } from "@sanity/workflow-engine";
|
|
12
19
|
import { MutationGuardAction } from "@sanity/workflow-engine";
|
|
13
20
|
import { MutationGuardDoc } from "@sanity/workflow-engine";
|
|
14
21
|
import type { OperationArgs } from "@sanity/workflow-engine";
|
|
22
|
+
import { OperationResult } from "@sanity/workflow-engine";
|
|
15
23
|
import { PendingEffect } from "@sanity/workflow-engine";
|
|
24
|
+
import type { SetStageArgs } from "@sanity/workflow-engine";
|
|
16
25
|
import type { StartInstanceArgs } from "@sanity/workflow-engine";
|
|
17
26
|
import type { WorkflowAccess } from "@sanity/workflow-engine";
|
|
27
|
+
import { WorkflowDefinitionInput } from "@sanity/workflow-engine";
|
|
18
28
|
import { WorkflowEvaluation } from "@sanity/workflow-engine";
|
|
19
29
|
import { WorkflowInstance } from "@sanity/workflow-engine";
|
|
20
30
|
import type { WorkflowResource } from "@sanity/workflow-engine";
|
|
21
31
|
|
|
22
32
|
declare type Action = ReleaseAction | VersionAction;
|
|
23
33
|
|
|
34
|
+
/** Options for `action()`. Only `tag` is meaningful to the fake. */
|
|
35
|
+
declare interface ActionOptions {
|
|
36
|
+
/** Request tag, recorded on the {@link CallRecord} (`null` = explicit suppression). */
|
|
37
|
+
tag?: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
declare interface ActionResult {
|
|
25
41
|
transactionId: string;
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
/**
|
|
29
|
-
* The bench's default
|
|
30
|
-
*
|
|
31
|
-
* engine
|
|
32
|
-
*
|
|
45
|
+
* The bench's default identity — wildcard user + wildcard grants,
|
|
46
|
+
* registered as the default token on the bench's fake client so the
|
|
47
|
+
* engine token-resolves it (`/users/me` + the ACL path) exactly like
|
|
48
|
+
* production.
|
|
33
49
|
*
|
|
34
50
|
* Production code never sees this; pass `createBench({ access })` to
|
|
35
51
|
* test denial paths.
|
|
@@ -51,73 +67,73 @@ export declare type Bench = BenchState &
|
|
|
51
67
|
EngineWrappers &
|
|
52
68
|
ReadHelpers &
|
|
53
69
|
GuardHelpers &
|
|
54
|
-
QueryHelpers
|
|
70
|
+
QueryHelpers &
|
|
71
|
+
ComposedHelpers;
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"client" | "tag" | "workflowResource" | "access"
|
|
59
|
-
> & {
|
|
60
|
-
access?: WorkflowAccess;
|
|
61
|
-
/** Shortcut for `access.actor`. */
|
|
62
|
-
actor?: Actor;
|
|
63
|
-
};
|
|
73
|
+
/** Default engine-scope partition tag for benches that don't pass one. */
|
|
74
|
+
export declare const BENCH_TAG = "bench";
|
|
64
75
|
|
|
65
|
-
declare type
|
|
66
|
-
EditFieldArgs,
|
|
67
|
-
"client" | "tag" | "workflowResource" | "access"
|
|
68
|
-
> & {
|
|
69
|
-
access?: WorkflowAccess;
|
|
70
|
-
/** Shortcut for `access.actor`. */
|
|
71
|
-
actor?: Actor;
|
|
72
|
-
/** Shortcut for `access.grants`. */
|
|
73
|
-
grants?: Grant[];
|
|
74
|
-
};
|
|
76
|
+
declare type BenchAbortInstanceArgs = BenchArgs<AbortInstanceArgs>;
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
/**
|
|
79
|
+
* A bench verb's args: the engine verb's args plus the bench's per-call
|
|
80
|
+
* identity shortcuts — the composed pair runs the verb through a
|
|
81
|
+
* token-scoped sibling client.
|
|
82
|
+
*/
|
|
83
|
+
declare type BenchArgs<T> = T & {
|
|
80
84
|
access?: WorkflowAccess;
|
|
81
85
|
/** Shortcut for `access.actor`. */
|
|
82
86
|
actor?: Actor;
|
|
83
|
-
/** Shortcut for `access.grants`. */
|
|
84
|
-
grants?: Grant[];
|
|
85
87
|
};
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"client" | "tag" | "workflowResource" | "access"
|
|
90
|
-
> & {
|
|
91
|
-
access?: WorkflowAccess;
|
|
92
|
-
/** Shortcut for `access.actor`. */
|
|
93
|
-
actor?: Actor;
|
|
89
|
+
/** {@link BenchArgs} for the verbs that also take the `grants` shortcut. */
|
|
90
|
+
declare type BenchArgsWithGrants<T> = BenchArgs<T> & {
|
|
94
91
|
/** Shortcut for `access.grants`. */
|
|
95
92
|
grants?: Grant[];
|
|
96
93
|
};
|
|
97
94
|
|
|
98
|
-
declare type
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
declare type BenchCompleteEffectArgs = BenchArgs<CompleteEffectArgs>;
|
|
96
|
+
|
|
97
|
+
declare type BenchEditFieldArgs = BenchArgsWithGrants<EditFieldArgs>;
|
|
98
|
+
|
|
99
|
+
declare type BenchEvaluateArgs = BenchArgsWithGrants<EvaluateArgs>;
|
|
100
|
+
|
|
101
|
+
declare type BenchFireActionArgs = BenchArgsWithGrants<FireActionArgs>;
|
|
102
|
+
|
|
103
|
+
declare type BenchSetStageArgs = BenchArgs<SetStageArgs>;
|
|
104
|
+
|
|
105
|
+
declare type BenchStartInstanceArgs = BenchArgs<StartInstanceArgs>;
|
|
106
106
|
|
|
107
107
|
/** Base bench state, distinct from the engine/read/guard/query helper groups. */
|
|
108
108
|
declare interface BenchState {
|
|
109
109
|
/** The fake Sanity client. Passed automatically by bench wrappers. */
|
|
110
110
|
client: TestClient;
|
|
111
|
+
/** The engine-scope partition tag every wrapped call runs under. */
|
|
112
|
+
tag: string;
|
|
113
|
+
/** The workflow resource every wrapped call targets. */
|
|
114
|
+
workflowResource: WorkflowResource;
|
|
115
|
+
/** The declared execution context stamped on every wrapped call's history
|
|
116
|
+
* entries (default `{kind: 'test'}`) — inherited by {@link createBenchEngine}. */
|
|
117
|
+
executionContext: DeclaredExecutionContext;
|
|
111
118
|
/**
|
|
112
|
-
* The
|
|
113
|
-
*
|
|
114
|
-
* `ALL_ACCESS`.
|
|
119
|
+
* The bench's default identity (actor + grants) — registered as the
|
|
120
|
+
* default token on the fake client. Defaults to `ALL_ACCESS`.
|
|
115
121
|
*/
|
|
116
122
|
access: WorkflowAccess;
|
|
117
123
|
/** Convenience: `access.actor`. */
|
|
118
124
|
currentUser: Actor;
|
|
119
125
|
/** Convenience: `access.grants ?? []`. */
|
|
120
126
|
grants: Grant[];
|
|
127
|
+
/**
|
|
128
|
+
* A token-bound sibling client acting as `access` — the bench's ONE
|
|
129
|
+
* identity-registration seam. Registers the pair on the fake client's
|
|
130
|
+
* family-shared registry (idempotent, cached per identity; omitted
|
|
131
|
+
* grants default to wildcard) and returns the sibling whose
|
|
132
|
+
* `/users/me` + ACL path serve it — the engine token-resolves it like
|
|
133
|
+
* production. Bind it to {@link createBenchEngine} to act as another
|
|
134
|
+
* user end-to-end; never hand-roll `addIdentity` + `withConfig`.
|
|
135
|
+
*/
|
|
136
|
+
clientFor: (access: WorkflowAccess) => TestClient;
|
|
121
137
|
/** Add documents to the store after construction. */
|
|
122
138
|
seedDocuments: (documents: StoreDocument[]) => void;
|
|
123
139
|
/**
|
|
@@ -142,14 +158,7 @@ declare interface BenchState {
|
|
|
142
158
|
advance: (ms: number) => void;
|
|
143
159
|
}
|
|
144
160
|
|
|
145
|
-
declare type BenchTickArgs =
|
|
146
|
-
OperationArgs,
|
|
147
|
-
"client" | "tag" | "workflowResource" | "access"
|
|
148
|
-
> & {
|
|
149
|
-
access?: WorkflowAccess;
|
|
150
|
-
/** Shortcut for `access.actor`. */
|
|
151
|
-
actor?: Actor;
|
|
152
|
-
};
|
|
161
|
+
declare type BenchTickArgs = BenchArgs<OperationArgs>;
|
|
153
162
|
|
|
154
163
|
/**
|
|
155
164
|
* One recorded invocation of a client method — the spy layer behind
|
|
@@ -167,10 +176,27 @@ declare interface CallRecord {
|
|
|
167
176
|
result?: unknown;
|
|
168
177
|
/** Thrown or rejected value, set if the call failed. */
|
|
169
178
|
error?: unknown;
|
|
179
|
+
/** Request tag from the call's options, when one was passed (`null` = explicit suppression). */
|
|
180
|
+
tag?: string | null;
|
|
170
181
|
projectId: string;
|
|
171
182
|
dataset: string;
|
|
172
183
|
}
|
|
173
184
|
|
|
185
|
+
/** Helpers composed across the wrapper + read groups at bench assembly. */
|
|
186
|
+
declare interface ComposedHelpers {
|
|
187
|
+
/**
|
|
188
|
+
* Complete the pending effect named `effect` — {@link EngineWrappers.completeEffect}
|
|
189
|
+
* with the `effectKey` looked up by name, throwing loud when no such effect
|
|
190
|
+
* is pending. The idiomatic way to play an external handler reporting its
|
|
191
|
+
* result; pass `ops` to write outcome fields in the same commit.
|
|
192
|
+
*/
|
|
193
|
+
completePendingEffect: (
|
|
194
|
+
args: Omit<BenchCompleteEffectArgs, "effectKey"> & {
|
|
195
|
+
effect: string;
|
|
196
|
+
},
|
|
197
|
+
) => Promise<OperationResult>;
|
|
198
|
+
}
|
|
199
|
+
|
|
174
200
|
/**
|
|
175
201
|
* Create a fresh bench. By default the bench is fully isolated — two
|
|
176
202
|
* `createBench()` calls have no shared state. Pass `client` + `tag`
|
|
@@ -178,14 +204,28 @@ declare interface CallRecord {
|
|
|
178
204
|
*/
|
|
179
205
|
export declare function createBench(options?: CreateBenchOptions): Bench;
|
|
180
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Build an out-of-band {@link Engine} on a bench's client, inheriting the
|
|
209
|
+
* bench's tag, workflow resource, and clock (so `setNow` / `advance` govern
|
|
210
|
+
* the engine's `$now` too). Use this when a test needs an engine of its own —
|
|
211
|
+
* custom effect handlers, racing two drainers — rather than the wrappers the
|
|
212
|
+
* bench exposes. `overrides` win over every inherited value.
|
|
213
|
+
*/
|
|
214
|
+
export declare function createBenchEngine(
|
|
215
|
+
bench: EngineScope & {
|
|
216
|
+
now: Clock;
|
|
217
|
+
},
|
|
218
|
+
overrides?: Partial<CreateEngineArgs>,
|
|
219
|
+
): Engine;
|
|
220
|
+
|
|
181
221
|
export declare interface CreateBenchOptions {
|
|
182
222
|
/** Subject documents seeded into the store. */
|
|
183
223
|
documents?: StoreDocument[];
|
|
184
224
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* test denial paths.
|
|
225
|
+
* The bench's default identity (actor + grants), registered as the
|
|
226
|
+
* default token on the fake client — the engine token-resolves it like
|
|
227
|
+
* production. Defaults to `ALL_ACCESS` — wildcard user + wildcard
|
|
228
|
+
* grants. Pass a restrictive value to test denial paths.
|
|
189
229
|
*
|
|
190
230
|
* Mutually exclusive with the `currentUser` / `grants` shortcuts
|
|
191
231
|
* (which compose into the same shape internally).
|
|
@@ -196,28 +236,7 @@ export declare interface CreateBenchOptions {
|
|
|
196
236
|
/** Shortcut for `access.grants`. Composed with `currentUser`. */
|
|
197
237
|
grants?: Grant[];
|
|
198
238
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* When set, the bench's underlying TestClient:
|
|
202
|
-
* - Exposes `client.request({ url })` serving `/users/me` + the
|
|
203
|
-
* configured ACL path, so the engine can resolve `WorkflowAccess`
|
|
204
|
-
* from "the token" exactly like production.
|
|
205
|
-
* - Rejects writes the grants don't permit with a Sanity-style
|
|
206
|
-
* `WriteAccessDeniedError` — the same 403 a real lake would
|
|
207
|
-
* return.
|
|
208
|
-
*
|
|
209
|
-
* Use this to demonstrate what would happen if the engine's
|
|
210
|
-
* soft-gate were bypassed (or disagreed with) — the lake is still
|
|
211
|
-
* authoritative. The bench's `access` field is the engine-side
|
|
212
|
-
* override; `accessControl` is the lake-side enforcement. They
|
|
213
|
-
* usually carry the same `actor` + `grants` but can be made to
|
|
214
|
-
* disagree to test edge cases.
|
|
215
|
-
*
|
|
216
|
-
* Mutually exclusive with passing a pre-built `client`.
|
|
217
|
-
*/
|
|
218
|
-
accessControl?: TestClientAccessControl;
|
|
219
|
-
/**
|
|
220
|
-
* Engine-scope environment partition. Defaults to `"bench"`. Pass
|
|
239
|
+
* Engine-scope environment partition. Defaults to {@link BENCH_TAG}. Pass
|
|
221
240
|
* different tags to two benches sharing a `client` to assert partition
|
|
222
241
|
* isolation; pass the same tag to assert visibility through a shared
|
|
223
242
|
* partition.
|
|
@@ -247,6 +266,12 @@ export declare interface CreateBenchOptions {
|
|
|
247
266
|
* start on the wall clock (`advance` / `setNow` can freeze it later).
|
|
248
267
|
*/
|
|
249
268
|
now?: string;
|
|
269
|
+
/**
|
|
270
|
+
* Declared execution context stamped (with the inferred runtime) on every
|
|
271
|
+
* history entry the bench's engine calls append. Defaults to
|
|
272
|
+
* `{ kind: "test" }`; override to exercise provenance rendering.
|
|
273
|
+
*/
|
|
274
|
+
executionContext?: DeclaredExecutionContext;
|
|
250
275
|
}
|
|
251
276
|
|
|
252
277
|
declare interface CurrentUser {
|
|
@@ -279,17 +304,44 @@ declare interface DocumentBody {
|
|
|
279
304
|
|
|
280
305
|
declare type DocumentValuePermission = "create" | "read" | "update";
|
|
281
306
|
|
|
282
|
-
/**
|
|
307
|
+
/**
|
|
308
|
+
* How {@link GuardHelpers.editDocument} mutates the document. Each present
|
|
309
|
+
* key becomes one patch op, applied in `set` → `unset` → `insert` order in a
|
|
310
|
+
* single commit; the shapes mirror the client's patch builder.
|
|
311
|
+
*/
|
|
312
|
+
export declare type EditDocumentPatch = {
|
|
313
|
+
set?: Record<string, unknown>;
|
|
314
|
+
unset?: string[];
|
|
315
|
+
insert?: {
|
|
316
|
+
position: "before" | "after" | "replace";
|
|
317
|
+
anchor: string;
|
|
318
|
+
items: unknown[];
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/** The bench-owned engine call context injected into every wrapped verb. */
|
|
323
|
+
declare type EngineScope = {
|
|
324
|
+
client: TestClient;
|
|
325
|
+
tag: string;
|
|
326
|
+
workflowResource: WorkflowResource;
|
|
327
|
+
executionContext: DeclaredExecutionContext;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/** Engine-call wrappers that inject the bench-owned client + tag + identity. */
|
|
283
331
|
declare type EngineWrappers = {
|
|
284
|
-
deployDefinitions: (
|
|
285
|
-
args:
|
|
332
|
+
deployDefinitions: <T extends WorkflowDefinitionInput<T>>(
|
|
333
|
+
args: DeployDefinitionsArgs<T>,
|
|
286
334
|
) => Promise<DeployDefinitionsResult>;
|
|
287
|
-
startInstance: (args: BenchStartInstanceArgs) => Promise<
|
|
288
|
-
fireAction: (args: BenchFireActionArgs) => Promise<
|
|
289
|
-
editField: (args: BenchEditFieldArgs) => Promise<
|
|
290
|
-
completeEffect: (args: BenchCompleteEffectArgs) => Promise<
|
|
291
|
-
tick: (args: BenchTickArgs) => Promise<
|
|
335
|
+
startInstance: (args: BenchStartInstanceArgs) => Promise<OperationResult>;
|
|
336
|
+
fireAction: (args: BenchFireActionArgs) => Promise<OperationResult>;
|
|
337
|
+
editField: (args: BenchEditFieldArgs) => Promise<OperationResult>;
|
|
338
|
+
completeEffect: (args: BenchCompleteEffectArgs) => Promise<OperationResult>;
|
|
339
|
+
tick: (args: BenchTickArgs) => Promise<OperationResult>;
|
|
292
340
|
evaluate: (args: BenchEvaluateArgs) => Promise<WorkflowEvaluation>;
|
|
341
|
+
/** Admin override — bypass filters/transitions and force the stage. */
|
|
342
|
+
setStage: (args: BenchSetStageArgs) => Promise<OperationResult>;
|
|
343
|
+
/** Admin override — hard-stop an in-flight instance where it stands. */
|
|
344
|
+
abortInstance: (args: BenchAbortInstanceArgs) => Promise<OperationResult>;
|
|
293
345
|
guardsForInstance: (instanceId: string) => Promise<MutationGuardDoc[]>;
|
|
294
346
|
guardsForDefinition: (definition: string) => Promise<MutationGuardDoc[]>;
|
|
295
347
|
};
|
|
@@ -370,8 +422,12 @@ declare interface FetchOptions {
|
|
|
370
422
|
* raw response. Ignored when `filterResponse` is not `false`.
|
|
371
423
|
*/
|
|
372
424
|
returnQuery?: boolean;
|
|
373
|
-
/**
|
|
374
|
-
|
|
425
|
+
/**
|
|
426
|
+
* Request tag, recorded on the {@link CallRecord} for assertions.
|
|
427
|
+
* `null` mirrors `@sanity/client`'s explicit-suppression form and is
|
|
428
|
+
* recorded as-is. No other behavior is simulated.
|
|
429
|
+
*/
|
|
430
|
+
tag?: string | null;
|
|
375
431
|
/** Accepted for signature parity; not simulated. */
|
|
376
432
|
signal?: AbortSignal;
|
|
377
433
|
}
|
|
@@ -382,7 +438,7 @@ declare interface Grant_2 {
|
|
|
382
438
|
}
|
|
383
439
|
|
|
384
440
|
declare type GuardHelpers = {
|
|
385
|
-
/** All deployed
|
|
441
|
+
/** All deployed guard docs ({@link MutationGuardDoc}) in the store. */
|
|
386
442
|
listGuards: () => Promise<MutationGuardDoc[]>;
|
|
387
443
|
/**
|
|
388
444
|
* Guards that would deny an arbitrary edit to `documentId` right now (i.e.
|
|
@@ -394,15 +450,17 @@ declare type GuardHelpers = {
|
|
|
394
450
|
as?: Actor,
|
|
395
451
|
) => Promise<MutationGuardDoc[]>;
|
|
396
452
|
/**
|
|
397
|
-
* Write to a document through the client's
|
|
453
|
+
* Write to a document through the client's guarded write seam — the bench
|
|
398
454
|
* client gates the write against deployed guards (incl. `identity()`) and
|
|
399
|
-
* throws `MutationGuardDeniedError` on denial
|
|
400
|
-
* `as` routes the write through a
|
|
401
|
-
* resolves to that actor.
|
|
455
|
+
* throws `MutationGuardDeniedError` on denial — the lake's intended
|
|
456
|
+
* rejection, simulated by the bench. `as` routes the write through a
|
|
457
|
+
* token-scoped sibling so `identity()` resolves to that actor. A `patch`
|
|
458
|
+
* ({@link EditDocumentPatch}) is required for edits and rejected for
|
|
459
|
+
* `action: 'delete'` — the combination is contradictory input.
|
|
402
460
|
*/
|
|
403
461
|
editDocument: (opts: {
|
|
404
462
|
documentId: string;
|
|
405
|
-
patch
|
|
463
|
+
patch?: EditDocumentPatch;
|
|
406
464
|
action?: MutationGuardAction;
|
|
407
465
|
as?: Actor;
|
|
408
466
|
}) => Promise<StoreDocument>;
|
|
@@ -424,6 +482,8 @@ declare interface MutationLogEntry {
|
|
|
424
482
|
projectId: string;
|
|
425
483
|
dataset: string;
|
|
426
484
|
mutations: TxnMutation[];
|
|
485
|
+
/** Request tag the commit carried, when one was passed (`null` = explicit suppression). */
|
|
486
|
+
tag?: string | null;
|
|
427
487
|
}
|
|
428
488
|
|
|
429
489
|
/** The operation recorded per affected document, matching `@sanity/client`. */
|
|
@@ -451,8 +511,21 @@ declare interface MutationOptions {
|
|
|
451
511
|
visibility?: "sync" | "async" | "deferred";
|
|
452
512
|
/** Accepted for signature parity; not simulated. */
|
|
453
513
|
autoGenerateArrayKeys?: boolean;
|
|
514
|
+
/**
|
|
515
|
+
* Request tag, recorded on the {@link CallRecord} and the
|
|
516
|
+
* {@link MutationLogEntry} for assertions. `null` mirrors
|
|
517
|
+
* `@sanity/client`'s explicit-suppression form and is recorded as-is.
|
|
518
|
+
* No other behavior is simulated.
|
|
519
|
+
*/
|
|
520
|
+
tag?: string | null;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** Options for `PatchHandle.commit()`. Only `tag` is meaningful to the fake. */
|
|
524
|
+
declare interface PatchCommitOptions {
|
|
525
|
+
/** Request tag, recorded on the mutation log entry (`null` = explicit suppression). */
|
|
526
|
+
tag?: string | null;
|
|
454
527
|
/** Accepted for signature parity; not simulated. */
|
|
455
|
-
|
|
528
|
+
visibility?: "sync" | "async" | "deferred";
|
|
456
529
|
}
|
|
457
530
|
|
|
458
531
|
declare interface PatchHandle {
|
|
@@ -467,7 +540,7 @@ declare interface PatchHandle {
|
|
|
467
540
|
items: unknown[],
|
|
468
541
|
) => PatchHandle;
|
|
469
542
|
ifRevisionId: (rev: string) => PatchHandle;
|
|
470
|
-
commit: () => Promise<SanityDocument>;
|
|
543
|
+
commit: (options?: PatchCommitOptions) => Promise<SanityDocument>;
|
|
471
544
|
/** Test-only introspection — not on real Patch */
|
|
472
545
|
toJSON: () => {
|
|
473
546
|
id: string;
|
|
@@ -561,9 +634,9 @@ declare type ReadHelpers = {
|
|
|
561
634
|
* Spawned children of `parentInstanceId`. If `activity` is provided,
|
|
562
635
|
* only children spawned by that specific activity on the parent.
|
|
563
636
|
*
|
|
564
|
-
* Walks `history.spawned` entries on the parent —
|
|
565
|
-
*
|
|
566
|
-
*
|
|
637
|
+
* Walks `history.spawned` entries on the parent — the durable record
|
|
638
|
+
* (the workflow-scope registry tracks the same children, keyed for
|
|
639
|
+
* adoption rather than enumeration).
|
|
567
640
|
*
|
|
568
641
|
* Returns children ordered by `startedAt` ascending.
|
|
569
642
|
*/
|
|
@@ -598,6 +671,15 @@ declare type ReadHelpers = {
|
|
|
598
671
|
* non-reactive content forwarder needs. Sorted by `startedAt` ascending.
|
|
599
672
|
*/
|
|
600
673
|
instancesForDocument: (document: GdrUri) => Promise<WorkflowInstance[]>;
|
|
674
|
+
/**
|
|
675
|
+
* The startable half of {@link ReadHelpers.instancesForDocument}: the
|
|
676
|
+
* latest deployed version of every definition applicable to the LOADED
|
|
677
|
+
* candidate document — startable, a required subject entry accepts its
|
|
678
|
+
* `_type`, `applicableWhen` passes. All matches, name ascending.
|
|
679
|
+
*/
|
|
680
|
+
definitionsForDocument: (
|
|
681
|
+
document: CandidateDocument,
|
|
682
|
+
) => Promise<DeployedDefinition[]>;
|
|
601
683
|
/** Snapshot of all documents in the store — for inspecting the world. */
|
|
602
684
|
snapshot: () => readonly StoreDocument[];
|
|
603
685
|
};
|
|
@@ -650,6 +732,18 @@ declare interface ReleaseDocument extends SanityDocument {
|
|
|
650
732
|
publishAt?: string;
|
|
651
733
|
}
|
|
652
734
|
|
|
735
|
+
/**
|
|
736
|
+
* A `release.ref` entry pointing at `_.releases.<releaseName>` in the bench's
|
|
737
|
+
* default resource — what a release-targeting workflow's `release` field
|
|
738
|
+
* expects at start.
|
|
739
|
+
*/
|
|
740
|
+
export declare function releaseField(
|
|
741
|
+
releaseName: string,
|
|
742
|
+
opts?: {
|
|
743
|
+
name?: string;
|
|
744
|
+
},
|
|
745
|
+
): InitialFieldValue;
|
|
746
|
+
|
|
653
747
|
declare interface ReleaseMetadata {
|
|
654
748
|
title?: string;
|
|
655
749
|
description?: string;
|
|
@@ -737,6 +831,19 @@ declare type StoreDocument = {
|
|
|
737
831
|
[key: string]: unknown;
|
|
738
832
|
};
|
|
739
833
|
|
|
834
|
+
/**
|
|
835
|
+
* The conventional workflow-scope `subject` doc.ref entry, keyed by a bare
|
|
836
|
+
* doc id in the bench's default resource. `type` defaults to `'document'`;
|
|
837
|
+
* pass the subject's schema type when a filter or projection reads it.
|
|
838
|
+
*/
|
|
839
|
+
export declare function subjectField(
|
|
840
|
+
documentId: string,
|
|
841
|
+
opts?: {
|
|
842
|
+
name?: string;
|
|
843
|
+
type?: string;
|
|
844
|
+
},
|
|
845
|
+
): InitialFieldValue;
|
|
846
|
+
|
|
740
847
|
declare interface SystemFields {
|
|
741
848
|
_rev: string;
|
|
742
849
|
_createdAt: string;
|
|
@@ -758,22 +865,18 @@ declare interface TestClient {
|
|
|
758
865
|
options?: FetchOptions,
|
|
759
866
|
) => Promise<(T | null)[]>;
|
|
760
867
|
/**
|
|
761
|
-
* Sanity-style HTTP request hook. Present iff the
|
|
762
|
-
* created with `accessControl
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
* Test cases that want to exercise the engine's token-resolution
|
|
768
|
-
* path should configure `accessControl`; cases that want bench-style
|
|
769
|
-
* always-allow should leave it off and pass `access` through the
|
|
770
|
-
* bench.
|
|
868
|
+
* Sanity-style HTTP request hook. Present iff the handle has an
|
|
869
|
+
* acting identity — created with `accessControl`, or bound to a
|
|
870
|
+
* registered token (`identities` + `token`). The engine's access
|
|
871
|
+
* resolver pokes `/users/me` and the configured ACL path through
|
|
872
|
+
* this — without it the engine throws on any verb that needs an
|
|
873
|
+
* actor.
|
|
771
874
|
*/
|
|
772
875
|
request?: <T>(opts: {
|
|
773
876
|
url?: string;
|
|
774
877
|
uri?: string;
|
|
775
878
|
signal?: AbortSignal;
|
|
776
|
-
tag?: string;
|
|
879
|
+
tag?: string | null;
|
|
777
880
|
}) => Promise<T>;
|
|
778
881
|
create: <T extends SanityDocumentLike>(
|
|
779
882
|
doc: T,
|
|
@@ -811,7 +914,10 @@ declare interface TestClient {
|
|
|
811
914
|
* Returns a synthetic `transactionId`. Multi-action arrays are applied
|
|
812
915
|
* atomically — any failure rolls back the batch.
|
|
813
916
|
*/
|
|
814
|
-
action: (
|
|
917
|
+
action: (
|
|
918
|
+
action: Action | Action[],
|
|
919
|
+
options?: ActionOptions,
|
|
920
|
+
) => Promise<ActionResult>;
|
|
815
921
|
/** Helpers mirroring `@sanity/client`'s `client.releases.*` surface. */
|
|
816
922
|
releases: ReleasesHandle;
|
|
817
923
|
/** Helper for `sanity.action.document.version.create`. */
|
|
@@ -825,11 +931,15 @@ declare interface TestClient {
|
|
|
825
931
|
releaseId: string;
|
|
826
932
|
publishedId: string;
|
|
827
933
|
}) => Promise<ActionResult>;
|
|
828
|
-
/**
|
|
934
|
+
/**
|
|
935
|
+
* Current `(projectId, dataset)` and active perspective. `token` is
|
|
936
|
+
* present iff the handle is bound to a registered identity.
|
|
937
|
+
*/
|
|
829
938
|
config: () => {
|
|
830
939
|
projectId: string;
|
|
831
940
|
dataset: string;
|
|
832
941
|
perspective: Perspective;
|
|
942
|
+
token?: string;
|
|
833
943
|
};
|
|
834
944
|
/**
|
|
835
945
|
* Return a new client handle pointing at a different `(projectId, dataset)`
|
|
@@ -846,8 +956,18 @@ declare interface TestClient {
|
|
|
846
956
|
* Rebind the acting identity (and its grants) on a sibling that shares
|
|
847
957
|
* the same store — the test analogue of a token-scoped client. Lets two
|
|
848
958
|
* actors write the same dataset, e.g. for `identity()`-based guards.
|
|
959
|
+
* Mutually exclusive with `token`.
|
|
849
960
|
*/
|
|
850
961
|
accessControl: TestClientAccessControl;
|
|
962
|
+
/**
|
|
963
|
+
* Bind the sibling to a token registered in the family's identity
|
|
964
|
+
* registry (`createTestClient({ identities })` / `addIdentity`): its
|
|
965
|
+
* `/users/me` serves that identity's user, its ACL path serves that
|
|
966
|
+
* identity's grants, and reads/writes are gated through them — the
|
|
967
|
+
* token determines identity, exactly like production. Unknown tokens
|
|
968
|
+
* throw. Mutually exclusive with `accessControl`.
|
|
969
|
+
*/
|
|
970
|
+
token: string;
|
|
851
971
|
}>,
|
|
852
972
|
) => TestClient;
|
|
853
973
|
/** Replace the current dataset with the given documents. */
|
|
@@ -864,9 +984,21 @@ declare interface TestClient {
|
|
|
864
984
|
* Simulate `count` concurrent edits to `id` in the current dataset: the
|
|
865
985
|
* next `count` revision-guarded mutations (those passing `ifRevisionId`)
|
|
866
986
|
* to that document throw a 409-shaped conflict, as if another client
|
|
867
|
-
* committed between the caller's read and write.
|
|
868
|
-
*
|
|
869
|
-
*
|
|
987
|
+
* committed between the caller's read and write. Mutations that don't
|
|
988
|
+
* pass `ifRevisionId` are unaffected.
|
|
989
|
+
*
|
|
990
|
+
* Reach for this when the code under test reads a document's `_rev` and
|
|
991
|
+
* commits with `ifRevisionId` internally — so you can't stage the race
|
|
992
|
+
* by mutating the document first (the code would just re-read the new
|
|
993
|
+
* revision and its commit would match). The counter is consumed one per
|
|
994
|
+
* guarded commit, so `count` lets a test drive a recover-after-N-losses
|
|
995
|
+
* path or exhaust a retry budget, deterministically and without relying
|
|
996
|
+
* on promise interleaving.
|
|
997
|
+
*
|
|
998
|
+
* Note: this forces the conflict *outcome* — it short-circuits the
|
|
999
|
+
* revision comparison rather than changing the stored `_rev`. The code
|
|
1000
|
+
* under test sees the identical 409 error a real lost race produces; the
|
|
1001
|
+
* stored revision is left untouched.
|
|
870
1002
|
*/
|
|
871
1003
|
simulateConcurrentEdit: (id: string, count?: number) => void;
|
|
872
1004
|
/** List `(projectId, dataset)` pairs known in this registry. */
|
|
@@ -884,6 +1016,16 @@ declare interface TestClient {
|
|
|
884
1016
|
stub: (name: string, handler: unknown) => void;
|
|
885
1017
|
/** The currently-registered stubs, keyed by member name. */
|
|
886
1018
|
stubs: () => Record<string, unknown>;
|
|
1019
|
+
/**
|
|
1020
|
+
* Register (or upsert) a token→identity entry in the family's shared
|
|
1021
|
+
* registry. Siblings already bound to `token` resolve lazily, so a
|
|
1022
|
+
* re-registration takes effect on them immediately. Registering an
|
|
1023
|
+
* identity does not bind it — pass `token` at `createTestClient` or
|
|
1024
|
+
* `withConfig({ token })` to act as it.
|
|
1025
|
+
*/
|
|
1026
|
+
addIdentity: (token: string, identity: TestClientIdentity) => void;
|
|
1027
|
+
/** Snapshot of the registered token→identity entries. */
|
|
1028
|
+
identities: () => Record<string, TestClientIdentity>;
|
|
887
1029
|
/**
|
|
888
1030
|
* Every mutation committed through this client's registry, flattened
|
|
889
1031
|
* and in order — `create` / `createOrReplace` / `createIfNotExists` /
|
|
@@ -928,6 +1070,20 @@ declare interface TestClientAccessControl {
|
|
|
928
1070
|
aclPath?: string;
|
|
929
1071
|
}
|
|
930
1072
|
|
|
1073
|
+
/**
|
|
1074
|
+
* One entry in the token→identity registry (`createTestClient({ identities })`).
|
|
1075
|
+
* A handle bound to the entry's token behaves exactly like one configured
|
|
1076
|
+
* with `accessControl: { currentUser: user, grants }` — `/users/me` serves
|
|
1077
|
+
* `user`, the default ACL path serves `grants`, and reads/writes are gated
|
|
1078
|
+
* through them.
|
|
1079
|
+
*/
|
|
1080
|
+
declare interface TestClientIdentity {
|
|
1081
|
+
/** What `/users/me` returns for clients bound to this token. */
|
|
1082
|
+
user: CurrentUser;
|
|
1083
|
+
/** Grants served at the ACL path and enforced on reads/writes. */
|
|
1084
|
+
grants: Grant_2[];
|
|
1085
|
+
}
|
|
1086
|
+
|
|
931
1087
|
/**
|
|
932
1088
|
* In-memory document store backing a TestClient. Closure-scoped — created
|
|
933
1089
|
* fresh per test, no global state.
|
|
@@ -983,6 +1139,12 @@ declare interface TxnCommitOptions {
|
|
|
983
1139
|
returnFirst?: boolean;
|
|
984
1140
|
/** Override the synthetic transaction id (mirrors `client.transaction(undefined, { transactionId })`). */
|
|
985
1141
|
transactionId?: string;
|
|
1142
|
+
/**
|
|
1143
|
+
* Request tag, recorded on the mutation log entry for assertions.
|
|
1144
|
+
* `null` mirrors `@sanity/client`'s explicit-suppression form and is
|
|
1145
|
+
* recorded as-is. No other behavior is simulated.
|
|
1146
|
+
*/
|
|
1147
|
+
tag?: string | null;
|
|
986
1148
|
}
|
|
987
1149
|
|
|
988
1150
|
declare type TxnCommitResult =
|