@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.
- package/README.md +586 -123
- package/dist/chunk-LH6AAEIK.mjs +326 -0
- package/dist/chunk-LH6AAEIK.mjs.map +1 -0
- package/dist/index.js +784 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +588 -58
- package/dist/index.mjs.map +1 -1
- package/dist/server.js +537 -76
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +363 -27
- package/dist/server.mjs.map +1 -1
- package/dist/types/client.d.ts +16 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/hooks.d.ts +20 -0
- package/dist/types/hooks.d.ts.map +1 -0
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/item.d.ts +60 -0
- package/dist/types/item.d.ts.map +1 -0
- package/dist/types/loader.d.ts +25 -0
- package/dist/types/loader.d.ts.map +1 -0
- package/dist/types/main.d.ts +11 -0
- package/dist/types/main.d.ts.map +1 -0
- package/dist/types/provider.d.ts +82 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/registry.d.ts +15 -0
- package/dist/types/registry.d.ts.map +1 -0
- package/dist/types/renderer.d.ts +21 -0
- package/dist/types/renderer.d.ts.map +1 -0
- package/dist/types/request-info.d.ts +19 -0
- package/dist/types/request-info.d.ts.map +1 -0
- package/dist/types/revalidate-item.d.ts +82 -0
- package/dist/types/revalidate-item.d.ts.map +1 -0
- package/dist/types/root-provider.d.ts +62 -0
- package/dist/types/root-provider.d.ts.map +1 -0
- package/dist/types/runtime.d.ts +113 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/seo.d.ts +55 -0
- package/dist/types/seo.d.ts.map +1 -0
- package/dist/types/server/configs.d.ts +27 -0
- package/dist/types/server/configs.d.ts.map +1 -0
- package/dist/types/server/normalize-page-url.d.ts +19 -0
- package/dist/types/server/normalize-page-url.d.ts.map +1 -0
- package/dist/types/server/revalidate-handler.d.ts +55 -0
- package/dist/types/server/revalidate-handler.d.ts.map +1 -0
- package/dist/types/server/server-client.d.ts +21 -0
- package/dist/types/server/server-client.d.ts.map +1 -0
- package/dist/types/server.d.ts +19 -0
- package/dist/types/server.d.ts.map +1 -0
- package/dist/types/studio-bridge.d.ts +16 -0
- package/dist/types/studio-bridge.d.ts.map +1 -0
- package/dist/types/studio-connect.d.ts +27 -0
- package/dist/types/studio-connect.d.ts.map +1 -0
- package/dist/types/studio-router.d.ts +50 -0
- package/dist/types/studio-router.d.ts.map +1 -0
- package/dist/types/studio-script-src.d.ts +12 -0
- package/dist/types/studio-script-src.d.ts.map +1 -0
- package/dist/types/theme-settings-store.d.ts +18 -0
- package/dist/types/theme-settings-store.d.ts.map +1 -0
- package/dist/types/translation-context.d.ts +156 -0
- package/dist/types/translation-context.d.ts.map +1 -0
- package/dist/types/translation-store.d.ts +44 -0
- package/dist/types/translation-store.d.ts.map +1 -0
- package/dist/types/types.d.ts +665 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/use-weaverse-next-studio.d.ts +34 -0
- package/dist/types/use-weaverse-next-studio.d.ts.map +1 -0
- package/dist/types/utils.d.ts +12 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +12 -8
- package/src/client.ts +102 -0
- package/src/hooks.ts +50 -0
- package/src/index.ts +153 -0
- package/src/item.ts +167 -0
- package/src/loader.ts +135 -0
- package/src/main.tsx +45 -0
- package/src/provider.tsx +225 -0
- package/src/registry.ts +63 -0
- package/src/renderer.tsx +100 -0
- package/src/request-info.ts +124 -0
- package/src/revalidate-item.ts +211 -0
- package/src/root-provider.tsx +182 -0
- package/src/runtime.ts +555 -0
- package/src/seo.ts +157 -0
- package/src/server/configs.ts +178 -0
- package/src/server/normalize-page-url.ts +73 -0
- package/src/server/revalidate-handler.ts +357 -0
- package/src/server/server-client.ts +728 -0
- package/src/server.ts +67 -0
- package/src/studio-bridge.tsx +82 -0
- package/src/studio-connect.tsx +88 -0
- package/src/studio-router.ts +85 -0
- package/src/studio-script-src.ts +128 -0
- package/src/theme-settings-store.ts +47 -0
- package/src/translation-context.tsx +299 -0
- package/src/translation-store.ts +69 -0
- package/src/types.ts +753 -0
- package/src/use-weaverse-next-studio.tsx +106 -0
- package/src/utils.ts +37 -0
- package/dist/chunk-SS6QUKYY.mjs +0 -149
- package/dist/chunk-SS6QUKYY.mjs.map +0 -1
- package/dist/index.d.mts +0 -250
- package/dist/index.d.ts +0 -250
- package/dist/server.d.mts +0 -60
- package/dist/server.d.ts +0 -60
- package/dist/types-DvEeLX6L.d.mts +0 -321
- package/dist/types-DvEeLX6L.d.ts +0 -321
package/dist/index.js
CHANGED
|
@@ -27,13 +27,101 @@ function generateDataFromSchema(schema) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
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
|
+
}
|
|
30
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
|
+
/** Create an item store from serialized component data and its runtime. */
|
|
31
58
|
constructor(initialData, weaverse) {
|
|
32
59
|
super(initialData, weaverse);
|
|
33
|
-
let { data, ...rest } = initialData;
|
|
34
60
|
let schemaData = generateDataFromSchema(this.Element?.schema);
|
|
35
|
-
Object.assign(this._store, schemaData,
|
|
61
|
+
Object.assign(this._store, schemaData, flattenItemData(initialData));
|
|
36
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Flatten nested `data` on updates exactly like the constructor does. Core's
|
|
65
|
+
* `initProject()` calls `setData(item)` when a new runtime serves a page
|
|
66
|
+
* whose item instances already exist (e.g. locale/client navigation).
|
|
67
|
+
*
|
|
68
|
+
* A complete serialized item (it carries `id` and `type`; core's
|
|
69
|
+
* `initProject()` always passes one) replaces the store the way the
|
|
70
|
+
* constructor builds it: the schema defaults of the item's own `type` — the
|
|
71
|
+
* Builder can replace a section in place, keeping its id — then the item's
|
|
72
|
+
* settings. A payload omits a setting — or the whole optional `data` — when
|
|
73
|
+
* its value equals the schema default, so merging into the previous store
|
|
74
|
+
* would keep the previous locale's value visible. Partial updates still
|
|
75
|
+
* merge. Either way the `_store` ref changes, which invalidates the memoized
|
|
76
|
+
* snapshot below. The subscriber emit is queued instead of fired when a
|
|
77
|
+
* render-phase deferral is active (see `collectDeferredItemUpdates`).
|
|
78
|
+
*/
|
|
79
|
+
setData = (update) => {
|
|
80
|
+
if ("id" in update && "type" in update) {
|
|
81
|
+
let { data, type } = update;
|
|
82
|
+
let schema = this.weaverse.elementRegistry.get(type)?.schema;
|
|
83
|
+
this._store = {
|
|
84
|
+
...generateDataFromSchema(schema),
|
|
85
|
+
...data === void 0 ? {} : { data },
|
|
86
|
+
...flattenItemData(update)
|
|
87
|
+
};
|
|
88
|
+
} else {
|
|
89
|
+
this.data = flattenItemData(update);
|
|
90
|
+
}
|
|
91
|
+
if (deferredItemUpdates) {
|
|
92
|
+
deferredItemUpdates.add(this);
|
|
93
|
+
} else {
|
|
94
|
+
this.triggerUpdate();
|
|
95
|
+
}
|
|
96
|
+
return this.data;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Overlay item-level translations onto the base store when the runtime is in
|
|
100
|
+
* translation mode. Mirrors Hydrogen's `WeaverseHydrogenItem.getSnapShot()`:
|
|
101
|
+
* memoized by `_store` ref + translation entry ref so the returned object is
|
|
102
|
+
* referentially stable, and a fast path that returns the base store untouched
|
|
103
|
+
* when the item has no translations.
|
|
104
|
+
*/
|
|
105
|
+
getSnapShot = () => {
|
|
106
|
+
let base = this._store;
|
|
107
|
+
let translations = this.weaverse.translationMap[this._id];
|
|
108
|
+
if (!translations) {
|
|
109
|
+
return base;
|
|
110
|
+
}
|
|
111
|
+
if (this._cachedSnapshot && this._snapshotStoreRef === base && this._snapshotTranslationRef === translations) {
|
|
112
|
+
return this._cachedSnapshot;
|
|
113
|
+
}
|
|
114
|
+
let merged = { ...base };
|
|
115
|
+
for (let [key, entry] of Object.entries(translations)) {
|
|
116
|
+
if (entry.translatedValue !== void 0) {
|
|
117
|
+
merged[key] = entry.translatedValue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this._cachedSnapshot = merged;
|
|
121
|
+
this._snapshotStoreRef = base;
|
|
122
|
+
this._snapshotTranslationRef = translations;
|
|
123
|
+
return merged;
|
|
124
|
+
};
|
|
37
125
|
};
|
|
38
126
|
function ensureNextItemConstructor() {
|
|
39
127
|
if (!react.Weaverse.ItemConstructor || react.Weaverse.ItemConstructor === react.WeaverseItemStore || react.Weaverse.ItemConstructor === WeaverseNextItem) {
|
|
@@ -158,30 +246,264 @@ function createWeaverseNextThemeSettingsStore(options = {}) {
|
|
|
158
246
|
};
|
|
159
247
|
return store;
|
|
160
248
|
}
|
|
249
|
+
function getNestedKey(obj, path, fallback) {
|
|
250
|
+
let keys = path.split(".");
|
|
251
|
+
let current = obj;
|
|
252
|
+
for (let key of keys) {
|
|
253
|
+
if (current == null || typeof current !== "object") {
|
|
254
|
+
return fallback;
|
|
255
|
+
}
|
|
256
|
+
current = current[key];
|
|
257
|
+
}
|
|
258
|
+
if (typeof current === "string") {
|
|
259
|
+
return current;
|
|
260
|
+
}
|
|
261
|
+
return fallback;
|
|
262
|
+
}
|
|
263
|
+
function interpolate(template, variables) {
|
|
264
|
+
if (!variables) {
|
|
265
|
+
return template;
|
|
266
|
+
}
|
|
267
|
+
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
268
|
+
let value = variables[key];
|
|
269
|
+
return value === void 0 ? match : String(value);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
var TranslationContext = react$1.createContext(null);
|
|
273
|
+
TranslationContext.displayName = "WeaverseNextTranslationContext";
|
|
274
|
+
function createTranslate(sources) {
|
|
275
|
+
let { staticContent, merchantOverrides, designOverrides, externalT } = sources;
|
|
276
|
+
return (key, variables) => {
|
|
277
|
+
if (externalT) {
|
|
278
|
+
let external = externalT(key, variables);
|
|
279
|
+
if (external && external !== key) {
|
|
280
|
+
return external;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
let design = designOverrides && Object.hasOwn(designOverrides, key) ? designOverrides[key] : void 0;
|
|
284
|
+
let override = merchantOverrides ? getNestedKey(merchantOverrides, key) : void 0;
|
|
285
|
+
let staticValue = getNestedKey(staticContent, key);
|
|
286
|
+
let raw = design ?? override ?? staticValue ?? key;
|
|
287
|
+
return interpolate(raw, variables);
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
var EMPTY = {};
|
|
291
|
+
var NOOP_SUBSCRIBE = () => () => {
|
|
292
|
+
};
|
|
293
|
+
var NOOP_SNAPSHOT = () => EMPTY;
|
|
294
|
+
function TranslationProvider({
|
|
295
|
+
staticContent,
|
|
296
|
+
merchantOverrides,
|
|
297
|
+
t: externalT,
|
|
298
|
+
translationStore,
|
|
299
|
+
themeTextStore,
|
|
300
|
+
children
|
|
301
|
+
}) {
|
|
302
|
+
let store = translationStore ?? themeTextStore ?? null;
|
|
303
|
+
let designOverrides = react.useSafeExternalStore(
|
|
304
|
+
store?.subscribe ?? NOOP_SUBSCRIBE,
|
|
305
|
+
store?.getSnapshot ?? NOOP_SNAPSHOT,
|
|
306
|
+
store?.getServerSnapshot ?? NOOP_SNAPSHOT
|
|
307
|
+
);
|
|
308
|
+
let value = react$1.useMemo(
|
|
309
|
+
() => ({
|
|
310
|
+
t: createTranslate({
|
|
311
|
+
staticContent,
|
|
312
|
+
merchantOverrides,
|
|
313
|
+
designOverrides,
|
|
314
|
+
externalT
|
|
315
|
+
}),
|
|
316
|
+
translationStore: store,
|
|
317
|
+
// Deprecated alias — same instance, kept for backward compatibility.
|
|
318
|
+
themeTextStore: store,
|
|
319
|
+
merchantOverrides: merchantOverrides ?? null
|
|
320
|
+
}),
|
|
321
|
+
[staticContent, merchantOverrides, designOverrides, externalT, store]
|
|
322
|
+
);
|
|
323
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TranslationContext.Provider, { value, children });
|
|
324
|
+
}
|
|
325
|
+
function useTranslation() {
|
|
326
|
+
let ctx = react$1.useContext(TranslationContext);
|
|
327
|
+
if (!ctx) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
"useTranslation must be used within <TranslationProvider>. Wrap your app with <WeaverseNextRootProvider>."
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
return ctx;
|
|
333
|
+
}
|
|
334
|
+
var ThemeTextProvider = TranslationProvider;
|
|
335
|
+
var useThemeText = useTranslation;
|
|
336
|
+
|
|
337
|
+
// src/translation-store.ts
|
|
338
|
+
var TranslationStore = class {
|
|
339
|
+
/** Current flat translation overrides keyed by dot path. */
|
|
340
|
+
overrides = {};
|
|
341
|
+
listeners = /* @__PURE__ */ new Set();
|
|
342
|
+
/** Replace every override and notify subscribers. */
|
|
343
|
+
setOverrides = (overrides) => {
|
|
344
|
+
this.overrides = { ...overrides };
|
|
345
|
+
this.emit();
|
|
346
|
+
};
|
|
347
|
+
/** Merge changed overrides into the current values and notify subscribers. */
|
|
348
|
+
updateOverrides = (newOverrides) => {
|
|
349
|
+
this.overrides = { ...this.overrides, ...newOverrides };
|
|
350
|
+
this.emit();
|
|
351
|
+
};
|
|
352
|
+
/** Return the current client-side override snapshot. */
|
|
353
|
+
getSnapshot = () => this.overrides;
|
|
354
|
+
/** Return the override snapshot used during server rendering. */
|
|
355
|
+
getServerSnapshot = () => this.overrides;
|
|
356
|
+
/** Subscribe to override changes and return an unsubscribe function. */
|
|
357
|
+
subscribe = (callback) => {
|
|
358
|
+
this.listeners.add(callback);
|
|
359
|
+
return () => {
|
|
360
|
+
this.listeners.delete(callback);
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
emit = () => {
|
|
364
|
+
for (const listener of Array.from(this.listeners)) {
|
|
365
|
+
try {
|
|
366
|
+
listener();
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error("TranslationStore: Listener error", error);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
};
|
|
373
|
+
var ThemeTextStore = TranslationStore;
|
|
374
|
+
var WeaverseNextRootContext = react$1.createContext(null);
|
|
375
|
+
function WeaverseNextRootProvider(props) {
|
|
376
|
+
let {
|
|
377
|
+
children,
|
|
378
|
+
initialThemeSettings,
|
|
379
|
+
merchantOverrides,
|
|
380
|
+
staticContent,
|
|
381
|
+
t,
|
|
382
|
+
themeSchema,
|
|
383
|
+
publicEnv
|
|
384
|
+
} = props;
|
|
385
|
+
let storeRef = react$1.useRef(null);
|
|
386
|
+
if (!storeRef.current) {
|
|
387
|
+
storeRef.current = createWeaverseNextThemeSettingsStore({
|
|
388
|
+
publicEnv,
|
|
389
|
+
schema: themeSchema,
|
|
390
|
+
settings: initialThemeSettings
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
let themeSettingsStore = storeRef.current;
|
|
394
|
+
let translationStoreRef = react$1.useRef(null);
|
|
395
|
+
if (!translationStoreRef.current) {
|
|
396
|
+
translationStoreRef.current = new TranslationStore();
|
|
397
|
+
}
|
|
398
|
+
let translationStore = translationStoreRef.current;
|
|
399
|
+
let appliedThemeSettingsRef = react$1.useRef(initialThemeSettings);
|
|
400
|
+
react$1.useEffect(() => {
|
|
401
|
+
if (initialThemeSettings && appliedThemeSettingsRef.current !== initialThemeSettings) {
|
|
402
|
+
themeSettingsStore.updateThemeSettings(initialThemeSettings);
|
|
403
|
+
appliedThemeSettingsRef.current = initialThemeSettings;
|
|
404
|
+
}
|
|
405
|
+
}, [initialThemeSettings, themeSettingsStore]);
|
|
406
|
+
let resolvedStaticContent = staticContent ?? getSchemaStaticContent(themeSchema);
|
|
407
|
+
let rootContextValue = react$1.useMemo(
|
|
408
|
+
() => ({
|
|
409
|
+
merchantOverrides,
|
|
410
|
+
staticContent: resolvedStaticContent,
|
|
411
|
+
themeSettingsStore,
|
|
412
|
+
translationStore
|
|
413
|
+
}),
|
|
414
|
+
[
|
|
415
|
+
merchantOverrides,
|
|
416
|
+
resolvedStaticContent,
|
|
417
|
+
themeSettingsStore,
|
|
418
|
+
translationStore
|
|
419
|
+
]
|
|
420
|
+
);
|
|
421
|
+
let contextValue = react$1.useMemo(
|
|
422
|
+
() => ({
|
|
423
|
+
merchantOverrides,
|
|
424
|
+
themeSettings: themeSettingsStore.getSnapshot(),
|
|
425
|
+
themeSettingsStore,
|
|
426
|
+
translationStore
|
|
427
|
+
}),
|
|
428
|
+
[merchantOverrides, themeSettingsStore, translationStore]
|
|
429
|
+
);
|
|
430
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextRootContext.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
431
|
+
TranslationProvider,
|
|
432
|
+
{
|
|
433
|
+
merchantOverrides,
|
|
434
|
+
staticContent: resolvedStaticContent,
|
|
435
|
+
t,
|
|
436
|
+
translationStore,
|
|
437
|
+
children
|
|
438
|
+
}
|
|
439
|
+
) }) });
|
|
440
|
+
}
|
|
161
441
|
var EMPTY_THEME_SETTINGS = {};
|
|
442
|
+
var EMPTY_STATIC_CONTENT = {};
|
|
443
|
+
function getSchemaStaticContent(schema) {
|
|
444
|
+
let i18n = schema?.i18n;
|
|
445
|
+
return i18n?.staticContent ?? EMPTY_STATIC_CONTENT;
|
|
446
|
+
}
|
|
162
447
|
var WeaverseNextContext = react$1.createContext(null);
|
|
163
448
|
function WeaverseNextProvider(props) {
|
|
164
449
|
let { children, client, rootData, pageData, commerce, themeSettings } = props;
|
|
450
|
+
let rootContext = react$1.useContext(WeaverseNextRootContext);
|
|
165
451
|
let themeSettingsValue = themeSettings ?? client?.themeSettings ?? EMPTY_THEME_SETTINGS;
|
|
166
|
-
let
|
|
167
|
-
|
|
452
|
+
let ownTranslationStoreRef = react$1.useRef(null);
|
|
453
|
+
if (!(rootContext || ownTranslationStoreRef.current)) {
|
|
454
|
+
ownTranslationStoreRef.current = new TranslationStore();
|
|
455
|
+
}
|
|
456
|
+
let translationStore = rootContext?.translationStore ?? ownTranslationStoreRef.current;
|
|
457
|
+
let staticContent = props.staticContent ?? rootContext?.staticContent ?? getSchemaStaticContent(client?.themeSchema);
|
|
458
|
+
let merchantOverrides = props.merchantOverrides ?? rootContext?.merchantOverrides;
|
|
459
|
+
let ownThemeSettingsStore = react$1.useMemo(() => {
|
|
460
|
+
if (rootContext) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
return createWeaverseNextThemeSettingsStore({
|
|
168
464
|
schema: client?.themeSchema,
|
|
169
465
|
settings: themeSettingsValue
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
|
|
466
|
+
});
|
|
467
|
+
}, [rootContext, client?.themeSchema, themeSettingsValue]);
|
|
468
|
+
let themeSettingsStore = rootContext?.themeSettingsStore ?? ownThemeSettingsStore;
|
|
469
|
+
let mergedIntoRootRef = react$1.useRef(void 0);
|
|
470
|
+
react$1.useEffect(() => {
|
|
471
|
+
if (rootContext && themeSettingsValue !== EMPTY_THEME_SETTINGS && mergedIntoRootRef.current !== themeSettingsValue) {
|
|
472
|
+
rootContext.themeSettingsStore.updateThemeSettings(themeSettingsValue);
|
|
473
|
+
mergedIntoRootRef.current = themeSettingsValue;
|
|
474
|
+
}
|
|
475
|
+
}, [rootContext, themeSettingsValue]);
|
|
173
476
|
let value = react$1.useMemo(
|
|
174
477
|
() => ({
|
|
175
478
|
client,
|
|
176
479
|
rootData,
|
|
177
480
|
pageData,
|
|
178
481
|
commerce: commerce ?? client?.commerce,
|
|
482
|
+
merchantOverrides,
|
|
179
483
|
themeSettings: themeSettingsStore.getSnapshot(),
|
|
180
|
-
themeSettingsStore
|
|
484
|
+
themeSettingsStore,
|
|
485
|
+
translationStore
|
|
181
486
|
}),
|
|
182
|
-
[
|
|
487
|
+
[
|
|
488
|
+
client,
|
|
489
|
+
rootData,
|
|
490
|
+
pageData,
|
|
491
|
+
commerce,
|
|
492
|
+
merchantOverrides,
|
|
493
|
+
themeSettingsStore,
|
|
494
|
+
translationStore
|
|
495
|
+
]
|
|
183
496
|
);
|
|
184
|
-
return /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextContext.Provider, { value, children
|
|
497
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WeaverseNextContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
498
|
+
TranslationProvider,
|
|
499
|
+
{
|
|
500
|
+
merchantOverrides,
|
|
501
|
+
staticContent,
|
|
502
|
+
t: props.t,
|
|
503
|
+
translationStore,
|
|
504
|
+
children
|
|
505
|
+
}
|
|
506
|
+
) });
|
|
185
507
|
}
|
|
186
508
|
|
|
187
509
|
// src/hooks.ts
|
|
@@ -302,13 +624,22 @@ function getSearchParamsFromContext(context) {
|
|
|
302
624
|
}
|
|
303
625
|
return new URLSearchParams();
|
|
304
626
|
}
|
|
627
|
+
function withPathPrefix(pathname, i18n) {
|
|
628
|
+
let raw = typeof i18n?.pathPrefix === "string" ? i18n.pathPrefix : "";
|
|
629
|
+
let segments = raw.split("/").filter(Boolean);
|
|
630
|
+
let prefix = segments.length > 0 ? `/${segments.join("/")}` : "";
|
|
631
|
+
if (prefix === "" || pathname === prefix || pathname.startsWith(`${prefix}/`)) {
|
|
632
|
+
return pathname;
|
|
633
|
+
}
|
|
634
|
+
return pathname === "/" ? prefix : `${prefix}${pathname}`;
|
|
635
|
+
}
|
|
305
636
|
function getPathnameFromContext(context) {
|
|
306
637
|
if (context?.pathname) {
|
|
307
|
-
return context.pathname;
|
|
638
|
+
return withPathPrefix(context.pathname, context.i18n);
|
|
308
639
|
}
|
|
309
640
|
if (context?.url) {
|
|
310
641
|
let url = toUrl(context.url);
|
|
311
|
-
return url.pathname || "/";
|
|
642
|
+
return withPathPrefix(url.pathname || "/", context.i18n);
|
|
312
643
|
}
|
|
313
644
|
return "/";
|
|
314
645
|
}
|
|
@@ -328,14 +659,30 @@ function toQueryRecord(searchParams) {
|
|
|
328
659
|
}
|
|
329
660
|
return queries;
|
|
330
661
|
}
|
|
662
|
+
var TRANSIENT_STUDIO_PARAMS = /* @__PURE__ */ new Set([
|
|
663
|
+
"weaverseDraftItem",
|
|
664
|
+
// Legacy name from the original design-mode draft-item spike. Keep it out of
|
|
665
|
+
// runtime requestInfo too, in case an older Builder bundle is in front of a
|
|
666
|
+
// newer Next storefront.
|
|
667
|
+
"__weaverseDraftItem"
|
|
668
|
+
]);
|
|
669
|
+
function getRequestInfoSearchParams(context) {
|
|
670
|
+
let searchParams = new URLSearchParams(getSearchParamsFromContext(context));
|
|
671
|
+
for (let key of TRANSIENT_STUDIO_PARAMS) {
|
|
672
|
+
searchParams.delete(key);
|
|
673
|
+
}
|
|
674
|
+
return searchParams;
|
|
675
|
+
}
|
|
331
676
|
function buildWeaverseNextRequestInfo(context) {
|
|
332
|
-
let searchParams =
|
|
677
|
+
let searchParams = getRequestInfoSearchParams(context);
|
|
333
678
|
let search = searchParams.toString();
|
|
334
679
|
return {
|
|
335
680
|
pathname: getPathnameFromContext(context),
|
|
336
681
|
search: search ? `?${search}` : "",
|
|
337
682
|
queries: toQueryRecord(searchParams),
|
|
338
|
-
...context?.i18n ? { i18n: context.i18n } : {}
|
|
683
|
+
...context?.i18n ? { i18n: context.i18n } : {},
|
|
684
|
+
...context?.pageType ? { pageType: context.pageType } : {},
|
|
685
|
+
...context?.handle ? { handle: context.handle } : {}
|
|
339
686
|
};
|
|
340
687
|
}
|
|
341
688
|
|
|
@@ -352,6 +699,12 @@ function getConfigString(configs, key) {
|
|
|
352
699
|
let value = configs?.[key];
|
|
353
700
|
return typeof value === "string" ? value : void 0;
|
|
354
701
|
}
|
|
702
|
+
function resolveModeFlag(config, key) {
|
|
703
|
+
return config.client?.requestContext?.[key] === true || config.data.configs?.[key] === true;
|
|
704
|
+
}
|
|
705
|
+
function resolveSectionType(config) {
|
|
706
|
+
return config.client?.requestContext?.sectionType || getConfigString(config.data.configs, "sectionType") || void 0;
|
|
707
|
+
}
|
|
355
708
|
function getRecordString(record, key) {
|
|
356
709
|
let value = record?.[key];
|
|
357
710
|
return typeof value === "string" ? value : void 0;
|
|
@@ -388,38 +741,87 @@ function registerRuntime(runtime) {
|
|
|
388
741
|
runtimeWindow.__weaverses[runtime.pageId] = runtime;
|
|
389
742
|
runtimeWindow.__weaverse = runtime;
|
|
390
743
|
}
|
|
744
|
+
function rebindPageItemsToRuntime(runtime) {
|
|
745
|
+
let items = runtime.data?.items;
|
|
746
|
+
if (!items) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
for (let { id } of items) {
|
|
750
|
+
let instance = runtime.itemInstances.get(id);
|
|
751
|
+
if (instance && instance.weaverse !== runtime) {
|
|
752
|
+
instance.weaverse = runtime;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
391
756
|
var WeaverseNextRuntime = class extends react.Weaverse {
|
|
757
|
+
/** Stable ID of the page owned by this runtime. */
|
|
392
758
|
pageId;
|
|
759
|
+
/** Mutable callbacks and stores consumed by the Studio bridge. */
|
|
393
760
|
internal;
|
|
761
|
+
/** Path, query, and locale metadata for Studio navigation. */
|
|
394
762
|
requestInfo;
|
|
763
|
+
/** Whether this runtime renders a component preview. */
|
|
395
764
|
isPreviewMode;
|
|
765
|
+
/** Whether this runtime renders a saved revision. */
|
|
396
766
|
isRevisionPreview;
|
|
767
|
+
/** Component type rendered in section preview mode. */
|
|
397
768
|
sectionType;
|
|
769
|
+
// ─── Item-level translation sidecar (design mode only) ─────────────
|
|
770
|
+
/**
|
|
771
|
+
* Translation map received from Builder in design mode, keyed by item ID.
|
|
772
|
+
* `WeaverseNextItem.getSnapShot()` overlays these onto the data handed to
|
|
773
|
+
* components, so translations render without mutating the base `_store`.
|
|
774
|
+
*/
|
|
398
775
|
translationMap = {};
|
|
399
|
-
|
|
400
|
-
|
|
776
|
+
/** Locale associated with the item-level translation sidecar. */
|
|
777
|
+
translationLocale = String();
|
|
778
|
+
/** Weaverse language ID used when saving translation changes. */
|
|
779
|
+
translationLanguageId = String();
|
|
780
|
+
/**
|
|
781
|
+
* Fresh page data captured while React was rendering. Runtime reuse happens
|
|
782
|
+
* inside the renderer's render phase (`useMemo`), where applying it through
|
|
783
|
+
* `setProjectData()` would emit item updates to `useSyncExternalStore`
|
|
784
|
+
* subscribers mid-render. The renderer applies it post-commit via
|
|
785
|
+
* `flushRenderPhaseUpdates()`. Never set on design-mode runtimes — the live
|
|
786
|
+
* Studio tree owns the page data there.
|
|
787
|
+
*/
|
|
788
|
+
pendingProjectData;
|
|
789
|
+
/**
|
|
790
|
+
* Reused item instances whose data was refreshed while this runtime was
|
|
791
|
+
* constructed during render (core `initProject()` calls `setData()` on
|
|
792
|
+
* them). Their data is already fresh; only the subscriber notification was
|
|
793
|
+
* deferred to `flushRenderPhaseUpdates()`.
|
|
794
|
+
*/
|
|
795
|
+
pendingItemUpdates = [];
|
|
796
|
+
/** Create a browser runtime from a loaded page and its request context. */
|
|
401
797
|
constructor(config) {
|
|
402
798
|
let { client, data } = config;
|
|
403
799
|
let page = getRenderablePage(data);
|
|
404
800
|
let configs = data.configs;
|
|
405
801
|
let requestContext = client?.requestContext;
|
|
802
|
+
let isDesignMode = resolveModeFlag(config, "isDesignMode");
|
|
803
|
+
let isPreviewMode = resolveModeFlag(config, "isPreviewMode");
|
|
804
|
+
let isRevisionPreview = resolveModeFlag(config, "isRevisionPreview");
|
|
805
|
+
let sectionType = resolveSectionType(config);
|
|
406
806
|
let projectId = resolveProjectId(client, data, page.id);
|
|
407
807
|
ensureNextItemConstructor();
|
|
408
808
|
super({
|
|
409
809
|
projectId,
|
|
410
810
|
data: page,
|
|
411
|
-
isDesignMode
|
|
811
|
+
isDesignMode,
|
|
412
812
|
weaverseHost: getConfigString(configs, "weaverseHost"),
|
|
413
813
|
weaverseVersion: getConfigString(configs, "weaverseVersion")
|
|
414
814
|
});
|
|
415
815
|
this.pageId = page.id;
|
|
416
816
|
this.dataContext = resolveDataContext(config);
|
|
417
817
|
this.requestInfo = buildWeaverseNextRequestInfo(requestContext);
|
|
418
|
-
this.isDesignMode =
|
|
419
|
-
this.isPreviewMode =
|
|
420
|
-
this.isRevisionPreview =
|
|
421
|
-
this.sectionType =
|
|
818
|
+
this.isDesignMode = isDesignMode;
|
|
819
|
+
this.isPreviewMode = isPreviewMode;
|
|
820
|
+
this.isRevisionPreview = isRevisionPreview;
|
|
821
|
+
this.sectionType = sectionType;
|
|
822
|
+
let translationStore = config.translationStore ?? new TranslationStore();
|
|
422
823
|
this.internal = {
|
|
824
|
+
merchantOverrides: config.merchantOverrides,
|
|
423
825
|
navigate: config.navigate,
|
|
424
826
|
pageAssignment: data.pageAssignment,
|
|
425
827
|
project: data.project,
|
|
@@ -427,17 +829,119 @@ var WeaverseNextRuntime = class extends react.Weaverse {
|
|
|
427
829
|
themeSettingsStore: config.themeSettingsStore ?? createWeaverseNextThemeSettingsStore({
|
|
428
830
|
schema: client?.themeSchema,
|
|
429
831
|
settings: client?.themeSettings
|
|
430
|
-
})
|
|
832
|
+
}),
|
|
833
|
+
translationStore,
|
|
834
|
+
themeTextStore: translationStore
|
|
431
835
|
};
|
|
836
|
+
this.extractTranslationSidecar();
|
|
432
837
|
}
|
|
433
|
-
|
|
838
|
+
/**
|
|
839
|
+
* Pull `translationMap` / `translationLocale` / `translationLanguageId` off
|
|
840
|
+
* the current page data (Builder attaches them in design mode). Unlike
|
|
841
|
+
* Hydrogen, this also clears the sidecar to defaults when the new page data
|
|
842
|
+
* carries none, so a reused runtime / project-data swap can't leave a stale
|
|
843
|
+
* map overlaying the fresh tree.
|
|
844
|
+
*/
|
|
845
|
+
extractTranslationSidecar = () => {
|
|
846
|
+
let pageData = this.data;
|
|
847
|
+
let translationMap = pageData?.translationMap;
|
|
848
|
+
if (translationMap) {
|
|
849
|
+
this.translationMap = translationMap;
|
|
850
|
+
this.translationLocale = pageData?.translationLocale || "";
|
|
851
|
+
this.translationLanguageId = pageData?.translationLanguageId || "";
|
|
852
|
+
} else {
|
|
853
|
+
this.translationMap = {};
|
|
854
|
+
this.translationLocale = "";
|
|
855
|
+
this.translationLanguageId = "";
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
/**
|
|
859
|
+
* Replace the translation sidecar (Builder pushes this when translation data
|
|
860
|
+
* arrives or the active language changes) and refresh every item so their
|
|
861
|
+
* merged snapshots recompute.
|
|
862
|
+
*/
|
|
434
863
|
setTranslationSidecar = (map = {}, locale = "", languageId = "") => {
|
|
435
864
|
this.translationMap = map;
|
|
436
865
|
this.translationLocale = locale;
|
|
437
866
|
this.translationLanguageId = languageId;
|
|
867
|
+
this.refreshAllItems();
|
|
868
|
+
};
|
|
869
|
+
/**
|
|
870
|
+
* Update a single translatable field for one item. Assigns a new per-item
|
|
871
|
+
* entry object so `WeaverseNextItem.getSnapShot()`'s translation-ref cache
|
|
872
|
+
* invalidates, then triggers only that item's subscribers.
|
|
873
|
+
*/
|
|
874
|
+
updateTranslation = (itemId, key, originalValue, translatedValue) => {
|
|
875
|
+
if (!this.translationMap[itemId]) {
|
|
876
|
+
this.translationMap[itemId] = {};
|
|
877
|
+
}
|
|
878
|
+
this.translationMap[itemId] = {
|
|
879
|
+
...this.translationMap[itemId],
|
|
880
|
+
[key]: { originalValue, translatedValue }
|
|
881
|
+
};
|
|
882
|
+
let instance = this.itemInstances.get(itemId);
|
|
883
|
+
if (instance) {
|
|
884
|
+
instance.triggerUpdate();
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* Collect translation changes for the save flow. Returns `undefined` unless
|
|
889
|
+
* both the locale and language ID are set and at least one entry exists;
|
|
890
|
+
* each entry key is namespaced as `data.<field>` to match Builder.
|
|
891
|
+
*/
|
|
892
|
+
getTranslationChanges = () => {
|
|
893
|
+
let languageId = this.translationLanguageId;
|
|
894
|
+
if (!(this.translationLocale && languageId)) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
let entries = [];
|
|
898
|
+
for (let [itemId, fields] of Object.entries(this.translationMap)) {
|
|
899
|
+
for (let [key, entry] of Object.entries(fields)) {
|
|
900
|
+
entries.push({
|
|
901
|
+
itemId,
|
|
902
|
+
key: `data.${key}`,
|
|
903
|
+
originalValue: entry.originalValue,
|
|
904
|
+
translatedValue: entry.translatedValue
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (entries.length === 0) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
return { languageId, entries };
|
|
912
|
+
};
|
|
913
|
+
/**
|
|
914
|
+
* Re-extract the translation sidecar whenever project data is replaced (route
|
|
915
|
+
* change / non-design reuse), before rebuilding item stores. Takes the
|
|
916
|
+
* renderable page shape (`rootId` resolved) that `getRenderablePage()` and
|
|
917
|
+
* the reuse branch pass in.
|
|
918
|
+
*/
|
|
919
|
+
setProjectData = (data) => {
|
|
920
|
+
this.data = data;
|
|
921
|
+
this.extractTranslationSidecar();
|
|
922
|
+
this.initProject();
|
|
923
|
+
};
|
|
924
|
+
/**
|
|
925
|
+
* Apply updates deferred off the render phase: swap in any pending page data
|
|
926
|
+
* captured on runtime reuse, then notify items whose data was refreshed
|
|
927
|
+
* during construction. The renderer calls this from a layout effect right
|
|
928
|
+
* after commit, so subscribers re-render before paint (no content flash) and
|
|
929
|
+
* without React's setState-in-render warning.
|
|
930
|
+
*/
|
|
931
|
+
flushRenderPhaseUpdates = () => {
|
|
932
|
+
let pendingData = this.pendingProjectData;
|
|
933
|
+
if (pendingData) {
|
|
934
|
+
this.pendingProjectData = void 0;
|
|
935
|
+
this.setProjectData(pendingData);
|
|
936
|
+
}
|
|
937
|
+
let pendingItems = this.pendingItemUpdates;
|
|
938
|
+
if (pendingItems.length > 0) {
|
|
939
|
+
this.pendingItemUpdates = [];
|
|
940
|
+
for (let item of pendingItems) {
|
|
941
|
+
item.triggerUpdate();
|
|
942
|
+
}
|
|
943
|
+
}
|
|
438
944
|
};
|
|
439
|
-
updateTranslation = () => void 0;
|
|
440
|
-
getTranslationChanges = () => void 0;
|
|
441
945
|
};
|
|
442
946
|
function createWeaverseNextRuntime(config) {
|
|
443
947
|
let requestInfo = buildWeaverseNextRequestInfo(config.client?.requestContext);
|
|
@@ -446,19 +950,26 @@ function createWeaverseNextRuntime(config) {
|
|
|
446
950
|
let existing = runtimeWindow?.__weaverses?.[page.id];
|
|
447
951
|
let requestKey = getRuntimeKey(page.id, requestInfo);
|
|
448
952
|
if (existing?.__weaverseNextRequestKey === requestKey) {
|
|
449
|
-
let
|
|
450
|
-
|
|
451
|
-
|
|
953
|
+
let configs = config.data.configs;
|
|
954
|
+
let nextIsDesignMode = resolveModeFlag(config, "isDesignMode");
|
|
955
|
+
let nextIsPreviewMode = resolveModeFlag(config, "isPreviewMode");
|
|
956
|
+
let nextIsRevisionPreview = resolveModeFlag(config, "isRevisionPreview");
|
|
957
|
+
let nextSectionType = resolveSectionType(config);
|
|
958
|
+
if (existing.isDesignMode || nextIsDesignMode) {
|
|
959
|
+
existing.pendingProjectData = void 0;
|
|
960
|
+
} else {
|
|
961
|
+
existing.pendingProjectData = page;
|
|
452
962
|
}
|
|
963
|
+
existing.__weaverseNextLatestData = page;
|
|
453
964
|
existing.dataContext = resolveDataContext(config);
|
|
454
965
|
existing.requestInfo = requestInfo;
|
|
455
966
|
existing.projectId = resolveProjectId(config.client, config.data, page.id);
|
|
456
967
|
existing.isDesignMode = nextIsDesignMode;
|
|
457
|
-
existing.isPreviewMode =
|
|
458
|
-
existing.isRevisionPreview =
|
|
459
|
-
existing.sectionType =
|
|
460
|
-
existing.weaverseHost = getConfigString(
|
|
461
|
-
existing.weaverseVersion = getConfigString(
|
|
968
|
+
existing.isPreviewMode = nextIsPreviewMode;
|
|
969
|
+
existing.isRevisionPreview = nextIsRevisionPreview;
|
|
970
|
+
existing.sectionType = nextSectionType;
|
|
971
|
+
existing.weaverseHost = getConfigString(configs, "weaverseHost") ?? existing.weaverseHost;
|
|
972
|
+
existing.weaverseVersion = getConfigString(configs, "weaverseVersion") ?? existing.weaverseVersion;
|
|
462
973
|
existing.internal.pageAssignment = config.data.pageAssignment;
|
|
463
974
|
existing.internal.project = config.data.project;
|
|
464
975
|
if (config.navigate) {
|
|
@@ -470,11 +981,21 @@ function createWeaverseNextRuntime(config) {
|
|
|
470
981
|
if (config.themeSettingsStore) {
|
|
471
982
|
existing.internal.themeSettingsStore = config.themeSettingsStore;
|
|
472
983
|
}
|
|
984
|
+
if (config.translationStore) {
|
|
985
|
+
existing.internal.translationStore = config.translationStore;
|
|
986
|
+
existing.internal.themeTextStore = config.translationStore;
|
|
987
|
+
}
|
|
988
|
+
existing.internal.merchantOverrides = config.merchantOverrides;
|
|
473
989
|
registerRuntime(existing);
|
|
474
990
|
return existing;
|
|
475
991
|
}
|
|
476
|
-
let runtime =
|
|
992
|
+
let [runtime, refreshedItems] = collectDeferredItemUpdates(
|
|
993
|
+
() => new WeaverseNextRuntime(config)
|
|
994
|
+
);
|
|
995
|
+
runtime.pendingItemUpdates = refreshedItems;
|
|
996
|
+
rebindPageItemsToRuntime(runtime);
|
|
477
997
|
runtime.__weaverseNextRequestKey = requestKey;
|
|
998
|
+
runtime.__weaverseNextLatestData = page;
|
|
478
999
|
registerRuntime(runtime);
|
|
479
1000
|
return runtime;
|
|
480
1001
|
}
|
|
@@ -487,22 +1008,110 @@ function bindWeaverseNextStudioRuntime(runtime) {
|
|
|
487
1008
|
return false;
|
|
488
1009
|
}
|
|
489
1010
|
let boundRuntime = runtime;
|
|
490
|
-
|
|
1011
|
+
let activeRuntime = studio.weaverse;
|
|
1012
|
+
let activeRequestInfo = activeRuntime?.requestInfo;
|
|
1013
|
+
let isActiveUrl = !activeRequestInfo || activeRequestInfo.pathname === runtime.requestInfo.pathname && activeRequestInfo.search === runtime.requestInfo.search;
|
|
1014
|
+
if (!(boundRuntime.__weaverseNextStudioBound && isActiveUrl)) {
|
|
491
1015
|
studio.init?.(runtime);
|
|
492
1016
|
boundRuntime.__weaverseNextStudioBound = true;
|
|
493
1017
|
return true;
|
|
494
1018
|
}
|
|
495
1019
|
studio.refreshStudio?.({
|
|
496
|
-
data: runtime.data,
|
|
1020
|
+
data: boundRuntime.__weaverseNextLatestData ?? runtime.data,
|
|
497
1021
|
pageId: runtime.pageId,
|
|
498
1022
|
requestInfo: runtime.requestInfo
|
|
499
1023
|
});
|
|
500
1024
|
return true;
|
|
501
1025
|
}
|
|
1026
|
+
var DEFAULT_REVALIDATE_ENDPOINT = "/api/weaverse/revalidate";
|
|
1027
|
+
var MAX_ROUTE_CONTEXT_HANDLE_LENGTH = 512;
|
|
1028
|
+
var DENIED_ROUTE_CONTEXT_PARAMS = [
|
|
1029
|
+
"weaverseprojectid",
|
|
1030
|
+
"weaversehost",
|
|
1031
|
+
"weaverseapikey",
|
|
1032
|
+
"weaverseapibase",
|
|
1033
|
+
"weaversepublicapibase",
|
|
1034
|
+
"weaverseversion",
|
|
1035
|
+
"projectid",
|
|
1036
|
+
"weaversedraftitem",
|
|
1037
|
+
"__weaversedraftitem",
|
|
1038
|
+
"_rsc"
|
|
1039
|
+
];
|
|
1040
|
+
var ROUTE_CONTEXT_I18N_KEYS = [
|
|
1041
|
+
"country",
|
|
1042
|
+
"label",
|
|
1043
|
+
"language",
|
|
1044
|
+
"locale",
|
|
1045
|
+
"pathPrefix"
|
|
1046
|
+
];
|
|
1047
|
+
function sanitizeRouteContextSearch(search) {
|
|
1048
|
+
let params = new URLSearchParams(search);
|
|
1049
|
+
let denied = new Set(DENIED_ROUTE_CONTEXT_PARAMS);
|
|
1050
|
+
for (let key of [...new Set(params.keys())]) {
|
|
1051
|
+
if (denied.has(key.toLowerCase())) {
|
|
1052
|
+
params.delete(key);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
let serialized = params.toString();
|
|
1056
|
+
return serialized ? `?${serialized}` : "";
|
|
1057
|
+
}
|
|
1058
|
+
function buildWeaverseNextRevalidateRouteContext(requestInfo) {
|
|
1059
|
+
if (!requestInfo || typeof requestInfo.pathname !== "string" || !requestInfo.pathname) {
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
let routeContext = {
|
|
1063
|
+
pathname: requestInfo.pathname,
|
|
1064
|
+
search: sanitizeRouteContextSearch(requestInfo.search ?? "")
|
|
1065
|
+
};
|
|
1066
|
+
let { i18n } = requestInfo;
|
|
1067
|
+
if (i18n && typeof i18n === "object") {
|
|
1068
|
+
let picked = {};
|
|
1069
|
+
for (let key of ROUTE_CONTEXT_I18N_KEYS) {
|
|
1070
|
+
let value = i18n[key];
|
|
1071
|
+
if (typeof value === "string") {
|
|
1072
|
+
picked[key] = value;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (Object.keys(picked).length) {
|
|
1076
|
+
routeContext.i18n = picked;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (requestInfo.pageType && schema.PageTypeSchema.safeParse(requestInfo.pageType).success) {
|
|
1080
|
+
routeContext.pageType = requestInfo.pageType;
|
|
1081
|
+
}
|
|
1082
|
+
if (typeof requestInfo.handle === "string" && requestInfo.handle && requestInfo.handle.length <= MAX_ROUTE_CONTEXT_HANDLE_LENGTH) {
|
|
1083
|
+
routeContext.handle = requestInfo.handle;
|
|
1084
|
+
}
|
|
1085
|
+
return routeContext;
|
|
1086
|
+
}
|
|
1087
|
+
async function revalidateWeaverseNextItem(runtime, draftItem, endpoint = DEFAULT_REVALIDATE_ENDPOINT) {
|
|
1088
|
+
let instance = runtime.itemInstances.get(draftItem.id);
|
|
1089
|
+
if (!instance) {
|
|
1090
|
+
throw new Error(
|
|
1091
|
+
`[weaverse-next] No live item instance for "${draftItem.id}".`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
let routeContext = buildWeaverseNextRevalidateRouteContext(
|
|
1095
|
+
runtime.requestInfo
|
|
1096
|
+
);
|
|
1097
|
+
let body = routeContext ? { draftItem, routeContext } : { draftItem };
|
|
1098
|
+
let response = await fetch(endpoint, {
|
|
1099
|
+
method: "POST",
|
|
1100
|
+
headers: { "Content-Type": "application/json" },
|
|
1101
|
+
body: JSON.stringify(body)
|
|
1102
|
+
});
|
|
1103
|
+
if (!response.ok) {
|
|
1104
|
+
throw new Error(
|
|
1105
|
+
`[weaverse-next] Revalidate endpoint responded ${response.status}.`
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
let { loaderData } = await response.json();
|
|
1109
|
+
instance.setData({ loaderData: loaderData ?? null });
|
|
1110
|
+
}
|
|
502
1111
|
var STUDIO_BIND_RETRY_INTERVAL_MS = 50;
|
|
503
1112
|
var STUDIO_BIND_MAX_ATTEMPTS = 100;
|
|
504
1113
|
function WeaverseNextStudioBridge(props) {
|
|
505
|
-
let { runtime, navigate, revalidate } = props;
|
|
1114
|
+
let { runtime, navigate, revalidate, revalidateItem } = props;
|
|
506
1115
|
let lastRuntimeRef = react$1.useRef(runtime);
|
|
507
1116
|
let lastRuntimeDataRef = react$1.useRef(runtime.data);
|
|
508
1117
|
let lastRequestInfoRef = react$1.useRef(runtime.requestInfo);
|
|
@@ -513,6 +1122,9 @@ function WeaverseNextStudioBridge(props) {
|
|
|
513
1122
|
if (revalidate) {
|
|
514
1123
|
runtime.internal.revalidate = revalidate;
|
|
515
1124
|
}
|
|
1125
|
+
if (revalidateItem) {
|
|
1126
|
+
runtime.internal.revalidateItem = revalidateItem;
|
|
1127
|
+
}
|
|
516
1128
|
if (bindWeaverseNextStudioRuntime(runtime) || !runtime.isDesignMode) {
|
|
517
1129
|
return;
|
|
518
1130
|
}
|
|
@@ -524,7 +1136,7 @@ function WeaverseNextStudioBridge(props) {
|
|
|
524
1136
|
}
|
|
525
1137
|
}, STUDIO_BIND_RETRY_INTERVAL_MS);
|
|
526
1138
|
return () => window.clearInterval(interval);
|
|
527
|
-
}, [runtime, navigate, revalidate]);
|
|
1139
|
+
}, [runtime, navigate, revalidate, revalidateItem]);
|
|
528
1140
|
react$1.useEffect(() => {
|
|
529
1141
|
if (lastRuntimeRef.current !== runtime) {
|
|
530
1142
|
lastRuntimeRef.current = runtime;
|
|
@@ -577,18 +1189,29 @@ function useWeaverseNextStudioInternals(options) {
|
|
|
577
1189
|
return ref.current.internals;
|
|
578
1190
|
}
|
|
579
1191
|
function WeaverseNextStudio(props) {
|
|
580
|
-
let { runtime, replace } = props;
|
|
1192
|
+
let { runtime, replace, revalidateEndpoint } = props;
|
|
581
1193
|
let { navigate, revalidate } = useWeaverseNextStudioInternals({ replace });
|
|
1194
|
+
let endpoint = revalidateEndpoint ?? DEFAULT_REVALIDATE_ENDPOINT;
|
|
1195
|
+
let revalidateItemRef = react$1.useRef(null);
|
|
1196
|
+
if (revalidateItemRef.current?.endpoint !== endpoint || revalidateItemRef.current.runtime !== runtime) {
|
|
1197
|
+
revalidateItemRef.current = {
|
|
1198
|
+
endpoint,
|
|
1199
|
+
revalidateItem: (draftItem) => revalidateWeaverseNextItem(runtime, draftItem, endpoint),
|
|
1200
|
+
runtime
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
582
1203
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
583
1204
|
WeaverseNextStudioBridge,
|
|
584
1205
|
{
|
|
585
1206
|
navigate,
|
|
586
1207
|
revalidate,
|
|
1208
|
+
revalidateItem: revalidateItemRef.current.revalidateItem,
|
|
587
1209
|
runtime
|
|
588
1210
|
}
|
|
589
1211
|
);
|
|
590
1212
|
}
|
|
591
1213
|
var EMPTY_DATA_CONTEXT = {};
|
|
1214
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? react$1.useEffect : react$1.useLayoutEffect;
|
|
592
1215
|
function getRenderablePage2(data) {
|
|
593
1216
|
let page = data?.page;
|
|
594
1217
|
if (!page) {
|
|
@@ -611,9 +1234,22 @@ var WeaverseNextRenderer = react$1.memo(function WeaverseNextRendererComponent(p
|
|
|
611
1234
|
client,
|
|
612
1235
|
data: { ...data, page },
|
|
613
1236
|
dataContext,
|
|
614
|
-
|
|
1237
|
+
merchantOverrides: context?.merchantOverrides,
|
|
1238
|
+
themeSettingsStore: context?.themeSettingsStore,
|
|
1239
|
+
translationStore: context?.translationStore
|
|
615
1240
|
});
|
|
616
|
-
}, [
|
|
1241
|
+
}, [
|
|
1242
|
+
client,
|
|
1243
|
+
page,
|
|
1244
|
+
data,
|
|
1245
|
+
dataContext,
|
|
1246
|
+
context?.merchantOverrides,
|
|
1247
|
+
context?.themeSettingsStore,
|
|
1248
|
+
context?.translationStore
|
|
1249
|
+
]);
|
|
1250
|
+
useIsomorphicLayoutEffect(() => {
|
|
1251
|
+
weaverse?.flushRenderPhaseUpdates();
|
|
1252
|
+
}, [weaverse]);
|
|
617
1253
|
if (!weaverse) {
|
|
618
1254
|
return null;
|
|
619
1255
|
}
|
|
@@ -623,6 +1259,71 @@ var WeaverseNextRenderer = react$1.memo(function WeaverseNextRendererComponent(p
|
|
|
623
1259
|
] });
|
|
624
1260
|
});
|
|
625
1261
|
|
|
1262
|
+
// src/seo.ts
|
|
1263
|
+
function hasOpenGraphContent(seo) {
|
|
1264
|
+
return Boolean(seo?.title || seo?.description || seo?.image || seo?.type);
|
|
1265
|
+
}
|
|
1266
|
+
function hasTwitterContent(seo) {
|
|
1267
|
+
return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType);
|
|
1268
|
+
}
|
|
1269
|
+
function normalizeTwitterCard(cardType) {
|
|
1270
|
+
return cardType === "summary_large_image" ? "summary_large_image" : "summary";
|
|
1271
|
+
}
|
|
1272
|
+
function normalizeOpenGraphType(type) {
|
|
1273
|
+
if (type === "article" || type === "profile" || type === "video.other") {
|
|
1274
|
+
return type;
|
|
1275
|
+
}
|
|
1276
|
+
return "website";
|
|
1277
|
+
}
|
|
1278
|
+
function formatWeaverseNextSeoMetadata(seo) {
|
|
1279
|
+
let index = seo?.robots?.index !== false;
|
|
1280
|
+
let follow = seo?.robots?.follow !== false;
|
|
1281
|
+
let metadata = {
|
|
1282
|
+
robots: { index, follow }
|
|
1283
|
+
};
|
|
1284
|
+
if (!seo) {
|
|
1285
|
+
return metadata;
|
|
1286
|
+
}
|
|
1287
|
+
if (seo.title) {
|
|
1288
|
+
metadata.title = seo.title;
|
|
1289
|
+
}
|
|
1290
|
+
if (seo.description) {
|
|
1291
|
+
metadata.description = seo.description;
|
|
1292
|
+
}
|
|
1293
|
+
if (seo.keywords) {
|
|
1294
|
+
metadata.keywords = seo.keywords;
|
|
1295
|
+
}
|
|
1296
|
+
if (seo.canonicalUrl) {
|
|
1297
|
+
metadata.alternates = { canonical: seo.canonicalUrl };
|
|
1298
|
+
}
|
|
1299
|
+
let openGraph = seo.openGraph;
|
|
1300
|
+
if (hasOpenGraphContent(openGraph)) {
|
|
1301
|
+
metadata.openGraph = {
|
|
1302
|
+
title: openGraph?.title,
|
|
1303
|
+
description: openGraph?.description,
|
|
1304
|
+
images: openGraph?.image ? [openGraph.image] : void 0,
|
|
1305
|
+
type: normalizeOpenGraphType(openGraph?.type)
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
let twitter = seo.twitter;
|
|
1309
|
+
if (hasTwitterContent(twitter)) {
|
|
1310
|
+
metadata.twitter = {
|
|
1311
|
+
card: normalizeTwitterCard(twitter?.cardType),
|
|
1312
|
+
title: twitter?.title,
|
|
1313
|
+
description: twitter?.description,
|
|
1314
|
+
images: twitter?.image ? [twitter.image] : void 0
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
return metadata;
|
|
1318
|
+
}
|
|
1319
|
+
function getWeaverseNextSeoMetadata(data) {
|
|
1320
|
+
if (!data) {
|
|
1321
|
+
return formatWeaverseNextSeoMetadata(null);
|
|
1322
|
+
}
|
|
1323
|
+
let page = "items" in data && "id" in data ? data : data.page ?? null;
|
|
1324
|
+
return formatWeaverseNextSeoMetadata(page?.seo ?? null);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
626
1327
|
// src/studio-script-src.ts
|
|
627
1328
|
var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
|
|
628
1329
|
function isLoopbackHostname(hostname) {
|
|
@@ -686,10 +1387,9 @@ function resolveWeaverseNextStudioScriptSrc(context, options = {}) {
|
|
|
686
1387
|
if (!isTrustedStudioOrigin(origin, storefrontHostname)) {
|
|
687
1388
|
return null;
|
|
688
1389
|
}
|
|
689
|
-
let
|
|
690
|
-
let bundle = isDesignMode ? "index.js" : "preview.js";
|
|
1390
|
+
let bundle = isPreviewMode || isRevisionPreview ? "preview.js" : "index.js";
|
|
691
1391
|
let version = last(searchParams, "weaverseVersion");
|
|
692
|
-
let url = `${origin}/static/studio/${
|
|
1392
|
+
let url = `${origin}/static/studio/next/${bundle}`;
|
|
693
1393
|
return version ? `${url}?v=${encodeURIComponent(version)}` : url;
|
|
694
1394
|
}
|
|
695
1395
|
|
|
@@ -713,21 +1413,26 @@ function loadScript(src) {
|
|
|
713
1413
|
document.head.appendChild(script);
|
|
714
1414
|
loadedScriptSrcs.add(src);
|
|
715
1415
|
}
|
|
1416
|
+
function loadWeaverseNextStudioScript(context, options = {}) {
|
|
1417
|
+
let resolvedContext = context;
|
|
1418
|
+
if (!resolvedContext && typeof window !== "undefined") {
|
|
1419
|
+
resolvedContext = {
|
|
1420
|
+
searchParams: new URLSearchParams(window.location.search),
|
|
1421
|
+
url: window.location.href
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
let src = resolveWeaverseNextStudioScriptSrc(resolvedContext, {
|
|
1425
|
+
storefrontHostname: options.storefrontHostname ?? (typeof window === "undefined" ? void 0 : window.location.hostname)
|
|
1426
|
+
});
|
|
1427
|
+
if (src) {
|
|
1428
|
+
loadScript(src);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
716
1431
|
function WeaverseNextStudioConnect(props) {
|
|
717
1432
|
react$1.useEffect(() => {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
context = {
|
|
721
|
-
searchParams: new URLSearchParams(window.location.search),
|
|
722
|
-
url: window.location.href
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
let src = resolveWeaverseNextStudioScriptSrc(context, {
|
|
726
|
-
storefrontHostname: props.storefrontHostname ?? (typeof window === "undefined" ? void 0 : window.location.hostname)
|
|
1433
|
+
loadWeaverseNextStudioScript(props.context, {
|
|
1434
|
+
storefrontHostname: props.storefrontHostname
|
|
727
1435
|
});
|
|
728
|
-
if (src) {
|
|
729
|
-
loadScript(src);
|
|
730
|
-
}
|
|
731
1436
|
}, [props.context, props.storefrontHostname]);
|
|
732
1437
|
return null;
|
|
733
1438
|
}
|
|
@@ -756,28 +1461,47 @@ Object.defineProperty(exports, "useWeaverse", {
|
|
|
756
1461
|
enumerable: true,
|
|
757
1462
|
get: function () { return react.useWeaverse; }
|
|
758
1463
|
});
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
1464
|
+
exports.DEFAULT_REVALIDATE_ENDPOINT = DEFAULT_REVALIDATE_ENDPOINT;
|
|
1465
|
+
exports.ThemeTextProvider = ThemeTextProvider;
|
|
1466
|
+
exports.ThemeTextStore = ThemeTextStore;
|
|
1467
|
+
exports.TranslationProvider = TranslationProvider;
|
|
1468
|
+
exports.TranslationStore = TranslationStore;
|
|
763
1469
|
exports.WeaverseNextProvider = WeaverseNextProvider;
|
|
764
1470
|
exports.WeaverseNextRenderer = WeaverseNextRenderer;
|
|
1471
|
+
exports.WeaverseNextRootProvider = WeaverseNextRootProvider;
|
|
765
1472
|
exports.WeaverseNextRuntime = WeaverseNextRuntime;
|
|
766
1473
|
exports.WeaverseNextStudio = WeaverseNextStudio;
|
|
767
1474
|
exports.WeaverseNextStudioBridge = WeaverseNextStudioBridge;
|
|
768
1475
|
exports.WeaverseNextStudioConnect = WeaverseNextStudioConnect;
|
|
769
1476
|
exports.bindWeaverseNextStudioRuntime = bindWeaverseNextStudioRuntime;
|
|
770
1477
|
exports.buildWeaverseNextRequestInfo = buildWeaverseNextRequestInfo;
|
|
1478
|
+
exports.buildWeaverseNextRevalidateRouteContext = buildWeaverseNextRevalidateRouteContext;
|
|
1479
|
+
exports.createTranslate = createTranslate;
|
|
771
1480
|
exports.createWeaverseNextClient = createWeaverseNextClient;
|
|
772
1481
|
exports.createWeaverseNextRuntime = createWeaverseNextRuntime;
|
|
773
1482
|
exports.createWeaverseNextStudioInternals = createWeaverseNextStudioInternals;
|
|
774
1483
|
exports.createWeaverseNextThemeSettingsStore = createWeaverseNextThemeSettingsStore;
|
|
1484
|
+
exports.formatWeaverseNextSeoMetadata = formatWeaverseNextSeoMetadata;
|
|
1485
|
+
exports.generateDataFromSchema = generateDataFromSchema;
|
|
1486
|
+
exports.getNestedKey = getNestedKey;
|
|
1487
|
+
exports.getWeaverseNextSeoMetadata = getWeaverseNextSeoMetadata;
|
|
1488
|
+
exports.interpolate = interpolate;
|
|
1489
|
+
exports.loadWeaverseNextStudioScript = loadWeaverseNextStudioScript;
|
|
775
1490
|
exports.resolveWeaverseNextStudioScriptSrc = resolveWeaverseNextStudioScriptSrc;
|
|
1491
|
+
exports.revalidateWeaverseNextItem = revalidateWeaverseNextItem;
|
|
776
1492
|
exports.runWeaverseComponentLoaders = runWeaverseComponentLoaders;
|
|
777
1493
|
exports.useThemeSettings = useThemeSettings;
|
|
1494
|
+
exports.useThemeText = useThemeText;
|
|
1495
|
+
exports.useTranslation = useTranslation;
|
|
778
1496
|
exports.useWeaverseCommerce = useWeaverseCommerce;
|
|
779
1497
|
exports.useWeaverseNextStudioInternals = useWeaverseNextStudioInternals;
|
|
780
1498
|
exports.useWeaversePageData = useWeaversePageData;
|
|
781
1499
|
exports.useWeaverseRootData = useWeaverseRootData;
|
|
1500
|
+
Object.keys(schema).forEach(function (k) {
|
|
1501
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
1502
|
+
enumerable: true,
|
|
1503
|
+
get: function () { return schema[k]; }
|
|
1504
|
+
});
|
|
1505
|
+
});
|
|
782
1506
|
//# sourceMappingURL=index.js.map
|
|
783
1507
|
//# sourceMappingURL=index.js.map
|