@weaverse/next 0.1.0-alpha.0 → 0.1.0-alpha.10
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 +562 -0
- package/dist/chunk-XXGMPTWH.mjs +219 -0
- package/dist/chunk-XXGMPTWH.mjs.map +1 -0
- package/dist/index.d.mts +487 -161
- package/dist/index.d.ts +487 -161
- package/dist/index.js +1060 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +896 -106
- package/dist/index.mjs.map +1 -1
- package/dist/seo-CgO21rLg.d.mts +517 -0
- package/dist/seo-CgO21rLg.d.ts +517 -0
- package/dist/server.d.mts +99 -0
- package/dist/server.d.ts +99 -0
- package/dist/server.js +892 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +671 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +23 -4
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
import { generateDataFromSchema, buildWeaverseNextRequestInfo, runWeaverseComponentLoaders } from './chunk-XXGMPTWH.mjs';
|
|
2
|
+
export { formatWeaverseNextSeoMetadata, getWeaverseNextSeoMetadata } from './chunk-XXGMPTWH.mjs';
|
|
3
|
+
|
|
4
|
+
// src/server/configs.ts
|
|
5
|
+
var DEFAULT_WEAVERSE_HOST = "https://studio.weaverse.io";
|
|
6
|
+
var DEFAULT_WEAVERSE_API_BASE = "https://api.weaverse.io";
|
|
7
|
+
var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
|
|
8
|
+
function isTrustedStudioHost(host) {
|
|
9
|
+
let parsed;
|
|
10
|
+
try {
|
|
11
|
+
parsed = new URL(host);
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (parsed.protocol !== "https:") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
let { hostname } = parsed;
|
|
19
|
+
return TRUSTED_STUDIO_DOMAINS.some(
|
|
20
|
+
(domain) => hostname === domain || hostname.endsWith(`.${domain}`)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function toUrl(value) {
|
|
24
|
+
try {
|
|
25
|
+
return typeof value === "string" ? new URL(value, "http://localhost") : value;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getContextSearchParams(context) {
|
|
31
|
+
if (context?.searchParams) {
|
|
32
|
+
return new URLSearchParams(context.searchParams);
|
|
33
|
+
}
|
|
34
|
+
if (context?.url) {
|
|
35
|
+
let url = toUrl(context.url);
|
|
36
|
+
if (url) {
|
|
37
|
+
return new URLSearchParams(url.searchParams);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return new URLSearchParams();
|
|
41
|
+
}
|
|
42
|
+
function lastParam(searchParams, key) {
|
|
43
|
+
let values = searchParams.getAll(key);
|
|
44
|
+
return values.length ? values.at(-1) : void 0;
|
|
45
|
+
}
|
|
46
|
+
function coerceBool(value) {
|
|
47
|
+
return value === "true";
|
|
48
|
+
}
|
|
49
|
+
function readEnv(env, key) {
|
|
50
|
+
let fromConfig = env?.[key];
|
|
51
|
+
if (fromConfig !== void 0) {
|
|
52
|
+
return fromConfig;
|
|
53
|
+
}
|
|
54
|
+
if (typeof process !== "undefined" && process.env) {
|
|
55
|
+
return process.env[key];
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
function getWeaverseNextConfigs(context, options = {}) {
|
|
60
|
+
let searchParams = getContextSearchParams(context);
|
|
61
|
+
let { env } = options;
|
|
62
|
+
let queryHost = lastParam(searchParams, "weaverseHost");
|
|
63
|
+
let trustedUrlHost = queryHost && isTrustedStudioHost(queryHost) ? new URL(queryHost).origin : "";
|
|
64
|
+
let configuredHost = options.weaverseHost || readEnv(env, "WEAVERSE_HOST") || "";
|
|
65
|
+
let weaverseHost = trustedUrlHost || configuredHost || DEFAULT_WEAVERSE_HOST;
|
|
66
|
+
let weaverseApiBase = trustedUrlHost || options.weaverseApiBase || readEnv(env, "WEAVERSE_PUBLIC_API_BASE") || (configuredHost && configuredHost !== DEFAULT_WEAVERSE_HOST ? configuredHost : DEFAULT_WEAVERSE_API_BASE);
|
|
67
|
+
let isRevisionPreview = context?.isRevisionPreview ?? searchParams.has("__revisionId");
|
|
68
|
+
let isDesignMode = context?.isDesignMode ?? coerceBool(lastParam(searchParams, "isDesignMode"));
|
|
69
|
+
let isPreviewMode = context?.isPreviewMode ?? coerceBool(lastParam(searchParams, "isPreviewMode"));
|
|
70
|
+
return {
|
|
71
|
+
queryProjectId: lastParam(searchParams, "weaverseProjectId") || "",
|
|
72
|
+
envProjectId: readEnv(env, "WEAVERSE_PROJECT_ID") || "",
|
|
73
|
+
weaverseHost,
|
|
74
|
+
weaverseApiBase,
|
|
75
|
+
weaverseApiKey: lastParam(searchParams, "weaverseApiKey") || readEnv(env, "WEAVERSE_API_KEY") || "",
|
|
76
|
+
weaverseVersion: lastParam(searchParams, "weaverseVersion") || options.weaverseVersion || "",
|
|
77
|
+
isDesignMode,
|
|
78
|
+
isPreviewMode,
|
|
79
|
+
isRevisionPreview,
|
|
80
|
+
sectionType: context?.sectionType || lastParam(searchParams, "sectionType") || "",
|
|
81
|
+
publicEnv: {
|
|
82
|
+
PUBLIC_STORE_DOMAIN: readEnv(env, "PUBLIC_STORE_DOMAIN") || "",
|
|
83
|
+
PUBLIC_STOREFRONT_API_TOKEN: readEnv(env, "PUBLIC_STOREFRONT_API_TOKEN") || ""
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/server/normalize-page-url.ts
|
|
89
|
+
var KEPT_PARAM_RE = /^(?:__revisionId|__weaverseDraftItem|isDesignMode|weaverse)/;
|
|
90
|
+
var DATA_SUFFIX = ".data";
|
|
91
|
+
function normalizeNextPageUrl(url) {
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = new URL(url);
|
|
95
|
+
} catch {
|
|
96
|
+
try {
|
|
97
|
+
parsed = new URL(url, "http://localhost");
|
|
98
|
+
} catch {
|
|
99
|
+
return url;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let kept = [...parsed.searchParams].filter(([key]) => KEPT_PARAM_RE.test(key));
|
|
103
|
+
kept.sort(([a], [b]) => a.localeCompare(b));
|
|
104
|
+
let search = new URLSearchParams(kept).toString();
|
|
105
|
+
let { pathname } = parsed;
|
|
106
|
+
if (pathname.endsWith(DATA_SUFFIX)) {
|
|
107
|
+
pathname = pathname.slice(0, -DATA_SUFFIX.length);
|
|
108
|
+
}
|
|
109
|
+
return `${parsed.origin}${pathname}${search ? `?${search}` : ""}`;
|
|
110
|
+
}
|
|
111
|
+
function resolveRequestUrl(context) {
|
|
112
|
+
if (context?.url) {
|
|
113
|
+
return typeof context.url === "string" ? context.url : context.url.toString();
|
|
114
|
+
}
|
|
115
|
+
let pathname = context?.pathname || "/";
|
|
116
|
+
let search = context?.searchParams ? new URLSearchParams(context.searchParams).toString() : "";
|
|
117
|
+
return `http://localhost${pathname}${search ? `?${search}` : ""}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/server/revalidate-handler.ts
|
|
121
|
+
var NO_STORE_HEADERS = {
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
"Cache-Control": "no-store"
|
|
124
|
+
};
|
|
125
|
+
var STATUS_OK = 200;
|
|
126
|
+
var STATUS_BAD_REQUEST = 400;
|
|
127
|
+
var STATUS_NOT_FOUND = 404;
|
|
128
|
+
var STATUS_UNPROCESSABLE = 422;
|
|
129
|
+
var STATUS_SERVER_ERROR = 500;
|
|
130
|
+
function json(body, status) {
|
|
131
|
+
return new Response(JSON.stringify(body), {
|
|
132
|
+
status,
|
|
133
|
+
headers: NO_STORE_HEADERS
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function parseDraftItem(payload) {
|
|
137
|
+
if (typeof payload !== "object" || payload === null) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
let draftItem = payload.draftItem;
|
|
141
|
+
if (typeof draftItem !== "object" || draftItem === null) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
let { id, type } = draftItem;
|
|
145
|
+
if (typeof id !== "string" || !id || typeof type !== "string" || !type) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return draftItem;
|
|
149
|
+
}
|
|
150
|
+
function createWeaverseNextRevalidateHandler(config) {
|
|
151
|
+
return {
|
|
152
|
+
POST: async (request) => {
|
|
153
|
+
let payload;
|
|
154
|
+
try {
|
|
155
|
+
payload = await request.json();
|
|
156
|
+
} catch {
|
|
157
|
+
return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
|
|
158
|
+
}
|
|
159
|
+
let draftItem = parseDraftItem(payload);
|
|
160
|
+
if (!draftItem) {
|
|
161
|
+
return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
|
|
162
|
+
}
|
|
163
|
+
let client;
|
|
164
|
+
try {
|
|
165
|
+
client = await config.getClient(request);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error("\u274C Revalidate handler getClient failed.", error);
|
|
168
|
+
return json({ error: "client-init-failed" }, STATUS_SERVER_ERROR);
|
|
169
|
+
}
|
|
170
|
+
let component = client.components.find(
|
|
171
|
+
(candidate) => candidate?.schema?.type === draftItem.type
|
|
172
|
+
);
|
|
173
|
+
if (!component) {
|
|
174
|
+
return json({ error: "unknown-component-type" }, STATUS_NOT_FOUND);
|
|
175
|
+
}
|
|
176
|
+
if (typeof component.loader !== "function") {
|
|
177
|
+
return json({ error: "missing-loader" }, STATUS_UNPROCESSABLE);
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
let loaderData = await component.loader({
|
|
181
|
+
data: {
|
|
182
|
+
...generateDataFromSchema(component.schema),
|
|
183
|
+
...draftItem.data
|
|
184
|
+
},
|
|
185
|
+
weaverse: client,
|
|
186
|
+
context: client.requestContext,
|
|
187
|
+
commerce: client.commerce
|
|
188
|
+
});
|
|
189
|
+
return json({ loaderData: loaderData ?? null }, STATUS_OK);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(
|
|
192
|
+
"\u274C Item loader revalidation failed.",
|
|
193
|
+
draftItem.type,
|
|
194
|
+
draftItem.id,
|
|
195
|
+
error
|
|
196
|
+
);
|
|
197
|
+
return json({ error: "loader-failed" }, STATUS_SERVER_ERROR);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/server/server-client.ts
|
|
204
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
205
|
+
var RANDOM_ID_RADIX = 36;
|
|
206
|
+
var RANDOM_ID_SLICE_START = 2;
|
|
207
|
+
var MAX_CUSTOM_PAGE_PAGES = 100;
|
|
208
|
+
var JSON_HEADERS = {
|
|
209
|
+
"Content-Type": "application/json",
|
|
210
|
+
Accept: "application/json",
|
|
211
|
+
"Accept-Encoding": "gzip, deflate, br"
|
|
212
|
+
};
|
|
213
|
+
function asThemeSchema(schema) {
|
|
214
|
+
return schema && typeof schema === "object" ? schema : void 0;
|
|
215
|
+
}
|
|
216
|
+
function generateUuid() {
|
|
217
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
218
|
+
return crypto.randomUUID();
|
|
219
|
+
}
|
|
220
|
+
return `id-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
|
|
221
|
+
}
|
|
222
|
+
function normalizeWeaverseNextPageAssignment(input) {
|
|
223
|
+
if (!input || typeof input !== "object") {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
let { projectId, type, handle, locale, meta } = input;
|
|
227
|
+
if (typeof projectId !== "string" || typeof type !== "string" || typeof handle !== "string") {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
let normalized = {
|
|
231
|
+
projectId,
|
|
232
|
+
type,
|
|
233
|
+
handle,
|
|
234
|
+
locale: typeof locale === "string" ? locale : ""
|
|
235
|
+
};
|
|
236
|
+
if (meta && typeof meta === "object") {
|
|
237
|
+
normalized.meta = meta;
|
|
238
|
+
}
|
|
239
|
+
return normalized;
|
|
240
|
+
}
|
|
241
|
+
var NextServerClient = class {
|
|
242
|
+
components;
|
|
243
|
+
commerce;
|
|
244
|
+
requestContext;
|
|
245
|
+
themeSchema;
|
|
246
|
+
themeSettings;
|
|
247
|
+
data = null;
|
|
248
|
+
dataContext = {};
|
|
249
|
+
configs;
|
|
250
|
+
_projectIdInput;
|
|
251
|
+
_baseConfigs;
|
|
252
|
+
_fetch;
|
|
253
|
+
_cache;
|
|
254
|
+
_fetchTimeoutMs;
|
|
255
|
+
_resolvedProjectId;
|
|
256
|
+
constructor(config) {
|
|
257
|
+
this.components = config.components;
|
|
258
|
+
this.commerce = config.commerce;
|
|
259
|
+
this.requestContext = config.requestContext;
|
|
260
|
+
this.themeSchema = config.themeSchema;
|
|
261
|
+
this.themeSettings = {
|
|
262
|
+
...generateDataFromSchema(config.themeSchema),
|
|
263
|
+
...config.themeSettings
|
|
264
|
+
};
|
|
265
|
+
this._projectIdInput = config.projectId;
|
|
266
|
+
this._fetch = config.fetch ?? globalThis.fetch;
|
|
267
|
+
this._cache = config.cache;
|
|
268
|
+
this._fetchTimeoutMs = config.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
269
|
+
this._baseConfigs = getWeaverseNextConfigs(config.requestContext, {
|
|
270
|
+
env: config.env,
|
|
271
|
+
weaverseHost: config.weaverseHost,
|
|
272
|
+
weaverseApiBase: config.weaverseApiBase,
|
|
273
|
+
weaverseVersion: config.weaverseVersion
|
|
274
|
+
});
|
|
275
|
+
this.configs = this._buildPublicConfigs(
|
|
276
|
+
this._baseConfigs.queryProjectId || (typeof this._projectIdInput === "string" ? this._projectIdInput.trim() : "") || this._baseConfigs.envProjectId
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
get projectId() {
|
|
280
|
+
return this.configs.projectId;
|
|
281
|
+
}
|
|
282
|
+
/** Compatibility alias so loaders can call `weaverse.storefront.query(...)`. */
|
|
283
|
+
get storefront() {
|
|
284
|
+
return this.commerce?.storefront;
|
|
285
|
+
}
|
|
286
|
+
_buildPublicConfigs(projectId) {
|
|
287
|
+
let base = this._baseConfigs;
|
|
288
|
+
return {
|
|
289
|
+
projectId,
|
|
290
|
+
weaverseHost: base.weaverseHost,
|
|
291
|
+
weaverseApiBase: base.weaverseApiBase,
|
|
292
|
+
weaversePublicApiBase: base.weaverseApiBase,
|
|
293
|
+
weaverseVersion: base.weaverseVersion,
|
|
294
|
+
isDesignMode: base.isDesignMode,
|
|
295
|
+
isPreviewMode: base.isPreviewMode,
|
|
296
|
+
isRevisionPreview: base.isRevisionPreview,
|
|
297
|
+
sectionType: base.sectionType,
|
|
298
|
+
publicEnv: base.publicEnv
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
resolveProjectId = async () => {
|
|
302
|
+
if (this._resolvedProjectId !== void 0) {
|
|
303
|
+
return this._resolvedProjectId;
|
|
304
|
+
}
|
|
305
|
+
let resolved = this._baseConfigs.queryProjectId;
|
|
306
|
+
if (!resolved && typeof this._projectIdInput === "function") {
|
|
307
|
+
try {
|
|
308
|
+
let value = await this._projectIdInput(this.requestContext);
|
|
309
|
+
if (typeof value === "string" && value.trim()) {
|
|
310
|
+
resolved = value.trim();
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.warn(
|
|
314
|
+
"[WeaverseNextServerClient] projectId resolver threw, falling back.",
|
|
315
|
+
error
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!resolved && typeof this._projectIdInput === "string") {
|
|
320
|
+
resolved = this._projectIdInput.trim();
|
|
321
|
+
}
|
|
322
|
+
if (!resolved) {
|
|
323
|
+
resolved = this._baseConfigs.envProjectId;
|
|
324
|
+
}
|
|
325
|
+
this._resolvedProjectId = resolved;
|
|
326
|
+
this.configs = this._buildPublicConfigs(resolved);
|
|
327
|
+
return resolved;
|
|
328
|
+
};
|
|
329
|
+
fetchWithCache = async (url, options = {}) => {
|
|
330
|
+
let { revalidate, tags, ...init } = options;
|
|
331
|
+
let requestInit = { ...init };
|
|
332
|
+
let { isDesignMode, isRevisionPreview } = this._baseConfigs;
|
|
333
|
+
if (isDesignMode || isRevisionPreview) {
|
|
334
|
+
requestInit.cache = "no-store";
|
|
335
|
+
} else if (requestInit.cache === void 0) {
|
|
336
|
+
let effectiveRevalidate = revalidate ?? this._cache?.revalidate;
|
|
337
|
+
let effectiveTags = tags ?? this._cache?.tags;
|
|
338
|
+
if (effectiveRevalidate !== void 0 || effectiveTags?.length) {
|
|
339
|
+
requestInit.next = {
|
|
340
|
+
...effectiveRevalidate === void 0 ? {} : { revalidate: effectiveRevalidate },
|
|
341
|
+
...effectiveTags?.length ? { tags: effectiveTags } : {}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
let controller = new AbortController();
|
|
346
|
+
let timeoutId = setTimeout(() => controller.abort(), this._fetchTimeoutMs);
|
|
347
|
+
try {
|
|
348
|
+
let response = await this._fetch(url, {
|
|
349
|
+
...requestInit,
|
|
350
|
+
signal: requestInit.signal ?? controller.signal
|
|
351
|
+
});
|
|
352
|
+
if (!response.ok) {
|
|
353
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
354
|
+
}
|
|
355
|
+
return await response.json();
|
|
356
|
+
} finally {
|
|
357
|
+
clearTimeout(timeoutId);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
_buildCustomPagesUrl(projectId, options, cursor) {
|
|
361
|
+
let params = new URLSearchParams();
|
|
362
|
+
if (options.locale) {
|
|
363
|
+
params.set("locale", options.locale);
|
|
364
|
+
}
|
|
365
|
+
if (options.limit) {
|
|
366
|
+
params.set("limit", String(options.limit));
|
|
367
|
+
}
|
|
368
|
+
if (cursor) {
|
|
369
|
+
params.set("cursor", cursor);
|
|
370
|
+
}
|
|
371
|
+
let qs = params.toString();
|
|
372
|
+
return `${this._baseConfigs.weaverseApiBase}/api/public/v1/projects/${projectId}/custom-pages${qs ? `?${qs}` : ""}`;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Fetch all published custom pages for sitemap generation. Mirrors Hydrogen's
|
|
376
|
+
* helper but uses Next's fetch cache knobs (`revalidate` / `tags`). It returns
|
|
377
|
+
* accumulated results on partial pagination failure so a single API hiccup
|
|
378
|
+
* does not break sitemap generation entirely.
|
|
379
|
+
*/
|
|
380
|
+
fetchCustomPages = async (options = {}) => {
|
|
381
|
+
let projectId = await this.resolveProjectId();
|
|
382
|
+
let entries = [];
|
|
383
|
+
if (!projectId) {
|
|
384
|
+
console.warn(
|
|
385
|
+
"[WeaverseNext] Failed to fetch custom pages: missing projectId"
|
|
386
|
+
);
|
|
387
|
+
return entries;
|
|
388
|
+
}
|
|
389
|
+
let cursor = null;
|
|
390
|
+
for (let page = 0; page < MAX_CUSTOM_PAGE_PAGES; page += 1) {
|
|
391
|
+
let url = this._buildCustomPagesUrl(projectId, options, cursor);
|
|
392
|
+
try {
|
|
393
|
+
let result = await this.fetchWithCache(url, {
|
|
394
|
+
revalidate: options.revalidate,
|
|
395
|
+
tags: options.tags
|
|
396
|
+
});
|
|
397
|
+
entries.push(...result.data ?? []);
|
|
398
|
+
if (!result.nextCursor) {
|
|
399
|
+
return entries;
|
|
400
|
+
}
|
|
401
|
+
cursor = result.nextCursor;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
404
|
+
console.warn(
|
|
405
|
+
entries.length === 0 ? `[WeaverseNext] Failed to fetch custom pages: ${message}` : `[WeaverseNext] Custom pages pagination interrupted: ${message}`
|
|
406
|
+
);
|
|
407
|
+
return entries;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
console.warn("[WeaverseNext] Custom pages pagination cap reached");
|
|
411
|
+
return entries;
|
|
412
|
+
};
|
|
413
|
+
_generateFallbackPage(message) {
|
|
414
|
+
let rootId = generateUuid();
|
|
415
|
+
return {
|
|
416
|
+
id: `fallback_page_${rootId}`,
|
|
417
|
+
rootId,
|
|
418
|
+
name: "Default Page",
|
|
419
|
+
items: [
|
|
420
|
+
{
|
|
421
|
+
type: "main",
|
|
422
|
+
id: rootId,
|
|
423
|
+
data: { dangerouslySetInnerHTML: { __html: message } }
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Recursively materialize a section preset (and its preset children) into a
|
|
430
|
+
* flat list of page items, returning the new item id. Ported from Hydrogen's
|
|
431
|
+
* `recursivelyAddDataItem`. Returns `undefined` when the type isn't registered.
|
|
432
|
+
*/
|
|
433
|
+
_buildPreviewItems(type, items, initData = {}) {
|
|
434
|
+
let component = this.components.find((c) => c.schema?.type === type);
|
|
435
|
+
if (!component) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
let { children, ...data } = component.schema?.presets ?? {};
|
|
439
|
+
let childIds = [];
|
|
440
|
+
if (children) {
|
|
441
|
+
for (let child of children) {
|
|
442
|
+
let { type: childType, children: _children, ...childData } = child;
|
|
443
|
+
let childId = this._buildPreviewItems(childType, items, childData);
|
|
444
|
+
if (childId) {
|
|
445
|
+
childIds.push({ id: childId });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
let id = generateUuid();
|
|
450
|
+
items.push({ id, type, data: { ...data, ...initData }, children: childIds });
|
|
451
|
+
return id;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Synthetic loader data for Studio "section preview" mode. Mirrors Hydrogen's
|
|
455
|
+
* `getPreviewData`: it builds a single-section page from the requested
|
|
456
|
+
* `sectionType`'s presets without hitting `/api/public/project`, so preview
|
|
457
|
+
* works even without a resolved `projectId`.
|
|
458
|
+
*/
|
|
459
|
+
_generatePreviewData(projectId) {
|
|
460
|
+
let { sectionType, weaverseHost } = this._baseConfigs;
|
|
461
|
+
let items = [];
|
|
462
|
+
let sectionId = sectionType ? this._buildPreviewItems(sectionType, items) : void 0;
|
|
463
|
+
let configs = {
|
|
464
|
+
...this._buildPublicConfigs(projectId),
|
|
465
|
+
isPreviewMode: true,
|
|
466
|
+
weaverseHost,
|
|
467
|
+
requestInfo: buildWeaverseNextRequestInfo(this.requestContext)
|
|
468
|
+
};
|
|
469
|
+
return {
|
|
470
|
+
project: {
|
|
471
|
+
id: projectId,
|
|
472
|
+
weaverseShopId: "shop-id",
|
|
473
|
+
name: "Section Preview"
|
|
474
|
+
},
|
|
475
|
+
configs,
|
|
476
|
+
page: {
|
|
477
|
+
id: "weaverse-preview-page",
|
|
478
|
+
name: "Preview section",
|
|
479
|
+
rootId: "1",
|
|
480
|
+
items: [
|
|
481
|
+
{
|
|
482
|
+
id: "1",
|
|
483
|
+
type: "main",
|
|
484
|
+
data: {},
|
|
485
|
+
children: sectionId ? [{ id: sectionId }] : []
|
|
486
|
+
},
|
|
487
|
+
...items
|
|
488
|
+
]
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
_getLoadPageInput(input) {
|
|
493
|
+
let {
|
|
494
|
+
cache,
|
|
495
|
+
projectId: routeProjectId,
|
|
496
|
+
...rest
|
|
497
|
+
} = input;
|
|
498
|
+
let params = {};
|
|
499
|
+
for (let [key, value] of Object.entries(rest)) {
|
|
500
|
+
if (value !== void 0) {
|
|
501
|
+
params[key] = value;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return { cache, params, routeProjectId };
|
|
505
|
+
}
|
|
506
|
+
async _resolveLoadPageProjectId(routeProjectId) {
|
|
507
|
+
let effectiveProjectId = typeof routeProjectId === "string" && routeProjectId.trim() || await this.resolveProjectId();
|
|
508
|
+
if (effectiveProjectId) {
|
|
509
|
+
this.configs = this._buildPublicConfigs(effectiveProjectId);
|
|
510
|
+
}
|
|
511
|
+
return effectiveProjectId;
|
|
512
|
+
}
|
|
513
|
+
_fetchPagePayload(projectId, params, cache) {
|
|
514
|
+
let i18n = this.requestContext?.i18n ?? this.commerce?.storefront?.i18n;
|
|
515
|
+
let url = normalizeNextPageUrl(resolveRequestUrl(this.requestContext));
|
|
516
|
+
let apiUrl = `${this._baseConfigs.weaverseApiBase}/api/public/project`;
|
|
517
|
+
let userAgent = this.requestContext?.headers?.get("user-agent") ?? "";
|
|
518
|
+
return this.fetchWithCache(apiUrl, {
|
|
519
|
+
method: "POST",
|
|
520
|
+
body: JSON.stringify({
|
|
521
|
+
projectId,
|
|
522
|
+
url,
|
|
523
|
+
i18n,
|
|
524
|
+
params,
|
|
525
|
+
isDesignMode: this._baseConfigs.isDesignMode
|
|
526
|
+
}),
|
|
527
|
+
headers: { ...JSON_HEADERS, "X-Visitor-UA": userAgent },
|
|
528
|
+
...cache ?? {}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
_assertNoPageError(payload) {
|
|
532
|
+
if ("error" in payload && typeof payload.error === "string") {
|
|
533
|
+
throw new Error(payload.error);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
_createLoaderData(payload, projectId) {
|
|
537
|
+
this._assertNoPageError(payload);
|
|
538
|
+
let page = payload.page;
|
|
539
|
+
let project = payload.project;
|
|
540
|
+
let pageAssignment = normalizeWeaverseNextPageAssignment(
|
|
541
|
+
payload.pageAssignment
|
|
542
|
+
);
|
|
543
|
+
page ??= this._generateFallbackPage(
|
|
544
|
+
'<div style="text-align: center;">Please add new section to start.</div>'
|
|
545
|
+
);
|
|
546
|
+
project ??= {
|
|
547
|
+
id: projectId,
|
|
548
|
+
name: "Weaverse project not found.",
|
|
549
|
+
weaverseShopId: ""
|
|
550
|
+
};
|
|
551
|
+
if (Array.isArray(page.items) && page.items.length === 0) {
|
|
552
|
+
page.items.push({
|
|
553
|
+
type: "main",
|
|
554
|
+
id: page.rootId || generateUuid(),
|
|
555
|
+
data: {}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
page,
|
|
560
|
+
project,
|
|
561
|
+
pageAssignment,
|
|
562
|
+
configs: {
|
|
563
|
+
...this._buildPublicConfigs(projectId),
|
|
564
|
+
requestInfo: buildWeaverseNextRequestInfo(this.requestContext)
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
async _runLoaders(data) {
|
|
569
|
+
this.data = data;
|
|
570
|
+
let withLoaders = await runWeaverseComponentLoaders({ client: this, data });
|
|
571
|
+
this.data = withLoaders ?? data;
|
|
572
|
+
return this.data;
|
|
573
|
+
}
|
|
574
|
+
loadPage = async (input = {}) => {
|
|
575
|
+
try {
|
|
576
|
+
let { cache, params, routeProjectId } = this._getLoadPageInput(input);
|
|
577
|
+
let projectId = await this._resolveLoadPageProjectId(routeProjectId);
|
|
578
|
+
if (this._baseConfigs.isPreviewMode) {
|
|
579
|
+
let previewData = this._generatePreviewData(projectId || "x");
|
|
580
|
+
this.data = previewData;
|
|
581
|
+
return previewData;
|
|
582
|
+
}
|
|
583
|
+
if (!projectId) {
|
|
584
|
+
throw new Error("Missing Weaverse projectId!");
|
|
585
|
+
}
|
|
586
|
+
let payload = await this._fetchPagePayload(projectId, params, cache);
|
|
587
|
+
let data = this._createLoaderData(payload, projectId);
|
|
588
|
+
return await this._runLoaders(data);
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.error("\u274C Page load failed", {
|
|
591
|
+
url: this.requestContext?.url,
|
|
592
|
+
projectId: input.projectId || this.configs.projectId,
|
|
593
|
+
message: error instanceof Error ? error.message : String(error)
|
|
594
|
+
});
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
loadThemeSettings = async (optionsInput = {}) => {
|
|
599
|
+
let options = optionsInput;
|
|
600
|
+
let themeSchema = asThemeSchema(this.themeSchema);
|
|
601
|
+
let defaultThemeSettings = generateDataFromSchema(this.themeSchema);
|
|
602
|
+
let staticContent = themeSchema?.i18n?.staticContent;
|
|
603
|
+
try {
|
|
604
|
+
let projectId = await this.resolveProjectId();
|
|
605
|
+
if (!projectId) {
|
|
606
|
+
throw new Error("Missing Weaverse projectId!");
|
|
607
|
+
}
|
|
608
|
+
let { isDesignMode } = this._baseConfigs;
|
|
609
|
+
let url = `${this._baseConfigs.weaverseApiBase}/api/public/project_configs`;
|
|
610
|
+
let result = await this.fetchWithCache(
|
|
611
|
+
url,
|
|
612
|
+
{
|
|
613
|
+
method: "POST",
|
|
614
|
+
body: JSON.stringify({ projectId, isDesignMode }),
|
|
615
|
+
headers: JSON_HEADERS,
|
|
616
|
+
revalidate: options.revalidate,
|
|
617
|
+
tags: options.tags
|
|
618
|
+
}
|
|
619
|
+
);
|
|
620
|
+
if (typeof result !== "object" || result === null) {
|
|
621
|
+
result = { theme: defaultThemeSettings };
|
|
622
|
+
}
|
|
623
|
+
if (themeSchema?.settings || themeSchema?.inspector) {
|
|
624
|
+
if (!result.theme || typeof result.theme !== "object") {
|
|
625
|
+
result.theme = defaultThemeSettings;
|
|
626
|
+
} else {
|
|
627
|
+
result.theme = { ...defaultThemeSettings, ...result.theme };
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if (staticContent) {
|
|
631
|
+
result.staticContent = staticContent;
|
|
632
|
+
}
|
|
633
|
+
if (isDesignMode) {
|
|
634
|
+
let serializableSchema = themeSchema ? {
|
|
635
|
+
...themeSchema,
|
|
636
|
+
settings: themeSchema.settings?.map((group) => ({
|
|
637
|
+
...group,
|
|
638
|
+
inputs: group?.inputs?.map(
|
|
639
|
+
(inputItem) => typeof inputItem?.condition === "function" ? {
|
|
640
|
+
...inputItem,
|
|
641
|
+
condition: inputItem.condition.toString()
|
|
642
|
+
} : inputItem
|
|
643
|
+
)
|
|
644
|
+
}))
|
|
645
|
+
} : void 0;
|
|
646
|
+
result = {
|
|
647
|
+
...result,
|
|
648
|
+
schema: serializableSchema,
|
|
649
|
+
publicEnv: this._baseConfigs.publicEnv
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
return result;
|
|
653
|
+
} catch (error) {
|
|
654
|
+
let errorDetails = error instanceof Error ? error.message : String(error);
|
|
655
|
+
console.error("Unable to load theme settings", errorDetails);
|
|
656
|
+
return {
|
|
657
|
+
theme: defaultThemeSettings,
|
|
658
|
+
staticContent,
|
|
659
|
+
_error: errorDetails,
|
|
660
|
+
_loadFailed: true
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
function createWeaverseNextServerClient(config) {
|
|
666
|
+
return new NextServerClient(config);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export { createWeaverseNextRevalidateHandler, createWeaverseNextServerClient, getWeaverseNextConfigs, normalizeNextPageUrl, resolveRequestUrl };
|
|
670
|
+
//# sourceMappingURL=server.mjs.map
|
|
671
|
+
//# sourceMappingURL=server.mjs.map
|