@sanity-labs/client-fake-for-test 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -68,6 +68,53 @@ function makeRequestImpl(accessControl, projectId, dataset) {
68
68
  );
69
69
  };
70
70
  }
71
+ function createTestStore(initial) {
72
+ const docs = /* @__PURE__ */ new Map(), promote2 = (doc) => {
73
+ if (typeof doc._id != "string" || doc._id.length === 0)
74
+ throw new Error(`document is missing _id: ${JSON.stringify(doc)}`);
75
+ if (typeof doc._type != "string" || doc._type.length === 0)
76
+ throw new Error(`document ${doc._id} is missing _type`);
77
+ const now = (/* @__PURE__ */ new Date()).toISOString();
78
+ return {
79
+ ...doc,
80
+ _id: doc._id,
81
+ _type: doc._type,
82
+ _rev: doc._rev ?? cryptoRandomKey(),
83
+ _createdAt: doc._createdAt ?? now,
84
+ _updatedAt: doc._updatedAt ?? now
85
+ };
86
+ }, seed = (documents) => {
87
+ docs.clear();
88
+ for (const d of documents) {
89
+ const promoted = promote2(d);
90
+ docs.set(promoted._id, promoted);
91
+ }
92
+ };
93
+ return initial && seed(initial), {
94
+ seed,
95
+ add: (documents) => {
96
+ for (const d of documents) {
97
+ const promoted = promote2(d);
98
+ docs.set(promoted._id, promoted);
99
+ }
100
+ },
101
+ reset: () => docs.clear(),
102
+ all: () => [...docs.values()],
103
+ get: (id) => docs.get(id),
104
+ put: (doc) => {
105
+ docs.set(doc._id, doc);
106
+ },
107
+ remove: (id) => {
108
+ const doc = docs.get(id);
109
+ return doc && docs.delete(id), doc;
110
+ },
111
+ snapshot: () => [...docs.values()]
112
+ };
113
+ }
114
+ function cryptoRandomKey(length = 12) {
115
+ const bytes = new Uint8Array(length);
116
+ return globalThis.crypto.getRandomValues(bytes), [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
117
+ }
71
118
  function createPatch(documentId, executor) {
72
119
  const ops = [];
73
120
  let ifRev;
@@ -246,52 +293,301 @@ function insertIntoArray(doc, position, anchor, items) {
246
293
  const idx = resolveSelector(arr, last.selector);
247
294
  position === "replace" ? arr.splice(idx, 1, ...items) : position === "before" ? arr.splice(idx, 0, ...items) : arr.splice(idx + 1, 0, ...items);
248
295
  }
249
- function createTestStore(initial) {
250
- const docs = /* @__PURE__ */ new Map(), promote = (doc) => {
251
- if (typeof doc._id != "string" || doc._id.length === 0)
252
- throw new Error(`document is missing _id: ${JSON.stringify(doc)}`);
253
- if (typeof doc._type != "string" || doc._type.length === 0)
254
- throw new Error(`document ${doc._id} is missing _type`);
255
- const now = (/* @__PURE__ */ new Date()).toISOString();
256
- return {
257
- ...doc,
258
- _id: doc._id,
259
- _type: doc._type,
260
- _rev: doc._rev ?? cryptoRandomKey(),
261
- _createdAt: doc._createdAt ?? now,
262
- _updatedAt: doc._updatedAt ?? now
263
- };
264
- }, seed = (documents) => {
265
- docs.clear();
266
- for (const d of documents) {
267
- const promoted = promote(d);
268
- docs.set(promoted._id, promoted);
269
- }
296
+ const RELEASE_DOC_PREFIX$1 = "_.releases.", VERSIONS_PREFIX$1 = "versions.", releaseDocId = (releaseId) => `${RELEASE_DOC_PREFIX$1}${releaseId}`, versionDocId = (releaseId, publishedId) => `${VERSIONS_PREFIX$1}${releaseId}.${publishedId}`;
297
+ function parseVersionId(versionId) {
298
+ if (!versionId.startsWith(VERSIONS_PREFIX$1))
299
+ throw new Error(`Not a version id: ${versionId}`);
300
+ const rest = versionId.slice(VERSIONS_PREFIX$1.length), dot = rest.indexOf(".");
301
+ if (dot === -1)
302
+ throw new Error(`Not a version id: ${versionId}`);
303
+ return { releaseId: rest.slice(0, dot), publishedId: rest.slice(dot + 1) };
304
+ }
305
+ function getRelease(store, releaseId) {
306
+ const doc = store.get(releaseDocId(releaseId));
307
+ if (doc === void 0)
308
+ throw new Error(`Release not found: ${releaseId}`);
309
+ return doc;
310
+ }
311
+ function maybeRelease(store, releaseId) {
312
+ return store.get(releaseDocId(releaseId));
313
+ }
314
+ function nowIso() {
315
+ return (/* @__PURE__ */ new Date()).toISOString();
316
+ }
317
+ function promote(doc) {
318
+ const now = nowIso();
319
+ return {
320
+ ...doc,
321
+ _id: doc._id,
322
+ _type: doc._type,
323
+ _rev: cryptoRandomKey(),
324
+ _createdAt: doc._createdAt ?? now,
325
+ _updatedAt: now
270
326
  };
271
- return initial && seed(initial), {
272
- seed,
273
- add: (documents) => {
274
- for (const d of documents) {
275
- const promoted = promote(d);
276
- docs.set(promoted._id, promoted);
327
+ }
328
+ function bumpRev(doc, patch) {
329
+ return {
330
+ ...doc,
331
+ ...patch,
332
+ _rev: cryptoRandomKey(),
333
+ _updatedAt: nowIso()
334
+ };
335
+ }
336
+ function requireActive(release) {
337
+ if (release.state !== "active")
338
+ throw new Error(
339
+ `Release ${release.name} is ${release.state}; only active releases accept version writes`
340
+ );
341
+ }
342
+ function applyAction(store, action) {
343
+ switch (action.actionType) {
344
+ case "sanity.action.release.create": {
345
+ if (maybeRelease(store, action.releaseId) !== void 0)
346
+ throw new Error(`Release ${action.releaseId} already exists`);
347
+ const now = nowIso(), metadata = {
348
+ releaseType: "undecided",
349
+ ...action.metadata
350
+ }, doc = {
351
+ _id: releaseDocId(action.releaseId),
352
+ _type: "system.release",
353
+ _rev: cryptoRandomKey(),
354
+ _createdAt: now,
355
+ _updatedAt: now,
356
+ name: action.releaseId,
357
+ state: "active",
358
+ metadata
359
+ };
360
+ return store.put(doc), { transactionId: cryptoRandomKey() };
361
+ }
362
+ case "sanity.action.release.edit": {
363
+ const release = getRelease(store, action.releaseId), patchOps = [];
364
+ action.patch.set !== void 0 && patchOps.push({ kind: "set", props: action.patch.set }), action.patch.unset !== void 0 && patchOps.push({ kind: "unset", paths: action.patch.unset });
365
+ const patched = applyPatchOps(release, patchOps);
366
+ return store.put(bumpRev(patched, {})), { transactionId: cryptoRandomKey() };
367
+ }
368
+ case "sanity.action.release.schedule": {
369
+ const release = getRelease(store, action.releaseId);
370
+ if (release.state !== "active")
371
+ throw new Error(`Cannot schedule release in state ${release.state}`);
372
+ return store.put(
373
+ bumpRev(release, {
374
+ state: "scheduled",
375
+ publishAt: action.publishAt
376
+ })
377
+ ), { transactionId: cryptoRandomKey() };
378
+ }
379
+ case "sanity.action.release.unschedule": {
380
+ const release = getRelease(store, action.releaseId);
381
+ if (release.state !== "scheduled")
382
+ throw new Error(`Cannot unschedule release in state ${release.state}`);
383
+ const next = { ...release, state: "active" };
384
+ return delete next.publishAt, store.put(bumpRev(next, {})), { transactionId: cryptoRandomKey() };
385
+ }
386
+ case "sanity.action.release.publish": {
387
+ const release = getRelease(store, action.releaseId);
388
+ if (release.state === "published")
389
+ return { transactionId: cryptoRandomKey() };
390
+ if (release.state !== "active" && release.state !== "scheduled")
391
+ throw new Error(`Cannot publish release in state ${release.state}`);
392
+ const prefix = `${VERSIONS_PREFIX$1}${action.releaseId}.`, sources = store.all().filter((d) => d._id.startsWith(prefix));
393
+ for (const v of sources) {
394
+ const pubId = v._id.slice(prefix.length);
395
+ if (v._system?.delete === !0)
396
+ store.remove(pubId);
397
+ else {
398
+ const body = {
399
+ ...v,
400
+ _id: pubId
401
+ };
402
+ delete body._originalId, delete body._system;
403
+ const promoted = promote(body), existing = store.get(pubId);
404
+ existing !== void 0 && (promoted._createdAt = existing._createdAt), store.put(promoted);
405
+ }
406
+ store.remove(v._id);
277
407
  }
278
- },
279
- reset: () => docs.clear(),
280
- all: () => [...docs.values()],
281
- get: (id) => docs.get(id),
282
- put: (doc) => {
283
- docs.set(doc._id, doc);
284
- },
285
- remove: (id) => {
286
- const doc = docs.get(id);
287
- return doc && docs.delete(id), doc;
288
- },
289
- snapshot: () => [...docs.values()]
408
+ return store.put(bumpRev(release, { state: "published" })), { transactionId: cryptoRandomKey() };
409
+ }
410
+ case "sanity.action.release.archive": {
411
+ const release = getRelease(store, action.releaseId);
412
+ if (release.state !== "active" && release.state !== "scheduled")
413
+ throw new Error(`Cannot archive release in state ${release.state}`);
414
+ return store.put(bumpRev(release, { state: "archived" })), { transactionId: cryptoRandomKey() };
415
+ }
416
+ case "sanity.action.release.unarchive": {
417
+ const release = getRelease(store, action.releaseId);
418
+ if (release.state !== "archived")
419
+ throw new Error(`Cannot unarchive release in state ${release.state}`);
420
+ return store.put(bumpRev(release, { state: "active" })), { transactionId: cryptoRandomKey() };
421
+ }
422
+ case "sanity.action.release.delete": {
423
+ const release = getRelease(store, action.releaseId);
424
+ if (release.state !== "archived" && release.state !== "published")
425
+ throw new Error(
426
+ `Cannot delete release in state ${release.state}; archive or publish it first`
427
+ );
428
+ const prefix = `${VERSIONS_PREFIX$1}${action.releaseId}.`;
429
+ for (const v of store.all())
430
+ v._id.startsWith(prefix) && store.remove(v._id);
431
+ return store.remove(release._id), { transactionId: cryptoRandomKey() };
432
+ }
433
+ case "sanity.action.document.version.create": {
434
+ const releaseId = action.releaseId ?? (action.document._id !== void 0 ? parseVersionId(action.document._id).releaseId : void 0);
435
+ if (releaseId === void 0)
436
+ throw new Error("version.create needs releaseId or a document._id that encodes it");
437
+ const release = getRelease(store, releaseId);
438
+ requireActive(release);
439
+ const vid = versionDocId(releaseId, action.publishedId), doc = {
440
+ ...action.document,
441
+ _id: vid
442
+ }, existing = store.get(vid), promoted = promote(doc);
443
+ return existing !== void 0 && (promoted._createdAt = existing._createdAt), store.put(promoted), { transactionId: cryptoRandomKey() };
444
+ }
445
+ case "sanity.action.document.version.replace": {
446
+ const id = action.document._id;
447
+ if (typeof id != "string")
448
+ throw new Error("version.replace needs document._id");
449
+ const { releaseId } = parseVersionId(id), release = getRelease(store, releaseId);
450
+ requireActive(release);
451
+ const existing = store.get(id), promoted = promote(action.document);
452
+ return existing !== void 0 && (promoted._createdAt = existing._createdAt), store.put(promoted), { transactionId: cryptoRandomKey() };
453
+ }
454
+ case "sanity.action.document.version.discard": {
455
+ const { releaseId } = parseVersionId(action.versionId), release = getRelease(store, releaseId);
456
+ return requireActive(release), store.remove(action.versionId), { transactionId: cryptoRandomKey() };
457
+ }
458
+ case "sanity.action.document.version.unpublish": {
459
+ const { releaseId } = parseVersionId(action.versionId), release = getRelease(store, releaseId);
460
+ requireActive(release);
461
+ const doc = {
462
+ _id: action.versionId,
463
+ _type: "stub",
464
+ _system: { delete: !0 }
465
+ };
466
+ return store.put(promote(doc)), { transactionId: cryptoRandomKey() };
467
+ }
468
+ }
469
+ }
470
+ function applyActions(store, actions) {
471
+ const list = Array.isArray(actions) ? actions : [actions], snapshot = /* @__PURE__ */ new Map(), remember = (id) => {
472
+ snapshot.has(id) || snapshot.set(id, store.get(id));
290
473
  };
474
+ try {
475
+ let last = { transactionId: cryptoRandomKey() };
476
+ for (const a of list) {
477
+ switch (a.actionType) {
478
+ case "sanity.action.release.create":
479
+ case "sanity.action.release.edit":
480
+ case "sanity.action.release.schedule":
481
+ case "sanity.action.release.unschedule":
482
+ case "sanity.action.release.archive":
483
+ case "sanity.action.release.unarchive":
484
+ case "sanity.action.release.delete":
485
+ remember(`${RELEASE_DOC_PREFIX$1}${a.releaseId}`);
486
+ break;
487
+ case "sanity.action.release.publish": {
488
+ remember(`${RELEASE_DOC_PREFIX$1}${a.releaseId}`);
489
+ const prefix = `${VERSIONS_PREFIX$1}${a.releaseId}.`;
490
+ for (const d of store.all())
491
+ d._id.startsWith(prefix) && (remember(d._id), remember(d._id.slice(prefix.length)));
492
+ break;
493
+ }
494
+ case "sanity.action.document.version.create": {
495
+ const releaseId = a.releaseId ?? (typeof a.document._id == "string" ? parseVersionId(a.document._id).releaseId : void 0);
496
+ releaseId !== void 0 && remember(versionDocId(releaseId, a.publishedId));
497
+ break;
498
+ }
499
+ case "sanity.action.document.version.replace":
500
+ typeof a.document._id == "string" && remember(a.document._id);
501
+ break;
502
+ case "sanity.action.document.version.discard":
503
+ case "sanity.action.document.version.unpublish":
504
+ remember(a.versionId);
505
+ break;
506
+ }
507
+ last = applyAction(store, a);
508
+ }
509
+ return last;
510
+ } catch (err) {
511
+ for (const [id, doc] of snapshot)
512
+ doc === void 0 ? store.remove(id) : store.put(doc);
513
+ throw err;
514
+ }
291
515
  }
292
- function cryptoRandomKey(length = 12) {
293
- const bytes = new Uint8Array(length);
294
- return globalThis.crypto.getRandomValues(bytes), [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
516
+ const DRAFTS_PREFIX = "drafts.", VERSIONS_PREFIX = "versions.", RELEASE_DOC_PREFIX = "_.releases.";
517
+ function publishedIdOf(id) {
518
+ if (id.startsWith(DRAFTS_PREFIX)) return id.slice(DRAFTS_PREFIX.length);
519
+ if (id.startsWith(VERSIONS_PREFIX)) {
520
+ const rest = id.slice(VERSIONS_PREFIX.length), dot = rest.indexOf(".");
521
+ return dot === -1 ? rest : rest.slice(dot + 1);
522
+ }
523
+ return id;
524
+ }
525
+ function releaseNameOf(id) {
526
+ if (!id.startsWith(VERSIONS_PREFIX)) return;
527
+ const rest = id.slice(VERSIONS_PREFIX.length), dot = rest.indexOf(".");
528
+ if (dot !== -1)
529
+ return rest.slice(0, dot);
530
+ }
531
+ const isDraftId = (id) => id.startsWith(DRAFTS_PREFIX), isVersionId = (id) => id.startsWith(VERSIONS_PREFIX), isSystemId = (id) => id.startsWith("_."), isReleaseDocId = (id) => id.startsWith(RELEASE_DOC_PREFIX);
532
+ function applyPerspective(docs, perspective) {
533
+ if (perspective === "raw") return [...docs];
534
+ if (perspective === "published")
535
+ return docs.filter((d) => !isDraftId(d._id) && !isVersionId(d._id) && !isSystemId(d._id));
536
+ if (perspective === "drafts")
537
+ return mergeStack(docs, [], { includeDrafts: !0, includeReleaseDocs: !1 });
538
+ const stack = [];
539
+ let includeDrafts = !1;
540
+ for (const entry of perspective)
541
+ entry === "drafts" ? includeDrafts = !0 : stack.push(entry);
542
+ return mergeStack(docs, stack, { includeDrafts, includeReleaseDocs: !0 });
543
+ }
544
+ function mergeStack(docs, releaseStack, options) {
545
+ const groups = /* @__PURE__ */ new Map(), systemReleases = [];
546
+ for (const doc of docs) {
547
+ if (isReleaseDocId(doc._id)) {
548
+ options.includeReleaseDocs && systemReleases.push(doc);
549
+ continue;
550
+ }
551
+ if (isSystemId(doc._id)) continue;
552
+ const pubId = publishedIdOf(doc._id);
553
+ let group = groups.get(pubId);
554
+ if (group === void 0 && (group = { versions: /* @__PURE__ */ new Map() }, groups.set(pubId, group)), isDraftId(doc._id))
555
+ group.draft = doc;
556
+ else if (isVersionId(doc._id)) {
557
+ const releaseName = releaseNameOf(doc._id);
558
+ releaseName !== void 0 && group.versions.set(releaseName, doc);
559
+ } else
560
+ group.published = doc;
561
+ }
562
+ const out = [];
563
+ for (const [pubId, group] of groups) {
564
+ let chosen;
565
+ for (const releaseName of releaseStack) {
566
+ const v = group.versions.get(releaseName);
567
+ if (v !== void 0) {
568
+ if (isUnpublishStub(v)) {
569
+ chosen = void 0;
570
+ break;
571
+ }
572
+ chosen = v;
573
+ break;
574
+ }
575
+ }
576
+ chosen === void 0 && options.includeDrafts && group.draft !== void 0 && (chosen = group.draft), chosen === void 0 && group.published !== void 0 && (releaseStack.some((name) => {
577
+ const v = group.versions.get(name);
578
+ return v !== void 0 && isUnpublishStub(v);
579
+ }) || (chosen = group.published)), chosen !== void 0 && out.push(projectToPublishedId(chosen, pubId));
580
+ }
581
+ return [...out, ...systemReleases];
582
+ }
583
+ function isUnpublishStub(doc) {
584
+ return doc._system?.delete === !0;
585
+ }
586
+ function projectToPublishedId(doc, pubId) {
587
+ return doc._id === pubId ? doc : { ...doc, _id: pubId, _originalId: doc._id };
588
+ }
589
+ function resolveDocument(docs, id, perspective) {
590
+ return perspective === "raw" ? docs.find((d) => d._id === id) : applyPerspective(docs, perspective).find((d) => d._id === id);
295
591
  }
296
592
  function createTransaction(executor) {
297
593
  const mutations = [], handle = {
@@ -338,14 +634,20 @@ function createTestClient(options = {}) {
338
634
  const { projectId, dataset } = parseRegistryKey(key);
339
635
  ensureStore(projectId, dataset).seed(docs);
340
636
  }
341
- return makeHandle(registry, defaultProjectId, defaultDataset, options.accessControl);
637
+ return makeHandle(
638
+ registry,
639
+ defaultProjectId,
640
+ defaultDataset,
641
+ options.accessControl,
642
+ options.perspective ?? "raw"
643
+ );
342
644
  }
343
- function makeHandle(registry, projectId, dataset, accessControl) {
645
+ function makeHandle(registry, projectId, dataset, accessControl, perspective) {
344
646
  const store = (() => {
345
647
  const key = registryKey(projectId, dataset);
346
648
  let s = registry.get(key);
347
649
  return s === void 0 && (s = createTestStore(), registry.set(key, s)), s;
348
- })(), promote = (doc) => {
650
+ })(), promote2 = (doc) => {
349
651
  const now = (/* @__PURE__ */ new Date()).toISOString();
350
652
  if (typeof doc._id != "string" || doc._id.length === 0)
351
653
  throw new Error("document is missing _id");
@@ -400,7 +702,7 @@ function makeHandle(registry, projectId, dataset, accessControl) {
400
702
  switch (mut.kind) {
401
703
  case "create": {
402
704
  remember(mut.doc._id);
403
- const promoted = promote(mut.doc);
705
+ const promoted = promote2(mut.doc);
404
706
  if (store.get(promoted._id) !== void 0)
405
707
  throw new Error(`create failed: document ${promoted._id} already exists`);
406
708
  await gate(promoted, "create"), store.put(promoted), touchedIds.push(promoted._id), operations.push({ id: promoted._id, operation: "create" });
@@ -408,7 +710,7 @@ function makeHandle(registry, projectId, dataset, accessControl) {
408
710
  }
409
711
  case "createOrReplace": {
410
712
  remember(mut.doc._id);
411
- const promoted = promote(mut.doc), existing = store.get(promoted._id);
713
+ const promoted = promote2(mut.doc), existing = store.get(promoted._id);
412
714
  await gate(existing ?? promoted, existing !== void 0 ? "update" : "create");
413
715
  const wasPresent = existing !== void 0;
414
716
  store.put(promoted), touchedIds.push(promoted._id), operations.push({ id: promoted._id, operation: wasPresent ? "update" : "create" });
@@ -416,7 +718,7 @@ function makeHandle(registry, projectId, dataset, accessControl) {
416
718
  }
417
719
  case "createIfNotExists": {
418
720
  if (remember(mut.doc._id), store.get(mut.doc._id) === void 0) {
419
- const promoted = promote(mut.doc);
721
+ const promoted = promote2(mut.doc);
420
722
  await gate(promoted, "create"), store.put(promoted), touchedIds.push(promoted._id), operations.push({ id: promoted._id, operation: "create" });
421
723
  }
422
724
  break;
@@ -445,50 +747,63 @@ function makeHandle(registry, projectId, dataset, accessControl) {
445
747
  transactionId: cryptoRandomKey(),
446
748
  results: operations
447
749
  };
448
- }, visibleDocs = async () => {
750
+ }, accessVisibleDocs = async () => {
449
751
  const all = store.all();
450
752
  if (accessControl === void 0) return all;
451
753
  const out = [];
452
754
  for (const doc of all)
453
755
  await canRead(doc) && out.push(doc);
454
756
  return out;
757
+ }, visibleDocs = async (override) => {
758
+ const accessible = await accessVisibleDocs();
759
+ return applyPerspective(accessible, override ?? perspective);
455
760
  };
456
761
  return {
457
- fetch: async (query, params) => {
762
+ fetch: async (query, params, opts) => {
458
763
  const tree = parse(query, params ? { params } : void 0);
459
764
  return await (await evaluate(tree, {
460
- dataset: await visibleDocs(),
765
+ dataset: await visibleDocs(opts?.perspective),
461
766
  ...params !== void 0 ? { params } : {}
462
767
  })).get();
463
768
  },
464
- getDocument: async (id) => {
465
- const doc = store.get(id);
466
- return doc === void 0 || !await canRead(doc) ? null : doc;
467
- },
468
- getDocuments: async (ids) => {
469
- const result = [];
470
- for (const id of ids) {
769
+ getDocument: async (id, opts) => {
770
+ const effective = opts?.perspective ?? perspective;
771
+ if (effective === "raw") {
471
772
  const doc = store.get(id);
472
- doc === void 0 ? result.push(null) : await canRead(doc) ? result.push(doc) : result.push(null);
773
+ return doc === void 0 || !await canRead(doc) ? null : doc;
473
774
  }
474
- return result;
775
+ const view = await visibleDocs(effective);
776
+ return resolveDocument(view, id, effective) ?? null;
777
+ },
778
+ getDocuments: async (ids, opts) => {
779
+ const effective = opts?.perspective ?? perspective;
780
+ if (effective === "raw") {
781
+ const result = [];
782
+ for (const id of ids) {
783
+ const doc = store.get(id);
784
+ doc === void 0 ? result.push(null) : await canRead(doc) ? result.push(doc) : result.push(null);
785
+ }
786
+ return result;
787
+ }
788
+ const view = await visibleDocs(effective);
789
+ return ids.map((id) => resolveDocument(view, id, effective) ?? null);
475
790
  },
476
791
  create: async (doc, opts) => {
477
792
  if (checkRevision(doc._id, opts?.ifRevisionId), store.get(doc._id) !== void 0)
478
793
  throw new Error(`create failed: document ${doc._id} already exists`);
479
- const promoted = promote(doc);
794
+ const promoted = promote2(doc);
480
795
  return await gate(promoted, "create"), store.put(promoted), promoted;
481
796
  },
482
797
  createOrReplace: async (doc, opts) => {
483
798
  checkRevision(doc._id, opts?.ifRevisionId);
484
- const promoted = promote(doc), existing = store.get(doc._id);
799
+ const promoted = promote2(doc), existing = store.get(doc._id);
485
800
  return await gate(existing ?? promoted, existing !== void 0 ? "update" : "create"), store.put(promoted), promoted;
486
801
  },
487
802
  createIfNotExists: async (doc, opts) => {
488
803
  checkRevision(doc._id, opts?.ifRevisionId);
489
804
  const existing = store.get(doc._id);
490
805
  if (existing !== void 0) return existing;
491
- const promoted = promote(doc);
806
+ const promoted = promote2(doc);
492
807
  return await gate(promoted, "create"), store.put(promoted), promoted;
493
808
  },
494
809
  delete: async (id, opts) => {
@@ -498,9 +813,57 @@ function makeHandle(registry, projectId, dataset, accessControl) {
498
813
  },
499
814
  patch: (documentId) => createPatch(documentId, { apply: applyPatch }),
500
815
  transaction: () => createTransaction({ apply: applyTransaction }),
816
+ action: async (action) => applyActions(store, action),
817
+ releases: {
818
+ create: async ({ releaseId, metadata }) => {
819
+ const result = applyActions(store, {
820
+ actionType: "sanity.action.release.create",
821
+ releaseId,
822
+ ...metadata !== void 0 ? { metadata } : {}
823
+ }), doc = store.get(`_.releases.${releaseId}`);
824
+ return {
825
+ ...result,
826
+ releaseId,
827
+ metadata: doc?.metadata ?? { releaseType: "undecided" }
828
+ };
829
+ },
830
+ edit: async ({ releaseId, patch }) => applyActions(store, { actionType: "sanity.action.release.edit", releaseId, patch }),
831
+ get: async ({ releaseId }) => store.get(`_.releases.${releaseId}`),
832
+ fetchDocuments: async ({ releaseId }) => {
833
+ const prefix = `versions.${releaseId}.`;
834
+ return store.all().filter((d) => d._id.startsWith(prefix));
835
+ },
836
+ schedule: async ({ releaseId, publishAt }) => applyActions(store, {
837
+ actionType: "sanity.action.release.schedule",
838
+ releaseId,
839
+ publishAt
840
+ }),
841
+ unschedule: async ({ releaseId }) => applyActions(store, { actionType: "sanity.action.release.unschedule", releaseId }),
842
+ publish: async ({ releaseId }) => applyActions(store, { actionType: "sanity.action.release.publish", releaseId }),
843
+ archive: async ({ releaseId }) => applyActions(store, { actionType: "sanity.action.release.archive", releaseId }),
844
+ unarchive: async ({ releaseId }) => applyActions(store, { actionType: "sanity.action.release.unarchive", releaseId }),
845
+ delete: async ({ releaseId }) => applyActions(store, { actionType: "sanity.action.release.delete", releaseId })
846
+ },
847
+ createVersion: async ({ releaseId, publishedId, document }) => applyActions(store, {
848
+ actionType: "sanity.action.document.version.create",
849
+ releaseId,
850
+ publishedId,
851
+ document
852
+ }),
853
+ unpublishVersion: async ({ releaseId, publishedId }) => applyActions(store, {
854
+ actionType: "sanity.action.document.version.unpublish",
855
+ versionId: `versions.${releaseId}.${publishedId}`,
856
+ publishedId
857
+ }),
501
858
  ...accessControl !== void 0 ? { request: makeRequestImpl(accessControl, projectId, dataset) } : {},
502
- config: () => ({ projectId, dataset }),
503
- withConfig: (config) => makeHandle(registry, config.projectId ?? projectId, config.dataset ?? dataset, accessControl),
859
+ config: () => ({ projectId, dataset, perspective }),
860
+ withConfig: (config) => makeHandle(
861
+ registry,
862
+ config.projectId ?? projectId,
863
+ config.dataset ?? dataset,
864
+ accessControl,
865
+ config.perspective ?? perspective
866
+ ),
504
867
  seed: (documents) => store.seed(documents),
505
868
  add: (documents) => store.add(documents),
506
869
  reset: () => store.reset(),
@@ -512,11 +875,18 @@ function makeHandle(registry, projectId, dataset, accessControl) {
512
875
  export {
513
876
  WriteAccessDeniedError,
514
877
  applyPatchOps,
878
+ applyPerspective,
515
879
  createPatch,
516
880
  createTestClient,
517
881
  createTestStore,
518
882
  createTransaction,
519
883
  defaultAclPath,
520
- evaluateWriteAccess
884
+ evaluateWriteAccess,
885
+ isDraftId,
886
+ isReleaseDocId,
887
+ isSystemId,
888
+ isVersionId,
889
+ publishedIdOf,
890
+ releaseNameOf
521
891
  };
522
892
  //# sourceMappingURL=index.js.map