@avocadostudio-ai/site-sdk 0.26.0 → 0.27.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/dist/draft-fetch.js +92 -5
- package/package.json +6 -6
package/dist/draft-fetch.js
CHANGED
|
@@ -18,10 +18,64 @@ import { pageDocSchemaLenient, siteConfigSchema } from "@avocadostudio-ai/shared
|
|
|
18
18
|
* may import the other. See `orchestrator-core/src/handler/library-mount.ts`.
|
|
19
19
|
*/
|
|
20
20
|
const LIBRARY_MOUNT_KEY = "__avocado_library_mount__";
|
|
21
|
+
function libraryMount() {
|
|
22
|
+
return globalThis[LIBRARY_MOUNT_KEY];
|
|
23
|
+
}
|
|
21
24
|
function libraryModeOrchestratorUrl() {
|
|
22
|
-
const mount =
|
|
25
|
+
const mount = libraryMount();
|
|
23
26
|
return mount?.observed ?? mount?.declared ?? null;
|
|
24
27
|
}
|
|
28
|
+
/*
|
|
29
|
+
* Read from this process's own orchestrator directly, when that is the one meant.
|
|
30
|
+
*
|
|
31
|
+
* Over HTTP a draft read is a request like any other, so a credentialed mount
|
|
32
|
+
* refuses it unless this site also holds `ORCHESTRATOR_ACCESS_TOKEN`. A site
|
|
33
|
+
* that set only the password rendered published content in its preview, with
|
|
34
|
+
* a 200 everywhere, and every edit landed in a draft nobody could see. A mount
|
|
35
|
+
* in this process registers a dispatcher instead (orchestrator-core's
|
|
36
|
+
* `library-mount.ts`), and a read through it needs no network, no URL and no
|
|
37
|
+
* credential.
|
|
38
|
+
*
|
|
39
|
+
* It is the mount that is meant unless something says otherwise: a per-call
|
|
40
|
+
* `orchestratorUrl` always goes over HTTP, and so does an `ORCHESTRATOR_URL`
|
|
41
|
+
* whose path is not this mount's — that names a different orchestrator.
|
|
42
|
+
* `ORCHESTRATOR_URL` pointing at the mount itself, the usual library-mode
|
|
43
|
+
* setting, still reads in-process.
|
|
44
|
+
*/
|
|
45
|
+
function inProcessDispatch(explicitUrl) {
|
|
46
|
+
if (explicitUrl)
|
|
47
|
+
return null;
|
|
48
|
+
const mount = libraryMount();
|
|
49
|
+
if (!mount?.dispatch || mount.basePath === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
const configured = process.env.ORCHESTRATOR_URL?.trim();
|
|
52
|
+
if (configured) {
|
|
53
|
+
let configuredPath;
|
|
54
|
+
try {
|
|
55
|
+
configuredPath = new URL(configured).pathname.replace(/\/+$/, "");
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (configuredPath !== mount.basePath)
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const { dispatch, basePath } = mount;
|
|
64
|
+
return (pathAndQuery) => dispatch(new Request(`http://in-process.invalid${basePath}${pathAndQuery}`));
|
|
65
|
+
}
|
|
66
|
+
/** The in-process answer, or null when the read should go over HTTP instead. */
|
|
67
|
+
async function readInProcess(pathAndQuery, options) {
|
|
68
|
+
const local = inProcessDispatch(options?.orchestratorUrl);
|
|
69
|
+
if (!local)
|
|
70
|
+
return null;
|
|
71
|
+
try {
|
|
72
|
+
return await local(pathAndQuery);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.warn(`[site-sdk/draft] in-process read of ${pathAndQuery.split("?")[0]} failed; trying HTTP —`, error);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
25
79
|
let warnedAboutFallback = false;
|
|
26
80
|
export function getOrchestratorUrl() {
|
|
27
81
|
const value = process.env.ORCHESTRATOR_URL?.trim();
|
|
@@ -116,7 +170,10 @@ function warnRefused(fn, status, hadToken) {
|
|
|
116
170
|
console.warn(`[site-sdk/draft] ${fn}: the orchestrator answered ${status}. ` +
|
|
117
171
|
(hadToken
|
|
118
172
|
? "The token that was sent is not the one it accepts — check ORCHESTRATOR_ACCESS_TOKEN on both sides."
|
|
119
|
-
: "No credential was sent.
|
|
173
|
+
: "No credential was sent. A library-mode site reads its own orchestrator in-process and needs " +
|
|
174
|
+
"none; this read went over HTTP because ORCHESTRATOR_URL names a different path, or because " +
|
|
175
|
+
"the orchestrator route has not been loaded in this process (separate serverless functions). " +
|
|
176
|
+
"Over HTTP, set ORCHESTRATOR_ACCESS_TOKEN or pass `accessToken`.") +
|
|
120
177
|
" Drafts are unavailable, so this page is rendering published content.");
|
|
121
178
|
}
|
|
122
179
|
/** For tests: forget which refusals have already been reported. */
|
|
@@ -124,6 +181,18 @@ export function resetDraftFetchWarnings() {
|
|
|
124
181
|
warned.clear();
|
|
125
182
|
}
|
|
126
183
|
export async function fetchEditorPage(slug, session, siteId, options) {
|
|
184
|
+
const query = `/draft/pages?session=${encodeURIComponent(session)}&siteId=${encodeURIComponent(siteId)}&slug=${encodeURIComponent(slug)}`;
|
|
185
|
+
const local = await readInProcess(query, options);
|
|
186
|
+
if (local) {
|
|
187
|
+
if (!local.ok)
|
|
188
|
+
return null;
|
|
189
|
+
const parsed = pageDocSchemaLenient.safeParse(await local.json());
|
|
190
|
+
if (parsed.success)
|
|
191
|
+
return parsed.data;
|
|
192
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
193
|
+
console.warn(`[site-sdk/draft] fetchEditorPage: schema parse failed for slug=${slug} session=${session} siteId=${siteId} — ${issues}`);
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
127
196
|
const configuredBaseUrl = options?.orchestratorUrl ?? getOrchestratorUrl();
|
|
128
197
|
if (!configuredBaseUrl)
|
|
129
198
|
return null;
|
|
@@ -132,7 +201,7 @@ export async function fetchEditorPage(slug, session, siteId, options) {
|
|
|
132
201
|
const baseUrls = buildCandidateBaseUrls(configuredBaseUrl);
|
|
133
202
|
for (const baseUrl of baseUrls) {
|
|
134
203
|
try {
|
|
135
|
-
const url = `${baseUrl}
|
|
204
|
+
const url = `${baseUrl}${query}`;
|
|
136
205
|
const res = await fetchWithTimeout(url, timeout, token);
|
|
137
206
|
if (REFUSAL_STATUSES.has(res.status)) {
|
|
138
207
|
warnRefused("fetchEditorPage", res.status, Boolean(token));
|
|
@@ -158,6 +227,16 @@ export async function fetchEditorPage(slug, session, siteId, options) {
|
|
|
158
227
|
return null;
|
|
159
228
|
}
|
|
160
229
|
export async function fetchEditorSlugs(session, siteId, options) {
|
|
230
|
+
const query = `/draft/slugs?session=${encodeURIComponent(session)}&siteId=${encodeURIComponent(siteId)}`;
|
|
231
|
+
const local = await readInProcess(query, options);
|
|
232
|
+
if (local) {
|
|
233
|
+
if (!local.ok)
|
|
234
|
+
return [];
|
|
235
|
+
const payload = (await local.json());
|
|
236
|
+
return Array.isArray(payload.slugs)
|
|
237
|
+
? payload.slugs.filter((item) => typeof item === "string" && item.length > 0)
|
|
238
|
+
: [];
|
|
239
|
+
}
|
|
161
240
|
const configuredBaseUrl = options?.orchestratorUrl ?? getOrchestratorUrl();
|
|
162
241
|
if (!configuredBaseUrl)
|
|
163
242
|
return [];
|
|
@@ -166,7 +245,7 @@ export async function fetchEditorSlugs(session, siteId, options) {
|
|
|
166
245
|
const baseUrls = buildCandidateBaseUrls(configuredBaseUrl);
|
|
167
246
|
for (const baseUrl of baseUrls) {
|
|
168
247
|
try {
|
|
169
|
-
const url = `${baseUrl}
|
|
248
|
+
const url = `${baseUrl}${query}`;
|
|
170
249
|
const res = await fetchWithTimeout(url, timeout, token);
|
|
171
250
|
if (REFUSAL_STATUSES.has(res.status)) {
|
|
172
251
|
warnRefused("fetchEditorSlugs", res.status, Boolean(token));
|
|
@@ -188,6 +267,14 @@ export async function fetchEditorSlugs(session, siteId, options) {
|
|
|
188
267
|
return [];
|
|
189
268
|
}
|
|
190
269
|
export async function fetchEditorSiteConfig(session, siteId, options) {
|
|
270
|
+
const query = `/draft/site-config?session=${encodeURIComponent(session)}&siteId=${encodeURIComponent(siteId)}`;
|
|
271
|
+
const local = await readInProcess(query, options);
|
|
272
|
+
if (local) {
|
|
273
|
+
if (!local.ok)
|
|
274
|
+
return {};
|
|
275
|
+
const parsed = siteConfigSchema.safeParse(await local.json());
|
|
276
|
+
return parsed.success ? parsed.data : {};
|
|
277
|
+
}
|
|
191
278
|
const configuredBaseUrl = options?.orchestratorUrl ?? getOrchestratorUrl();
|
|
192
279
|
if (!configuredBaseUrl)
|
|
193
280
|
return {};
|
|
@@ -196,7 +283,7 @@ export async function fetchEditorSiteConfig(session, siteId, options) {
|
|
|
196
283
|
const baseUrls = buildCandidateBaseUrls(configuredBaseUrl);
|
|
197
284
|
for (const baseUrl of baseUrls) {
|
|
198
285
|
try {
|
|
199
|
-
const url = `${baseUrl}
|
|
286
|
+
const url = `${baseUrl}${query}`;
|
|
200
287
|
const res = await fetchWithTimeout(url, timeout, token);
|
|
201
288
|
if (REFUSAL_STATUSES.has(res.status)) {
|
|
202
289
|
warnRefused("fetchEditorSiteConfig", res.status, Boolean(token));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avocadostudio-ai/site-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -159,17 +159,17 @@
|
|
|
159
159
|
],
|
|
160
160
|
"dependencies": {
|
|
161
161
|
"zod": "^4.3.6",
|
|
162
|
-
"@avocadostudio-ai/blocks": "^0.
|
|
163
|
-
"@avocadostudio-ai/preview-adapter": "^0.
|
|
164
|
-
"@avocadostudio-ai/shared": "^0.
|
|
165
|
-
"@avocadostudio-ai/richtext": "^0.
|
|
162
|
+
"@avocadostudio-ai/blocks": "^0.27.0",
|
|
163
|
+
"@avocadostudio-ai/preview-adapter": "^0.27.0",
|
|
164
|
+
"@avocadostudio-ai/shared": "^0.27.0",
|
|
165
|
+
"@avocadostudio-ai/richtext": "^0.27.0"
|
|
166
166
|
},
|
|
167
167
|
"peerDependencies": {
|
|
168
168
|
"better-sqlite3": ">=12.0.0",
|
|
169
169
|
"next": ">=15.0.0",
|
|
170
170
|
"react": ">=19.0.0",
|
|
171
171
|
"react-dom": ">=19.0.0",
|
|
172
|
-
"@avocadostudio-ai/orchestrator-core": "^0.
|
|
172
|
+
"@avocadostudio-ai/orchestrator-core": "^0.27.0"
|
|
173
173
|
},
|
|
174
174
|
"peerDependenciesMeta": {
|
|
175
175
|
"next": {
|