@sanity-labs/client-fake-for-test 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -5
- package/dist/index.cjs +280 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -24
- package/dist/index.d.ts +122 -24
- package/dist/index.js +280 -163
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -59,69 +59,65 @@ async function enforceWriteAccess(args) {
|
|
|
59
59
|
function defaultAclPath(projectId, dataset) {
|
|
60
60
|
return `/projects/${projectId}/datasets/${dataset}/acl`;
|
|
61
61
|
}
|
|
62
|
-
function
|
|
62
|
+
function projectUserOf(identity, projectId) {
|
|
63
|
+
return identity.usersByProject?.[projectId] ?? identity.user;
|
|
64
|
+
}
|
|
65
|
+
function serveProjectUsers(args) {
|
|
66
|
+
const { requestedIds, pathProjectId, identities } = args, toRecord = (identity) => {
|
|
67
|
+
const projectUser = projectUserOf(identity, pathProjectId);
|
|
68
|
+
return {
|
|
69
|
+
id: projectUser.id,
|
|
70
|
+
sanityUserId: identity.globalUser?.id ?? projectUser.id,
|
|
71
|
+
displayName: projectUser.name ?? projectUser.id,
|
|
72
|
+
...projectUser.email !== void 0 ? { email: projectUser.email } : {}
|
|
73
|
+
};
|
|
74
|
+
}, byId = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const identity of identities) {
|
|
76
|
+
const resolvedId = projectUserOf(identity, pathProjectId).id, existing = byId.get(resolvedId);
|
|
77
|
+
if (existing !== void 0 && JSON.stringify(toRecord(existing)) !== JSON.stringify(toRecord(identity)))
|
|
78
|
+
throw fakeError(
|
|
79
|
+
`request(): two registered identities resolve to project user "${resolvedId}" in project "${pathProjectId}" with different records. The directory cannot answer unambiguously \u2014 fix the identity registrations so duplicate ids carry identical records.`
|
|
80
|
+
);
|
|
81
|
+
byId.set(resolvedId, identity);
|
|
82
|
+
}
|
|
83
|
+
const ids = requestedIds.split(","), [firstId] = ids;
|
|
84
|
+
if (ids.length === 1 && firstId !== void 0) {
|
|
85
|
+
const identity = byId.get(firstId);
|
|
86
|
+
if (identity === void 0)
|
|
87
|
+
throw Object.assign(
|
|
88
|
+
fakeError(
|
|
89
|
+
`request(): no registered identity resolves to project user "${firstId}" in project "${pathProjectId}". Register it via createTestClient({ identities }) or addIdentity().`
|
|
90
|
+
),
|
|
91
|
+
{ statusCode: 404 }
|
|
92
|
+
);
|
|
93
|
+
return toRecord(identity);
|
|
94
|
+
}
|
|
95
|
+
return ids.flatMap((id) => {
|
|
96
|
+
const identity = byId.get(id);
|
|
97
|
+
return identity === void 0 ? [] : [toRecord(identity)];
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const PROJECT_USERS_PATH = /^\/projects\/([^/]+)\/users\/([^/]+)$/;
|
|
101
|
+
function makeRequestImpl(ctx) {
|
|
102
|
+
const { getAccess, projectId, dataset, useProjectHostname, listIdentities } = ctx;
|
|
63
103
|
return async (opts) => {
|
|
64
104
|
const accessControl = getAccess(), aclPath = accessControl.aclPath ?? defaultAclPath(projectId, dataset), path = opts.uri ?? opts.url;
|
|
65
105
|
if (path === "/users/me")
|
|
66
|
-
return accessControl.currentUser;
|
|
106
|
+
return useProjectHostname ? accessControl.currentUser : accessControl.globalUser ?? accessControl.currentUser;
|
|
67
107
|
if (path === aclPath)
|
|
68
108
|
return accessControl.grants;
|
|
109
|
+
const projectUsers = path?.match(PROJECT_USERS_PATH), [, pathProjectId, requestedIds] = projectUsers ?? [];
|
|
110
|
+
if (pathProjectId !== void 0 && requestedIds !== void 0)
|
|
111
|
+
return serveProjectUsers({
|
|
112
|
+
requestedIds,
|
|
113
|
+
pathProjectId,
|
|
114
|
+
identities: listIdentities()
|
|
115
|
+
});
|
|
69
116
|
throw fakeError(
|
|
70
|
-
`request(): no handler for path "${path}". With access control configured, the fake
|
|
117
|
+
`request(): no handler for path "${path}". With access control configured, the fake serves "/users/me", "${aclPath}", and "/projects/<projectId>/users/<ids>". For any other endpoint, register a stub (client.stub("request", fn)) or intercept the HTTP call with a tool like MSW.`
|
|
71
118
|
);
|
|
72
119
|
};
|
|
73
120
|
}
|
|
74
|
-
function createTestStore(initial) {
|
|
75
|
-
const docs = /* @__PURE__ */ new Map(), promote2 = (doc) => {
|
|
76
|
-
if (typeof doc._id != "string" || doc._id.length === 0)
|
|
77
|
-
throw fakeError(
|
|
78
|
-
`Cannot seed/store a document without a non-empty string \`_id\` (got ${JSON.stringify(doc._id)}). This fake never auto-generates ids \u2014 give every document an explicit _id. Document: ${JSON.stringify(doc)}`
|
|
79
|
-
);
|
|
80
|
-
if (typeof doc._type != "string" || doc._type.length === 0)
|
|
81
|
-
throw fakeError(
|
|
82
|
-
`Document "${doc._id}" is missing a non-empty string \`_type\` (got ${JSON.stringify(doc._type)}). Every Sanity document needs a _type.`
|
|
83
|
-
);
|
|
84
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
85
|
-
return {
|
|
86
|
-
...doc,
|
|
87
|
-
_id: doc._id,
|
|
88
|
-
_type: doc._type,
|
|
89
|
-
_rev: doc._rev ?? cryptoRandomKey(),
|
|
90
|
-
_createdAt: doc._createdAt ?? now,
|
|
91
|
-
_updatedAt: doc._updatedAt ?? now
|
|
92
|
-
};
|
|
93
|
-
}, seed = (documents) => {
|
|
94
|
-
docs.clear();
|
|
95
|
-
for (const d of documents) {
|
|
96
|
-
const promoted = promote2(d);
|
|
97
|
-
docs.set(promoted._id, promoted);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
return initial && seed(initial), {
|
|
101
|
-
seed,
|
|
102
|
-
add: (documents) => {
|
|
103
|
-
for (const d of documents) {
|
|
104
|
-
const promoted = promote2(d);
|
|
105
|
-
docs.set(promoted._id, promoted);
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
reset: () => docs.clear(),
|
|
109
|
-
all: () => [...docs.values()],
|
|
110
|
-
get: (id) => docs.get(id),
|
|
111
|
-
put: (doc) => {
|
|
112
|
-
docs.set(doc._id, doc);
|
|
113
|
-
},
|
|
114
|
-
remove: (id) => {
|
|
115
|
-
const doc = docs.get(id);
|
|
116
|
-
return doc && docs.delete(id), doc;
|
|
117
|
-
},
|
|
118
|
-
snapshot: () => [...docs.values()]
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function cryptoRandomKey(length = 12) {
|
|
122
|
-
const bytes = new Uint8Array(length);
|
|
123
|
-
return globalThis.crypto.getRandomValues(bytes), [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
|
|
124
|
-
}
|
|
125
121
|
function describeType(value) {
|
|
126
122
|
return value === null ? "null" : Array.isArray(value) ? "an array" : `a ${typeof value}`;
|
|
127
123
|
}
|
|
@@ -324,13 +320,140 @@ function insertIntoArray(doc, position, anchor, items) {
|
|
|
324
320
|
const idx = resolveSelector(arr, last.selector);
|
|
325
321
|
position === "replace" ? arr.splice(idx, 1, ...items) : position === "before" ? arr.splice(idx, 0, ...items) : arr.splice(idx + 1, 0, ...items);
|
|
326
322
|
}
|
|
327
|
-
const
|
|
323
|
+
const DRAFTS_PREFIX = "drafts.", VERSIONS_PREFIX$1 = "versions.", RELEASE_DOC_PREFIX$1 = "_.releases.";
|
|
324
|
+
function publishedIdOf(id) {
|
|
325
|
+
if (id.startsWith(DRAFTS_PREFIX)) return id.slice(DRAFTS_PREFIX.length);
|
|
326
|
+
if (id.startsWith(VERSIONS_PREFIX$1)) {
|
|
327
|
+
const rest = id.slice(VERSIONS_PREFIX$1.length), dot = rest.indexOf(".");
|
|
328
|
+
return dot === -1 ? rest : rest.slice(dot + 1);
|
|
329
|
+
}
|
|
330
|
+
return id;
|
|
331
|
+
}
|
|
332
|
+
function releaseNameOf(id) {
|
|
333
|
+
if (!id.startsWith(VERSIONS_PREFIX$1)) return;
|
|
334
|
+
const rest = id.slice(VERSIONS_PREFIX$1.length), dot = rest.indexOf(".");
|
|
335
|
+
if (dot !== -1)
|
|
336
|
+
return rest.slice(0, dot);
|
|
337
|
+
}
|
|
338
|
+
const isDraftId = (id) => id.startsWith(DRAFTS_PREFIX), isVersionId = (id) => id.startsWith(VERSIONS_PREFIX$1), isSystemId = (id) => id.startsWith("_."), isReleaseDocId = (id) => id.startsWith(RELEASE_DOC_PREFIX$1);
|
|
339
|
+
function applyPerspective(docs, perspective) {
|
|
340
|
+
if (perspective === "raw") return [...docs];
|
|
341
|
+
if (perspective === "published")
|
|
342
|
+
return docs.filter((d) => !isDraftId(d._id) && !isVersionId(d._id) && !isSystemId(d._id));
|
|
343
|
+
if (perspective === "drafts")
|
|
344
|
+
return mergeStack(docs, [], { includeDrafts: !0, includeReleaseDocs: !1 });
|
|
345
|
+
const stack = [];
|
|
346
|
+
let includeDrafts = !1;
|
|
347
|
+
for (const entry of perspective)
|
|
348
|
+
entry === "drafts" ? includeDrafts = !0 : stack.push(entry);
|
|
349
|
+
return mergeStack(docs, stack, { includeDrafts, includeReleaseDocs: !0 });
|
|
350
|
+
}
|
|
351
|
+
function mergeStack(docs, releaseStack, options) {
|
|
352
|
+
const groups = /* @__PURE__ */ new Map(), systemReleases = [];
|
|
353
|
+
for (const doc of docs) {
|
|
354
|
+
if (isReleaseDocId(doc._id)) {
|
|
355
|
+
options.includeReleaseDocs && systemReleases.push(doc);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (isSystemId(doc._id)) continue;
|
|
359
|
+
const pubId = publishedIdOf(doc._id);
|
|
360
|
+
let group = groups.get(pubId);
|
|
361
|
+
if (group === void 0 && (group = { versions: /* @__PURE__ */ new Map() }, groups.set(pubId, group)), isDraftId(doc._id))
|
|
362
|
+
group.draft = doc;
|
|
363
|
+
else if (isVersionId(doc._id)) {
|
|
364
|
+
const releaseName = releaseNameOf(doc._id);
|
|
365
|
+
releaseName !== void 0 && group.versions.set(releaseName, doc);
|
|
366
|
+
} else
|
|
367
|
+
group.published = doc;
|
|
368
|
+
}
|
|
369
|
+
const out = [];
|
|
370
|
+
for (const [pubId, group] of groups) {
|
|
371
|
+
let chosen;
|
|
372
|
+
for (const releaseName of releaseStack) {
|
|
373
|
+
const v = group.versions.get(releaseName);
|
|
374
|
+
if (v !== void 0) {
|
|
375
|
+
if (isUnpublishStub(v)) {
|
|
376
|
+
chosen = void 0;
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
chosen = v;
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
chosen === void 0 && options.includeDrafts && group.draft !== void 0 && (chosen = group.draft), chosen === void 0 && group.published !== void 0 && (releaseStack.some((name) => {
|
|
384
|
+
const v = group.versions.get(name);
|
|
385
|
+
return v !== void 0 && isUnpublishStub(v);
|
|
386
|
+
}) || (chosen = group.published)), chosen !== void 0 && out.push(projectToPublishedId(chosen, pubId));
|
|
387
|
+
}
|
|
388
|
+
return [...out, ...systemReleases];
|
|
389
|
+
}
|
|
390
|
+
function isUnpublishStub(doc) {
|
|
391
|
+
return doc._system?.delete === !0;
|
|
392
|
+
}
|
|
393
|
+
function projectToPublishedId(doc, pubId) {
|
|
394
|
+
return doc._id === pubId ? doc : { ...doc, _id: pubId, _originalId: doc._id };
|
|
395
|
+
}
|
|
396
|
+
function resolveDocument(docs, id, perspective) {
|
|
397
|
+
return perspective === "raw" ? docs.find((d) => d._id === id) : applyPerspective(docs, perspective).find((d) => d._id === id);
|
|
398
|
+
}
|
|
399
|
+
function createTestStore(initial) {
|
|
400
|
+
const docs = /* @__PURE__ */ new Map(), promote2 = (doc) => {
|
|
401
|
+
if (typeof doc._id != "string" || doc._id.length === 0)
|
|
402
|
+
throw fakeError(
|
|
403
|
+
`Cannot seed/store a document without a non-empty string \`_id\` (got ${JSON.stringify(doc._id)}). This fake never auto-generates ids \u2014 give every document an explicit _id. Document: ${JSON.stringify(doc)}`
|
|
404
|
+
);
|
|
405
|
+
if (typeof doc._type != "string" || doc._type.length === 0)
|
|
406
|
+
throw fakeError(
|
|
407
|
+
`Document "${doc._id}" is missing a non-empty string \`_type\` (got ${JSON.stringify(doc._type)}). Every Sanity document needs a _type.`
|
|
408
|
+
);
|
|
409
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
410
|
+
return {
|
|
411
|
+
...doc,
|
|
412
|
+
_id: doc._id,
|
|
413
|
+
_type: doc._type,
|
|
414
|
+
_rev: doc._rev ?? cryptoRandomKey(),
|
|
415
|
+
_createdAt: doc._createdAt ?? now,
|
|
416
|
+
_updatedAt: doc._updatedAt ?? now
|
|
417
|
+
};
|
|
418
|
+
}, seed = (documents) => {
|
|
419
|
+
docs.clear();
|
|
420
|
+
for (const d of documents) {
|
|
421
|
+
const promoted = promote2(d);
|
|
422
|
+
docs.set(promoted._id, promoted);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
return initial && seed(initial), {
|
|
426
|
+
seed,
|
|
427
|
+
add: (documents) => {
|
|
428
|
+
for (const d of documents) {
|
|
429
|
+
const promoted = promote2(d);
|
|
430
|
+
docs.set(promoted._id, promoted);
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
reset: () => docs.clear(),
|
|
434
|
+
all: () => [...docs.values()],
|
|
435
|
+
get: (id) => docs.get(id),
|
|
436
|
+
put: (doc) => {
|
|
437
|
+
docs.set(doc._id, doc);
|
|
438
|
+
},
|
|
439
|
+
remove: (id) => {
|
|
440
|
+
const doc = docs.get(id);
|
|
441
|
+
return doc && docs.delete(id), doc;
|
|
442
|
+
},
|
|
443
|
+
snapshot: () => [...docs.values()]
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function cryptoRandomKey(length = 12) {
|
|
447
|
+
const bytes = new Uint8Array(length);
|
|
448
|
+
return globalThis.crypto.getRandomValues(bytes), [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
|
|
449
|
+
}
|
|
450
|
+
const RELEASE_DOC_PREFIX = "_.releases.", VERSIONS_PREFIX = "versions.", releaseDocId = (releaseId) => `${RELEASE_DOC_PREFIX}${releaseId}`, versionDocId = (releaseId, publishedId) => `${VERSIONS_PREFIX}${releaseId}.${publishedId}`;
|
|
328
451
|
function parseVersionId(versionId) {
|
|
329
|
-
if (!versionId.startsWith(VERSIONS_PREFIX
|
|
452
|
+
if (!versionId.startsWith(VERSIONS_PREFIX))
|
|
330
453
|
throw fakeError(
|
|
331
454
|
`"${versionId}" is not a version document id. Version ids look like "versions.<releaseId>.<publishedId>" (e.g. "versions.summer-drop.product-1").`
|
|
332
455
|
);
|
|
333
|
-
const rest = versionId.slice(VERSIONS_PREFIX
|
|
456
|
+
const rest = versionId.slice(VERSIONS_PREFIX.length), dot = rest.indexOf(".");
|
|
334
457
|
if (dot === -1)
|
|
335
458
|
throw fakeError(
|
|
336
459
|
`"${versionId}" is not a version document id. Version ids look like "versions.<releaseId>.<publishedId>" (e.g. "versions.summer-drop.product-1").`
|
|
@@ -376,7 +499,7 @@ function requireActive(release) {
|
|
|
376
499
|
`Release "${release.name}" is "${release.state}", but only "active" releases accept version writes (create / replace / discard / unpublish). Unschedule or unarchive it back to "active" first, or target a different release.`
|
|
377
500
|
);
|
|
378
501
|
}
|
|
379
|
-
function applyAction(store, action) {
|
|
502
|
+
async function applyAction(store, action, hooks) {
|
|
380
503
|
switch (action.actionType) {
|
|
381
504
|
case "sanity.action.release.create": {
|
|
382
505
|
if (maybeRelease(store, action.releaseId) !== void 0)
|
|
@@ -434,7 +557,7 @@ function applyAction(store, action) {
|
|
|
434
557
|
throw fakeError(
|
|
435
558
|
`Cannot publish release "${action.releaseId}": it is "${release.state}", and only "active" or "scheduled" releases can be published.`
|
|
436
559
|
);
|
|
437
|
-
const prefix = `${VERSIONS_PREFIX
|
|
560
|
+
const prefix = `${VERSIONS_PREFIX}${action.releaseId}.`, sources = store.all().filter((d) => d._id.startsWith(prefix));
|
|
438
561
|
for (const v of sources) {
|
|
439
562
|
const pubId = v._id.slice(prefix.length);
|
|
440
563
|
if (v._system?.delete === !0)
|
|
@@ -474,7 +597,7 @@ function applyAction(store, action) {
|
|
|
474
597
|
throw fakeError(
|
|
475
598
|
`Cannot delete release "${action.releaseId}": it is "${release.state}", and only "archived" or "published" releases can be deleted. Archive it first (sanity.action.release.archive) or publish it.`
|
|
476
599
|
);
|
|
477
|
-
const prefix = `${VERSIONS_PREFIX
|
|
600
|
+
const prefix = `${VERSIONS_PREFIX}${action.releaseId}.`;
|
|
478
601
|
for (const v of store.all())
|
|
479
602
|
v._id.startsWith(prefix) && store.remove(v._id);
|
|
480
603
|
return store.remove(release._id), { transactionId: cryptoRandomKey() };
|
|
@@ -518,9 +641,46 @@ function applyAction(store, action) {
|
|
|
518
641
|
};
|
|
519
642
|
return store.put(promote(doc)), { transactionId: cryptoRandomKey() };
|
|
520
643
|
}
|
|
644
|
+
case "sanity.action.document.publish": {
|
|
645
|
+
requireDraftOf(action);
|
|
646
|
+
const draft = store.get(action.draftId);
|
|
647
|
+
if (draft === void 0)
|
|
648
|
+
throw fakeError(
|
|
649
|
+
`Cannot publish "${action.publishedId}": draft "${action.draftId}" not found. document.publish promotes an existing draft onto the published id \u2014 create the draft first (e.g. client.createOrReplace({ _id: "${action.draftId}", ... })).`
|
|
650
|
+
);
|
|
651
|
+
const published = store.get(action.publishedId);
|
|
652
|
+
requireRevisionMatch(action.draftId, draft._rev, action.ifDraftRevisionId), requireRevisionMatch(action.publishedId, published?._rev, action.ifPublishedRevisionId);
|
|
653
|
+
const promoted = promote({ ...draft, _id: action.publishedId });
|
|
654
|
+
return published !== void 0 && (promoted._createdAt = published._createdAt), await hooks?.gate?.(promoted, published === void 0 ? "create" : "update"), await hooks?.guard?.("publish", published ?? null, promoted), store.put(promoted), store.remove(action.draftId), { transactionId: cryptoRandomKey() };
|
|
655
|
+
}
|
|
656
|
+
case "sanity.action.document.unpublish": {
|
|
657
|
+
requireDraftOf(action);
|
|
658
|
+
const published = store.get(action.publishedId);
|
|
659
|
+
if (published === void 0)
|
|
660
|
+
throw fakeError(
|
|
661
|
+
`Cannot unpublish "${action.publishedId}": no published document with that _id exists. document.unpublish retracts a live published document into "${action.draftId}".`
|
|
662
|
+
);
|
|
663
|
+
if (await hooks?.gate?.(published, "update"), await hooks?.guard?.("unpublish", published, null), store.get(action.draftId) === void 0) {
|
|
664
|
+
const draft = promote({ ...published, _id: action.draftId });
|
|
665
|
+
draft._createdAt = published._createdAt, store.put(draft);
|
|
666
|
+
}
|
|
667
|
+
return store.remove(action.publishedId), { transactionId: cryptoRandomKey() };
|
|
668
|
+
}
|
|
521
669
|
}
|
|
522
670
|
}
|
|
523
|
-
function
|
|
671
|
+
function requireDraftOf(action) {
|
|
672
|
+
if (!(publishedIdOf(action.draftId) === action.publishedId && action.draftId !== action.publishedId))
|
|
673
|
+
throw fakeError(
|
|
674
|
+
`${action.actionType}: draftId "${action.draftId}" is not the draft of publishedId "${action.publishedId}" \u2014 expected draftId "drafts.${action.publishedId}".`
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
function requireRevisionMatch(documentId, actualRev, expectedRev) {
|
|
678
|
+
if (!(expectedRev === void 0 || actualRev === expectedRev))
|
|
679
|
+
throw fakeError(
|
|
680
|
+
`Revision precondition failed for "${documentId}": expected _rev "${expectedRev}" but the document is at ${actualRev === void 0 ? "no revision (missing)" : `"${actualRev}"`}.`
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
async function applyActions(store, actions, hooks) {
|
|
524
684
|
const list = Array.isArray(actions) ? actions : [actions], snapshot = /* @__PURE__ */ new Map(), remember = (id) => {
|
|
525
685
|
snapshot.has(id) || snapshot.set(id, store.get(id));
|
|
526
686
|
};
|
|
@@ -535,11 +695,11 @@ function applyActions(store, actions) {
|
|
|
535
695
|
case "sanity.action.release.archive":
|
|
536
696
|
case "sanity.action.release.unarchive":
|
|
537
697
|
case "sanity.action.release.delete":
|
|
538
|
-
remember(`${RELEASE_DOC_PREFIX
|
|
698
|
+
remember(`${RELEASE_DOC_PREFIX}${a.releaseId}`);
|
|
539
699
|
break;
|
|
540
700
|
case "sanity.action.release.publish": {
|
|
541
|
-
remember(`${RELEASE_DOC_PREFIX
|
|
542
|
-
const prefix = `${VERSIONS_PREFIX
|
|
701
|
+
remember(`${RELEASE_DOC_PREFIX}${a.releaseId}`);
|
|
702
|
+
const prefix = `${VERSIONS_PREFIX}${a.releaseId}.`;
|
|
543
703
|
for (const d of store.all())
|
|
544
704
|
d._id.startsWith(prefix) && (remember(d._id), remember(d._id.slice(prefix.length)));
|
|
545
705
|
break;
|
|
@@ -556,8 +716,12 @@ function applyActions(store, actions) {
|
|
|
556
716
|
case "sanity.action.document.version.unpublish":
|
|
557
717
|
remember(a.versionId);
|
|
558
718
|
break;
|
|
719
|
+
case "sanity.action.document.publish":
|
|
720
|
+
case "sanity.action.document.unpublish":
|
|
721
|
+
remember(a.draftId), remember(a.publishedId);
|
|
722
|
+
break;
|
|
559
723
|
}
|
|
560
|
-
last = applyAction(store, a);
|
|
724
|
+
last = await applyAction(store, a, hooks);
|
|
561
725
|
}
|
|
562
726
|
return last;
|
|
563
727
|
} catch (err) {
|
|
@@ -566,82 +730,6 @@ function applyActions(store, actions) {
|
|
|
566
730
|
throw err;
|
|
567
731
|
}
|
|
568
732
|
}
|
|
569
|
-
const DRAFTS_PREFIX = "drafts.", VERSIONS_PREFIX = "versions.", RELEASE_DOC_PREFIX = "_.releases.";
|
|
570
|
-
function publishedIdOf(id) {
|
|
571
|
-
if (id.startsWith(DRAFTS_PREFIX)) return id.slice(DRAFTS_PREFIX.length);
|
|
572
|
-
if (id.startsWith(VERSIONS_PREFIX)) {
|
|
573
|
-
const rest = id.slice(VERSIONS_PREFIX.length), dot = rest.indexOf(".");
|
|
574
|
-
return dot === -1 ? rest : rest.slice(dot + 1);
|
|
575
|
-
}
|
|
576
|
-
return id;
|
|
577
|
-
}
|
|
578
|
-
function releaseNameOf(id) {
|
|
579
|
-
if (!id.startsWith(VERSIONS_PREFIX)) return;
|
|
580
|
-
const rest = id.slice(VERSIONS_PREFIX.length), dot = rest.indexOf(".");
|
|
581
|
-
if (dot !== -1)
|
|
582
|
-
return rest.slice(0, dot);
|
|
583
|
-
}
|
|
584
|
-
const isDraftId = (id) => id.startsWith(DRAFTS_PREFIX), isVersionId = (id) => id.startsWith(VERSIONS_PREFIX), isSystemId = (id) => id.startsWith("_."), isReleaseDocId = (id) => id.startsWith(RELEASE_DOC_PREFIX);
|
|
585
|
-
function applyPerspective(docs, perspective) {
|
|
586
|
-
if (perspective === "raw") return [...docs];
|
|
587
|
-
if (perspective === "published")
|
|
588
|
-
return docs.filter((d) => !isDraftId(d._id) && !isVersionId(d._id) && !isSystemId(d._id));
|
|
589
|
-
if (perspective === "drafts")
|
|
590
|
-
return mergeStack(docs, [], { includeDrafts: !0, includeReleaseDocs: !1 });
|
|
591
|
-
const stack = [];
|
|
592
|
-
let includeDrafts = !1;
|
|
593
|
-
for (const entry of perspective)
|
|
594
|
-
entry === "drafts" ? includeDrafts = !0 : stack.push(entry);
|
|
595
|
-
return mergeStack(docs, stack, { includeDrafts, includeReleaseDocs: !0 });
|
|
596
|
-
}
|
|
597
|
-
function mergeStack(docs, releaseStack, options) {
|
|
598
|
-
const groups = /* @__PURE__ */ new Map(), systemReleases = [];
|
|
599
|
-
for (const doc of docs) {
|
|
600
|
-
if (isReleaseDocId(doc._id)) {
|
|
601
|
-
options.includeReleaseDocs && systemReleases.push(doc);
|
|
602
|
-
continue;
|
|
603
|
-
}
|
|
604
|
-
if (isSystemId(doc._id)) continue;
|
|
605
|
-
const pubId = publishedIdOf(doc._id);
|
|
606
|
-
let group = groups.get(pubId);
|
|
607
|
-
if (group === void 0 && (group = { versions: /* @__PURE__ */ new Map() }, groups.set(pubId, group)), isDraftId(doc._id))
|
|
608
|
-
group.draft = doc;
|
|
609
|
-
else if (isVersionId(doc._id)) {
|
|
610
|
-
const releaseName = releaseNameOf(doc._id);
|
|
611
|
-
releaseName !== void 0 && group.versions.set(releaseName, doc);
|
|
612
|
-
} else
|
|
613
|
-
group.published = doc;
|
|
614
|
-
}
|
|
615
|
-
const out = [];
|
|
616
|
-
for (const [pubId, group] of groups) {
|
|
617
|
-
let chosen;
|
|
618
|
-
for (const releaseName of releaseStack) {
|
|
619
|
-
const v = group.versions.get(releaseName);
|
|
620
|
-
if (v !== void 0) {
|
|
621
|
-
if (isUnpublishStub(v)) {
|
|
622
|
-
chosen = void 0;
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
chosen = v;
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
chosen === void 0 && options.includeDrafts && group.draft !== void 0 && (chosen = group.draft), chosen === void 0 && group.published !== void 0 && (releaseStack.some((name) => {
|
|
630
|
-
const v = group.versions.get(name);
|
|
631
|
-
return v !== void 0 && isUnpublishStub(v);
|
|
632
|
-
}) || (chosen = group.published)), chosen !== void 0 && out.push(projectToPublishedId(chosen, pubId));
|
|
633
|
-
}
|
|
634
|
-
return [...out, ...systemReleases];
|
|
635
|
-
}
|
|
636
|
-
function isUnpublishStub(doc) {
|
|
637
|
-
return doc._system?.delete === !0;
|
|
638
|
-
}
|
|
639
|
-
function projectToPublishedId(doc, pubId) {
|
|
640
|
-
return doc._id === pubId ? doc : { ...doc, _id: pubId, _originalId: doc._id };
|
|
641
|
-
}
|
|
642
|
-
function resolveDocument(docs, id, perspective) {
|
|
643
|
-
return perspective === "raw" ? docs.find((d) => d._id === id) : applyPerspective(docs, perspective).find((d) => d._id === id);
|
|
644
|
-
}
|
|
645
733
|
function createTransaction(executor) {
|
|
646
734
|
const mutations = [], handle = {
|
|
647
735
|
create: (doc) => (mutations.push({ kind: "create", doc }), handle),
|
|
@@ -737,7 +825,7 @@ const ISSUES_URL = "https://github.com/sanity-labs/client-fake-for-test/issues",
|
|
|
737
825
|
},
|
|
738
826
|
request: {
|
|
739
827
|
what: "generic HTTP request hook \u2014 only wired up when the handle has an acting identity",
|
|
740
|
-
workaround: "Create the client with `accessControl`, or bind a registered token (`identities` + `token` / `withConfig({ token })`), to enable request() (it serves /users/me
|
|
828
|
+
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, and /projects/<projectId>/users/<ids>). Or register a stub."
|
|
741
829
|
},
|
|
742
830
|
dryRun: {
|
|
743
831
|
what: "not a method \u2014 did you mean the `{ dryRun }` mutation option?",
|
|
@@ -920,15 +1008,16 @@ function createTestClient(options = {}) {
|
|
|
920
1008
|
const { projectId, dataset } = parseRegistryKey(key);
|
|
921
1009
|
ensureStore(projectId, dataset).seed(docs);
|
|
922
1010
|
}
|
|
923
|
-
return makeHandle(
|
|
1011
|
+
return makeHandle({
|
|
924
1012
|
backend,
|
|
925
|
-
defaultProjectId,
|
|
926
|
-
defaultDataset,
|
|
927
|
-
options.accessControl,
|
|
928
|
-
options.perspective ?? "raw",
|
|
929
|
-
options.mutationGuard,
|
|
930
|
-
options.token
|
|
931
|
-
|
|
1013
|
+
projectId: defaultProjectId,
|
|
1014
|
+
dataset: defaultDataset,
|
|
1015
|
+
accessControl: options.accessControl,
|
|
1016
|
+
perspective: options.perspective ?? "raw",
|
|
1017
|
+
mutationGuard: options.mutationGuard,
|
|
1018
|
+
token: options.token,
|
|
1019
|
+
useProjectHostname: options.useProjectHostname ?? !0
|
|
1020
|
+
});
|
|
932
1021
|
}
|
|
933
1022
|
function dedupeIds(results) {
|
|
934
1023
|
const seen = /* @__PURE__ */ new Set(), out = [];
|
|
@@ -980,13 +1069,26 @@ function toTxnMutations(operations) {
|
|
|
980
1069
|
}
|
|
981
1070
|
];
|
|
982
1071
|
}
|
|
983
|
-
function makeHandle(
|
|
984
|
-
const {
|
|
1072
|
+
function makeHandle(handleArgs) {
|
|
1073
|
+
const {
|
|
1074
|
+
backend,
|
|
1075
|
+
projectId,
|
|
1076
|
+
dataset,
|
|
1077
|
+
accessControl,
|
|
1078
|
+
perspective,
|
|
1079
|
+
mutationGuard,
|
|
1080
|
+
token,
|
|
1081
|
+
useProjectHostname
|
|
1082
|
+
} = handleArgs, { registry } = backend, activeAccess = () => {
|
|
985
1083
|
if (token === void 0) return accessControl;
|
|
986
1084
|
const identity = backend.identities.get(token);
|
|
987
1085
|
if (identity === void 0)
|
|
988
1086
|
throw fakeError(`no identity is registered for token "${token}" (was it removed?).`);
|
|
989
|
-
return {
|
|
1087
|
+
return {
|
|
1088
|
+
currentUser: projectUserOf(identity, projectId),
|
|
1089
|
+
...identity.globalUser !== void 0 ? { globalUser: identity.globalUser } : {},
|
|
1090
|
+
grants: identity.grants
|
|
1091
|
+
};
|
|
990
1092
|
}, requireActiveAccess = () => {
|
|
991
1093
|
const access = activeAccess();
|
|
992
1094
|
if (access === void 0)
|
|
@@ -1136,7 +1238,7 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1136
1238
|
txnOptions?.returnDocuments ?? !1,
|
|
1137
1239
|
txnOptions?.returnFirst ?? !1
|
|
1138
1240
|
);
|
|
1139
|
-
}, runAction = (actions) => serializeWrite(backend, async () => applyActions(store, actions)), accessVisibleDocs = async () => {
|
|
1241
|
+
}, runAction = (actions) => serializeWrite(backend, async () => applyActions(store, actions, { gate, guard: guardGate })), accessVisibleDocs = async () => {
|
|
1140
1242
|
const all = store.all();
|
|
1141
1243
|
if (activeAccess() === void 0) return all;
|
|
1142
1244
|
const out = [];
|
|
@@ -1307,20 +1409,35 @@ function makeHandle(backend, projectId, dataset, accessControl, perspective, mut
|
|
|
1307
1409
|
versionId: `versions.${releaseId}.${publishedId}`,
|
|
1308
1410
|
publishedId
|
|
1309
1411
|
}),
|
|
1310
|
-
...token !== void 0 || accessControl !== void 0 ? {
|
|
1311
|
-
|
|
1412
|
+
...token !== void 0 || accessControl !== void 0 ? {
|
|
1413
|
+
request: makeRequestImpl({
|
|
1414
|
+
getAccess: requireActiveAccess,
|
|
1415
|
+
projectId,
|
|
1416
|
+
dataset,
|
|
1417
|
+
useProjectHostname,
|
|
1418
|
+
listIdentities: () => [...backend.identities.values()]
|
|
1419
|
+
})
|
|
1420
|
+
} : {},
|
|
1421
|
+
config: () => ({
|
|
1422
|
+
projectId,
|
|
1423
|
+
dataset,
|
|
1424
|
+
perspective,
|
|
1425
|
+
...token !== void 0 ? { token } : {},
|
|
1426
|
+
useProjectHostname
|
|
1427
|
+
}),
|
|
1312
1428
|
withConfig: (config) => {
|
|
1313
1429
|
rejectAmbiguousIdentity("withConfig()", config), config.token !== void 0 && requireIdentity(backend, config.token, "withConfig()");
|
|
1314
1430
|
const rebinds = config.token !== void 0 || config.accessControl !== void 0;
|
|
1315
|
-
return makeHandle(
|
|
1431
|
+
return makeHandle({
|
|
1316
1432
|
backend,
|
|
1317
|
-
config.projectId ?? projectId,
|
|
1318
|
-
config.dataset ?? dataset,
|
|
1319
|
-
rebinds ? config.accessControl : accessControl,
|
|
1320
|
-
config.perspective ?? perspective,
|
|
1433
|
+
projectId: config.projectId ?? projectId,
|
|
1434
|
+
dataset: config.dataset ?? dataset,
|
|
1435
|
+
accessControl: rebinds ? config.accessControl : accessControl,
|
|
1436
|
+
perspective: config.perspective ?? perspective,
|
|
1321
1437
|
mutationGuard,
|
|
1322
|
-
rebinds ? config.token : token
|
|
1323
|
-
|
|
1438
|
+
token: rebinds ? config.token : token,
|
|
1439
|
+
useProjectHostname: config.useProjectHostname ?? useProjectHostname
|
|
1440
|
+
});
|
|
1324
1441
|
},
|
|
1325
1442
|
seed: (documents) => store.seed(documents),
|
|
1326
1443
|
add: (documents) => store.add(documents),
|