@weaverse/next 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (107) hide show
  1. package/README.md +586 -123
  2. package/dist/chunk-LH6AAEIK.mjs +326 -0
  3. package/dist/chunk-LH6AAEIK.mjs.map +1 -0
  4. package/dist/index.js +784 -60
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +588 -58
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/server.js +537 -76
  9. package/dist/server.js.map +1 -1
  10. package/dist/server.mjs +363 -27
  11. package/dist/server.mjs.map +1 -1
  12. package/dist/types/client.d.ts +16 -0
  13. package/dist/types/client.d.ts.map +1 -0
  14. package/dist/types/hooks.d.ts +20 -0
  15. package/dist/types/hooks.d.ts.map +1 -0
  16. package/dist/types/index.d.ts +34 -0
  17. package/dist/types/index.d.ts.map +1 -0
  18. package/dist/types/item.d.ts +60 -0
  19. package/dist/types/item.d.ts.map +1 -0
  20. package/dist/types/loader.d.ts +25 -0
  21. package/dist/types/loader.d.ts.map +1 -0
  22. package/dist/types/main.d.ts +11 -0
  23. package/dist/types/main.d.ts.map +1 -0
  24. package/dist/types/provider.d.ts +82 -0
  25. package/dist/types/provider.d.ts.map +1 -0
  26. package/dist/types/registry.d.ts +15 -0
  27. package/dist/types/registry.d.ts.map +1 -0
  28. package/dist/types/renderer.d.ts +21 -0
  29. package/dist/types/renderer.d.ts.map +1 -0
  30. package/dist/types/request-info.d.ts +19 -0
  31. package/dist/types/request-info.d.ts.map +1 -0
  32. package/dist/types/revalidate-item.d.ts +82 -0
  33. package/dist/types/revalidate-item.d.ts.map +1 -0
  34. package/dist/types/root-provider.d.ts +62 -0
  35. package/dist/types/root-provider.d.ts.map +1 -0
  36. package/dist/types/runtime.d.ts +113 -0
  37. package/dist/types/runtime.d.ts.map +1 -0
  38. package/dist/types/seo.d.ts +55 -0
  39. package/dist/types/seo.d.ts.map +1 -0
  40. package/dist/types/server/configs.d.ts +27 -0
  41. package/dist/types/server/configs.d.ts.map +1 -0
  42. package/dist/types/server/normalize-page-url.d.ts +19 -0
  43. package/dist/types/server/normalize-page-url.d.ts.map +1 -0
  44. package/dist/types/server/revalidate-handler.d.ts +55 -0
  45. package/dist/types/server/revalidate-handler.d.ts.map +1 -0
  46. package/dist/types/server/server-client.d.ts +21 -0
  47. package/dist/types/server/server-client.d.ts.map +1 -0
  48. package/dist/types/server.d.ts +19 -0
  49. package/dist/types/server.d.ts.map +1 -0
  50. package/dist/types/studio-bridge.d.ts +16 -0
  51. package/dist/types/studio-bridge.d.ts.map +1 -0
  52. package/dist/types/studio-connect.d.ts +27 -0
  53. package/dist/types/studio-connect.d.ts.map +1 -0
  54. package/dist/types/studio-router.d.ts +50 -0
  55. package/dist/types/studio-router.d.ts.map +1 -0
  56. package/dist/types/studio-script-src.d.ts +12 -0
  57. package/dist/types/studio-script-src.d.ts.map +1 -0
  58. package/dist/types/theme-settings-store.d.ts +18 -0
  59. package/dist/types/theme-settings-store.d.ts.map +1 -0
  60. package/dist/types/translation-context.d.ts +156 -0
  61. package/dist/types/translation-context.d.ts.map +1 -0
  62. package/dist/types/translation-store.d.ts +44 -0
  63. package/dist/types/translation-store.d.ts.map +1 -0
  64. package/dist/types/types.d.ts +665 -0
  65. package/dist/types/types.d.ts.map +1 -0
  66. package/dist/types/use-weaverse-next-studio.d.ts +34 -0
  67. package/dist/types/use-weaverse-next-studio.d.ts.map +1 -0
  68. package/dist/types/utils.d.ts +12 -0
  69. package/dist/types/utils.d.ts.map +1 -0
  70. package/package.json +12 -8
  71. package/src/client.ts +102 -0
  72. package/src/hooks.ts +50 -0
  73. package/src/index.ts +153 -0
  74. package/src/item.ts +167 -0
  75. package/src/loader.ts +135 -0
  76. package/src/main.tsx +45 -0
  77. package/src/provider.tsx +225 -0
  78. package/src/registry.ts +63 -0
  79. package/src/renderer.tsx +100 -0
  80. package/src/request-info.ts +124 -0
  81. package/src/revalidate-item.ts +211 -0
  82. package/src/root-provider.tsx +182 -0
  83. package/src/runtime.ts +555 -0
  84. package/src/seo.ts +157 -0
  85. package/src/server/configs.ts +178 -0
  86. package/src/server/normalize-page-url.ts +73 -0
  87. package/src/server/revalidate-handler.ts +357 -0
  88. package/src/server/server-client.ts +728 -0
  89. package/src/server.ts +67 -0
  90. package/src/studio-bridge.tsx +82 -0
  91. package/src/studio-connect.tsx +88 -0
  92. package/src/studio-router.ts +85 -0
  93. package/src/studio-script-src.ts +128 -0
  94. package/src/theme-settings-store.ts +47 -0
  95. package/src/translation-context.tsx +299 -0
  96. package/src/translation-store.ts +69 -0
  97. package/src/types.ts +753 -0
  98. package/src/use-weaverse-next-studio.tsx +106 -0
  99. package/src/utils.ts +37 -0
  100. package/dist/chunk-SS6QUKYY.mjs +0 -149
  101. package/dist/chunk-SS6QUKYY.mjs.map +0 -1
  102. package/dist/index.d.mts +0 -250
  103. package/dist/index.d.ts +0 -250
  104. package/dist/server.d.mts +0 -60
  105. package/dist/server.d.ts +0 -60
  106. package/dist/types-DvEeLX6L.d.mts +0 -321
  107. package/dist/types-DvEeLX6L.d.ts +0 -321
package/dist/server.js CHANGED
@@ -1,5 +1,72 @@
1
1
  'use strict';
2
2
 
3
+ var schema = require('@weaverse/schema');
4
+
5
+ // src/seo.ts
6
+ function hasOpenGraphContent(seo) {
7
+ return Boolean(seo?.title || seo?.description || seo?.image || seo?.type);
8
+ }
9
+ function hasTwitterContent(seo) {
10
+ return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType);
11
+ }
12
+ function normalizeTwitterCard(cardType) {
13
+ return cardType === "summary_large_image" ? "summary_large_image" : "summary";
14
+ }
15
+ function normalizeOpenGraphType(type) {
16
+ if (type === "article" || type === "profile" || type === "video.other") {
17
+ return type;
18
+ }
19
+ return "website";
20
+ }
21
+ function formatWeaverseNextSeoMetadata(seo) {
22
+ let index = seo?.robots?.index !== false;
23
+ let follow = seo?.robots?.follow !== false;
24
+ let metadata = {
25
+ robots: { index, follow }
26
+ };
27
+ if (!seo) {
28
+ return metadata;
29
+ }
30
+ if (seo.title) {
31
+ metadata.title = seo.title;
32
+ }
33
+ if (seo.description) {
34
+ metadata.description = seo.description;
35
+ }
36
+ if (seo.keywords) {
37
+ metadata.keywords = seo.keywords;
38
+ }
39
+ if (seo.canonicalUrl) {
40
+ metadata.alternates = { canonical: seo.canonicalUrl };
41
+ }
42
+ let openGraph = seo.openGraph;
43
+ if (hasOpenGraphContent(openGraph)) {
44
+ metadata.openGraph = {
45
+ title: openGraph?.title,
46
+ description: openGraph?.description,
47
+ images: openGraph?.image ? [openGraph.image] : void 0,
48
+ type: normalizeOpenGraphType(openGraph?.type)
49
+ };
50
+ }
51
+ let twitter = seo.twitter;
52
+ if (hasTwitterContent(twitter)) {
53
+ metadata.twitter = {
54
+ card: normalizeTwitterCard(twitter?.cardType),
55
+ title: twitter?.title,
56
+ description: twitter?.description,
57
+ images: twitter?.image ? [twitter.image] : void 0
58
+ };
59
+ }
60
+ return metadata;
61
+ }
62
+ function getWeaverseNextSeoMetadata(data) {
63
+ if (!data) {
64
+ return formatWeaverseNextSeoMetadata(null);
65
+ }
66
+ let page = "items" in data && "id" in data ? data : data.page ?? null;
67
+ return formatWeaverseNextSeoMetadata(page?.seo ?? null);
68
+ }
69
+
3
70
  // src/server/configs.ts
4
71
  var DEFAULT_WEAVERSE_HOST = "https://studio.weaverse.io";
5
72
  var DEFAULT_WEAVERSE_API_BASE = "https://api.weaverse.io";
@@ -71,7 +138,6 @@ function getWeaverseNextConfigs(context, options = {}) {
71
138
  envProjectId: readEnv(env, "WEAVERSE_PROJECT_ID") || "",
72
139
  weaverseHost,
73
140
  weaverseApiBase,
74
- weaverseApiKey: lastParam(searchParams, "weaverseApiKey") || readEnv(env, "WEAVERSE_API_KEY") || "",
75
141
  weaverseVersion: lastParam(searchParams, "weaverseVersion") || options.weaverseVersion || "",
76
142
  isDesignMode,
77
143
  isPreviewMode,
@@ -84,8 +150,84 @@ function getWeaverseNextConfigs(context, options = {}) {
84
150
  };
85
151
  }
86
152
 
153
+ // src/request-info.ts
154
+ function toUrl2(value) {
155
+ return typeof value === "string" ? new URL(value, "http://localhost") : value;
156
+ }
157
+ function getSearchParamsFromContext(context) {
158
+ if (context?.searchParams) {
159
+ return new URLSearchParams(context.searchParams);
160
+ }
161
+ if (context?.url) {
162
+ let url = toUrl2(context.url);
163
+ return new URLSearchParams(url.searchParams);
164
+ }
165
+ return new URLSearchParams();
166
+ }
167
+ function withPathPrefix(pathname, i18n) {
168
+ let raw = typeof i18n?.pathPrefix === "string" ? i18n.pathPrefix : "";
169
+ let segments = raw.split("/").filter(Boolean);
170
+ let prefix = segments.length > 0 ? `/${segments.join("/")}` : "";
171
+ if (prefix === "" || pathname === prefix || pathname.startsWith(`${prefix}/`)) {
172
+ return pathname;
173
+ }
174
+ return pathname === "/" ? prefix : `${prefix}${pathname}`;
175
+ }
176
+ function getPathnameFromContext(context) {
177
+ if (context?.pathname) {
178
+ return withPathPrefix(context.pathname, context.i18n);
179
+ }
180
+ if (context?.url) {
181
+ let url = toUrl2(context.url);
182
+ return withPathPrefix(url.pathname || "/", context.i18n);
183
+ }
184
+ return "/";
185
+ }
186
+ function coerceQueryValue(value) {
187
+ if (value === "true") {
188
+ return true;
189
+ }
190
+ if (value === "false") {
191
+ return false;
192
+ }
193
+ return value;
194
+ }
195
+ function toQueryRecord(searchParams) {
196
+ let queries = {};
197
+ for (let [key, value] of searchParams.entries()) {
198
+ queries[key] = coerceQueryValue(value);
199
+ }
200
+ return queries;
201
+ }
202
+ var TRANSIENT_STUDIO_PARAMS = /* @__PURE__ */ new Set([
203
+ "weaverseDraftItem",
204
+ // Legacy name from the original design-mode draft-item spike. Keep it out of
205
+ // runtime requestInfo too, in case an older Builder bundle is in front of a
206
+ // newer Next storefront.
207
+ "__weaverseDraftItem"
208
+ ]);
209
+ function getRequestInfoSearchParams(context) {
210
+ let searchParams = new URLSearchParams(getSearchParamsFromContext(context));
211
+ for (let key of TRANSIENT_STUDIO_PARAMS) {
212
+ searchParams.delete(key);
213
+ }
214
+ return searchParams;
215
+ }
216
+ function buildWeaverseNextRequestInfo(context) {
217
+ let searchParams = getRequestInfoSearchParams(context);
218
+ let search = searchParams.toString();
219
+ return {
220
+ pathname: getPathnameFromContext(context),
221
+ search: search ? `?${search}` : "",
222
+ queries: toQueryRecord(searchParams),
223
+ ...context?.i18n ? { i18n: context.i18n } : {},
224
+ ...context?.pageType ? { pageType: context.pageType } : {},
225
+ ...context?.handle ? { handle: context.handle } : {}
226
+ };
227
+ }
228
+
87
229
  // src/server/normalize-page-url.ts
88
- var KEPT_PARAM_RE = /^(?:__revisionId|isDesignMode|weaverse)/;
230
+ var KEPT_PARAM_RE = /^(?:__revisionId|__weaverseDraftItem|isDesignMode|weaverse)/;
89
231
  var DATA_SUFFIX = ".data";
90
232
  function normalizeNextPageUrl(url) {
91
233
  let parsed;
@@ -109,12 +251,49 @@ function normalizeNextPageUrl(url) {
109
251
  }
110
252
  function resolveRequestUrl(context) {
111
253
  if (context?.url) {
112
- return typeof context.url === "string" ? context.url : context.url.toString();
254
+ let raw = String(context.url);
255
+ let url = new URL(raw, "http://localhost");
256
+ let pathname2 = withPathPrefix(url.pathname, context.i18n);
257
+ if (pathname2 === url.pathname) {
258
+ return raw;
259
+ }
260
+ url.pathname = pathname2;
261
+ return url.toString();
113
262
  }
114
- let pathname = context?.pathname || "/";
263
+ let pathname = withPathPrefix(context?.pathname || "/", context?.i18n);
115
264
  let search = context?.searchParams ? new URLSearchParams(context.searchParams).toString() : "";
116
265
  return `http://localhost${pathname}${search ? `?${search}` : ""}`;
117
266
  }
267
+ var DENIED_ROUTE_CONTEXT_PARAMS = [
268
+ "weaverseprojectid",
269
+ "weaversehost",
270
+ "weaverseapikey",
271
+ "weaverseapibase",
272
+ "weaversepublicapibase",
273
+ "weaverseversion",
274
+ "projectid",
275
+ "weaversedraftitem",
276
+ "__weaversedraftitem",
277
+ "_rsc"
278
+ ];
279
+ var ROUTE_CONTEXT_I18N_KEYS = [
280
+ "country",
281
+ "label",
282
+ "language",
283
+ "locale",
284
+ "pathPrefix"
285
+ ];
286
+ function sanitizeRouteContextSearch(search) {
287
+ let params = new URLSearchParams(search);
288
+ let denied = new Set(DENIED_ROUTE_CONTEXT_PARAMS);
289
+ for (let key of [...new Set(params.keys())]) {
290
+ if (denied.has(key.toLowerCase())) {
291
+ params.delete(key);
292
+ }
293
+ }
294
+ let serialized = params.toString();
295
+ return serialized ? `?${serialized}` : "";
296
+ }
118
297
 
119
298
  // src/utils.ts
120
299
  function generateDataFromSchema(schema) {
@@ -134,6 +313,211 @@ function generateDataFromSchema(schema) {
134
313
  return data;
135
314
  }
136
315
 
316
+ // src/server/revalidate-handler.ts
317
+ var NO_STORE_HEADERS = {
318
+ "Content-Type": "application/json",
319
+ "Cache-Control": "no-store"
320
+ };
321
+ var STATUS_OK = 200;
322
+ var STATUS_BAD_REQUEST = 400;
323
+ var STATUS_NOT_FOUND = 404;
324
+ var STATUS_UNPROCESSABLE = 422;
325
+ var STATUS_SERVER_ERROR = 500;
326
+ var MAX_PATHNAME_LENGTH = 2048;
327
+ var MAX_SEARCH_LENGTH = 8192;
328
+ var MAX_HANDLE_LENGTH = 512;
329
+ var MAX_I18N_FIELD_LENGTH = 256;
330
+ var CONTROL_CHAR_PATTERN = /[\u0000-\u001F\u007F]/;
331
+ function isValidPathname(pathname) {
332
+ if (!pathname || pathname.length > MAX_PATHNAME_LENGTH) {
333
+ return false;
334
+ }
335
+ if (pathname[0] !== "/" || pathname.startsWith("//")) {
336
+ return false;
337
+ }
338
+ if (pathname.includes("\\") || pathname.includes("?")) {
339
+ return false;
340
+ }
341
+ if (pathname.includes("#") || CONTROL_CHAR_PATTERN.test(pathname)) {
342
+ return false;
343
+ }
344
+ return true;
345
+ }
346
+ function isPlainObject(value) {
347
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
348
+ return false;
349
+ }
350
+ let proto = Object.getPrototypeOf(value);
351
+ return proto === Object.prototype || proto === null;
352
+ }
353
+ function sanitizeRouteContextI18n(input) {
354
+ if (input === void 0) {
355
+ return;
356
+ }
357
+ if (!isPlainObject(input)) {
358
+ return null;
359
+ }
360
+ let source = input;
361
+ let allowed = new Set(ROUTE_CONTEXT_I18N_KEYS);
362
+ for (let key of Object.keys(source)) {
363
+ if (!allowed.has(key)) {
364
+ return null;
365
+ }
366
+ }
367
+ let result = {};
368
+ for (let key of ROUTE_CONTEXT_I18N_KEYS) {
369
+ let value = source[key];
370
+ if (value === void 0) {
371
+ continue;
372
+ }
373
+ if (typeof value !== "string" || value.length > MAX_I18N_FIELD_LENGTH) {
374
+ return null;
375
+ }
376
+ result[key] = value;
377
+ }
378
+ return Object.keys(result).length ? result : void 0;
379
+ }
380
+ function reconstructRouteContext(request, routeContext) {
381
+ if (typeof routeContext !== "object" || routeContext === null || Array.isArray(routeContext)) {
382
+ return null;
383
+ }
384
+ let { pathname, search, handle, pageType, i18n } = routeContext;
385
+ if (typeof pathname !== "string" || !isValidPathname(pathname)) {
386
+ return null;
387
+ }
388
+ if (typeof search !== "string" || search.length > MAX_SEARCH_LENGTH) {
389
+ return null;
390
+ }
391
+ let rawSearch = search;
392
+ if (rawSearch.length && !rawSearch.startsWith("?")) {
393
+ return null;
394
+ }
395
+ let sanitizedI18n = sanitizeRouteContextI18n(i18n);
396
+ if (sanitizedI18n === null) {
397
+ return null;
398
+ }
399
+ let resolvedPageType;
400
+ if (pageType !== void 0) {
401
+ let parsed = schema.PageTypeSchema.safeParse(pageType);
402
+ if (!parsed.success) {
403
+ return null;
404
+ }
405
+ resolvedPageType = parsed.data;
406
+ }
407
+ let resolvedHandle;
408
+ if (handle !== void 0) {
409
+ if (typeof handle !== "string" || !handle || handle.length > MAX_HANDLE_LENGTH) {
410
+ return null;
411
+ }
412
+ resolvedHandle = handle;
413
+ }
414
+ let endpointUrl = new URL(request.url);
415
+ let routeUrl = new URL(endpointUrl.origin);
416
+ routeUrl.pathname = pathname;
417
+ routeUrl.search = sanitizeRouteContextSearch(rawSearch);
418
+ let canonicalPathname = routeUrl.pathname;
419
+ let searchParams = routeUrl.searchParams;
420
+ let lastParam2 = (key) => {
421
+ let values = searchParams.getAll(key);
422
+ return values.length ? values.at(-1) : void 0;
423
+ };
424
+ return {
425
+ pathname: canonicalPathname,
426
+ searchParams,
427
+ url: routeUrl,
428
+ // Headers come from the actual same-origin POST, never its JSON body.
429
+ headers: new Headers(request.headers),
430
+ ...sanitizedI18n ? { i18n: sanitizedI18n } : {},
431
+ ...resolvedPageType ? { pageType: resolvedPageType } : {},
432
+ ...resolvedHandle ? { handle: resolvedHandle } : {},
433
+ isDesignMode: lastParam2("isDesignMode") === "true",
434
+ isPreviewMode: lastParam2("isPreviewMode") === "true",
435
+ isRevisionPreview: searchParams.has("__revisionId"),
436
+ sectionType: lastParam2("sectionType") || void 0
437
+ };
438
+ }
439
+ function json(body, status) {
440
+ return new Response(JSON.stringify(body), {
441
+ status,
442
+ headers: NO_STORE_HEADERS
443
+ });
444
+ }
445
+ function parseDraftItem(payload) {
446
+ if (typeof payload !== "object" || payload === null) {
447
+ return null;
448
+ }
449
+ let draftItem = payload.draftItem;
450
+ if (typeof draftItem !== "object" || draftItem === null) {
451
+ return null;
452
+ }
453
+ let { id, type } = draftItem;
454
+ if (typeof id !== "string" || !id || typeof type !== "string" || !type) {
455
+ return null;
456
+ }
457
+ return draftItem;
458
+ }
459
+ function createWeaverseNextRevalidateHandler(config) {
460
+ return {
461
+ POST: async (request) => {
462
+ let payload;
463
+ try {
464
+ payload = await request.json();
465
+ } catch {
466
+ return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
467
+ }
468
+ let draftItem = parseDraftItem(payload);
469
+ if (!draftItem) {
470
+ return json({ error: "invalid-payload" }, STATUS_BAD_REQUEST);
471
+ }
472
+ let requestContext;
473
+ let body = payload;
474
+ if (Object.hasOwn(body, "routeContext")) {
475
+ let reconstructed = reconstructRouteContext(request, body.routeContext);
476
+ if (!reconstructed) {
477
+ return json({ error: "invalid-route-context" }, STATUS_BAD_REQUEST);
478
+ }
479
+ requestContext = reconstructed;
480
+ }
481
+ let client;
482
+ try {
483
+ client = await config.getClient(request, requestContext);
484
+ } catch (error) {
485
+ console.error("\u274C Revalidate handler getClient failed.", error);
486
+ return json({ error: "client-init-failed" }, STATUS_SERVER_ERROR);
487
+ }
488
+ let component = client.components.find(
489
+ (candidate) => candidate?.schema?.type === draftItem.type
490
+ );
491
+ if (!component) {
492
+ return json({ error: "unknown-component-type" }, STATUS_NOT_FOUND);
493
+ }
494
+ if (typeof component.loader !== "function") {
495
+ return json({ error: "missing-loader" }, STATUS_UNPROCESSABLE);
496
+ }
497
+ try {
498
+ let loaderData = await component.loader({
499
+ data: {
500
+ ...generateDataFromSchema(component.schema),
501
+ ...draftItem.data
502
+ },
503
+ weaverse: client,
504
+ context: client.requestContext,
505
+ commerce: client.commerce
506
+ });
507
+ return json({ loaderData: loaderData ?? null }, STATUS_OK);
508
+ } catch (error) {
509
+ console.error(
510
+ "\u274C Item loader revalidation failed.",
511
+ draftItem.type,
512
+ draftItem.id,
513
+ error
514
+ );
515
+ return json({ error: "loader-failed" }, STATUS_SERVER_ERROR);
516
+ }
517
+ }
518
+ };
519
+ }
520
+
137
521
  // src/loader.ts
138
522
  function isItemNode(node) {
139
523
  return typeof node === "object" && node !== null && typeof node.type === "string";
@@ -211,61 +595,11 @@ async function runWeaverseComponentLoaders(args) {
211
595
  return data;
212
596
  }
213
597
 
214
- // src/request-info.ts
215
- function toUrl2(value) {
216
- return typeof value === "string" ? new URL(value, "http://localhost") : value;
217
- }
218
- function getSearchParamsFromContext(context) {
219
- if (context?.searchParams) {
220
- return new URLSearchParams(context.searchParams);
221
- }
222
- if (context?.url) {
223
- let url = toUrl2(context.url);
224
- return new URLSearchParams(url.searchParams);
225
- }
226
- return new URLSearchParams();
227
- }
228
- function getPathnameFromContext(context) {
229
- if (context?.pathname) {
230
- return context.pathname;
231
- }
232
- if (context?.url) {
233
- let url = toUrl2(context.url);
234
- return url.pathname || "/";
235
- }
236
- return "/";
237
- }
238
- function coerceQueryValue(value) {
239
- if (value === "true") {
240
- return true;
241
- }
242
- if (value === "false") {
243
- return false;
244
- }
245
- return value;
246
- }
247
- function toQueryRecord(searchParams) {
248
- let queries = {};
249
- for (let [key, value] of searchParams.entries()) {
250
- queries[key] = coerceQueryValue(value);
251
- }
252
- return queries;
253
- }
254
- function buildWeaverseNextRequestInfo(context) {
255
- let searchParams = getSearchParamsFromContext(context);
256
- let search = searchParams.toString();
257
- return {
258
- pathname: getPathnameFromContext(context),
259
- search: search ? `?${search}` : "",
260
- queries: toQueryRecord(searchParams),
261
- ...context?.i18n ? { i18n: context.i18n } : {}
262
- };
263
- }
264
-
265
598
  // src/server/server-client.ts
266
599
  var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
267
600
  var RANDOM_ID_RADIX = 36;
268
601
  var RANDOM_ID_SLICE_START = 2;
602
+ var MAX_CUSTOM_PAGE_PAGES = 100;
269
603
  var JSON_HEADERS = {
270
604
  "Content-Type": "application/json",
271
605
  Accept: "application/json",
@@ -280,6 +614,25 @@ function generateUuid() {
280
614
  }
281
615
  return `id-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
282
616
  }
617
+ function normalizeWeaverseNextPageAssignment(input) {
618
+ if (!input || typeof input !== "object") {
619
+ return;
620
+ }
621
+ let { projectId, type, handle, locale, meta } = input;
622
+ if (typeof projectId !== "string" || typeof type !== "string" || typeof handle !== "string") {
623
+ return;
624
+ }
625
+ let normalized = {
626
+ projectId,
627
+ type,
628
+ handle,
629
+ locale: typeof locale === "string" ? locale : ""
630
+ };
631
+ if (meta && typeof meta === "object") {
632
+ normalized.meta = meta;
633
+ }
634
+ return normalized;
635
+ }
283
636
  var NextServerClient = class {
284
637
  components;
285
638
  commerce;
@@ -399,6 +752,59 @@ var NextServerClient = class {
399
752
  clearTimeout(timeoutId);
400
753
  }
401
754
  };
755
+ _buildCustomPagesUrl(projectId, options, cursor) {
756
+ let params = new URLSearchParams();
757
+ if (options.locale) {
758
+ params.set("locale", options.locale);
759
+ }
760
+ if (options.limit) {
761
+ params.set("limit", String(options.limit));
762
+ }
763
+ if (cursor) {
764
+ params.set("cursor", cursor);
765
+ }
766
+ let qs = params.toString();
767
+ return `${this._baseConfigs.weaverseApiBase}/api/public/v1/projects/${projectId}/custom-pages${qs ? `?${qs}` : ""}`;
768
+ }
769
+ /**
770
+ * Fetch all published custom pages for sitemap generation. Mirrors Hydrogen's
771
+ * helper but uses Next's fetch cache knobs (`revalidate` / `tags`). It returns
772
+ * accumulated results on partial pagination failure so a single API hiccup
773
+ * does not break sitemap generation entirely.
774
+ */
775
+ fetchCustomPages = async (options = {}) => {
776
+ let projectId = await this.resolveProjectId();
777
+ let entries = [];
778
+ if (!projectId) {
779
+ console.warn(
780
+ "[WeaverseNext] Failed to fetch custom pages: missing projectId"
781
+ );
782
+ return entries;
783
+ }
784
+ let cursor = null;
785
+ for (let page = 0; page < MAX_CUSTOM_PAGE_PAGES; page += 1) {
786
+ let url = this._buildCustomPagesUrl(projectId, options, cursor);
787
+ try {
788
+ let result = await this.fetchWithCache(url, {
789
+ revalidate: options.revalidate,
790
+ tags: options.tags
791
+ });
792
+ entries.push(...result.data ?? []);
793
+ if (!result.nextCursor) {
794
+ return entries;
795
+ }
796
+ cursor = result.nextCursor;
797
+ } catch (error) {
798
+ let message = error instanceof Error ? error.message : String(error);
799
+ console.warn(
800
+ entries.length === 0 ? `[WeaverseNext] Failed to fetch custom pages: ${message}` : `[WeaverseNext] Custom pages pagination interrupted: ${message}`
801
+ );
802
+ return entries;
803
+ }
804
+ }
805
+ console.warn("[WeaverseNext] Custom pages pagination cap reached");
806
+ return entries;
807
+ };
402
808
  _generateFallbackPage(message) {
403
809
  let rootId = generateUuid();
404
810
  return {
@@ -526,7 +932,9 @@ var NextServerClient = class {
526
932
  this._assertNoPageError(payload);
527
933
  let page = payload.page;
528
934
  let project = payload.project;
529
- let pageAssignment = payload.pageAssignment;
935
+ let pageAssignment = normalizeWeaverseNextPageAssignment(
936
+ payload.pageAssignment
937
+ );
530
938
  page ??= this._generateFallbackPage(
531
939
  '<div style="text-align: center;">Please add new section to start.</div>'
532
940
  );
@@ -582,12 +990,58 @@ var NextServerClient = class {
582
990
  return null;
583
991
  }
584
992
  };
993
+ /**
994
+ * Resolve the request locale (e.g. `fr-ca`) used for merchant overrides.
995
+ * Unlike Hydrogen — which defaults to `en`/`us` because a Hydrogen
996
+ * storefront always has an i18n context — Next apps may not thread one
997
+ * through, so a missing language or country means "no explicit locale" and
998
+ * the translation fetch is skipped rather than guessed.
999
+ */
1000
+ _resolveOverridesLocale() {
1001
+ let i18n = this.requestContext?.i18n;
1002
+ let language = typeof i18n?.language === "string" ? i18n.language.trim() : "";
1003
+ let country = typeof i18n?.country === "string" ? i18n.country.trim() : "";
1004
+ if (!(language && country)) {
1005
+ return;
1006
+ }
1007
+ return `${language.toLowerCase()}-${country.toLowerCase()}`;
1008
+ }
1009
+ /**
1010
+ * Fetch locale-specific static-text overrides from the translation API.
1011
+ * Ported from Hydrogen's `fetchMerchantOverrides`: skipped entirely when the
1012
+ * theme has no `i18n` schema (nothing translatable) or when the request has
1013
+ * no explicit locale, and never allowed to fail theme settings.
1014
+ */
1015
+ async _fetchMerchantOverrides(projectId, options) {
1016
+ let { weaverseHost } = this._baseConfigs;
1017
+ if (!(asThemeSchema(this.themeSchema)?.i18n && projectId && weaverseHost)) {
1018
+ return;
1019
+ }
1020
+ let locale = this._resolveOverridesLocale();
1021
+ if (!locale) {
1022
+ return;
1023
+ }
1024
+ let url = `${weaverseHost}/api/translation/static?projectId=${projectId}&locale=${locale}`;
1025
+ try {
1026
+ let overrides = await this.fetchWithCache(url, {
1027
+ method: "GET",
1028
+ headers: { Accept: "application/json" },
1029
+ revalidate: options.revalidate,
1030
+ tags: options.tags
1031
+ });
1032
+ return typeof overrides === "object" && overrides !== null ? overrides : void 0;
1033
+ } catch (error) {
1034
+ console.warn(
1035
+ "[WeaverseNext] Unable to load merchant overrides, using theme defaults:",
1036
+ error instanceof Error ? error.message : String(error)
1037
+ );
1038
+ return;
1039
+ }
1040
+ }
585
1041
  loadThemeSettings = async (optionsInput = {}) => {
586
1042
  let options = optionsInput;
587
1043
  let themeSchema = asThemeSchema(this.themeSchema);
588
- let defaultThemeSettings = generateDataFromSchema(
589
- this.themeSchema
590
- );
1044
+ let defaultThemeSettings = generateDataFromSchema(this.themeSchema);
591
1045
  let staticContent = themeSchema?.i18n?.staticContent;
592
1046
  try {
593
1047
  let projectId = await this.resolveProjectId();
@@ -596,16 +1050,16 @@ var NextServerClient = class {
596
1050
  }
597
1051
  let { isDesignMode } = this._baseConfigs;
598
1052
  let url = `${this._baseConfigs.weaverseApiBase}/api/public/project_configs`;
599
- let result = await this.fetchWithCache(
600
- url,
601
- {
1053
+ let [result, merchantOverrides] = await Promise.all([
1054
+ this.fetchWithCache(url, {
602
1055
  method: "POST",
603
1056
  body: JSON.stringify({ projectId, isDesignMode }),
604
1057
  headers: JSON_HEADERS,
605
1058
  revalidate: options.revalidate,
606
1059
  tags: options.tags
607
- }
608
- );
1060
+ }),
1061
+ this._fetchMerchantOverrides(projectId, options)
1062
+ ]);
609
1063
  if (typeof result !== "object" || result === null) {
610
1064
  result = { theme: defaultThemeSettings };
611
1065
  }
@@ -619,21 +1073,25 @@ var NextServerClient = class {
619
1073
  if (staticContent) {
620
1074
  result.staticContent = staticContent;
621
1075
  }
1076
+ if (merchantOverrides) {
1077
+ result.merchantOverrides = merchantOverrides;
1078
+ }
622
1079
  if (isDesignMode) {
1080
+ let serializableSchema = themeSchema ? {
1081
+ ...themeSchema,
1082
+ settings: themeSchema.settings?.map((group) => ({
1083
+ ...group,
1084
+ inputs: group?.inputs?.map(
1085
+ (inputItem) => typeof inputItem?.condition === "function" ? {
1086
+ ...inputItem,
1087
+ condition: inputItem.condition.toString()
1088
+ } : inputItem
1089
+ )
1090
+ }))
1091
+ } : void 0;
623
1092
  result = {
624
1093
  ...result,
625
- schema: themeSchema ? {
626
- ...themeSchema,
627
- settings: themeSchema.settings?.map((group) => ({
628
- ...group,
629
- inputs: group?.inputs?.map(
630
- (inputItem) => typeof inputItem?.condition === "function" ? {
631
- ...inputItem,
632
- condition: inputItem.condition.toString()
633
- } : inputItem
634
- )
635
- }))
636
- } : void 0,
1094
+ schema: serializableSchema,
637
1095
  publicEnv: this._baseConfigs.publicEnv
638
1096
  };
639
1097
  }
@@ -654,8 +1112,11 @@ function createWeaverseNextServerClient(config) {
654
1112
  return new NextServerClient(config);
655
1113
  }
656
1114
 
1115
+ exports.createWeaverseNextRevalidateHandler = createWeaverseNextRevalidateHandler;
657
1116
  exports.createWeaverseNextServerClient = createWeaverseNextServerClient;
1117
+ exports.formatWeaverseNextSeoMetadata = formatWeaverseNextSeoMetadata;
658
1118
  exports.getWeaverseNextConfigs = getWeaverseNextConfigs;
1119
+ exports.getWeaverseNextSeoMetadata = getWeaverseNextSeoMetadata;
659
1120
  exports.normalizeNextPageUrl = normalizeNextPageUrl;
660
1121
  exports.resolveRequestUrl = resolveRequestUrl;
661
1122
  //# sourceMappingURL=server.js.map