@weaverse/next 0.1.0-alpha.9 → 1.0.1

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 (116) hide show
  1. package/README.md +161 -73
  2. package/dist/{chunk-6PMZ77RA.mjs → chunk-OSC5HR5V.mjs} +117 -8
  3. package/dist/chunk-OSC5HR5V.mjs.map +1 -0
  4. package/dist/client-boundary.js +1425 -0
  5. package/dist/client-boundary.js.map +1 -0
  6. package/dist/client-boundary.mjs +1395 -0
  7. package/dist/client-boundary.mjs.map +1 -0
  8. package/dist/index.js +292 -1003
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +90 -966
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/server.js +321 -91
  13. package/dist/server.js.map +1 -1
  14. package/dist/server.mjs +205 -28
  15. package/dist/server.mjs.map +1 -1
  16. package/dist/types/client-boundary.d.ts +25 -0
  17. package/dist/types/client-boundary.d.ts.map +1 -0
  18. package/dist/types/client.d.ts +16 -0
  19. package/dist/types/client.d.ts.map +1 -0
  20. package/dist/types/hooks.d.ts +20 -0
  21. package/dist/types/hooks.d.ts.map +1 -0
  22. package/dist/types/index.d.ts +33 -0
  23. package/dist/types/index.d.ts.map +1 -0
  24. package/dist/types/item.d.ts +60 -0
  25. package/dist/types/item.d.ts.map +1 -0
  26. package/dist/types/loader.d.ts +25 -0
  27. package/dist/types/loader.d.ts.map +1 -0
  28. package/dist/types/main.d.ts +11 -0
  29. package/dist/types/main.d.ts.map +1 -0
  30. package/dist/types/provider.d.ts +82 -0
  31. package/dist/types/provider.d.ts.map +1 -0
  32. package/dist/types/registry.d.ts +15 -0
  33. package/dist/types/registry.d.ts.map +1 -0
  34. package/dist/types/renderer.d.ts +21 -0
  35. package/dist/types/renderer.d.ts.map +1 -0
  36. package/dist/types/request-info.d.ts +19 -0
  37. package/dist/types/request-info.d.ts.map +1 -0
  38. package/dist/types/revalidate-item.d.ts +82 -0
  39. package/dist/types/revalidate-item.d.ts.map +1 -0
  40. package/dist/types/root-provider.d.ts +62 -0
  41. package/dist/types/root-provider.d.ts.map +1 -0
  42. package/dist/types/runtime.d.ts +113 -0
  43. package/dist/types/runtime.d.ts.map +1 -0
  44. package/dist/types/seo.d.ts +55 -0
  45. package/dist/types/seo.d.ts.map +1 -0
  46. package/dist/types/server/configs.d.ts +27 -0
  47. package/dist/types/server/configs.d.ts.map +1 -0
  48. package/dist/types/server/normalize-page-url.d.ts +19 -0
  49. package/dist/types/server/normalize-page-url.d.ts.map +1 -0
  50. package/dist/types/server/revalidate-handler.d.ts +55 -0
  51. package/dist/types/server/revalidate-handler.d.ts.map +1 -0
  52. package/dist/types/server/server-client.d.ts +21 -0
  53. package/dist/types/server/server-client.d.ts.map +1 -0
  54. package/dist/types/server.d.ts +19 -0
  55. package/dist/types/server.d.ts.map +1 -0
  56. package/dist/types/studio-bridge.d.ts +16 -0
  57. package/dist/types/studio-bridge.d.ts.map +1 -0
  58. package/dist/types/studio-connect.d.ts +27 -0
  59. package/dist/types/studio-connect.d.ts.map +1 -0
  60. package/dist/types/studio-router.d.ts +50 -0
  61. package/dist/types/studio-router.d.ts.map +1 -0
  62. package/dist/types/studio-script-src.d.ts +12 -0
  63. package/dist/types/studio-script-src.d.ts.map +1 -0
  64. package/dist/types/theme-settings-store.d.ts +18 -0
  65. package/dist/types/theme-settings-store.d.ts.map +1 -0
  66. package/dist/types/translate.d.ts +56 -0
  67. package/dist/types/translate.d.ts.map +1 -0
  68. package/dist/types/translation-context.d.ts +109 -0
  69. package/dist/types/translation-context.d.ts.map +1 -0
  70. package/dist/types/translation-store.d.ts +44 -0
  71. package/dist/types/translation-store.d.ts.map +1 -0
  72. package/dist/types/types.d.ts +665 -0
  73. package/dist/types/types.d.ts.map +1 -0
  74. package/dist/types/use-weaverse-next-studio.d.ts +34 -0
  75. package/dist/types/use-weaverse-next-studio.d.ts.map +1 -0
  76. package/dist/types/utils.d.ts +22 -0
  77. package/dist/types/utils.d.ts.map +1 -0
  78. package/package.json +12 -27
  79. package/src/client-boundary.ts +50 -0
  80. package/src/client.ts +102 -0
  81. package/src/hooks.ts +50 -0
  82. package/src/index.ts +143 -0
  83. package/src/item.ts +167 -0
  84. package/src/loader.ts +135 -0
  85. package/src/main.tsx +45 -0
  86. package/src/provider.tsx +223 -0
  87. package/src/registry.ts +63 -0
  88. package/src/renderer.tsx +100 -0
  89. package/src/request-info.ts +124 -0
  90. package/src/revalidate-item.ts +211 -0
  91. package/src/root-provider.tsx +180 -0
  92. package/src/runtime.ts +555 -0
  93. package/src/seo.ts +157 -0
  94. package/src/server/configs.ts +178 -0
  95. package/src/server/normalize-page-url.ts +73 -0
  96. package/src/server/revalidate-handler.ts +357 -0
  97. package/src/server/server-client.ts +728 -0
  98. package/src/server.ts +67 -0
  99. package/src/studio-bridge.tsx +82 -0
  100. package/src/studio-connect.tsx +88 -0
  101. package/src/studio-router.ts +85 -0
  102. package/src/studio-script-src.ts +128 -0
  103. package/src/theme-settings-store.ts +47 -0
  104. package/src/translate.ts +125 -0
  105. package/src/translation-context.tsx +182 -0
  106. package/src/translation-store.ts +69 -0
  107. package/src/types.ts +753 -0
  108. package/src/use-weaverse-next-studio.tsx +106 -0
  109. package/src/utils.ts +49 -0
  110. package/dist/chunk-6PMZ77RA.mjs.map +0 -1
  111. package/dist/index.d.mts +0 -580
  112. package/dist/index.d.ts +0 -580
  113. package/dist/seo-Dos9t22_.d.mts +0 -477
  114. package/dist/seo-Dos9t22_.d.ts +0 -477
  115. package/dist/server.d.mts +0 -99
  116. package/dist/server.d.ts +0 -99
package/dist/index.js CHANGED
@@ -1,10 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var react = require('@weaverse/react');
4
3
  var schema = require('@weaverse/schema');
5
- var jsxRuntime = require('react/jsx-runtime');
6
- var react$1 = require('react');
7
- var navigation = require('next/navigation');
4
+ var clientBoundary_js = require('./client-boundary.js');
8
5
 
9
6
  // src/index.ts
10
7
 
@@ -25,490 +22,8 @@ function generateDataFromSchema(schema) {
25
22
  }
26
23
  return data;
27
24
  }
28
-
29
- // src/item.ts
30
- function flattenItemData(update) {
31
- let { data, ...rest } = update;
32
- if (!(data && typeof data === "object" && !Array.isArray(data))) {
33
- return rest;
34
- }
35
- return { ...data, ...rest };
36
- }
37
- var deferredItemUpdates = null;
38
- function collectDeferredItemUpdates(create) {
39
- let previousQueue = deferredItemUpdates;
40
- let queue = /* @__PURE__ */ new Set();
41
- deferredItemUpdates = queue;
42
- try {
43
- return [create(), [...queue]];
44
- } finally {
45
- deferredItemUpdates = previousQueue;
46
- }
47
- }
48
- var WeaverseNextItem = class extends react.WeaverseItemStore {
49
- /**
50
- * Cached merged snapshot (base `_store` + translations). Reset whenever the
51
- * `_store` ref or the item's translation entry ref changes, so
52
- * `useSyncExternalStore` sees a stable object between unrelated re-renders.
53
- */
54
- _cachedSnapshot = null;
55
- _snapshotStoreRef = null;
56
- _snapshotTranslationRef = null;
57
- constructor(initialData, weaverse) {
58
- super(initialData, weaverse);
59
- let schemaData = generateDataFromSchema(this.Element?.schema);
60
- Object.assign(this._store, schemaData, flattenItemData(initialData));
61
- }
62
- /**
63
- * Flatten nested `data` on updates exactly like the constructor does. Core's
64
- * `initProject()` calls `setData(item)` when a new runtime serves a page
65
- * whose item instances already exist (e.g. locale/client navigation), and
66
- * the inherited shallow merge would leave stale top-level props behind.
67
- * Assigning through the inherited `data` setter swaps the `_store` ref,
68
- * which invalidates the memoized snapshot below. The subscriber emit is
69
- * queued instead of fired when a render-phase deferral is active (see
70
- * `collectDeferredItemUpdates`).
71
- */
72
- setData = (update) => {
73
- this.data = flattenItemData(update);
74
- if (deferredItemUpdates) {
75
- deferredItemUpdates.add(this);
76
- } else {
77
- this.triggerUpdate();
78
- }
79
- return this.data;
80
- };
81
- /**
82
- * Overlay item-level translations onto the base store when the runtime is in
83
- * translation mode. Mirrors Hydrogen's `WeaverseHydrogenItem.getSnapShot()`:
84
- * memoized by `_store` ref + translation entry ref so the returned object is
85
- * referentially stable, and a fast path that returns the base store untouched
86
- * when the item has no translations.
87
- */
88
- getSnapShot = () => {
89
- let base = this._store;
90
- let translations = this.weaverse.translationMap[this._id];
91
- if (!translations) {
92
- return base;
93
- }
94
- if (this._cachedSnapshot && this._snapshotStoreRef === base && this._snapshotTranslationRef === translations) {
95
- return this._cachedSnapshot;
96
- }
97
- let merged = { ...base };
98
- for (let [key, entry] of Object.entries(translations)) {
99
- if (entry.translatedValue !== void 0) {
100
- merged[key] = entry.translatedValue;
101
- }
102
- }
103
- this._cachedSnapshot = merged;
104
- this._snapshotStoreRef = base;
105
- this._snapshotTranslationRef = translations;
106
- return merged;
107
- };
108
- };
109
- function ensureNextItemConstructor() {
110
- if (!react.Weaverse.ItemConstructor || react.Weaverse.ItemConstructor === react.WeaverseItemStore || react.Weaverse.ItemConstructor === WeaverseNextItem) {
111
- react.Weaverse.ItemConstructor = WeaverseNextItem;
112
- }
113
- }
114
- function Main(props) {
115
- let { children, loaderData, ...rest } = props;
116
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ...rest, children });
117
- }
118
- Main.displayName = "main";
119
- var mainSchema = schema.createSchema({
120
- type: "main",
121
- title: "Main"
122
- });
123
- var defaultMainComponent = {
124
- default: Main,
125
- schema: mainSchema
126
- };
127
-
128
- // src/registry.ts
129
- var MAIN_TYPE = "main";
130
- var registeredComponentTypes = /* @__PURE__ */ new Set();
131
- function registerWeaverseNextComponents(components) {
132
- ensureNextItemConstructor();
133
- let hasUserMain = components.some(
134
- (component) => component.schema?.type === MAIN_TYPE
135
- );
136
- let effectiveComponents = hasUserMain ? components : [defaultMainComponent, ...components];
137
- for (let component of effectiveComponents) {
138
- let type = component.schema.type;
139
- let element = {
140
- type,
141
- Component: component.default,
142
- schema: component.schema,
143
- loader: component.loader
144
- };
145
- if (type === MAIN_TYPE && hasUserMain) {
146
- react.Weaverse.elementRegistry.set(type, element);
147
- registeredComponentTypes.add(type);
148
- continue;
149
- }
150
- if (type && !registeredComponentTypes.has(type)) {
151
- react.Weaverse.registerElement(element);
152
- registeredComponentTypes.add(type);
153
- }
154
- }
155
- }
156
-
157
- // src/client.ts
158
- var NextClient = class {
159
- projectId;
160
- components;
161
- themeSchema;
162
- themeSettings;
163
- requestContext;
164
- commerce;
165
- data = null;
166
- dataContext = {};
167
- _fetchPage;
168
- _fetchThemeSettings;
169
- constructor(config) {
170
- this.projectId = config.projectId;
171
- this.components = config.components;
172
- registerWeaverseNextComponents(this.components);
173
- this.themeSchema = config.themeSchema;
174
- this.themeSettings = {
175
- ...generateDataFromSchema(config.themeSchema),
176
- ...config.themeSettings
177
- };
178
- this.requestContext = config.requestContext;
179
- this.commerce = config.commerce;
180
- this._fetchPage = config.fetchPage;
181
- this._fetchThemeSettings = config.fetchThemeSettings;
182
- }
183
- /** Compatibility alias so loaders can call `weaverse.storefront.query(...)`. */
184
- get storefront() {
185
- return this.commerce?.storefront;
186
- }
187
- loadPage = (input = {}) => {
188
- if (!this._fetchPage) {
189
- throw new Error(
190
- "[WeaverseNextClient] loadPage requires a `fetchPage` function in client config."
191
- );
192
- }
193
- return this._fetchPage(input);
194
- };
195
- loadThemeSettings = (context) => {
196
- if (!this._fetchThemeSettings) {
197
- throw new Error(
198
- "[WeaverseNextClient] loadThemeSettings requires a `fetchThemeSettings` function in client config."
199
- );
200
- }
201
- return this._fetchThemeSettings(context ?? this.requestContext);
202
- };
203
- };
204
- function createWeaverseNextClient(config) {
205
- return new NextClient(config);
206
- }
207
-
208
- // src/theme-settings-store.ts
209
- function createWeaverseNextThemeSettingsStore(options = {}) {
210
- let listeners = /* @__PURE__ */ new Set();
211
- let store = {
212
- publicEnv: options.publicEnv,
213
- schema: options.schema,
214
- settings: { ...options.settings ?? {} },
215
- getServerSnapshot: () => store.settings,
216
- getSnapshot: () => store.settings,
217
- subscribe: (listener) => {
218
- listeners.add(listener);
219
- return () => {
220
- listeners.delete(listener);
221
- };
222
- },
223
- updateThemeSettings: (next) => {
224
- store.settings = { ...store.settings, ...next };
225
- for (let listener of listeners) {
226
- listener();
227
- }
228
- }
229
- };
230
- return store;
231
- }
232
- function getNestedKey(obj, path, fallback) {
233
- let keys = path.split(".");
234
- let current = obj;
235
- for (let key of keys) {
236
- if (current == null || typeof current !== "object") {
237
- return fallback;
238
- }
239
- current = current[key];
240
- }
241
- if (typeof current === "string") {
242
- return current;
243
- }
244
- return fallback;
245
- }
246
- function interpolate(template, variables) {
247
- if (!variables) {
248
- return template;
249
- }
250
- return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
251
- let value = variables[key];
252
- return value === void 0 ? match : String(value);
253
- });
254
- }
255
- var TranslationContext = react$1.createContext(null);
256
- TranslationContext.displayName = "WeaverseNextTranslationContext";
257
- function createTranslate(sources) {
258
- let { staticContent, merchantOverrides, designOverrides, externalT } = sources;
259
- return (key, variables) => {
260
- if (externalT) {
261
- let external = externalT(key, variables);
262
- if (external && external !== key) {
263
- return external;
264
- }
265
- }
266
- let design = designOverrides && Object.hasOwn(designOverrides, key) ? designOverrides[key] : void 0;
267
- let override = merchantOverrides ? getNestedKey(merchantOverrides, key) : void 0;
268
- let staticValue = getNestedKey(staticContent, key);
269
- let raw = design ?? override ?? staticValue ?? key;
270
- return interpolate(raw, variables);
271
- };
272
- }
273
- var EMPTY = {};
274
- var NOOP_SUBSCRIBE = () => () => {
275
- };
276
- var NOOP_SNAPSHOT = () => EMPTY;
277
- function TranslationProvider({
278
- staticContent,
279
- merchantOverrides,
280
- t: externalT,
281
- translationStore,
282
- themeTextStore,
283
- children
284
- }) {
285
- let store = translationStore ?? themeTextStore ?? null;
286
- let designOverrides = react.useSafeExternalStore(
287
- store?.subscribe ?? NOOP_SUBSCRIBE,
288
- store?.getSnapshot ?? NOOP_SNAPSHOT,
289
- store?.getServerSnapshot ?? NOOP_SNAPSHOT
290
- );
291
- let value = react$1.useMemo(
292
- () => ({
293
- t: createTranslate({
294
- staticContent,
295
- merchantOverrides,
296
- designOverrides,
297
- externalT
298
- }),
299
- translationStore: store,
300
- // Deprecated alias — same instance, kept for backward compatibility.
301
- themeTextStore: store,
302
- merchantOverrides: merchantOverrides ?? null
303
- }),
304
- [staticContent, merchantOverrides, designOverrides, externalT, store]
305
- );
306
- return /* @__PURE__ */ jsxRuntime.jsx(TranslationContext.Provider, { value, children });
307
- }
308
- function useTranslation() {
309
- let ctx = react$1.useContext(TranslationContext);
310
- if (!ctx) {
311
- throw new Error(
312
- "useTranslation must be used within <TranslationProvider>. Wrap your app with <WeaverseNextRootProvider>."
313
- );
314
- }
315
- return ctx;
316
- }
317
- var ThemeTextProvider = TranslationProvider;
318
- var useThemeText = useTranslation;
319
-
320
- // src/translation-store.ts
321
- var TranslationStore = class {
322
- overrides = {};
323
- listeners = /* @__PURE__ */ new Set();
324
- setOverrides = (overrides) => {
325
- this.overrides = { ...overrides };
326
- this.emit();
327
- };
328
- updateOverrides = (newOverrides) => {
329
- this.overrides = { ...this.overrides, ...newOverrides };
330
- this.emit();
331
- };
332
- getSnapshot = () => this.overrides;
333
- getServerSnapshot = () => this.overrides;
334
- subscribe = (callback) => {
335
- this.listeners.add(callback);
336
- return () => {
337
- this.listeners.delete(callback);
338
- };
339
- };
340
- emit = () => {
341
- for (const listener of Array.from(this.listeners)) {
342
- try {
343
- listener();
344
- } catch (error) {
345
- console.error("TranslationStore: Listener error", error);
346
- }
347
- }
348
- };
349
- };
350
- var ThemeTextStore = TranslationStore;
351
- var WeaverseNextRootContext = react$1.createContext(null);
352
- function WeaverseNextRootProvider(props) {
353
- let {
354
- children,
355
- initialThemeSettings,
356
- merchantOverrides,
357
- staticContent,
358
- t,
359
- themeSchema,
360
- publicEnv
361
- } = props;
362
- let storeRef = react$1.useRef(null);
363
- if (!storeRef.current) {
364
- storeRef.current = createWeaverseNextThemeSettingsStore({
365
- publicEnv,
366
- schema: themeSchema,
367
- settings: initialThemeSettings
368
- });
369
- }
370
- let themeSettingsStore = storeRef.current;
371
- let translationStoreRef = react$1.useRef(null);
372
- if (!translationStoreRef.current) {
373
- translationStoreRef.current = new TranslationStore();
374
- }
375
- let translationStore = translationStoreRef.current;
376
- let appliedThemeSettingsRef = react$1.useRef(initialThemeSettings);
377
- react$1.useEffect(() => {
378
- if (initialThemeSettings && appliedThemeSettingsRef.current !== initialThemeSettings) {
379
- themeSettingsStore.updateThemeSettings(initialThemeSettings);
380
- appliedThemeSettingsRef.current = initialThemeSettings;
381
- }
382
- }, [initialThemeSettings, themeSettingsStore]);
383
- let resolvedStaticContent = staticContent ?? getSchemaStaticContent(themeSchema);
384
- let rootContextValue = react$1.useMemo(
385
- () => ({
386
- merchantOverrides,
387
- staticContent: resolvedStaticContent,
388
- themeSettingsStore,
389
- translationStore
390
- }),
391
- [
392
- merchantOverrides,
393
- resolvedStaticContent,
394
- themeSettingsStore,
395
- translationStore
396
- ]
397
- );
398
- let contextValue = react$1.useMemo(
399
- () => ({
400
- merchantOverrides,
401
- themeSettings: themeSettingsStore.getSnapshot(),
402
- themeSettingsStore,
403
- translationStore
404
- }),
405
- [merchantOverrides, themeSettingsStore, translationStore]
406
- );
407
- return /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextRootContext.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
408
- TranslationProvider,
409
- {
410
- merchantOverrides,
411
- staticContent: resolvedStaticContent,
412
- t,
413
- translationStore,
414
- children
415
- }
416
- ) }) });
417
- }
418
- var EMPTY_THEME_SETTINGS = {};
419
- var EMPTY_STATIC_CONTENT = {};
420
- function getSchemaStaticContent(schema) {
421
- let i18n = schema?.i18n;
422
- return i18n?.staticContent ?? EMPTY_STATIC_CONTENT;
423
- }
424
- var WeaverseNextContext = react$1.createContext(null);
425
- function WeaverseNextProvider(props) {
426
- let { children, client, rootData, pageData, commerce, themeSettings } = props;
427
- let rootContext = react$1.useContext(WeaverseNextRootContext);
428
- let themeSettingsValue = themeSettings ?? client?.themeSettings ?? EMPTY_THEME_SETTINGS;
429
- let ownTranslationStoreRef = react$1.useRef(null);
430
- if (!(rootContext || ownTranslationStoreRef.current)) {
431
- ownTranslationStoreRef.current = new TranslationStore();
432
- }
433
- let translationStore = rootContext?.translationStore ?? ownTranslationStoreRef.current;
434
- let staticContent = props.staticContent ?? rootContext?.staticContent ?? getSchemaStaticContent(client?.themeSchema);
435
- let merchantOverrides = props.merchantOverrides ?? rootContext?.merchantOverrides;
436
- let ownThemeSettingsStore = react$1.useMemo(() => {
437
- if (rootContext) {
438
- return null;
439
- }
440
- return createWeaverseNextThemeSettingsStore({
441
- schema: client?.themeSchema,
442
- settings: themeSettingsValue
443
- });
444
- }, [rootContext, client?.themeSchema, themeSettingsValue]);
445
- let themeSettingsStore = rootContext?.themeSettingsStore ?? ownThemeSettingsStore;
446
- let mergedIntoRootRef = react$1.useRef(void 0);
447
- react$1.useEffect(() => {
448
- if (rootContext && themeSettingsValue !== EMPTY_THEME_SETTINGS && mergedIntoRootRef.current !== themeSettingsValue) {
449
- rootContext.themeSettingsStore.updateThemeSettings(themeSettingsValue);
450
- mergedIntoRootRef.current = themeSettingsValue;
451
- }
452
- }, [rootContext, themeSettingsValue]);
453
- let value = react$1.useMemo(
454
- () => ({
455
- client,
456
- rootData,
457
- pageData,
458
- commerce: commerce ?? client?.commerce,
459
- merchantOverrides,
460
- themeSettings: themeSettingsStore.getSnapshot(),
461
- themeSettingsStore,
462
- translationStore
463
- }),
464
- [
465
- client,
466
- rootData,
467
- pageData,
468
- commerce,
469
- merchantOverrides,
470
- themeSettingsStore,
471
- translationStore
472
- ]
473
- );
474
- return /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(
475
- TranslationProvider,
476
- {
477
- merchantOverrides,
478
- staticContent,
479
- t: props.t,
480
- translationStore,
481
- children
482
- }
483
- ) });
484
- }
485
-
486
- // src/hooks.ts
487
- var OUTSIDE_PROVIDER_ERROR = "must be used inside a <WeaverseNextProvider>.";
488
- function useWeaverseNextContext(hookName) {
489
- let context = react$1.useContext(WeaverseNextContext);
490
- if (!context) {
491
- throw new Error(`[@weaverse/next] ${hookName} ${OUTSIDE_PROVIDER_ERROR}`);
492
- }
493
- return context;
494
- }
495
- function useWeaverseRootData() {
496
- return useWeaverseNextContext("useWeaverseRootData").rootData;
497
- }
498
- function useWeaversePageData() {
499
- return useWeaverseNextContext("useWeaversePageData").pageData;
500
- }
501
- function useWeaverseCommerce() {
502
- return useWeaverseNextContext("useWeaverseCommerce").commerce;
503
- }
504
- function useThemeSettings() {
505
- let { themeSettingsStore } = useWeaverseNextContext("useThemeSettings");
506
- return react$1.useSyncExternalStore(
507
- themeSettingsStore.subscribe,
508
- themeSettingsStore.getSnapshot,
509
- themeSettingsStore.getServerSnapshot
510
- );
511
- }
25
+ var isBrowser = typeof window !== "undefined";
26
+ var isIframe = isBrowser && window.top !== window.self;
512
27
 
513
28
  // src/loader.ts
514
29
  function isItemNode(node) {
@@ -601,13 +116,22 @@ function getSearchParamsFromContext(context) {
601
116
  }
602
117
  return new URLSearchParams();
603
118
  }
119
+ function withPathPrefix(pathname, i18n) {
120
+ let raw = typeof i18n?.pathPrefix === "string" ? i18n.pathPrefix : "";
121
+ let segments = raw.split("/").filter(Boolean);
122
+ let prefix = segments.length > 0 ? `/${segments.join("/")}` : "";
123
+ if (prefix === "" || pathname === prefix || pathname.startsWith(`${prefix}/`)) {
124
+ return pathname;
125
+ }
126
+ return pathname === "/" ? prefix : `${prefix}${pathname}`;
127
+ }
604
128
  function getPathnameFromContext(context) {
605
129
  if (context?.pathname) {
606
- return context.pathname;
130
+ return withPathPrefix(context.pathname, context.i18n);
607
131
  }
608
132
  if (context?.url) {
609
133
  let url = toUrl(context.url);
610
- return url.pathname || "/";
134
+ return withPathPrefix(url.pathname || "/", context.i18n);
611
135
  }
612
136
  return "/";
613
137
  }
@@ -648,311 +172,72 @@ function buildWeaverseNextRequestInfo(context) {
648
172
  pathname: getPathnameFromContext(context),
649
173
  search: search ? `?${search}` : "",
650
174
  queries: toQueryRecord(searchParams),
651
- ...context?.i18n ? { i18n: context.i18n } : {}
175
+ ...context?.i18n ? { i18n: context.i18n } : {},
176
+ ...context?.pageType ? { pageType: context.pageType } : {},
177
+ ...context?.handle ? { handle: context.handle } : {}
652
178
  };
653
179
  }
654
-
655
- // src/runtime.ts
656
- function getRuntimeWindow() {
657
- return typeof window === "undefined" ? void 0 : window;
658
- }
659
- function getRenderablePage(data) {
660
- let page = data.page;
661
- let rootId = page.rootId ?? page.items.find((item) => item.type === "main")?.id ?? page.items[0]?.id ?? page.id;
662
- return { ...page, rootId };
663
- }
664
- function getConfigString(configs, key) {
665
- let value = configs?.[key];
666
- return typeof value === "string" ? value : void 0;
667
- }
668
- function getRecordString(record, key) {
669
- let value = record?.[key];
670
- return typeof value === "string" ? value : void 0;
671
- }
672
- function resolveProjectId(client, data, fallback) {
673
- let projectId = client?.projectId;
674
- if (projectId) {
675
- return projectId;
676
- }
677
- let configProjectId = getConfigString(data.configs, "projectId");
678
- if (configProjectId) {
679
- return configProjectId;
680
- }
681
- return getRecordString(data.project, "id") ?? fallback;
682
- }
683
- function resolveDataContext(config) {
684
- if (config.dataContext) {
685
- return config.dataContext;
686
- }
687
- if (config.client?.dataContext) {
688
- return config.client.dataContext;
689
- }
690
- return {};
691
- }
692
- function getRuntimeKey(pageId, requestInfo) {
693
- return `${pageId}:${requestInfo.pathname}:${requestInfo.search}`;
694
- }
695
- function registerRuntime(runtime) {
696
- let runtimeWindow = getRuntimeWindow();
697
- if (!runtimeWindow) {
180
+ var DEFAULT_REVALIDATE_ENDPOINT = "/api/weaverse/revalidate";
181
+ var MAX_ROUTE_CONTEXT_HANDLE_LENGTH = 512;
182
+ var DENIED_ROUTE_CONTEXT_PARAMS = [
183
+ "weaverseprojectid",
184
+ "weaversehost",
185
+ "weaverseapikey",
186
+ "weaverseapibase",
187
+ "weaversepublicapibase",
188
+ "weaverseversion",
189
+ "projectid",
190
+ "weaversedraftitem",
191
+ "__weaversedraftitem",
192
+ "_rsc"
193
+ ];
194
+ var ROUTE_CONTEXT_I18N_KEYS = [
195
+ "country",
196
+ "label",
197
+ "language",
198
+ "locale",
199
+ "pathPrefix"
200
+ ];
201
+ function sanitizeRouteContextSearch(search) {
202
+ let params = new URLSearchParams(search);
203
+ let denied = new Set(DENIED_ROUTE_CONTEXT_PARAMS);
204
+ for (let key of [...new Set(params.keys())]) {
205
+ if (denied.has(key.toLowerCase())) {
206
+ params.delete(key);
207
+ }
208
+ }
209
+ let serialized = params.toString();
210
+ return serialized ? `?${serialized}` : "";
211
+ }
212
+ function buildWeaverseNextRevalidateRouteContext(requestInfo) {
213
+ if (!requestInfo || typeof requestInfo.pathname !== "string" || !requestInfo.pathname) {
698
214
  return;
699
215
  }
700
- runtimeWindow.__weaverses ??= {};
701
- runtimeWindow.__weaverses[runtime.pageId] = runtime;
702
- runtimeWindow.__weaverse = runtime;
703
- }
704
- var WeaverseNextRuntime = class extends react.Weaverse {
705
- pageId;
706
- internal;
707
- requestInfo;
708
- isPreviewMode;
709
- isRevisionPreview;
710
- sectionType;
711
- // ─── Item-level translation sidecar (design mode only) ─────────────
712
- /**
713
- * Translation map received from Builder in design mode, keyed by item ID.
714
- * `WeaverseNextItem.getSnapShot()` overlays these onto the data handed to
715
- * components, so translations render without mutating the base `_store`.
716
- */
717
- translationMap = {};
718
- translationLocale = String();
719
- translationLanguageId = String();
720
- /**
721
- * Fresh page data captured while React was rendering. Runtime reuse happens
722
- * inside the renderer's render phase (`useMemo`), where applying it through
723
- * `setProjectData()` would emit item updates to `useSyncExternalStore`
724
- * subscribers mid-render. The renderer applies it post-commit via
725
- * `flushRenderPhaseUpdates()`. Never set on design-mode runtimes — the live
726
- * Studio tree owns the page data there.
727
- */
728
- pendingProjectData;
729
- /**
730
- * Reused item instances whose data was refreshed while this runtime was
731
- * constructed during render (core `initProject()` calls `setData()` on
732
- * them). Their data is already fresh; only the subscriber notification was
733
- * deferred to `flushRenderPhaseUpdates()`.
734
- */
735
- pendingItemUpdates = [];
736
- constructor(config) {
737
- let { client, data } = config;
738
- let page = getRenderablePage(data);
739
- let configs = data.configs;
740
- let requestContext = client?.requestContext;
741
- let projectId = resolveProjectId(client, data, page.id);
742
- ensureNextItemConstructor();
743
- super({
744
- projectId,
745
- data: page,
746
- isDesignMode: requestContext?.isDesignMode ?? false,
747
- weaverseHost: getConfigString(configs, "weaverseHost"),
748
- weaverseVersion: getConfigString(configs, "weaverseVersion")
749
- });
750
- this.pageId = page.id;
751
- this.dataContext = resolveDataContext(config);
752
- this.requestInfo = buildWeaverseNextRequestInfo(requestContext);
753
- this.isDesignMode = requestContext?.isDesignMode ?? false;
754
- this.isPreviewMode = requestContext?.isPreviewMode ?? false;
755
- this.isRevisionPreview = requestContext?.isRevisionPreview ?? false;
756
- this.sectionType = requestContext?.sectionType;
757
- let translationStore = config.translationStore ?? new TranslationStore();
758
- this.internal = {
759
- merchantOverrides: config.merchantOverrides,
760
- navigate: config.navigate,
761
- pageAssignment: data.pageAssignment,
762
- project: data.project,
763
- revalidate: config.revalidate,
764
- themeSettingsStore: config.themeSettingsStore ?? createWeaverseNextThemeSettingsStore({
765
- schema: client?.themeSchema,
766
- settings: client?.themeSettings
767
- }),
768
- translationStore,
769
- themeTextStore: translationStore
770
- };
771
- this.extractTranslationSidecar();
772
- }
773
- /**
774
- * Pull `translationMap` / `translationLocale` / `translationLanguageId` off
775
- * the current page data (Builder attaches them in design mode). Unlike
776
- * Hydrogen, this also clears the sidecar to defaults when the new page data
777
- * carries none, so a reused runtime / project-data swap can't leave a stale
778
- * map overlaying the fresh tree.
779
- */
780
- extractTranslationSidecar = () => {
781
- let pageData = this.data;
782
- let translationMap = pageData?.translationMap;
783
- if (translationMap) {
784
- this.translationMap = translationMap;
785
- this.translationLocale = pageData?.translationLocale || "";
786
- this.translationLanguageId = pageData?.translationLanguageId || "";
787
- } else {
788
- this.translationMap = {};
789
- this.translationLocale = "";
790
- this.translationLanguageId = "";
791
- }
216
+ let routeContext = {
217
+ pathname: requestInfo.pathname,
218
+ search: sanitizeRouteContextSearch(requestInfo.search ?? "")
792
219
  };
793
- /**
794
- * Replace the translation sidecar (Builder pushes this when translation data
795
- * arrives or the active language changes) and refresh every item so their
796
- * merged snapshots recompute.
797
- */
798
- setTranslationSidecar = (map = {}, locale = "", languageId = "") => {
799
- this.translationMap = map;
800
- this.translationLocale = locale;
801
- this.translationLanguageId = languageId;
802
- this.refreshAllItems();
803
- };
804
- /**
805
- * Update a single translatable field for one item. Assigns a new per-item
806
- * entry object so `WeaverseNextItem.getSnapShot()`'s translation-ref cache
807
- * invalidates, then triggers only that item's subscribers.
808
- */
809
- updateTranslation = (itemId, key, originalValue, translatedValue) => {
810
- if (!this.translationMap[itemId]) {
811
- this.translationMap[itemId] = {};
812
- }
813
- this.translationMap[itemId] = {
814
- ...this.translationMap[itemId],
815
- [key]: { originalValue, translatedValue }
816
- };
817
- let instance = this.itemInstances.get(itemId);
818
- if (instance) {
819
- instance.triggerUpdate();
820
- }
821
- };
822
- /**
823
- * Collect translation changes for the save flow. Returns `undefined` unless
824
- * both the locale and language ID are set and at least one entry exists;
825
- * each entry key is namespaced as `data.<field>` to match Builder.
826
- */
827
- getTranslationChanges = () => {
828
- let languageId = this.translationLanguageId;
829
- if (!(this.translationLocale && languageId)) {
830
- return;
831
- }
832
- let entries = [];
833
- for (let [itemId, fields] of Object.entries(this.translationMap)) {
834
- for (let [key, entry] of Object.entries(fields)) {
835
- entries.push({
836
- itemId,
837
- key: `data.${key}`,
838
- originalValue: entry.originalValue,
839
- translatedValue: entry.translatedValue
840
- });
220
+ let { i18n } = requestInfo;
221
+ if (i18n && typeof i18n === "object") {
222
+ let picked = {};
223
+ for (let key of ROUTE_CONTEXT_I18N_KEYS) {
224
+ let value = i18n[key];
225
+ if (typeof value === "string") {
226
+ picked[key] = value;
841
227
  }
842
228
  }
843
- if (entries.length === 0) {
844
- return;
229
+ if (Object.keys(picked).length) {
230
+ routeContext.i18n = picked;
845
231
  }
846
- return { languageId, entries };
847
- };
848
- /**
849
- * Re-extract the translation sidecar whenever project data is replaced (route
850
- * change / non-design reuse), before rebuilding item stores. Takes the
851
- * renderable page shape (`rootId` resolved) that `getRenderablePage()` and
852
- * the reuse branch pass in.
853
- */
854
- setProjectData = (data) => {
855
- this.data = data;
856
- this.extractTranslationSidecar();
857
- this.initProject();
858
- };
859
- /**
860
- * Apply updates deferred off the render phase: swap in any pending page data
861
- * captured on runtime reuse, then notify items whose data was refreshed
862
- * during construction. The renderer calls this from a layout effect right
863
- * after commit, so subscribers re-render before paint (no content flash) and
864
- * without React's setState-in-render warning.
865
- */
866
- flushRenderPhaseUpdates = () => {
867
- let pendingData = this.pendingProjectData;
868
- if (pendingData) {
869
- this.pendingProjectData = void 0;
870
- this.setProjectData(pendingData);
871
- }
872
- let pendingItems = this.pendingItemUpdates;
873
- if (pendingItems.length > 0) {
874
- this.pendingItemUpdates = [];
875
- for (let item of pendingItems) {
876
- item.triggerUpdate();
877
- }
878
- }
879
- };
880
- };
881
- function createWeaverseNextRuntime(config) {
882
- let requestInfo = buildWeaverseNextRequestInfo(config.client?.requestContext);
883
- let page = getRenderablePage(config.data);
884
- let runtimeWindow = getRuntimeWindow();
885
- let existing = runtimeWindow?.__weaverses?.[page.id];
886
- let requestKey = getRuntimeKey(page.id, requestInfo);
887
- if (existing?.__weaverseNextRequestKey === requestKey) {
888
- let nextIsDesignMode = config.client?.requestContext?.isDesignMode ?? false;
889
- if (existing.isDesignMode || nextIsDesignMode) {
890
- existing.pendingProjectData = void 0;
891
- } else {
892
- existing.pendingProjectData = page;
893
- }
894
- existing.__weaverseNextLatestData = page;
895
- existing.dataContext = resolveDataContext(config);
896
- existing.requestInfo = requestInfo;
897
- existing.projectId = resolveProjectId(config.client, config.data, page.id);
898
- existing.isDesignMode = nextIsDesignMode;
899
- existing.isPreviewMode = config.client?.requestContext?.isPreviewMode ?? false;
900
- existing.isRevisionPreview = config.client?.requestContext?.isRevisionPreview ?? false;
901
- existing.sectionType = config.client?.requestContext?.sectionType;
902
- existing.weaverseHost = getConfigString(config.data.configs, "weaverseHost") ?? existing.weaverseHost;
903
- existing.weaverseVersion = getConfigString(config.data.configs, "weaverseVersion") ?? existing.weaverseVersion;
904
- existing.internal.pageAssignment = config.data.pageAssignment;
905
- existing.internal.project = config.data.project;
906
- if (config.navigate) {
907
- existing.internal.navigate = config.navigate;
908
- }
909
- if (config.revalidate) {
910
- existing.internal.revalidate = config.revalidate;
911
- }
912
- if (config.themeSettingsStore) {
913
- existing.internal.themeSettingsStore = config.themeSettingsStore;
914
- }
915
- if (config.translationStore) {
916
- existing.internal.translationStore = config.translationStore;
917
- existing.internal.themeTextStore = config.translationStore;
918
- }
919
- existing.internal.merchantOverrides = config.merchantOverrides;
920
- registerRuntime(existing);
921
- return existing;
922
- }
923
- let [runtime, refreshedItems] = collectDeferredItemUpdates(
924
- () => new WeaverseNextRuntime(config)
925
- );
926
- runtime.pendingItemUpdates = refreshedItems;
927
- runtime.__weaverseNextRequestKey = requestKey;
928
- runtime.__weaverseNextLatestData = page;
929
- registerRuntime(runtime);
930
- return runtime;
931
- }
932
- function bindWeaverseNextStudioRuntime(runtime) {
933
- if (!runtime.isDesignMode) {
934
- return false;
935
232
  }
936
- let studio = getRuntimeWindow()?.weaverseStudio;
937
- if (!studio) {
938
- return false;
233
+ if (requestInfo.pageType && schema.PageTypeSchema.safeParse(requestInfo.pageType).success) {
234
+ routeContext.pageType = requestInfo.pageType;
939
235
  }
940
- let boundRuntime = runtime;
941
- if (!boundRuntime.__weaverseNextStudioBound) {
942
- studio.init?.(runtime);
943
- boundRuntime.__weaverseNextStudioBound = true;
944
- return true;
236
+ if (typeof requestInfo.handle === "string" && requestInfo.handle && requestInfo.handle.length <= MAX_ROUTE_CONTEXT_HANDLE_LENGTH) {
237
+ routeContext.handle = requestInfo.handle;
945
238
  }
946
- studio.refreshStudio?.({
947
- data: boundRuntime.__weaverseNextLatestData ?? runtime.data,
948
- pageId: runtime.pageId,
949
- requestInfo: runtime.requestInfo
950
- });
951
- return true;
239
+ return routeContext;
952
240
  }
953
-
954
- // src/revalidate-item.ts
955
- var DEFAULT_REVALIDATE_ENDPOINT = "/api/weaverse/revalidate";
956
241
  async function revalidateWeaverseNextItem(runtime, draftItem, endpoint = DEFAULT_REVALIDATE_ENDPOINT) {
957
242
  let instance = runtime.itemInstances.get(draftItem.id);
958
243
  if (!instance) {
@@ -960,10 +245,14 @@ async function revalidateWeaverseNextItem(runtime, draftItem, endpoint = DEFAULT
960
245
  `[weaverse-next] No live item instance for "${draftItem.id}".`
961
246
  );
962
247
  }
248
+ let routeContext = buildWeaverseNextRevalidateRouteContext(
249
+ runtime.requestInfo
250
+ );
251
+ let body = routeContext ? { draftItem, routeContext } : { draftItem };
963
252
  let response = await fetch(endpoint, {
964
253
  method: "POST",
965
254
  headers: { "Content-Type": "application/json" },
966
- body: JSON.stringify({ draftItem })
255
+ body: JSON.stringify(body)
967
256
  });
968
257
  if (!response.ok) {
969
258
  throw new Error(
@@ -973,156 +262,6 @@ async function revalidateWeaverseNextItem(runtime, draftItem, endpoint = DEFAULT
973
262
  let { loaderData } = await response.json();
974
263
  instance.setData({ loaderData: loaderData ?? null });
975
264
  }
976
- var STUDIO_BIND_RETRY_INTERVAL_MS = 50;
977
- var STUDIO_BIND_MAX_ATTEMPTS = 100;
978
- function WeaverseNextStudioBridge(props) {
979
- let { runtime, navigate, revalidate, revalidateItem } = props;
980
- let lastRuntimeRef = react$1.useRef(runtime);
981
- let lastRuntimeDataRef = react$1.useRef(runtime.data);
982
- let lastRequestInfoRef = react$1.useRef(runtime.requestInfo);
983
- react$1.useEffect(() => {
984
- if (navigate) {
985
- runtime.internal.navigate = navigate;
986
- }
987
- if (revalidate) {
988
- runtime.internal.revalidate = revalidate;
989
- }
990
- if (revalidateItem) {
991
- runtime.internal.revalidateItem = revalidateItem;
992
- }
993
- if (bindWeaverseNextStudioRuntime(runtime) || !runtime.isDesignMode) {
994
- return;
995
- }
996
- let attempts = 0;
997
- let interval = window.setInterval(() => {
998
- attempts += 1;
999
- if (bindWeaverseNextStudioRuntime(runtime) || attempts >= STUDIO_BIND_MAX_ATTEMPTS) {
1000
- window.clearInterval(interval);
1001
- }
1002
- }, STUDIO_BIND_RETRY_INTERVAL_MS);
1003
- return () => window.clearInterval(interval);
1004
- }, [runtime, navigate, revalidate, revalidateItem]);
1005
- react$1.useEffect(() => {
1006
- if (lastRuntimeRef.current !== runtime) {
1007
- lastRuntimeRef.current = runtime;
1008
- lastRuntimeDataRef.current = runtime.data;
1009
- lastRequestInfoRef.current = runtime.requestInfo;
1010
- return;
1011
- }
1012
- if (lastRuntimeDataRef.current === runtime.data && lastRequestInfoRef.current === runtime.requestInfo) {
1013
- return;
1014
- }
1015
- lastRuntimeDataRef.current = runtime.data;
1016
- lastRequestInfoRef.current = runtime.requestInfo;
1017
- bindWeaverseNextStudioRuntime(runtime);
1018
- }, [runtime, runtime.data, runtime.requestInfo]);
1019
- return null;
1020
- }
1021
-
1022
- // src/studio-router.ts
1023
- function shouldResetScroll(options) {
1024
- if (!options || typeof options !== "object") {
1025
- return true;
1026
- }
1027
- return !options.preventScrollReset;
1028
- }
1029
- function createWeaverseNextStudioInternals(router, options = {}) {
1030
- return {
1031
- navigate: (to, navigateOptions) => {
1032
- let scroll = shouldResetScroll(navigateOptions);
1033
- if (options.replace) {
1034
- router.replace(to, { scroll });
1035
- } else {
1036
- router.push(to, { scroll });
1037
- }
1038
- },
1039
- revalidate: () => {
1040
- router.refresh();
1041
- }
1042
- };
1043
- }
1044
- function useWeaverseNextStudioInternals(options) {
1045
- let router = navigation.useRouter();
1046
- let ref = react$1.useRef(null);
1047
- if (!ref.current || ref.current.router !== router || ref.current.replace !== options?.replace) {
1048
- ref.current = {
1049
- internals: createWeaverseNextStudioInternals(router, options),
1050
- replace: options?.replace,
1051
- router
1052
- };
1053
- }
1054
- return ref.current.internals;
1055
- }
1056
- function WeaverseNextStudio(props) {
1057
- let { runtime, replace, revalidateEndpoint } = props;
1058
- let { navigate, revalidate } = useWeaverseNextStudioInternals({ replace });
1059
- let endpoint = revalidateEndpoint ?? DEFAULT_REVALIDATE_ENDPOINT;
1060
- let revalidateItemRef = react$1.useRef(null);
1061
- if (revalidateItemRef.current?.endpoint !== endpoint || revalidateItemRef.current.runtime !== runtime) {
1062
- revalidateItemRef.current = {
1063
- endpoint,
1064
- revalidateItem: (draftItem) => revalidateWeaverseNextItem(runtime, draftItem, endpoint),
1065
- runtime
1066
- };
1067
- }
1068
- return /* @__PURE__ */ jsxRuntime.jsx(
1069
- WeaverseNextStudioBridge,
1070
- {
1071
- navigate,
1072
- revalidate,
1073
- revalidateItem: revalidateItemRef.current.revalidateItem,
1074
- runtime
1075
- }
1076
- );
1077
- }
1078
- var EMPTY_DATA_CONTEXT = {};
1079
- var useIsomorphicLayoutEffect = typeof window === "undefined" ? react$1.useEffect : react$1.useLayoutEffect;
1080
- function getRenderablePage2(data) {
1081
- let page = data?.page;
1082
- if (!page) {
1083
- return null;
1084
- }
1085
- let rootId = page.rootId ?? page.items.find((item) => item.type === "main")?.id ?? page.items[0]?.id ?? page.id;
1086
- return { ...page, rootId };
1087
- }
1088
- var WeaverseNextRenderer = react$1.memo(function WeaverseNextRendererComponent(props) {
1089
- let context = react$1.useContext(WeaverseNextContext);
1090
- let client = props.client ?? context?.client;
1091
- let data = props.data ?? client?.data ?? null;
1092
- let dataContext = props.dataContext ?? client?.dataContext ?? EMPTY_DATA_CONTEXT;
1093
- let page = react$1.useMemo(() => getRenderablePage2(data), [data]);
1094
- let weaverse = react$1.useMemo(() => {
1095
- if (!page) {
1096
- return null;
1097
- }
1098
- return createWeaverseNextRuntime({
1099
- client,
1100
- data: { ...data, page },
1101
- dataContext,
1102
- merchantOverrides: context?.merchantOverrides,
1103
- themeSettingsStore: context?.themeSettingsStore,
1104
- translationStore: context?.translationStore
1105
- });
1106
- }, [
1107
- client,
1108
- page,
1109
- data,
1110
- dataContext,
1111
- context?.merchantOverrides,
1112
- context?.themeSettingsStore,
1113
- context?.translationStore
1114
- ]);
1115
- useIsomorphicLayoutEffect(() => {
1116
- weaverse?.flushRenderPhaseUpdates();
1117
- }, [weaverse]);
1118
- if (!weaverse) {
1119
- return null;
1120
- }
1121
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1122
- /* @__PURE__ */ jsxRuntime.jsx(react.WeaverseRoot, { context: weaverse }),
1123
- /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextStudio, { runtime: weaverse })
1124
- ] });
1125
- });
1126
265
 
1127
266
  // src/seo.ts
1128
267
  function hasOpenGraphContent(seo) {
@@ -1131,6 +270,15 @@ function hasOpenGraphContent(seo) {
1131
270
  function hasTwitterContent(seo) {
1132
271
  return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType);
1133
272
  }
273
+ function normalizeTwitterCard(cardType) {
274
+ return cardType === "summary_large_image" ? "summary_large_image" : "summary";
275
+ }
276
+ function normalizeOpenGraphType(type) {
277
+ if (type === "article" || type === "profile" || type === "video.other") {
278
+ return type;
279
+ }
280
+ return "website";
281
+ }
1134
282
  function formatWeaverseNextSeoMetadata(seo) {
1135
283
  let index = seo?.robots?.index !== false;
1136
284
  let follow = seo?.robots?.follow !== false;
@@ -1158,13 +306,13 @@ function formatWeaverseNextSeoMetadata(seo) {
1158
306
  title: openGraph?.title,
1159
307
  description: openGraph?.description,
1160
308
  images: openGraph?.image ? [openGraph.image] : void 0,
1161
- type: openGraph?.type || "website"
309
+ type: normalizeOpenGraphType(openGraph?.type)
1162
310
  };
1163
311
  }
1164
312
  let twitter = seo.twitter;
1165
313
  if (hasTwitterContent(twitter)) {
1166
314
  metadata.twitter = {
1167
- card: twitter?.cardType || "summary",
315
+ card: normalizeTwitterCard(twitter?.cardType),
1168
316
  title: twitter?.title,
1169
317
  description: twitter?.description,
1170
318
  images: twitter?.image ? [twitter.image] : void 0
@@ -1180,6 +328,29 @@ function getWeaverseNextSeoMetadata(data) {
1180
328
  return formatWeaverseNextSeoMetadata(page?.seo ?? null);
1181
329
  }
1182
330
 
331
+ // src/studio-router.ts
332
+ function shouldResetScroll(options) {
333
+ if (!options || typeof options !== "object") {
334
+ return true;
335
+ }
336
+ return !options.preventScrollReset;
337
+ }
338
+ function createWeaverseNextStudioInternals(router, options = {}) {
339
+ return {
340
+ navigate: (to, navigateOptions) => {
341
+ let scroll = shouldResetScroll(navigateOptions);
342
+ if (options.replace) {
343
+ router.replace(to, { scroll });
344
+ } else {
345
+ router.push(to, { scroll });
346
+ }
347
+ },
348
+ revalidate: () => {
349
+ router.refresh();
350
+ }
351
+ };
352
+ }
353
+
1183
354
  // src/studio-script-src.ts
1184
355
  var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
1185
356
  function isLoopbackHostname(hostname) {
@@ -1243,117 +414,235 @@ function resolveWeaverseNextStudioScriptSrc(context, options = {}) {
1243
414
  if (!isTrustedStudioOrigin(origin, storefrontHostname)) {
1244
415
  return null;
1245
416
  }
1246
- let bundle = isDesignMode ? "index.js" : "preview.js";
417
+ let bundle = isPreviewMode || isRevisionPreview ? "preview.js" : "index.js";
1247
418
  let version = last(searchParams, "weaverseVersion");
1248
419
  let url = `${origin}/static/studio/next/${bundle}`;
1249
420
  return version ? `${url}?v=${encodeURIComponent(version)}` : url;
1250
421
  }
1251
422
 
1252
- // src/studio-connect.tsx
1253
- var loadedScriptSrcs = /* @__PURE__ */ new Set();
1254
- function loadScript(src) {
1255
- if (typeof document === "undefined") {
1256
- return;
1257
- }
1258
- if (loadedScriptSrcs.has(src)) {
1259
- return;
423
+ // src/theme-settings-store.ts
424
+ function createWeaverseNextThemeSettingsStore(options = {}) {
425
+ let listeners = /* @__PURE__ */ new Set();
426
+ let store = {
427
+ publicEnv: options.publicEnv,
428
+ schema: options.schema,
429
+ settings: { ...options.settings ?? {} },
430
+ getServerSnapshot: () => store.settings,
431
+ getSnapshot: () => store.settings,
432
+ subscribe: (listener) => {
433
+ listeners.add(listener);
434
+ return () => {
435
+ listeners.delete(listener);
436
+ };
437
+ },
438
+ updateThemeSettings: (next) => {
439
+ store.settings = { ...store.settings, ...next };
440
+ for (let listener of listeners) {
441
+ listener();
442
+ }
443
+ }
444
+ };
445
+ return store;
446
+ }
447
+
448
+ // src/translate.ts
449
+ function getNestedKey(obj, path, fallback) {
450
+ let keys = path.split(".");
451
+ let current = obj;
452
+ for (let key of keys) {
453
+ if (current == null || typeof current !== "object") {
454
+ return fallback;
455
+ }
456
+ current = current[key];
1260
457
  }
1261
- let existing = document.querySelector(`script[src="${src}"]`);
1262
- if (existing) {
1263
- loadedScriptSrcs.add(src);
1264
- return;
458
+ if (typeof current === "string") {
459
+ return current;
1265
460
  }
1266
- let script = document.createElement("script");
1267
- script.async = true;
1268
- script.src = src;
1269
- document.head.appendChild(script);
1270
- loadedScriptSrcs.add(src);
461
+ return fallback;
1271
462
  }
1272
- function loadWeaverseNextStudioScript(context, options = {}) {
1273
- let resolvedContext = context;
1274
- if (!resolvedContext && typeof window !== "undefined") {
1275
- resolvedContext = {
1276
- searchParams: new URLSearchParams(window.location.search),
1277
- url: window.location.href
1278
- };
463
+ function interpolate(template, variables) {
464
+ if (!variables) {
465
+ return template;
1279
466
  }
1280
- let src = resolveWeaverseNextStudioScriptSrc(resolvedContext, {
1281
- storefrontHostname: options.storefrontHostname ?? (typeof window === "undefined" ? void 0 : window.location.hostname)
467
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
468
+ let value = variables[key];
469
+ return value === void 0 ? match : String(value);
1282
470
  });
1283
- if (src) {
1284
- loadScript(src);
1285
- }
1286
471
  }
1287
- function WeaverseNextStudioConnect(props) {
1288
- react$1.useEffect(() => {
1289
- loadWeaverseNextStudioScript(props.context, {
1290
- storefrontHostname: props.storefrontHostname
1291
- });
1292
- }, [props.context, props.storefrontHostname]);
1293
- return null;
472
+ function createTranslate(sources) {
473
+ let { staticContent, merchantOverrides, designOverrides, externalT } = sources;
474
+ return (key, variables) => {
475
+ if (externalT) {
476
+ let external = externalT(key, variables);
477
+ if (external && external !== key) {
478
+ return external;
479
+ }
480
+ }
481
+ let design = designOverrides && Object.hasOwn(designOverrides, key) ? designOverrides[key] : void 0;
482
+ let override = merchantOverrides ? getNestedKey(merchantOverrides, key) : void 0;
483
+ let staticValue = getNestedKey(staticContent, key);
484
+ let raw = design ?? override ?? staticValue ?? key;
485
+ return interpolate(raw, variables);
486
+ };
1294
487
  }
1295
488
 
1296
- Object.defineProperty(exports, "isBrowser", {
489
+ // src/translation-store.ts
490
+ var TranslationStore = class {
491
+ /** Current flat translation overrides keyed by dot path. */
492
+ overrides = {};
493
+ listeners = /* @__PURE__ */ new Set();
494
+ /** Replace every override and notify subscribers. */
495
+ setOverrides = (overrides) => {
496
+ this.overrides = { ...overrides };
497
+ this.emit();
498
+ };
499
+ /** Merge changed overrides into the current values and notify subscribers. */
500
+ updateOverrides = (newOverrides) => {
501
+ this.overrides = { ...this.overrides, ...newOverrides };
502
+ this.emit();
503
+ };
504
+ /** Return the current client-side override snapshot. */
505
+ getSnapshot = () => this.overrides;
506
+ /** Return the override snapshot used during server rendering. */
507
+ getServerSnapshot = () => this.overrides;
508
+ /** Subscribe to override changes and return an unsubscribe function. */
509
+ subscribe = (callback) => {
510
+ this.listeners.add(callback);
511
+ return () => {
512
+ this.listeners.delete(callback);
513
+ };
514
+ };
515
+ emit = () => {
516
+ for (const listener of Array.from(this.listeners)) {
517
+ try {
518
+ listener();
519
+ } catch (error) {
520
+ console.error("TranslationStore: Listener error", error);
521
+ }
522
+ }
523
+ };
524
+ };
525
+ var ThemeTextStore = TranslationStore;
526
+
527
+ Object.defineProperty(exports, "ThemeTextProvider", {
528
+ enumerable: true,
529
+ get: function () { return clientBoundary_js.ThemeTextProvider; }
530
+ });
531
+ Object.defineProperty(exports, "TranslationProvider", {
1297
532
  enumerable: true,
1298
- get: function () { return react.isBrowser; }
533
+ get: function () { return clientBoundary_js.TranslationProvider; }
1299
534
  });
1300
- Object.defineProperty(exports, "isIframe", {
535
+ Object.defineProperty(exports, "WeaverseNextProvider", {
1301
536
  enumerable: true,
1302
- get: function () { return react.isIframe; }
537
+ get: function () { return clientBoundary_js.WeaverseNextProvider; }
538
+ });
539
+ Object.defineProperty(exports, "WeaverseNextRenderer", {
540
+ enumerable: true,
541
+ get: function () { return clientBoundary_js.WeaverseNextRenderer; }
542
+ });
543
+ Object.defineProperty(exports, "WeaverseNextRootProvider", {
544
+ enumerable: true,
545
+ get: function () { return clientBoundary_js.WeaverseNextRootProvider; }
546
+ });
547
+ Object.defineProperty(exports, "WeaverseNextRuntime", {
548
+ enumerable: true,
549
+ get: function () { return clientBoundary_js.WeaverseNextRuntime; }
550
+ });
551
+ Object.defineProperty(exports, "WeaverseNextStudio", {
552
+ enumerable: true,
553
+ get: function () { return clientBoundary_js.WeaverseNextStudio; }
554
+ });
555
+ Object.defineProperty(exports, "WeaverseNextStudioBridge", {
556
+ enumerable: true,
557
+ get: function () { return clientBoundary_js.WeaverseNextStudioBridge; }
558
+ });
559
+ Object.defineProperty(exports, "WeaverseNextStudioConnect", {
560
+ enumerable: true,
561
+ get: function () { return clientBoundary_js.WeaverseNextStudioConnect; }
562
+ });
563
+ Object.defineProperty(exports, "bindWeaverseNextStudioRuntime", {
564
+ enumerable: true,
565
+ get: function () { return clientBoundary_js.bindWeaverseNextStudioRuntime; }
566
+ });
567
+ Object.defineProperty(exports, "createWeaverseNextClient", {
568
+ enumerable: true,
569
+ get: function () { return clientBoundary_js.createWeaverseNextClient; }
570
+ });
571
+ Object.defineProperty(exports, "createWeaverseNextRuntime", {
572
+ enumerable: true,
573
+ get: function () { return clientBoundary_js.createWeaverseNextRuntime; }
574
+ });
575
+ Object.defineProperty(exports, "loadWeaverseNextStudioScript", {
576
+ enumerable: true,
577
+ get: function () { return clientBoundary_js.loadWeaverseNextStudioScript; }
1303
578
  });
1304
579
  Object.defineProperty(exports, "useChildInstances", {
1305
580
  enumerable: true,
1306
- get: function () { return react.useChildInstances; }
581
+ get: function () { return clientBoundary_js.useChildInstances; }
1307
582
  });
1308
583
  Object.defineProperty(exports, "useItemInstance", {
1309
584
  enumerable: true,
1310
- get: function () { return react.useItemInstance; }
585
+ get: function () { return clientBoundary_js.useItemInstance; }
1311
586
  });
1312
587
  Object.defineProperty(exports, "useParentInstance", {
1313
588
  enumerable: true,
1314
- get: function () { return react.useParentInstance; }
589
+ get: function () { return clientBoundary_js.useParentInstance; }
590
+ });
591
+ Object.defineProperty(exports, "useThemeSettings", {
592
+ enumerable: true,
593
+ get: function () { return clientBoundary_js.useThemeSettings; }
594
+ });
595
+ Object.defineProperty(exports, "useThemeText", {
596
+ enumerable: true,
597
+ get: function () { return clientBoundary_js.useThemeText; }
598
+ });
599
+ Object.defineProperty(exports, "useTranslation", {
600
+ enumerable: true,
601
+ get: function () { return clientBoundary_js.useTranslation; }
1315
602
  });
1316
603
  Object.defineProperty(exports, "useWeaverse", {
1317
604
  enumerable: true,
1318
- get: function () { return react.useWeaverse; }
605
+ get: function () { return clientBoundary_js.useWeaverse; }
606
+ });
607
+ Object.defineProperty(exports, "useWeaverseCommerce", {
608
+ enumerable: true,
609
+ get: function () { return clientBoundary_js.useWeaverseCommerce; }
1319
610
  });
1320
- Object.defineProperty(exports, "createSchema", {
611
+ Object.defineProperty(exports, "useWeaverseNextStudioInternals", {
1321
612
  enumerable: true,
1322
- get: function () { return schema.createSchema; }
613
+ get: function () { return clientBoundary_js.useWeaverseNextStudioInternals; }
614
+ });
615
+ Object.defineProperty(exports, "useWeaversePageData", {
616
+ enumerable: true,
617
+ get: function () { return clientBoundary_js.useWeaversePageData; }
618
+ });
619
+ Object.defineProperty(exports, "useWeaverseRootData", {
620
+ enumerable: true,
621
+ get: function () { return clientBoundary_js.useWeaverseRootData; }
1323
622
  });
1324
623
  exports.DEFAULT_REVALIDATE_ENDPOINT = DEFAULT_REVALIDATE_ENDPOINT;
1325
- exports.ThemeTextProvider = ThemeTextProvider;
1326
624
  exports.ThemeTextStore = ThemeTextStore;
1327
- exports.TranslationProvider = TranslationProvider;
1328
625
  exports.TranslationStore = TranslationStore;
1329
- exports.WeaverseNextProvider = WeaverseNextProvider;
1330
- exports.WeaverseNextRenderer = WeaverseNextRenderer;
1331
- exports.WeaverseNextRootProvider = WeaverseNextRootProvider;
1332
- exports.WeaverseNextRuntime = WeaverseNextRuntime;
1333
- exports.WeaverseNextStudio = WeaverseNextStudio;
1334
- exports.WeaverseNextStudioBridge = WeaverseNextStudioBridge;
1335
- exports.WeaverseNextStudioConnect = WeaverseNextStudioConnect;
1336
- exports.bindWeaverseNextStudioRuntime = bindWeaverseNextStudioRuntime;
1337
626
  exports.buildWeaverseNextRequestInfo = buildWeaverseNextRequestInfo;
627
+ exports.buildWeaverseNextRevalidateRouteContext = buildWeaverseNextRevalidateRouteContext;
1338
628
  exports.createTranslate = createTranslate;
1339
- exports.createWeaverseNextClient = createWeaverseNextClient;
1340
- exports.createWeaverseNextRuntime = createWeaverseNextRuntime;
1341
629
  exports.createWeaverseNextStudioInternals = createWeaverseNextStudioInternals;
1342
630
  exports.createWeaverseNextThemeSettingsStore = createWeaverseNextThemeSettingsStore;
1343
631
  exports.formatWeaverseNextSeoMetadata = formatWeaverseNextSeoMetadata;
632
+ exports.generateDataFromSchema = generateDataFromSchema;
1344
633
  exports.getNestedKey = getNestedKey;
1345
634
  exports.getWeaverseNextSeoMetadata = getWeaverseNextSeoMetadata;
1346
635
  exports.interpolate = interpolate;
1347
- exports.loadWeaverseNextStudioScript = loadWeaverseNextStudioScript;
636
+ exports.isBrowser = isBrowser;
637
+ exports.isIframe = isIframe;
1348
638
  exports.resolveWeaverseNextStudioScriptSrc = resolveWeaverseNextStudioScriptSrc;
1349
639
  exports.revalidateWeaverseNextItem = revalidateWeaverseNextItem;
1350
640
  exports.runWeaverseComponentLoaders = runWeaverseComponentLoaders;
1351
- exports.useThemeSettings = useThemeSettings;
1352
- exports.useThemeText = useThemeText;
1353
- exports.useTranslation = useTranslation;
1354
- exports.useWeaverseCommerce = useWeaverseCommerce;
1355
- exports.useWeaverseNextStudioInternals = useWeaverseNextStudioInternals;
1356
- exports.useWeaversePageData = useWeaversePageData;
1357
- exports.useWeaverseRootData = useWeaverseRootData;
641
+ Object.keys(schema).forEach(function (k) {
642
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
643
+ enumerable: true,
644
+ get: function () { return schema[k]; }
645
+ });
646
+ });
1358
647
  //# sourceMappingURL=index.js.map
1359
648
  //# sourceMappingURL=index.js.map