@craftware/crafty-sdk 0.2.1 → 0.3.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.
Files changed (84) hide show
  1. package/README.md +64 -0
  2. package/dist/attrs.d.ts +33 -0
  3. package/dist/attrs.d.ts.map +1 -0
  4. package/dist/attrs.js +52 -0
  5. package/dist/attrs.js.map +1 -0
  6. package/dist/cdn.d.ts +5 -0
  7. package/dist/cdn.d.ts.map +1 -0
  8. package/dist/cdn.js +5 -0
  9. package/dist/cdn.js.map +1 -0
  10. package/dist/client.d.ts +12 -7
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +13 -8
  13. package/dist/client.js.map +1 -1
  14. package/dist/crafty-visual-editing.global.js +4 -0
  15. package/dist/crafty-visual-editing.global.js.map +7 -0
  16. package/dist/http.d.ts +7 -1
  17. package/dist/http.d.ts.map +1 -1
  18. package/dist/http.js +32 -2
  19. package/dist/http.js.map +1 -1
  20. package/dist/index.d.ts +10 -6
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +4 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/preview.d.ts +56 -0
  25. package/dist/preview.d.ts.map +1 -0
  26. package/dist/preview.js +174 -0
  27. package/dist/preview.js.map +1 -0
  28. package/dist/resources/collections.d.ts +2 -2
  29. package/dist/resources/collections.d.ts.map +1 -1
  30. package/dist/resources/entries.d.ts +3 -2
  31. package/dist/resources/entries.d.ts.map +1 -1
  32. package/dist/resources/entries.js +20 -4
  33. package/dist/resources/entries.js.map +1 -1
  34. package/dist/resources/forms.d.ts +2 -2
  35. package/dist/resources/forms.d.ts.map +1 -1
  36. package/dist/resources/forms.js +1 -1
  37. package/dist/resources/forms.js.map +1 -1
  38. package/dist/resources/mediaCollections.d.ts +2 -2
  39. package/dist/resources/mediaCollections.d.ts.map +1 -1
  40. package/dist/resources/mediaFiles.d.ts +2 -2
  41. package/dist/resources/mediaFiles.d.ts.map +1 -1
  42. package/dist/resources/pages.d.ts +2 -2
  43. package/dist/resources/pages.d.ts.map +1 -1
  44. package/dist/resources/pages.js +4 -0
  45. package/dist/resources/pages.js.map +1 -1
  46. package/dist/types/public.d.ts +14 -2
  47. package/dist/types/public.d.ts.map +1 -1
  48. package/dist/version.d.ts +2 -0
  49. package/dist/version.d.ts.map +1 -0
  50. package/dist/version.js +3 -0
  51. package/dist/version.js.map +1 -0
  52. package/dist/visual-editing/bridge.d.ts +35 -0
  53. package/dist/visual-editing/bridge.d.ts.map +1 -0
  54. package/dist/visual-editing/bridge.js +385 -0
  55. package/dist/visual-editing/bridge.js.map +1 -0
  56. package/dist/visual-editing/index.d.ts +10 -0
  57. package/dist/visual-editing/index.d.ts.map +1 -0
  58. package/dist/visual-editing/index.js +10 -0
  59. package/dist/visual-editing/index.js.map +1 -0
  60. package/dist/visual-editing/live-preview.d.ts +15 -0
  61. package/dist/visual-editing/live-preview.d.ts.map +1 -0
  62. package/dist/visual-editing/live-preview.js +63 -0
  63. package/dist/visual-editing/live-preview.js.map +1 -0
  64. package/dist/visual-editing/markers.d.ts +11 -0
  65. package/dist/visual-editing/markers.d.ts.map +1 -0
  66. package/dist/visual-editing/markers.js +43 -0
  67. package/dist/visual-editing/markers.js.map +1 -0
  68. package/dist/visual-editing/origin.d.ts +10 -0
  69. package/dist/visual-editing/origin.d.ts.map +1 -0
  70. package/dist/visual-editing/origin.js +37 -0
  71. package/dist/visual-editing/origin.js.map +1 -0
  72. package/dist/visual-editing/overlay.d.ts +13 -0
  73. package/dist/visual-editing/overlay.d.ts.map +1 -0
  74. package/dist/visual-editing/overlay.js +107 -0
  75. package/dist/visual-editing/overlay.js.map +1 -0
  76. package/dist/visual-editing/protocol.d.ts +79 -0
  77. package/dist/visual-editing/protocol.d.ts.map +1 -0
  78. package/dist/visual-editing/protocol.js +150 -0
  79. package/dist/visual-editing/protocol.js.map +1 -0
  80. package/package.json +13 -4
  81. package/skills/crafty-cms/SKILL.md +16 -2
  82. package/skills/crafty-cms/references/mcp-configuration.md +23 -0
  83. package/skills/crafty-cms/references/sdk-development.md +13 -1
  84. package/skills/crafty-cms/references/visual-editor.md +218 -0
package/README.md CHANGED
@@ -41,6 +41,8 @@ const crafty = createCraftyClient({
41
41
  - `fetch` (optional): custom fetch implementation.
42
42
  - `headers` (optional): default headers applied to all requests.
43
43
  - `timeoutMs` (optional): default timeout (ms) for all requests. A timeout is only applied when the value is a number greater than `0`.
44
+ - `preview` (optional): visual editor preview token sent as `X-Crafty-Preview`. Omit it in the browser (the SDK reads `?crafty_preview=` and keeps it in `sessionStorage`); on the server pass `previewFromRequest(req)`; pass `false` to never send one.
45
+ - `onPreviewInvalid` (optional): called when the API answers `X-Crafty-Preview-Status: invalid` (expired or foreign token; the response is the public content).
44
46
 
45
47
  ## Available resources
46
48
 
@@ -229,6 +231,68 @@ const processFirst = landing.firstByType.process;
229
231
  const processList = landing.byType.process ?? [];
230
232
  ```
231
233
 
234
+ ## Visual editor
235
+
236
+ Crafty's visual editor loads your site in an iframe and lets editors click a text or image to change it. Three pieces make a site editable:
237
+
238
+ **1. Mark editable elements** with `crafty.attrs(target, field, { label })` (also exported as `attrs`). `target` is a block or an entry from the API (or `{ kind: "block" | "entry", id }`); `field` is a field key or a JSON path inside a `json` field.
239
+
240
+ ```tsx
241
+ <h1 {...crafty.attrs(block, "title")}>{block.data.title}</h1>
242
+ <h2 {...crafty.attrs(entry, `sections[${i}].title`, { label: "Título de la sección" })}>{section.title}</h2>
243
+ ```
244
+
245
+ String templates use `attrsHtml(target, field, options)`, which returns escaped `data-crafty-*="…"` pairs. Attributes are always emitted, so static builds keep them.
246
+
247
+ **2. Carry the preview token.** In the browser nothing is needed. With server rendering, read it per request:
248
+
249
+ ```ts
250
+ import { createCraftyClient, previewFromRequest } from "@craftware/crafty-sdk";
251
+
252
+ const crafty = createCraftyClient({ baseUrl, apiKey, preview: previewFromRequest(req) });
253
+ ```
254
+
255
+ `previewFromRequest` accepts a URL string, `URL`, a Web `Request`, a Node request (`req.url`) or an Express request (`req.query`).
256
+
257
+ **3. Enable the bridge** once, in the browser:
258
+
259
+ ```ts
260
+ import { enableVisualEditing } from "@craftware/crafty-sdk/visual-editing";
261
+
262
+ enableVisualEditing({ onRefresh: () => reloadContent() });
263
+ ```
264
+
265
+ It does nothing unless the page is inside the Crafty admin iframe with a preview token, so it is safe in production. Even then it stays inert until the backend confirms the token (a response with `X-Crafty-Preview-Status: active`) — normally the site's own first Crafty request during that page load. Sites where no browser-side request carries the token (pure SSR, where content is only ever fetched on the server) must pass `client` so the bridge can confirm it itself with one lightweight request (`pages.list({ per_page: 1 })`):
266
+
267
+ ```ts
268
+ enableVisualEditing({ client: crafty });
269
+ ```
270
+
271
+ If the API answers `invalid` (expired or foreign token), the bridge stays inert and tells the admin instead of activating. Options: `adminOrigins` (defaults to `https://*.crafty.do`), `onRefresh` (re-fetch instead of a full reload), `page` / `entries` (what a server-rendered page shows), `touchHighlightMs`, `client` (verify the token instead of waiting for the site's own request).
272
+
273
+ **Custom fetch layers.** If a site fetches Crafty with something other than this SDK's client (so neither the site's own request nor `client` ever runs through `HttpClient`), report the `X-Crafty-Preview-Status` response header yourself, from `@craftware/crafty-sdk/visual-editing`:
274
+
275
+ ```ts
276
+ import { getPreviewStatus, notifyPreviewActive, notifyPreviewInvalid } from "@craftware/crafty-sdk/visual-editing";
277
+
278
+ const status = response.headers.get("X-Crafty-Preview-Status");
279
+ if (status === "active") notifyPreviewActive();
280
+ if (status === "invalid") notifyPreviewInvalid();
281
+ ```
282
+
283
+ `getPreviewStatus()` reads back the last status reported this way (or by the SDK's own client); `PREVIEW_ACTIVE_EVENT` / `PREVIEW_INVALID_EVENT` are the event names a pending bridge waits for.
284
+
285
+ Without a bundler:
286
+
287
+ ```html
288
+ <script src="https://cdn.jsdelivr.net/npm/@craftware/crafty-sdk@0.3/dist/crafty-visual-editing.global.js"></script>
289
+ <script>CraftyVisualEditing.enableVisualEditing();</script>
290
+ ```
291
+
292
+ For production, pin an exact version (`@0.3.0`) and add `integrity="sha384-…" crossorigin="anonymous"` with the hash jsDelivr shows for that file.
293
+
294
+ Sites not hosted on Crafty Hosting must allow the admin to frame them: `Content-Security-Policy: frame-ancestors 'self' https://<tenant>.crafty.do`.
295
+
232
296
  ## Notes
233
297
 
234
298
  - The SDK normalizes `baseUrl` and avoids duplicating `/api/v1`.
@@ -0,0 +1,33 @@
1
+ import type { CraftyBlock, CraftyEntry } from "./types/public.js";
2
+ export type CraftyEditTargetType = "block" | "entry";
3
+ /**
4
+ * A block or entry as returned by the API, or an explicit reference for
5
+ * ambiguous shapes (e.g. an entry with fields named `order`, `data` and `type`).
6
+ */
7
+ export type CraftyEditTarget = CraftyBlock | CraftyEntry | {
8
+ kind: CraftyEditTargetType;
9
+ id: number | string;
10
+ };
11
+ export type CraftyAttrsOptions = {
12
+ /** Human label shown by the visual editor, e.g. "Título de la sección". */
13
+ label?: string;
14
+ };
15
+ export type CraftyEditAttributes = {
16
+ "data-crafty-block"?: string;
17
+ "data-crafty-entry"?: string;
18
+ "data-crafty-field"?: string;
19
+ "data-crafty-label"?: string;
20
+ };
21
+ export declare function resolveEditTarget(target: CraftyEditTarget | null | undefined): {
22
+ type: CraftyEditTargetType;
23
+ id: number;
24
+ } | null;
25
+ /**
26
+ * DOM attributes that let the visual editor map an element to a field.
27
+ * Always emitted (static sites render them at build time without a token).
28
+ * `field` is a top-level field key or a JSON path such as `sections[3].title`.
29
+ */
30
+ export declare function attrs(target: CraftyEditTarget | null | undefined, field: string, options?: CraftyAttrsOptions): CraftyEditAttributes;
31
+ /** Same as `attrs()`, rendered for HTML string templates. */
32
+ export declare function attrsHtml(target: CraftyEditTarget | null | undefined, field: string, options?: CraftyAttrsOptions): string;
33
+ //# sourceMappingURL=attrs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attrs.d.ts","sourceRoot":"","sources":["../src/attrs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,OAAO,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,WAAW,GACX;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC1C;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBnD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CACnB,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC3C,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,oBAAoB,CAoBtB;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CACvB,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC3C,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAIR"}
package/dist/attrs.js ADDED
@@ -0,0 +1,52 @@
1
+ export function resolveEditTarget(target) {
2
+ if (target === null || target === undefined || typeof target !== "object") {
3
+ return null;
4
+ }
5
+ const record = target;
6
+ const id = typeof record.id === "string" ? Number(record.id.trim()) : record.id;
7
+ if (typeof id !== "number" || !Number.isInteger(id) || id <= 0) {
8
+ return null;
9
+ }
10
+ if (record.kind === "block" || record.kind === "entry") {
11
+ return { type: record.kind, id };
12
+ }
13
+ const looksLikeBlock = typeof record.order === "number" &&
14
+ typeof record.data === "object" &&
15
+ record.data !== null &&
16
+ "type" in record;
17
+ return { type: looksLikeBlock ? "block" : "entry", id };
18
+ }
19
+ /**
20
+ * DOM attributes that let the visual editor map an element to a field.
21
+ * Always emitted (static sites render them at build time without a token).
22
+ * `field` is a top-level field key or a JSON path such as `sections[3].title`.
23
+ */
24
+ export function attrs(target, field, options = {}) {
25
+ const resolved = resolveEditTarget(target);
26
+ const normalizedField = typeof field === "string" ? field.trim() : "";
27
+ if (resolved === null || normalizedField === "") {
28
+ return {};
29
+ }
30
+ const attributes = resolved.type === "block"
31
+ ? { "data-crafty-block": String(resolved.id), "data-crafty-field": normalizedField }
32
+ : { "data-crafty-entry": String(resolved.id), "data-crafty-field": normalizedField };
33
+ const label = typeof options.label === "string" ? options.label.trim() : "";
34
+ if (label !== "") {
35
+ attributes["data-crafty-label"] = label;
36
+ }
37
+ return attributes;
38
+ }
39
+ /** Same as `attrs()`, rendered for HTML string templates. */
40
+ export function attrsHtml(target, field, options = {}) {
41
+ return Object.entries(attrs(target, field, options))
42
+ .map(([name, value]) => `${name}="${escapeAttribute(String(value))}"`)
43
+ .join(" ");
44
+ }
45
+ function escapeAttribute(value) {
46
+ return value
47
+ .replace(/&/g, "&amp;")
48
+ .replace(/"/g, "&quot;")
49
+ .replace(/</g, "&lt;")
50
+ .replace(/>/g, "&gt;");
51
+ }
52
+ //# sourceMappingURL=attrs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attrs.js","sourceRoot":"","sources":["../src/attrs.ts"],"names":[],"mappings":"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"}
package/dist/cdn.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** Entry for the `<script>` build: exposed as `window.CraftyVisualEditing`. */
2
+ export { attrs, attrsHtml } from "./attrs.js";
3
+ export { enableVisualEditing } from "./visual-editing/bridge.js";
4
+ export { PROTOCOL_VERSION } from "./visual-editing/protocol.js";
5
+ //# sourceMappingURL=cdn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC"}
package/dist/cdn.js ADDED
@@ -0,0 +1,5 @@
1
+ /** Entry for the `<script>` build: exposed as `window.CraftyVisualEditing`. */
2
+ export { attrs, attrsHtml } from "./attrs.js";
3
+ export { enableVisualEditing } from "./visual-editing/bridge.js";
4
+ export { PROTOCOL_VERSION } from "./visual-editing/protocol.js";
5
+ //# sourceMappingURL=cdn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn.js","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC"}
package/dist/client.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- import { CollectionsResource } from "./resources/collections";
2
- import { EntriesResource } from "./resources/entries";
3
- import { FormsResource } from "./resources/forms";
4
- import { MediaCollectionsResource } from "./resources/mediaCollections";
5
- import { MediaFilesResource } from "./resources/mediaFiles";
6
- import { PagesResource } from "./resources/pages";
7
- import type { CraftyClientConfig } from "./types/public";
1
+ import { attrs, attrsHtml } from "./attrs.js";
2
+ import { previewFromRequest } from "./preview.js";
3
+ import { CollectionsResource } from "./resources/collections.js";
4
+ import { EntriesResource } from "./resources/entries.js";
5
+ import { FormsResource } from "./resources/forms.js";
6
+ import { MediaCollectionsResource } from "./resources/mediaCollections.js";
7
+ import { MediaFilesResource } from "./resources/mediaFiles.js";
8
+ import { PagesResource } from "./resources/pages.js";
9
+ import type { CraftyClientConfig } from "./types/public.js";
8
10
  export type CraftyClient = {
9
11
  collections: CollectionsResource;
10
12
  entries: EntriesResource;
@@ -12,6 +14,9 @@ export type CraftyClient = {
12
14
  mediaCollections: MediaCollectionsResource;
13
15
  mediaFiles: MediaFilesResource;
14
16
  pages: PagesResource;
17
+ attrs: typeof attrs;
18
+ attrsHtml: typeof attrsHtml;
19
+ previewFromRequest: typeof previewFromRequest;
15
20
  };
16
21
  export declare function createCraftyClient(config: CraftyClientConfig): CraftyClient;
17
22
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,mBAAmB,CAAC;IACjC,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,aAAa,CAAC;IACrB,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAW3E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,mBAAmB,CAAC;IACjC,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,aAAa,CAAC;IACrB,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;CAC/C,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAc3E"}
package/dist/client.js CHANGED
@@ -1,10 +1,12 @@
1
- import { HttpClient } from "./http";
2
- import { CollectionsResource } from "./resources/collections";
3
- import { EntriesResource } from "./resources/entries";
4
- import { FormsResource } from "./resources/forms";
5
- import { MediaCollectionsResource } from "./resources/mediaCollections";
6
- import { MediaFilesResource } from "./resources/mediaFiles";
7
- import { PagesResource } from "./resources/pages";
1
+ import { attrs, attrsHtml } from "./attrs.js";
2
+ import { HttpClient } from "./http.js";
3
+ import { previewFromRequest } from "./preview.js";
4
+ import { CollectionsResource } from "./resources/collections.js";
5
+ import { EntriesResource } from "./resources/entries.js";
6
+ import { FormsResource } from "./resources/forms.js";
7
+ import { MediaCollectionsResource } from "./resources/mediaCollections.js";
8
+ import { MediaFilesResource } from "./resources/mediaFiles.js";
9
+ import { PagesResource } from "./resources/pages.js";
8
10
  export function createCraftyClient(config) {
9
11
  const http = HttpClient.fromClientConfig(config);
10
12
  return {
@@ -13,7 +15,10 @@ export function createCraftyClient(config) {
13
15
  forms: new FormsResource(http),
14
16
  mediaCollections: new MediaCollectionsResource(http),
15
17
  mediaFiles: new MediaFilesResource(http),
16
- pages: new PagesResource(http)
18
+ pages: new PagesResource(http),
19
+ attrs,
20
+ attrsHtml,
21
+ previewFromRequest
17
22
  };
18
23
  }
19
24
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAYlD,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAEjD,OAAO;QACL,WAAW,EAAE,IAAI,mBAAmB,CAAC,IAAI,CAAC;QAC1C,OAAO,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC;QAClC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;QAC9B,gBAAgB,EAAE,IAAI,wBAAwB,CAAC,IAAI,CAAC;QACpD,UAAU,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC;QACxC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;KAC/B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAerD,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAEjD,OAAO;QACL,WAAW,EAAE,IAAI,mBAAmB,CAAC,IAAI,CAAC;QAC1C,OAAO,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC;QAClC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;QAC9B,gBAAgB,EAAE,IAAI,wBAAwB,CAAC,IAAI,CAAC;QACpD,UAAU,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC;QACxC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;QAC9B,KAAK;QACL,SAAS;QACT,kBAAkB;KACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ "use strict";var CraftyVisualEditing=(()=>{var M=Object.defineProperty;var Pe=Object.getOwnPropertyDescriptor;var Ae=Object.getOwnPropertyNames;var Le=Object.prototype.hasOwnProperty;var xe=(e,t)=>{for(var n in t)M(e,n,{get:t[n],enumerable:!0})},Se=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Ae(t))!Le.call(e,i)&&i!==n&&M(e,i,{get:()=>t[i],enumerable:!(r=Pe(t,i))||r.enumerable});return e};var Ne=e=>Se(M({},"__esModule",{value:!0}),e);var tt={};xe(tt,{PROTOCOL_VERSION:()=>b,attrs:()=>I,attrsHtml:()=>ne,enableVisualEditing:()=>me});function Ve(e){if(e==null||typeof e!="object")return null;let t=e,n=typeof t.id=="string"?Number(t.id.trim()):t.id;return typeof n!="number"||!Number.isInteger(n)||n<=0?null:t.kind==="block"||t.kind==="entry"?{type:t.kind,id:n}:{type:typeof t.order=="number"&&typeof t.data=="object"&&t.data!==null&&"type"in t?"block":"entry",id:n}}function I(e,t,n={}){let r=Ve(e),i=typeof t=="string"?t.trim():"";if(r===null||i==="")return{};let a=r.type==="block"?{"data-crafty-block":String(r.id),"data-crafty-field":i}:{"data-crafty-entry":String(r.id),"data-crafty-field":i},o=typeof n.label=="string"?n.label.trim():"";return o!==""&&(a["data-crafty-label"]=o),a}function ne(e,t,n={}){return Object.entries(I(e,t,n)).map(([r,i])=>`${r}="${Me(String(i))}"`).join(" ")}function Me(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}var K="crafty_preview";var O="crafty:preview-invalid",D="crafty:preview-content",U="crafty:preview-active",H="crafty:preview-token",re="crafty:preview-url-token",Ie="crafty:preview-invalid-token",Fe="__CRAFTY_PREVIEW_CONTENT__",He="__CRAFTY_PREVIEW_STATUS__";function _(){return typeof window=="undefined"?null:window}function F(e){var t,n;try{return(n=(t=_())==null?void 0:t.sessionStorage.getItem(e))!=null?n:null}catch{return null}}function W(e,t){var n;try{(n=_())==null||n.sessionStorage.setItem(e,t)}catch{}}function $(e){return typeof e=="string"&&e.trim()!==""?e.trim():null}function We(e){try{return $(new URL(e,"http://localhost").searchParams.get(K))}catch{return null}}function ie(){var r;let e=_();if(e===null)return null;let t=We(e.location.href),n;return t!==null&&t!==F(re)?(W(re,t),W(H,t),n=t):n=(r=$(F(H)))!=null?r:t,n!==null&&n===F(Ie)?null:n}function oe(e){let t=$(e);t!==null&&W(H,t)}function le(){let e=_(),t=e==null?void 0:e[Fe];return t?{page:t.page,entries:[...t.entries]}:{page:null,entries:[]}}function ae(){let e=_(),t=e==null?void 0:e[He];return t==="active"||t==="invalid"?t:null}var se="0.3.0";var b=1;var Ke=/^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+|\[\d+\])*$/,De=/^[\x21-\x7E]+$/;function L(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ue(e){return typeof e=="number"&&Number.isInteger(e)&&e>0}function A(e){if(!L(e))return null;let{type:t,id:n,field:r}=e;if(t!=="block"&&t!=="entry"||!Ue(n)||typeof r!="string")return null;let i=r.trim();return i===""||i.length>255||!Ke.test(i)?null:{type:t,id:n,field:i}}function de(e){let t=A(e);if(t===null)return null;let n=L(e)?e.label:void 0,r=typeof n=="string"?n.trim().slice(0,120).trim():"";return r===""?t:{...t,label:r}}function $e(e){if(!L(e)||e.v!==1||typeof e.type!="string")return null;let t=e.payload===void 0?{}:e.payload;return L(t)?{type:e.type,payload:t}:null}function ue(e){let t=$e(e);if(t===null)return null;let{payload:n}=t;switch(t.type){case"crafty:token":{let r=typeof n.token=="string"?n.token.trim():"";return r!==""&&r.length<=4096&&De.test(r)?{v:1,type:"crafty:token",payload:{token:r}}:null}case"crafty:refresh":return{v:1,type:"crafty:refresh",payload:{}};case"crafty:highlight":{let r=A(n);return r?{v:1,type:"crafty:highlight",payload:r}:null}case"crafty:preview-field":{let r=A(n),i=n.value;return r&&typeof i=="string"&&i.length<=1e5?{v:1,type:"crafty:preview-field",payload:{...r,value:i}}:null}case"crafty:preview-reset":{let r=A(n);return r?{v:1,type:"crafty:preview-reset",payload:r}:null}default:return null}}var P="[data-crafty-field]";function T(e){var a;let t=e.getAttribute("data-crafty-block"),n=e.getAttribute("data-crafty-entry"),r=t!=null?t:n;if(r===null)return null;let i=e.getAttribute("data-crafty-label");return de({type:t!==null?"block":"entry",id:r.trim()===""?Number.NaN:Number(r),field:(a=e.getAttribute("data-crafty-field"))!=null?a:"",...i!==null?{label:i}:{}})}function E(e){return`${e.type}:${e.id}:${e.field}`}function x(e){let t=[];return e.querySelectorAll(P).forEach(n=>{let r=T(n);r!==null&&t.push({element:n,field:r})}),t}function G(e){let t=new Set,n=[];for(let{field:r}of e){let i=E(r);t.has(i)||(t.add(i),n.push(r))}return n}function ce(e){let t=new Map,n=new Map,r=o=>Array.from(e.querySelectorAll(P)).filter(s=>{let u=T(s);return u!==null&&E(u)===o}),i=(o,s)=>{var u;n.has(o)||n.set(o,(u=o.textContent)!=null?u:""),o.textContent!==s&&(o.textContent=s)},a=o=>{for(let[s,u]of n){let y=T(s);(o===null||y!==null&&E(y)===o)&&(s.textContent!==u&&(s.textContent=u),n.delete(s))}};return{set(o,s){let u=E(o);t.set(u,s),r(u).forEach(y=>i(y,s))},reset(o){let s=E(o);t.delete(s),a(s)},reapply(){for(let o of[...n.keys()])o.isConnected||n.delete(o);for(let[o,s]of t)r(o).forEach(u=>i(u,s))},resetAll(){t.clear(),a(null)},get size(){return t.size}}}var fe=[/^https:\/\/([a-z0-9-]+\.)*crafty\.do$/];function ye(e){try{return new URL(e).origin}catch{return null}}function pe(e,t){return e===""||e==="null"?!1:t.some(n=>typeof n=="string"?ye(n)===e:(n.lastIndex=0,n.test(e)))}function ge(e){let t=e.location.ancestorOrigins,n=t&&t.length>0?t[0]:void 0;if(typeof n=="string"&&n!=="")return n;let r=e.document.referrer;return r?ye(r):null}var Ge="crafty-visual-editing-style",Y="#4f46e5",z="crafty-ve-flash",Ye=["[data-crafty-field]{cursor:pointer}",`.crafty-ve-box{position:fixed;top:0;left:0;pointer-events:none;z-index:2147483647;box-sizing:border-box;border:2px solid ${Y};border-radius:4px;background:rgba(79,70,229,.06)}`,".crafty-ve-box[hidden]{display:none}",`.crafty-ve-label{position:absolute;left:-2px;bottom:100%;margin-bottom:4px;padding:2px 8px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:4px;background:${Y};color:#fff;font:500 12px/1.5 system-ui,-apple-system,sans-serif}`,`.${z}{outline:2px dashed ${Y} !important;outline-offset:2px !important}`].join(`
2
+ `);function Ee(e){let t=e.defaultView,n=e.createElement("style");n.id=Ge,n.textContent=Ye;let r=e.createElement("div");r.setAttribute("data-crafty-overlay",""),r.className="crafty-ve-box",r.hidden=!0;let i=e.createElement("span");i.className="crafty-ve-label",r.appendChild(i);let a=null,o=[],s,u=t==null?void 0:t.ResizeObserver,y=()=>{if(a===null)return;let c=a.getBoundingClientRect();r.style.top=`${c.top}px`,r.style.left=`${c.left}px`,r.style.width=`${c.width}px`,r.style.height=`${c.height}px`},f=typeof u=="function"?new u(()=>y()):null,v=c=>{a!==c&&(a!==null&&(f==null||f.unobserve(a)),a=c,a!==null&&(f==null||f.observe(a)))},g=()=>{o.forEach(c=>c.classList.remove(z)),o=[],s!==void 0&&(t==null||t.clearTimeout(s),s=void 0)},C=()=>{var c,m;n.isConnected||((c=e.head)!=null?c:e.documentElement).appendChild(n),r.isConnected||((m=e.body)!=null?m:e.documentElement).appendChild(r)};return C(),t==null||t.addEventListener("scroll",y,{capture:!0,passive:!0}),t==null||t.addEventListener("resize",y,{passive:!0}),{show(c,m){C(),v(c),i.textContent!==m&&(i.textContent=m),r.hidden=!1,y()},hide(){v(null),r.hidden=!0},reposition(){y()},flash(c,m){g(),o=[...c],o.forEach(k=>k.classList.add(z)),s=t==null?void 0:t.setTimeout(g,m)},ensureAttached:C,contains(c){return c!==null&&(r===c||r.contains(c)||n===c||n.contains(c))},destroy(){g(),t==null||t.removeEventListener("scroll",y,{capture:!0}),t==null||t.removeEventListener("resize",y),f==null||f.disconnect(),r.remove(),n.remove(),a=null}}}var B="crafty:scroll-y",ze=50,Be=1500,Xe=["data-crafty-field","data-crafty-block","data-crafty-entry","data-crafty-label"],S={active:!1,disable:()=>{}},w=null;function me(e={}){var i;if(typeof window=="undefined"||typeof document=="undefined")return S;if(w!==null)return w;let t=window;if(t.parent===null||t.parent===t||ie()===null)return S;let n=ge(t);if(n===null||!pe(n,(i=e.adminOrigins)!=null?i:fe))return S;let r=ae();return r==="invalid"?(he(t,n),S):(w=r==="active"?Ce(t,n,e):je(t,n,e),w)}function he(e,t){let n={v:1,type:"crafty:preview-invalid",payload:{}};e.parent.postMessage(n,t)}function je(e,t,n){var f;let r=null,i=!1,a=()=>{i=!0,e.removeEventListener(U,s),e.removeEventListener(O,u)},o=()=>{w===y&&(w=null)},s=()=>{i||(a(),r=Ce(e,t,n,o))},u=()=>{i||(a(),o(),he(e,t))},y={get active(){var v;return(v=r==null?void 0:r.active)!=null?v:!1},disable(){a(),r==null||r.disable(),o()}};return e.addEventListener(U,s),e.addEventListener(O,u),(f=n.client)==null||f.pages.list({per_page:1}).catch(()=>{}),y}function Ce(e,t,n,r){let i=e.document,a=Ee(i),o=ce(i),s=Je(e),u=[],y=null,f,v=!1,g=!0,C=l=>{e.parent.postMessage(l,t)},c=()=>{var p,h,R;let l=G(u),d=le();y=ve(l),C({v:1,type:"crafty:ready",payload:{sdkVersion:se,path:we(e),fields:l,page:(h=(p=d.page)!=null?p:n.page)!=null?h:null,entries:qe(d.entries,(R=n.entries)!=null?R:[])}})},m=()=>{f=void 0,g&&(a.ensureAttached(),u=x(i),o.reapply(),(v||ve(G(u))!==y)&&(v=!1,c()))},k=()=>{g&&f===void 0&&(f=e.setTimeout(m,ze))},X=new MutationObserver(l=>{l.some(d=>!a.contains(d.target))&&k()}),j=l=>{var h,R;let d=(R=(h=Re(l))==null?void 0:h.closest(P))!=null?R:null,p=d!==null?T(d):null;return d!==null&&p!==null?{element:d,field:p}:null},N=l=>{var d;return(d=l.label)!=null?d:l.field},q=l=>{if(l.pointerType==="touch")return;let d=j(l.target);d!==null?a.show(d.element,N(d.field)):a.hide()},Q=l=>{let d=j(l.target);if(d!==null){l.preventDefault(),l.stopPropagation(),a.show(d.element,N(d.field)),C({v:1,type:"crafty:select",payload:d.field});return}let p=Qe(l,e);p!==null&&(l.preventDefault(),l.stopPropagation(),C({v:1,type:"crafty:navigate",payload:{path:p}}))},Te=l=>{var R;let d=E(l),p=x(i).find(_e=>E(_e.field)===d);if(p===void 0)return;let h=p.element;(R=h.scrollIntoView)==null||R.call(h,{block:"center",behavior:"smooth"}),a.show(p.element,N(p.field))},ke=async()=>{if(n.onRefresh){try{await n.onRefresh()}finally{k()}return}try{e.sessionStorage.setItem(B,String(e.scrollY))}catch{}e.location.reload()},Oe=l=>{switch(l.type){case"crafty:token":oe(l.payload.token);return;case"crafty:refresh":ke().catch(d=>console.error("[crafty] onRefresh failed",d));return;case"crafty:highlight":Te(l.payload);return;case"crafty:preview-field":o.set(l.payload,l.payload.value),a.reposition();return;case"crafty:preview-reset":o.reset(l.payload),a.reposition();return}},Z=l=>{if(l.source!==e.parent||l.origin!==t)return;let d=ue(l.data);d!==null&&Oe(d)},J=()=>{C({v:1,type:"crafty:preview-invalid",payload:{}})},ee=()=>{v=!0,k()},V=()=>{var l;g&&(u=x(i),c(),Ze(e)&&a.flash(u.map(d=>d.element),(l=n.touchHighlightMs)!=null?l:Be),et(e,s))};e.addEventListener("message",Z),i.addEventListener("click",Q,!0),i.addEventListener("pointerover",q,!0),e.addEventListener(O,J),e.addEventListener(D,ee),X.observe(i.documentElement,{childList:!0,subtree:!0,attributes:!0,attributeFilter:Xe}),i.readyState==="loading"?i.addEventListener("DOMContentLoaded",V,{once:!0}):V();let te={get active(){return g},disable(){g&&(g=!1,e.removeEventListener("message",Z),i.removeEventListener("click",Q,!0),i.removeEventListener("pointerover",q,!0),i.removeEventListener("DOMContentLoaded",V),e.removeEventListener(O,J),e.removeEventListener(D,ee),X.disconnect(),f!==void 0&&(e.clearTimeout(f),f=void 0),o.resetAll(),a.destroy(),w===te&&(w=null),r==null||r())}};return te}function Re(e){return e!==null&&typeof e.closest=="function"?e:null}function ve(e){return e.map(t=>{var n;return`${E(t)}|${(n=t.label)!=null?n:""}`}).join(`
3
+ `)}function qe(e,t){let n=new Set;return[...e,...t].filter(r=>{let i=`${r.collection}:${r.id}`;return n.has(i)?!1:(n.add(i),!0)})}function be(e){return e.searchParams.delete(K),`${e.pathname}${e.search}`}function we(e){return be(new URL(e.location.href))}function Qe(e,t){var o,s,u;if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return null;let n=(s=(o=Re(e.target))==null?void 0:o.closest("a[href]"))!=null?s:null;if(n===null||n.hasAttribute("download"))return null;let r=n.getAttribute("target");if(r!==null&&r!==""&&r!=="_self")return null;let i;try{i=new URL((u=n.getAttribute("href"))!=null?u:"",t.location.href)}catch{return null}if(i.origin!==t.location.origin)return null;let a=be(i);return i.hash!==""&&a===we(t)?null:a}function Ze(e){return typeof e.matchMedia=="function"&&e.matchMedia("(hover: none)").matches}function Je(e){try{let t=e.sessionStorage.getItem(B);e.sessionStorage.removeItem(B);let n=t===null?Number.NaN:Number(t);return Number.isFinite(n)?n:null}catch{return null}}function et(e,t){if(t===null)return;let n=()=>e.scrollTo(0,t);e.document.readyState==="complete"?n():e.addEventListener("load",n,{once:!0})}return Ne(tt);})();
4
+ //# sourceMappingURL=crafty-visual-editing.global.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cdn.ts", "../src/attrs.ts", "../src/preview.ts", "../src/version.ts", "../src/visual-editing/protocol.ts", "../src/visual-editing/markers.ts", "../src/visual-editing/live-preview.ts", "../src/visual-editing/origin.ts", "../src/visual-editing/overlay.ts", "../src/visual-editing/bridge.ts"],
4
+ "sourcesContent": ["/** Entry for the `<script>` build: exposed as `window.CraftyVisualEditing`. */\nexport { attrs, attrsHtml } from \"./attrs.js\";\nexport { enableVisualEditing } from \"./visual-editing/bridge.js\";\nexport { PROTOCOL_VERSION } from \"./visual-editing/protocol.js\";\n", "import type { CraftyBlock, CraftyEntry } from \"./types/public.js\";\n\nexport type CraftyEditTargetType = \"block\" | \"entry\";\n\n/**\n * A block or entry as returned by the API, or an explicit reference for\n * ambiguous shapes (e.g. an entry with fields named `order`, `data` and `type`).\n */\nexport type CraftyEditTarget =\n | CraftyBlock\n | CraftyEntry\n | { kind: CraftyEditTargetType; id: number | string };\n\nexport type CraftyAttrsOptions = {\n /** Human label shown by the visual editor, e.g. \"T\u00EDtulo de la secci\u00F3n\". */\n label?: string;\n};\n\nexport type CraftyEditAttributes = {\n \"data-crafty-block\"?: string;\n \"data-crafty-entry\"?: string;\n \"data-crafty-field\"?: string;\n \"data-crafty-label\"?: string;\n};\n\nexport function resolveEditTarget(\n target: CraftyEditTarget | null | undefined\n): { type: CraftyEditTargetType; id: number } | null {\n if (target === null || target === undefined || typeof target !== \"object\") {\n return null;\n }\n\n const record = target as Record<string, unknown>;\n const id = typeof record.id === \"string\" ? Number(record.id.trim()) : record.id;\n\n if (typeof id !== \"number\" || !Number.isInteger(id) || id <= 0) {\n return null;\n }\n\n if (record.kind === \"block\" || record.kind === \"entry\") {\n return { type: record.kind, id };\n }\n\n const looksLikeBlock =\n typeof record.order === \"number\" &&\n typeof record.data === \"object\" &&\n record.data !== null &&\n \"type\" in record;\n\n return { type: looksLikeBlock ? \"block\" : \"entry\", id };\n}\n\n/**\n * DOM attributes that let the visual editor map an element to a field.\n * Always emitted (static sites render them at build time without a token).\n * `field` is a top-level field key or a JSON path such as `sections[3].title`.\n */\nexport function attrs(\n target: CraftyEditTarget | null | undefined,\n field: string,\n options: CraftyAttrsOptions = {}\n): CraftyEditAttributes {\n const resolved = resolveEditTarget(target);\n const normalizedField = typeof field === \"string\" ? field.trim() : \"\";\n\n if (resolved === null || normalizedField === \"\") {\n return {};\n }\n\n const attributes: CraftyEditAttributes =\n resolved.type === \"block\"\n ? { \"data-crafty-block\": String(resolved.id), \"data-crafty-field\": normalizedField }\n : { \"data-crafty-entry\": String(resolved.id), \"data-crafty-field\": normalizedField };\n\n const label = typeof options.label === \"string\" ? options.label.trim() : \"\";\n\n if (label !== \"\") {\n attributes[\"data-crafty-label\"] = label;\n }\n\n return attributes;\n}\n\n/** Same as `attrs()`, rendered for HTML string templates. */\nexport function attrsHtml(\n target: CraftyEditTarget | null | undefined,\n field: string,\n options: CraftyAttrsOptions = {}\n): string {\n return Object.entries(attrs(target, field, options))\n .map(([name, value]) => `${name}=\"${escapeAttribute(String(value))}\"`)\n .join(\" \");\n}\n\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n", "export const PREVIEW_QUERY_PARAM = \"crafty_preview\";\nexport const PREVIEW_HEADER = \"X-Crafty-Preview\";\nexport const PREVIEW_STATUS_HEADER = \"X-Crafty-Preview-Status\";\nexport const PREVIEW_INVALID_EVENT = \"crafty:preview-invalid\";\nexport const PREVIEW_CONTENT_EVENT = \"crafty:preview-content\";\nexport const PREVIEW_ACTIVE_EVENT = \"crafty:preview-active\";\n\nconst TOKEN_STORAGE_KEY = \"crafty:preview-token\";\nconst URL_TOKEN_STORAGE_KEY = \"crafty:preview-url-token\";\nconst INVALID_TOKEN_STORAGE_KEY = \"crafty:preview-invalid-token\";\nconst CONTENT_GLOBAL_KEY = \"__CRAFTY_PREVIEW_CONTENT__\";\nconst STATUS_GLOBAL_KEY = \"__CRAFTY_PREVIEW_STATUS__\";\n\nexport type PreviewPage = { id: number; slug: string };\nexport type PreviewEntry = { id: number; collection: string };\nexport type PreviewContent = { page: PreviewPage | null; entries: PreviewEntry[] };\n/** Last `X-Crafty-Preview-Status` the API answered in this window, or null before any preview request. */\nexport type PreviewStatus = \"active\" | \"invalid\";\n\n/** A URL, a Web `Request`, a Node `IncomingMessage` or an Express-style request. */\nexport type PreviewRequestLike =\n | string\n | URL\n | { url?: string | undefined; query?: Record<string, unknown> | undefined };\n\ntype ContentHost = Record<string, PreviewContent | undefined>;\n\nfunction browserWindow(): Window | null {\n return typeof window === \"undefined\" ? null : window;\n}\n\nfunction storageGet(key: string): string | null {\n try {\n return browserWindow()?.sessionStorage.getItem(key) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction storageSet(key: string, value: string): void {\n try {\n browserWindow()?.sessionStorage.setItem(key, value);\n } catch {\n // Storage can be blocked (private mode, sandboxed frames); the URL token still works.\n }\n}\n\nfunction storageRemove(key: string): void {\n try {\n browserWindow()?.sessionStorage.removeItem(key);\n } catch {\n // Ignore blocked storage.\n }\n}\n\nfunction normalizeToken(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() !== \"\" ? value.trim() : null;\n}\n\nfunction tokenFromUrl(url: string): string | null {\n try {\n return normalizeToken(new URL(url, \"http://localhost\").searchParams.get(PREVIEW_QUERY_PARAM));\n } catch {\n return null;\n }\n}\n\n/**\n * Preview token for browser requests. A token the URL brings for the first time\n * wins; afterwards the stored token wins, so a renewal received over\n * `postMessage` is not overwritten by the stale token still in the URL.\n */\nexport function readPreviewToken(): string | null {\n const win = browserWindow();\n\n if (win === null) {\n return null;\n }\n\n const urlToken = tokenFromUrl(win.location.href);\n let token: string | null;\n\n if (urlToken !== null && urlToken !== storageGet(URL_TOKEN_STORAGE_KEY)) {\n storageSet(URL_TOKEN_STORAGE_KEY, urlToken);\n storageSet(TOKEN_STORAGE_KEY, urlToken);\n token = urlToken;\n } else {\n token = normalizeToken(storageGet(TOKEN_STORAGE_KEY)) ?? urlToken;\n }\n\n return token !== null && token === storageGet(INVALID_TOKEN_STORAGE_KEY) ? null : token;\n}\n\n/**\n * Forgets a browser token the API reported invalid, so later requests stop\n * sending it (the stale copy in the URL included) until a new token arrives\n * over `postMessage` or a new URL. Explicit server-side tokens are untouched.\n */\nexport function discardPreviewToken(token: string): void {\n const win = browserWindow();\n const normalized = normalizeToken(token);\n\n if (win === null || normalized === null) {\n return;\n }\n\n const isStored = normalizeToken(storageGet(TOKEN_STORAGE_KEY)) === normalized;\n\n if (!isStored && tokenFromUrl(win.location.href) !== normalized) {\n return;\n }\n\n if (isStored) {\n storageRemove(TOKEN_STORAGE_KEY);\n }\n\n storageSet(INVALID_TOKEN_STORAGE_KEY, normalized);\n}\n\nexport function storePreviewToken(token: string): void {\n const normalized = normalizeToken(token);\n\n if (normalized !== null) {\n storageSet(TOKEN_STORAGE_KEY, normalized);\n }\n}\n\nexport function clearPreviewToken(): void {\n storageRemove(TOKEN_STORAGE_KEY);\n storageRemove(URL_TOKEN_STORAGE_KEY);\n storageRemove(INVALID_TOKEN_STORAGE_KEY);\n setPreviewStatus(null);\n}\n\n/** Server-side helper: the preview token of an incoming request, or null. */\nexport function previewFromRequest(request: PreviewRequestLike | null | undefined): string | null {\n if (request === null || request === undefined) {\n return null;\n }\n\n if (typeof request === \"string\" || request instanceof URL) {\n return tokenFromUrl(String(request));\n }\n\n const queryValue = request.query?.[PREVIEW_QUERY_PARAM];\n const fromQuery = normalizeToken(Array.isArray(queryValue) ? queryValue[0] : queryValue);\n\n if (fromQuery !== null) {\n return fromQuery;\n }\n\n return typeof request.url === \"string\" ? tokenFromUrl(request.url) : null;\n}\n\nexport function getPreviewContent(): PreviewContent {\n const win = browserWindow() as (Window & ContentHost) | null;\n const content = win?.[CONTENT_GLOBAL_KEY];\n\n return content ? { page: content.page, entries: [...content.entries] } : { page: null, entries: [] };\n}\n\nexport function recordPreviewContent(\n item: ({ type: \"page\" } & PreviewPage) | ({ type: \"entry\" } & PreviewEntry)\n): void {\n const win = browserWindow() as (Window & ContentHost) | null;\n\n if (win === null) {\n return;\n }\n\n const content = win[CONTENT_GLOBAL_KEY] ?? { page: null, entries: [] };\n win[CONTENT_GLOBAL_KEY] = content;\n\n if (item.type === \"page\") {\n content.page = { id: item.id, slug: item.slug };\n } else if (!content.entries.some((entry) => entry.id === item.id && entry.collection === item.collection)) {\n content.entries.push({ id: item.id, collection: item.collection });\n }\n\n dispatch(PREVIEW_CONTENT_EVENT);\n}\n\nexport function resetPreviewContent(): void {\n const win = browserWindow() as (Window & ContentHost) | null;\n\n if (win !== null) {\n win[CONTENT_GLOBAL_KEY] = undefined;\n }\n}\n\n/**\n * Kept on `window` (like the shown content) so a separately bundled bridge, such\n * as the `<script>` build, sees what the site's own SDK copy recorded.\n */\nexport function getPreviewStatus(): PreviewStatus | null {\n const win = browserWindow() as (Window & Record<string, unknown>) | null;\n const status = win?.[STATUS_GLOBAL_KEY];\n\n return status === \"active\" || status === \"invalid\" ? status : null;\n}\n\nfunction setPreviewStatus(status: PreviewStatus | null): void {\n const win = browserWindow() as (Window & Record<string, unknown>) | null;\n\n if (win !== null) {\n win[STATUS_GLOBAL_KEY] = status ?? undefined;\n }\n}\n\nexport function notifyPreviewActive(): void {\n setPreviewStatus(\"active\");\n dispatch(PREVIEW_ACTIVE_EVENT);\n}\n\nexport function notifyPreviewInvalid(): void {\n setPreviewStatus(\"invalid\");\n dispatch(PREVIEW_INVALID_EVENT);\n}\n\nfunction dispatch(name: string): void {\n const win = browserWindow();\n\n if (win !== null && typeof win.dispatchEvent === \"function\") {\n win.dispatchEvent(new Event(name));\n }\n}\n", "// Generated by scripts/write-version.mjs from package.json. Do not edit.\nexport const SDK_VERSION = \"0.3.0\";\n", "export const PROTOCOL_VERSION = 1;\n\nexport type CraftyTargetType = \"block\" | \"entry\";\nexport type CraftyFieldRef = { type: CraftyTargetType; id: number; field: string };\nexport type CraftyMarkedField = CraftyFieldRef & { label?: string };\nexport type CraftyShownPage = { id: number; slug: string };\nexport type CraftyShownEntry = { id: number; collection: string };\n\nexport type ReadyPayload = {\n sdkVersion: string;\n path: string;\n fields: CraftyMarkedField[];\n page: CraftyShownPage | null;\n entries: CraftyShownEntry[];\n};\n\ntype Version = typeof PROTOCOL_VERSION;\ntype EmptyPayload = Record<string, never>;\n\n/** iframe -> admin. `crafty:ready` is a snapshot that may be re-sent; the last one wins. */\nexport type OutboundMessage =\n | { v: Version; type: \"crafty:ready\"; payload: ReadyPayload }\n | { v: Version; type: \"crafty:select\"; payload: CraftyMarkedField }\n | { v: Version; type: \"crafty:navigate\"; payload: { path: string } }\n | { v: Version; type: \"crafty:preview-invalid\"; payload: EmptyPayload };\n\n/** admin -> iframe. */\nexport type InboundMessage =\n | { v: Version; type: \"crafty:token\"; payload: { token: string } }\n | { v: Version; type: \"crafty:refresh\"; payload: EmptyPayload }\n | { v: Version; type: \"crafty:highlight\"; payload: CraftyFieldRef }\n | { v: Version; type: \"crafty:preview-field\"; payload: CraftyFieldRef & { value: string } }\n | { v: Version; type: \"crafty:preview-reset\"; payload: CraftyFieldRef };\n\n/** Same limits as the Crafty admin and backend (content_drafts.field / label). */\nconst MAX_FIELD_LENGTH = 255;\nconst MAX_LABEL_LENGTH = 120;\nconst MAX_TOKEN_LENGTH = 4096;\nconst MAX_VALUE_LENGTH = 100_000;\nconst MAX_PATH_LENGTH = 2048;\nconst FIELD_PATTERN = /^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+|\\[\\d+\\])*$/;\nconst TOKEN_PATTERN = /^[\\x21-\\x7E]+$/;\nconst CONTROL_CHARACTERS = /[\\u0000-\\u001F\\u007F]/;\nconst PATH_BASE = \"https://crafty.invalid\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value > 0;\n}\n\nexport function parseFieldRef(value: unknown): CraftyFieldRef | null {\n if (!isRecord(value)) {\n return null;\n }\n\n const { type, id, field } = value;\n\n if ((type !== \"block\" && type !== \"entry\") || !isPositiveInteger(id) || typeof field !== \"string\") {\n return null;\n }\n\n const trimmed = field.trim();\n\n return trimmed === \"\" || trimmed.length > MAX_FIELD_LENGTH || !FIELD_PATTERN.test(trimmed)\n ? null\n : { type, id, field: trimmed };\n}\n\nexport function parseMarkedField(value: unknown): CraftyMarkedField | null {\n const ref = parseFieldRef(value);\n\n if (ref === null) {\n return null;\n }\n\n const rawLabel = isRecord(value) ? value.label : undefined;\n const label = typeof rawLabel === \"string\" ? rawLabel.trim().slice(0, MAX_LABEL_LENGTH).trim() : \"\";\n\n return label === \"\" ? ref : { ...ref, label };\n}\n\nfunction isSameOriginPath(value: string): boolean {\n if (!value.startsWith(\"/\") || value.startsWith(\"//\") || value.startsWith(\"/\\\\\")) {\n return false;\n }\n\n try {\n const url = new URL(value, PATH_BASE);\n\n // Dot segments can still resolve to a protocol-relative path (\"/.//evil.example\").\n return url.origin === PATH_BASE && !url.pathname.startsWith(\"//\") && !url.pathname.startsWith(\"/\\\\\");\n } catch {\n return false;\n }\n}\n\nfunction parsePath(value: unknown): string | null {\n if (typeof value !== \"string\" || value.length > MAX_PATH_LENGTH || CONTROL_CHARACTERS.test(value)) {\n return null;\n }\n\n return isSameOriginPath(value) ? value : null;\n}\n\nfunction parseShownEntry(value: unknown): CraftyShownEntry | null {\n return isRecord(value) && isPositiveInteger(value.id) && typeof value.collection === \"string\"\n ? { id: value.id, collection: value.collection }\n : null;\n}\n\nfunction readEnvelope(data: unknown): { type: string; payload: Record<string, unknown> } | null {\n if (!isRecord(data) || data.v !== PROTOCOL_VERSION || typeof data.type !== \"string\") {\n return null;\n }\n\n const payload = data.payload === undefined ? {} : data.payload;\n\n return isRecord(payload) ? { type: data.type, payload } : null;\n}\n\nexport function parseInboundMessage(data: unknown): InboundMessage | null {\n const message = readEnvelope(data);\n\n if (message === null) {\n return null;\n }\n\n const { payload } = message;\n\n switch (message.type) {\n case \"crafty:token\": {\n const token = typeof payload.token === \"string\" ? payload.token.trim() : \"\";\n\n return token !== \"\" && token.length <= MAX_TOKEN_LENGTH && TOKEN_PATTERN.test(token)\n ? { v: PROTOCOL_VERSION, type: \"crafty:token\", payload: { token } }\n : null;\n }\n case \"crafty:refresh\":\n return { v: PROTOCOL_VERSION, type: \"crafty:refresh\", payload: {} };\n case \"crafty:highlight\": {\n const ref = parseFieldRef(payload);\n\n return ref ? { v: PROTOCOL_VERSION, type: \"crafty:highlight\", payload: ref } : null;\n }\n case \"crafty:preview-field\": {\n const ref = parseFieldRef(payload);\n const value = payload.value;\n\n return ref && typeof value === \"string\" && value.length <= MAX_VALUE_LENGTH\n ? { v: PROTOCOL_VERSION, type: \"crafty:preview-field\", payload: { ...ref, value } }\n : null;\n }\n case \"crafty:preview-reset\": {\n const ref = parseFieldRef(payload);\n\n return ref ? { v: PROTOCOL_VERSION, type: \"crafty:preview-reset\", payload: ref } : null;\n }\n default:\n return null;\n }\n}\n\nexport function parseOutboundMessage(data: unknown): OutboundMessage | null {\n const message = readEnvelope(data);\n\n if (message === null) {\n return null;\n }\n\n const { payload } = message;\n\n switch (message.type) {\n case \"crafty:ready\": {\n const path = parsePath(payload.path);\n const rawPage = payload.page;\n\n if (typeof payload.sdkVersion !== \"string\" || path === null || !Array.isArray(payload.fields) || !Array.isArray(payload.entries)) {\n return null;\n }\n\n return {\n v: PROTOCOL_VERSION,\n type: \"crafty:ready\",\n payload: {\n sdkVersion: payload.sdkVersion,\n path,\n fields: payload.fields\n .map((field: unknown) => parseMarkedField(field))\n .filter((field): field is CraftyMarkedField => field !== null),\n page:\n isRecord(rawPage) && isPositiveInteger(rawPage.id) && typeof rawPage.slug === \"string\"\n ? { id: rawPage.id, slug: rawPage.slug }\n : null,\n entries: payload.entries\n .map((entry: unknown) => parseShownEntry(entry))\n .filter((entry): entry is CraftyShownEntry => entry !== null)\n }\n };\n }\n case \"crafty:select\": {\n const field = parseMarkedField(payload);\n\n return field ? { v: PROTOCOL_VERSION, type: \"crafty:select\", payload: field } : null;\n }\n case \"crafty:navigate\": {\n const path = parsePath(payload.path);\n\n return path ? { v: PROTOCOL_VERSION, type: \"crafty:navigate\", payload: { path } } : null;\n }\n case \"crafty:preview-invalid\":\n return { v: PROTOCOL_VERSION, type: \"crafty:preview-invalid\", payload: {} };\n default:\n return null;\n }\n}\n", "import { parseMarkedField, type CraftyFieldRef, type CraftyMarkedField } from \"./protocol.js\";\n\nexport const MARKED_SELECTOR = \"[data-crafty-field]\";\n\nexport type MarkedElement = { element: Element; field: CraftyMarkedField };\n\nexport function readMarkedField(element: Element): CraftyMarkedField | null {\n const blockId = element.getAttribute(\"data-crafty-block\");\n const entryId = element.getAttribute(\"data-crafty-entry\");\n const rawId = blockId ?? entryId;\n\n if (rawId === null) {\n return null;\n }\n\n const label = element.getAttribute(\"data-crafty-label\");\n\n return parseMarkedField({\n type: blockId !== null ? \"block\" : \"entry\",\n id: rawId.trim() === \"\" ? Number.NaN : Number(rawId),\n field: element.getAttribute(\"data-crafty-field\") ?? \"\",\n ...(label !== null ? { label } : {})\n });\n}\n\nexport function refKey(ref: CraftyFieldRef): string {\n return `${ref.type}:${ref.id}:${ref.field}`;\n}\n\nexport function scanMarked(root: ParentNode): MarkedElement[] {\n const marked: MarkedElement[] = [];\n\n root.querySelectorAll(MARKED_SELECTOR).forEach((element) => {\n const field = readMarkedField(element);\n\n if (field !== null) {\n marked.push({ element, field });\n }\n });\n\n return marked;\n}\n\nexport function uniqueFields(marked: MarkedElement[]): CraftyMarkedField[] {\n const seen = new Set<string>();\n const fields: CraftyMarkedField[] = [];\n\n for (const { field } of marked) {\n const key = refKey(field);\n\n if (!seen.has(key)) {\n seen.add(key);\n fields.push(field);\n }\n }\n\n return fields;\n}\n", "import { MARKED_SELECTOR, readMarkedField, refKey } from \"./markers.js\";\nimport type { CraftyFieldRef } from \"./protocol.js\";\n\nexport type LivePreview = {\n set(ref: CraftyFieldRef, value: string): void;\n reset(ref: CraftyFieldRef): void;\n reapply(): void;\n resetAll(): void;\n readonly size: number;\n};\n\n/**\n * Plain-text live preview. Values are written with `textContent` (never HTML),\n * and each element's own original text is kept so a reset restores exactly what\n * the site rendered, even for elements rendered after the value was set.\n */\nexport function createLivePreview(root: ParentNode): LivePreview {\n const values = new Map<string, string>();\n const originals = new Map<Element, string>();\n\n const matching = (key: string): Element[] =>\n Array.from(root.querySelectorAll(MARKED_SELECTOR)).filter((element) => {\n const field = readMarkedField(element);\n\n return field !== null && refKey(field) === key;\n });\n\n const write = (element: Element, value: string): void => {\n if (!originals.has(element)) {\n originals.set(element, element.textContent ?? \"\");\n }\n\n if (element.textContent !== value) {\n element.textContent = value;\n }\n };\n\n const restore = (key: string | null): void => {\n for (const [element, original] of originals) {\n const field = readMarkedField(element);\n\n if (key === null || (field !== null && refKey(field) === key)) {\n if (element.textContent !== original) {\n element.textContent = original;\n }\n\n originals.delete(element);\n }\n }\n };\n\n return {\n set(ref, value) {\n const key = refKey(ref);\n values.set(key, value);\n matching(key).forEach((element) => write(element, value));\n },\n reset(ref) {\n const key = refKey(ref);\n values.delete(key);\n restore(key);\n },\n reapply() {\n for (const element of [...originals.keys()]) {\n if (!element.isConnected) {\n originals.delete(element);\n }\n }\n\n for (const [key, value] of values) {\n matching(key).forEach((element) => write(element, value));\n }\n },\n resetAll() {\n values.clear();\n restore(null);\n },\n get size() {\n return values.size;\n }\n };\n}\n", "/** Crafty admin panels: https://crafty.do and https://<tenant>.crafty.do. */\nexport const DEFAULT_ADMIN_ORIGINS: ReadonlyArray<string | RegExp> = [/^https:\\/\\/([a-z0-9-]+\\.)*crafty\\.do$/];\n\nfunction originOf(value: string): string | null {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n}\n\nexport function isAllowedAdminOrigin(origin: string, allowlist: ReadonlyArray<string | RegExp>): boolean {\n if (origin === \"\" || origin === \"null\") {\n return false;\n }\n\n return allowlist.some((allowed) => {\n if (typeof allowed === \"string\") {\n return originOf(allowed) === origin;\n }\n\n allowed.lastIndex = 0;\n\n return allowed.test(origin);\n });\n}\n\n/**\n * Origin of the embedding admin. `ancestorOrigins` (Chromium, WebKit) cannot be\n * spoofed by the framed page; Firefox falls back to the referrer the admin sent\n * when it set the iframe `src`.\n */\nexport function resolveParentOrigin(win: Window): string | null {\n const ancestors = win.location.ancestorOrigins as ArrayLike<string> | undefined;\n const first = ancestors && ancestors.length > 0 ? ancestors[0] : undefined;\n\n if (typeof first === \"string\" && first !== \"\") {\n return first;\n }\n\n const referrer = win.document.referrer;\n\n return referrer ? originOf(referrer) : null;\n}\n", "const STYLE_ELEMENT_ID = \"crafty-visual-editing-style\";\nconst ACCENT = \"#4f46e5\";\n\nexport const FLASH_CLASS = \"crafty-ve-flash\";\n\nconst STYLES = [\n \"[data-crafty-field]{cursor:pointer}\",\n `.crafty-ve-box{position:fixed;top:0;left:0;pointer-events:none;z-index:2147483647;box-sizing:border-box;border:2px solid ${ACCENT};border-radius:4px;background:rgba(79,70,229,.06)}`,\n \".crafty-ve-box[hidden]{display:none}\",\n `.crafty-ve-label{position:absolute;left:-2px;bottom:100%;margin-bottom:4px;padding:2px 8px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:4px;background:${ACCENT};color:#fff;font:500 12px/1.5 system-ui,-apple-system,sans-serif}`,\n `.${FLASH_CLASS}{outline:2px dashed ${ACCENT} !important;outline-offset:2px !important}`\n].join(\"\\n\");\n\nexport type Overlay = {\n show(element: Element, label: string): void;\n hide(): void;\n /** Re-measures the highlighted element, e.g. after its content changed. */\n reposition(): void;\n flash(elements: Element[], durationMs: number): void;\n ensureAttached(): void;\n contains(node: Node | null): boolean;\n destroy(): void;\n};\n\nexport function createOverlay(doc: Document): Overlay {\n const win = doc.defaultView;\n const style = doc.createElement(\"style\");\n style.id = STYLE_ELEMENT_ID;\n style.textContent = STYLES;\n\n const box = doc.createElement(\"div\");\n box.setAttribute(\"data-crafty-overlay\", \"\");\n box.className = \"crafty-ve-box\";\n box.hidden = true;\n\n const label = doc.createElement(\"span\");\n label.className = \"crafty-ve-label\";\n box.appendChild(label);\n\n let target: Element | null = null;\n let flashed: Element[] = [];\n let flashTimer: number | undefined;\n const Observer = (win as (Window & { ResizeObserver?: typeof ResizeObserver }) | null)?.ResizeObserver;\n\n const place = (): void => {\n if (target === null) {\n return;\n }\n\n const rect = target.getBoundingClientRect();\n box.style.top = `${rect.top}px`;\n box.style.left = `${rect.left}px`;\n box.style.width = `${rect.width}px`;\n box.style.height = `${rect.height}px`;\n };\n\n const resizeObserver = typeof Observer === \"function\" ? new Observer(() => place()) : null;\n\n const track = (element: Element | null): void => {\n if (target === element) {\n return;\n }\n\n if (target !== null) {\n resizeObserver?.unobserve(target);\n }\n\n target = element;\n\n if (target !== null) {\n resizeObserver?.observe(target);\n }\n };\n\n const clearFlash = (): void => {\n flashed.forEach((element) => element.classList.remove(FLASH_CLASS));\n flashed = [];\n\n if (flashTimer !== undefined) {\n win?.clearTimeout(flashTimer);\n flashTimer = undefined;\n }\n };\n\n const ensureAttached = (): void => {\n if (!style.isConnected) {\n (doc.head ?? doc.documentElement).appendChild(style);\n }\n\n if (!box.isConnected) {\n (doc.body ?? doc.documentElement).appendChild(box);\n }\n };\n\n ensureAttached();\n win?.addEventListener(\"scroll\", place, { capture: true, passive: true });\n win?.addEventListener(\"resize\", place, { passive: true });\n\n return {\n show(element, text) {\n ensureAttached();\n track(element);\n\n if (label.textContent !== text) {\n label.textContent = text;\n }\n\n box.hidden = false;\n place();\n },\n hide() {\n track(null);\n box.hidden = true;\n },\n reposition() {\n place();\n },\n flash(elements, durationMs) {\n clearFlash();\n flashed = [...elements];\n flashed.forEach((element) => element.classList.add(FLASH_CLASS));\n flashTimer = win?.setTimeout(clearFlash, durationMs);\n },\n ensureAttached,\n contains(node) {\n return node !== null && (box === node || box.contains(node) || style === node || style.contains(node));\n },\n destroy() {\n clearFlash();\n win?.removeEventListener(\"scroll\", place, { capture: true });\n win?.removeEventListener(\"resize\", place);\n resizeObserver?.disconnect();\n box.remove();\n style.remove();\n target = null;\n }\n };\n}\n", "import type { CraftyClient } from \"../client.js\";\nimport {\n PREVIEW_ACTIVE_EVENT,\n PREVIEW_CONTENT_EVENT,\n PREVIEW_INVALID_EVENT,\n PREVIEW_QUERY_PARAM,\n getPreviewContent,\n getPreviewStatus,\n readPreviewToken,\n storePreviewToken\n} from \"../preview.js\";\nimport { SDK_VERSION } from \"../version.js\";\nimport { createLivePreview } from \"./live-preview.js\";\nimport { MARKED_SELECTOR, readMarkedField, refKey, scanMarked, uniqueFields, type MarkedElement } from \"./markers.js\";\nimport { DEFAULT_ADMIN_ORIGINS, isAllowedAdminOrigin, resolveParentOrigin } from \"./origin.js\";\nimport { createOverlay } from \"./overlay.js\";\nimport {\n PROTOCOL_VERSION,\n parseInboundMessage,\n type CraftyFieldRef,\n type CraftyMarkedField,\n type CraftyShownEntry,\n type CraftyShownPage,\n type InboundMessage,\n type OutboundMessage\n} from \"./protocol.js\";\n\nexport type VisualEditingOptions = {\n /** Allowed admin origins. Replaces the default (`https://*.crafty.do`). */\n adminOrigins?: ReadonlyArray<string | RegExp>;\n /** Re-fetch and re-render instead of a full reload when the admin asks for a refresh. */\n onRefresh?: () => void | Promise<void>;\n /** Page rendered by the server (SSR sites, where the browser did not fetch it). */\n page?: CraftyShownPage | null;\n /** Entries rendered by the server (SSR sites). */\n entries?: CraftyShownEntry[];\n /** How long touch devices highlight every editable after load. Default 1500 ms. */\n touchHighlightMs?: number;\n /**\n * Client used to confirm the preview token when no preview request has been\n * answered yet (one `pages.list({ per_page: 1 })` call). Without it the bridge\n * waits for the site's own first Crafty request to confirm the token.\n */\n client?: Pick<CraftyClient, \"pages\">;\n};\n\nexport type VisualEditingHandle = {\n readonly active: boolean;\n disable(): void;\n};\n\nconst SCROLL_STORAGE_KEY = \"crafty:scroll-y\";\nconst RESCAN_DELAY_MS = 50;\nconst DEFAULT_TOUCH_HIGHLIGHT_MS = 1500;\nconst WATCHED_ATTRIBUTES = [\"data-crafty-field\", \"data-crafty-block\", \"data-crafty-entry\", \"data-crafty-label\"];\n\nconst inactiveHandle: VisualEditingHandle = { active: false, disable: () => undefined };\nlet activeHandle: VisualEditingHandle | null = null;\n\n/**\n * Connects the page to the Crafty visual editor. No-op unless the page runs in\n * an iframe, carries a preview token, and its parent is an allowed admin origin.\n *\n * The default allowlist (`*.crafty.do`) also covers sites hosted by Crafty, so\n * the bridge stays inert (no overlay, no listeners, no messages) until the API\n * answers a preview request with `X-Crafty-Preview-Status: active`. On `invalid`\n * it only tells the admin `crafty:preview-invalid` and stays inert.\n */\nexport function enableVisualEditing(options: VisualEditingOptions = {}): VisualEditingHandle {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return inactiveHandle;\n }\n\n if (activeHandle !== null) {\n return activeHandle;\n }\n\n const win = window;\n\n if (win.parent === null || win.parent === win || readPreviewToken() === null) {\n return inactiveHandle;\n }\n\n const parentOrigin = resolveParentOrigin(win);\n\n if (parentOrigin === null || !isAllowedAdminOrigin(parentOrigin, options.adminOrigins ?? DEFAULT_ADMIN_ORIGINS)) {\n return inactiveHandle;\n }\n\n const status = getPreviewStatus();\n\n if (status === \"invalid\") {\n postPreviewInvalid(win, parentOrigin);\n\n return inactiveHandle;\n }\n\n activeHandle = status === \"active\" ? startBridge(win, parentOrigin, options) : awaitConfirmation(win, parentOrigin, options);\n\n return activeHandle;\n}\n\nfunction postPreviewInvalid(win: Window, parentOrigin: string): void {\n const message: OutboundMessage = { v: PROTOCOL_VERSION, type: \"crafty:preview-invalid\", payload: {} };\n win.parent.postMessage(message, parentOrigin);\n}\n\n/**\n * Pending handle: waits for the API to confirm the token before starting the\n * bridge, verifying it through `options.client` when one is given.\n */\nfunction awaitConfirmation(win: Window, parentOrigin: string, options: VisualEditingOptions): VisualEditingHandle {\n let bridge: VisualEditingHandle | null = null;\n let settled = false;\n\n const stopWaiting = (): void => {\n settled = true;\n win.removeEventListener(PREVIEW_ACTIVE_EVENT, onActive);\n win.removeEventListener(PREVIEW_INVALID_EVENT, onInvalid);\n };\n\n const release = (): void => {\n if (activeHandle === handle) {\n activeHandle = null;\n }\n };\n\n const onActive = (): void => {\n if (settled) {\n return;\n }\n\n stopWaiting();\n bridge = startBridge(win, parentOrigin, options, release);\n };\n\n const onInvalid = (): void => {\n if (settled) {\n return;\n }\n\n stopWaiting();\n release();\n postPreviewInvalid(win, parentOrigin);\n };\n\n const handle: VisualEditingHandle = {\n get active() {\n return bridge?.active ?? false;\n },\n disable() {\n stopWaiting();\n bridge?.disable();\n release();\n }\n };\n\n win.addEventListener(PREVIEW_ACTIVE_EVENT, onActive);\n win.addEventListener(PREVIEW_INVALID_EVENT, onInvalid);\n\n // The status handlers above settle the outcome; request errors leave the bridge waiting.\n options.client?.pages.list({ per_page: 1 }).catch(() => undefined);\n\n return handle;\n}\n\nfunction startBridge(\n win: Window,\n parentOrigin: string,\n options: VisualEditingOptions,\n onDisable?: () => void\n): VisualEditingHandle {\n const doc = win.document;\n const overlay = createOverlay(doc);\n const livePreview = createLivePreview(doc);\n const pendingScrollY = takeStoredScroll(win);\n\n let marked: MarkedElement[] = [];\n let sentSignature: string | null = null;\n let rescanTimer: number | undefined;\n let forceReady = false;\n let active = true;\n\n const post = (message: OutboundMessage): void => {\n win.parent.postMessage(message, parentOrigin);\n };\n\n const sendReady = (): void => {\n const fields = uniqueFields(marked);\n const content = getPreviewContent();\n sentSignature = signatureOf(fields);\n\n post({\n v: PROTOCOL_VERSION,\n type: \"crafty:ready\",\n payload: {\n sdkVersion: SDK_VERSION,\n path: currentPath(win),\n fields,\n page: content.page ?? options.page ?? null,\n entries: mergeEntries(content.entries, options.entries ?? [])\n }\n });\n };\n\n const rescan = (): void => {\n rescanTimer = undefined;\n\n if (!active) {\n return;\n }\n\n overlay.ensureAttached();\n marked = scanMarked(doc);\n livePreview.reapply();\n\n if (forceReady || signatureOf(uniqueFields(marked)) !== sentSignature) {\n forceReady = false;\n sendReady();\n }\n };\n\n const scheduleRescan = (): void => {\n if (active && rescanTimer === undefined) {\n rescanTimer = win.setTimeout(rescan, RESCAN_DELAY_MS);\n }\n };\n\n const observer = new MutationObserver((records) => {\n if (records.some((record) => !overlay.contains(record.target))) {\n scheduleRescan();\n }\n });\n\n const hitFrom = (target: EventTarget | null): MarkedElement | null => {\n const element = asElement(target)?.closest(MARKED_SELECTOR) ?? null;\n const field = element !== null ? readMarkedField(element) : null;\n\n return element !== null && field !== null ? { element, field } : null;\n };\n\n const labelOf = (field: CraftyMarkedField): string => field.label ?? field.field;\n\n const onPointerOver = (event: PointerEvent): void => {\n if (event.pointerType === \"touch\") {\n return;\n }\n\n const hit = hitFrom(event.target);\n\n if (hit !== null) {\n overlay.show(hit.element, labelOf(hit.field));\n } else {\n overlay.hide();\n }\n };\n\n const onClick = (event: MouseEvent): void => {\n const hit = hitFrom(event.target);\n\n if (hit !== null) {\n event.preventDefault();\n event.stopPropagation();\n overlay.show(hit.element, labelOf(hit.field));\n post({ v: PROTOCOL_VERSION, type: \"crafty:select\", payload: hit.field });\n\n return;\n }\n\n const path = internalLinkPath(event, win);\n\n if (path !== null) {\n event.preventDefault();\n event.stopPropagation();\n post({ v: PROTOCOL_VERSION, type: \"crafty:navigate\", payload: { path } });\n }\n };\n\n const highlight = (ref: CraftyFieldRef): void => {\n const key = refKey(ref);\n const match = scanMarked(doc).find((item) => refKey(item.field) === key);\n\n if (match === undefined) {\n return;\n }\n\n const element = match.element as Element & { scrollIntoView?: (options?: ScrollIntoViewOptions) => void };\n element.scrollIntoView?.({ block: \"center\", behavior: \"smooth\" });\n overlay.show(match.element, labelOf(match.field));\n };\n\n const refresh = async (): Promise<void> => {\n if (options.onRefresh) {\n try {\n await options.onRefresh();\n } finally {\n scheduleRescan();\n }\n\n return;\n }\n\n try {\n win.sessionStorage.setItem(SCROLL_STORAGE_KEY, String(win.scrollY));\n } catch {\n // Storage blocked: reload without restoring the scroll position.\n }\n\n win.location.reload();\n };\n\n const handleInbound = (message: InboundMessage): void => {\n switch (message.type) {\n case \"crafty:token\":\n storePreviewToken(message.payload.token);\n return;\n case \"crafty:refresh\":\n refresh().catch((error: unknown) => console.error(\"[crafty] onRefresh failed\", error));\n return;\n case \"crafty:highlight\":\n highlight(message.payload);\n return;\n case \"crafty:preview-field\":\n livePreview.set(message.payload, message.payload.value);\n overlay.reposition();\n return;\n case \"crafty:preview-reset\":\n livePreview.reset(message.payload);\n overlay.reposition();\n return;\n }\n };\n\n const onMessage = (event: MessageEvent): void => {\n if (event.source !== win.parent || event.origin !== parentOrigin) {\n return;\n }\n\n const message = parseInboundMessage(event.data);\n\n if (message !== null) {\n handleInbound(message);\n }\n };\n\n const onPreviewInvalid = (): void => {\n post({ v: PROTOCOL_VERSION, type: \"crafty:preview-invalid\", payload: {} });\n };\n\n const onPreviewContent = (): void => {\n forceReady = true;\n scheduleRescan();\n };\n\n const start = (): void => {\n if (!active) {\n return;\n }\n\n marked = scanMarked(doc);\n sendReady();\n\n if (isTouchDevice(win)) {\n overlay.flash(\n marked.map((item) => item.element),\n options.touchHighlightMs ?? DEFAULT_TOUCH_HIGHLIGHT_MS\n );\n }\n\n restoreScroll(win, pendingScrollY);\n };\n\n win.addEventListener(\"message\", onMessage);\n doc.addEventListener(\"click\", onClick, true);\n doc.addEventListener(\"pointerover\", onPointerOver, true);\n win.addEventListener(PREVIEW_INVALID_EVENT, onPreviewInvalid);\n win.addEventListener(PREVIEW_CONTENT_EVENT, onPreviewContent);\n observer.observe(doc.documentElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: WATCHED_ATTRIBUTES\n });\n\n if (doc.readyState === \"loading\") {\n doc.addEventListener(\"DOMContentLoaded\", start, { once: true });\n } else {\n start();\n }\n\n const handle: VisualEditingHandle = {\n get active() {\n return active;\n },\n disable() {\n if (!active) {\n return;\n }\n\n active = false;\n win.removeEventListener(\"message\", onMessage);\n doc.removeEventListener(\"click\", onClick, true);\n doc.removeEventListener(\"pointerover\", onPointerOver, true);\n doc.removeEventListener(\"DOMContentLoaded\", start);\n win.removeEventListener(PREVIEW_INVALID_EVENT, onPreviewInvalid);\n win.removeEventListener(PREVIEW_CONTENT_EVENT, onPreviewContent);\n observer.disconnect();\n\n if (rescanTimer !== undefined) {\n win.clearTimeout(rescanTimer);\n rescanTimer = undefined;\n }\n\n livePreview.resetAll();\n overlay.destroy();\n\n if (activeHandle === handle) {\n activeHandle = null;\n }\n\n onDisable?.();\n }\n };\n\n return handle;\n}\n\nfunction asElement(target: EventTarget | null): Element | null {\n return target !== null && typeof (target as Element).closest === \"function\" ? (target as Element) : null;\n}\n\nfunction signatureOf(fields: CraftyMarkedField[]): string {\n return fields.map((field) => `${refKey(field)}|${field.label ?? \"\"}`).join(\"\\n\");\n}\n\nfunction mergeEntries(fetched: CraftyShownEntry[], declared: CraftyShownEntry[]): CraftyShownEntry[] {\n const seen = new Set<string>();\n\n return [...fetched, ...declared].filter((entry) => {\n const key = `${entry.collection}:${entry.id}`;\n\n if (seen.has(key)) {\n return false;\n }\n\n seen.add(key);\n\n return true;\n });\n}\n\nfunction stripPreviewParam(url: URL): string {\n url.searchParams.delete(PREVIEW_QUERY_PARAM);\n\n return `${url.pathname}${url.search}`;\n}\n\nfunction currentPath(win: Window): string {\n return stripPreviewParam(new URL(win.location.href));\n}\n\nfunction internalLinkPath(event: MouseEvent, win: Window): string | null {\n if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return null;\n }\n\n const anchor = asElement(event.target)?.closest(\"a[href]\") ?? null;\n\n if (anchor === null || anchor.hasAttribute(\"download\")) {\n return null;\n }\n\n const target = anchor.getAttribute(\"target\");\n\n if (target !== null && target !== \"\" && target !== \"_self\") {\n return null;\n }\n\n let url: URL;\n\n try {\n url = new URL(anchor.getAttribute(\"href\") ?? \"\", win.location.href);\n } catch {\n return null;\n }\n\n if (url.origin !== win.location.origin) {\n return null;\n }\n\n const path = stripPreviewParam(url);\n\n if (url.hash !== \"\" && path === currentPath(win)) {\n return null;\n }\n\n return path;\n}\n\nfunction isTouchDevice(win: Window): boolean {\n return typeof win.matchMedia === \"function\" && win.matchMedia(\"(hover: none)\").matches;\n}\n\nfunction takeStoredScroll(win: Window): number | null {\n try {\n const raw = win.sessionStorage.getItem(SCROLL_STORAGE_KEY);\n win.sessionStorage.removeItem(SCROLL_STORAGE_KEY);\n const scrollY = raw === null ? Number.NaN : Number(raw);\n\n return Number.isFinite(scrollY) ? scrollY : null;\n } catch {\n return null;\n }\n}\n\nfunction restoreScroll(win: Window, scrollY: number | null): void {\n if (scrollY === null) {\n return;\n }\n\n const apply = (): void => win.scrollTo(0, scrollY);\n\n if (win.document.readyState === \"complete\") {\n apply();\n } else {\n win.addEventListener(\"load\", apply, { once: true });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["cdn_exports", "__export", "PROTOCOL_VERSION", "attrs", "attrsHtml", "enableVisualEditing", "resolveEditTarget", "target", "record", "id", "attrs", "field", "options", "resolved", "normalizedField", "attributes", "label", "attrsHtml", "name", "value", "escapeAttribute", "PREVIEW_QUERY_PARAM", "PREVIEW_INVALID_EVENT", "PREVIEW_CONTENT_EVENT", "PREVIEW_ACTIVE_EVENT", "TOKEN_STORAGE_KEY", "URL_TOKEN_STORAGE_KEY", "INVALID_TOKEN_STORAGE_KEY", "CONTENT_GLOBAL_KEY", "STATUS_GLOBAL_KEY", "browserWindow", "storageGet", "key", "_a", "_b", "storageSet", "value", "normalizeToken", "value", "tokenFromUrl", "url", "PREVIEW_QUERY_PARAM", "readPreviewToken", "_a", "win", "browserWindow", "urlToken", "token", "storageGet", "URL_TOKEN_STORAGE_KEY", "storageSet", "TOKEN_STORAGE_KEY", "INVALID_TOKEN_STORAGE_KEY", "storePreviewToken", "token", "normalized", "normalizeToken", "storageSet", "TOKEN_STORAGE_KEY", "getPreviewContent", "win", "browserWindow", "content", "CONTENT_GLOBAL_KEY", "getPreviewStatus", "win", "browserWindow", "status", "STATUS_GLOBAL_KEY", "SDK_VERSION", "PROTOCOL_VERSION", "FIELD_PATTERN", "TOKEN_PATTERN", "isRecord", "value", "isPositiveInteger", "parseFieldRef", "type", "id", "field", "trimmed", "FIELD_PATTERN", "parseMarkedField", "ref", "rawLabel", "label", "readEnvelope", "data", "isRecord", "payload", "parseInboundMessage", "message", "token", "TOKEN_PATTERN", "ref", "parseFieldRef", "value", "MARKED_SELECTOR", "readMarkedField", "element", "_a", "blockId", "entryId", "rawId", "label", "parseMarkedField", "refKey", "ref", "scanMarked", "root", "marked", "field", "uniqueFields", "seen", "fields", "key", "createLivePreview", "root", "values", "originals", "matching", "key", "MARKED_SELECTOR", "element", "field", "readMarkedField", "refKey", "write", "value", "_a", "restore", "original", "ref", "DEFAULT_ADMIN_ORIGINS", "originOf", "value", "isAllowedAdminOrigin", "origin", "allowlist", "allowed", "resolveParentOrigin", "win", "ancestors", "first", "referrer", "STYLE_ELEMENT_ID", "ACCENT", "FLASH_CLASS", "STYLES", "createOverlay", "doc", "win", "style", "box", "label", "target", "flashed", "flashTimer", "Observer", "place", "rect", "resizeObserver", "track", "element", "clearFlash", "ensureAttached", "_a", "_b", "text", "elements", "durationMs", "node", "SCROLL_STORAGE_KEY", "RESCAN_DELAY_MS", "DEFAULT_TOUCH_HIGHLIGHT_MS", "WATCHED_ATTRIBUTES", "inactiveHandle", "activeHandle", "enableVisualEditing", "options", "_a", "win", "readPreviewToken", "parentOrigin", "resolveParentOrigin", "isAllowedAdminOrigin", "DEFAULT_ADMIN_ORIGINS", "status", "getPreviewStatus", "postPreviewInvalid", "startBridge", "awaitConfirmation", "message", "bridge", "settled", "stopWaiting", "PREVIEW_ACTIVE_EVENT", "onActive", "PREVIEW_INVALID_EVENT", "onInvalid", "release", "handle", "onDisable", "doc", "overlay", "createOverlay", "livePreview", "createLivePreview", "pendingScrollY", "takeStoredScroll", "marked", "sentSignature", "rescanTimer", "forceReady", "active", "post", "sendReady", "_b", "_c", "fields", "uniqueFields", "content", "getPreviewContent", "signatureOf", "SDK_VERSION", "currentPath", "mergeEntries", "rescan", "scanMarked", "scheduleRescan", "observer", "records", "record", "hitFrom", "target", "element", "asElement", "MARKED_SELECTOR", "field", "readMarkedField", "labelOf", "onPointerOver", "event", "hit", "onClick", "path", "internalLinkPath", "highlight", "ref", "key", "refKey", "match", "item", "refresh", "handleInbound", "storePreviewToken", "error", "onMessage", "parseInboundMessage", "onPreviewInvalid", "onPreviewContent", "start", "isTouchDevice", "restoreScroll", "PREVIEW_CONTENT_EVENT", "fetched", "declared", "seen", "entry", "stripPreviewParam", "url", "PREVIEW_QUERY_PARAM", "anchor", "raw", "scrollY", "apply"]
7
+ }
package/dist/http.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CraftyClientConfig, RequestOptions } from "./types/public";
1
+ import type { CraftyClientConfig, RequestOptions } from "./types/public.js";
2
2
  type QueryValue = string | number | boolean | null | undefined;
3
3
  type QueryParams = Record<string, QueryValue>;
4
4
  type HttpRequestOptions = RequestOptions & {
@@ -11,6 +11,8 @@ type RequiredTransportConfig = {
11
11
  apiKey: string | undefined;
12
12
  siteSlug: string | undefined;
13
13
  timeoutMs: number | undefined;
14
+ preview: string | false | null | undefined;
15
+ onPreviewInvalid: (() => void) | undefined;
14
16
  };
15
17
  export declare class HttpClient {
16
18
  private readonly apiBaseUrl;
@@ -19,9 +21,13 @@ export declare class HttpClient {
19
21
  private readonly apiKey;
20
22
  private readonly siteSlug;
21
23
  private readonly defaultTimeoutMs;
24
+ private readonly preview;
25
+ private readonly onPreviewInvalid;
22
26
  constructor(config: RequiredTransportConfig);
23
27
  static fromClientConfig(config: CraftyClientConfig): HttpClient;
24
28
  getSiteSlug(): string | undefined;
29
+ /** Token to send as `X-Crafty-Preview`, or null when not previewing. */
30
+ previewToken(): string | null;
25
31
  get<T>(path: string, options?: HttpRequestOptions): Promise<T>;
26
32
  post<T>(path: string, body: unknown, options?: HttpRequestOptions & {
27
33
  skipApiKey?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErF,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAC/D,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE9C,KAAK,kBAAkB,GAAG,cAAc,GAAG;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,cAAc,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;gBAEnC,MAAM,EAAE,uBAAuB;WASpC,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,UAAU;IAyB/D,WAAW,IAAI,MAAM,GAAG,SAAS;IAI3B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAI9D,IAAI,CAAC,CAAC,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,kBAAkB,GAAG;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACtD,OAAO,CAAC,CAAC,CAAC;IAcA,OAAO,CAAC,CAAC,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,GAAG;QAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpD,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GACA,OAAO,CAAC,CAAC,CAAC;IA2Fb,OAAO,CAAC,QAAQ;CAejB"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExF,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAC/D,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE9C,KAAK,kBAAkB,GAAG,cAAc,GAAG;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,cAAc,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C,gBAAgB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;CAC5C,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;gBAEzC,MAAM,EAAE,uBAAuB;WAWpC,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,UAAU;IA2B/D,WAAW,IAAI,MAAM,GAAG,SAAS;IAIxC,wEAAwE;IACjE,YAAY,IAAI,MAAM,GAAG,IAAI;IAYvB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAI9D,IAAI,CAAC,CAAC,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,kBAAkB,GAAG;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACtD,OAAO,CAAC,CAAC,CAAC;IAcA,OAAO,CAAC,CAAC,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,GAAG;QAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpD,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GACA,OAAO,CAAC,CAAC,CAAC;IA2Gb,OAAO,CAAC,QAAQ;CAejB"}
package/dist/http.js CHANGED
@@ -1,4 +1,5 @@
1
- import { CraftyError, mapStatusToErrorCode } from "./errors";
1
+ import { CraftyError, mapStatusToErrorCode } from "./errors.js";
2
+ import { PREVIEW_HEADER, PREVIEW_STATUS_HEADER, discardPreviewToken, notifyPreviewActive, notifyPreviewInvalid, readPreviewToken } from "./preview.js";
2
3
  export class HttpClient {
3
4
  apiBaseUrl;
4
5
  fetchImpl;
@@ -6,6 +7,8 @@ export class HttpClient {
6
7
  apiKey;
7
8
  siteSlug;
8
9
  defaultTimeoutMs;
10
+ preview;
11
+ onPreviewInvalid;
9
12
  constructor(config) {
10
13
  this.apiBaseUrl = config.apiBaseUrl;
11
14
  this.fetchImpl = config.fetchImpl;
@@ -13,6 +16,8 @@ export class HttpClient {
13
16
  this.apiKey = config.apiKey;
14
17
  this.siteSlug = config.siteSlug;
15
18
  this.defaultTimeoutMs = config.timeoutMs;
19
+ this.preview = config.preview;
20
+ this.onPreviewInvalid = config.onPreviewInvalid;
16
21
  }
17
22
  static fromClientConfig(config) {
18
23
  const fetchImpl = config.fetch ?? getBoundGlobalFetch();
@@ -32,12 +37,24 @@ export class HttpClient {
32
37
  defaultHeaders: config.headers,
33
38
  apiKey: config.apiKey,
34
39
  siteSlug: config.siteSlug,
35
- timeoutMs: config.timeoutMs
40
+ timeoutMs: config.timeoutMs,
41
+ preview: config.preview,
42
+ onPreviewInvalid: config.onPreviewInvalid
36
43
  });
37
44
  }
38
45
  getSiteSlug() {
39
46
  return this.siteSlug;
40
47
  }
48
+ /** Token to send as `X-Crafty-Preview`, or null when not previewing. */
49
+ previewToken() {
50
+ if (this.preview === false) {
51
+ return null;
52
+ }
53
+ if (typeof this.preview === "string" && this.preview.trim() !== "") {
54
+ return this.preview.trim();
55
+ }
56
+ return readPreviewToken();
57
+ }
41
58
  async get(path, options) {
42
59
  return this.request(path, { ...options, method: "GET" });
43
60
  }
@@ -68,6 +85,10 @@ export class HttpClient {
68
85
  if (options.body !== undefined && !headers.has("Content-Type")) {
69
86
  headers.set("Content-Type", "application/json");
70
87
  }
88
+ const previewToken = this.previewToken();
89
+ if (previewToken !== null && !headers.has(PREVIEW_HEADER)) {
90
+ headers.set(PREVIEW_HEADER, previewToken);
91
+ }
71
92
  const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
72
93
  const timeoutController = typeof timeoutMs === "number" && timeoutMs > 0
73
94
  ? new AbortController()
@@ -87,6 +108,15 @@ export class HttpClient {
87
108
  fetchInit.body = JSON.stringify(options.body);
88
109
  }
89
110
  const response = await this.fetchImpl(url, fetchInit);
111
+ const previewStatus = previewToken !== null ? response.headers.get(PREVIEW_STATUS_HEADER) : null;
112
+ if (previewStatus === "invalid" && previewToken !== null) {
113
+ discardPreviewToken(previewToken);
114
+ this.onPreviewInvalid?.();
115
+ notifyPreviewInvalid();
116
+ }
117
+ else if (previewStatus === "active") {
118
+ notifyPreviewActive();
119
+ }
90
120
  const payload = await parseResponsePayload(response);
91
121
  if (!response.ok) {
92
122
  const requestId = response.headers.get("x-request-id") ?? undefined;