@sanity/workflow-engine-test 0.9.0 → 0.11.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 +531 -0
- package/README.md +66 -7
- package/dist/index.cjs +398 -5210
- package/dist/index.d.cts +185 -773
- package/dist/index.d.ts +185 -773
- package/dist/index.js +387 -5217
- package/package.json +6 -5
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,36 +1,46 @@
|
|
|
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 { ChildrenArgs } 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";
|
|
9
|
+
import { DefinitionsForDocumentArgs } from "@sanity/workflow-engine";
|
|
10
|
+
import type { DeleteDefinitionArgs } from "@sanity/workflow-engine";
|
|
11
|
+
import { DeleteDefinitionResult } from "@sanity/workflow-engine";
|
|
4
12
|
import { DeployDefinitionsArgs } from "@sanity/workflow-engine";
|
|
5
13
|
import { DeployDefinitionsResult } from "@sanity/workflow-engine";
|
|
14
|
+
import { DeployedDefinition } from "@sanity/workflow-engine";
|
|
6
15
|
import type { EditFieldArgs } from "@sanity/workflow-engine";
|
|
16
|
+
import { Engine } from "@sanity/workflow-engine";
|
|
7
17
|
import type { EvaluateArgs } from "@sanity/workflow-engine";
|
|
18
|
+
import { FindPendingEffectsArgs } from "@sanity/workflow-engine";
|
|
8
19
|
import type { FireActionArgs } from "@sanity/workflow-engine";
|
|
9
|
-
import { GdrUri } from "@sanity/workflow-engine";
|
|
10
20
|
import type { Grant } from "@sanity/workflow-engine";
|
|
11
21
|
import { InitialFieldValue } from "@sanity/workflow-engine";
|
|
22
|
+
import { InstanceRefArgs } from "@sanity/workflow-engine";
|
|
23
|
+
import { InstancesForDocumentArgs } from "@sanity/workflow-engine";
|
|
24
|
+
import { InstancesQueryFilter } from "@sanity/workflow-engine";
|
|
12
25
|
import { MutationGuardAction } from "@sanity/workflow-engine";
|
|
13
26
|
import { MutationGuardDoc } from "@sanity/workflow-engine";
|
|
14
27
|
import type { OperationArgs } from "@sanity/workflow-engine";
|
|
15
28
|
import { OperationResult } from "@sanity/workflow-engine";
|
|
16
29
|
import { PendingEffect } from "@sanity/workflow-engine";
|
|
30
|
+
import type { SetStageArgs } from "@sanity/workflow-engine";
|
|
17
31
|
import type { StartInstanceArgs } from "@sanity/workflow-engine";
|
|
32
|
+
import type { TestClient } from "@sanity-labs/client-fake-for-test";
|
|
18
33
|
import type { WorkflowAccess } from "@sanity/workflow-engine";
|
|
34
|
+
import { WorkflowDefinitionInput } from "@sanity/workflow-engine";
|
|
19
35
|
import { WorkflowEvaluation } from "@sanity/workflow-engine";
|
|
20
36
|
import { WorkflowInstance } from "@sanity/workflow-engine";
|
|
21
37
|
import type { WorkflowResource } from "@sanity/workflow-engine";
|
|
22
38
|
|
|
23
|
-
declare type Action = ReleaseAction | VersionAction;
|
|
24
|
-
|
|
25
|
-
declare interface ActionResult {
|
|
26
|
-
transactionId: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
39
|
/**
|
|
30
|
-
* The bench's default
|
|
31
|
-
*
|
|
32
|
-
* engine
|
|
33
|
-
*
|
|
40
|
+
* The bench's default identity — wildcard user + wildcard grants,
|
|
41
|
+
* registered as the default token on the bench's fake client so the
|
|
42
|
+
* engine token-resolves it (`/users/me` + the ACL path) exactly like
|
|
43
|
+
* production.
|
|
34
44
|
*
|
|
35
45
|
* Production code never sees this; pass `createBench({ access })` to
|
|
36
46
|
* test denial paths.
|
|
@@ -55,56 +65,73 @@ export declare type Bench = BenchState &
|
|
|
55
65
|
QueryHelpers &
|
|
56
66
|
ComposedHelpers;
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/** Shortcut for `access.actor`. */
|
|
61
|
-
actor?: Actor;
|
|
62
|
-
};
|
|
68
|
+
/** Default engine-scope partition tag for benches that don't pass one. */
|
|
69
|
+
export declare const BENCH_TAG = "bench";
|
|
63
70
|
|
|
64
|
-
declare type
|
|
65
|
-
access?: WorkflowAccess;
|
|
66
|
-
/** Shortcut for `access.actor`. */
|
|
67
|
-
actor?: Actor;
|
|
68
|
-
/** Shortcut for `access.grants`. */
|
|
69
|
-
grants?: Grant[];
|
|
70
|
-
};
|
|
71
|
+
declare type BenchAbortInstanceArgs = BenchArgs<AbortInstanceArgs>;
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
/**
|
|
74
|
+
* A bench verb's args: the engine verb's args plus the bench's per-call
|
|
75
|
+
* identity shortcuts — the composed pair runs the verb through a
|
|
76
|
+
* token-scoped sibling client.
|
|
77
|
+
*/
|
|
78
|
+
declare type BenchArgs<T> = T & {
|
|
73
79
|
access?: WorkflowAccess;
|
|
74
80
|
/** Shortcut for `access.actor`. */
|
|
75
81
|
actor?: Actor;
|
|
76
|
-
/** Shortcut for `access.grants`. */
|
|
77
|
-
grants?: Grant[];
|
|
78
82
|
};
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/** Shortcut for `access.actor`. */
|
|
83
|
-
actor?: Actor;
|
|
84
|
+
/** {@link BenchArgs} for the verbs that also take the `grants` shortcut. */
|
|
85
|
+
declare type BenchArgsWithGrants<T> = BenchArgs<T> & {
|
|
84
86
|
/** Shortcut for `access.grants`. */
|
|
85
87
|
grants?: Grant[];
|
|
86
88
|
};
|
|
87
89
|
|
|
88
|
-
declare type
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
declare type BenchCompleteEffectArgs = BenchArgs<CompleteEffectArgs>;
|
|
91
|
+
|
|
92
|
+
declare type BenchDeleteDefinitionArgs =
|
|
93
|
+
BenchArgsWithGrants<DeleteDefinitionArgs>;
|
|
94
|
+
|
|
95
|
+
declare type BenchEditFieldArgs = BenchArgsWithGrants<EditFieldArgs>;
|
|
96
|
+
|
|
97
|
+
declare type BenchEvaluateArgs = BenchArgsWithGrants<EvaluateArgs>;
|
|
98
|
+
|
|
99
|
+
declare type BenchFireActionArgs = BenchArgsWithGrants<FireActionArgs>;
|
|
100
|
+
|
|
101
|
+
declare type BenchSetStageArgs = BenchArgs<SetStageArgs>;
|
|
102
|
+
|
|
103
|
+
declare type BenchStartInstanceArgs = BenchArgs<StartInstanceArgs>;
|
|
93
104
|
|
|
94
105
|
/** Base bench state, distinct from the engine/read/guard/query helper groups. */
|
|
95
106
|
declare interface BenchState {
|
|
96
107
|
/** The fake Sanity client. Passed automatically by bench wrappers. */
|
|
97
108
|
client: TestClient;
|
|
109
|
+
/** The engine-scope partition tag every wrapped call runs under. */
|
|
110
|
+
tag: string;
|
|
111
|
+
/** The workflow resource every wrapped call targets. */
|
|
112
|
+
workflowResource: WorkflowResource;
|
|
113
|
+
/** The declared execution context stamped on every wrapped call's history
|
|
114
|
+
* entries (default `{kind: 'test'}`) — inherited by {@link createBenchEngine}. */
|
|
115
|
+
executionContext: DeclaredExecutionContext;
|
|
98
116
|
/**
|
|
99
|
-
* The
|
|
100
|
-
*
|
|
101
|
-
* `ALL_ACCESS`.
|
|
117
|
+
* The bench's default identity (actor + grants) — registered as the
|
|
118
|
+
* default token on the fake client. Defaults to `ALL_ACCESS`.
|
|
102
119
|
*/
|
|
103
120
|
access: WorkflowAccess;
|
|
104
121
|
/** Convenience: `access.actor`. */
|
|
105
122
|
currentUser: Actor;
|
|
106
123
|
/** Convenience: `access.grants ?? []`. */
|
|
107
124
|
grants: Grant[];
|
|
125
|
+
/**
|
|
126
|
+
* A token-bound sibling client acting as `access` — the bench's ONE
|
|
127
|
+
* identity-registration seam. Registers the pair on the fake client's
|
|
128
|
+
* family-shared registry (idempotent, cached per identity; omitted
|
|
129
|
+
* grants default to wildcard) and returns the sibling whose
|
|
130
|
+
* `/users/me` + ACL path serve it — the engine token-resolves it like
|
|
131
|
+
* production. Bind it to {@link createBenchEngine} to act as another
|
|
132
|
+
* user end-to-end; never hand-roll `addIdentity` + `withConfig`.
|
|
133
|
+
*/
|
|
134
|
+
clientFor: (access: WorkflowAccess) => TestClient;
|
|
108
135
|
/** Add documents to the store after construction. */
|
|
109
136
|
seedDocuments: (documents: StoreDocument[]) => void;
|
|
110
137
|
/**
|
|
@@ -117,8 +144,7 @@ declare interface BenchState {
|
|
|
117
144
|
* Freeze the bench clock at an explicit ISO instant. Every subsequent
|
|
118
145
|
* engine call (`startInstance` / `fireAction` / `tick` / `completeEffect`
|
|
119
146
|
* / `evaluate`) sees this as `$now` and stamps it on history, so
|
|
120
|
-
* time-based `
|
|
121
|
-
* deterministic.
|
|
147
|
+
* time-based `when` triggers and SLA predicates are deterministic.
|
|
122
148
|
*/
|
|
123
149
|
setNow: (iso: string) => void;
|
|
124
150
|
/**
|
|
@@ -129,31 +155,7 @@ declare interface BenchState {
|
|
|
129
155
|
advance: (ms: number) => void;
|
|
130
156
|
}
|
|
131
157
|
|
|
132
|
-
declare type BenchTickArgs =
|
|
133
|
-
access?: WorkflowAccess;
|
|
134
|
-
/** Shortcut for `access.actor`. */
|
|
135
|
-
actor?: Actor;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* One recorded invocation of a client method — the spy layer behind
|
|
140
|
-
* `client.calls()` / `client.callCount()`. `result` holds the resolved
|
|
141
|
-
* value (awaited, for async methods); `error` holds the thrown/rejected
|
|
142
|
-
* value. Builder-returning methods (`patch`, `transaction`) record the
|
|
143
|
-
* builder as `result`; their eventual writes show up in the mutation log.
|
|
144
|
-
*/
|
|
145
|
-
declare interface CallRecord {
|
|
146
|
-
/** Method name; namespace methods are dotted, e.g. `releases.create`. */
|
|
147
|
-
method: string;
|
|
148
|
-
/** Arguments as passed (stored by reference, like `vi.fn`). */
|
|
149
|
-
args: unknown[];
|
|
150
|
-
/** Resolved/returned value, set once the call settles successfully. */
|
|
151
|
-
result?: unknown;
|
|
152
|
-
/** Thrown or rejected value, set if the call failed. */
|
|
153
|
-
error?: unknown;
|
|
154
|
-
projectId: string;
|
|
155
|
-
dataset: string;
|
|
156
|
-
}
|
|
158
|
+
declare type BenchTickArgs = BenchArgs<OperationArgs>;
|
|
157
159
|
|
|
158
160
|
/** Helpers composed across the wrapper + read groups at bench assembly. */
|
|
159
161
|
declare interface ComposedHelpers {
|
|
@@ -177,14 +179,28 @@ declare interface ComposedHelpers {
|
|
|
177
179
|
*/
|
|
178
180
|
export declare function createBench(options?: CreateBenchOptions): Bench;
|
|
179
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Build an out-of-band {@link Engine} on a bench's client, inheriting the
|
|
184
|
+
* bench's tag, workflow resource, and clock (so `setNow` / `advance` govern
|
|
185
|
+
* the engine's `$now` too). Use this when a test needs an engine of its own —
|
|
186
|
+
* custom effect handlers, racing two drainers — rather than the wrappers the
|
|
187
|
+
* bench exposes. `overrides` win over every inherited value.
|
|
188
|
+
*/
|
|
189
|
+
export declare function createBenchEngine(
|
|
190
|
+
bench: EngineScope & {
|
|
191
|
+
now: Clock;
|
|
192
|
+
},
|
|
193
|
+
overrides?: Partial<CreateEngineArgs>,
|
|
194
|
+
): Engine;
|
|
195
|
+
|
|
180
196
|
export declare interface CreateBenchOptions {
|
|
181
197
|
/** Subject documents seeded into the store. */
|
|
182
198
|
documents?: StoreDocument[];
|
|
183
199
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* test denial paths.
|
|
200
|
+
* The bench's default identity (actor + grants), registered as the
|
|
201
|
+
* default token on the fake client — the engine token-resolves it like
|
|
202
|
+
* production. Defaults to `ALL_ACCESS` — wildcard user + wildcard
|
|
203
|
+
* grants. Pass a restrictive value to test denial paths.
|
|
188
204
|
*
|
|
189
205
|
* Mutually exclusive with the `currentUser` / `grants` shortcuts
|
|
190
206
|
* (which compose into the same shape internally).
|
|
@@ -195,28 +211,7 @@ export declare interface CreateBenchOptions {
|
|
|
195
211
|
/** Shortcut for `access.grants`. Composed with `currentUser`. */
|
|
196
212
|
grants?: Grant[];
|
|
197
213
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* When set, the bench's underlying TestClient:
|
|
201
|
-
* - Exposes `client.request({ url })` serving `/users/me` + the
|
|
202
|
-
* configured ACL path, so the engine can resolve `WorkflowAccess`
|
|
203
|
-
* from "the token" exactly like production.
|
|
204
|
-
* - Rejects writes the grants don't permit with a Sanity-style
|
|
205
|
-
* `WriteAccessDeniedError` — the same 403 a real lake would
|
|
206
|
-
* return.
|
|
207
|
-
*
|
|
208
|
-
* Use this to demonstrate what would happen if the engine's
|
|
209
|
-
* soft-gate were bypassed (or disagreed with) — the lake is still
|
|
210
|
-
* authoritative. The bench's `access` field is the engine-side
|
|
211
|
-
* override; `accessControl` is the lake-side enforcement. They
|
|
212
|
-
* usually carry the same `actor` + `grants` but can be made to
|
|
213
|
-
* disagree to test edge cases.
|
|
214
|
-
*
|
|
215
|
-
* Mutually exclusive with passing a pre-built `client`.
|
|
216
|
-
*/
|
|
217
|
-
accessControl?: TestClientAccessControl;
|
|
218
|
-
/**
|
|
219
|
-
* Engine-scope environment partition. Defaults to `"bench"`. Pass
|
|
214
|
+
* Engine-scope environment partition. Defaults to {@link BENCH_TAG}. Pass
|
|
220
215
|
* different tags to two benches sharing a `client` to assert partition
|
|
221
216
|
* isolation; pass the same tag to assert visibility through a shared
|
|
222
217
|
* partition.
|
|
@@ -246,140 +241,71 @@ export declare interface CreateBenchOptions {
|
|
|
246
241
|
* start on the wall clock (`advance` / `setNow` can freeze it later).
|
|
247
242
|
*/
|
|
248
243
|
now?: string;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
/** @deprecated singular `role` on legacy responses */
|
|
256
|
-
role?: string;
|
|
257
|
-
roles?: {
|
|
258
|
-
name: string;
|
|
259
|
-
title?: string;
|
|
260
|
-
description?: string;
|
|
261
|
-
}[];
|
|
244
|
+
/**
|
|
245
|
+
* Declared execution context stamped (with the inferred runtime) on every
|
|
246
|
+
* history entry the bench's engine calls append. Defaults to
|
|
247
|
+
* `{ kind: "test" }`; override to exercise provenance rendering.
|
|
248
|
+
*/
|
|
249
|
+
executionContext?: DeclaredExecutionContext;
|
|
262
250
|
}
|
|
263
251
|
|
|
264
252
|
/** Default workflow resource for benches that don't pass one. */
|
|
265
253
|
export declare const DEFAULT_WORKFLOW_RESOURCE: WorkflowResource;
|
|
266
254
|
|
|
267
255
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
256
|
+
* The write {@link GuardHelpers.editDocument} performs. Every arm is a real
|
|
257
|
+
* distinct client write the guard seam vets under that action — `create` is
|
|
258
|
+
* deliberately absent because editing addresses an existing document; create
|
|
259
|
+
* through the client (`bench.client.create` / `clientFor(...).create`), which
|
|
260
|
+
* the same seam gates as a create.
|
|
272
261
|
*/
|
|
273
|
-
declare
|
|
274
|
-
_id?: string;
|
|
275
|
-
_type: string;
|
|
276
|
-
[field: string]: unknown;
|
|
277
|
-
}
|
|
262
|
+
export declare type EditDocumentAction = Exclude<MutationGuardAction, "create">;
|
|
278
263
|
|
|
279
|
-
|
|
264
|
+
/**
|
|
265
|
+
* How {@link GuardHelpers.editDocument} mutates the document. Each present
|
|
266
|
+
* key becomes one patch op, applied in `set` → `unset` → `insert` order in a
|
|
267
|
+
* single commit; the shapes mirror the client's patch builder.
|
|
268
|
+
*/
|
|
269
|
+
export declare type EditDocumentPatch = {
|
|
270
|
+
set?: Record<string, unknown>;
|
|
271
|
+
unset?: string[];
|
|
272
|
+
insert?: {
|
|
273
|
+
position: "before" | "after" | "replace";
|
|
274
|
+
anchor: string;
|
|
275
|
+
items: unknown[];
|
|
276
|
+
};
|
|
277
|
+
};
|
|
280
278
|
|
|
281
|
-
/**
|
|
279
|
+
/** The bench-owned engine call context injected into every wrapped verb. */
|
|
280
|
+
declare type EngineScope = {
|
|
281
|
+
client: TestClient;
|
|
282
|
+
tag: string;
|
|
283
|
+
workflowResource: WorkflowResource;
|
|
284
|
+
executionContext: DeclaredExecutionContext;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/** Engine-call wrappers that inject the bench-owned client + tag + identity. */
|
|
282
288
|
declare type EngineWrappers = {
|
|
283
|
-
deployDefinitions: (
|
|
284
|
-
args: DeployDefinitionsArgs
|
|
289
|
+
deployDefinitions: <T extends WorkflowDefinitionInput<T>>(
|
|
290
|
+
args: DeployDefinitionsArgs<T>,
|
|
285
291
|
) => Promise<DeployDefinitionsResult>;
|
|
292
|
+
deleteDefinition: (
|
|
293
|
+
args: BenchDeleteDefinitionArgs,
|
|
294
|
+
) => Promise<DeleteDefinitionResult>;
|
|
286
295
|
startInstance: (args: BenchStartInstanceArgs) => Promise<OperationResult>;
|
|
287
296
|
fireAction: (args: BenchFireActionArgs) => Promise<OperationResult>;
|
|
288
297
|
editField: (args: BenchEditFieldArgs) => Promise<OperationResult>;
|
|
289
298
|
completeEffect: (args: BenchCompleteEffectArgs) => Promise<OperationResult>;
|
|
290
299
|
tick: (args: BenchTickArgs) => Promise<OperationResult>;
|
|
291
300
|
evaluate: (args: BenchEvaluateArgs) => Promise<WorkflowEvaluation>;
|
|
301
|
+
/** Admin override — bypass filters/transitions and force the stage. */
|
|
302
|
+
setStage: (args: BenchSetStageArgs) => Promise<OperationResult>;
|
|
303
|
+
/** Admin override — hard-stop an in-flight instance where it stands. */
|
|
304
|
+
abortInstance: (args: BenchAbortInstanceArgs) => Promise<OperationResult>;
|
|
292
305
|
guardsForInstance: (instanceId: string) => Promise<MutationGuardDoc[]>;
|
|
293
306
|
guardsForDefinition: (definition: string) => Promise<MutationGuardDoc[]>;
|
|
294
307
|
};
|
|
295
308
|
|
|
296
|
-
/**
|
|
297
|
-
* A single mutation in the raw array form accepted by `mutate()` — the
|
|
298
|
-
* same shape `@sanity/client` accepts. The inline `patch` form mirrors
|
|
299
|
-
* the real client's keyed patch object.
|
|
300
|
-
*/
|
|
301
|
-
declare type FakeMutation =
|
|
302
|
-
| {
|
|
303
|
-
create: SanityDocumentLike;
|
|
304
|
-
}
|
|
305
|
-
| {
|
|
306
|
-
createOrReplace: SanityDocumentLike;
|
|
307
|
-
}
|
|
308
|
-
| {
|
|
309
|
-
createIfNotExists: SanityDocumentLike;
|
|
310
|
-
}
|
|
311
|
-
| {
|
|
312
|
-
delete: {
|
|
313
|
-
id: string;
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
| {
|
|
317
|
-
patch: {
|
|
318
|
-
id: string;
|
|
319
|
-
ifRevisionID?: string;
|
|
320
|
-
set?: Record<string, unknown>;
|
|
321
|
-
setIfMissing?: Record<string, unknown>;
|
|
322
|
-
unset?: string[];
|
|
323
|
-
inc?: Record<string, number>;
|
|
324
|
-
dec?: Record<string, number>;
|
|
325
|
-
insert?: {
|
|
326
|
-
before?: string;
|
|
327
|
-
after?: string;
|
|
328
|
-
replace?: string;
|
|
329
|
-
items: unknown[];
|
|
330
|
-
};
|
|
331
|
-
};
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Data-plane subset of `@sanity/client`'s surface, with multi-dataset support.
|
|
336
|
-
*
|
|
337
|
-
* One `createTestClient()` call creates a registry of stores keyed by
|
|
338
|
-
* `(projectId, dataset)`. The returned client points at one of them.
|
|
339
|
-
* `withConfig({ projectId, dataset })` returns a new handle into the same
|
|
340
|
-
* registry but pointing at a different store — useful for workflows where
|
|
341
|
-
* definitions live in one dataset and instances live in another.
|
|
342
|
-
*
|
|
343
|
-
* Two separate `createTestClient()` calls have separate registries, so
|
|
344
|
-
* tests stay isolated from each other.
|
|
345
|
-
*
|
|
346
|
-
* Methods not on the real client:
|
|
347
|
-
*
|
|
348
|
-
* - `seed`, `add`, `reset`, `snapshot`, `store` — bulk-load helpers,
|
|
349
|
-
* scoped to the current dataset.
|
|
350
|
-
* - `simulateConcurrentEdit()` — force the next N revision-guarded writes
|
|
351
|
-
* to a document to 409, for optimistic-locking retry tests.
|
|
352
|
-
* - `datasets()` — lists known dataset keys in this registry.
|
|
353
|
-
*
|
|
354
|
-
* Methods deliberately omitted:
|
|
355
|
-
*
|
|
356
|
-
* - Project / agent / asset / live / listen APIs.
|
|
357
|
-
*/
|
|
358
|
-
declare interface FetchOptions {
|
|
359
|
-
/** Override the client's configured perspective for this read. */
|
|
360
|
-
perspective?: Perspective;
|
|
361
|
-
/**
|
|
362
|
-
* When `false`, resolve to a {@link RawQueryResponse} (`{ query, ms,
|
|
363
|
-
* result }`) instead of the bare result — mirrors `@sanity/client`.
|
|
364
|
-
* Defaults to `true`.
|
|
365
|
-
*/
|
|
366
|
-
filterResponse?: boolean;
|
|
367
|
-
/**
|
|
368
|
-
* With `filterResponse: false`, set to `false` to omit `query` from the
|
|
369
|
-
* raw response. Ignored when `filterResponse` is not `false`.
|
|
370
|
-
*/
|
|
371
|
-
returnQuery?: boolean;
|
|
372
|
-
/** Accepted for signature parity; not simulated. */
|
|
373
|
-
tag?: string;
|
|
374
|
-
/** Accepted for signature parity; not simulated. */
|
|
375
|
-
signal?: AbortSignal;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
declare interface Grant_2 {
|
|
379
|
-
filter: string;
|
|
380
|
-
permissions: DocumentValuePermission[];
|
|
381
|
-
}
|
|
382
|
-
|
|
383
309
|
declare type GuardHelpers = {
|
|
384
310
|
/** All deployed guard docs ({@link MutationGuardDoc}) in the store. */
|
|
385
311
|
listGuards: () => Promise<MutationGuardDoc[]>;
|
|
@@ -398,119 +324,27 @@ declare type GuardHelpers = {
|
|
|
398
324
|
* throws `MutationGuardDeniedError` on denial — the lake's intended
|
|
399
325
|
* rejection, simulated by the bench. `as` routes the write through a
|
|
400
326
|
* token-scoped sibling so `identity()` resolves to that actor.
|
|
327
|
+
*
|
|
328
|
+
* Each {@link EditDocumentAction} performs that real write, so a guard
|
|
329
|
+
* scoped to it is genuinely consulted: `update` (default) applies `patch`;
|
|
330
|
+
* `delete` deletes; `publish` promotes the existing draft
|
|
331
|
+
* (`drafts.<documentId>`) onto `documentId`; `unpublish` retracts
|
|
332
|
+
* `documentId` into its draft. `documentId` is always the published id.
|
|
333
|
+
* A `patch` ({@link EditDocumentPatch}) is required for `update` and
|
|
334
|
+
* rejected for every other action — the combination is contradictory input.
|
|
335
|
+
*
|
|
336
|
+
* Resolves to the post-write document as the DEFAULT identity reads it —
|
|
337
|
+
* `undefined` after `delete`/`unpublish` (the doc is gone), or when the
|
|
338
|
+
* default identity cannot read the written doc.
|
|
401
339
|
*/
|
|
402
340
|
editDocument: (opts: {
|
|
403
341
|
documentId: string;
|
|
404
|
-
patch
|
|
405
|
-
action?:
|
|
342
|
+
patch?: EditDocumentPatch;
|
|
343
|
+
action?: EditDocumentAction;
|
|
406
344
|
as?: Actor;
|
|
407
|
-
}) => Promise<StoreDocument>;
|
|
345
|
+
}) => Promise<StoreDocument | undefined>;
|
|
408
346
|
};
|
|
409
347
|
|
|
410
|
-
/** Result of a mutation when ids are requested for all affected documents. */
|
|
411
|
-
declare interface MultipleMutationResult {
|
|
412
|
-
transactionId: string;
|
|
413
|
-
documentIds: string[];
|
|
414
|
-
results: {
|
|
415
|
-
id: string;
|
|
416
|
-
operation: MutationOperation;
|
|
417
|
-
}[];
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/** Append-only record of one committed transaction (a single mutation is a 1-entry transaction). */
|
|
421
|
-
declare interface MutationLogEntry {
|
|
422
|
-
transactionId: string;
|
|
423
|
-
projectId: string;
|
|
424
|
-
dataset: string;
|
|
425
|
-
mutations: TxnMutation[];
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/** The operation recorded per affected document, matching `@sanity/client`. */
|
|
429
|
-
declare type MutationOperation = "create" | "update" | "delete" | "none";
|
|
430
|
-
|
|
431
|
-
declare interface MutationOptions {
|
|
432
|
-
ifRevisionId?: string;
|
|
433
|
-
/**
|
|
434
|
-
* Shape the resolved value, mirroring `@sanity/client`:
|
|
435
|
-
* - default → the mutated document
|
|
436
|
-
* - `returnDocuments: false` → an id-shaped mutation result
|
|
437
|
-
* ({@link SingleMutationResult} / {@link MultipleMutationResult})
|
|
438
|
-
* - `returnFirst: false` with documents → an array
|
|
439
|
-
*
|
|
440
|
-
* The static return type reflects the default (the document); pass a
|
|
441
|
-
* type argument or cast when requesting an id-shaped result.
|
|
442
|
-
*/
|
|
443
|
-
returnDocuments?: boolean;
|
|
444
|
-
returnFirst?: boolean;
|
|
445
|
-
/** Override the synthetic transaction id used in the result and log. */
|
|
446
|
-
transactionId?: string;
|
|
447
|
-
/** When `true`, validate + shape the result but don't persist. */
|
|
448
|
-
dryRun?: boolean;
|
|
449
|
-
/** Accepted for signature parity; not simulated. */
|
|
450
|
-
visibility?: "sync" | "async" | "deferred";
|
|
451
|
-
/** Accepted for signature parity; not simulated. */
|
|
452
|
-
autoGenerateArrayKeys?: boolean;
|
|
453
|
-
/** Accepted for signature parity; not simulated. */
|
|
454
|
-
tag?: string;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
declare interface PatchHandle {
|
|
458
|
-
set: (props: Record<string, unknown>) => PatchHandle;
|
|
459
|
-
setIfMissing: (props: Record<string, unknown>) => PatchHandle;
|
|
460
|
-
unset: (paths: string[]) => PatchHandle;
|
|
461
|
-
inc: (props: Record<string, number>) => PatchHandle;
|
|
462
|
-
dec: (props: Record<string, number>) => PatchHandle;
|
|
463
|
-
insert: (
|
|
464
|
-
position: "before" | "after" | "replace",
|
|
465
|
-
anchor: string,
|
|
466
|
-
items: unknown[],
|
|
467
|
-
) => PatchHandle;
|
|
468
|
-
ifRevisionId: (rev: string) => PatchHandle;
|
|
469
|
-
commit: () => Promise<SanityDocument>;
|
|
470
|
-
/** Test-only introspection — not on real Patch */
|
|
471
|
-
toJSON: () => {
|
|
472
|
-
id: string;
|
|
473
|
-
ops: PatchOp[];
|
|
474
|
-
ifRev?: string;
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Patch operations the test client supports.
|
|
480
|
-
*
|
|
481
|
-
* Subset of `@sanity/client`'s PatchOperations. Each entry is one
|
|
482
|
-
* mutation step applied in order at commit time.
|
|
483
|
-
*/
|
|
484
|
-
declare type PatchOp =
|
|
485
|
-
| {
|
|
486
|
-
kind: "set";
|
|
487
|
-
props: Record<string, unknown>;
|
|
488
|
-
}
|
|
489
|
-
| {
|
|
490
|
-
kind: "setIfMissing";
|
|
491
|
-
props: Record<string, unknown>;
|
|
492
|
-
}
|
|
493
|
-
| {
|
|
494
|
-
kind: "unset";
|
|
495
|
-
paths: string[];
|
|
496
|
-
}
|
|
497
|
-
| {
|
|
498
|
-
kind: "inc";
|
|
499
|
-
props: Record<string, number>;
|
|
500
|
-
}
|
|
501
|
-
| {
|
|
502
|
-
kind: "dec";
|
|
503
|
-
props: Record<string, number>;
|
|
504
|
-
}
|
|
505
|
-
| {
|
|
506
|
-
kind: "insert";
|
|
507
|
-
position: "before" | "after" | "replace";
|
|
508
|
-
anchor: string;
|
|
509
|
-
items: unknown[];
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
declare type Perspective = "raw" | "published" | "drafts" | string[];
|
|
513
|
-
|
|
514
348
|
declare type QueryHelpers = {
|
|
515
349
|
/**
|
|
516
350
|
* Run an arbitrary GROQ query through `workflow.query`. The query MUST
|
|
@@ -542,34 +376,35 @@ declare type QueryHelpers = {
|
|
|
542
376
|
};
|
|
543
377
|
|
|
544
378
|
declare type ReadHelpers = {
|
|
545
|
-
/**
|
|
546
|
-
|
|
379
|
+
/**
|
|
380
|
+
* The engine's instance point-read: throws typed `InstanceNotFoundError`
|
|
381
|
+
* when the doc is missing or belongs to another engine's tag partition.
|
|
382
|
+
*/
|
|
383
|
+
getInstance: (args: InstanceRefArgs) => Promise<WorkflowInstance>;
|
|
547
384
|
/** Current stage name of an instance. */
|
|
548
385
|
currentStage: (instanceId: string) => Promise<string>;
|
|
549
|
-
/**
|
|
550
|
-
|
|
386
|
+
/** Every pending effect on an instance — claimed and unclaimed alike. */
|
|
387
|
+
listPendingEffects: (args: InstanceRefArgs) => Promise<PendingEffect[]>;
|
|
388
|
+
/** Pending effects filtered by claim presence and/or effect names. */
|
|
389
|
+
findPendingEffects: (
|
|
390
|
+
args: FindPendingEffectsArgs,
|
|
391
|
+
) => Promise<PendingEffect[]>;
|
|
551
392
|
/** Status of a single activity on an instance, or undefined if missing. */
|
|
552
393
|
activityStatus: (
|
|
553
394
|
instanceId: string,
|
|
554
395
|
activity: string,
|
|
555
396
|
) => Promise<ActivityStatus | undefined>;
|
|
556
|
-
/** The rendered `$
|
|
557
|
-
*
|
|
558
|
-
|
|
397
|
+
/** The rendered `$context` map — the start-time seed + a parent's spawn
|
|
398
|
+
* handoff, keyed by entry `name`. */
|
|
399
|
+
contextMap: (instanceId: string) => Promise<Record<string, unknown>>;
|
|
400
|
+
/** The rendered `$effects` map — completed effects' outputs, namespaced
|
|
401
|
+
* by effect name. */
|
|
402
|
+
effectOutputsMap: (instanceId: string) => Promise<Record<string, unknown>>;
|
|
559
403
|
/**
|
|
560
|
-
* Spawned children of
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
* Walks `history.spawned` entries on the parent — this is the durable
|
|
564
|
-
* record. (The per-activity `spawnedInstances` field only lives on the
|
|
565
|
-
* open StageEntry's activities; history survives transitions.)
|
|
566
|
-
*
|
|
567
|
-
* Returns children ordered by `startedAt` ascending.
|
|
404
|
+
* Spawned children of the instance, optionally restricted to those
|
|
405
|
+
* spawned by `activity`. Ordered by `startedAt` ascending.
|
|
568
406
|
*/
|
|
569
|
-
children: (
|
|
570
|
-
parentInstanceId: string,
|
|
571
|
-
activity?: string,
|
|
572
|
-
) => Promise<WorkflowInstance[]>;
|
|
407
|
+
children: (args: ChildrenArgs) => Promise<WorkflowInstance[]>;
|
|
573
408
|
/**
|
|
574
409
|
* All workflow instances whose `doc.ref` field entry named
|
|
575
410
|
* `entryName` (default `"subject"`) matches the given doc id. Useful
|
|
@@ -581,74 +416,37 @@ declare type ReadHelpers = {
|
|
|
581
416
|
entryName?: string,
|
|
582
417
|
) => Promise<WorkflowInstance[]>;
|
|
583
418
|
/**
|
|
584
|
-
* All instances of `
|
|
585
|
-
*
|
|
586
|
-
*
|
|
419
|
+
* All instances of `definition` currently at `stage`. In-flight only by
|
|
420
|
+
* default; `includeCompleted: true` widens to completed/aborted instances.
|
|
421
|
+
* The options derive from the engine's {@link InstancesQueryFilter}, so a
|
|
422
|
+
* vocabulary drift is a compile error here, not a silently-dropped filter.
|
|
587
423
|
*/
|
|
588
|
-
instancesByStage: (
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}) => Promise<WorkflowInstance[]>;
|
|
424
|
+
instancesByStage: (
|
|
425
|
+
options: Required<Pick<InstancesQueryFilter, "definition" | "stage">> &
|
|
426
|
+
Pick<InstancesQueryFilter, "includeCompleted">,
|
|
427
|
+
) => Promise<WorkflowInstance[]>;
|
|
593
428
|
/**
|
|
594
429
|
* Every in-flight instance whose reactive watch-set includes `document`
|
|
595
430
|
* (a resource-qualified GDR URI) — the reverse of the forward watch-set.
|
|
596
431
|
* Answers "which instances should this changed doc `tick`?" the way a
|
|
597
432
|
* non-reactive content forwarder needs. Sorted by `startedAt` ascending.
|
|
598
433
|
*/
|
|
599
|
-
instancesForDocument: (
|
|
434
|
+
instancesForDocument: (
|
|
435
|
+
args: InstancesForDocumentArgs,
|
|
436
|
+
) => Promise<WorkflowInstance[]>;
|
|
437
|
+
/**
|
|
438
|
+
* The startable half of {@link ReadHelpers.instancesForDocument}: the
|
|
439
|
+
* latest deployed version of every definition applicable to the LOADED
|
|
440
|
+
* candidate document — startable, a required subject entry accepts its
|
|
441
|
+
* `_type`, `start.filter` passes. All matches, name ascending.
|
|
442
|
+
*/
|
|
443
|
+
definitionsForDocument: (
|
|
444
|
+
args: DefinitionsForDocumentArgs,
|
|
445
|
+
) => Promise<DeployedDefinition[]>;
|
|
600
446
|
/** Snapshot of all documents in the store — for inspecting the world. */
|
|
601
447
|
snapshot: () => readonly StoreDocument[];
|
|
602
448
|
};
|
|
603
449
|
|
|
604
|
-
declare type ReleaseAction =
|
|
605
|
-
| {
|
|
606
|
-
actionType: "sanity.action.release.create";
|
|
607
|
-
releaseId: string;
|
|
608
|
-
metadata?: Partial<ReleaseMetadata>;
|
|
609
|
-
}
|
|
610
|
-
| {
|
|
611
|
-
actionType: "sanity.action.release.edit";
|
|
612
|
-
releaseId: string;
|
|
613
|
-
patch: {
|
|
614
|
-
set?: Record<string, unknown>;
|
|
615
|
-
unset?: string[];
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
| {
|
|
619
|
-
actionType: "sanity.action.release.schedule";
|
|
620
|
-
releaseId: string;
|
|
621
|
-
publishAt: string;
|
|
622
|
-
}
|
|
623
|
-
| {
|
|
624
|
-
actionType: "sanity.action.release.unschedule";
|
|
625
|
-
releaseId: string;
|
|
626
|
-
}
|
|
627
|
-
| {
|
|
628
|
-
actionType: "sanity.action.release.publish";
|
|
629
|
-
releaseId: string;
|
|
630
|
-
}
|
|
631
|
-
| {
|
|
632
|
-
actionType: "sanity.action.release.archive";
|
|
633
|
-
releaseId: string;
|
|
634
|
-
}
|
|
635
|
-
| {
|
|
636
|
-
actionType: "sanity.action.release.unarchive";
|
|
637
|
-
releaseId: string;
|
|
638
|
-
}
|
|
639
|
-
| {
|
|
640
|
-
actionType: "sanity.action.release.delete";
|
|
641
|
-
releaseId: string;
|
|
642
|
-
};
|
|
643
|
-
|
|
644
|
-
declare interface ReleaseDocument extends SanityDocument {
|
|
645
|
-
_type: "system.release";
|
|
646
|
-
name: string;
|
|
647
|
-
state: ReleaseState;
|
|
648
|
-
metadata: ReleaseMetadata;
|
|
649
|
-
publishAt?: string;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
450
|
/**
|
|
653
451
|
* A `release.ref` entry pointing at `_.releases.<releaseName>` in the bench's
|
|
654
452
|
* default resource — what a release-targeting workflow's `release` field
|
|
@@ -661,86 +459,6 @@ export declare function releaseField(
|
|
|
661
459
|
},
|
|
662
460
|
): InitialFieldValue;
|
|
663
461
|
|
|
664
|
-
declare interface ReleaseMetadata {
|
|
665
|
-
title?: string;
|
|
666
|
-
description?: string;
|
|
667
|
-
releaseType?: ReleaseType;
|
|
668
|
-
intendedPublishAt?: string;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
declare interface ReleasesHandle {
|
|
672
|
-
create: (params: {
|
|
673
|
-
releaseId: string;
|
|
674
|
-
metadata?: Partial<ReleaseMetadata>;
|
|
675
|
-
}) => Promise<
|
|
676
|
-
ActionResult & {
|
|
677
|
-
releaseId: string;
|
|
678
|
-
metadata: ReleaseMetadata;
|
|
679
|
-
}
|
|
680
|
-
>;
|
|
681
|
-
edit: (params: {
|
|
682
|
-
releaseId: string;
|
|
683
|
-
patch: {
|
|
684
|
-
set?: Record<string, unknown>;
|
|
685
|
-
unset?: string[];
|
|
686
|
-
};
|
|
687
|
-
}) => Promise<ActionResult>;
|
|
688
|
-
get: (params: { releaseId: string }) => Promise<ReleaseDocument | undefined>;
|
|
689
|
-
fetchDocuments: (params: { releaseId: string }) => Promise<SanityDocument[]>;
|
|
690
|
-
schedule: (params: {
|
|
691
|
-
releaseId: string;
|
|
692
|
-
publishAt: string;
|
|
693
|
-
}) => Promise<ActionResult>;
|
|
694
|
-
unschedule: (params: { releaseId: string }) => Promise<ActionResult>;
|
|
695
|
-
publish: (params: { releaseId: string }) => Promise<ActionResult>;
|
|
696
|
-
archive: (params: { releaseId: string }) => Promise<ActionResult>;
|
|
697
|
-
unarchive: (params: { releaseId: string }) => Promise<ActionResult>;
|
|
698
|
-
delete: (params: { releaseId: string }) => Promise<ActionResult>;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
declare type ReleaseState = "active" | "scheduled" | "published" | "archived";
|
|
702
|
-
|
|
703
|
-
declare type ReleaseType = "asap" | "scheduled" | "undecided";
|
|
704
|
-
|
|
705
|
-
/** @public */
|
|
706
|
-
declare interface SanityDocument {
|
|
707
|
-
_id: string;
|
|
708
|
-
_type: string;
|
|
709
|
-
_createdAt: string;
|
|
710
|
-
_updatedAt: string;
|
|
711
|
-
_rev: string;
|
|
712
|
-
[key: string]: unknown;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* Similar to `SanityDocument` but only requires the `_id` and `_type`
|
|
717
|
-
*
|
|
718
|
-
* @see SanityDocument
|
|
719
|
-
*
|
|
720
|
-
* @public
|
|
721
|
-
*/
|
|
722
|
-
declare interface SanityDocumentLike {
|
|
723
|
-
_id: string;
|
|
724
|
-
_type: string;
|
|
725
|
-
_createdAt?: string;
|
|
726
|
-
_updatedAt?: string;
|
|
727
|
-
_rev?: string;
|
|
728
|
-
_system?: {
|
|
729
|
-
delete?: boolean;
|
|
730
|
-
};
|
|
731
|
-
[key: string]: unknown;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/** Result of a mutation when `returnFirst` resolves true and ids are requested. */
|
|
735
|
-
declare interface SingleMutationResult {
|
|
736
|
-
transactionId: string;
|
|
737
|
-
documentId: string;
|
|
738
|
-
results: {
|
|
739
|
-
id: string;
|
|
740
|
-
operation: MutationOperation;
|
|
741
|
-
}[];
|
|
742
|
-
}
|
|
743
|
-
|
|
744
462
|
/** A document as stored in the bench's fake lake. */
|
|
745
463
|
declare type StoreDocument = {
|
|
746
464
|
_id: string;
|
|
@@ -761,312 +479,6 @@ export declare function subjectField(
|
|
|
761
479
|
},
|
|
762
480
|
): InitialFieldValue;
|
|
763
481
|
|
|
764
|
-
declare interface SystemFields {
|
|
765
|
-
_rev: string;
|
|
766
|
-
_createdAt: string;
|
|
767
|
-
_updatedAt: string;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
declare interface TestClient {
|
|
771
|
-
fetch: <T = unknown>(
|
|
772
|
-
query: string,
|
|
773
|
-
params?: Record<string, unknown>,
|
|
774
|
-
options?: FetchOptions,
|
|
775
|
-
) => Promise<T>;
|
|
776
|
-
getDocument: <T = SanityDocument>(
|
|
777
|
-
id: string,
|
|
778
|
-
options?: FetchOptions,
|
|
779
|
-
) => Promise<T | undefined>;
|
|
780
|
-
getDocuments: <T = SanityDocument>(
|
|
781
|
-
ids: string[],
|
|
782
|
-
options?: FetchOptions,
|
|
783
|
-
) => Promise<(T | null)[]>;
|
|
784
|
-
/**
|
|
785
|
-
* Sanity-style HTTP request hook. Present iff the client was
|
|
786
|
-
* created with `accessControl`. The engine's access resolver pokes
|
|
787
|
-
* `/users/me` and the configured ACL path through this — without it
|
|
788
|
-
* the engine throws on any verb that needs an actor (which is the
|
|
789
|
-
* cue for the bench to supply an `access` override).
|
|
790
|
-
*
|
|
791
|
-
* Test cases that want to exercise the engine's token-resolution
|
|
792
|
-
* path should configure `accessControl`; cases that want bench-style
|
|
793
|
-
* always-allow should leave it off and pass `access` through the
|
|
794
|
-
* bench.
|
|
795
|
-
*/
|
|
796
|
-
request?: <T>(opts: {
|
|
797
|
-
url?: string;
|
|
798
|
-
uri?: string;
|
|
799
|
-
signal?: AbortSignal;
|
|
800
|
-
tag?: string;
|
|
801
|
-
}) => Promise<T>;
|
|
802
|
-
create: <T extends SanityDocumentLike>(
|
|
803
|
-
doc: T,
|
|
804
|
-
options?: MutationOptions,
|
|
805
|
-
) => Promise<T & SystemFields>;
|
|
806
|
-
createOrReplace: <T extends SanityDocumentLike>(
|
|
807
|
-
doc: T,
|
|
808
|
-
options?: MutationOptions,
|
|
809
|
-
) => Promise<T & SystemFields>;
|
|
810
|
-
createIfNotExists: <T extends SanityDocumentLike>(
|
|
811
|
-
doc: T,
|
|
812
|
-
options?: MutationOptions,
|
|
813
|
-
) => Promise<T & SystemFields>;
|
|
814
|
-
delete: (
|
|
815
|
-
id: string,
|
|
816
|
-
options?: MutationOptions,
|
|
817
|
-
) => Promise<{
|
|
818
|
-
_id: string;
|
|
819
|
-
} | null>;
|
|
820
|
-
patch: (documentId: string) => PatchHandle;
|
|
821
|
-
transaction: () => TransactionHandle;
|
|
822
|
-
/**
|
|
823
|
-
* Apply a batch of mutations in the raw array form (or a `patch()` /
|
|
824
|
-
* `transaction()` builder) — mirrors `@sanity/client`'s `mutate()`.
|
|
825
|
-
* Atomic: any failure rolls the whole batch back. The resolved shape
|
|
826
|
-
* follows `options.returnDocuments` / `returnFirst` exactly like the
|
|
827
|
-
* real client (default → {@link MultipleMutationResult}).
|
|
828
|
-
*/
|
|
829
|
-
mutate: (
|
|
830
|
-
operations: FakeMutation[] | PatchHandle | TransactionHandle,
|
|
831
|
-
options?: TxnCommitOptions,
|
|
832
|
-
) => Promise<TxnCommitResult>;
|
|
833
|
-
/**
|
|
834
|
-
* Dispatch one or more release / version actions against the store.
|
|
835
|
-
* Returns a synthetic `transactionId`. Multi-action arrays are applied
|
|
836
|
-
* atomically — any failure rolls back the batch.
|
|
837
|
-
*/
|
|
838
|
-
action: (action: Action | Action[]) => Promise<ActionResult>;
|
|
839
|
-
/** Helpers mirroring `@sanity/client`'s `client.releases.*` surface. */
|
|
840
|
-
releases: ReleasesHandle;
|
|
841
|
-
/** Helper for `sanity.action.document.version.create`. */
|
|
842
|
-
createVersion: (params: {
|
|
843
|
-
releaseId: string;
|
|
844
|
-
publishedId: string;
|
|
845
|
-
document: DocumentBody;
|
|
846
|
-
}) => Promise<ActionResult>;
|
|
847
|
-
/** Helper for `sanity.action.document.version.unpublish`. */
|
|
848
|
-
unpublishVersion: (params: {
|
|
849
|
-
releaseId: string;
|
|
850
|
-
publishedId: string;
|
|
851
|
-
}) => Promise<ActionResult>;
|
|
852
|
-
/** Current `(projectId, dataset)` and active perspective. */
|
|
853
|
-
config: () => {
|
|
854
|
-
projectId: string;
|
|
855
|
-
dataset: string;
|
|
856
|
-
perspective: Perspective;
|
|
857
|
-
};
|
|
858
|
-
/**
|
|
859
|
-
* Return a new client handle pointing at a different `(projectId, dataset)`
|
|
860
|
-
* within the SAME backing registry. Either field can be omitted to keep
|
|
861
|
-
* the current value. Two clients produced by `withConfig` see each
|
|
862
|
-
* other's writes if they point at the same `(projectId, dataset)`.
|
|
863
|
-
*/
|
|
864
|
-
withConfig: (
|
|
865
|
-
config: Partial<{
|
|
866
|
-
projectId: string;
|
|
867
|
-
dataset: string;
|
|
868
|
-
perspective: Perspective;
|
|
869
|
-
/**
|
|
870
|
-
* Rebind the acting identity (and its grants) on a sibling that shares
|
|
871
|
-
* the same store — the test analogue of a token-scoped client. Lets two
|
|
872
|
-
* actors write the same dataset, e.g. for `identity()`-based guards.
|
|
873
|
-
*/
|
|
874
|
-
accessControl: TestClientAccessControl;
|
|
875
|
-
}>,
|
|
876
|
-
) => TestClient;
|
|
877
|
-
/** Replace the current dataset with the given documents. */
|
|
878
|
-
seed: (documents: SanityDocumentLike[]) => void;
|
|
879
|
-
/** Add documents to the current dataset without dropping existing ones. */
|
|
880
|
-
add: (documents: SanityDocumentLike[]) => void;
|
|
881
|
-
/** Drop all documents in the current dataset. */
|
|
882
|
-
reset: () => void;
|
|
883
|
-
/** Read-only snapshot of all documents in the current dataset. */
|
|
884
|
-
snapshot: () => readonly SanityDocument[];
|
|
885
|
-
/** Direct store access for the current dataset. */
|
|
886
|
-
store: () => TestStore;
|
|
887
|
-
/**
|
|
888
|
-
* Simulate `count` concurrent edits to `id` in the current dataset: the
|
|
889
|
-
* next `count` revision-guarded mutations (those passing `ifRevisionId`)
|
|
890
|
-
* to that document throw a 409-shaped conflict, as if another client
|
|
891
|
-
* committed between the caller's read and write. Lets a test exercise
|
|
892
|
-
* optimistic-locking retry paths deterministically, without timing or
|
|
893
|
-
* interleaving. Mutations that don't pass `ifRevisionId` are unaffected.
|
|
894
|
-
*/
|
|
895
|
-
simulateConcurrentEdit: (id: string, count?: number) => void;
|
|
896
|
-
/** List `(projectId, dataset)` pairs known in this registry. */
|
|
897
|
-
datasets: () => {
|
|
898
|
-
projectId: string;
|
|
899
|
-
dataset: string;
|
|
900
|
-
}[];
|
|
901
|
-
/**
|
|
902
|
-
* Register a handler for a `@sanity/client` member the fake doesn't
|
|
903
|
-
* implement (e.g. `listen`, `assets`). Accessing that member on the
|
|
904
|
-
* client then returns the handler instead of throwing the
|
|
905
|
-
* "not implemented" guard error. Shared across sibling `withConfig`
|
|
906
|
-
* handles. Stubs fill gaps only — they don't shadow implemented methods.
|
|
907
|
-
*/
|
|
908
|
-
stub: (name: string, handler: unknown) => void;
|
|
909
|
-
/** The currently-registered stubs, keyed by member name. */
|
|
910
|
-
stubs: () => Record<string, unknown>;
|
|
911
|
-
/**
|
|
912
|
-
* Every mutation committed through this client's registry, flattened
|
|
913
|
-
* and in order — `create` / `createOrReplace` / `createIfNotExists` /
|
|
914
|
-
* `delete` / `patch`, whether issued directly, via `transaction()`, or
|
|
915
|
-
* via `mutate()`. The spy hook for "assert exactly what writes my code
|
|
916
|
-
* submitted".
|
|
917
|
-
*/
|
|
918
|
-
mutations: () => TxnMutation[];
|
|
919
|
-
/**
|
|
920
|
-
* Every committed transaction, in order. A direct mutation is recorded
|
|
921
|
-
* as a one-mutation transaction. Each entry carries its synthetic
|
|
922
|
-
* `transactionId` and the `(projectId, dataset)` it targeted.
|
|
923
|
-
*/
|
|
924
|
-
transactions: () => MutationLogEntry[];
|
|
925
|
-
/** Clear the recorded mutation / transaction log. */
|
|
926
|
-
clearLog: () => void;
|
|
927
|
-
/**
|
|
928
|
-
* Every recorded method call, in order — the spy layer for "was `fetch`
|
|
929
|
-
* called, how many times, and with what params". Records the
|
|
930
|
-
* `@sanity/client`-surface methods (`fetch`, `getDocument`, `create`,
|
|
931
|
-
* `patch`, `mutate`, `releases.*`, …), not the test-only helpers.
|
|
932
|
-
* Pass a method name to filter (e.g. `calls("fetch")`); namespace
|
|
933
|
-
* methods are dotted (`calls("releases.create")`). Shared across
|
|
934
|
-
* `withConfig` siblings.
|
|
935
|
-
*/
|
|
936
|
-
calls: (method?: string) => CallRecord[];
|
|
937
|
-
/** Number of recorded calls, optionally filtered by method name. */
|
|
938
|
-
callCount: (method?: string) => number;
|
|
939
|
-
/** Clear the recorded call log. */
|
|
940
|
-
clearCalls: () => void;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
declare interface TestClientAccessControl {
|
|
944
|
-
/** What `client.request({ url: "/users/me" })` returns. */
|
|
945
|
-
currentUser: CurrentUser;
|
|
946
|
-
/** What `client.request({ url: aclPath })` returns. */
|
|
947
|
-
grants: Grant_2[];
|
|
948
|
-
/**
|
|
949
|
-
* URL path the engine's access resolver hits to fetch grants.
|
|
950
|
-
* Defaults to `/projects/<projectId>/datasets/<dataset>/acl`.
|
|
951
|
-
*/
|
|
952
|
-
aclPath?: string;
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
/**
|
|
956
|
-
* In-memory document store backing a TestClient. Closure-scoped — created
|
|
957
|
-
* fresh per test, no global state.
|
|
958
|
-
*
|
|
959
|
-
* The shape mirrors what `groq-js` evaluates against: an array of
|
|
960
|
-
* documents. Internally we keep a Map by `_id` for O(1) lookup, and
|
|
961
|
-
* project to an array on each query.
|
|
962
|
-
*/
|
|
963
|
-
declare interface TestStore {
|
|
964
|
-
/** Replace the entire dataset with the given documents. Existing docs are dropped. */
|
|
965
|
-
seed: (documents: SanityDocumentLike[]) => void;
|
|
966
|
-
/** Add documents without dropping existing ones. Conflicts (same _id) overwrite. */
|
|
967
|
-
add: (documents: SanityDocumentLike[]) => void;
|
|
968
|
-
/** Remove all documents. */
|
|
969
|
-
reset: () => void;
|
|
970
|
-
/** All documents as an array, in insertion order. */
|
|
971
|
-
all: () => SanityDocument[];
|
|
972
|
-
/** Get one document by _id, or undefined. */
|
|
973
|
-
get: (id: string) => SanityDocument | undefined;
|
|
974
|
-
/** Insert a fully-formed document by _id. Replaces if it already exists. */
|
|
975
|
-
put: (doc: SanityDocument) => void;
|
|
976
|
-
/** Remove a document by _id. Returns the removed doc or undefined. */
|
|
977
|
-
remove: (id: string) => SanityDocument | undefined;
|
|
978
|
-
/** Snapshot of all docs, useful for assertions. */
|
|
979
|
-
snapshot: () => readonly SanityDocument[];
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
declare interface TransactionHandle {
|
|
983
|
-
create: (doc: SanityDocumentLike) => TransactionHandle;
|
|
984
|
-
createOrReplace: (doc: SanityDocumentLike) => TransactionHandle;
|
|
985
|
-
createIfNotExists: (doc: SanityDocumentLike) => TransactionHandle;
|
|
986
|
-
delete: (id: string) => TransactionHandle;
|
|
987
|
-
/** Accept either a Patch builder (drained at commit) or an inline doc id + ops. */
|
|
988
|
-
patch: (
|
|
989
|
-
documentIdOrPatch: string | PatchHandle,
|
|
990
|
-
ops?: PatchOp[],
|
|
991
|
-
ifRev?: string,
|
|
992
|
-
) => TransactionHandle;
|
|
993
|
-
commit: (options?: TxnCommitOptions) => Promise<TxnCommitResult>;
|
|
994
|
-
/** Test-only introspection — not on real Transaction. */
|
|
995
|
-
toJSON: () => TxnMutation[];
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
declare interface TxnCommitOptions {
|
|
999
|
-
/**
|
|
1000
|
-
* Shape the resolved value (mirrors `@sanity/client`):
|
|
1001
|
-
* - default (both unset) → {@link MultipleMutationResult}
|
|
1002
|
-
* - `returnDocuments: true` → the resulting document(s)
|
|
1003
|
-
* - `returnFirst: true` with `returnDocuments` → a single document
|
|
1004
|
-
* - `returnDocuments: false` → an id-shaped mutation result
|
|
1005
|
-
*/
|
|
1006
|
-
returnDocuments?: boolean;
|
|
1007
|
-
returnFirst?: boolean;
|
|
1008
|
-
/** Override the synthetic transaction id (mirrors `client.transaction(undefined, { transactionId })`). */
|
|
1009
|
-
transactionId?: string;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
declare type TxnCommitResult =
|
|
1013
|
-
| MultipleMutationResult
|
|
1014
|
-
| SingleMutationResult
|
|
1015
|
-
| SanityDocument
|
|
1016
|
-
| SanityDocument[];
|
|
1017
|
-
|
|
1018
|
-
/**
|
|
1019
|
-
* A transaction collects mutations and applies them atomically at commit
|
|
1020
|
-
* time. Mutations are applied in declaration order; if any one fails, the
|
|
1021
|
-
* whole transaction throws and no writes land in the store.
|
|
1022
|
-
*
|
|
1023
|
-
* Subset of `@sanity/client`'s Transaction. Same shape, narrower surface.
|
|
1024
|
-
*/
|
|
1025
|
-
declare type TxnMutation =
|
|
1026
|
-
| {
|
|
1027
|
-
kind: "create";
|
|
1028
|
-
doc: SanityDocumentLike;
|
|
1029
|
-
}
|
|
1030
|
-
| {
|
|
1031
|
-
kind: "createOrReplace";
|
|
1032
|
-
doc: SanityDocumentLike;
|
|
1033
|
-
}
|
|
1034
|
-
| {
|
|
1035
|
-
kind: "createIfNotExists";
|
|
1036
|
-
doc: SanityDocumentLike;
|
|
1037
|
-
}
|
|
1038
|
-
| {
|
|
1039
|
-
kind: "delete";
|
|
1040
|
-
id: string;
|
|
1041
|
-
}
|
|
1042
|
-
| {
|
|
1043
|
-
kind: "patch";
|
|
1044
|
-
documentId: string;
|
|
1045
|
-
ops: PatchOp[];
|
|
1046
|
-
ifRev?: string;
|
|
1047
|
-
};
|
|
1048
|
-
|
|
1049
|
-
declare type VersionAction =
|
|
1050
|
-
| {
|
|
1051
|
-
actionType: "sanity.action.document.version.create";
|
|
1052
|
-
publishedId: string;
|
|
1053
|
-
document: DocumentBody;
|
|
1054
|
-
releaseId?: string;
|
|
1055
|
-
}
|
|
1056
|
-
| {
|
|
1057
|
-
actionType: "sanity.action.document.version.replace";
|
|
1058
|
-
document: SanityDocumentLike;
|
|
1059
|
-
}
|
|
1060
|
-
| {
|
|
1061
|
-
actionType: "sanity.action.document.version.discard";
|
|
1062
|
-
versionId: string;
|
|
1063
|
-
}
|
|
1064
|
-
| {
|
|
1065
|
-
actionType: "sanity.action.document.version.unpublish";
|
|
1066
|
-
versionId: string;
|
|
1067
|
-
publishedId: string;
|
|
1068
|
-
};
|
|
1069
|
-
|
|
1070
482
|
/**
|
|
1071
483
|
* The default grants used when tests don't specify any. A single
|
|
1072
484
|
* permissive grant matches every document and confers all permissions.
|