@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/README.md
CHANGED
|
@@ -77,10 +77,10 @@ More patterns — passing the fake to code typed against `SanityClient`, asserti
|
|
|
77
77
|
| Transaction | `transaction()` chain → `.commit()` | Mutations: `.create`, `.createOrReplace`, `.createIfNotExists`, `.delete`, `.patch(id, recipe)`. Atomic — any throw rolls back the whole batch. Default result is a `MultipleMutationResult` (`{ transactionId, documentIds, results }`) |
|
|
78
78
|
| Actions | `action(action \| action[])` | Content Lake release / version actions, applied atomically |
|
|
79
79
|
| Releases | `releases.*`, `createVersion`, `unpublishVersion` | Content Releases lifecycle — create / edit / schedule / publish / archive / delete, plus version create / replace / discard / unpublish |
|
|
80
|
-
| Config | `config()` | Returns `{ projectId, dataset, perspective }` |
|
|
81
|
-
| Config | `withConfig({ projectId?, dataset?, perspective?, accessControl? })` | Sibling handle into the same multi-dataset registry; `
|
|
80
|
+
| Config | `config()` | Returns `{ projectId, dataset, perspective, token? }` |
|
|
81
|
+
| Config | `withConfig({ projectId?, dataset?, perspective?, accessControl?, token? })` | Sibling handle into the same multi-dataset registry; `token` binds a registered identity (see the token→identity registry below), `accessControl` rebinds it inline — the two are mutually exclusive |
|
|
82
82
|
| Perspectives | `raw` / `published` / `drafts` / release stacks | Per-client default or per-`fetch` override; draft/version layering with `_originalId` projection |
|
|
83
|
-
| HTTP | `request({ url? \| uri? })` | Present **only** when the
|
|
83
|
+
| HTTP | `request({ url? \| uri? })` | Present **only** when the handle has an acting identity (`accessControl`, or a bound `token`). Serves `/users/me` + the configured ACL path. Calling it without one throws a guidance error. |
|
|
84
84
|
|
|
85
85
|
#### Mutation return shapes
|
|
86
86
|
|
|
@@ -119,18 +119,24 @@ await expect(client.patch("article-1").set({ body: "no" }).commit()).rejects.toT
|
|
|
119
119
|
|
|
120
120
|
#### Identity is the token
|
|
121
121
|
|
|
122
|
-
`identity` is the id
|
|
122
|
+
`identity` is the id of the handle's acting identity. To act as different users against the **same** store (e.g. to exercise `identity()`-based guards), register a token→identity registry and mint token-scoped siblings — the token determines identity, exactly like production:
|
|
123
123
|
|
|
124
124
|
```ts
|
|
125
125
|
const alice = createTestClient({
|
|
126
126
|
documents: [article],
|
|
127
|
-
|
|
127
|
+
token: "t-alice",
|
|
128
|
+
identities: {
|
|
129
|
+
"t-alice": { user: { id: "alice" }, grants },
|
|
130
|
+
"t-bob": { user: { id: "bob" }, grants },
|
|
131
|
+
},
|
|
128
132
|
mutationGuard: onlyTheAssignedReviewerMayWrite,
|
|
129
133
|
});
|
|
130
|
-
const bob = alice.withConfig({
|
|
134
|
+
const bob = alice.withConfig({ token: "t-bob" });
|
|
131
135
|
// alice and bob share one store; each write carries its own identity().
|
|
132
136
|
```
|
|
133
137
|
|
|
138
|
+
Register more identities after creation with `client.addIdentity(token, { user, grants })` — an upsert into the family-shared registry; already-bound siblings resolve lazily and pick the change up immediately. For a one-off identity without a registry, `withConfig({ accessControl })` still works (mutually exclusive with `token` in the same call).
|
|
139
|
+
|
|
134
140
|
> [!NOTE]
|
|
135
141
|
> Treat `before` / `after` as read-only — they are the live store images, not copies; mutating them corrupts the store.
|
|
136
142
|
|
|
@@ -154,6 +160,8 @@ Useful in test bodies, but a giveaway that the code under test isn't using the r
|
|
|
154
160
|
| `client.clearCalls()` | Reset the call log |
|
|
155
161
|
| `client.stub(name, handler)` | Register a handler for any unimplemented `@sanity/client` member (see below) |
|
|
156
162
|
| `client.stubs()` | The currently-registered stubs |
|
|
163
|
+
| `client.addIdentity(token, identity)` | Register (upsert) a token→identity entry in the family-shared registry |
|
|
164
|
+
| `client.identities()` | Snapshot of the registered token→identity entries |
|
|
157
165
|
| `createTestClient({ datasets: { "p:d": [...] } })` | Seed multiple `(projectId, dataset)` stores up front |
|
|
158
166
|
|
|
159
167
|
#### Two layers of observability
|
|
@@ -212,7 +220,7 @@ Nested access reports the full path (`client.assets.upload(...)` → `client.ass
|
|
|
212
220
|
| `listen(...)` — real-time | ❌ Guarded | Test subscription logic separately, or `client.stub("listen", fn)` |
|
|
213
221
|
| `clone()` | ❌ Guarded | `withConfig({})` returns a sibling handle |
|
|
214
222
|
| `getUrl(uri)` / `getDataUrl(...)` — URL builders | ❌ Guarded | Hard-code in tests, or stub |
|
|
215
|
-
| `request(...)` — generic HTTP | ⚠️ Only with `accessControl`
|
|
223
|
+
| `request(...)` — generic HTTP | ⚠️ Only with an acting identity | Configure `accessControl` or bind a registered `token`, stub it, or use [MSW](https://mswjs.io) |
|
|
216
224
|
| `agent.*` / `assets.*` / `projects.*` / `users.*` / `live.*` / `mediaLibrary.*` | ❌ Guarded | Seed docs directly, or `client.stub(name, fn)` |
|
|
217
225
|
| `observable.*` — RxJS API | ❌ Guarded | Wrap the Promise API with `from()` if needed |
|
|
218
226
|
| Patch ops: `.diffMatchPatch`, `.merge`, `.append`, `.prepend`, `.replace` | ❌ Absent | `.insert("after", "field[-1]", items)` for append; `.insert("before", "field[0]", items)` for prepend |
|
package/dist/index.cjs
CHANGED
|
@@ -61,16 +61,15 @@ async function enforceWriteAccess(args) {
|
|
|
61
61
|
function defaultAclPath(projectId, dataset) {
|
|
62
62
|
return `/projects/${projectId}/datasets/${dataset}/acl`;
|
|
63
63
|
}
|
|
64
|
-
function makeRequestImpl(
|
|
65
|
-
const aclPath = accessControl.aclPath ?? defaultAclPath(projectId, dataset);
|
|
64
|
+
function makeRequestImpl(getAccess, projectId, dataset) {
|
|
66
65
|
return async (opts) => {
|
|
67
|
-
const path = opts.uri ?? opts.url;
|
|
66
|
+
const accessControl = getAccess(), aclPath = accessControl.aclPath ?? defaultAclPath(projectId, dataset), path = opts.uri ?? opts.url;
|
|
68
67
|
if (path === "/users/me")
|
|
69
68
|
return accessControl.currentUser;
|
|
70
69
|
if (path === aclPath)
|
|
71
70
|
return accessControl.grants;
|
|
72
71
|
throw fakeError(
|
|
73
|
-
`request(): no handler for path "${path}". With
|
|
72
|
+
`request(): no handler for path "${path}". With access control configured, the fake only serves "/users/me" and "${aclPath}". For any other endpoint, register a stub (client.stub("request", fn)) or intercept the HTTP call with a tool like MSW.`
|
|
74
73
|
);
|
|
75
74
|
};
|
|
76
75
|
}
|
|
@@ -139,7 +138,7 @@ function createPatch(documentId, executor) {
|
|
|
139
138
|
dec: (props) => (ops.push({ kind: "dec", props }), handle),
|
|
140
139
|
insert: (position, anchor, items) => (ops.push({ kind: "insert", position, anchor, items }), handle),
|
|
141
140
|
ifRevisionId: (rev) => (ifRev = rev, handle),
|
|
142
|
-
commit: () => executor.apply(documentId, ops, ifRev),
|
|
141
|
+
commit: (options) => executor.apply(documentId, ops, ifRev, options),
|
|
143
142
|
toJSON: () => ({
|
|
144
143
|
id: documentId,
|
|
145
144
|
ops: [...ops],
|
|
@@ -690,7 +689,7 @@ const ISSUES_URL = "https://github.com/sanity-labs/client-fake-for-test/issues",
|
|
|
690
689
|
"releases",
|
|
691
690
|
"createVersion",
|
|
692
691
|
"unpublishVersion",
|
|
693
|
-
"request (only with accessControl)",
|
|
692
|
+
"request (only with accessControl or a bound token)",
|
|
694
693
|
"config",
|
|
695
694
|
"withConfig"
|
|
696
695
|
], UNSUPPORTED_MEMBERS = {
|
|
@@ -739,8 +738,8 @@ const ISSUES_URL = "https://github.com/sanity-labs/client-fake-for-test/issues",
|
|
|
739
738
|
workaround: "Out of scope \u2014 seed documents directly or stub the call site."
|
|
740
739
|
},
|
|
741
740
|
request: {
|
|
742
|
-
what: "generic HTTP request hook \u2014 only wired up when
|
|
743
|
-
workaround: "Create the client with `accessControl` to enable request() (it serves /users/me + the ACL path)
|
|
741
|
+
what: "generic HTTP request hook \u2014 only wired up when the handle has an acting identity",
|
|
742
|
+
workaround: "Create the client with `accessControl`, or bind a registered token (`identities` + `token` / `withConfig({ token })`), to enable request() (it serves /users/me + the ACL path). Or register a stub."
|
|
744
743
|
},
|
|
745
744
|
dryRun: {
|
|
746
745
|
what: "not a method \u2014 did you mean the `{ dryRun }` mutation option?",
|
|
@@ -840,6 +839,25 @@ function wrapWithGuards(client, config) {
|
|
|
840
839
|
}
|
|
841
840
|
});
|
|
842
841
|
}
|
|
842
|
+
const OPTIONS_ARG_POSITION = {
|
|
843
|
+
fetch: 2,
|
|
844
|
+
getDocument: 1,
|
|
845
|
+
getDocuments: 1,
|
|
846
|
+
create: 1,
|
|
847
|
+
createOrReplace: 1,
|
|
848
|
+
createIfNotExists: 1,
|
|
849
|
+
delete: 1,
|
|
850
|
+
mutate: 1,
|
|
851
|
+
action: 1,
|
|
852
|
+
request: 0
|
|
853
|
+
};
|
|
854
|
+
function tagFromArgs(method, args) {
|
|
855
|
+
const position = OPTIONS_ARG_POSITION[method];
|
|
856
|
+
if (position === void 0) return;
|
|
857
|
+
const options = args[position];
|
|
858
|
+
if (!(typeof options != "object" || options === null || !("tag" in options)))
|
|
859
|
+
return options.tag;
|
|
860
|
+
}
|
|
843
861
|
const RECORDED_METHODS = /* @__PURE__ */ new Set([
|
|
844
862
|
"fetch",
|
|
845
863
|
"getDocument",
|
|
@@ -856,7 +874,21 @@ const RECORDED_METHODS = /* @__PURE__ */ new Set([
|
|
|
856
874
|
"createVersion",
|
|
857
875
|
"unpublishVersion",
|
|
858
876
|
"request"
|
|
859
|
-
]), DEFAULT_PROJECT_ID = "test", DEFAULT_DATASET = "test"
|
|
877
|
+
]), DEFAULT_PROJECT_ID = "test", DEFAULT_DATASET = "test";
|
|
878
|
+
function requireIdentity(backend, token, site) {
|
|
879
|
+
if (backend.identities.has(token)) return;
|
|
880
|
+
const known = [...backend.identities.keys()];
|
|
881
|
+
throw fakeError(
|
|
882
|
+
`${site}: unknown token "${token}" \u2014 no identity is registered for it. ` + (known.length > 0 ? `Registered tokens: ${known.map((t) => `"${t}"`).join(", ")}. ` : "No identities are registered. ") + "Register identities via createTestClient({ identities }) or client.addIdentity(token, identity)."
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
function rejectAmbiguousIdentity(site, config) {
|
|
886
|
+
if (config.token !== void 0 && config.accessControl !== void 0)
|
|
887
|
+
throw fakeError(
|
|
888
|
+
`${site}: \`token\` and \`accessControl\` are mutually exclusive \u2014 a handle acts either as a registered identity (token) or as an inline accessControl config, not both.`
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
const registryKey = (projectId, dataset) => `${projectId}:${dataset}`, parseRegistryKey = (key) => {
|
|
860
892
|
const [projectId, dataset] = key.split(":");
|
|
861
893
|
return { projectId, dataset };
|
|
862
894
|
};
|
|
@@ -866,8 +898,11 @@ function createTestClient(options = {}) {
|
|
|
866
898
|
log: [],
|
|
867
899
|
stubs: /* @__PURE__ */ new Map(),
|
|
868
900
|
calls: [],
|
|
869
|
-
concurrentEdits: /* @__PURE__ */ new Map()
|
|
870
|
-
|
|
901
|
+
concurrentEdits: /* @__PURE__ */ new Map(),
|
|
902
|
+
identities: new Map(Object.entries(options.identities ?? {}))
|
|
903
|
+
}, registry = backend.registry;
|
|
904
|
+
rejectAmbiguousIdentity("createTestClient()", options), options.token !== void 0 && requireIdentity(backend, options.token, "createTestClient()");
|
|
905
|
+
const ensureStore = (projectId, dataset) => {
|
|
871
906
|
const key = registryKey(projectId, dataset);
|
|
872
907
|
let store = registry.get(key);
|
|
873
908
|
return store === void 0 && (store = createTestStore(), registry.set(key, store)), store;
|
|
@@ -883,7 +918,8 @@ function createTestClient(options = {}) {
|
|
|
883
918
|
defaultDataset,
|
|
884
919
|
options.accessControl,
|
|
885
920
|
options.perspective ?? "raw",
|
|
886
|
-
options.mutationGuard
|
|
921
|
+
options.mutationGuard,
|
|
922
|
+
options.token
|
|
887
923
|
);
|
|
888
924
|
}
|
|
889
925
|
function dedupeIds(results) {
|
|
@@ -936,13 +972,30 @@ function toTxnMutations(operations) {
|
|
|
936
972
|
}
|
|
937
973
|
];
|
|
938
974
|
}
|
|
939
|
-
function makeHandle(backend, projectId, dataset, accessControl, perspective, mutationGuard) {
|
|
940
|
-
const { registry } = backend,
|
|
975
|
+
function makeHandle(backend, projectId, dataset, accessControl, perspective, mutationGuard, token) {
|
|
976
|
+
const { registry } = backend, activeAccess = () => {
|
|
977
|
+
if (token === void 0) return accessControl;
|
|
978
|
+
const identity = backend.identities.get(token);
|
|
979
|
+
if (identity === void 0)
|
|
980
|
+
throw fakeError(`no identity is registered for token "${token}" (was it removed?).`);
|
|
981
|
+
return { currentUser: identity.user, grants: identity.grants };
|
|
982
|
+
}, requireActiveAccess = () => {
|
|
983
|
+
const access = activeAccess();
|
|
984
|
+
if (access === void 0)
|
|
985
|
+
throw fakeError("this handle has no acting identity (no token or accessControl bound).");
|
|
986
|
+
return access;
|
|
987
|
+
}, store = (() => {
|
|
941
988
|
const key = registryKey(projectId, dataset);
|
|
942
989
|
let s = registry.get(key);
|
|
943
990
|
return s === void 0 && (s = createTestStore(), registry.set(key, s)), s;
|
|
944
|
-
})(), concurrentEditKey = (id) => registryKey(projectId, dataset) + " " + id, record = (transactionId, mutations) => {
|
|
945
|
-
backend.log.push({
|
|
991
|
+
})(), concurrentEditKey = (id) => registryKey(projectId, dataset) + " " + id, record = (transactionId, mutations, tag) => {
|
|
992
|
+
backend.log.push({
|
|
993
|
+
transactionId,
|
|
994
|
+
projectId,
|
|
995
|
+
dataset,
|
|
996
|
+
mutations,
|
|
997
|
+
...tag !== void 0 ? { tag } : {}
|
|
998
|
+
});
|
|
946
999
|
}, writeResult = (documents, results, transactionId, opts) => formatMutationResult(
|
|
947
1000
|
documents,
|
|
948
1001
|
results,
|
|
@@ -984,20 +1037,24 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
984
1037
|
`ifRevisionId check failed: document "${id}" is at _rev="${existing._rev}" but the mutation required _rev="${ifRevisionId}". The document changed since you read it \u2014 re-read it and retry. (This mirrors Sanity's optimistic-locking 409.)`
|
|
985
1038
|
);
|
|
986
1039
|
}, gate = async (document, permission) => {
|
|
987
|
-
|
|
1040
|
+
const access = activeAccess();
|
|
1041
|
+
access !== void 0 && await enforceWriteAccess({
|
|
988
1042
|
document,
|
|
989
|
-
grants:
|
|
1043
|
+
grants: access.grants,
|
|
990
1044
|
permission,
|
|
991
|
-
userId:
|
|
1045
|
+
userId: access.currentUser.id
|
|
992
1046
|
});
|
|
993
1047
|
}, guardGate = async (action, before, after) => {
|
|
994
|
-
mutationGuard !== void 0 && await mutationGuard({ action, before, after, identity:
|
|
995
|
-
}, canRead = async (document) =>
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1048
|
+
mutationGuard !== void 0 && await mutationGuard({ action, before, after, identity: activeAccess()?.currentUser.id });
|
|
1049
|
+
}, canRead = async (document) => {
|
|
1050
|
+
const access = activeAccess();
|
|
1051
|
+
return access === void 0 ? !0 : canAccess({
|
|
1052
|
+
document,
|
|
1053
|
+
grants: access.grants,
|
|
1054
|
+
permission: "read",
|
|
1055
|
+
userId: access.currentUser.id
|
|
1056
|
+
});
|
|
1057
|
+
}, applyPatch = async (documentId, ops, ifRev) => {
|
|
1001
1058
|
const existing = store.get(documentId);
|
|
1002
1059
|
if (existing === void 0)
|
|
1003
1060
|
throw fakeError(
|
|
@@ -1062,7 +1119,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1062
1119
|
doc === void 0 ? store.remove(id) : store.put(doc);
|
|
1063
1120
|
throw error;
|
|
1064
1121
|
}
|
|
1065
|
-
record(transactionId, mutations);
|
|
1122
|
+
record(transactionId, mutations, txnOptions?.tag);
|
|
1066
1123
|
const documents = touchedIds.map((id) => store.get(id)).filter((d) => d !== void 0);
|
|
1067
1124
|
return formatMutationResult(
|
|
1068
1125
|
documents,
|
|
@@ -1073,7 +1130,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1073
1130
|
);
|
|
1074
1131
|
}, accessVisibleDocs = async () => {
|
|
1075
1132
|
const all = store.all();
|
|
1076
|
-
if (
|
|
1133
|
+
if (activeAccess() === void 0) return all;
|
|
1077
1134
|
const out = [];
|
|
1078
1135
|
for (const doc of all)
|
|
1079
1136
|
await canRead(doc) && out.push(doc);
|
|
@@ -1123,7 +1180,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1123
1180
|
const promoted = promote2(doc);
|
|
1124
1181
|
await gate(promoted, "create"), await guardGate("create", null, promoted);
|
|
1125
1182
|
const transactionId = opts?.transactionId ?? cryptoRandomKey();
|
|
1126
|
-
return opts?.dryRun !== !0 && (store.put(promoted), record(transactionId, [{ kind: "create", doc }])), writeResult(
|
|
1183
|
+
return opts?.dryRun !== !0 && (store.put(promoted), record(transactionId, [{ kind: "create", doc }], opts?.tag)), writeResult(
|
|
1127
1184
|
[promoted],
|
|
1128
1185
|
[{ id: promoted._id, operation: "create" }],
|
|
1129
1186
|
transactionId,
|
|
@@ -1135,7 +1192,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1135
1192
|
const promoted = promote2(doc), existing = store.get(doc._id);
|
|
1136
1193
|
await gate(existing ?? promoted, existing !== void 0 ? "update" : "create"), await guardGate(existing !== void 0 ? "update" : "create", existing ?? null, promoted);
|
|
1137
1194
|
const transactionId = opts?.transactionId ?? cryptoRandomKey();
|
|
1138
|
-
opts?.dryRun !== !0 && (store.put(promoted), record(transactionId, [{ kind: "createOrReplace", doc }]));
|
|
1195
|
+
opts?.dryRun !== !0 && (store.put(promoted), record(transactionId, [{ kind: "createOrReplace", doc }], opts?.tag));
|
|
1139
1196
|
const operation = existing !== void 0 ? "update" : "create";
|
|
1140
1197
|
return writeResult(
|
|
1141
1198
|
[promoted],
|
|
@@ -1155,7 +1212,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1155
1212
|
opts
|
|
1156
1213
|
);
|
|
1157
1214
|
const promoted = promote2(doc);
|
|
1158
|
-
return await gate(promoted, "create"), await guardGate("create", null, promoted), opts?.dryRun !== !0 && (store.put(promoted), record(transactionId, [{ kind: "createIfNotExists", doc }])), writeResult(
|
|
1215
|
+
return await gate(promoted, "create"), await guardGate("create", null, promoted), opts?.dryRun !== !0 && (store.put(promoted), record(transactionId, [{ kind: "createIfNotExists", doc }], opts?.tag)), writeResult(
|
|
1159
1216
|
[promoted],
|
|
1160
1217
|
[{ id: promoted._id, operation: "create" }],
|
|
1161
1218
|
transactionId,
|
|
@@ -1168,7 +1225,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1168
1225
|
existing !== void 0 && await gate(existing, "update"), existing !== void 0 && await guardGate("delete", existing, null);
|
|
1169
1226
|
const transactionId = opts?.transactionId ?? cryptoRandomKey();
|
|
1170
1227
|
let removed = existing;
|
|
1171
|
-
return opts?.dryRun !== !0 && (removed = store.remove(id), removed !== void 0 && record(transactionId, [{ kind: "delete", id }])), opts?.returnDocuments === void 0 && opts?.returnFirst === void 0 ? removed !== void 0 ? { _id: id } : null : formatMutationResult(
|
|
1228
|
+
return opts?.dryRun !== !0 && (removed = store.remove(id), removed !== void 0 && record(transactionId, [{ kind: "delete", id }], opts?.tag)), opts?.returnDocuments === void 0 && opts?.returnFirst === void 0 ? removed !== void 0 ? { _id: id } : null : formatMutationResult(
|
|
1172
1229
|
removed !== void 0 ? [removed] : [],
|
|
1173
1230
|
removed !== void 0 ? [{ id, operation: "delete" }] : [],
|
|
1174
1231
|
transactionId,
|
|
@@ -1177,11 +1234,13 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1177
1234
|
);
|
|
1178
1235
|
},
|
|
1179
1236
|
patch: (documentId) => createPatch(documentId, {
|
|
1180
|
-
apply: async (docId, ops, ifRev) => {
|
|
1237
|
+
apply: async (docId, ops, ifRev, options) => {
|
|
1181
1238
|
const doc = await applyPatch(docId, ops, ifRev);
|
|
1182
|
-
return record(
|
|
1183
|
-
|
|
1184
|
-
|
|
1239
|
+
return record(
|
|
1240
|
+
cryptoRandomKey(),
|
|
1241
|
+
[{ kind: "patch", documentId: docId, ops, ...ifRev !== void 0 ? { ifRev } : {} }],
|
|
1242
|
+
options?.tag
|
|
1243
|
+
), doc;
|
|
1185
1244
|
}
|
|
1186
1245
|
}),
|
|
1187
1246
|
transaction: () => createTransaction({ apply: applyTransaction }),
|
|
@@ -1228,16 +1287,21 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1228
1287
|
versionId: `versions.${releaseId}.${publishedId}`,
|
|
1229
1288
|
publishedId
|
|
1230
1289
|
}),
|
|
1231
|
-
...accessControl !== void 0 ? { request: makeRequestImpl(
|
|
1232
|
-
config: () => ({ projectId, dataset, perspective }),
|
|
1233
|
-
withConfig: (config) =>
|
|
1234
|
-
backend,
|
|
1235
|
-
config.
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1290
|
+
...token !== void 0 || accessControl !== void 0 ? { request: makeRequestImpl(requireActiveAccess, projectId, dataset) } : {},
|
|
1291
|
+
config: () => ({ projectId, dataset, perspective, ...token !== void 0 ? { token } : {} }),
|
|
1292
|
+
withConfig: (config) => {
|
|
1293
|
+
rejectAmbiguousIdentity("withConfig()", config), config.token !== void 0 && requireIdentity(backend, config.token, "withConfig()");
|
|
1294
|
+
const rebinds = config.token !== void 0 || config.accessControl !== void 0;
|
|
1295
|
+
return makeHandle(
|
|
1296
|
+
backend,
|
|
1297
|
+
config.projectId ?? projectId,
|
|
1298
|
+
config.dataset ?? dataset,
|
|
1299
|
+
rebinds ? config.accessControl : accessControl,
|
|
1300
|
+
config.perspective ?? perspective,
|
|
1301
|
+
mutationGuard,
|
|
1302
|
+
rebinds ? config.token : token
|
|
1303
|
+
);
|
|
1304
|
+
},
|
|
1241
1305
|
seed: (documents) => store.seed(documents),
|
|
1242
1306
|
add: (documents) => store.add(documents),
|
|
1243
1307
|
reset: () => store.reset(),
|
|
@@ -1252,6 +1316,10 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1252
1316
|
backend.stubs.set(name, handler);
|
|
1253
1317
|
},
|
|
1254
1318
|
stubs: () => Object.fromEntries(backend.stubs),
|
|
1319
|
+
addIdentity: (identityToken, identity) => {
|
|
1320
|
+
backend.identities.set(identityToken, identity);
|
|
1321
|
+
},
|
|
1322
|
+
identities: () => Object.fromEntries(backend.identities),
|
|
1255
1323
|
mutations: () => backend.log.flatMap((entry) => entry.mutations),
|
|
1256
1324
|
transactions: () => [...backend.log],
|
|
1257
1325
|
clearLog: () => {
|
|
@@ -1266,8 +1334,8 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1266
1334
|
return wrapWithGuards(client, {
|
|
1267
1335
|
getStub: (name) => backend.stubs.get(name),
|
|
1268
1336
|
onCall: (method, args) => {
|
|
1269
|
-
const rec = { method, args, projectId, dataset };
|
|
1270
|
-
return backend.calls.push(rec), (outcome) => {
|
|
1337
|
+
const rec = { method, args, projectId, dataset }, tag = tagFromArgs(method, args);
|
|
1338
|
+
return tag !== void 0 && (rec.tag = tag), backend.calls.push(rec), (outcome) => {
|
|
1271
1339
|
"error" in outcome ? rec.error = outcome.error : rec.result = outcome.result;
|
|
1272
1340
|
};
|
|
1273
1341
|
},
|