@preprio/toolkit 0.5.0-rc.5 → 0.5.0-rc.6

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.
Files changed (38) hide show
  1. package/README.md +125 -26
  2. package/dist/astro/index.cjs +126 -30
  3. package/dist/astro/index.js +130 -30
  4. package/dist/{chunk-TG6N3C22.js → chunk-IPMB32AZ.js} +1 -1
  5. package/dist/{chunk-74KY5EAM.js → chunk-KD3GK6TO.js} +2 -0
  6. package/dist/{chunk-3X55JL52.js → chunk-KZIRM54F.js} +1 -1
  7. package/dist/{chunk-SBLZANL5.js → chunk-L3SDL6MP.js} +11 -3
  8. package/dist/{chunk-CMUFTCG3.js → chunk-WHZNCYUS.js} +1 -1
  9. package/dist/codegen/index.cjs +79 -0
  10. package/dist/codegen/index.d.cts +46 -0
  11. package/dist/codegen/index.d.ts +46 -0
  12. package/dist/codegen/index.js +62 -0
  13. package/dist/index.cjs +133 -28
  14. package/dist/index.d.cts +14 -2
  15. package/dist/index.d.ts +14 -2
  16. package/dist/index.js +108 -6
  17. package/dist/{live-preview-H4643QYF.js → live-preview-DLXGXRE5.js} +2 -2
  18. package/dist/nextjs/components.cjs +11 -3
  19. package/dist/nextjs/components.js +2 -2
  20. package/dist/nextjs/index.cjs +124 -28
  21. package/dist/nextjs/index.js +128 -28
  22. package/dist/nextjs/live-action.js +3 -3
  23. package/dist/nextjs/live.js +1 -1
  24. package/dist/nuxt/chunk-BWPGO25E.js +340 -0
  25. package/dist/nuxt/index.cjs +1 -1
  26. package/dist/nuxt/index.js +1 -1
  27. package/dist/nuxt/{live-preview-O2ZTJNW4.js → live-preview-2RCU35W5.js} +6 -337
  28. package/dist/nuxt/live.cjs +1007 -911
  29. package/dist/nuxt/live.js +109 -5
  30. package/dist/react/components.cjs +11 -3
  31. package/dist/react/components.js +11 -3
  32. package/dist/sveltekit/chunk-BWPGO25E.js +340 -0
  33. package/dist/sveltekit/index.cjs +1 -1
  34. package/dist/sveltekit/index.js +1 -1
  35. package/dist/sveltekit/{live-preview-O2ZTJNW4.js → live-preview-2RCU35W5.js} +6 -337
  36. package/dist/sveltekit/live.cjs +1008 -909
  37. package/dist/sveltekit/live.js +118 -11
  38. package/package.json +11 -1
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(src_exports, {
23
23
  PreprError: () => PreprError,
24
24
  PreprToolbarElement: () => PreprToolbarElement,
25
25
  VERSION: () => VERSION,
26
+ annotateEditLinks: () => annotateEditLinks,
26
27
  createPreprPreview: () => createPreprPreview,
27
28
  createToolbarStore: () => createToolbarStore,
28
29
  definePreprToolbar: () => definePreprToolbar,
@@ -49,7 +50,7 @@ __export(src_exports, {
49
50
  module.exports = __toCommonJS(src_exports);
50
51
 
51
52
  // src/version.ts
52
- var VERSION = "0.5.0-rc.5";
53
+ var VERSION = "0.5.0-rc.6";
53
54
 
54
55
  // src/core/features.ts
55
56
  function isEnabled(config) {
@@ -570,6 +571,7 @@ function decodeStega(str) {
570
571
 
571
572
  // src/core/stega/dom.ts
572
573
  var IGNORED_ANCESTORS = "script, style, noscript";
574
+ var AUTO_ENTRY_ATTR = "data-prepr-entry-auto";
573
575
  function isIgnoredTextNode(node) {
574
576
  if (node.parentElement?.closest(IGNORED_ANCESTORS)) return true;
575
577
  return !node.textContent?.trim();
@@ -603,7 +605,9 @@ function parseEditHref(href) {
603
605
  return {
604
606
  id: url.pathname.split("/").pop() || void 0,
605
607
  field: url.searchParams.get("field") || void 0,
606
- locale: url.searchParams.get("locale") || void 0
608
+ locale: url.searchParams.get("locale") || void 0,
609
+ // Added by the toolkit's `annotateEditLinks`, not by Prepr.
610
+ entry: url.searchParams.get("entry") || void 0
607
611
  };
608
612
  } catch {
609
613
  return {};
@@ -620,11 +624,16 @@ function tagEncodedElement(element, decoded) {
620
624
  element.setAttribute("data-prepr-encoded", "");
621
625
  element.setAttribute("data-prepr-href", decoded.href);
622
626
  element.setAttribute("data-prepr-origin", decoded.origin);
623
- const { id, field, locale } = parseEditHref(decoded.href);
627
+ const { id, field, locale, entry } = parseEditHref(decoded.href);
624
628
  setOrRemove(element, "data-prepr-id", id);
625
629
  const fieldHint = element.getAttribute("data-prepr-edit-target")?.trim() || void 0;
626
630
  setOrRemove(element, "data-prepr-field", field ?? fieldHint);
627
631
  setOrRemove(element, "data-prepr-locale", locale);
632
+ const siteEntry = element.hasAttribute("data-prepr-entry-id") && !element.hasAttribute(AUTO_ENTRY_ATTR);
633
+ if (!siteEntry) {
634
+ setOrRemove(element, "data-prepr-entry-id", entry);
635
+ setOrRemove(element, AUTO_ENTRY_ATTR, entry && "true");
636
+ }
628
637
  }
629
638
  function readEditPayload(element) {
630
639
  return {
@@ -3593,14 +3602,14 @@ function indexSnapshot(data) {
3593
3602
  const index = /* @__PURE__ */ new Map();
3594
3603
  const visited = /* @__PURE__ */ new WeakSet();
3595
3604
  const path = [];
3596
- function walk(node) {
3605
+ function walk2(node) {
3597
3606
  if (node === null || typeof node !== "object") return;
3598
3607
  if (visited.has(node)) return;
3599
3608
  visited.add(node);
3600
3609
  if (Array.isArray(node)) {
3601
3610
  for (let i2 = 0; i2 < node.length; i2++) {
3602
3611
  path.push(i2);
3603
- walk(node[i2]);
3612
+ walk2(node[i2]);
3604
3613
  path.pop();
3605
3614
  }
3606
3615
  return;
@@ -3618,11 +3627,11 @@ function indexSnapshot(data) {
3618
3627
  for (const key in record) {
3619
3628
  if (!Object.prototype.hasOwnProperty.call(record, key)) continue;
3620
3629
  path.push(key);
3621
- walk(record[key]);
3630
+ walk2(record[key]);
3622
3631
  path.pop();
3623
3632
  }
3624
3633
  }
3625
- walk(data);
3634
+ walk2(data);
3626
3635
  return index;
3627
3636
  }
3628
3637
 
@@ -4161,12 +4170,12 @@ function resolveFields(selections, typename, doc, options) {
4161
4170
  }
4162
4171
  };
4163
4172
  const applies = (on, list) => conditionMatches(on, typename) || typename !== void 0 && typeConditions(list, doc).includes(typename);
4164
- const walk = (list, matched) => {
4173
+ const walk2 = (list, matched) => {
4165
4174
  for (const s2 of list) {
4166
4175
  if (s2.kind === "field") {
4167
4176
  add(matched ? exact : loose, s2);
4168
4177
  } else if (s2.kind === "inline") {
4169
- walk(s2.selections, matched && applies(s2.on, s2.selections));
4178
+ walk2(s2.selections, matched && applies(s2.on, s2.selections));
4170
4179
  } else {
4171
4180
  const fragment = doc.fragments.get(s2.name);
4172
4181
  if (!fragment) continue;
@@ -4174,11 +4183,11 @@ function resolveFields(selections, typename, doc, options) {
4174
4183
  if (walkedMatched.has(fragment)) continue;
4175
4184
  if (!asMatched && walkedLoose.has(fragment)) continue;
4176
4185
  (asMatched ? walkedMatched : walkedLoose).add(fragment);
4177
- walk(fragment.selections, asMatched);
4186
+ walk2(fragment.selections, asMatched);
4178
4187
  }
4179
4188
  }
4180
4189
  };
4181
- walk(selections, true);
4190
+ walk2(selections, true);
4182
4191
  if (!options.strict) {
4183
4192
  for (const [key, field] of loose) {
4184
4193
  if (!exact.has(key)) exact.set(key, field);
@@ -4195,21 +4204,21 @@ function typeConditions(selections, doc) {
4195
4204
  if (hit) return hit;
4196
4205
  const names = /* @__PURE__ */ new Set();
4197
4206
  const seen = /* @__PURE__ */ new Set();
4198
- const walk = (list) => {
4207
+ const walk2 = (list) => {
4199
4208
  for (const s2 of list) {
4200
4209
  if (s2.kind === "inline") {
4201
4210
  if (s2.on) names.add(s2.on);
4202
- walk(s2.selections);
4211
+ walk2(s2.selections);
4203
4212
  } else if (s2.kind === "spread") {
4204
4213
  const fragment = doc.fragments.get(s2.name);
4205
4214
  if (!fragment || seen.has(fragment)) continue;
4206
4215
  seen.add(fragment);
4207
4216
  names.add(fragment.on);
4208
- walk(fragment.selections);
4217
+ walk2(fragment.selections);
4209
4218
  }
4210
4219
  }
4211
4220
  };
4212
- walk(selections);
4221
+ walk2(selections);
4213
4222
  const conditions = [...names];
4214
4223
  conditionsCache.set(selections, conditions);
4215
4224
  return conditions;
@@ -4504,7 +4513,7 @@ function resolveTargets(root, doc, ownerPath, segments) {
4504
4513
  let current = root;
4505
4514
  const out = [];
4506
4515
  const plain = /* @__PURE__ */ new Set();
4507
- const walk = (node, sel, i2, path) => {
4516
+ const walk2 = (node, sel, i2, path) => {
4508
4517
  if (i2 === segments.length) {
4509
4518
  out.push(path);
4510
4519
  return;
@@ -4514,10 +4523,10 @@ function resolveTargets(root, doc, ownerPath, segments) {
4514
4523
  if (INDEX_SEGMENT3.test(segment)) {
4515
4524
  const position = Number(segment);
4516
4525
  if (position < node.length) {
4517
- walk(node[position], sel, i2 + 1, [...path, position]);
4526
+ walk2(node[position], sel, i2 + 1, [...path, position]);
4518
4527
  }
4519
4528
  } else if (segment === "items") {
4520
- walk(node, sel, i2 + 1, path);
4529
+ walk2(node, sel, i2 + 1, path);
4521
4530
  }
4522
4531
  return;
4523
4532
  }
@@ -4550,11 +4559,11 @@ function resolveTargets(root, doc, ownerPath, segments) {
4550
4559
  current = writeAtPath(current, fieldPath, value);
4551
4560
  }
4552
4561
  }
4553
- walk(value, field.selections, i2 + 1, fieldPath);
4562
+ walk2(value, field.selections, i2 + 1, fieldPath);
4554
4563
  }
4555
- if (!matched && segment === "items") walk(node, sel, i2 + 1, path);
4564
+ if (!matched && segment === "items") walk2(node, sel, i2 + 1, path);
4556
4565
  };
4557
- walk(owner, selections, 0, ownerPath);
4566
+ walk2(owner, selections, 0, ownerPath);
4558
4567
  return { root: current, targets: out, plain };
4559
4568
  }
4560
4569
  var NON_ALPHANUMERIC = /[^a-z0-9]/gi;
@@ -4677,11 +4686,11 @@ function listKeysIn(result) {
4677
4686
  if (!byType) {
4678
4687
  byType = /* @__PURE__ */ new Map();
4679
4688
  const seen = /* @__PURE__ */ new WeakSet();
4680
- const walk = (node) => {
4689
+ const walk2 = (node) => {
4681
4690
  if (node === null || typeof node !== "object" || seen.has(node)) return;
4682
4691
  seen.add(node);
4683
4692
  if (Array.isArray(node)) {
4684
- node.forEach(walk);
4693
+ node.forEach(walk2);
4685
4694
  return;
4686
4695
  }
4687
4696
  const record = node;
@@ -4692,10 +4701,10 @@ function listKeysIn(result) {
4692
4701
  if (!keys) byType.set(type, keys = /* @__PURE__ */ new Set());
4693
4702
  keys.add(key);
4694
4703
  }
4695
- walk(value);
4704
+ walk2(value);
4696
4705
  }
4697
4706
  };
4698
- walk(result);
4707
+ walk2(result);
4699
4708
  listKeysCache.set(result, byType);
4700
4709
  }
4701
4710
  return byType.get(typename) ?? NO_KEYS;
@@ -4737,7 +4746,7 @@ function findEditLink(root) {
4737
4746
  if (linkCache.has(root)) return linkCache.get(root) ?? null;
4738
4747
  let found = null;
4739
4748
  const seen = /* @__PURE__ */ new WeakSet();
4740
- const walk = (node) => {
4749
+ const walk2 = (node) => {
4741
4750
  if (found) return;
4742
4751
  if (typeof node === "string") {
4743
4752
  if (!(0, import_stega6.vercelStegaSplit)(node).encoded) return;
@@ -4752,13 +4761,108 @@ function findEditLink(root) {
4752
4761
  }
4753
4762
  if (node === null || typeof node !== "object" || seen.has(node)) return;
4754
4763
  seen.add(node);
4755
- for (const value of Object.values(node)) walk(value);
4764
+ for (const value of Object.values(node)) walk2(value);
4756
4765
  };
4757
- walk(root);
4766
+ walk2(root);
4758
4767
  linkCache.set(root, found);
4759
4768
  return found;
4760
4769
  }
4761
4770
 
4771
+ // src/core/live-preview/edit-links.ts
4772
+ var import_stega7 = require("@vercel/stega");
4773
+ var ENTRY_PARAM = "entry";
4774
+ var TOP_LEVEL_ENTRY = /^[A-Za-z0-9_]+\.\d+$/;
4775
+ function entryIdOf(element) {
4776
+ if (!isRecord(element)) return void 0;
4777
+ if (typeof element._id === "string") return element._id;
4778
+ if (element.__typename !== "Assets" || !Array.isArray(element.items)) {
4779
+ return void 0;
4780
+ }
4781
+ const first = element.items[0];
4782
+ return isRecord(first) && typeof first._id === "string" ? first._id : void 0;
4783
+ }
4784
+ function isRecord(value) {
4785
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4786
+ }
4787
+ function annotate(value, ctx) {
4788
+ if (!ctx.entryId) return value;
4789
+ const { cleaned, encoded } = (0, import_stega7.vercelStegaSplit)(value);
4790
+ if (!encoded) return value;
4791
+ let payload;
4792
+ try {
4793
+ payload = (0, import_stega7.vercelStegaDecode)(encoded);
4794
+ } catch {
4795
+ return value;
4796
+ }
4797
+ if (typeof payload?.href !== "string") return value;
4798
+ let url;
4799
+ try {
4800
+ url = new URL(payload.href);
4801
+ } catch {
4802
+ return value;
4803
+ }
4804
+ if (url.searchParams.has(ENTRY_PARAM)) return value;
4805
+ url.searchParams.set(ENTRY_PARAM, ctx.entryId);
4806
+ const itemId = decodeURIComponent(url.pathname.split("/").pop() ?? "");
4807
+ if (!url.searchParams.has("field") && ctx.entryField && TOP_LEVEL_ENTRY.test(ctx.entryField) && itemId === ctx.entryOwnerId) {
4808
+ url.searchParams.set("field", ctx.entryField);
4809
+ }
4810
+ return (0, import_stega7.vercelStegaCombine)(
4811
+ cleaned,
4812
+ { ...payload, href: url.toString() },
4813
+ false
4814
+ );
4815
+ }
4816
+ function walk(value, selections, ctx) {
4817
+ if (typeof value === "string") return annotate(value, ctx);
4818
+ if (Array.isArray(value)) {
4819
+ let changed2 = false;
4820
+ const next2 = value.map((element, index) => {
4821
+ const path = [...ctx.path, String(index)];
4822
+ const entryId = ctx.inAssets ? void 0 : entryIdOf(element);
4823
+ const entry = entryId ? {
4824
+ entryId,
4825
+ entryField: ctx.ownerId ? path.join(".") : void 0,
4826
+ entryOwnerId: ctx.ownerId,
4827
+ inAssets: isRecord(element) && element._id === void 0
4828
+ } : {};
4829
+ const out = walk(element, selections, { ...ctx, ...entry, path });
4830
+ if (out !== element) changed2 = true;
4831
+ return out;
4832
+ });
4833
+ return changed2 ? next2 : value;
4834
+ }
4835
+ if (!isRecord(value)) return value;
4836
+ const id = typeof value._id === "string" ? value._id : void 0;
4837
+ const typename = typeof value.__typename === "string" ? value.__typename : void 0;
4838
+ const fields = ctx.doc && selections ? collectFields(selections, typename, ctx.doc) : null;
4839
+ let changed = false;
4840
+ const next = {};
4841
+ for (const [key, child] of Object.entries(value)) {
4842
+ const field = fields?.get(key);
4843
+ const name = field?.name ?? key;
4844
+ const out = walk(child, field?.selections ?? null, {
4845
+ ...ctx,
4846
+ ownerId: id ?? ctx.ownerId,
4847
+ path: id ? [name] : [...ctx.path, name]
4848
+ });
4849
+ if (out !== child) changed = true;
4850
+ next[key] = out;
4851
+ }
4852
+ return changed ? next : value;
4853
+ }
4854
+ function annotateEditLinks(result, query, operationName) {
4855
+ let doc = null;
4856
+ if (query !== void 0) {
4857
+ try {
4858
+ doc = readQueryDocument(query, operationName);
4859
+ } catch {
4860
+ doc = null;
4861
+ }
4862
+ }
4863
+ return walk(result, doc?.operation ?? null, { doc, path: [] });
4864
+ }
4865
+
4762
4866
  // src/core/live-preview/bridge-events.ts
4763
4867
  var debug12 = createScopedLogger("live-preview:bridge");
4764
4868
  function createLiveBridge(store, options = {}) {
@@ -5530,6 +5634,7 @@ function setTrackingParam(key, value) {
5530
5634
  PreprError,
5531
5635
  PreprToolbarElement,
5532
5636
  VERSION,
5637
+ annotateEditLinks,
5533
5638
  createPreprPreview,
5534
5639
  createToolbarStore,
5535
5640
  definePreprToolbar,
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j
5
5
  * Package version; must match package.json. Lives in its own leaf module so
6
6
  * core can import it without cycling through the barrel.
7
7
  */
8
- declare const VERSION = "0.5.0-rc.5";
8
+ declare const VERSION = "0.5.0-rc.6";
9
9
 
10
10
  /**
11
11
  * Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
@@ -355,6 +355,18 @@ declare function parsePatchLog(raw: unknown): LivePatch[];
355
355
  */
356
356
  declare function normalizeQuery<T>(query: QueryInput, result: T, options?: NormalizeQueryOptions): T;
357
357
 
358
+ /**
359
+ * Adds the list entry each piece of text belongs to (a dynamic content
360
+ * entry, a repeated component) to its hidden edit link, so the visual editor
361
+ * can find the entry by id without `data-prepr-entry-id` on the page. A
362
+ * `Text` entry's link, which names no field, also gets the entry's field
363
+ * path, so `data-prepr-edit-target` is not needed to name it.
364
+ *
365
+ * Needs `_id` on the entries. Returns the result itself when nothing
366
+ * changed. Pass the query to name fields by their API name under aliases.
367
+ */
368
+ declare function annotateEditLinks<T>(result: T, query?: QueryInput, operationName?: string): T;
369
+
358
370
  interface HtmlLivePreviewOptions {
359
371
  /**
360
372
  * Replace the `*.prepr.io` editor-origin wildcard with an exact list, as
@@ -476,4 +488,4 @@ interface PreprMiddlewareOptions {
476
488
  */
477
489
  declare function processPreprRequest(request: Request, opts?: PreprMiddlewareOptions): PreprMiddlewareResult;
478
490
 
479
- export { type CookieSpec, type CreatePreprPreviewOptions, type HtmlLivePreview, type HtmlLivePreviewOptions, type IframeBridgeOptions, type LivePatch, type NormalizeQueryOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, type PreviewAttrsOptions, type PreviewAttrsTarget, type QueryInput, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, normalizeQuery, parsePatchLog, previewAttrs, processPreprRequest, readLiveLogFromRequest, resolveFeatures, startHtmlLivePreview, stegaClean, validatePreprToken };
491
+ export { type CookieSpec, type CreatePreprPreviewOptions, type HtmlLivePreview, type HtmlLivePreviewOptions, type IframeBridgeOptions, type LivePatch, type NormalizeQueryOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, type PreviewAttrsOptions, type PreviewAttrsTarget, type QueryInput, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, annotateEditLinks, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, normalizeQuery, parsePatchLog, previewAttrs, processPreprRequest, readLiveLogFromRequest, resolveFeatures, startHtmlLivePreview, stegaClean, validatePreprToken };
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j
5
5
  * Package version; must match package.json. Lives in its own leaf module so
6
6
  * core can import it without cycling through the barrel.
7
7
  */
8
- declare const VERSION = "0.5.0-rc.5";
8
+ declare const VERSION = "0.5.0-rc.6";
9
9
 
10
10
  /**
11
11
  * Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
@@ -355,6 +355,18 @@ declare function parsePatchLog(raw: unknown): LivePatch[];
355
355
  */
356
356
  declare function normalizeQuery<T>(query: QueryInput, result: T, options?: NormalizeQueryOptions): T;
357
357
 
358
+ /**
359
+ * Adds the list entry each piece of text belongs to (a dynamic content
360
+ * entry, a repeated component) to its hidden edit link, so the visual editor
361
+ * can find the entry by id without `data-prepr-entry-id` on the page. A
362
+ * `Text` entry's link, which names no field, also gets the entry's field
363
+ * path, so `data-prepr-edit-target` is not needed to name it.
364
+ *
365
+ * Needs `_id` on the entries. Returns the result itself when nothing
366
+ * changed. Pass the query to name fields by their API name under aliases.
367
+ */
368
+ declare function annotateEditLinks<T>(result: T, query?: QueryInput, operationName?: string): T;
369
+
358
370
  interface HtmlLivePreviewOptions {
359
371
  /**
360
372
  * Replace the `*.prepr.io` editor-origin wildcard with an exact list, as
@@ -476,4 +488,4 @@ interface PreprMiddlewareOptions {
476
488
  */
477
489
  declare function processPreprRequest(request: Request, opts?: PreprMiddlewareOptions): PreprMiddlewareResult;
478
490
 
479
- export { type CookieSpec, type CreatePreprPreviewOptions, type HtmlLivePreview, type HtmlLivePreviewOptions, type IframeBridgeOptions, type LivePatch, type NormalizeQueryOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, type PreviewAttrsOptions, type PreviewAttrsTarget, type QueryInput, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, normalizeQuery, parsePatchLog, previewAttrs, processPreprRequest, readLiveLogFromRequest, resolveFeatures, startHtmlLivePreview, stegaClean, validatePreprToken };
491
+ export { type CookieSpec, type CreatePreprPreviewOptions, type HtmlLivePreview, type HtmlLivePreviewOptions, type IframeBridgeOptions, type LivePatch, type NormalizeQueryOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, type PreviewAttrsOptions, type PreviewAttrsTarget, type QueryInput, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, annotateEditLinks, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, normalizeQuery, parsePatchLog, previewAttrs, processPreprRequest, readLiveLogFromRequest, resolveFeatures, startHtmlLivePreview, stegaClean, validatePreprToken };
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  setTrackingParam,
18
18
  stegaClean,
19
19
  trackEvent
20
- } from "./chunk-SBLZANL5.js";
20
+ } from "./chunk-L3SDL6MP.js";
21
21
  import {
22
22
  PreprError,
23
23
  extractAccessToken,
@@ -28,20 +28,22 @@ import {
28
28
  getPreprUUIDFromHeaders,
29
29
  getToolbarPropsFromHeaders,
30
30
  validatePreprToken
31
- } from "./chunk-3X55JL52.js";
31
+ } from "./chunk-KZIRM54F.js";
32
32
  import {
33
33
  VERSION,
34
34
  resolveFeatures
35
- } from "./chunk-CMUFTCG3.js";
35
+ } from "./chunk-WHZNCYUS.js";
36
36
  import {
37
37
  createLiveBridge,
38
38
  createLiveStore
39
- } from "./chunk-TG6N3C22.js";
39
+ } from "./chunk-IPMB32AZ.js";
40
40
  import "./chunk-URXSDJOU.js";
41
41
  import {
42
+ collectFields,
42
43
  normalizeQuery,
43
- parsePatchLog
44
- } from "./chunk-74KY5EAM.js";
44
+ parsePatchLog,
45
+ readQueryDocument
46
+ } from "./chunk-KD3GK6TO.js";
45
47
  import {
46
48
  createScopedLogger
47
49
  } from "./chunk-S3VT4UWM.js";
@@ -59,6 +61,105 @@ function previewAttrs(target, options) {
59
61
  return attrs;
60
62
  }
61
63
 
64
+ // src/core/live-preview/edit-links.ts
65
+ import {
66
+ vercelStegaCombine,
67
+ vercelStegaDecode,
68
+ vercelStegaSplit
69
+ } from "@vercel/stega";
70
+ var ENTRY_PARAM = "entry";
71
+ var TOP_LEVEL_ENTRY = /^[A-Za-z0-9_]+\.\d+$/;
72
+ function entryIdOf(element) {
73
+ if (!isRecord(element)) return void 0;
74
+ if (typeof element._id === "string") return element._id;
75
+ if (element.__typename !== "Assets" || !Array.isArray(element.items)) {
76
+ return void 0;
77
+ }
78
+ const first = element.items[0];
79
+ return isRecord(first) && typeof first._id === "string" ? first._id : void 0;
80
+ }
81
+ function isRecord(value) {
82
+ return typeof value === "object" && value !== null && !Array.isArray(value);
83
+ }
84
+ function annotate(value, ctx) {
85
+ if (!ctx.entryId) return value;
86
+ const { cleaned, encoded } = vercelStegaSplit(value);
87
+ if (!encoded) return value;
88
+ let payload;
89
+ try {
90
+ payload = vercelStegaDecode(encoded);
91
+ } catch {
92
+ return value;
93
+ }
94
+ if (typeof payload?.href !== "string") return value;
95
+ let url;
96
+ try {
97
+ url = new URL(payload.href);
98
+ } catch {
99
+ return value;
100
+ }
101
+ if (url.searchParams.has(ENTRY_PARAM)) return value;
102
+ url.searchParams.set(ENTRY_PARAM, ctx.entryId);
103
+ const itemId = decodeURIComponent(url.pathname.split("/").pop() ?? "");
104
+ if (!url.searchParams.has("field") && ctx.entryField && TOP_LEVEL_ENTRY.test(ctx.entryField) && itemId === ctx.entryOwnerId) {
105
+ url.searchParams.set("field", ctx.entryField);
106
+ }
107
+ return vercelStegaCombine(
108
+ cleaned,
109
+ { ...payload, href: url.toString() },
110
+ false
111
+ );
112
+ }
113
+ function walk(value, selections, ctx) {
114
+ if (typeof value === "string") return annotate(value, ctx);
115
+ if (Array.isArray(value)) {
116
+ let changed2 = false;
117
+ const next2 = value.map((element, index) => {
118
+ const path = [...ctx.path, String(index)];
119
+ const entryId = ctx.inAssets ? void 0 : entryIdOf(element);
120
+ const entry = entryId ? {
121
+ entryId,
122
+ entryField: ctx.ownerId ? path.join(".") : void 0,
123
+ entryOwnerId: ctx.ownerId,
124
+ inAssets: isRecord(element) && element._id === void 0
125
+ } : {};
126
+ const out = walk(element, selections, { ...ctx, ...entry, path });
127
+ if (out !== element) changed2 = true;
128
+ return out;
129
+ });
130
+ return changed2 ? next2 : value;
131
+ }
132
+ if (!isRecord(value)) return value;
133
+ const id = typeof value._id === "string" ? value._id : void 0;
134
+ const typename = typeof value.__typename === "string" ? value.__typename : void 0;
135
+ const fields = ctx.doc && selections ? collectFields(selections, typename, ctx.doc) : null;
136
+ let changed = false;
137
+ const next = {};
138
+ for (const [key, child] of Object.entries(value)) {
139
+ const field = fields?.get(key);
140
+ const name = field?.name ?? key;
141
+ const out = walk(child, field?.selections ?? null, {
142
+ ...ctx,
143
+ ownerId: id ?? ctx.ownerId,
144
+ path: id ? [name] : [...ctx.path, name]
145
+ });
146
+ if (out !== child) changed = true;
147
+ next[key] = out;
148
+ }
149
+ return changed ? next : value;
150
+ }
151
+ function annotateEditLinks(result, query, operationName) {
152
+ let doc = null;
153
+ if (query !== void 0) {
154
+ try {
155
+ doc = readQueryDocument(query, operationName);
156
+ } catch {
157
+ doc = null;
158
+ }
159
+ }
160
+ return walk(result, doc?.operation ?? null, { doc, path: [] });
161
+ }
162
+
62
163
  // src/core/live-preview/morph.ts
63
164
  function morphElement(current, next) {
64
165
  syncAttributes(current, next);
@@ -376,6 +477,7 @@ export {
376
477
  PreprError,
377
478
  PreprToolbarElement,
378
479
  VERSION,
480
+ annotateEditLinks,
379
481
  createPreprPreview,
380
482
  createToolbarStore,
381
483
  definePreprToolbar,
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  createLiveBridge,
3
3
  createLiveStore
4
- } from "./chunk-TG6N3C22.js";
4
+ } from "./chunk-IPMB32AZ.js";
5
5
  import "./chunk-URXSDJOU.js";
6
6
  import {
7
7
  applyPatchLog,
8
8
  normalizeQuery,
9
9
  parsePatchLog
10
- } from "./chunk-74KY5EAM.js";
10
+ } from "./chunk-KD3GK6TO.js";
11
11
  import "./chunk-S3VT4UWM.js";
12
12
  export {
13
13
  applyPatchLog,
@@ -537,6 +537,7 @@ function decodeStega(str) {
537
537
 
538
538
  // src/core/stega/dom.ts
539
539
  var IGNORED_ANCESTORS = "script, style, noscript";
540
+ var AUTO_ENTRY_ATTR = "data-prepr-entry-auto";
540
541
  function isIgnoredTextNode(node) {
541
542
  if (node.parentElement?.closest(IGNORED_ANCESTORS)) return true;
542
543
  return !node.textContent?.trim();
@@ -570,7 +571,9 @@ function parseEditHref(href) {
570
571
  return {
571
572
  id: url.pathname.split("/").pop() || void 0,
572
573
  field: url.searchParams.get("field") || void 0,
573
- locale: url.searchParams.get("locale") || void 0
574
+ locale: url.searchParams.get("locale") || void 0,
575
+ // Added by the toolkit's `annotateEditLinks`, not by Prepr.
576
+ entry: url.searchParams.get("entry") || void 0
574
577
  };
575
578
  } catch {
576
579
  return {};
@@ -587,11 +590,16 @@ function tagEncodedElement(element, decoded) {
587
590
  element.setAttribute("data-prepr-encoded", "");
588
591
  element.setAttribute("data-prepr-href", decoded.href);
589
592
  element.setAttribute("data-prepr-origin", decoded.origin);
590
- const { id, field, locale } = parseEditHref(decoded.href);
593
+ const { id, field, locale, entry } = parseEditHref(decoded.href);
591
594
  setOrRemove(element, "data-prepr-id", id);
592
595
  const fieldHint = element.getAttribute("data-prepr-edit-target")?.trim() || void 0;
593
596
  setOrRemove(element, "data-prepr-field", field ?? fieldHint);
594
597
  setOrRemove(element, "data-prepr-locale", locale);
598
+ const siteEntry = element.hasAttribute("data-prepr-entry-id") && !element.hasAttribute(AUTO_ENTRY_ATTR);
599
+ if (!siteEntry) {
600
+ setOrRemove(element, "data-prepr-entry-id", entry);
601
+ setOrRemove(element, AUTO_ENTRY_ATTR, entry && "true");
602
+ }
595
603
  }
596
604
  function readEditPayload(element) {
597
605
  return {
@@ -3517,7 +3525,7 @@ function createPreprPreview(opts = {}) {
3517
3525
  var import_react = require("react");
3518
3526
 
3519
3527
  // src/version.ts
3520
- var VERSION = "0.5.0-rc.5";
3528
+ var VERSION = "0.5.0-rc.6";
3521
3529
 
3522
3530
  // src/core/pixel.ts
3523
3531
  var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  createPreprPreview,
4
4
  loadTrackingPixel
5
- } from "../chunk-SBLZANL5.js";
6
- import "../chunk-CMUFTCG3.js";
5
+ } from "../chunk-L3SDL6MP.js";
6
+ import "../chunk-WHZNCYUS.js";
7
7
  import "../chunk-URXSDJOU.js";
8
8
  import "../chunk-S3VT4UWM.js";
9
9