@weaverse/next 1.0.0 → 1.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +7 -1
  2. package/dist/client-boundary.js +1061 -0
  3. package/dist/client-boundary.js.map +1 -0
  4. package/dist/client-boundary.mjs +1017 -0
  5. package/dist/client-boundary.mjs.map +1 -0
  6. package/dist/index.js +307 -1477
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +149 -1100
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/seo-BCFNGChj.js +319 -0
  11. package/dist/seo-BCFNGChj.js.map +1 -0
  12. package/dist/seo-C-S2viuI.mjs +242 -0
  13. package/dist/seo-C-S2viuI.mjs.map +1 -0
  14. package/dist/server.js +626 -1076
  15. package/dist/server.js.map +1 -1
  16. package/dist/server.mjs +620 -803
  17. package/dist/server.mjs.map +1 -1
  18. package/dist/types/client-boundary.d.ts +25 -0
  19. package/dist/types/client-boundary.d.ts.map +1 -0
  20. package/dist/types/index.d.ts +11 -12
  21. package/dist/types/index.d.ts.map +1 -1
  22. package/dist/types/item.d.ts +1 -1
  23. package/dist/types/item.d.ts.map +1 -1
  24. package/dist/types/provider.d.ts +1 -1
  25. package/dist/types/provider.d.ts.map +1 -1
  26. package/dist/types/revalidate-item.d.ts +1 -1
  27. package/dist/types/revalidate-item.d.ts.map +1 -1
  28. package/dist/types/root-provider.d.ts +1 -1
  29. package/dist/types/root-provider.d.ts.map +1 -1
  30. package/dist/types/runtime.d.ts.map +1 -1
  31. package/dist/types/translate.d.ts +56 -0
  32. package/dist/types/translate.d.ts.map +1 -0
  33. package/dist/types/translation-context.d.ts +1 -48
  34. package/dist/types/translation-context.d.ts.map +1 -1
  35. package/dist/types/translation-store.d.ts.map +1 -1
  36. package/dist/types/utils.d.ts +10 -0
  37. package/dist/types/utils.d.ts.map +1 -1
  38. package/package.json +11 -25
  39. package/src/client-boundary.ts +50 -0
  40. package/src/index.ts +42 -52
  41. package/src/provider.tsx +2 -4
  42. package/src/root-provider.tsx +2 -4
  43. package/src/translate.ts +125 -0
  44. package/src/translation-context.tsx +1 -118
  45. package/src/utils.ts +12 -0
  46. package/dist/chunk-LH6AAEIK.mjs +0 -326
  47. package/dist/chunk-LH6AAEIK.mjs.map +0 -1
package/dist/server.mjs CHANGED
@@ -1,849 +1,666 @@
1
- import { withPathPrefix, generateDataFromSchema, sanitizeRouteContextSearch, buildWeaverseNextRequestInfo, runWeaverseComponentLoaders, ROUTE_CONTEXT_I18N_KEYS } from './chunk-LH6AAEIK.mjs';
2
- export { formatWeaverseNextSeoMetadata, getWeaverseNextSeoMetadata } from './chunk-LH6AAEIK.mjs';
3
- import { PageTypeSchema } from '@weaverse/schema';
4
-
5
- // src/server/configs.ts
6
- var DEFAULT_WEAVERSE_HOST = "https://studio.weaverse.io";
7
- var DEFAULT_WEAVERSE_API_BASE = "https://api.weaverse.io";
8
- var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
1
+ import { c as buildWeaverseNextRequestInfo, d as generateDataFromSchema, i as ROUTE_CONTEXT_I18N_KEYS, l as withPathPrefix, n as getWeaverseNextSeoMetadata, s as sanitizeRouteContextSearch, t as formatWeaverseNextSeoMetadata, u as runWeaverseComponentLoaders } from "./seo-C-S2viuI.mjs";
2
+ import { PageTypeSchema } from "@weaverse/schema";
3
+ //#region src/server/configs.ts
4
+ const DEFAULT_WEAVERSE_HOST = "https://studio.weaverse.io";
5
+ const DEFAULT_WEAVERSE_API_BASE = "https://api.weaverse.io";
6
+ const TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
9
7
  function isTrustedStudioHost(host) {
10
- let parsed;
11
- try {
12
- parsed = new URL(host);
13
- } catch {
14
- return false;
15
- }
16
- if (parsed.protocol !== "https:") {
17
- return false;
18
- }
19
- let { hostname } = parsed;
20
- return TRUSTED_STUDIO_DOMAINS.some(
21
- (domain) => hostname === domain || hostname.endsWith(`.${domain}`)
22
- );
8
+ let parsed;
9
+ try {
10
+ parsed = new URL(host);
11
+ } catch {
12
+ return false;
13
+ }
14
+ if (parsed.protocol !== "https:") return false;
15
+ let { hostname } = parsed;
16
+ return TRUSTED_STUDIO_DOMAINS.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
23
17
  }
24
18
  function toUrl(value) {
25
- try {
26
- return typeof value === "string" ? new URL(value, "http://localhost") : value;
27
- } catch {
28
- return null;
29
- }
19
+ try {
20
+ return typeof value === "string" ? new URL(value, "http://localhost") : value;
21
+ } catch {
22
+ return null;
23
+ }
30
24
  }
31
25
  function getContextSearchParams(context) {
32
- if (context?.searchParams) {
33
- return new URLSearchParams(context.searchParams);
34
- }
35
- if (context?.url) {
36
- let url = toUrl(context.url);
37
- if (url) {
38
- return new URLSearchParams(url.searchParams);
39
- }
40
- }
41
- return new URLSearchParams();
26
+ if (context?.searchParams) return new URLSearchParams(context.searchParams);
27
+ if (context?.url) {
28
+ let url = toUrl(context.url);
29
+ if (url) return new URLSearchParams(url.searchParams);
30
+ }
31
+ return new URLSearchParams();
42
32
  }
43
33
  function lastParam(searchParams, key) {
44
- let values = searchParams.getAll(key);
45
- return values.length ? values.at(-1) : void 0;
34
+ let values = searchParams.getAll(key);
35
+ return values.length ? values.at(-1) : void 0;
46
36
  }
47
37
  function coerceBool(value) {
48
- return value === "true";
38
+ return value === "true";
49
39
  }
50
40
  function readEnv(env, key) {
51
- let fromConfig = env?.[key];
52
- if (fromConfig !== void 0) {
53
- return fromConfig;
54
- }
55
- if (typeof process !== "undefined" && process.env) {
56
- return process.env[key];
57
- }
58
- return;
41
+ let fromConfig = env?.[key];
42
+ if (fromConfig !== void 0) return fromConfig;
43
+ if (typeof process !== "undefined" && process.env) return process.env[key];
59
44
  }
60
45
  function getWeaverseNextConfigs(context, options = {}) {
61
- let searchParams = getContextSearchParams(context);
62
- let { env } = options;
63
- let queryHost = lastParam(searchParams, "weaverseHost");
64
- let trustedUrlHost = queryHost && isTrustedStudioHost(queryHost) ? new URL(queryHost).origin : "";
65
- let configuredHost = options.weaverseHost || readEnv(env, "WEAVERSE_HOST") || "";
66
- let weaverseHost = trustedUrlHost || configuredHost || DEFAULT_WEAVERSE_HOST;
67
- let weaverseApiBase = trustedUrlHost || options.weaverseApiBase || readEnv(env, "WEAVERSE_PUBLIC_API_BASE") || (configuredHost && configuredHost !== DEFAULT_WEAVERSE_HOST ? configuredHost : DEFAULT_WEAVERSE_API_BASE);
68
- let isRevisionPreview = context?.isRevisionPreview ?? searchParams.has("__revisionId");
69
- let isDesignMode = context?.isDesignMode ?? coerceBool(lastParam(searchParams, "isDesignMode"));
70
- let isPreviewMode = context?.isPreviewMode ?? coerceBool(lastParam(searchParams, "isPreviewMode"));
71
- return {
72
- queryProjectId: lastParam(searchParams, "weaverseProjectId") || "",
73
- envProjectId: readEnv(env, "WEAVERSE_PROJECT_ID") || "",
74
- weaverseHost,
75
- weaverseApiBase,
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
- };
46
+ let searchParams = getContextSearchParams(context);
47
+ let { env } = options;
48
+ let queryHost = lastParam(searchParams, "weaverseHost");
49
+ let trustedUrlHost = queryHost && isTrustedStudioHost(queryHost) ? new URL(queryHost).origin : "";
50
+ let configuredHost = options.weaverseHost || readEnv(env, "WEAVERSE_HOST") || "";
51
+ let weaverseHost = trustedUrlHost || configuredHost || DEFAULT_WEAVERSE_HOST;
52
+ let weaverseApiBase = trustedUrlHost || options.weaverseApiBase || readEnv(env, "WEAVERSE_PUBLIC_API_BASE") || (configuredHost && configuredHost !== DEFAULT_WEAVERSE_HOST ? configuredHost : DEFAULT_WEAVERSE_API_BASE);
53
+ let isRevisionPreview = context?.isRevisionPreview ?? searchParams.has("__revisionId");
54
+ let isDesignMode = context?.isDesignMode ?? coerceBool(lastParam(searchParams, "isDesignMode"));
55
+ let isPreviewMode = context?.isPreviewMode ?? coerceBool(lastParam(searchParams, "isPreviewMode"));
56
+ return {
57
+ queryProjectId: lastParam(searchParams, "weaverseProjectId") || "",
58
+ envProjectId: readEnv(env, "WEAVERSE_PROJECT_ID") || "",
59
+ weaverseHost,
60
+ weaverseApiBase,
61
+ weaverseVersion: lastParam(searchParams, "weaverseVersion") || options.weaverseVersion || "",
62
+ isDesignMode,
63
+ isPreviewMode,
64
+ isRevisionPreview,
65
+ sectionType: context?.sectionType || lastParam(searchParams, "sectionType") || "",
66
+ publicEnv: {
67
+ PUBLIC_STORE_DOMAIN: readEnv(env, "PUBLIC_STORE_DOMAIN") || "",
68
+ PUBLIC_STOREFRONT_API_TOKEN: readEnv(env, "PUBLIC_STOREFRONT_API_TOKEN") || ""
69
+ }
70
+ };
86
71
  }
87
-
88
- // src/server/normalize-page-url.ts
89
- var KEPT_PARAM_RE = /^(?:__revisionId|__weaverseDraftItem|isDesignMode|weaverse)/;
90
- var DATA_SUFFIX = ".data";
72
+ //#endregion
73
+ //#region src/server/normalize-page-url.ts
74
+ const KEPT_PARAM_RE = /^(?:__revisionId|__weaverseDraftItem|isDesignMode|weaverse)/;
75
+ const DATA_SUFFIX = ".data";
91
76
  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}` : ""}`;
77
+ let parsed;
78
+ try {
79
+ parsed = new URL(url);
80
+ } catch {
81
+ try {
82
+ parsed = new URL(url, "http://localhost");
83
+ } catch {
84
+ return url;
85
+ }
86
+ }
87
+ let kept = [...parsed.searchParams].filter(([key]) => KEPT_PARAM_RE.test(key));
88
+ kept.sort(([a], [b]) => a.localeCompare(b));
89
+ let search = new URLSearchParams(kept).toString();
90
+ let { pathname } = parsed;
91
+ if (pathname.endsWith(DATA_SUFFIX)) pathname = pathname.slice(0, -5);
92
+ return `${parsed.origin}${pathname}${search ? `?${search}` : ""}`;
110
93
  }
111
94
  function resolveRequestUrl(context) {
112
- if (context?.url) {
113
- let raw = String(context.url);
114
- let url = new URL(raw, "http://localhost");
115
- let pathname2 = withPathPrefix(url.pathname, context.i18n);
116
- if (pathname2 === url.pathname) {
117
- return raw;
118
- }
119
- url.pathname = pathname2;
120
- return url.toString();
121
- }
122
- let pathname = withPathPrefix(context?.pathname || "/", context?.i18n);
123
- let search = context?.searchParams ? new URLSearchParams(context.searchParams).toString() : "";
124
- return `http://localhost${pathname}${search ? `?${search}` : ""}`;
95
+ if (context?.url) {
96
+ let raw = String(context.url);
97
+ let url = new URL(raw, "http://localhost");
98
+ let pathname = withPathPrefix(url.pathname, context.i18n);
99
+ if (pathname === url.pathname) return raw;
100
+ url.pathname = pathname;
101
+ return url.toString();
102
+ }
103
+ let pathname = withPathPrefix(context?.pathname || "/", context?.i18n);
104
+ let search = context?.searchParams ? new URLSearchParams(context.searchParams).toString() : "";
105
+ return `http://localhost${pathname}${search ? `?${search}` : ""}`;
125
106
  }
126
- var NO_STORE_HEADERS = {
127
- "Content-Type": "application/json",
128
- "Cache-Control": "no-store"
107
+ //#endregion
108
+ //#region src/server/revalidate-handler.ts
109
+ const NO_STORE_HEADERS = {
110
+ "Content-Type": "application/json",
111
+ "Cache-Control": "no-store"
129
112
  };
130
- var STATUS_OK = 200;
131
- var STATUS_BAD_REQUEST = 400;
132
- var STATUS_NOT_FOUND = 404;
133
- var STATUS_UNPROCESSABLE = 422;
134
- var STATUS_SERVER_ERROR = 500;
135
- var MAX_PATHNAME_LENGTH = 2048;
136
- var MAX_SEARCH_LENGTH = 8192;
137
- var MAX_HANDLE_LENGTH = 512;
138
- var MAX_I18N_FIELD_LENGTH = 256;
139
- var CONTROL_CHAR_PATTERN = /[\u0000-\u001F\u007F]/;
113
+ const STATUS_OK = 200;
114
+ const STATUS_BAD_REQUEST = 400;
115
+ const STATUS_NOT_FOUND = 404;
116
+ const STATUS_UNPROCESSABLE = 422;
117
+ const STATUS_SERVER_ERROR = 500;
118
+ const MAX_PATHNAME_LENGTH = 2048;
119
+ const MAX_SEARCH_LENGTH = 8192;
120
+ const MAX_HANDLE_LENGTH = 512;
121
+ const MAX_I18N_FIELD_LENGTH = 256;
122
+ const CONTROL_CHAR_PATTERN = /[\u0000-\u001F\u007F]/;
140
123
  function isValidPathname(pathname) {
141
- if (!pathname || pathname.length > MAX_PATHNAME_LENGTH) {
142
- return false;
143
- }
144
- if (pathname[0] !== "/" || pathname.startsWith("//")) {
145
- return false;
146
- }
147
- if (pathname.includes("\\") || pathname.includes("?")) {
148
- return false;
149
- }
150
- if (pathname.includes("#") || CONTROL_CHAR_PATTERN.test(pathname)) {
151
- return false;
152
- }
153
- return true;
124
+ if (!pathname || pathname.length > MAX_PATHNAME_LENGTH) return false;
125
+ if (pathname[0] !== "/" || pathname.startsWith("//")) return false;
126
+ if (pathname.includes("\\") || pathname.includes("?")) return false;
127
+ if (pathname.includes("#") || CONTROL_CHAR_PATTERN.test(pathname)) return false;
128
+ return true;
154
129
  }
155
130
  function isPlainObject(value) {
156
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
157
- return false;
158
- }
159
- let proto = Object.getPrototypeOf(value);
160
- return proto === Object.prototype || proto === null;
131
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
132
+ let proto = Object.getPrototypeOf(value);
133
+ return proto === Object.prototype || proto === null;
161
134
  }
162
135
  function sanitizeRouteContextI18n(input) {
163
- if (input === void 0) {
164
- return;
165
- }
166
- if (!isPlainObject(input)) {
167
- return null;
168
- }
169
- let source = input;
170
- let allowed = new Set(ROUTE_CONTEXT_I18N_KEYS);
171
- for (let key of Object.keys(source)) {
172
- if (!allowed.has(key)) {
173
- return null;
174
- }
175
- }
176
- let result = {};
177
- for (let key of ROUTE_CONTEXT_I18N_KEYS) {
178
- let value = source[key];
179
- if (value === void 0) {
180
- continue;
181
- }
182
- if (typeof value !== "string" || value.length > MAX_I18N_FIELD_LENGTH) {
183
- return null;
184
- }
185
- result[key] = value;
186
- }
187
- return Object.keys(result).length ? result : void 0;
136
+ if (input === void 0) return;
137
+ if (!isPlainObject(input)) return null;
138
+ let source = input;
139
+ let allowed = new Set(ROUTE_CONTEXT_I18N_KEYS);
140
+ for (let key of Object.keys(source)) if (!allowed.has(key)) return null;
141
+ let result = {};
142
+ for (let key of ROUTE_CONTEXT_I18N_KEYS) {
143
+ let value = source[key];
144
+ if (value === void 0) continue;
145
+ if (typeof value !== "string" || value.length > MAX_I18N_FIELD_LENGTH) return null;
146
+ result[key] = value;
147
+ }
148
+ return Object.keys(result).length ? result : void 0;
188
149
  }
189
150
  function reconstructRouteContext(request, routeContext) {
190
- if (typeof routeContext !== "object" || routeContext === null || Array.isArray(routeContext)) {
191
- return null;
192
- }
193
- let { pathname, search, handle, pageType, i18n } = routeContext;
194
- if (typeof pathname !== "string" || !isValidPathname(pathname)) {
195
- return null;
196
- }
197
- if (typeof search !== "string" || search.length > MAX_SEARCH_LENGTH) {
198
- return null;
199
- }
200
- let rawSearch = search;
201
- if (rawSearch.length && !rawSearch.startsWith("?")) {
202
- return null;
203
- }
204
- let sanitizedI18n = sanitizeRouteContextI18n(i18n);
205
- if (sanitizedI18n === null) {
206
- return null;
207
- }
208
- let resolvedPageType;
209
- if (pageType !== void 0) {
210
- let parsed = PageTypeSchema.safeParse(pageType);
211
- if (!parsed.success) {
212
- return null;
213
- }
214
- resolvedPageType = parsed.data;
215
- }
216
- let resolvedHandle;
217
- if (handle !== void 0) {
218
- if (typeof handle !== "string" || !handle || handle.length > MAX_HANDLE_LENGTH) {
219
- return null;
220
- }
221
- resolvedHandle = handle;
222
- }
223
- let endpointUrl = new URL(request.url);
224
- let routeUrl = new URL(endpointUrl.origin);
225
- routeUrl.pathname = pathname;
226
- routeUrl.search = sanitizeRouteContextSearch(rawSearch);
227
- let canonicalPathname = routeUrl.pathname;
228
- let searchParams = routeUrl.searchParams;
229
- let lastParam2 = (key) => {
230
- let values = searchParams.getAll(key);
231
- return values.length ? values.at(-1) : void 0;
232
- };
233
- return {
234
- pathname: canonicalPathname,
235
- searchParams,
236
- url: routeUrl,
237
- // Headers come from the actual same-origin POST, never its JSON body.
238
- headers: new Headers(request.headers),
239
- ...sanitizedI18n ? { i18n: sanitizedI18n } : {},
240
- ...resolvedPageType ? { pageType: resolvedPageType } : {},
241
- ...resolvedHandle ? { handle: resolvedHandle } : {},
242
- isDesignMode: lastParam2("isDesignMode") === "true",
243
- isPreviewMode: lastParam2("isPreviewMode") === "true",
244
- isRevisionPreview: searchParams.has("__revisionId"),
245
- sectionType: lastParam2("sectionType") || void 0
246
- };
151
+ if (typeof routeContext !== "object" || routeContext === null || Array.isArray(routeContext)) return null;
152
+ let { pathname, search, handle, pageType, i18n } = routeContext;
153
+ if (typeof pathname !== "string" || !isValidPathname(pathname)) return null;
154
+ if (typeof search !== "string" || search.length > MAX_SEARCH_LENGTH) return null;
155
+ let rawSearch = search;
156
+ if (rawSearch.length && !rawSearch.startsWith("?")) return null;
157
+ let sanitizedI18n = sanitizeRouteContextI18n(i18n);
158
+ if (sanitizedI18n === null) return null;
159
+ let resolvedPageType;
160
+ if (pageType !== void 0) {
161
+ let parsed = PageTypeSchema.safeParse(pageType);
162
+ if (!parsed.success) return null;
163
+ resolvedPageType = parsed.data;
164
+ }
165
+ let resolvedHandle;
166
+ if (handle !== void 0) {
167
+ if (typeof handle !== "string" || !handle || handle.length > MAX_HANDLE_LENGTH) return null;
168
+ resolvedHandle = handle;
169
+ }
170
+ let endpointUrl = new URL(request.url);
171
+ let routeUrl = new URL(endpointUrl.origin);
172
+ routeUrl.pathname = pathname;
173
+ routeUrl.search = sanitizeRouteContextSearch(rawSearch);
174
+ let canonicalPathname = routeUrl.pathname;
175
+ let searchParams = routeUrl.searchParams;
176
+ let lastParam = (key) => {
177
+ let values = searchParams.getAll(key);
178
+ return values.length ? values.at(-1) : void 0;
179
+ };
180
+ return {
181
+ pathname: canonicalPathname,
182
+ searchParams,
183
+ url: routeUrl,
184
+ headers: new Headers(request.headers),
185
+ ...sanitizedI18n ? { i18n: sanitizedI18n } : {},
186
+ ...resolvedPageType ? { pageType: resolvedPageType } : {},
187
+ ...resolvedHandle ? { handle: resolvedHandle } : {},
188
+ isDesignMode: lastParam("isDesignMode") === "true",
189
+ isPreviewMode: lastParam("isPreviewMode") === "true",
190
+ isRevisionPreview: searchParams.has("__revisionId"),
191
+ sectionType: lastParam("sectionType") || void 0
192
+ };
247
193
  }
248
194
  function json(body, status) {
249
- return new Response(JSON.stringify(body), {
250
- status,
251
- headers: NO_STORE_HEADERS
252
- });
195
+ return new Response(JSON.stringify(body), {
196
+ status,
197
+ headers: NO_STORE_HEADERS
198
+ });
253
199
  }
254
200
  function parseDraftItem(payload) {
255
- if (typeof payload !== "object" || payload === null) {
256
- return null;
257
- }
258
- let draftItem = payload.draftItem;
259
- if (typeof draftItem !== "object" || draftItem === null) {
260
- return null;
261
- }
262
- let { id, type } = draftItem;
263
- if (typeof id !== "string" || !id || typeof type !== "string" || !type) {
264
- return null;
265
- }
266
- return draftItem;
201
+ if (typeof payload !== "object" || payload === null) return null;
202
+ let draftItem = payload.draftItem;
203
+ if (typeof draftItem !== "object" || draftItem === null) return null;
204
+ let { id, type } = draftItem;
205
+ if (typeof id !== "string" || !id || typeof type !== "string" || !type) return null;
206
+ return draftItem;
267
207
  }
268
208
  function createWeaverseNextRevalidateHandler(config) {
269
- return {
270
- POST: async (request) => {
271
- let payload;
272
- try {
273
- payload = await request.json();
274
- } catch {
275
- return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
276
- }
277
- let draftItem = parseDraftItem(payload);
278
- if (!draftItem) {
279
- return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
280
- }
281
- let requestContext;
282
- let body = payload;
283
- if (Object.hasOwn(body, "routeContext")) {
284
- let reconstructed = reconstructRouteContext(request, body.routeContext);
285
- if (!reconstructed) {
286
- return json({ error: "invalid-route-context" }, STATUS_BAD_REQUEST);
287
- }
288
- requestContext = reconstructed;
289
- }
290
- let client;
291
- try {
292
- client = await config.getClient(request, requestContext);
293
- } catch (error) {
294
- console.error("\u274C Revalidate handler getClient failed.", error);
295
- return json({ error: "client-init-failed" }, STATUS_SERVER_ERROR);
296
- }
297
- let component = client.components.find(
298
- (candidate) => candidate?.schema?.type === draftItem.type
299
- );
300
- if (!component) {
301
- return json({ error: "unknown-component-type" }, STATUS_NOT_FOUND);
302
- }
303
- if (typeof component.loader !== "function") {
304
- return json({ error: "missing-loader" }, STATUS_UNPROCESSABLE);
305
- }
306
- try {
307
- let loaderData = await component.loader({
308
- data: {
309
- ...generateDataFromSchema(component.schema),
310
- ...draftItem.data
311
- },
312
- weaverse: client,
313
- context: client.requestContext,
314
- commerce: client.commerce
315
- });
316
- return json({ loaderData: loaderData ?? null }, STATUS_OK);
317
- } catch (error) {
318
- console.error(
319
- "\u274C Item loader revalidation failed.",
320
- draftItem.type,
321
- draftItem.id,
322
- error
323
- );
324
- return json({ error: "loader-failed" }, STATUS_SERVER_ERROR);
325
- }
326
- }
327
- };
209
+ return { POST: async (request) => {
210
+ let payload;
211
+ try {
212
+ payload = await request.json();
213
+ } catch {
214
+ return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
215
+ }
216
+ let draftItem = parseDraftItem(payload);
217
+ if (!draftItem) return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
218
+ let requestContext;
219
+ let body = payload;
220
+ if (Object.hasOwn(body, "routeContext")) {
221
+ let reconstructed = reconstructRouteContext(request, body.routeContext);
222
+ if (!reconstructed) return json({ error: "invalid-route-context" }, STATUS_BAD_REQUEST);
223
+ requestContext = reconstructed;
224
+ }
225
+ let client;
226
+ try {
227
+ client = await config.getClient(request, requestContext);
228
+ } catch (error) {
229
+ console.error("❌ Revalidate handler getClient failed.", error);
230
+ return json({ error: "client-init-failed" }, STATUS_SERVER_ERROR);
231
+ }
232
+ let component = client.components.find((candidate) => candidate?.schema?.type === draftItem.type);
233
+ if (!component) return json({ error: "unknown-component-type" }, STATUS_NOT_FOUND);
234
+ if (typeof component.loader !== "function") return json({ error: "missing-loader" }, STATUS_UNPROCESSABLE);
235
+ try {
236
+ return json({ loaderData: await component.loader({
237
+ data: {
238
+ ...generateDataFromSchema(component.schema),
239
+ ...draftItem.data
240
+ },
241
+ weaverse: client,
242
+ context: client.requestContext,
243
+ commerce: client.commerce
244
+ }) ?? null }, STATUS_OK);
245
+ } catch (error) {
246
+ console.error("❌ Item loader revalidation failed.", draftItem.type, draftItem.id, error);
247
+ return json({ error: "loader-failed" }, STATUS_SERVER_ERROR);
248
+ }
249
+ } };
328
250
  }
329
-
330
- // src/server/server-client.ts
331
- var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
332
- var RANDOM_ID_RADIX = 36;
333
- var RANDOM_ID_SLICE_START = 2;
334
- var MAX_CUSTOM_PAGE_PAGES = 100;
335
- var JSON_HEADERS = {
336
- "Content-Type": "application/json",
337
- Accept: "application/json",
338
- "Accept-Encoding": "gzip, deflate, br"
251
+ //#endregion
252
+ //#region src/server/server-client.ts
253
+ const DEFAULT_FETCH_TIMEOUT_MS = 1e4;
254
+ const RANDOM_ID_RADIX = 36;
255
+ const RANDOM_ID_SLICE_START = 2;
256
+ const MAX_CUSTOM_PAGE_PAGES = 100;
257
+ const JSON_HEADERS = {
258
+ "Content-Type": "application/json",
259
+ Accept: "application/json",
260
+ "Accept-Encoding": "gzip, deflate, br"
339
261
  };
340
262
  function asThemeSchema(schema) {
341
- return schema && typeof schema === "object" ? schema : void 0;
263
+ return schema && typeof schema === "object" ? schema : void 0;
342
264
  }
343
265
  function generateUuid() {
344
- if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
345
- return crypto.randomUUID();
346
- }
347
- return `id-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
266
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
267
+ return `id-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
348
268
  }
349
269
  function normalizeWeaverseNextPageAssignment(input) {
350
- if (!input || typeof input !== "object") {
351
- return;
352
- }
353
- let { projectId, type, handle, locale, meta } = input;
354
- if (typeof projectId !== "string" || typeof type !== "string" || typeof handle !== "string") {
355
- return;
356
- }
357
- let normalized = {
358
- projectId,
359
- type,
360
- handle,
361
- locale: typeof locale === "string" ? locale : ""
362
- };
363
- if (meta && typeof meta === "object") {
364
- normalized.meta = meta;
365
- }
366
- return normalized;
270
+ if (!input || typeof input !== "object") return;
271
+ let { projectId, type, handle, locale, meta } = input;
272
+ if (typeof projectId !== "string" || typeof type !== "string" || typeof handle !== "string") return;
273
+ let normalized = {
274
+ projectId,
275
+ type,
276
+ handle,
277
+ locale: typeof locale === "string" ? locale : ""
278
+ };
279
+ if (meta && typeof meta === "object") normalized.meta = meta;
280
+ return normalized;
367
281
  }
368
282
  var NextServerClient = class {
369
- components;
370
- commerce;
371
- requestContext;
372
- themeSchema;
373
- themeSettings;
374
- data = null;
375
- dataContext = {};
376
- configs;
377
- _projectIdInput;
378
- _baseConfigs;
379
- _fetch;
380
- _cache;
381
- _fetchTimeoutMs;
382
- _resolvedProjectId;
383
- constructor(config) {
384
- this.components = config.components;
385
- this.commerce = config.commerce;
386
- this.requestContext = config.requestContext;
387
- this.themeSchema = config.themeSchema;
388
- this.themeSettings = {
389
- ...generateDataFromSchema(config.themeSchema),
390
- ...config.themeSettings
391
- };
392
- this._projectIdInput = config.projectId;
393
- this._fetch = config.fetch ?? globalThis.fetch;
394
- this._cache = config.cache;
395
- this._fetchTimeoutMs = config.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
396
- this._baseConfigs = getWeaverseNextConfigs(config.requestContext, {
397
- env: config.env,
398
- weaverseHost: config.weaverseHost,
399
- weaverseApiBase: config.weaverseApiBase,
400
- weaverseVersion: config.weaverseVersion
401
- });
402
- this.configs = this._buildPublicConfigs(
403
- this._baseConfigs.queryProjectId || (typeof this._projectIdInput === "string" ? this._projectIdInput.trim() : "") || this._baseConfigs.envProjectId
404
- );
405
- }
406
- get projectId() {
407
- return this.configs.projectId;
408
- }
409
- /** Compatibility alias so loaders can call `weaverse.storefront.query(...)`. */
410
- get storefront() {
411
- return this.commerce?.storefront;
412
- }
413
- _buildPublicConfigs(projectId) {
414
- let base = this._baseConfigs;
415
- return {
416
- projectId,
417
- weaverseHost: base.weaverseHost,
418
- weaverseApiBase: base.weaverseApiBase,
419
- weaversePublicApiBase: base.weaverseApiBase,
420
- weaverseVersion: base.weaverseVersion,
421
- isDesignMode: base.isDesignMode,
422
- isPreviewMode: base.isPreviewMode,
423
- isRevisionPreview: base.isRevisionPreview,
424
- sectionType: base.sectionType,
425
- publicEnv: base.publicEnv
426
- };
427
- }
428
- resolveProjectId = async () => {
429
- if (this._resolvedProjectId !== void 0) {
430
- return this._resolvedProjectId;
431
- }
432
- let resolved = this._baseConfigs.queryProjectId;
433
- if (!resolved && typeof this._projectIdInput === "function") {
434
- try {
435
- let value = await this._projectIdInput(this.requestContext);
436
- if (typeof value === "string" && value.trim()) {
437
- resolved = value.trim();
438
- }
439
- } catch (error) {
440
- console.warn(
441
- "[WeaverseNextServerClient] projectId resolver threw, falling back.",
442
- error
443
- );
444
- }
445
- }
446
- if (!resolved && typeof this._projectIdInput === "string") {
447
- resolved = this._projectIdInput.trim();
448
- }
449
- if (!resolved) {
450
- resolved = this._baseConfigs.envProjectId;
451
- }
452
- this._resolvedProjectId = resolved;
453
- this.configs = this._buildPublicConfigs(resolved);
454
- return resolved;
455
- };
456
- fetchWithCache = async (url, options = {}) => {
457
- let { revalidate, tags, ...init } = options;
458
- let requestInit = { ...init };
459
- let { isDesignMode, isRevisionPreview } = this._baseConfigs;
460
- if (isDesignMode || isRevisionPreview) {
461
- requestInit.cache = "no-store";
462
- } else if (requestInit.cache === void 0) {
463
- let effectiveRevalidate = revalidate ?? this._cache?.revalidate;
464
- let effectiveTags = tags ?? this._cache?.tags;
465
- if (effectiveRevalidate !== void 0 || effectiveTags?.length) {
466
- requestInit.next = {
467
- ...effectiveRevalidate === void 0 ? {} : { revalidate: effectiveRevalidate },
468
- ...effectiveTags?.length ? { tags: effectiveTags } : {}
469
- };
470
- }
471
- }
472
- let controller = new AbortController();
473
- let timeoutId = setTimeout(() => controller.abort(), this._fetchTimeoutMs);
474
- try {
475
- let response = await this._fetch(url, {
476
- ...requestInit,
477
- signal: requestInit.signal ?? controller.signal
478
- });
479
- if (!response.ok) {
480
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
481
- }
482
- return await response.json();
483
- } finally {
484
- clearTimeout(timeoutId);
485
- }
486
- };
487
- _buildCustomPagesUrl(projectId, options, cursor) {
488
- let params = new URLSearchParams();
489
- if (options.locale) {
490
- params.set("locale", options.locale);
491
- }
492
- if (options.limit) {
493
- params.set("limit", String(options.limit));
494
- }
495
- if (cursor) {
496
- params.set("cursor", cursor);
497
- }
498
- let qs = params.toString();
499
- return `${this._baseConfigs.weaverseApiBase}/api/public/v1/projects/${projectId}/custom-pages${qs ? `?${qs}` : ""}`;
500
- }
501
- /**
502
- * Fetch all published custom pages for sitemap generation. Mirrors Hydrogen's
503
- * helper but uses Next's fetch cache knobs (`revalidate` / `tags`). It returns
504
- * accumulated results on partial pagination failure so a single API hiccup
505
- * does not break sitemap generation entirely.
506
- */
507
- fetchCustomPages = async (options = {}) => {
508
- let projectId = await this.resolveProjectId();
509
- let entries = [];
510
- if (!projectId) {
511
- console.warn(
512
- "[WeaverseNext] Failed to fetch custom pages: missing projectId"
513
- );
514
- return entries;
515
- }
516
- let cursor = null;
517
- for (let page = 0; page < MAX_CUSTOM_PAGE_PAGES; page += 1) {
518
- let url = this._buildCustomPagesUrl(projectId, options, cursor);
519
- try {
520
- let result = await this.fetchWithCache(url, {
521
- revalidate: options.revalidate,
522
- tags: options.tags
523
- });
524
- entries.push(...result.data ?? []);
525
- if (!result.nextCursor) {
526
- return entries;
527
- }
528
- cursor = result.nextCursor;
529
- } catch (error) {
530
- let message = error instanceof Error ? error.message : String(error);
531
- console.warn(
532
- entries.length === 0 ? `[WeaverseNext] Failed to fetch custom pages: ${message}` : `[WeaverseNext] Custom pages pagination interrupted: ${message}`
533
- );
534
- return entries;
535
- }
536
- }
537
- console.warn("[WeaverseNext] Custom pages pagination cap reached");
538
- return entries;
539
- };
540
- _generateFallbackPage(message) {
541
- let rootId = generateUuid();
542
- return {
543
- id: `fallback_page_${rootId}`,
544
- rootId,
545
- name: "Default Page",
546
- items: [
547
- {
548
- type: "main",
549
- id: rootId,
550
- data: { dangerouslySetInnerHTML: { __html: message } }
551
- }
552
- ]
553
- };
554
- }
555
- /**
556
- * Recursively materialize a section preset (and its preset children) into a
557
- * flat list of page items, returning the new item id. Ported from Hydrogen's
558
- * `recursivelyAddDataItem`. Returns `undefined` when the type isn't registered.
559
- */
560
- _buildPreviewItems(type, items, initData = {}) {
561
- let component = this.components.find((c) => c.schema?.type === type);
562
- if (!component) {
563
- return;
564
- }
565
- let { children, ...data } = component.schema?.presets ?? {};
566
- let childIds = [];
567
- if (children) {
568
- for (let child of children) {
569
- let { type: childType, children: _children, ...childData } = child;
570
- let childId = this._buildPreviewItems(childType, items, childData);
571
- if (childId) {
572
- childIds.push({ id: childId });
573
- }
574
- }
575
- }
576
- let id = generateUuid();
577
- items.push({ id, type, data: { ...data, ...initData }, children: childIds });
578
- return id;
579
- }
580
- /**
581
- * Synthetic loader data for Studio "section preview" mode. Mirrors Hydrogen's
582
- * `getPreviewData`: it builds a single-section page from the requested
583
- * `sectionType`'s presets without hitting `/api/public/project`, so preview
584
- * works even without a resolved `projectId`.
585
- */
586
- _generatePreviewData(projectId) {
587
- let { sectionType, weaverseHost } = this._baseConfigs;
588
- let items = [];
589
- let sectionId = sectionType ? this._buildPreviewItems(sectionType, items) : void 0;
590
- let configs = {
591
- ...this._buildPublicConfigs(projectId),
592
- isPreviewMode: true,
593
- weaverseHost,
594
- requestInfo: buildWeaverseNextRequestInfo(this.requestContext)
595
- };
596
- return {
597
- project: {
598
- id: projectId,
599
- weaverseShopId: "shop-id",
600
- name: "Section Preview"
601
- },
602
- configs,
603
- page: {
604
- id: "weaverse-preview-page",
605
- name: "Preview section",
606
- rootId: "1",
607
- items: [
608
- {
609
- id: "1",
610
- type: "main",
611
- data: {},
612
- children: sectionId ? [{ id: sectionId }] : []
613
- },
614
- ...items
615
- ]
616
- }
617
- };
618
- }
619
- _getLoadPageInput(input) {
620
- let {
621
- cache,
622
- projectId: routeProjectId,
623
- ...rest
624
- } = input;
625
- let params = {};
626
- for (let [key, value] of Object.entries(rest)) {
627
- if (value !== void 0) {
628
- params[key] = value;
629
- }
630
- }
631
- return { cache, params, routeProjectId };
632
- }
633
- async _resolveLoadPageProjectId(routeProjectId) {
634
- let effectiveProjectId = typeof routeProjectId === "string" && routeProjectId.trim() || await this.resolveProjectId();
635
- if (effectiveProjectId) {
636
- this.configs = this._buildPublicConfigs(effectiveProjectId);
637
- }
638
- return effectiveProjectId;
639
- }
640
- _fetchPagePayload(projectId, params, cache) {
641
- let i18n = this.requestContext?.i18n ?? this.commerce?.storefront?.i18n;
642
- let url = normalizeNextPageUrl(resolveRequestUrl(this.requestContext));
643
- let apiUrl = `${this._baseConfigs.weaverseApiBase}/api/public/project`;
644
- let userAgent = this.requestContext?.headers?.get("user-agent") ?? "";
645
- return this.fetchWithCache(apiUrl, {
646
- method: "POST",
647
- body: JSON.stringify({
648
- projectId,
649
- url,
650
- i18n,
651
- params,
652
- isDesignMode: this._baseConfigs.isDesignMode
653
- }),
654
- headers: { ...JSON_HEADERS, "X-Visitor-UA": userAgent },
655
- ...cache ?? {}
656
- });
657
- }
658
- _assertNoPageError(payload) {
659
- if ("error" in payload && typeof payload.error === "string") {
660
- throw new Error(payload.error);
661
- }
662
- }
663
- _createLoaderData(payload, projectId) {
664
- this._assertNoPageError(payload);
665
- let page = payload.page;
666
- let project = payload.project;
667
- let pageAssignment = normalizeWeaverseNextPageAssignment(
668
- payload.pageAssignment
669
- );
670
- page ??= this._generateFallbackPage(
671
- '<div style="text-align: center;">Please add new section to start.</div>'
672
- );
673
- project ??= {
674
- id: projectId,
675
- name: "Weaverse project not found.",
676
- weaverseShopId: ""
677
- };
678
- if (Array.isArray(page.items) && page.items.length === 0) {
679
- page.items.push({
680
- type: "main",
681
- id: page.rootId || generateUuid(),
682
- data: {}
683
- });
684
- }
685
- return {
686
- page,
687
- project,
688
- pageAssignment,
689
- configs: {
690
- ...this._buildPublicConfigs(projectId),
691
- requestInfo: buildWeaverseNextRequestInfo(this.requestContext)
692
- }
693
- };
694
- }
695
- async _runLoaders(data) {
696
- this.data = data;
697
- let withLoaders = await runWeaverseComponentLoaders({ client: this, data });
698
- this.data = withLoaders ?? data;
699
- return this.data;
700
- }
701
- loadPage = async (input = {}) => {
702
- try {
703
- let { cache, params, routeProjectId } = this._getLoadPageInput(input);
704
- let projectId = await this._resolveLoadPageProjectId(routeProjectId);
705
- if (this._baseConfigs.isPreviewMode) {
706
- let previewData = this._generatePreviewData(projectId || "x");
707
- this.data = previewData;
708
- return previewData;
709
- }
710
- if (!projectId) {
711
- throw new Error("Missing Weaverse projectId!");
712
- }
713
- let payload = await this._fetchPagePayload(projectId, params, cache);
714
- let data = this._createLoaderData(payload, projectId);
715
- return await this._runLoaders(data);
716
- } catch (error) {
717
- console.error("\u274C Page load failed", {
718
- url: this.requestContext?.url,
719
- projectId: input.projectId || this.configs.projectId,
720
- message: error instanceof Error ? error.message : String(error)
721
- });
722
- return null;
723
- }
724
- };
725
- /**
726
- * Resolve the request locale (e.g. `fr-ca`) used for merchant overrides.
727
- * Unlike Hydrogen — which defaults to `en`/`us` because a Hydrogen
728
- * storefront always has an i18n context — Next apps may not thread one
729
- * through, so a missing language or country means "no explicit locale" and
730
- * the translation fetch is skipped rather than guessed.
731
- */
732
- _resolveOverridesLocale() {
733
- let i18n = this.requestContext?.i18n;
734
- let language = typeof i18n?.language === "string" ? i18n.language.trim() : "";
735
- let country = typeof i18n?.country === "string" ? i18n.country.trim() : "";
736
- if (!(language && country)) {
737
- return;
738
- }
739
- return `${language.toLowerCase()}-${country.toLowerCase()}`;
740
- }
741
- /**
742
- * Fetch locale-specific static-text overrides from the translation API.
743
- * Ported from Hydrogen's `fetchMerchantOverrides`: skipped entirely when the
744
- * theme has no `i18n` schema (nothing translatable) or when the request has
745
- * no explicit locale, and never allowed to fail theme settings.
746
- */
747
- async _fetchMerchantOverrides(projectId, options) {
748
- let { weaverseHost } = this._baseConfigs;
749
- if (!(asThemeSchema(this.themeSchema)?.i18n && projectId && weaverseHost)) {
750
- return;
751
- }
752
- let locale = this._resolveOverridesLocale();
753
- if (!locale) {
754
- return;
755
- }
756
- let url = `${weaverseHost}/api/translation/static?projectId=${projectId}&locale=${locale}`;
757
- try {
758
- let overrides = await this.fetchWithCache(url, {
759
- method: "GET",
760
- headers: { Accept: "application/json" },
761
- revalidate: options.revalidate,
762
- tags: options.tags
763
- });
764
- return typeof overrides === "object" && overrides !== null ? overrides : void 0;
765
- } catch (error) {
766
- console.warn(
767
- "[WeaverseNext] Unable to load merchant overrides, using theme defaults:",
768
- error instanceof Error ? error.message : String(error)
769
- );
770
- return;
771
- }
772
- }
773
- loadThemeSettings = async (optionsInput = {}) => {
774
- let options = optionsInput;
775
- let themeSchema = asThemeSchema(this.themeSchema);
776
- let defaultThemeSettings = generateDataFromSchema(this.themeSchema);
777
- let staticContent = themeSchema?.i18n?.staticContent;
778
- try {
779
- let projectId = await this.resolveProjectId();
780
- if (!projectId) {
781
- throw new Error("Missing Weaverse projectId!");
782
- }
783
- let { isDesignMode } = this._baseConfigs;
784
- let url = `${this._baseConfigs.weaverseApiBase}/api/public/project_configs`;
785
- let [result, merchantOverrides] = await Promise.all([
786
- this.fetchWithCache(url, {
787
- method: "POST",
788
- body: JSON.stringify({ projectId, isDesignMode }),
789
- headers: JSON_HEADERS,
790
- revalidate: options.revalidate,
791
- tags: options.tags
792
- }),
793
- this._fetchMerchantOverrides(projectId, options)
794
- ]);
795
- if (typeof result !== "object" || result === null) {
796
- result = { theme: defaultThemeSettings };
797
- }
798
- if (themeSchema?.settings || themeSchema?.inspector) {
799
- if (!result.theme || typeof result.theme !== "object") {
800
- result.theme = defaultThemeSettings;
801
- } else {
802
- result.theme = { ...defaultThemeSettings, ...result.theme };
803
- }
804
- }
805
- if (staticContent) {
806
- result.staticContent = staticContent;
807
- }
808
- if (merchantOverrides) {
809
- result.merchantOverrides = merchantOverrides;
810
- }
811
- if (isDesignMode) {
812
- let serializableSchema = themeSchema ? {
813
- ...themeSchema,
814
- settings: themeSchema.settings?.map((group) => ({
815
- ...group,
816
- inputs: group?.inputs?.map(
817
- (inputItem) => typeof inputItem?.condition === "function" ? {
818
- ...inputItem,
819
- condition: inputItem.condition.toString()
820
- } : inputItem
821
- )
822
- }))
823
- } : void 0;
824
- result = {
825
- ...result,
826
- schema: serializableSchema,
827
- publicEnv: this._baseConfigs.publicEnv
828
- };
829
- }
830
- return result;
831
- } catch (error) {
832
- let errorDetails = error instanceof Error ? error.message : String(error);
833
- console.error("Unable to load theme settings", errorDetails);
834
- return {
835
- theme: defaultThemeSettings,
836
- staticContent,
837
- _error: errorDetails,
838
- _loadFailed: true
839
- };
840
- }
841
- };
283
+ components;
284
+ commerce;
285
+ requestContext;
286
+ themeSchema;
287
+ themeSettings;
288
+ data = null;
289
+ dataContext = {};
290
+ configs;
291
+ _projectIdInput;
292
+ _baseConfigs;
293
+ _fetch;
294
+ _cache;
295
+ _fetchTimeoutMs;
296
+ _resolvedProjectId;
297
+ constructor(config) {
298
+ this.components = config.components;
299
+ this.commerce = config.commerce;
300
+ this.requestContext = config.requestContext;
301
+ this.themeSchema = config.themeSchema;
302
+ this.themeSettings = {
303
+ ...generateDataFromSchema(config.themeSchema),
304
+ ...config.themeSettings
305
+ };
306
+ this._projectIdInput = config.projectId;
307
+ this._fetch = config.fetch ?? globalThis.fetch;
308
+ this._cache = config.cache;
309
+ this._fetchTimeoutMs = config.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
310
+ this._baseConfigs = getWeaverseNextConfigs(config.requestContext, {
311
+ env: config.env,
312
+ weaverseHost: config.weaverseHost,
313
+ weaverseApiBase: config.weaverseApiBase,
314
+ weaverseVersion: config.weaverseVersion
315
+ });
316
+ this.configs = this._buildPublicConfigs(this._baseConfigs.queryProjectId || (typeof this._projectIdInput === "string" ? this._projectIdInput.trim() : "") || this._baseConfigs.envProjectId);
317
+ }
318
+ get projectId() {
319
+ return this.configs.projectId;
320
+ }
321
+ get storefront() {
322
+ return this.commerce?.storefront;
323
+ }
324
+ _buildPublicConfigs(projectId) {
325
+ let base = this._baseConfigs;
326
+ return {
327
+ projectId,
328
+ weaverseHost: base.weaverseHost,
329
+ weaverseApiBase: base.weaverseApiBase,
330
+ weaversePublicApiBase: base.weaverseApiBase,
331
+ weaverseVersion: base.weaverseVersion,
332
+ isDesignMode: base.isDesignMode,
333
+ isPreviewMode: base.isPreviewMode,
334
+ isRevisionPreview: base.isRevisionPreview,
335
+ sectionType: base.sectionType,
336
+ publicEnv: base.publicEnv
337
+ };
338
+ }
339
+ resolveProjectId = async () => {
340
+ if (this._resolvedProjectId !== void 0) return this._resolvedProjectId;
341
+ let resolved = this._baseConfigs.queryProjectId;
342
+ if (!resolved && typeof this._projectIdInput === "function") try {
343
+ let value = await this._projectIdInput(this.requestContext);
344
+ if (typeof value === "string" && value.trim()) resolved = value.trim();
345
+ } catch (error) {
346
+ console.warn("[WeaverseNextServerClient] projectId resolver threw, falling back.", error);
347
+ }
348
+ if (!resolved && typeof this._projectIdInput === "string") resolved = this._projectIdInput.trim();
349
+ if (!resolved) resolved = this._baseConfigs.envProjectId;
350
+ this._resolvedProjectId = resolved;
351
+ this.configs = this._buildPublicConfigs(resolved);
352
+ return resolved;
353
+ };
354
+ fetchWithCache = async (url, options = {}) => {
355
+ let { revalidate, tags, ...init } = options;
356
+ let requestInit = { ...init };
357
+ let { isDesignMode, isRevisionPreview } = this._baseConfigs;
358
+ if (isDesignMode || isRevisionPreview) requestInit.cache = "no-store";
359
+ else if (requestInit.cache === void 0) {
360
+ let effectiveRevalidate = revalidate ?? this._cache?.revalidate;
361
+ let effectiveTags = tags ?? this._cache?.tags;
362
+ if (effectiveRevalidate !== void 0 || effectiveTags?.length) requestInit.next = {
363
+ ...effectiveRevalidate === void 0 ? {} : { revalidate: effectiveRevalidate },
364
+ ...effectiveTags?.length ? { tags: effectiveTags } : {}
365
+ };
366
+ }
367
+ let controller = new AbortController();
368
+ let timeoutId = setTimeout(() => controller.abort(), this._fetchTimeoutMs);
369
+ try {
370
+ let response = await this._fetch(url, {
371
+ ...requestInit,
372
+ signal: requestInit.signal ?? controller.signal
373
+ });
374
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
375
+ return await response.json();
376
+ } finally {
377
+ clearTimeout(timeoutId);
378
+ }
379
+ };
380
+ _buildCustomPagesUrl(projectId, options, cursor) {
381
+ let params = new URLSearchParams();
382
+ if (options.locale) params.set("locale", options.locale);
383
+ if (options.limit) params.set("limit", String(options.limit));
384
+ if (cursor) params.set("cursor", cursor);
385
+ let qs = params.toString();
386
+ return `${this._baseConfigs.weaverseApiBase}/api/public/v1/projects/${projectId}/custom-pages${qs ? `?${qs}` : ""}`;
387
+ }
388
+ fetchCustomPages = async (options = {}) => {
389
+ let projectId = await this.resolveProjectId();
390
+ let entries = [];
391
+ if (!projectId) {
392
+ console.warn("[WeaverseNext] Failed to fetch custom pages: missing projectId");
393
+ return entries;
394
+ }
395
+ let cursor = null;
396
+ for (let page = 0; page < MAX_CUSTOM_PAGE_PAGES; page += 1) {
397
+ let url = this._buildCustomPagesUrl(projectId, options, cursor);
398
+ try {
399
+ let result = await this.fetchWithCache(url, {
400
+ revalidate: options.revalidate,
401
+ tags: options.tags
402
+ });
403
+ entries.push(...result.data ?? []);
404
+ if (!result.nextCursor) return entries;
405
+ cursor = result.nextCursor;
406
+ } catch (error) {
407
+ let message = error instanceof Error ? error.message : String(error);
408
+ console.warn(entries.length === 0 ? `[WeaverseNext] Failed to fetch custom pages: ${message}` : `[WeaverseNext] Custom pages pagination interrupted: ${message}`);
409
+ return entries;
410
+ }
411
+ }
412
+ console.warn("[WeaverseNext] Custom pages pagination cap reached");
413
+ return entries;
414
+ };
415
+ _generateFallbackPage(message) {
416
+ let rootId = generateUuid();
417
+ return {
418
+ id: `fallback_page_${rootId}`,
419
+ rootId,
420
+ name: "Default Page",
421
+ items: [{
422
+ type: "main",
423
+ id: rootId,
424
+ data: { dangerouslySetInnerHTML: { __html: message } }
425
+ }]
426
+ };
427
+ }
428
+ _buildPreviewItems(type, items, initData = {}) {
429
+ let component = this.components.find((c) => c.schema?.type === type);
430
+ if (!component) return;
431
+ let { children, ...data } = component.schema?.presets ?? {};
432
+ let childIds = [];
433
+ if (children) for (let child of children) {
434
+ let { type: childType, children: _children, ...childData } = child;
435
+ let childId = this._buildPreviewItems(childType, items, childData);
436
+ if (childId) childIds.push({ id: childId });
437
+ }
438
+ let id = generateUuid();
439
+ items.push({
440
+ id,
441
+ type,
442
+ data: {
443
+ ...data,
444
+ ...initData
445
+ },
446
+ children: childIds
447
+ });
448
+ return id;
449
+ }
450
+ _generatePreviewData(projectId) {
451
+ let { sectionType, weaverseHost } = this._baseConfigs;
452
+ let items = [];
453
+ let sectionId = sectionType ? this._buildPreviewItems(sectionType, items) : void 0;
454
+ let configs = {
455
+ ...this._buildPublicConfigs(projectId),
456
+ isPreviewMode: true,
457
+ weaverseHost,
458
+ requestInfo: buildWeaverseNextRequestInfo(this.requestContext)
459
+ };
460
+ return {
461
+ project: {
462
+ id: projectId,
463
+ weaverseShopId: "shop-id",
464
+ name: "Section Preview"
465
+ },
466
+ configs,
467
+ page: {
468
+ id: "weaverse-preview-page",
469
+ name: "Preview section",
470
+ rootId: "1",
471
+ items: [{
472
+ id: "1",
473
+ type: "main",
474
+ data: {},
475
+ children: sectionId ? [{ id: sectionId }] : []
476
+ }, ...items]
477
+ }
478
+ };
479
+ }
480
+ _getLoadPageInput(input) {
481
+ let { cache, projectId: routeProjectId, ...rest } = input;
482
+ let params = {};
483
+ for (let [key, value] of Object.entries(rest)) if (value !== void 0) params[key] = value;
484
+ return {
485
+ cache,
486
+ params,
487
+ routeProjectId
488
+ };
489
+ }
490
+ async _resolveLoadPageProjectId(routeProjectId) {
491
+ let effectiveProjectId = typeof routeProjectId === "string" && routeProjectId.trim() || await this.resolveProjectId();
492
+ if (effectiveProjectId) this.configs = this._buildPublicConfigs(effectiveProjectId);
493
+ return effectiveProjectId;
494
+ }
495
+ _fetchPagePayload(projectId, params, cache) {
496
+ let i18n = this.requestContext?.i18n ?? this.commerce?.storefront?.i18n;
497
+ let url = normalizeNextPageUrl(resolveRequestUrl(this.requestContext));
498
+ let apiUrl = `${this._baseConfigs.weaverseApiBase}/api/public/project`;
499
+ let userAgent = this.requestContext?.headers?.get("user-agent") ?? "";
500
+ return this.fetchWithCache(apiUrl, {
501
+ method: "POST",
502
+ body: JSON.stringify({
503
+ projectId,
504
+ url,
505
+ i18n,
506
+ params,
507
+ isDesignMode: this._baseConfigs.isDesignMode
508
+ }),
509
+ headers: {
510
+ ...JSON_HEADERS,
511
+ "X-Visitor-UA": userAgent
512
+ },
513
+ ...cache ?? {}
514
+ });
515
+ }
516
+ _assertNoPageError(payload) {
517
+ if ("error" in payload && typeof payload.error === "string") throw new Error(payload.error);
518
+ }
519
+ _createLoaderData(payload, projectId) {
520
+ this._assertNoPageError(payload);
521
+ let page = payload.page;
522
+ let project = payload.project;
523
+ let pageAssignment = normalizeWeaverseNextPageAssignment(payload.pageAssignment);
524
+ page ??= this._generateFallbackPage("<div style=\"text-align: center;\">Please add new section to start.</div>");
525
+ project ??= {
526
+ id: projectId,
527
+ name: "Weaverse project not found.",
528
+ weaverseShopId: ""
529
+ };
530
+ if (Array.isArray(page.items) && page.items.length === 0) page.items.push({
531
+ type: "main",
532
+ id: page.rootId || generateUuid(),
533
+ data: {}
534
+ });
535
+ return {
536
+ page,
537
+ project,
538
+ pageAssignment,
539
+ configs: {
540
+ ...this._buildPublicConfigs(projectId),
541
+ requestInfo: buildWeaverseNextRequestInfo(this.requestContext)
542
+ }
543
+ };
544
+ }
545
+ async _runLoaders(data) {
546
+ this.data = data;
547
+ let withLoaders = await runWeaverseComponentLoaders({
548
+ client: this,
549
+ data
550
+ });
551
+ this.data = withLoaders ?? data;
552
+ return this.data;
553
+ }
554
+ loadPage = async (input = {}) => {
555
+ try {
556
+ let { cache, params, routeProjectId } = this._getLoadPageInput(input);
557
+ let projectId = await this._resolveLoadPageProjectId(routeProjectId);
558
+ if (this._baseConfigs.isPreviewMode) {
559
+ let previewData = this._generatePreviewData(projectId || "x");
560
+ this.data = previewData;
561
+ return previewData;
562
+ }
563
+ if (!projectId) throw new Error("Missing Weaverse projectId!");
564
+ let payload = await this._fetchPagePayload(projectId, params, cache);
565
+ let data = this._createLoaderData(payload, projectId);
566
+ return await this._runLoaders(data);
567
+ } catch (error) {
568
+ console.error("❌ Page load failed", {
569
+ url: this.requestContext?.url,
570
+ projectId: input.projectId || this.configs.projectId,
571
+ message: error instanceof Error ? error.message : String(error)
572
+ });
573
+ return null;
574
+ }
575
+ };
576
+ _resolveOverridesLocale() {
577
+ let i18n = this.requestContext?.i18n;
578
+ let language = typeof i18n?.language === "string" ? i18n.language.trim() : "";
579
+ let country = typeof i18n?.country === "string" ? i18n.country.trim() : "";
580
+ if (!(language && country)) return;
581
+ return `${language.toLowerCase()}-${country.toLowerCase()}`;
582
+ }
583
+ async _fetchMerchantOverrides(projectId, options) {
584
+ let { weaverseHost } = this._baseConfigs;
585
+ if (!(asThemeSchema(this.themeSchema)?.i18n && projectId && weaverseHost)) return;
586
+ let locale = this._resolveOverridesLocale();
587
+ if (!locale) return;
588
+ let url = `${weaverseHost}/api/translation/static?projectId=${projectId}&locale=${locale}`;
589
+ try {
590
+ let overrides = await this.fetchWithCache(url, {
591
+ method: "GET",
592
+ headers: { Accept: "application/json" },
593
+ revalidate: options.revalidate,
594
+ tags: options.tags
595
+ });
596
+ return typeof overrides === "object" && overrides !== null ? overrides : void 0;
597
+ } catch (error) {
598
+ console.warn("[WeaverseNext] Unable to load merchant overrides, using theme defaults:", error instanceof Error ? error.message : String(error));
599
+ return;
600
+ }
601
+ }
602
+ loadThemeSettings = async (optionsInput = {}) => {
603
+ let options = optionsInput;
604
+ let themeSchema = asThemeSchema(this.themeSchema);
605
+ let defaultThemeSettings = generateDataFromSchema(this.themeSchema);
606
+ let staticContent = themeSchema?.i18n?.staticContent;
607
+ try {
608
+ let projectId = await this.resolveProjectId();
609
+ if (!projectId) throw new Error("Missing Weaverse projectId!");
610
+ let { isDesignMode } = this._baseConfigs;
611
+ let url = `${this._baseConfigs.weaverseApiBase}/api/public/project_configs`;
612
+ let [result, merchantOverrides] = await Promise.all([this.fetchWithCache(url, {
613
+ method: "POST",
614
+ body: JSON.stringify({
615
+ projectId,
616
+ isDesignMode
617
+ }),
618
+ headers: JSON_HEADERS,
619
+ revalidate: options.revalidate,
620
+ tags: options.tags
621
+ }), this._fetchMerchantOverrides(projectId, options)]);
622
+ if (typeof result !== "object" || result === null) result = { theme: defaultThemeSettings };
623
+ if (themeSchema?.settings || themeSchema?.inspector) if (!result.theme || typeof result.theme !== "object") result.theme = defaultThemeSettings;
624
+ else result.theme = {
625
+ ...defaultThemeSettings,
626
+ ...result.theme
627
+ };
628
+ if (staticContent) result.staticContent = staticContent;
629
+ if (merchantOverrides) result.merchantOverrides = merchantOverrides;
630
+ if (isDesignMode) {
631
+ let serializableSchema = themeSchema ? {
632
+ ...themeSchema,
633
+ settings: themeSchema.settings?.map((group) => ({
634
+ ...group,
635
+ inputs: group?.inputs?.map((inputItem) => typeof inputItem?.condition === "function" ? {
636
+ ...inputItem,
637
+ condition: inputItem.condition.toString()
638
+ } : inputItem)
639
+ }))
640
+ } : void 0;
641
+ result = {
642
+ ...result,
643
+ schema: serializableSchema,
644
+ publicEnv: this._baseConfigs.publicEnv
645
+ };
646
+ }
647
+ return result;
648
+ } catch (error) {
649
+ let errorDetails = error instanceof Error ? error.message : String(error);
650
+ console.error("Unable to load theme settings", errorDetails);
651
+ return {
652
+ theme: defaultThemeSettings,
653
+ staticContent,
654
+ _error: errorDetails,
655
+ _loadFailed: true
656
+ };
657
+ }
658
+ };
842
659
  };
843
660
  function createWeaverseNextServerClient(config) {
844
- return new NextServerClient(config);
661
+ return new NextServerClient(config);
845
662
  }
663
+ //#endregion
664
+ export { createWeaverseNextRevalidateHandler, createWeaverseNextServerClient, formatWeaverseNextSeoMetadata, getWeaverseNextConfigs, getWeaverseNextSeoMetadata, normalizeNextPageUrl, resolveRequestUrl };
846
665
 
847
- export { createWeaverseNextRevalidateHandler, createWeaverseNextServerClient, getWeaverseNextConfigs, normalizeNextPageUrl, resolveRequestUrl };
848
- //# sourceMappingURL=server.mjs.map
849
666
  //# sourceMappingURL=server.mjs.map