@sanity-labs/client-fake-for-test 0.5.0 → 0.7.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 +15 -7
- package/dist/index.cjs +116 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +135 -21
- package/dist/index.d.ts +135 -21
- package/dist/index.js +116 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare type Action = ReleaseAction | VersionAction;
|
|
2
2
|
|
|
3
|
+
/** Options for `action()`. Only `tag` is meaningful to the fake. */
|
|
4
|
+
export declare interface ActionOptions {
|
|
5
|
+
/** Request tag, recorded on the {@link CallRecord} (`null` = explicit suppression). */
|
|
6
|
+
tag?: string | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
export declare interface ActionResult {
|
|
4
10
|
transactionId: string;
|
|
5
11
|
}
|
|
@@ -51,6 +57,8 @@ export declare interface CallRecord {
|
|
|
51
57
|
result?: unknown;
|
|
52
58
|
/** Thrown or rejected value, set if the call failed. */
|
|
53
59
|
error?: unknown;
|
|
60
|
+
/** Request tag from the call's options, when one was passed (`null` = explicit suppression). */
|
|
61
|
+
tag?: string | null;
|
|
54
62
|
projectId: string;
|
|
55
63
|
dataset: string;
|
|
56
64
|
}
|
|
@@ -110,6 +118,37 @@ export declare interface CreateTestClientOptions {
|
|
|
110
118
|
* unconditionally — the legacy behavior.
|
|
111
119
|
*/
|
|
112
120
|
accessControl?: TestClientAccessControl;
|
|
121
|
+
/**
|
|
122
|
+
* Token→identity registry, shared by the whole `withConfig` family:
|
|
123
|
+
* multiple identities over one store, keyed by the token a handle is
|
|
124
|
+
* bound to — the token determines identity, exactly like production.
|
|
125
|
+
*
|
|
126
|
+
* ```ts
|
|
127
|
+
* const client = createTestClient({
|
|
128
|
+
* token: "t-admin",
|
|
129
|
+
* identities: {
|
|
130
|
+
* "t-admin": { user: { id: "admin", roles: [{ name: "administrator" }] }, grants: WILDCARD },
|
|
131
|
+
* "t-bob": { user: { id: "bob", roles: [{ name: "editor" }] }, grants: READ_ONLY },
|
|
132
|
+
* },
|
|
133
|
+
* });
|
|
134
|
+
* const bob = client.withConfig({ token: "t-bob" });
|
|
135
|
+
* // same store; bob's /users/me → bob; bob's ACL path → bob's grants
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* A token-bound handle behaves exactly like one configured with
|
|
139
|
+
* `accessControl` derived from its identity (identity serving + grant
|
|
140
|
+
* gating; the ACL path defaults per `(projectId, dataset)`). Register
|
|
141
|
+
* more identities later with `addIdentity`.
|
|
142
|
+
*/
|
|
143
|
+
identities?: Record<string, TestClientIdentity>;
|
|
144
|
+
/**
|
|
145
|
+
* Token the root handle is bound to — must name an entry in
|
|
146
|
+
* `identities` (unknown tokens throw). Omit to leave the root handle
|
|
147
|
+
* identity-free (a bare factory for token-bound siblings): its
|
|
148
|
+
* `request` is undefined and its reads/writes are ungated. Mutually
|
|
149
|
+
* exclusive with `accessControl`.
|
|
150
|
+
*/
|
|
151
|
+
token?: string;
|
|
113
152
|
/**
|
|
114
153
|
* Pluggable write interceptor — runs on each write through the document
|
|
115
154
|
* mutation API (create/patch/delete/transaction; not the release/version
|
|
@@ -290,8 +329,12 @@ export declare interface FetchOptions {
|
|
|
290
329
|
* raw response. Ignored when `filterResponse` is not `false`.
|
|
291
330
|
*/
|
|
292
331
|
returnQuery?: boolean;
|
|
293
|
-
/**
|
|
294
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Request tag, recorded on the {@link CallRecord} for assertions.
|
|
334
|
+
* `null` mirrors `@sanity/client`'s explicit-suppression form and is
|
|
335
|
+
* recorded as-is. No other behavior is simulated.
|
|
336
|
+
*/
|
|
337
|
+
tag?: string | null;
|
|
295
338
|
/** Accepted for signature parity; not simulated. */
|
|
296
339
|
signal?: AbortSignal;
|
|
297
340
|
}
|
|
@@ -398,6 +441,8 @@ export declare interface MutationLogEntry {
|
|
|
398
441
|
projectId: string;
|
|
399
442
|
dataset: string;
|
|
400
443
|
mutations: TxnMutation[];
|
|
444
|
+
/** Request tag the commit carried, when one was passed (`null` = explicit suppression). */
|
|
445
|
+
tag?: string | null;
|
|
401
446
|
}
|
|
402
447
|
|
|
403
448
|
/** The operation recorded per affected document, matching `@sanity/client`. */
|
|
@@ -425,8 +470,21 @@ export declare interface MutationOptions {
|
|
|
425
470
|
visibility?: "sync" | "async" | "deferred";
|
|
426
471
|
/** Accepted for signature parity; not simulated. */
|
|
427
472
|
autoGenerateArrayKeys?: boolean;
|
|
473
|
+
/**
|
|
474
|
+
* Request tag, recorded on the {@link CallRecord} and the
|
|
475
|
+
* {@link MutationLogEntry} for assertions. `null` mirrors
|
|
476
|
+
* `@sanity/client`'s explicit-suppression form and is recorded as-is.
|
|
477
|
+
* No other behavior is simulated.
|
|
478
|
+
*/
|
|
479
|
+
tag?: string | null;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** Options for `PatchHandle.commit()`. Only `tag` is meaningful to the fake. */
|
|
483
|
+
export declare interface PatchCommitOptions {
|
|
484
|
+
/** Request tag, recorded on the mutation log entry (`null` = explicit suppression). */
|
|
485
|
+
tag?: string | null;
|
|
428
486
|
/** Accepted for signature parity; not simulated. */
|
|
429
|
-
|
|
487
|
+
visibility?: "sync" | "async" | "deferred";
|
|
430
488
|
}
|
|
431
489
|
|
|
432
490
|
export declare interface PatchExecutor {
|
|
@@ -434,6 +492,7 @@ export declare interface PatchExecutor {
|
|
|
434
492
|
documentId: string,
|
|
435
493
|
ops: PatchOp[],
|
|
436
494
|
ifRev: string | undefined,
|
|
495
|
+
options: PatchCommitOptions | undefined,
|
|
437
496
|
) => Promise<SanityDocument>;
|
|
438
497
|
}
|
|
439
498
|
|
|
@@ -449,7 +508,7 @@ export declare interface PatchHandle {
|
|
|
449
508
|
items: unknown[],
|
|
450
509
|
) => PatchHandle;
|
|
451
510
|
ifRevisionId: (rev: string) => PatchHandle;
|
|
452
|
-
commit: () => Promise<SanityDocument>;
|
|
511
|
+
commit: (options?: PatchCommitOptions) => Promise<SanityDocument>;
|
|
453
512
|
/** Test-only introspection — not on real Patch */
|
|
454
513
|
toJSON: () => {
|
|
455
514
|
id: string;
|
|
@@ -659,7 +718,7 @@ export declare const SUPPORTED_SURFACE: readonly [
|
|
|
659
718
|
"releases",
|
|
660
719
|
"createVersion",
|
|
661
720
|
"unpublishVersion",
|
|
662
|
-
"request (only with accessControl)",
|
|
721
|
+
"request (only with accessControl or a bound token)",
|
|
663
722
|
"config",
|
|
664
723
|
"withConfig",
|
|
665
724
|
];
|
|
@@ -685,22 +744,18 @@ export declare interface TestClient {
|
|
|
685
744
|
options?: FetchOptions,
|
|
686
745
|
) => Promise<(T | null)[]>;
|
|
687
746
|
/**
|
|
688
|
-
* Sanity-style HTTP request hook. Present iff the
|
|
689
|
-
* created with `accessControl
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
* Test cases that want to exercise the engine's token-resolution
|
|
695
|
-
* path should configure `accessControl`; cases that want bench-style
|
|
696
|
-
* always-allow should leave it off and pass `access` through the
|
|
697
|
-
* bench.
|
|
747
|
+
* Sanity-style HTTP request hook. Present iff the handle has an
|
|
748
|
+
* acting identity — created with `accessControl`, or bound to a
|
|
749
|
+
* registered token (`identities` + `token`). The engine's access
|
|
750
|
+
* resolver pokes `/users/me` and the configured ACL path through
|
|
751
|
+
* this — without it the engine throws on any verb that needs an
|
|
752
|
+
* actor.
|
|
698
753
|
*/
|
|
699
754
|
request?: <T>(opts: {
|
|
700
755
|
url?: string;
|
|
701
756
|
uri?: string;
|
|
702
757
|
signal?: AbortSignal;
|
|
703
|
-
tag?: string;
|
|
758
|
+
tag?: string | null;
|
|
704
759
|
}) => Promise<T>;
|
|
705
760
|
create: <T extends SanityDocumentLike>(
|
|
706
761
|
doc: T,
|
|
@@ -738,7 +793,10 @@ export declare interface TestClient {
|
|
|
738
793
|
* Returns a synthetic `transactionId`. Multi-action arrays are applied
|
|
739
794
|
* atomically — any failure rolls back the batch.
|
|
740
795
|
*/
|
|
741
|
-
action: (
|
|
796
|
+
action: (
|
|
797
|
+
action: Action | Action[],
|
|
798
|
+
options?: ActionOptions,
|
|
799
|
+
) => Promise<ActionResult>;
|
|
742
800
|
/** Helpers mirroring `@sanity/client`'s `client.releases.*` surface. */
|
|
743
801
|
releases: ReleasesHandle;
|
|
744
802
|
/** Helper for `sanity.action.document.version.create`. */
|
|
@@ -752,11 +810,15 @@ export declare interface TestClient {
|
|
|
752
810
|
releaseId: string;
|
|
753
811
|
publishedId: string;
|
|
754
812
|
}) => Promise<ActionResult>;
|
|
755
|
-
/**
|
|
813
|
+
/**
|
|
814
|
+
* Current `(projectId, dataset)` and active perspective. `token` is
|
|
815
|
+
* present iff the handle is bound to a registered identity.
|
|
816
|
+
*/
|
|
756
817
|
config: () => {
|
|
757
818
|
projectId: string;
|
|
758
819
|
dataset: string;
|
|
759
820
|
perspective: Perspective;
|
|
821
|
+
token?: string;
|
|
760
822
|
};
|
|
761
823
|
/**
|
|
762
824
|
* Return a new client handle pointing at a different `(projectId, dataset)`
|
|
@@ -773,8 +835,18 @@ export declare interface TestClient {
|
|
|
773
835
|
* Rebind the acting identity (and its grants) on a sibling that shares
|
|
774
836
|
* the same store — the test analogue of a token-scoped client. Lets two
|
|
775
837
|
* actors write the same dataset, e.g. for `identity()`-based guards.
|
|
838
|
+
* Mutually exclusive with `token`.
|
|
776
839
|
*/
|
|
777
840
|
accessControl: TestClientAccessControl;
|
|
841
|
+
/**
|
|
842
|
+
* Bind the sibling to a token registered in the family's identity
|
|
843
|
+
* registry (`createTestClient({ identities })` / `addIdentity`): its
|
|
844
|
+
* `/users/me` serves that identity's user, its ACL path serves that
|
|
845
|
+
* identity's grants, and reads/writes are gated through them — the
|
|
846
|
+
* token determines identity, exactly like production. Unknown tokens
|
|
847
|
+
* throw. Mutually exclusive with `accessControl`.
|
|
848
|
+
*/
|
|
849
|
+
token: string;
|
|
778
850
|
}>,
|
|
779
851
|
) => TestClient;
|
|
780
852
|
/** Replace the current dataset with the given documents. */
|
|
@@ -791,9 +863,21 @@ export declare interface TestClient {
|
|
|
791
863
|
* Simulate `count` concurrent edits to `id` in the current dataset: the
|
|
792
864
|
* next `count` revision-guarded mutations (those passing `ifRevisionId`)
|
|
793
865
|
* to that document throw a 409-shaped conflict, as if another client
|
|
794
|
-
* committed between the caller's read and write.
|
|
795
|
-
*
|
|
796
|
-
*
|
|
866
|
+
* committed between the caller's read and write. Mutations that don't
|
|
867
|
+
* pass `ifRevisionId` are unaffected.
|
|
868
|
+
*
|
|
869
|
+
* Reach for this when the code under test reads a document's `_rev` and
|
|
870
|
+
* commits with `ifRevisionId` internally — so you can't stage the race
|
|
871
|
+
* by mutating the document first (the code would just re-read the new
|
|
872
|
+
* revision and its commit would match). The counter is consumed one per
|
|
873
|
+
* guarded commit, so `count` lets a test drive a recover-after-N-losses
|
|
874
|
+
* path or exhaust a retry budget, deterministically and without relying
|
|
875
|
+
* on promise interleaving.
|
|
876
|
+
*
|
|
877
|
+
* Note: this forces the conflict *outcome* — it short-circuits the
|
|
878
|
+
* revision comparison rather than changing the stored `_rev`. The code
|
|
879
|
+
* under test sees the identical 409 error a real lost race produces; the
|
|
880
|
+
* stored revision is left untouched.
|
|
797
881
|
*/
|
|
798
882
|
simulateConcurrentEdit: (id: string, count?: number) => void;
|
|
799
883
|
/** List `(projectId, dataset)` pairs known in this registry. */
|
|
@@ -811,6 +895,16 @@ export declare interface TestClient {
|
|
|
811
895
|
stub: (name: string, handler: unknown) => void;
|
|
812
896
|
/** The currently-registered stubs, keyed by member name. */
|
|
813
897
|
stubs: () => Record<string, unknown>;
|
|
898
|
+
/**
|
|
899
|
+
* Register (or upsert) a token→identity entry in the family's shared
|
|
900
|
+
* registry. Siblings already bound to `token` resolve lazily, so a
|
|
901
|
+
* re-registration takes effect on them immediately. Registering an
|
|
902
|
+
* identity does not bind it — pass `token` at `createTestClient` or
|
|
903
|
+
* `withConfig({ token })` to act as it.
|
|
904
|
+
*/
|
|
905
|
+
addIdentity: (token: string, identity: TestClientIdentity) => void;
|
|
906
|
+
/** Snapshot of the registered token→identity entries. */
|
|
907
|
+
identities: () => Record<string, TestClientIdentity>;
|
|
814
908
|
/**
|
|
815
909
|
* Every mutation committed through this client's registry, flattened
|
|
816
910
|
* and in order — `create` / `createOrReplace` / `createIfNotExists` /
|
|
@@ -855,6 +949,20 @@ export declare interface TestClientAccessControl {
|
|
|
855
949
|
aclPath?: string;
|
|
856
950
|
}
|
|
857
951
|
|
|
952
|
+
/**
|
|
953
|
+
* One entry in the token→identity registry (`createTestClient({ identities })`).
|
|
954
|
+
* A handle bound to the entry's token behaves exactly like one configured
|
|
955
|
+
* with `accessControl: { currentUser: user, grants }` — `/users/me` serves
|
|
956
|
+
* `user`, the default ACL path serves `grants`, and reads/writes are gated
|
|
957
|
+
* through them.
|
|
958
|
+
*/
|
|
959
|
+
export declare interface TestClientIdentity {
|
|
960
|
+
/** What `/users/me` returns for clients bound to this token. */
|
|
961
|
+
user: CurrentUser;
|
|
962
|
+
/** Grants served at the ACL path and enforced on reads/writes. */
|
|
963
|
+
grants: Grant[];
|
|
964
|
+
}
|
|
965
|
+
|
|
858
966
|
/**
|
|
859
967
|
* In-memory document store backing a TestClient. Closure-scoped — created
|
|
860
968
|
* fresh per test, no global state.
|
|
@@ -910,6 +1018,12 @@ export declare interface TxnCommitOptions {
|
|
|
910
1018
|
returnFirst?: boolean;
|
|
911
1019
|
/** Override the synthetic transaction id (mirrors `client.transaction(undefined, { transactionId })`). */
|
|
912
1020
|
transactionId?: string;
|
|
1021
|
+
/**
|
|
1022
|
+
* Request tag, recorded on the mutation log entry for assertions.
|
|
1023
|
+
* `null` mirrors `@sanity/client`'s explicit-suppression form and is
|
|
1024
|
+
* recorded as-is. No other behavior is simulated.
|
|
1025
|
+
*/
|
|
1026
|
+
tag?: string | null;
|
|
913
1027
|
}
|
|
914
1028
|
|
|
915
1029
|
export declare type TxnCommitResult =
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare type Action = ReleaseAction | VersionAction;
|
|
2
2
|
|
|
3
|
+
/** Options for `action()`. Only `tag` is meaningful to the fake. */
|
|
4
|
+
export declare interface ActionOptions {
|
|
5
|
+
/** Request tag, recorded on the {@link CallRecord} (`null` = explicit suppression). */
|
|
6
|
+
tag?: string | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
export declare interface ActionResult {
|
|
4
10
|
transactionId: string;
|
|
5
11
|
}
|
|
@@ -51,6 +57,8 @@ export declare interface CallRecord {
|
|
|
51
57
|
result?: unknown;
|
|
52
58
|
/** Thrown or rejected value, set if the call failed. */
|
|
53
59
|
error?: unknown;
|
|
60
|
+
/** Request tag from the call's options, when one was passed (`null` = explicit suppression). */
|
|
61
|
+
tag?: string | null;
|
|
54
62
|
projectId: string;
|
|
55
63
|
dataset: string;
|
|
56
64
|
}
|
|
@@ -110,6 +118,37 @@ export declare interface CreateTestClientOptions {
|
|
|
110
118
|
* unconditionally — the legacy behavior.
|
|
111
119
|
*/
|
|
112
120
|
accessControl?: TestClientAccessControl;
|
|
121
|
+
/**
|
|
122
|
+
* Token→identity registry, shared by the whole `withConfig` family:
|
|
123
|
+
* multiple identities over one store, keyed by the token a handle is
|
|
124
|
+
* bound to — the token determines identity, exactly like production.
|
|
125
|
+
*
|
|
126
|
+
* ```ts
|
|
127
|
+
* const client = createTestClient({
|
|
128
|
+
* token: "t-admin",
|
|
129
|
+
* identities: {
|
|
130
|
+
* "t-admin": { user: { id: "admin", roles: [{ name: "administrator" }] }, grants: WILDCARD },
|
|
131
|
+
* "t-bob": { user: { id: "bob", roles: [{ name: "editor" }] }, grants: READ_ONLY },
|
|
132
|
+
* },
|
|
133
|
+
* });
|
|
134
|
+
* const bob = client.withConfig({ token: "t-bob" });
|
|
135
|
+
* // same store; bob's /users/me → bob; bob's ACL path → bob's grants
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* A token-bound handle behaves exactly like one configured with
|
|
139
|
+
* `accessControl` derived from its identity (identity serving + grant
|
|
140
|
+
* gating; the ACL path defaults per `(projectId, dataset)`). Register
|
|
141
|
+
* more identities later with `addIdentity`.
|
|
142
|
+
*/
|
|
143
|
+
identities?: Record<string, TestClientIdentity>;
|
|
144
|
+
/**
|
|
145
|
+
* Token the root handle is bound to — must name an entry in
|
|
146
|
+
* `identities` (unknown tokens throw). Omit to leave the root handle
|
|
147
|
+
* identity-free (a bare factory for token-bound siblings): its
|
|
148
|
+
* `request` is undefined and its reads/writes are ungated. Mutually
|
|
149
|
+
* exclusive with `accessControl`.
|
|
150
|
+
*/
|
|
151
|
+
token?: string;
|
|
113
152
|
/**
|
|
114
153
|
* Pluggable write interceptor — runs on each write through the document
|
|
115
154
|
* mutation API (create/patch/delete/transaction; not the release/version
|
|
@@ -290,8 +329,12 @@ export declare interface FetchOptions {
|
|
|
290
329
|
* raw response. Ignored when `filterResponse` is not `false`.
|
|
291
330
|
*/
|
|
292
331
|
returnQuery?: boolean;
|
|
293
|
-
/**
|
|
294
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Request tag, recorded on the {@link CallRecord} for assertions.
|
|
334
|
+
* `null` mirrors `@sanity/client`'s explicit-suppression form and is
|
|
335
|
+
* recorded as-is. No other behavior is simulated.
|
|
336
|
+
*/
|
|
337
|
+
tag?: string | null;
|
|
295
338
|
/** Accepted for signature parity; not simulated. */
|
|
296
339
|
signal?: AbortSignal;
|
|
297
340
|
}
|
|
@@ -398,6 +441,8 @@ export declare interface MutationLogEntry {
|
|
|
398
441
|
projectId: string;
|
|
399
442
|
dataset: string;
|
|
400
443
|
mutations: TxnMutation[];
|
|
444
|
+
/** Request tag the commit carried, when one was passed (`null` = explicit suppression). */
|
|
445
|
+
tag?: string | null;
|
|
401
446
|
}
|
|
402
447
|
|
|
403
448
|
/** The operation recorded per affected document, matching `@sanity/client`. */
|
|
@@ -425,8 +470,21 @@ export declare interface MutationOptions {
|
|
|
425
470
|
visibility?: "sync" | "async" | "deferred";
|
|
426
471
|
/** Accepted for signature parity; not simulated. */
|
|
427
472
|
autoGenerateArrayKeys?: boolean;
|
|
473
|
+
/**
|
|
474
|
+
* Request tag, recorded on the {@link CallRecord} and the
|
|
475
|
+
* {@link MutationLogEntry} for assertions. `null` mirrors
|
|
476
|
+
* `@sanity/client`'s explicit-suppression form and is recorded as-is.
|
|
477
|
+
* No other behavior is simulated.
|
|
478
|
+
*/
|
|
479
|
+
tag?: string | null;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** Options for `PatchHandle.commit()`. Only `tag` is meaningful to the fake. */
|
|
483
|
+
export declare interface PatchCommitOptions {
|
|
484
|
+
/** Request tag, recorded on the mutation log entry (`null` = explicit suppression). */
|
|
485
|
+
tag?: string | null;
|
|
428
486
|
/** Accepted for signature parity; not simulated. */
|
|
429
|
-
|
|
487
|
+
visibility?: "sync" | "async" | "deferred";
|
|
430
488
|
}
|
|
431
489
|
|
|
432
490
|
export declare interface PatchExecutor {
|
|
@@ -434,6 +492,7 @@ export declare interface PatchExecutor {
|
|
|
434
492
|
documentId: string,
|
|
435
493
|
ops: PatchOp[],
|
|
436
494
|
ifRev: string | undefined,
|
|
495
|
+
options: PatchCommitOptions | undefined,
|
|
437
496
|
) => Promise<SanityDocument>;
|
|
438
497
|
}
|
|
439
498
|
|
|
@@ -449,7 +508,7 @@ export declare interface PatchHandle {
|
|
|
449
508
|
items: unknown[],
|
|
450
509
|
) => PatchHandle;
|
|
451
510
|
ifRevisionId: (rev: string) => PatchHandle;
|
|
452
|
-
commit: () => Promise<SanityDocument>;
|
|
511
|
+
commit: (options?: PatchCommitOptions) => Promise<SanityDocument>;
|
|
453
512
|
/** Test-only introspection — not on real Patch */
|
|
454
513
|
toJSON: () => {
|
|
455
514
|
id: string;
|
|
@@ -659,7 +718,7 @@ export declare const SUPPORTED_SURFACE: readonly [
|
|
|
659
718
|
"releases",
|
|
660
719
|
"createVersion",
|
|
661
720
|
"unpublishVersion",
|
|
662
|
-
"request (only with accessControl)",
|
|
721
|
+
"request (only with accessControl or a bound token)",
|
|
663
722
|
"config",
|
|
664
723
|
"withConfig",
|
|
665
724
|
];
|
|
@@ -685,22 +744,18 @@ export declare interface TestClient {
|
|
|
685
744
|
options?: FetchOptions,
|
|
686
745
|
) => Promise<(T | null)[]>;
|
|
687
746
|
/**
|
|
688
|
-
* Sanity-style HTTP request hook. Present iff the
|
|
689
|
-
* created with `accessControl
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
* Test cases that want to exercise the engine's token-resolution
|
|
695
|
-
* path should configure `accessControl`; cases that want bench-style
|
|
696
|
-
* always-allow should leave it off and pass `access` through the
|
|
697
|
-
* bench.
|
|
747
|
+
* Sanity-style HTTP request hook. Present iff the handle has an
|
|
748
|
+
* acting identity — created with `accessControl`, or bound to a
|
|
749
|
+
* registered token (`identities` + `token`). The engine's access
|
|
750
|
+
* resolver pokes `/users/me` and the configured ACL path through
|
|
751
|
+
* this — without it the engine throws on any verb that needs an
|
|
752
|
+
* actor.
|
|
698
753
|
*/
|
|
699
754
|
request?: <T>(opts: {
|
|
700
755
|
url?: string;
|
|
701
756
|
uri?: string;
|
|
702
757
|
signal?: AbortSignal;
|
|
703
|
-
tag?: string;
|
|
758
|
+
tag?: string | null;
|
|
704
759
|
}) => Promise<T>;
|
|
705
760
|
create: <T extends SanityDocumentLike>(
|
|
706
761
|
doc: T,
|
|
@@ -738,7 +793,10 @@ export declare interface TestClient {
|
|
|
738
793
|
* Returns a synthetic `transactionId`. Multi-action arrays are applied
|
|
739
794
|
* atomically — any failure rolls back the batch.
|
|
740
795
|
*/
|
|
741
|
-
action: (
|
|
796
|
+
action: (
|
|
797
|
+
action: Action | Action[],
|
|
798
|
+
options?: ActionOptions,
|
|
799
|
+
) => Promise<ActionResult>;
|
|
742
800
|
/** Helpers mirroring `@sanity/client`'s `client.releases.*` surface. */
|
|
743
801
|
releases: ReleasesHandle;
|
|
744
802
|
/** Helper for `sanity.action.document.version.create`. */
|
|
@@ -752,11 +810,15 @@ export declare interface TestClient {
|
|
|
752
810
|
releaseId: string;
|
|
753
811
|
publishedId: string;
|
|
754
812
|
}) => Promise<ActionResult>;
|
|
755
|
-
/**
|
|
813
|
+
/**
|
|
814
|
+
* Current `(projectId, dataset)` and active perspective. `token` is
|
|
815
|
+
* present iff the handle is bound to a registered identity.
|
|
816
|
+
*/
|
|
756
817
|
config: () => {
|
|
757
818
|
projectId: string;
|
|
758
819
|
dataset: string;
|
|
759
820
|
perspective: Perspective;
|
|
821
|
+
token?: string;
|
|
760
822
|
};
|
|
761
823
|
/**
|
|
762
824
|
* Return a new client handle pointing at a different `(projectId, dataset)`
|
|
@@ -773,8 +835,18 @@ export declare interface TestClient {
|
|
|
773
835
|
* Rebind the acting identity (and its grants) on a sibling that shares
|
|
774
836
|
* the same store — the test analogue of a token-scoped client. Lets two
|
|
775
837
|
* actors write the same dataset, e.g. for `identity()`-based guards.
|
|
838
|
+
* Mutually exclusive with `token`.
|
|
776
839
|
*/
|
|
777
840
|
accessControl: TestClientAccessControl;
|
|
841
|
+
/**
|
|
842
|
+
* Bind the sibling to a token registered in the family's identity
|
|
843
|
+
* registry (`createTestClient({ identities })` / `addIdentity`): its
|
|
844
|
+
* `/users/me` serves that identity's user, its ACL path serves that
|
|
845
|
+
* identity's grants, and reads/writes are gated through them — the
|
|
846
|
+
* token determines identity, exactly like production. Unknown tokens
|
|
847
|
+
* throw. Mutually exclusive with `accessControl`.
|
|
848
|
+
*/
|
|
849
|
+
token: string;
|
|
778
850
|
}>,
|
|
779
851
|
) => TestClient;
|
|
780
852
|
/** Replace the current dataset with the given documents. */
|
|
@@ -791,9 +863,21 @@ export declare interface TestClient {
|
|
|
791
863
|
* Simulate `count` concurrent edits to `id` in the current dataset: the
|
|
792
864
|
* next `count` revision-guarded mutations (those passing `ifRevisionId`)
|
|
793
865
|
* to that document throw a 409-shaped conflict, as if another client
|
|
794
|
-
* committed between the caller's read and write.
|
|
795
|
-
*
|
|
796
|
-
*
|
|
866
|
+
* committed between the caller's read and write. Mutations that don't
|
|
867
|
+
* pass `ifRevisionId` are unaffected.
|
|
868
|
+
*
|
|
869
|
+
* Reach for this when the code under test reads a document's `_rev` and
|
|
870
|
+
* commits with `ifRevisionId` internally — so you can't stage the race
|
|
871
|
+
* by mutating the document first (the code would just re-read the new
|
|
872
|
+
* revision and its commit would match). The counter is consumed one per
|
|
873
|
+
* guarded commit, so `count` lets a test drive a recover-after-N-losses
|
|
874
|
+
* path or exhaust a retry budget, deterministically and without relying
|
|
875
|
+
* on promise interleaving.
|
|
876
|
+
*
|
|
877
|
+
* Note: this forces the conflict *outcome* — it short-circuits the
|
|
878
|
+
* revision comparison rather than changing the stored `_rev`. The code
|
|
879
|
+
* under test sees the identical 409 error a real lost race produces; the
|
|
880
|
+
* stored revision is left untouched.
|
|
797
881
|
*/
|
|
798
882
|
simulateConcurrentEdit: (id: string, count?: number) => void;
|
|
799
883
|
/** List `(projectId, dataset)` pairs known in this registry. */
|
|
@@ -811,6 +895,16 @@ export declare interface TestClient {
|
|
|
811
895
|
stub: (name: string, handler: unknown) => void;
|
|
812
896
|
/** The currently-registered stubs, keyed by member name. */
|
|
813
897
|
stubs: () => Record<string, unknown>;
|
|
898
|
+
/**
|
|
899
|
+
* Register (or upsert) a token→identity entry in the family's shared
|
|
900
|
+
* registry. Siblings already bound to `token` resolve lazily, so a
|
|
901
|
+
* re-registration takes effect on them immediately. Registering an
|
|
902
|
+
* identity does not bind it — pass `token` at `createTestClient` or
|
|
903
|
+
* `withConfig({ token })` to act as it.
|
|
904
|
+
*/
|
|
905
|
+
addIdentity: (token: string, identity: TestClientIdentity) => void;
|
|
906
|
+
/** Snapshot of the registered token→identity entries. */
|
|
907
|
+
identities: () => Record<string, TestClientIdentity>;
|
|
814
908
|
/**
|
|
815
909
|
* Every mutation committed through this client's registry, flattened
|
|
816
910
|
* and in order — `create` / `createOrReplace` / `createIfNotExists` /
|
|
@@ -855,6 +949,20 @@ export declare interface TestClientAccessControl {
|
|
|
855
949
|
aclPath?: string;
|
|
856
950
|
}
|
|
857
951
|
|
|
952
|
+
/**
|
|
953
|
+
* One entry in the token→identity registry (`createTestClient({ identities })`).
|
|
954
|
+
* A handle bound to the entry's token behaves exactly like one configured
|
|
955
|
+
* with `accessControl: { currentUser: user, grants }` — `/users/me` serves
|
|
956
|
+
* `user`, the default ACL path serves `grants`, and reads/writes are gated
|
|
957
|
+
* through them.
|
|
958
|
+
*/
|
|
959
|
+
export declare interface TestClientIdentity {
|
|
960
|
+
/** What `/users/me` returns for clients bound to this token. */
|
|
961
|
+
user: CurrentUser;
|
|
962
|
+
/** Grants served at the ACL path and enforced on reads/writes. */
|
|
963
|
+
grants: Grant[];
|
|
964
|
+
}
|
|
965
|
+
|
|
858
966
|
/**
|
|
859
967
|
* In-memory document store backing a TestClient. Closure-scoped — created
|
|
860
968
|
* fresh per test, no global state.
|
|
@@ -910,6 +1018,12 @@ export declare interface TxnCommitOptions {
|
|
|
910
1018
|
returnFirst?: boolean;
|
|
911
1019
|
/** Override the synthetic transaction id (mirrors `client.transaction(undefined, { transactionId })`). */
|
|
912
1020
|
transactionId?: string;
|
|
1021
|
+
/**
|
|
1022
|
+
* Request tag, recorded on the mutation log entry for assertions.
|
|
1023
|
+
* `null` mirrors `@sanity/client`'s explicit-suppression form and is
|
|
1024
|
+
* recorded as-is. No other behavior is simulated.
|
|
1025
|
+
*/
|
|
1026
|
+
tag?: string | null;
|
|
913
1027
|
}
|
|
914
1028
|
|
|
915
1029
|
export declare type TxnCommitResult =
|