@decocms/nextjs 8.1.0-next.0 → 8.5.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 (89) hide show
  1. package/README.md +97 -328
  2. package/dist/DraftPreviewIndicator.d.ts +7 -0
  3. package/dist/DraftPreviewIndicator.d.ts.map +1 -0
  4. package/dist/DraftPreviewIndicator.js +19 -0
  5. package/dist/DraftPreviewIndicator.js.map +1 -0
  6. package/dist/StudioBridge.d.ts +16 -0
  7. package/dist/StudioBridge.d.ts.map +1 -0
  8. package/dist/StudioBridge.js +17 -0
  9. package/dist/StudioBridge.js.map +1 -0
  10. package/dist/draft.d.ts +37 -0
  11. package/dist/draft.d.ts.map +1 -0
  12. package/dist/draft.js +45 -0
  13. package/dist/draft.js.map +1 -0
  14. package/dist/drafts.d.ts +94 -0
  15. package/dist/drafts.d.ts.map +1 -0
  16. package/dist/drafts.js +210 -0
  17. package/dist/drafts.js.map +1 -0
  18. package/dist/image-loader/index.d.ts +36 -0
  19. package/dist/image-loader/index.d.ts.map +1 -0
  20. package/dist/image-loader/index.js +27 -0
  21. package/dist/image-loader/index.js.map +1 -0
  22. package/dist/index.d.ts +29 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +29 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/instrumentation/index.d.ts +74 -0
  27. package/dist/instrumentation/index.d.ts.map +1 -0
  28. package/dist/instrumentation/index.js +294 -0
  29. package/dist/instrumentation/index.js.map +1 -0
  30. package/dist/proxy/index.d.ts +82 -0
  31. package/dist/proxy/index.d.ts.map +1 -0
  32. package/dist/proxy/index.js +91 -0
  33. package/dist/proxy/index.js.map +1 -0
  34. package/dist/server/index.d.ts +46 -0
  35. package/dist/server/index.d.ts.map +1 -0
  36. package/dist/server/index.js +64 -0
  37. package/dist/server/index.js.map +1 -0
  38. package/dist/server/slot.d.ts +6 -0
  39. package/dist/server/slot.d.ts.map +1 -0
  40. package/dist/server/slot.js +13 -0
  41. package/dist/server/slot.js.map +1 -0
  42. package/dist/studio.d.ts +30 -0
  43. package/dist/studio.d.ts.map +1 -0
  44. package/dist/studio.js +55 -0
  45. package/dist/studio.js.map +1 -0
  46. package/package.json +53 -22
  47. package/src/DraftPreviewIndicator.tsx +17 -16
  48. package/src/StudioBridge.ts +23 -0
  49. package/src/config/index.cjs +78 -0
  50. package/src/config/index.d.cts +27 -0
  51. package/src/draft.ts +32 -215
  52. package/src/drafts.ts +229 -0
  53. package/src/image-loader/index.ts +62 -0
  54. package/src/index.ts +31 -45
  55. package/src/instrumentation/index.ts +337 -0
  56. package/src/proxy/index.ts +164 -0
  57. package/src/server/index.ts +77 -0
  58. package/src/server/slot.ts +14 -0
  59. package/src/studio.ts +54 -0
  60. package/src/ClientOnlySection.tsx +0 -20
  61. package/src/DecoPageRenderer.test.tsx +0 -37
  62. package/src/DecoPageRenderer.tsx +0 -107
  63. package/src/DecoRootLayout.test.tsx +0 -22
  64. package/src/DecoRootLayout.tsx +0 -82
  65. package/src/DeferredSection.test.tsx +0 -27
  66. package/src/DeferredSection.tsx +0 -59
  67. package/src/DraftPreviewBadge.tsx +0 -16
  68. package/src/SectionRenderer.test.tsx +0 -97
  69. package/src/SectionRenderer.tsx +0 -58
  70. package/src/__conformance__/observability.test.ts +0 -66
  71. package/src/config.cjs +0 -92
  72. package/src/config.d.cts +0 -4
  73. package/src/config.test.ts +0 -86
  74. package/src/createDecoPage.draft.test.tsx +0 -91
  75. package/src/createDecoPage.test.tsx +0 -63
  76. package/src/createDecoPage.tsx +0 -173
  77. package/src/createDecoPreviewPage.test.tsx +0 -71
  78. package/src/createDecoPreviewPage.tsx +0 -108
  79. package/src/draft.test.ts +0 -73
  80. package/src/draftConstants.test.ts +0 -21
  81. package/src/draftConstants.ts +0 -30
  82. package/src/draftMiddleware.test.ts +0 -179
  83. package/src/draftMiddleware.ts +0 -163
  84. package/src/draftShell.test.ts +0 -76
  85. package/src/routeHandlers.test.ts +0 -243
  86. package/src/routeHandlers.ts +0 -227
  87. package/src/setup.test.ts +0 -253
  88. package/src/setup.ts +0 -180
  89. package/tsconfig.json +0 -7
package/dist/draft.js ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The draft names and headers of a Deco Next.js site: the `?__draft=` link,
3
+ * the cookie that keeps a preview going, the request header the proxy hands
4
+ * the pages, and the response headers of a page rendered from a draft. The
5
+ * proxy reads and writes them (`./drafts.ts`, `./proxy`); the pages read the
6
+ * header (`./server`); `./config/index.cjs` repeats them (`config.test.ts`
7
+ * holds them equal). This package's own copy: it never imports
8
+ * `@decocms/tanstack`. No imports, no state: safe in every bundle.
9
+ */
10
+ /**
11
+ * The query parameter a draft link carries (`?__draft=<pointer>`, or `off` to
12
+ * end a preview). The proxy reads it after its host check; reading it
13
+ * directly would skip that check.
14
+ */
15
+ export const DRAFT_PARAM = "__draft";
16
+ /**
17
+ * The cookie that keeps a preview going after the first draft link. Like
18
+ * {@link DRAFT_PARAM}, the proxy reads it after its host check.
19
+ */
20
+ export const DRAFT_COOKIE = "__deco_draft";
21
+ /**
22
+ * The request header the proxy sets on the request it hands the pages once it
23
+ * accepted the draft: the pointer, already checked. The proxy removes any
24
+ * copy that arrives from outside, and `bindDraft` repeats the host check, so a
25
+ * client can't send one itself.
26
+ */
27
+ export const DRAFT_HEADER = "x-deco-draft";
28
+ /** The URL prefix a drafted request is rewritten onto (`app/%5Fdraft/[[...path]]`). */
29
+ export const DRAFT_ROUTE_PREFIX = "/_draft";
30
+ /**
31
+ * The response headers of a page rendered from a draft: never stored by a
32
+ * browser or a CDN (`Cache-Control: no-store, private`, `Vary: Cookie`) and
33
+ * never indexed (`X-Robots-Tag: noindex, nofollow`). `{}` when `pointer` is
34
+ * `null`, so the result of the draft decision applies as is.
35
+ */
36
+ export function draftResponseHeaders(pointer) {
37
+ if (!pointer)
38
+ return {};
39
+ return {
40
+ "Cache-Control": "no-store, private",
41
+ Vary: "Cookie",
42
+ "X-Robots-Tag": "noindex, nofollow",
43
+ };
44
+ }
45
+ //# sourceMappingURL=draft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft.js","sourceRoot":"","sources":["../src/draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C,uFAAuF;AACvF,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAkC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO;QACL,eAAe,EAAE,mBAAmB;QACpC,IAAI,EAAE,QAAQ;QACd,cAAc,EAAE,mBAAmB;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Which hosts may render drafts: v7's allowlist (`@decocms/blocks` 7.x
3
+ * `cms/draftSource.ts`: `readAllowedHosts`, `isDraftHostAllowed`,
4
+ * `isDraftPreviewEnabled`), ported as is. v7 read its override from the
5
+ * `DECO_ALLOWED_PREVIEW_HOSTS` variable; here it is the `previewHosts` option,
6
+ * and the site passes the same options to the proxy (`decideDraft`) and to
7
+ * its pages (`bindDraft`).
8
+ *
9
+ * - The site's own preview hosts: the ones it declares, in the `CMS` block or
10
+ * as `createCMS({ preview: { hosts } })` (matched as `cms.draftPointer`
11
+ * matches them). A site that declares none has none, as in v7: blocks'
12
+ * own default (every host) doesn't count as a declaration.
13
+ * - The hosts Deco runs the site on, named after the site and always added:
14
+ * `<site>.deco.site` and `<site>.deco-cx.workers.dev`, exact.
15
+ * - `previewHosts: [...]` replaces the site's own list (the inferred hosts are
16
+ * still added); `previewHosts: "none"` turns previews off on every host.
17
+ *
18
+ * Hosts compare exactly, lowercased, port included (`localhost:3000`). The
19
+ * host is the one the visitor asked for: `x-forwarded-host` behind a proxy,
20
+ * else `host`.
21
+ *
22
+ * The draft itself is the capability: a host check only bounds where a draft
23
+ * link works. A request the check refuses gets the release, never an error.
24
+ *
25
+ * No React, no Next.js and no module state: the proxy and the pages share it.
26
+ */
27
+ import { type CMS, type RequestLike } from "@decocms/blocks";
28
+ export interface PreviewPolicy {
29
+ /** The site's name: `<site>.deco.site` and `<site>.deco-cx.workers.dev` may preview. */
30
+ site?: string;
31
+ /** Replaces the `CMS` block's preview hosts; `"none"` turns previews off everywhere. */
32
+ previewHosts?: readonly string[] | "none";
33
+ }
34
+ /** The hosts Deco runs `site` on, which may always preview (unless previews are off). */
35
+ export declare function inferredPreviewHosts(site: string | undefined): string[];
36
+ /**
37
+ * Whether `host` may preview without asking the CMS: `true` (an override or
38
+ * inferred host), `false` (previews off, or the override leaves it out), or
39
+ * `"cms"` when only the site's declared preview hosts can say (none declared:
40
+ * `false`, see {@link declaresPreviewHosts}).
41
+ */
42
+ export declare function previewHostDecision(host: string, policy: PreviewPolicy): boolean | "cms";
43
+ /**
44
+ * Whether any host may preview (v7's `isDraftPreviewEnabled`): a page checks
45
+ * it before it reads the request's headers, so a site with previews off never
46
+ * turns a static page dynamic. `false` for `previewHosts: "none"`; `true` with
47
+ * a site name or an override list; otherwise whether the site declares
48
+ * preview hosts and they allow any.
49
+ */
50
+ export declare function isDraftPreviewEnabled(cms: Pick<CMS, "settings" | "forRelease">, policy: PreviewPolicy): Promise<boolean>;
51
+ /**
52
+ * Whether the site declares its own preview hosts: in the `CMS` block, or as
53
+ * `createCMS`'s `preview.hosts`. When it declares none, `cms.settings()`
54
+ * reports blocks' default, every host (`["*"]`), which v7 never had: there,
55
+ * a site without a list previewed only on its Deco hosts. The saved `CMS`
56
+ * block tells an explicit `["*"]` from that default.
57
+ */
58
+ export declare function declaresPreviewHosts(cms: Pick<CMS, "settings" | "forRelease">): Promise<boolean>;
59
+ /**
60
+ * The pointer a request carries, as `@decocms/blocks` reads it: `?__draft=`
61
+ * first, then the draft cookie; `null` for `?__draft=off` and for a value
62
+ * that doesn't parse.
63
+ */
64
+ export declare function readDraftPointer(request: RequestLike): string | null;
65
+ /** Whether the request carries a draft signal at all: without one the CMS is never asked. */
66
+ export declare function hasDraftSignal(request: RequestLike): boolean;
67
+ /** The host the visitor asked for: the first `x-forwarded-host`, else `host`. */
68
+ export declare function requestHost(headers: {
69
+ get(name: string): string | null;
70
+ }): string | null;
71
+ /**
72
+ * The request as the draft check should see it: `pathAndQuery` on the host
73
+ * the visitor asked for (`x-forwarded-host` behind a proxy, else `host`), so
74
+ * the host check judges the public hostname rather than the origin's, or
75
+ * `fallbackHost` when the headers name none. `null` when no host is known.
76
+ */
77
+ export declare function requestLike(headers: Headers, pathAndQuery: string, fallbackHost?: string): RequestLike | null;
78
+ /**
79
+ * The draft `request` renders under `policy`, or `null` for the release: the
80
+ * pointer it carries, on a host the policy (or, when only it can say, the
81
+ * `CMS` block) allows.
82
+ */
83
+ export declare function allowedDraft(cms: Pick<CMS, "draftPointer" | "settings" | "forRelease">, request: RequestLike, policy: PreviewPolicy): Promise<string | null>;
84
+ /**
85
+ * The draft cookie: the same `Set-Cookie` `cms.draftCookie` writes (a test
86
+ * holds the two together). Studio frames the site cross-site, hence
87
+ * `SameSite=None; Partitioned`.
88
+ */
89
+ export declare function draftCookie(value: string, extra?: string[]): string;
90
+ /** Whether the request ends a preview (`?__draft=off`): the cookie goes on any host. */
91
+ export declare function endsPreview(request: RequestLike): boolean;
92
+ /** Whether the request's own `?__draft=` carries `pointer` (a draft link, not the cookie). */
93
+ export declare function fromDraftLink(request: RequestLike, pointer: string): boolean;
94
+ //# sourceMappingURL=drafts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drafts.d.ts","sourceRoot":"","sources":["../src/drafts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,KAAK,GAAG,EAAqB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGhF,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;CAC3C;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAGvE;AAOD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,GAAG,KAAK,CAQxF;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,YAAY,CAAC,EACzC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,YAAY,CAAC,GACxC,OAAO,CAAC,OAAO,CAAC,CAUlB;AA8BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAMpE;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAI5D;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAIxF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,WAAW,GAAG,IAAI,CAQpB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,cAAc,GAAG,UAAU,GAAG,YAAY,CAAC,EAC1D,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAexB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAUvE;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5E"}
package/dist/drafts.js ADDED
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Which hosts may render drafts: v7's allowlist (`@decocms/blocks` 7.x
3
+ * `cms/draftSource.ts`: `readAllowedHosts`, `isDraftHostAllowed`,
4
+ * `isDraftPreviewEnabled`), ported as is. v7 read its override from the
5
+ * `DECO_ALLOWED_PREVIEW_HOSTS` variable; here it is the `previewHosts` option,
6
+ * and the site passes the same options to the proxy (`decideDraft`) and to
7
+ * its pages (`bindDraft`).
8
+ *
9
+ * - The site's own preview hosts: the ones it declares, in the `CMS` block or
10
+ * as `createCMS({ preview: { hosts } })` (matched as `cms.draftPointer`
11
+ * matches them). A site that declares none has none, as in v7: blocks'
12
+ * own default (every host) doesn't count as a declaration.
13
+ * - The hosts Deco runs the site on, named after the site and always added:
14
+ * `<site>.deco.site` and `<site>.deco-cx.workers.dev`, exact.
15
+ * - `previewHosts: [...]` replaces the site's own list (the inferred hosts are
16
+ * still added); `previewHosts: "none"` turns previews off on every host.
17
+ *
18
+ * Hosts compare exactly, lowercased, port included (`localhost:3000`). The
19
+ * host is the one the visitor asked for: `x-forwarded-host` behind a proxy,
20
+ * else `host`.
21
+ *
22
+ * The draft itself is the capability: a host check only bounds where a draft
23
+ * link works. A request the check refuses gets the release, never an error.
24
+ *
25
+ * No React, no Next.js and no module state: the proxy and the pages share it.
26
+ */
27
+ import { parseDraftPointer } from "@decocms/blocks";
28
+ import { DRAFT_COOKIE, DRAFT_PARAM } from "./draft.js";
29
+ /** The hosts Deco runs `site` on, which may always preview (unless previews are off). */
30
+ export function inferredPreviewHosts(site) {
31
+ const s = (site ?? "").trim().toLowerCase();
32
+ return s ? [`${s}.deco.site`, `${s}.deco-cx.workers.dev`] : [];
33
+ }
34
+ function overrideHosts(policy) {
35
+ if (policy.previewHosts === "none")
36
+ return ["none"];
37
+ return (policy.previewHosts ?? []).map((h) => h.trim().toLowerCase()).filter(Boolean);
38
+ }
39
+ /**
40
+ * Whether `host` may preview without asking the CMS: `true` (an override or
41
+ * inferred host), `false` (previews off, or the override leaves it out), or
42
+ * `"cms"` when only the site's declared preview hosts can say (none declared:
43
+ * `false`, see {@link declaresPreviewHosts}).
44
+ */
45
+ export function previewHostDecision(host, policy) {
46
+ const override = overrideHosts(policy);
47
+ if (override.includes("none"))
48
+ return false;
49
+ const h = host.trim().toLowerCase();
50
+ if (h === "")
51
+ return false;
52
+ if (inferredPreviewHosts(policy.site).includes(h))
53
+ return true;
54
+ if (policy.previewHosts !== undefined)
55
+ return override.includes(h);
56
+ return "cms";
57
+ }
58
+ /**
59
+ * Whether any host may preview (v7's `isDraftPreviewEnabled`): a page checks
60
+ * it before it reads the request's headers, so a site with previews off never
61
+ * turns a static page dynamic. `false` for `previewHosts: "none"`; `true` with
62
+ * a site name or an override list; otherwise whether the site declares
63
+ * preview hosts and they allow any.
64
+ */
65
+ export async function isDraftPreviewEnabled(cms, policy) {
66
+ const override = overrideHosts(policy);
67
+ if (override.includes("none"))
68
+ return false;
69
+ if (inferredPreviewHosts(policy.site).length > 0)
70
+ return true;
71
+ if (policy.previewHosts !== undefined)
72
+ return override.length > 0;
73
+ return (await declaresPreviewHosts(cms)) && (await cms.settings()).preview.hosts.length > 0;
74
+ }
75
+ /**
76
+ * Whether the site declares its own preview hosts: in the `CMS` block, or as
77
+ * `createCMS`'s `preview.hosts`. When it declares none, `cms.settings()`
78
+ * reports blocks' default, every host (`["*"]`), which v7 never had: there,
79
+ * a site without a list previewed only on its Deco hosts. The saved `CMS`
80
+ * block tells an explicit `["*"]` from that default.
81
+ */
82
+ export async function declaresPreviewHosts(cms) {
83
+ const { hosts } = (await cms.settings()).preview;
84
+ if (hosts.length !== 1 || hosts[0] !== "*")
85
+ return true;
86
+ try {
87
+ const [block] = await cms.forRelease().resolve("CMS", { run: false });
88
+ const preview = isRecord(block) ? block.preview : undefined;
89
+ return isRecord(preview) && ("hosts" in preview || "__resolveType" in preview);
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ function isRecord(value) {
96
+ return typeof value === "object" && value !== null && !Array.isArray(value);
97
+ }
98
+ const DRAFT_OFF = "off";
99
+ function draftParam(request) {
100
+ try {
101
+ return new URL(request.url, "http://localhost").searchParams.get(DRAFT_PARAM);
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ }
107
+ function readCookie(header, name) {
108
+ if (!header)
109
+ return null;
110
+ for (const part of header.split(";")) {
111
+ const eq = part.indexOf("=");
112
+ if (eq === -1 || part.slice(0, eq).trim() !== name)
113
+ continue;
114
+ try {
115
+ return decodeURIComponent(part.slice(eq + 1).trim());
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ /**
124
+ * The pointer a request carries, as `@decocms/blocks` reads it: `?__draft=`
125
+ * first, then the draft cookie; `null` for `?__draft=off` and for a value
126
+ * that doesn't parse.
127
+ */
128
+ export function readDraftPointer(request) {
129
+ const param = draftParam(request);
130
+ if (param === DRAFT_OFF)
131
+ return null;
132
+ if (param && parseDraftPointer(param) !== null)
133
+ return param;
134
+ const cookie = readCookie(request.headers.get("cookie"), DRAFT_COOKIE);
135
+ return cookie && parseDraftPointer(cookie) !== null ? cookie : null;
136
+ }
137
+ /** Whether the request carries a draft signal at all: without one the CMS is never asked. */
138
+ export function hasDraftSignal(request) {
139
+ return (draftParam(request) !== null || readCookie(request.headers.get("cookie"), DRAFT_COOKIE) !== null);
140
+ }
141
+ /** The host the visitor asked for: the first `x-forwarded-host`, else `host`. */
142
+ export function requestHost(headers) {
143
+ const forwarded = headers.get("x-forwarded-host")?.split(",")[0]?.trim();
144
+ const host = forwarded || headers.get("host")?.trim();
145
+ return host || null;
146
+ }
147
+ /**
148
+ * The request as the draft check should see it: `pathAndQuery` on the host
149
+ * the visitor asked for (`x-forwarded-host` behind a proxy, else `host`), so
150
+ * the host check judges the public hostname rather than the origin's, or
151
+ * `fallbackHost` when the headers name none. `null` when no host is known.
152
+ */
153
+ export function requestLike(headers, pathAndQuery, fallbackHost) {
154
+ const host = requestHost(headers) ?? (fallbackHost || null);
155
+ if (!host)
156
+ return null;
157
+ try {
158
+ return { url: new URL(pathAndQuery, `https://${host}`).href, headers };
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ }
164
+ /**
165
+ * The draft `request` renders under `policy`, or `null` for the release: the
166
+ * pointer it carries, on a host the policy (or, when only it can say, the
167
+ * `CMS` block) allows.
168
+ */
169
+ export async function allowedDraft(cms, request, policy) {
170
+ const pointer = readDraftPointer(request);
171
+ if (pointer === null)
172
+ return null;
173
+ let host;
174
+ try {
175
+ host = new URL(request.url).host;
176
+ }
177
+ catch {
178
+ return null;
179
+ }
180
+ const decision = previewHostDecision(host, policy);
181
+ const allowed = decision === "cms"
182
+ ? (await declaresPreviewHosts(cms)) && (await cms.draftPointer(request)) !== null
183
+ : decision;
184
+ return allowed ? pointer : null;
185
+ }
186
+ /**
187
+ * The draft cookie: the same `Set-Cookie` `cms.draftCookie` writes (a test
188
+ * holds the two together). Studio frames the site cross-site, hence
189
+ * `SameSite=None; Partitioned`.
190
+ */
191
+ export function draftCookie(value, extra = []) {
192
+ return [
193
+ `${DRAFT_COOKIE}=${value}`,
194
+ "Path=/",
195
+ ...extra,
196
+ "HttpOnly",
197
+ "Secure",
198
+ "SameSite=None",
199
+ "Partitioned",
200
+ ].join("; ");
201
+ }
202
+ /** Whether the request ends a preview (`?__draft=off`): the cookie goes on any host. */
203
+ export function endsPreview(request) {
204
+ return draftParam(request) === DRAFT_OFF;
205
+ }
206
+ /** Whether the request's own `?__draft=` carries `pointer` (a draft link, not the cookie). */
207
+ export function fromDraftLink(request, pointer) {
208
+ return draftParam(request) === pointer;
209
+ }
210
+ //# sourceMappingURL=drafts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drafts.js","sourceRoot":"","sources":["../src/drafts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAY,iBAAiB,EAAoB,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASvD,yFAAyF;AACzF,MAAM,UAAU,oBAAoB,CAAC,IAAwB;IAC3D,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,SAAS,aAAa,CAAC,MAAqB;IAC1C,IAAI,MAAM,CAAC,YAAY,KAAK,MAAM;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACxF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAAqB;IACrE,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,GAAyC,EACzC,MAAqB;IAErB,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAClE,OAAO,CAAC,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAyC;IAEzC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC;IACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,CAAC,UAAU,EAAE,CAAC,OAAO,CAAU,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5D,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,eAAe,IAAI,OAAO,CAAC,CAAC;IACjF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,SAAS,GAAG,KAAK,CAAC;AAExB,SAAS,UAAU,CAAC,OAAoB;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,MAAqB,EAAE,IAAY;IACrD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI;YAAE,SAAS;QAC7D,IAAI,CAAC;YACH,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACnD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,KAAK,IAAI,iBAAiB,CAAC,KAAK,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;IACvE,OAAO,MAAM,IAAI,iBAAiB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAAC,OAAoB;IACjD,OAAO,CACL,UAAU,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,KAAK,IAAI,CACjG,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,WAAW,CAAC,OAA6C;IACvE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACzE,MAAM,IAAI,GAAG,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;IACtD,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,OAAgB,EAChB,YAAoB,EACpB,YAAqB;IAErB,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,CAAC;QACH,OAAO,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,YAAY,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAA0D,EAC1D,OAAoB,EACpB,MAAqB;IAErB,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,OAAO,GACX,QAAQ,KAAK,KAAK;QAChB,CAAC,CAAC,CAAC,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI;QACjF,CAAC,CAAC,QAAQ,CAAC;IACf,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,KAAK,GAAa,EAAE;IAC7D,OAAO;QACL,GAAG,YAAY,IAAI,KAAK,EAAE;QAC1B,QAAQ;QACR,GAAG,KAAK;QACR,UAAU;QACV,QAAQ;QACR,eAAe;QACf,aAAa;KACd,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC9C,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC;AAC3C,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,OAAoB,EAAE,OAAe;IACjE,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC;AACzC,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `@decocms/nextjs/image-loader`: a next/image loader over an `ImageUrl` from
3
+ * `@decocms/blocks/image`. The site's `loaderFile` is one line:
4
+ *
5
+ * ```ts
6
+ * // src/image-loader.ts (next.config: images: { loader: "custom", loaderFile: "./src/image-loader.ts" })
7
+ * import { createDecoImageLoader } from "@decocms/nextjs/image-loader";
8
+ * import { imageUrl } from "./imageUrl";
9
+ * export default createDecoImageLoader(imageUrl);
10
+ * ```
11
+ *
12
+ * A source the `ImageUrl` resizes gets its provider's URL; any other source
13
+ * goes to `fallback` (`nextImageOptimizer` sends it through Next's own
14
+ * `/_next/image`), or comes back unchanged. No React, no module state.
15
+ */
16
+ import type { ImageUrl, Quality } from "@decocms/blocks/image";
17
+ /** What next/image passes a loader (`ImageLoaderProps`), with Deco's named tiers allowed as quality. */
18
+ export interface DecoImageLoaderProps {
19
+ src: string;
20
+ width: number;
21
+ quality?: Quality;
22
+ }
23
+ export type DecoImageLoader = (props: DecoImageLoaderProps) => string;
24
+ export interface DecoImageLoaderOptions {
25
+ /** The URL of a source no provider resizes; `null` keeps `src`. Default: `src` unchanged. */
26
+ fallback?: (props: DecoImageLoaderProps) => string | null;
27
+ }
28
+ /**
29
+ * Next's built-in optimizer (`/_next/image?url=&w=&q=`), for sources no
30
+ * provider resizes: a local `/public` file, another host listed in
31
+ * `images.remotePatterns`.
32
+ */
33
+ export declare function nextImageOptimizer({ src, width, quality }: DecoImageLoaderProps): string;
34
+ /** The next/image loader for `url`. */
35
+ export declare function createDecoImageLoader(url: ImageUrl, options?: DecoImageLoaderOptions): DecoImageLoader;
36
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image-loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE/D,wGAAwG;AACxG,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,MAAM,CAAC;AAEtE,MAAM,WAAW,sBAAsB;IACrC,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,MAAM,GAAG,IAAI,CAAC;CAC3D;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,oBAAoB,GAAG,MAAM,CAQxF;AAED,uCAAuC;AACvC,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,GAAE,sBAA2B,GACnC,eAAe,CASjB"}
@@ -0,0 +1,27 @@
1
+ const TIER_VALUES = { low: 60, medium: 70, high: 80, original: 100 };
2
+ /**
3
+ * Next's built-in optimizer (`/_next/image?url=&w=&q=`), for sources no
4
+ * provider resizes: a local `/public` file, another host listed in
5
+ * `images.remotePatterns`.
6
+ */
7
+ export function nextImageOptimizer({ src, width, quality }) {
8
+ const params = new URLSearchParams();
9
+ params.set("url", src);
10
+ params.set("w", width.toString());
11
+ if (quality) {
12
+ params.set("q", String(typeof quality === "string" ? TIER_VALUES[quality] : quality));
13
+ }
14
+ return `/_next/image?${params.toString()}`;
15
+ }
16
+ /** The next/image loader for `url`. */
17
+ export function createDecoImageLoader(url, options = {}) {
18
+ const { fallback } = options;
19
+ return (props) => {
20
+ const { src, width, quality } = props;
21
+ if (url.isOptimizable(src)) {
22
+ return url(src, quality === undefined ? { width } : { width, quality });
23
+ }
24
+ return fallback?.(props) ?? src;
25
+ };
26
+ }
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/image-loader/index.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAW,CAAC;AAE9E;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAwB;IAC9E,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAClC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,gBAAgB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC7C,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,qBAAqB,CACnC,GAAa,EACb,OAAO,GAA2B,EAAE;IAEpC,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC7B,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QACtC,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `@decocms/nextjs`: the components a Next.js App Router site adds, and the
3
+ * names and lists the proxy, the pages and `withDeco()` share.
4
+ *
5
+ * - `DraftPreviewIndicator`: the "Preview mode" badge on a page rendered from
6
+ * a draft (`bindDraft` from `@decocms/nextjs/server`), outside Studio's frame.
7
+ * - `StudioBridge`: one line in the root layout, `<StudioBridge site="my-store" />`,
8
+ * lets Studio's site editor drive the page when Studio frames it
9
+ * (`@decocms/blocks/preview`'s, answering `STUDIO_ORIGINS`). It renders an
10
+ * inline script with no React hooks, so a Server Component renders it as is.
11
+ * Outside Studio's frame, "." opens the page in Studio's editor.
12
+ * - `StudioPage`: rendered inside each CMS page, `<StudioPage pathTemplate={page.path} />`,
13
+ * tells the bridge which page "." opens (the root layout doesn't re-render per page).
14
+ * - `DRAFT_PARAM`, `DRAFT_COOKIE`, `DRAFT_HEADER`, `draftResponseHeaders`,
15
+ * `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin`: for a site
16
+ * that needs them in its own code (a route that sets its own headers, a CSP
17
+ * it writes itself). The package's own copy: it never imports
18
+ * `@decocms/tanstack`.
19
+ *
20
+ * The proxy (`/proxy`), the pages (`/server`), `next.config` (`/config`) and the
21
+ * image loader (`/image-loader`) are separate subpaths, so the proxy never
22
+ * loads React.
23
+ */
24
+ export { DraftBadge, StudioPage, type StudioPageInfo } from "@decocms/blocks/preview";
25
+ export { DraftPreviewIndicator, type DraftPreviewIndicatorProps, } from "./DraftPreviewIndicator.tsx";
26
+ export { DRAFT_COOKIE, DRAFT_HEADER, DRAFT_PARAM, draftResponseHeaders } from "./draft.ts";
27
+ export { StudioBridge, type StudioBridgeProps } from "./StudioBridge.ts";
28
+ export { isStudioOrigin, STUDIO_FRAME_ANCESTORS, STUDIO_ORIGINS } from "./studio.ts";
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `@decocms/nextjs`: the components a Next.js App Router site adds, and the
3
+ * names and lists the proxy, the pages and `withDeco()` share.
4
+ *
5
+ * - `DraftPreviewIndicator`: the "Preview mode" badge on a page rendered from
6
+ * a draft (`bindDraft` from `@decocms/nextjs/server`), outside Studio's frame.
7
+ * - `StudioBridge`: one line in the root layout, `<StudioBridge site="my-store" />`,
8
+ * lets Studio's site editor drive the page when Studio frames it
9
+ * (`@decocms/blocks/preview`'s, answering `STUDIO_ORIGINS`). It renders an
10
+ * inline script with no React hooks, so a Server Component renders it as is.
11
+ * Outside Studio's frame, "." opens the page in Studio's editor.
12
+ * - `StudioPage`: rendered inside each CMS page, `<StudioPage pathTemplate={page.path} />`,
13
+ * tells the bridge which page "." opens (the root layout doesn't re-render per page).
14
+ * - `DRAFT_PARAM`, `DRAFT_COOKIE`, `DRAFT_HEADER`, `draftResponseHeaders`,
15
+ * `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin`: for a site
16
+ * that needs them in its own code (a route that sets its own headers, a CSP
17
+ * it writes itself). The package's own copy: it never imports
18
+ * `@decocms/tanstack`.
19
+ *
20
+ * The proxy (`/proxy`), the pages (`/server`), `next.config` (`/config`) and the
21
+ * image loader (`/image-loader`) are separate subpaths, so the proxy never
22
+ * loads React.
23
+ */
24
+ export { DraftBadge, StudioPage } from "@decocms/blocks/preview";
25
+ export { DraftPreviewIndicator, } from "./DraftPreviewIndicator.js";
26
+ export { DRAFT_COOKIE, DRAFT_HEADER, DRAFT_PARAM, draftResponseHeaders } from "./draft.js";
27
+ export { StudioBridge } from "./StudioBridge.js";
28
+ export { isStudioOrigin, STUDIO_FRAME_ANCESTORS, STUDIO_ORIGINS } from "./studio.js";
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAuB,MAAM,yBAAyB,CAAC;AACtF,OAAO,EACL,qBAAqB,GAEtB,MAAM,4BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC3F,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * `@decocms/nextjs/instrumentation`: `http.server.request.duration` for a
3
+ * Next.js site, from the request span Next.js already opens.
4
+ *
5
+ * Next.js measures every request it serves: `BaseServer.handleRequest` opens
6
+ * one root span per request and, when the response is done, sets its status,
7
+ * its method and, when Next routed it, the route's template (`/[slug]/p`,
8
+ * not the path). It creates spans only while a tracer provider is registered
9
+ * through `@opentelemetry/api` (the app's copy, or Next's compiled one when
10
+ * the app has none; both read the same global registration).
11
+ *
12
+ * `register()`, the site's `instrumentation.ts` hook, registers a provider
13
+ * whose only job is that span: when it ends, the request is recorded with
14
+ * `recordServerRequest` from `@decocms/blocks/metrics`, into the CMS's
15
+ * telemetry in the same process. No span is exported anywhere and every other
16
+ * span Next.js asks for is a shared non-recording one. With no
17
+ * `@opentelemetry/api` reachable, on the Edge runtime, or when another
18
+ * provider is already registered, it registers nothing and never throws.
19
+ *
20
+ * A site that registers its own OpenTelemetry SDK (`@vercel/otel`,
21
+ * `NodeSDK`) adds `serverRequestSpanProcessor()` to that SDK's span
22
+ * processors instead.
23
+ *
24
+ * Labels: `http.route` is Next's route template (`unmatched` when Next names
25
+ * none); RSC requests count under their page's route, as the requests they
26
+ * are. `cache` is `DRAFT` for a draft render (the proxy forwarded
27
+ * `x-deco-draft`, or the request was rewritten onto `/_draft`) and otherwise
28
+ * `BYPASS`: Next decides its cache (`x-nextjs-cache`) on the response, which
29
+ * the span never sees.
30
+ */
31
+ type Attributes = Record<string, unknown>;
32
+ /** What `serverRequestSpanProcessor()` reads from an ended span (an SDK's `ReadableSpan`). */
33
+ interface EndedSpan {
34
+ readonly attributes: Attributes;
35
+ /** `[seconds, nanoseconds]`, as OpenTelemetry's `HrTime`. */
36
+ readonly duration: readonly [number, number];
37
+ readonly status?: {
38
+ readonly code: number;
39
+ };
40
+ }
41
+ /** An OpenTelemetry SDK `SpanProcessor` that records nothing but the request metric. */
42
+ export interface ServerRequestSpanProcessor {
43
+ onStart(span: unknown, parentContext: unknown): void;
44
+ onEnd(span: EndedSpan): void;
45
+ forceFlush(): Promise<void>;
46
+ shutdown(): Promise<void>;
47
+ }
48
+ /**
49
+ * For a site with its own OpenTelemetry SDK: a span processor that records
50
+ * `http.server.request.duration` from Next's request span and leaves every
51
+ * span to the SDK's other processors and exporters.
52
+ *
53
+ * ```ts
54
+ * registerOTel({ serviceName: "my-store", spanProcessors: ["auto", serverRequestSpanProcessor()] });
55
+ * ```
56
+ */
57
+ export declare function serverRequestSpanProcessor(): ServerRequestSpanProcessor;
58
+ /**
59
+ * The site's `instrumentation.ts` hook:
60
+ *
61
+ * ```ts
62
+ * // instrumentation.ts
63
+ * export { register } from "@decocms/nextjs/instrumentation";
64
+ * ```
65
+ *
66
+ * Registers the request-metric tracer provider, once per process. Does
67
+ * nothing on the Edge runtime (the Node.js server's request span covers the
68
+ * Edge pages it serves), without `@opentelemetry/api` (the app's or Next's),
69
+ * without `AsyncLocalStorage`, or when another provider is registered (it
70
+ * then says to use `serverRequestSpanProcessor()`). Never throws.
71
+ */
72
+ export declare function register(): Promise<void>;
73
+ export {};
74
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/instrumentation/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA4BH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAQ1C,8FAA8F;AAC9F,UAAU,SAAS;IACjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,wFAAwF;AACxF,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IACrD,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAgCD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,0BAA0B,CAiBvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB9C"}