@avocadostudio-ai/site-sdk 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor-cors.js +45 -3
- package/next-config.mjs +11 -0
- package/package.json +6 -6
package/dist/editor-cors.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* site's draft content out of its own browser. Now the development default is
|
|
10
10
|
* only assumed in development, and a deployment says what it allows.
|
|
11
11
|
*/
|
|
12
|
+
import { withLoopbackSpellings } from "@avocadostudio-ai/shared";
|
|
12
13
|
/*
|
|
13
14
|
* Both spellings of the same loopback address, because the product uses both.
|
|
14
15
|
*
|
|
@@ -19,7 +20,10 @@
|
|
|
19
20
|
* and the symptom is a blank iframe with a console message naming an origin
|
|
20
21
|
* that looks like it should have matched.
|
|
21
22
|
*
|
|
22
|
-
*
|
|
23
|
+
* This is the development default. A deployment still has to name its editor —
|
|
24
|
+
* but whichever spelling it names, `withLoopbackSpellings` now grants the
|
|
25
|
+
* other, which is what this constant had been quietly doing for development
|
|
26
|
+
* only. See below.
|
|
23
27
|
*/
|
|
24
28
|
const DEV_EDITOR_ORIGINS = ["http://localhost:4100", "http://127.0.0.1:4100"];
|
|
25
29
|
let cachedOrigins;
|
|
@@ -41,7 +45,23 @@ export function getEditorCorsOrigins() {
|
|
|
41
45
|
const declaredEditor = parseOrigins(process.env.NEXT_PUBLIC_EDITOR_ORIGIN);
|
|
42
46
|
const isProduction = process.env.NODE_ENV === "production";
|
|
43
47
|
const defaults = isProduction ? [] : DEV_EDITOR_ORIGINS;
|
|
44
|
-
|
|
48
|
+
/*
|
|
49
|
+
* Expand loopback spellings, which is what this function claimed to do and
|
|
50
|
+
* did not.
|
|
51
|
+
*
|
|
52
|
+
* `next-config.mjs`'s `avocadoEditorOrigins` — the `frame-ancestors` half of
|
|
53
|
+
* the same decision — says in its own comment that it *mirrors* this
|
|
54
|
+
* function, and it has run every origin through the same expansion since it
|
|
55
|
+
* was written. This one applied it to the development constant above and
|
|
56
|
+
* nowhere else, so a production deployment that wrote
|
|
57
|
+
* `EDITOR_CORS_ORIGINS=http://localhost:4100` allowed the frame from
|
|
58
|
+
* `127.0.0.1:4100` and refused every API call from it: the editor paints and
|
|
59
|
+
* is inert, which is strictly worse than either half failing alone.
|
|
60
|
+
*
|
|
61
|
+
* `editor-origin-parity.test.ts` now asserts the two produce the same set,
|
|
62
|
+
* so the next person to change one is told about the other.
|
|
63
|
+
*/
|
|
64
|
+
cachedOrigins = new Set(withLoopbackSpellings([...defaults, ...declaredEditor, ...configured]));
|
|
45
65
|
return cachedOrigins;
|
|
46
66
|
}
|
|
47
67
|
/** Test seam — the cache is a module global, and env changes must be able to take effect. */
|
|
@@ -59,7 +79,29 @@ export function applyEditorCors(response, requestOrigin) {
|
|
|
59
79
|
return response;
|
|
60
80
|
response.headers.set("Access-Control-Allow-Origin", requestOrigin);
|
|
61
81
|
response.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
62
|
-
|
|
82
|
+
/*
|
|
83
|
+
* `x-access-token` is not optional here, and leaving it out shut the editor.
|
|
84
|
+
*
|
|
85
|
+
* The editor installs a global `fetch` wrapper that stamps its access token on
|
|
86
|
+
* every request whose *origin* matches the orchestrator's — not whose path
|
|
87
|
+
* does. In library mode the orchestrator is mounted inside the site, so
|
|
88
|
+
* `/api/avocado/*` and `/api/editor/*` share an origin and both get the
|
|
89
|
+
* header. The orchestrator's own CORS lists it; this list did not, so once a
|
|
90
|
+
* production gate was configured and a token minted, the browser refused the
|
|
91
|
+
* preflight for `/api/editor/blocks` and `/api/editor/pages` — the component
|
|
92
|
+
* manifest the whole editing surface is derived from — with the requests
|
|
93
|
+
* never leaving the browser. The server saw nothing and logged nothing.
|
|
94
|
+
*
|
|
95
|
+
* It only bites in production with a gate on, which is why `curl` and every
|
|
96
|
+
* development run looked fine: in development no token exists to stamp.
|
|
97
|
+
*
|
|
98
|
+
* These names must stay in step with the `access-control-allow-headers` the
|
|
99
|
+
* orchestrator's own preflight sends, in
|
|
100
|
+
* `orchestrator-core/src/handler/create-orchestrator.ts`. A header one group accepts
|
|
101
|
+
* and the other rejects is indistinguishable, from the browser, from the
|
|
102
|
+
* server being down.
|
|
103
|
+
*/
|
|
104
|
+
response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, x-access-token");
|
|
63
105
|
return response;
|
|
64
106
|
}
|
|
65
107
|
export function createEditorCorsOptionsHandler() {
|
package/next-config.mjs
CHANGED
|
@@ -427,6 +427,17 @@ function builtOrchestratorCore(from, linked) {
|
|
|
427
427
|
* *intersection* — putting `frame-ancestors 'self'` back and blocking the
|
|
428
428
|
* frame it just allowed.
|
|
429
429
|
*/
|
|
430
|
+
/*
|
|
431
|
+
* A deliberate copy of `bothLoopbackSpellings` from
|
|
432
|
+
* `@avocadostudio-ai/shared`, not an import.
|
|
433
|
+
*
|
|
434
|
+
* This file is shipped as raw ESM and evaluated inside `next.config.mjs`,
|
|
435
|
+
* before anything is built or resolved, and a helper that can fail to import
|
|
436
|
+
* there breaks a user's build for a header. So the rule is duplicated and
|
|
437
|
+
* `packages/site-sdk/src/editor-origin-parity.test.ts` asserts the two agree
|
|
438
|
+
* on every input either can see. That test exists because this very expansion
|
|
439
|
+
* was applied here and not to CORS, and the disagreement shipped.
|
|
440
|
+
*/
|
|
430
441
|
function bothLoopbackSpellings(origin) {
|
|
431
442
|
if (origin.includes("localhost")) return [origin, origin.replace("localhost", "127.0.0.1")]
|
|
432
443
|
if (origin.includes("127.0.0.1")) return [origin, origin.replace("127.0.0.1", "localhost")]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avocadostudio-ai/site-sdk",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -147,17 +147,17 @@
|
|
|
147
147
|
],
|
|
148
148
|
"dependencies": {
|
|
149
149
|
"zod": "^4.3.6",
|
|
150
|
-
"@avocadostudio-ai/blocks": "^0.11.
|
|
151
|
-
"@avocadostudio-ai/preview-adapter": "^0.11.
|
|
152
|
-
"@avocadostudio-ai/shared": "^0.11.
|
|
153
|
-
"@avocadostudio-ai/richtext": "^0.11.
|
|
150
|
+
"@avocadostudio-ai/blocks": "^0.11.4",
|
|
151
|
+
"@avocadostudio-ai/preview-adapter": "^0.11.4",
|
|
152
|
+
"@avocadostudio-ai/shared": "^0.11.4",
|
|
153
|
+
"@avocadostudio-ai/richtext": "^0.11.4"
|
|
154
154
|
},
|
|
155
155
|
"peerDependencies": {
|
|
156
156
|
"next": ">=15.0.0",
|
|
157
157
|
"react": ">=19.0.0",
|
|
158
158
|
"react-dom": ">=19.0.0",
|
|
159
159
|
"better-sqlite3": ">=12.0.0",
|
|
160
|
-
"@avocadostudio-ai/orchestrator-core": "^0.11.
|
|
160
|
+
"@avocadostudio-ai/orchestrator-core": "^0.11.4"
|
|
161
161
|
},
|
|
162
162
|
"peerDependenciesMeta": {
|
|
163
163
|
"@avocadostudio-ai/orchestrator-core": {
|