@decocms/nextjs 8.9.1 → 8.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/StudioBridge.d.ts +18 -5
- package/dist/StudioBridge.d.ts.map +1 -1
- package/dist/StudioBridge.js +12 -7
- package/dist/StudioBridge.js.map +1 -1
- package/dist/assignments.d.ts.map +1 -1
- package/dist/assignments.js +5 -9
- package/dist/assignments.js.map +1 -1
- package/dist/csp.d.ts +32 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +77 -0
- package/dist/csp.js.map +1 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/proxy/index.js +1 -1
- package/dist/proxy/index.js.map +1 -1
- package/dist/studio.d.ts +20 -0
- package/dist/studio.d.ts.map +1 -1
- package/dist/studio.js +47 -0
- package/dist/studio.js.map +1 -1
- package/package.json +2 -2
- package/src/StudioBridge.ts +27 -8
- package/src/assignments.ts +6 -14
- package/src/csp.ts +100 -0
- package/src/index.ts +18 -3
- package/src/proxy/index.ts +1 -1
- package/src/studio.ts +47 -0
package/README.md
CHANGED
|
@@ -132,6 +132,8 @@ import { StudioBridge } from "@decocms/nextjs";
|
|
|
132
132
|
|
|
133
133
|
Studio's origins are the package's own (`STUDIO_ORIGINS`), so the bridge takes only the site's name. v7's "." edit shortcut is on by default (`editShortcut={false}` turns it off), and `nonce` is optional.
|
|
134
134
|
|
|
135
|
+
The bridge also mounts the Studio channel on every page, production included: a small inline script (about 0.7 kB gzipped) through which Studio loads its own editor code (the section wireframe, the page swap after a save) into the page when it frames it. It is the only way Studio runs code in the page: the bridge evaluates no script Studio sends. The page trusts a list of exact Studio origins: `studioOrigins` when given, else the `DECO_STUDIO_ORIGIN` environment variable, comma-separated (a local Studio, such as `http://localhost:3000`), else Studio's production and staging origins (`DEFAULT_STUDIO_ORIGINS`). Framed, the page posts `{ type: "deco:ready" }` with each of them as the target origin (only a parent at one of them receives it) and imports a module only when Studio's answer, `{ type: "deco:load", url }`, comes from exactly one of them and names a URL on the origin of that message; not framed, or framed by any other origin, it does nothing. A site with a Content Security Policy passes it through `withStudioCsp(directives)`, which adds the same list to `script-src` (and a `frame-ancestors` with Studio's, when the policy has none): `headers: [{ source: "/:path*", headers: [{ key: "Content-Security-Policy", value: withStudioCsp("script-src 'self' 'unsafe-inline'; object-src 'none'").join("; ") }] }]`, or the same in the site's proxy. The inline scripts take the site's `nonce`. The message and module types are in `@decocms/blocks/protocol`.
|
|
136
|
+
|
|
135
137
|
On a page that isn't in Studio's frame, "." (or Ctrl+Shift+E) opens the page in Studio's editor, and Ctrl or Cmd+"." opens it in a new tab; Studio asks a visitor who isn't signed in to sign in first. To open the right page, each CMS page tells the bridge its path template: the root layout stays across navigations, so the page renders the marker, next to the sections it resolved:
|
|
136
138
|
|
|
137
139
|
```tsx
|
|
@@ -146,7 +148,7 @@ It renders a `<meta>` that React places in the `<head>`, so the page's own marku
|
|
|
146
148
|
|
|
147
149
|
## Names and Studio's origins
|
|
148
150
|
|
|
149
|
-
For a site that needs them in its own code (a route that sets its own headers, a CSP it writes itself), `@decocms/nextjs` exports what the proxy, the pages and `withDeco` use: `DRAFT_PARAM` (`__draft`), `DRAFT_COOKIE` (`__deco_draft`), `VERSION_HEADER` (`x-deco-version`), `VISITOR_HEADER` (`x-deco-visitor`), `ASSIGNMENTS_HEADER` (`x-deco-assignments`), `draftResponseHeaders(version)`, `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS` and `
|
|
151
|
+
For a site that needs them in its own code (a route that sets its own headers, a CSP it writes itself), `@decocms/nextjs` exports what the proxy, the pages and `withDeco` use: `DRAFT_PARAM` (`__draft`), `DRAFT_COOKIE` (`__deco_draft`), `VERSION_HEADER` (`x-deco-version`), `VISITOR_HEADER` (`x-deco-visitor`), `ASSIGNMENTS_HEADER` (`x-deco-assignments`), `draftResponseHeaders(version)`, `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin(origin)`, `DEFAULT_STUDIO_ORIGINS`, `studioOrigins(option?)` and `withStudioCsp(directives, options?)`. They have the same values as `@decocms/tanstack`'s; each binding keeps its own copy and never imports the other.
|
|
150
152
|
|
|
151
153
|
## next.config
|
|
152
154
|
|
package/dist/StudioBridge.d.ts
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `StudioBridge` of `@decocms/blocks/preview` with Studio's origins filled in
|
|
3
3
|
* (`STUDIO_ORIGINS`, `./studio.ts`): the package owns where Studio runs, so a
|
|
4
|
-
* site passes only its name.
|
|
5
|
-
*
|
|
4
|
+
* site passes only its name. With it, the Studio channel (`StudioChannel`):
|
|
5
|
+
* on every page, production included, framed by one of Studio's origins
|
|
6
|
+
* (`studioOrigins` / `DECO_STUDIO_ORIGIN` / Studio's production and staging
|
|
7
|
+
* origins, each exact) it loads the editor's own modules from that origin;
|
|
8
|
+
* otherwise it does nothing. Two inline scripts and no hooks, so a Server
|
|
9
|
+
* Component renders it as is. The site's CSP allows the same origins with
|
|
10
|
+
* `withStudioCsp`.
|
|
6
11
|
*/
|
|
7
12
|
import { type StudioBridgeProps as BridgeScriptProps } from "@decocms/blocks/preview";
|
|
8
13
|
import { type ReactNode } from "react";
|
|
9
|
-
/** `site`, and optionally `editShortcut` and `nonce`; Studio's origins are the package's. */
|
|
10
|
-
export
|
|
14
|
+
/** `site`, and optionally `editShortcut`, `page` and `nonce`; Studio's origins are the package's. */
|
|
15
|
+
export interface StudioBridgeProps extends Omit<BridgeScriptProps, "trustedOrigins"> {
|
|
16
|
+
/**
|
|
17
|
+
* The Studio origins the Studio channel trusts, each an exact origin.
|
|
18
|
+
* Default: `DECO_STUDIO_ORIGIN` (comma-separated), else Studio's production
|
|
19
|
+
* and staging origins (`DEFAULT_STUDIO_ORIGINS`). A site with a CSP passes
|
|
20
|
+
* the same list to `withStudioCsp`, which adds it to `script-src`.
|
|
21
|
+
*/
|
|
22
|
+
studioOrigins?: readonly string[];
|
|
23
|
+
}
|
|
11
24
|
/**
|
|
12
25
|
* Lets Studio's site editor select and edit sections on the page when Studio
|
|
13
26
|
* frames it. Render it once, in the root layout's `<body>`.
|
|
14
27
|
*/
|
|
15
|
-
export declare function StudioBridge(props: StudioBridgeProps): ReactNode;
|
|
28
|
+
export declare function StudioBridge({ studioOrigins: origins, ...props }: StudioBridgeProps): ReactNode;
|
|
16
29
|
//# sourceMappingURL=StudioBridge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StudioBridge.d.ts","sourceRoot":"","sources":["../src/StudioBridge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"StudioBridge.d.ts","sourceRoot":"","sources":["../src/StudioBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAEL,KAAK,iBAAiB,IAAI,iBAAiB,EAE5C,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAA2B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGhE,qGAAqG;AACrG,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,CAAC;IAClF;;;;;OAKG;IACH,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAO/F"}
|
package/dist/StudioBridge.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `StudioBridge` of `@decocms/blocks/preview` with Studio's origins filled in
|
|
3
3
|
* (`STUDIO_ORIGINS`, `./studio.ts`): the package owns where Studio runs, so a
|
|
4
|
-
* site passes only its name.
|
|
5
|
-
*
|
|
4
|
+
* site passes only its name. With it, the Studio channel (`StudioChannel`):
|
|
5
|
+
* on every page, production included, framed by one of Studio's origins
|
|
6
|
+
* (`studioOrigins` / `DECO_STUDIO_ORIGIN` / Studio's production and staging
|
|
7
|
+
* origins, each exact) it loads the editor's own modules from that origin;
|
|
8
|
+
* otherwise it does nothing. Two inline scripts and no hooks, so a Server
|
|
9
|
+
* Component renders it as is. The site's CSP allows the same origins with
|
|
10
|
+
* `withStudioCsp`.
|
|
6
11
|
*/
|
|
7
|
-
import { StudioBridge as BridgeScript, } from "@decocms/blocks/preview";
|
|
8
|
-
import { createElement } from "react";
|
|
9
|
-
import { STUDIO_ORIGINS } from "./studio.js";
|
|
12
|
+
import { StudioBridge as BridgeScript, StudioChannel, } from "@decocms/blocks/preview";
|
|
13
|
+
import { createElement, Fragment } from "react";
|
|
14
|
+
import { STUDIO_ORIGINS, studioOrigins } from "./studio.js";
|
|
10
15
|
/**
|
|
11
16
|
* Lets Studio's site editor select and edit sections on the page when Studio
|
|
12
17
|
* frames it. Render it once, in the root layout's `<body>`.
|
|
13
18
|
*/
|
|
14
|
-
export function StudioBridge(props) {
|
|
15
|
-
return createElement(BridgeScript, { ...props, trustedOrigins: STUDIO_ORIGINS });
|
|
19
|
+
export function StudioBridge({ studioOrigins: origins, ...props }) {
|
|
20
|
+
return createElement(Fragment, null, createElement(BridgeScript, { ...props, trustedOrigins: STUDIO_ORIGINS }), createElement(StudioChannel, { studioOrigins: studioOrigins(origins), nonce: props.nonce }));
|
|
16
21
|
}
|
|
17
22
|
//# sourceMappingURL=StudioBridge.js.map
|
package/dist/StudioBridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StudioBridge.js","sourceRoot":"","sources":["../src/StudioBridge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"StudioBridge.js","sourceRoot":"","sources":["../src/StudioBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,YAAY,IAAI,YAAY,EAE5B,aAAa,GACd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAa5D;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,KAAK,EAAqB;IAClF,OAAO,aAAa,CAClB,QAAQ,EACR,IAAI,EACJ,aAAa,CAAC,YAAY,EAAE,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC,EACzE,aAAa,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAC5F,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assignments.d.ts","sourceRoot":"","sources":["../src/assignments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,6DAA6D;IAC7D,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnC,oEAAoE;IACpE,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,oHAAoH;AACpH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"assignments.d.ts","sourceRoot":"","sources":["../src/assignments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,6DAA6D;IAC7D,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnC,oEAAoE;IACpE,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,oHAAoH;AACpH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;AAoCpF;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAE7B;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;IAAE,WAAW,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAqB/D;AASD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,kBAAkB,GAAG,MAAM,CAStE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AA8BxD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,eAAe,GAAG,MAAM,CAOvE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwB/E;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,MAAM,CAG3F;AAED,8GAA8G;AAC9G,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIhE;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAC5B,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,eAAe,CAAC,CAmB1B"}
|
package/dist/assignments.js
CHANGED
|
@@ -15,19 +15,15 @@ function record() {
|
|
|
15
15
|
return Object.create(null);
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* The saved `block` resolved, each name `true` or `false`; `null` when the
|
|
19
|
+
* content saves none. Whether it exists comes from the index (`NOT_FOUND`,
|
|
20
|
+
* which is no error to report), with no body listed.
|
|
20
21
|
*/
|
|
21
|
-
async function saves(client, type) {
|
|
22
|
-
const [list] = await client.list(type);
|
|
23
|
-
return (list?.length ?? 0) > 0;
|
|
24
|
-
}
|
|
25
|
-
/** The saved `block` resolved, each name `true` or `false`; `null` when the content saves none. */
|
|
26
22
|
async function decided(client, spec) {
|
|
27
23
|
try {
|
|
28
|
-
|
|
24
|
+
const [value, error] = await client.resolve(spec.block);
|
|
25
|
+
if (error?.code === "NOT_FOUND")
|
|
29
26
|
return null;
|
|
30
|
-
const [value] = await client.resolve(spec.block);
|
|
31
27
|
const out = record();
|
|
32
28
|
if (!isRecord(value))
|
|
33
29
|
return out;
|
package/dist/assignments.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assignments.js","sourceRoot":"","sources":["../src/assignments.ts"],"names":[],"mappings":"AA8BA;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAKvD,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAW,CAAC;AAC7E,MAAM,SAAS,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAW,CAAC;AAIpE,kEAAkE;AAClE,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAElF,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,SAAS,MAAM;IACb,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAgB,CAAC;AAC5C,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"assignments.js","sourceRoot":"","sources":["../src/assignments.ts"],"names":[],"mappings":"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"}
|
package/dist/csp.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** A Studio on this machine, in local development. */
|
|
2
|
+
export declare const LOOPBACK_ANCESTORS: readonly string[];
|
|
3
|
+
export interface StudioCspOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Studio's origins; default {@link studioOrigins}() (`DECO_STUDIO_ORIGIN`,
|
|
6
|
+
* else production and staging). Read through {@link studioOrigins} like any
|
|
7
|
+
* other value: an entry that isn't an exact http(s) origin is dropped.
|
|
8
|
+
*/
|
|
9
|
+
studioOrigins?: readonly string[];
|
|
10
|
+
/** More origins that may frame the site, besides Studio's (a marketing tool's editor). */
|
|
11
|
+
frameAncestors?: readonly string[];
|
|
12
|
+
/** Local development: loopback may frame the site. */
|
|
13
|
+
dev?: boolean;
|
|
14
|
+
/** Add `frame-ancestors` when the directives have none. Default `true`. */
|
|
15
|
+
addFrameAncestors?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** The directives of a policy (`"a; b"` or `["a", "b"]`), trimmed, empty ones dropped. */
|
|
18
|
+
export declare function cspDirectives(value: string | readonly string[] | undefined): string[];
|
|
19
|
+
/** The `frame-ancestors` sources: Studio's, the site's own, and loopback in development. */
|
|
20
|
+
export declare function studioFrameAncestors(options?: StudioCspOptions): string[];
|
|
21
|
+
/**
|
|
22
|
+
* A policy's directives with what Studio needs added: Studio's origins in
|
|
23
|
+
* `script-src` (and `script-src-elem`), so the Studio channel can import
|
|
24
|
+
* Studio's modules, and a `frame-ancestors` with Studio's (when the policy has
|
|
25
|
+
* none). A policy that restricts scripts only through `default-src` gets a
|
|
26
|
+
* `script-src` with `default-src`'s sources plus Studio's, which allows the
|
|
27
|
+
* same scripts as before and Studio's modules. A policy that doesn't restrict
|
|
28
|
+
* scripts, or forbids them all (`'none'`), keeps its scripts as they are.
|
|
29
|
+
* A site's own `frame-ancestors` is kept as is.
|
|
30
|
+
*/
|
|
31
|
+
export declare function withStudioCsp(directives: string | readonly string[] | undefined, options?: StudioCspOptions): string[];
|
|
32
|
+
//# sourceMappingURL=csp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.d.ts","sourceRoot":"","sources":["../src/csp.ts"],"names":[],"mappings":"AAYA,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAG9C,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,0FAA0F;IAC1F,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,sDAAsD;IACtD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAIrF;AAeD,4FAA4F;AAC5F,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,gBAAqB,GAAG,MAAM,EAAE,CAS7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,EAClD,OAAO,GAAE,gBAAqB,GAC7B,MAAM,EAAE,CAqBV"}
|
package/dist/csp.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What Studio needs in a page's Content Security Policy: its origins in
|
|
3
|
+
* `script-src` (the Studio channel imports Studio's modules from them) and in
|
|
4
|
+
* `frame-ancestors` (its site editor frames the page). A Next.js site writes
|
|
5
|
+
* its own policy (`next.config` headers, or its proxy); it passes the
|
|
6
|
+
* directives through {@link withStudioCsp}, which adds both. The origins are
|
|
7
|
+
* {@link studioOrigins}: the same list `StudioBridge`'s Studio channel trusts.
|
|
8
|
+
* The package's own copy (it never imports `@decocms/tanstack`). No framework
|
|
9
|
+
* code: safe in every bundle.
|
|
10
|
+
*/
|
|
11
|
+
import { STUDIO_FRAME_ANCESTORS, studioOrigins } from "./studio.js";
|
|
12
|
+
/** A Studio on this machine, in local development. */
|
|
13
|
+
export const LOOPBACK_ANCESTORS = Object.freeze([
|
|
14
|
+
"http://localhost:*",
|
|
15
|
+
"http://127.0.0.1:*",
|
|
16
|
+
]);
|
|
17
|
+
/** The directives of a policy (`"a; b"` or `["a", "b"]`), trimmed, empty ones dropped. */
|
|
18
|
+
export function cspDirectives(value) {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return [];
|
|
21
|
+
const list = typeof value === "string" ? value.split(";") : [...value];
|
|
22
|
+
return list.map((d) => d.trim()).filter(Boolean);
|
|
23
|
+
}
|
|
24
|
+
const nameOf = (directive) => (directive.split(/\s+/, 1)[0] ?? "").toLowerCase();
|
|
25
|
+
const isNone = (values) => values.length === 1 && values[0]?.toLowerCase() === "'none'";
|
|
26
|
+
/** `directive` with each of `sources` it lacks appended; one that is `'none'` stays as is. */
|
|
27
|
+
function withSources(directive, sources) {
|
|
28
|
+
const [name = "", ...values] = directive.split(/\s+/);
|
|
29
|
+
if (isNone(values))
|
|
30
|
+
return directive;
|
|
31
|
+
const have = new Set(values.map((v) => v.toLowerCase()));
|
|
32
|
+
const extra = sources.filter((s) => !have.has(s.toLowerCase()));
|
|
33
|
+
return extra.length ? [name, ...values, ...extra].join(" ") : directive;
|
|
34
|
+
}
|
|
35
|
+
/** The `frame-ancestors` sources: Studio's, the site's own, and loopback in development. */
|
|
36
|
+
export function studioFrameAncestors(options = {}) {
|
|
37
|
+
return [
|
|
38
|
+
...new Set([
|
|
39
|
+
...STUDIO_FRAME_ANCESTORS,
|
|
40
|
+
...studioOrigins(options.studioOrigins),
|
|
41
|
+
...(options.frameAncestors ?? []),
|
|
42
|
+
...(options.dev ? LOOPBACK_ANCESTORS : []),
|
|
43
|
+
]),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A policy's directives with what Studio needs added: Studio's origins in
|
|
48
|
+
* `script-src` (and `script-src-elem`), so the Studio channel can import
|
|
49
|
+
* Studio's modules, and a `frame-ancestors` with Studio's (when the policy has
|
|
50
|
+
* none). A policy that restricts scripts only through `default-src` gets a
|
|
51
|
+
* `script-src` with `default-src`'s sources plus Studio's, which allows the
|
|
52
|
+
* same scripts as before and Studio's modules. A policy that doesn't restrict
|
|
53
|
+
* scripts, or forbids them all (`'none'`), keeps its scripts as they are.
|
|
54
|
+
* A site's own `frame-ancestors` is kept as is.
|
|
55
|
+
*/
|
|
56
|
+
export function withStudioCsp(directives, options = {}) {
|
|
57
|
+
const origins = studioOrigins(options.studioOrigins);
|
|
58
|
+
let list = cspDirectives(directives);
|
|
59
|
+
if (origins.length) {
|
|
60
|
+
const isScript = (d) => ["script-src", "script-src-elem"].includes(nameOf(d));
|
|
61
|
+
if (list.some(isScript)) {
|
|
62
|
+
list = list.map((d) => (isScript(d) ? withSources(d, origins) : d));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const fallback = list.find((d) => nameOf(d) === "default-src");
|
|
66
|
+
const values = fallback?.split(/\s+/).slice(1) ?? [];
|
|
67
|
+
if (fallback && !isNone(values)) {
|
|
68
|
+
list.push(withSources(["script-src", ...values].join(" "), origins));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (options.addFrameAncestors !== false && !list.some((d) => nameOf(d) === "frame-ancestors")) {
|
|
73
|
+
list.push(`frame-ancestors ${studioFrameAncestors({ ...options, studioOrigins: origins }).join(" ")}`);
|
|
74
|
+
}
|
|
75
|
+
return list;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=csp.js.map
|
package/dist/csp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.js","sourceRoot":"","sources":["../src/csp.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,25 +8,33 @@
|
|
|
8
8
|
* lets Studio's site editor drive the page when Studio frames it
|
|
9
9
|
* (`@decocms/blocks/preview`'s, answering `STUDIO_ORIGINS`). It renders an
|
|
10
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.
|
|
11
|
+
* Outside Studio's frame, "." opens the page in Studio's editor. It also
|
|
12
|
+
* mounts the Studio channel (`StudioChannel`), through which Studio loads its
|
|
13
|
+
* own modules into the page when it frames it (`studioOrigins`).
|
|
12
14
|
* - `StudioPage`: rendered inside each CMS page, `<StudioPage pathTemplate={page.path} />`,
|
|
13
15
|
* tells the bridge which page "." opens (the root layout doesn't re-render per page).
|
|
14
16
|
* - `DRAFT_PARAM`, `DRAFT_COOKIE`, `VERSION_HEADER`, `draftResponseHeaders`,
|
|
15
17
|
* `VISITOR_HEADER`, `ASSIGNMENTS_HEADER`,
|
|
16
|
-
* `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin
|
|
18
|
+
* `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin`,
|
|
19
|
+
* `STUDIO_ORIGIN`, `DEFAULT_STUDIO_ORIGINS`, `studioOrigins`: for a site
|
|
17
20
|
* that needs them in its own code (a route that sets its own headers, a CSP
|
|
18
21
|
* it writes itself). The package's own copy: it never imports
|
|
19
22
|
* `@decocms/tanstack`.
|
|
20
23
|
*
|
|
24
|
+
* - `withStudioCsp(directives)`: the site's CSP with Studio's origins added to
|
|
25
|
+
* `script-src` (the Studio channel's modules) and `frame-ancestors` (when it
|
|
26
|
+
* has none), the same list the Studio channel trusts.
|
|
27
|
+
*
|
|
21
28
|
* The proxy (`/proxy`), the pages (`/server`), `next.config` (`/config`) and the
|
|
22
29
|
* image loader (`/image-loader`) are separate subpaths, so the proxy never
|
|
23
30
|
* loads React.
|
|
24
31
|
*/
|
|
25
32
|
export { DraftBadge, StudioPage, type StudioPageInfo } from "@decocms/blocks/preview";
|
|
26
33
|
export { ASSIGNMENTS_HEADER } from "./assignments.ts";
|
|
34
|
+
export { type StudioCspOptions, withStudioCsp } from "./csp.ts";
|
|
27
35
|
export { DraftPreviewIndicator, type DraftPreviewIndicatorProps, } from "./DraftPreviewIndicator.tsx";
|
|
28
36
|
export { DRAFT_COOKIE, DRAFT_PARAM, draftResponseHeaders, VERSION_HEADER } from "./draft.ts";
|
|
29
37
|
export { StudioBridge, type StudioBridgeProps } from "./StudioBridge.ts";
|
|
30
|
-
export { isStudioOrigin, STUDIO_FRAME_ANCESTORS, STUDIO_ORIGINS } from "./studio.ts";
|
|
38
|
+
export { DEFAULT_STUDIO_ORIGINS, isStudioOrigin, STUDIO_FRAME_ANCESTORS, STUDIO_ORIGIN, STUDIO_ORIGINS, studioOrigins, } from "./studio.ts";
|
|
31
39
|
export { VISITOR_HEADER } from "./visitor.ts";
|
|
32
40
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,KAAK,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,25 +8,33 @@
|
|
|
8
8
|
* lets Studio's site editor drive the page when Studio frames it
|
|
9
9
|
* (`@decocms/blocks/preview`'s, answering `STUDIO_ORIGINS`). It renders an
|
|
10
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.
|
|
11
|
+
* Outside Studio's frame, "." opens the page in Studio's editor. It also
|
|
12
|
+
* mounts the Studio channel (`StudioChannel`), through which Studio loads its
|
|
13
|
+
* own modules into the page when it frames it (`studioOrigins`).
|
|
12
14
|
* - `StudioPage`: rendered inside each CMS page, `<StudioPage pathTemplate={page.path} />`,
|
|
13
15
|
* tells the bridge which page "." opens (the root layout doesn't re-render per page).
|
|
14
16
|
* - `DRAFT_PARAM`, `DRAFT_COOKIE`, `VERSION_HEADER`, `draftResponseHeaders`,
|
|
15
17
|
* `VISITOR_HEADER`, `ASSIGNMENTS_HEADER`,
|
|
16
|
-
* `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin
|
|
18
|
+
* `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin`,
|
|
19
|
+
* `STUDIO_ORIGIN`, `DEFAULT_STUDIO_ORIGINS`, `studioOrigins`: for a site
|
|
17
20
|
* that needs them in its own code (a route that sets its own headers, a CSP
|
|
18
21
|
* it writes itself). The package's own copy: it never imports
|
|
19
22
|
* `@decocms/tanstack`.
|
|
20
23
|
*
|
|
24
|
+
* - `withStudioCsp(directives)`: the site's CSP with Studio's origins added to
|
|
25
|
+
* `script-src` (the Studio channel's modules) and `frame-ancestors` (when it
|
|
26
|
+
* has none), the same list the Studio channel trusts.
|
|
27
|
+
*
|
|
21
28
|
* The proxy (`/proxy`), the pages (`/server`), `next.config` (`/config`) and the
|
|
22
29
|
* image loader (`/image-loader`) are separate subpaths, so the proxy never
|
|
23
30
|
* loads React.
|
|
24
31
|
*/
|
|
25
32
|
export { DraftBadge, StudioPage } from "@decocms/blocks/preview";
|
|
26
33
|
export { ASSIGNMENTS_HEADER } from "./assignments.js";
|
|
34
|
+
export { withStudioCsp } from "./csp.js";
|
|
27
35
|
export { DraftPreviewIndicator, } from "./DraftPreviewIndicator.js";
|
|
28
36
|
export { DRAFT_COOKIE, DRAFT_PARAM, draftResponseHeaders, VERSION_HEADER } from "./draft.js";
|
|
29
37
|
export { StudioBridge } from "./StudioBridge.js";
|
|
30
|
-
export { isStudioOrigin, STUDIO_FRAME_ANCESTORS, STUDIO_ORIGINS } from "./studio.js";
|
|
38
|
+
export { DEFAULT_STUDIO_ORIGINS, isStudioOrigin, STUDIO_FRAME_ANCESTORS, STUDIO_ORIGIN, STUDIO_ORIGINS, studioOrigins, } from "./studio.js";
|
|
31
39
|
export { VISITOR_HEADER } from "./visitor.js";
|
|
32
40
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAuB,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAyB,aAAa,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EACL,qBAAqB,GAEtB,MAAM,4BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/proxy/index.js
CHANGED
|
@@ -62,7 +62,7 @@ export async function decideDraft(cms, request) {
|
|
|
62
62
|
*/
|
|
63
63
|
function isDraftGone(error) {
|
|
64
64
|
for (let e = error, depth = 0; e instanceof Error && depth < 5; e = e.cause, depth++) {
|
|
65
|
-
if (/draft changes: HTTP (404|410)\b/.test(e.message))
|
|
65
|
+
if (/draft (?:changes|index): HTTP (?:404|410)\b/.test(e.message))
|
|
66
66
|
return true;
|
|
67
67
|
}
|
|
68
68
|
return false;
|
package/dist/proxy/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAgB,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAAoB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EAEtB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAwC3C,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EAEtB,uBAAuB,EAEvB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,KAAK,GAAkB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/E,8FAA8F;AAC9F,MAAM,eAAe,GAAkB,MAAM,CAAC,MAAM,CAAC;IACnD,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;CACd,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAc,EACd,OAA8B;IAE9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/E,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,gFAAgF;IAChF,IAAI,WAAW,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,CAAC;IACjF,qEAAqE;IACrE,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IAC5D,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,eAAe,CAAC;IAC7C,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,wEAAwE;YACxE,6EAA6E;YAC7E,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9D,OAAO,EAAE,GAAG,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO;QACL,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;QAC9D,OAAO,EAAE,IAAI;QACb,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,KAAc;IACjC,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,YAAY,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;QACrF,IAAI,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"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"}
|
package/dist/studio.d.ts
CHANGED
|
@@ -21,6 +21,26 @@ export declare const STUDIO_ORIGINS: readonly string[];
|
|
|
21
21
|
* Studio runs on the same machine.
|
|
22
22
|
*/
|
|
23
23
|
export declare const STUDIO_FRAME_ANCESTORS: readonly string[];
|
|
24
|
+
/** Studio's production origin. */
|
|
25
|
+
export declare const STUDIO_ORIGIN = "https://studio.decocms.com";
|
|
26
|
+
/**
|
|
27
|
+
* The exact origins a page trusts for the Studio channel by default, and adds
|
|
28
|
+
* to its CSP (`script-src`, `frame-ancestors`): Studio's production and
|
|
29
|
+
* staging origins. No wildcard: each one is compared exactly.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEFAULT_STUDIO_ORIGINS: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* The Studio origins the page trusts: the Studio channel accepts messages and
|
|
34
|
+
* modules from them, and the binding adds them to the page's CSP `script-src`
|
|
35
|
+
* and `frame-ancestors`. The binding option when given (an empty list turns
|
|
36
|
+
* the channel off), else the `DECO_STUDIO_ORIGIN` environment variable, a
|
|
37
|
+
* comma-separated list (a local or staging Studio, such as
|
|
38
|
+
* `http://localhost:3000`; read where `process.env` exists, so on the
|
|
39
|
+
* server), else {@link DEFAULT_STUDIO_ORIGINS}. Each entry must be an exact
|
|
40
|
+
* http(s) origin; any other value (a path, a wildcard, a CSP keyword) is
|
|
41
|
+
* dropped, so the list never widens the CSP beyond origins.
|
|
42
|
+
*/
|
|
43
|
+
export declare function studioOrigins(option?: readonly string[]): readonly string[];
|
|
24
44
|
/**
|
|
25
45
|
* Whether `origin` (a `MessageEvent.origin`, an `Origin` header) is one of
|
|
26
46
|
* {@link STUDIO_ORIGINS}: `https`, the default port, and `decocms.com` or a
|
package/dist/studio.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAK1C,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAIlD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAazE"}
|
|
1
|
+
{"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAK1C,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAIlD,CAAC;AAEH,kCAAkC;AAClC,eAAO,MAAM,aAAa,+BAA+B,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAGlD,CAAC;AAkBH;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAK3E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAazE"}
|
package/dist/studio.js
CHANGED
|
@@ -30,6 +30,53 @@ export const STUDIO_FRAME_ANCESTORS = Object.freeze([
|
|
|
30
30
|
...STUDIO_ORIGINS,
|
|
31
31
|
"https://*.deco.studio",
|
|
32
32
|
]);
|
|
33
|
+
/** Studio's production origin. */
|
|
34
|
+
export const STUDIO_ORIGIN = "https://studio.decocms.com";
|
|
35
|
+
/**
|
|
36
|
+
* The exact origins a page trusts for the Studio channel by default, and adds
|
|
37
|
+
* to its CSP (`script-src`, `frame-ancestors`): Studio's production and
|
|
38
|
+
* staging origins. No wildcard: each one is compared exactly.
|
|
39
|
+
*/
|
|
40
|
+
export const DEFAULT_STUDIO_ORIGINS = Object.freeze([
|
|
41
|
+
STUDIO_ORIGIN,
|
|
42
|
+
"https://studio-stg.decocms.com",
|
|
43
|
+
]);
|
|
44
|
+
/** `value` as an exact http(s) origin (`https://host[:port]`, a trailing `/` dropped; no wildcard), or `""`. */
|
|
45
|
+
function asOrigin(value) {
|
|
46
|
+
if (typeof value !== "string")
|
|
47
|
+
return "";
|
|
48
|
+
const trimmed = value.trim();
|
|
49
|
+
try {
|
|
50
|
+
const url = new URL(trimmed);
|
|
51
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
52
|
+
return "";
|
|
53
|
+
// A host name or an IP address: a URL parser accepts `*` in a host, which a
|
|
54
|
+
// CSP source would read as a wildcard.
|
|
55
|
+
if (!/^(\[[0-9a-f:.]+\]|[a-z0-9_.-]+)$/.test(url.hostname))
|
|
56
|
+
return "";
|
|
57
|
+
return trimmed === url.origin || trimmed === `${url.origin}/` ? url.origin : "";
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The Studio origins the page trusts: the Studio channel accepts messages and
|
|
65
|
+
* modules from them, and the binding adds them to the page's CSP `script-src`
|
|
66
|
+
* and `frame-ancestors`. The binding option when given (an empty list turns
|
|
67
|
+
* the channel off), else the `DECO_STUDIO_ORIGIN` environment variable, a
|
|
68
|
+
* comma-separated list (a local or staging Studio, such as
|
|
69
|
+
* `http://localhost:3000`; read where `process.env` exists, so on the
|
|
70
|
+
* server), else {@link DEFAULT_STUDIO_ORIGINS}. Each entry must be an exact
|
|
71
|
+
* http(s) origin; any other value (a path, a wildcard, a CSP keyword) is
|
|
72
|
+
* dropped, so the list never widens the CSP beyond origins.
|
|
73
|
+
*/
|
|
74
|
+
export function studioOrigins(option) {
|
|
75
|
+
const env = globalThis.process
|
|
76
|
+
?.env?.DECO_STUDIO_ORIGIN;
|
|
77
|
+
const list = option ?? (env?.trim() ? env.split(",") : DEFAULT_STUDIO_ORIGINS);
|
|
78
|
+
return [...new Set(list.map(asOrigin).filter(Boolean))];
|
|
79
|
+
}
|
|
33
80
|
/**
|
|
34
81
|
* Whether `origin` (a `MessageEvent.origin`, an `Origin` header) is one of
|
|
35
82
|
* {@link STUDIO_ORIGINS}: `https`, the default port, and `decocms.com` or a
|
package/dist/studio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio.js","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC7D,4BAA4B;IAC5B,gCAAgC;IAChC,qBAAqB;IACrB,uBAAuB;CACxB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACrE,QAAQ;IACR,GAAG,cAAc;IACjB,uBAAuB;CACxB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAiC;IAC9D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,8EAA8E;IAC9E,yBAAyB;IACzB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACxF,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1B,OAAO,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACjE,CAAC"}
|
|
1
|
+
{"version":3,"file":"studio.js","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC7D,4BAA4B;IAC5B,gCAAgC;IAChC,qBAAqB;IACrB,uBAAuB;CACxB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACrE,QAAQ;IACR,GAAG,cAAc;IACjB,uBAAuB;CACxB,CAAC,CAAC;AAEH,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,4BAA4B,CAAC;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACrE,aAAa;IACb,gCAAgC;CACjC,CAAC,CAAC;AAEH,gHAAgH;AAChH,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,EAAE,CAAC;QACrE,4EAA4E;QAC5E,uCAAuC;QACvC,IAAI,CAAC,kCAAkC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QACtE,OAAO,OAAO,KAAK,GAAG,CAAC,MAAM,IAAI,OAAO,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,MAAM,GAAG,GAAI,UAAyE,CAAC,OAAO;QAC5F,EAAE,GAAG,EAAE,kBAAkB,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAiC;IAC9D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,8EAA8E;IAC9E,yBAAyB;IACzB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACxF,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1B,OAAO,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACjE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/nextjs",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24"
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@decocms/blocks": "8.
|
|
70
|
+
"@decocms/blocks": "8.10.0",
|
|
71
71
|
"@opentelemetry/api": "^1.9.1",
|
|
72
72
|
"@types/react": "^19.0.0",
|
|
73
73
|
"@types/react-dom": "^19.0.0",
|
package/src/StudioBridge.ts
CHANGED
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `StudioBridge` of `@decocms/blocks/preview` with Studio's origins filled in
|
|
3
3
|
* (`STUDIO_ORIGINS`, `./studio.ts`): the package owns where Studio runs, so a
|
|
4
|
-
* site passes only its name.
|
|
5
|
-
*
|
|
4
|
+
* site passes only its name. With it, the Studio channel (`StudioChannel`):
|
|
5
|
+
* on every page, production included, framed by one of Studio's origins
|
|
6
|
+
* (`studioOrigins` / `DECO_STUDIO_ORIGIN` / Studio's production and staging
|
|
7
|
+
* origins, each exact) it loads the editor's own modules from that origin;
|
|
8
|
+
* otherwise it does nothing. Two inline scripts and no hooks, so a Server
|
|
9
|
+
* Component renders it as is. The site's CSP allows the same origins with
|
|
10
|
+
* `withStudioCsp`.
|
|
6
11
|
*/
|
|
7
12
|
import {
|
|
8
13
|
StudioBridge as BridgeScript,
|
|
9
14
|
type StudioBridgeProps as BridgeScriptProps,
|
|
15
|
+
StudioChannel,
|
|
10
16
|
} from "@decocms/blocks/preview";
|
|
11
|
-
import { createElement, type ReactNode } from "react";
|
|
12
|
-
import { STUDIO_ORIGINS } from "./studio.ts";
|
|
17
|
+
import { createElement, Fragment, type ReactNode } from "react";
|
|
18
|
+
import { STUDIO_ORIGINS, studioOrigins } from "./studio.ts";
|
|
13
19
|
|
|
14
|
-
/** `site`, and optionally `editShortcut` and `nonce`; Studio's origins are the package's. */
|
|
15
|
-
export
|
|
20
|
+
/** `site`, and optionally `editShortcut`, `page` and `nonce`; Studio's origins are the package's. */
|
|
21
|
+
export interface StudioBridgeProps extends Omit<BridgeScriptProps, "trustedOrigins"> {
|
|
22
|
+
/**
|
|
23
|
+
* The Studio origins the Studio channel trusts, each an exact origin.
|
|
24
|
+
* Default: `DECO_STUDIO_ORIGIN` (comma-separated), else Studio's production
|
|
25
|
+
* and staging origins (`DEFAULT_STUDIO_ORIGINS`). A site with a CSP passes
|
|
26
|
+
* the same list to `withStudioCsp`, which adds it to `script-src`.
|
|
27
|
+
*/
|
|
28
|
+
studioOrigins?: readonly string[];
|
|
29
|
+
}
|
|
16
30
|
|
|
17
31
|
/**
|
|
18
32
|
* Lets Studio's site editor select and edit sections on the page when Studio
|
|
19
33
|
* frames it. Render it once, in the root layout's `<body>`.
|
|
20
34
|
*/
|
|
21
|
-
export function StudioBridge(props: StudioBridgeProps): ReactNode {
|
|
22
|
-
return createElement(
|
|
35
|
+
export function StudioBridge({ studioOrigins: origins, ...props }: StudioBridgeProps): ReactNode {
|
|
36
|
+
return createElement(
|
|
37
|
+
Fragment,
|
|
38
|
+
null,
|
|
39
|
+
createElement(BridgeScript, { ...props, trustedOrigins: STUDIO_ORIGINS }),
|
|
40
|
+
createElement(StudioChannel, { studioOrigins: studioOrigins(origins), nonce: props.nonce }),
|
|
41
|
+
);
|
|
23
42
|
}
|
package/src/assignments.ts
CHANGED
|
@@ -55,22 +55,14 @@ function record(): Assignments {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* The saved `block` resolved, each name `true` or `false`; `null` when the
|
|
59
|
+
* content saves none. Whether it exists comes from the index (`NOT_FOUND`,
|
|
60
|
+
* which is no error to report), with no body listed.
|
|
60
61
|
*/
|
|
61
|
-
async function
|
|
62
|
-
const [list] = await client.list(type);
|
|
63
|
-
return (list?.length ?? 0) > 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** The saved `block` resolved, each name `true` or `false`; `null` when the content saves none. */
|
|
67
|
-
async function decided(
|
|
68
|
-
client: Reader,
|
|
69
|
-
spec: { block: string; type: string },
|
|
70
|
-
): Promise<Assignments | null> {
|
|
62
|
+
async function decided(client: Reader, spec: { block: string }): Promise<Assignments | null> {
|
|
71
63
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
const [value, error] = await client.resolve<unknown>(spec.block);
|
|
65
|
+
if (error?.code === "NOT_FOUND") return null;
|
|
74
66
|
const out = record();
|
|
75
67
|
if (!isRecord(value)) return out;
|
|
76
68
|
for (const name of Object.keys(value).sort(byCodeUnit)) out[name] = value[name] === true;
|
package/src/csp.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What Studio needs in a page's Content Security Policy: its origins in
|
|
3
|
+
* `script-src` (the Studio channel imports Studio's modules from them) and in
|
|
4
|
+
* `frame-ancestors` (its site editor frames the page). A Next.js site writes
|
|
5
|
+
* its own policy (`next.config` headers, or its proxy); it passes the
|
|
6
|
+
* directives through {@link withStudioCsp}, which adds both. The origins are
|
|
7
|
+
* {@link studioOrigins}: the same list `StudioBridge`'s Studio channel trusts.
|
|
8
|
+
* The package's own copy (it never imports `@decocms/tanstack`). No framework
|
|
9
|
+
* code: safe in every bundle.
|
|
10
|
+
*/
|
|
11
|
+
import { STUDIO_FRAME_ANCESTORS, studioOrigins } from "./studio.ts";
|
|
12
|
+
|
|
13
|
+
/** A Studio on this machine, in local development. */
|
|
14
|
+
export const LOOPBACK_ANCESTORS: readonly string[] = Object.freeze([
|
|
15
|
+
"http://localhost:*",
|
|
16
|
+
"http://127.0.0.1:*",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
export interface StudioCspOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Studio's origins; default {@link studioOrigins}() (`DECO_STUDIO_ORIGIN`,
|
|
22
|
+
* else production and staging). Read through {@link studioOrigins} like any
|
|
23
|
+
* other value: an entry that isn't an exact http(s) origin is dropped.
|
|
24
|
+
*/
|
|
25
|
+
studioOrigins?: readonly string[];
|
|
26
|
+
/** More origins that may frame the site, besides Studio's (a marketing tool's editor). */
|
|
27
|
+
frameAncestors?: readonly string[];
|
|
28
|
+
/** Local development: loopback may frame the site. */
|
|
29
|
+
dev?: boolean;
|
|
30
|
+
/** Add `frame-ancestors` when the directives have none. Default `true`. */
|
|
31
|
+
addFrameAncestors?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The directives of a policy (`"a; b"` or `["a", "b"]`), trimmed, empty ones dropped. */
|
|
35
|
+
export function cspDirectives(value: string | readonly string[] | undefined): string[] {
|
|
36
|
+
if (value === undefined) return [];
|
|
37
|
+
const list = typeof value === "string" ? value.split(";") : [...value];
|
|
38
|
+
return list.map((d) => d.trim()).filter(Boolean);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const nameOf = (directive: string) => (directive.split(/\s+/, 1)[0] ?? "").toLowerCase();
|
|
42
|
+
const isNone = (values: readonly string[]) =>
|
|
43
|
+
values.length === 1 && values[0]?.toLowerCase() === "'none'";
|
|
44
|
+
|
|
45
|
+
/** `directive` with each of `sources` it lacks appended; one that is `'none'` stays as is. */
|
|
46
|
+
function withSources(directive: string, sources: readonly string[]): string {
|
|
47
|
+
const [name = "", ...values] = directive.split(/\s+/);
|
|
48
|
+
if (isNone(values)) return directive;
|
|
49
|
+
const have = new Set(values.map((v) => v.toLowerCase()));
|
|
50
|
+
const extra = sources.filter((s) => !have.has(s.toLowerCase()));
|
|
51
|
+
return extra.length ? [name, ...values, ...extra].join(" ") : directive;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The `frame-ancestors` sources: Studio's, the site's own, and loopback in development. */
|
|
55
|
+
export function studioFrameAncestors(options: StudioCspOptions = {}): string[] {
|
|
56
|
+
return [
|
|
57
|
+
...new Set([
|
|
58
|
+
...STUDIO_FRAME_ANCESTORS,
|
|
59
|
+
...studioOrigins(options.studioOrigins),
|
|
60
|
+
...(options.frameAncestors ?? []),
|
|
61
|
+
...(options.dev ? LOOPBACK_ANCESTORS : []),
|
|
62
|
+
]),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A policy's directives with what Studio needs added: Studio's origins in
|
|
68
|
+
* `script-src` (and `script-src-elem`), so the Studio channel can import
|
|
69
|
+
* Studio's modules, and a `frame-ancestors` with Studio's (when the policy has
|
|
70
|
+
* none). A policy that restricts scripts only through `default-src` gets a
|
|
71
|
+
* `script-src` with `default-src`'s sources plus Studio's, which allows the
|
|
72
|
+
* same scripts as before and Studio's modules. A policy that doesn't restrict
|
|
73
|
+
* scripts, or forbids them all (`'none'`), keeps its scripts as they are.
|
|
74
|
+
* A site's own `frame-ancestors` is kept as is.
|
|
75
|
+
*/
|
|
76
|
+
export function withStudioCsp(
|
|
77
|
+
directives: string | readonly string[] | undefined,
|
|
78
|
+
options: StudioCspOptions = {},
|
|
79
|
+
): string[] {
|
|
80
|
+
const origins = studioOrigins(options.studioOrigins);
|
|
81
|
+
let list = cspDirectives(directives);
|
|
82
|
+
if (origins.length) {
|
|
83
|
+
const isScript = (d: string) => ["script-src", "script-src-elem"].includes(nameOf(d));
|
|
84
|
+
if (list.some(isScript)) {
|
|
85
|
+
list = list.map((d) => (isScript(d) ? withSources(d, origins) : d));
|
|
86
|
+
} else {
|
|
87
|
+
const fallback = list.find((d) => nameOf(d) === "default-src");
|
|
88
|
+
const values = fallback?.split(/\s+/).slice(1) ?? [];
|
|
89
|
+
if (fallback && !isNone(values)) {
|
|
90
|
+
list.push(withSources(["script-src", ...values].join(" "), origins));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (options.addFrameAncestors !== false && !list.some((d) => nameOf(d) === "frame-ancestors")) {
|
|
95
|
+
list.push(
|
|
96
|
+
`frame-ancestors ${studioFrameAncestors({ ...options, studioOrigins: origins }).join(" ")}`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return list;
|
|
100
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,16 +8,23 @@
|
|
|
8
8
|
* lets Studio's site editor drive the page when Studio frames it
|
|
9
9
|
* (`@decocms/blocks/preview`'s, answering `STUDIO_ORIGINS`). It renders an
|
|
10
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.
|
|
11
|
+
* Outside Studio's frame, "." opens the page in Studio's editor. It also
|
|
12
|
+
* mounts the Studio channel (`StudioChannel`), through which Studio loads its
|
|
13
|
+
* own modules into the page when it frames it (`studioOrigins`).
|
|
12
14
|
* - `StudioPage`: rendered inside each CMS page, `<StudioPage pathTemplate={page.path} />`,
|
|
13
15
|
* tells the bridge which page "." opens (the root layout doesn't re-render per page).
|
|
14
16
|
* - `DRAFT_PARAM`, `DRAFT_COOKIE`, `VERSION_HEADER`, `draftResponseHeaders`,
|
|
15
17
|
* `VISITOR_HEADER`, `ASSIGNMENTS_HEADER`,
|
|
16
|
-
* `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin
|
|
18
|
+
* `STUDIO_ORIGINS`, `STUDIO_FRAME_ANCESTORS`, `isStudioOrigin`,
|
|
19
|
+
* `STUDIO_ORIGIN`, `DEFAULT_STUDIO_ORIGINS`, `studioOrigins`: for a site
|
|
17
20
|
* that needs them in its own code (a route that sets its own headers, a CSP
|
|
18
21
|
* it writes itself). The package's own copy: it never imports
|
|
19
22
|
* `@decocms/tanstack`.
|
|
20
23
|
*
|
|
24
|
+
* - `withStudioCsp(directives)`: the site's CSP with Studio's origins added to
|
|
25
|
+
* `script-src` (the Studio channel's modules) and `frame-ancestors` (when it
|
|
26
|
+
* has none), the same list the Studio channel trusts.
|
|
27
|
+
*
|
|
21
28
|
* The proxy (`/proxy`), the pages (`/server`), `next.config` (`/config`) and the
|
|
22
29
|
* image loader (`/image-loader`) are separate subpaths, so the proxy never
|
|
23
30
|
* loads React.
|
|
@@ -25,11 +32,19 @@
|
|
|
25
32
|
|
|
26
33
|
export { DraftBadge, StudioPage, type StudioPageInfo } from "@decocms/blocks/preview";
|
|
27
34
|
export { ASSIGNMENTS_HEADER } from "./assignments.ts";
|
|
35
|
+
export { type StudioCspOptions, withStudioCsp } from "./csp.ts";
|
|
28
36
|
export {
|
|
29
37
|
DraftPreviewIndicator,
|
|
30
38
|
type DraftPreviewIndicatorProps,
|
|
31
39
|
} from "./DraftPreviewIndicator.tsx";
|
|
32
40
|
export { DRAFT_COOKIE, DRAFT_PARAM, draftResponseHeaders, VERSION_HEADER } from "./draft.ts";
|
|
33
41
|
export { StudioBridge, type StudioBridgeProps } from "./StudioBridge.ts";
|
|
34
|
-
export {
|
|
42
|
+
export {
|
|
43
|
+
DEFAULT_STUDIO_ORIGINS,
|
|
44
|
+
isStudioOrigin,
|
|
45
|
+
STUDIO_FRAME_ANCESTORS,
|
|
46
|
+
STUDIO_ORIGIN,
|
|
47
|
+
STUDIO_ORIGINS,
|
|
48
|
+
studioOrigins,
|
|
49
|
+
} from "./studio.ts";
|
|
35
50
|
export { VISITOR_HEADER } from "./visitor.ts";
|
package/src/proxy/index.ts
CHANGED
|
@@ -189,7 +189,7 @@ export async function decideDraft(
|
|
|
189
189
|
*/
|
|
190
190
|
function isDraftGone(error: unknown): boolean {
|
|
191
191
|
for (let e = error, depth = 0; e instanceof Error && depth < 5; e = e.cause, depth++) {
|
|
192
|
-
if (/draft changes: HTTP (404|410)\b/.test(e.message)) return true;
|
|
192
|
+
if (/draft (?:changes|index): HTTP (?:404|410)\b/.test(e.message)) return true;
|
|
193
193
|
}
|
|
194
194
|
return false;
|
|
195
195
|
}
|
package/src/studio.ts
CHANGED
|
@@ -33,6 +33,53 @@ export const STUDIO_FRAME_ANCESTORS: readonly string[] = Object.freeze([
|
|
|
33
33
|
"https://*.deco.studio",
|
|
34
34
|
]);
|
|
35
35
|
|
|
36
|
+
/** Studio's production origin. */
|
|
37
|
+
export const STUDIO_ORIGIN = "https://studio.decocms.com";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The exact origins a page trusts for the Studio channel by default, and adds
|
|
41
|
+
* to its CSP (`script-src`, `frame-ancestors`): Studio's production and
|
|
42
|
+
* staging origins. No wildcard: each one is compared exactly.
|
|
43
|
+
*/
|
|
44
|
+
export const DEFAULT_STUDIO_ORIGINS: readonly string[] = Object.freeze([
|
|
45
|
+
STUDIO_ORIGIN,
|
|
46
|
+
"https://studio-stg.decocms.com",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
/** `value` as an exact http(s) origin (`https://host[:port]`, a trailing `/` dropped; no wildcard), or `""`. */
|
|
50
|
+
function asOrigin(value: unknown): string {
|
|
51
|
+
if (typeof value !== "string") return "";
|
|
52
|
+
const trimmed = value.trim();
|
|
53
|
+
try {
|
|
54
|
+
const url = new URL(trimmed);
|
|
55
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return "";
|
|
56
|
+
// A host name or an IP address: a URL parser accepts `*` in a host, which a
|
|
57
|
+
// CSP source would read as a wildcard.
|
|
58
|
+
if (!/^(\[[0-9a-f:.]+\]|[a-z0-9_.-]+)$/.test(url.hostname)) return "";
|
|
59
|
+
return trimmed === url.origin || trimmed === `${url.origin}/` ? url.origin : "";
|
|
60
|
+
} catch {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The Studio origins the page trusts: the Studio channel accepts messages and
|
|
67
|
+
* modules from them, and the binding adds them to the page's CSP `script-src`
|
|
68
|
+
* and `frame-ancestors`. The binding option when given (an empty list turns
|
|
69
|
+
* the channel off), else the `DECO_STUDIO_ORIGIN` environment variable, a
|
|
70
|
+
* comma-separated list (a local or staging Studio, such as
|
|
71
|
+
* `http://localhost:3000`; read where `process.env` exists, so on the
|
|
72
|
+
* server), else {@link DEFAULT_STUDIO_ORIGINS}. Each entry must be an exact
|
|
73
|
+
* http(s) origin; any other value (a path, a wildcard, a CSP keyword) is
|
|
74
|
+
* dropped, so the list never widens the CSP beyond origins.
|
|
75
|
+
*/
|
|
76
|
+
export function studioOrigins(option?: readonly string[]): readonly string[] {
|
|
77
|
+
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process
|
|
78
|
+
?.env?.DECO_STUDIO_ORIGIN;
|
|
79
|
+
const list = option ?? (env?.trim() ? env.split(",") : DEFAULT_STUDIO_ORIGINS);
|
|
80
|
+
return [...new Set(list.map(asOrigin).filter(Boolean))];
|
|
81
|
+
}
|
|
82
|
+
|
|
36
83
|
/**
|
|
37
84
|
* Whether `origin` (a `MessageEvent.origin`, an `Origin` header) is one of
|
|
38
85
|
* {@link STUDIO_ORIGINS}: `https`, the default port, and `decocms.com` or a
|