@weaverse/next 0.1.0-alpha.0 → 0.1.0-alpha.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.
package/dist/index.mjs CHANGED
@@ -1,31 +1,12 @@
1
- import { Weaverse, WeaverseRoot, WeaverseItemStore } from '@weaverse/react';
1
+ import { buildWeaverseNextRequestInfo, generateDataFromSchema } from './chunk-SS6QUKYY.mjs';
2
+ export { buildWeaverseNextRequestInfo, runWeaverseComponentLoaders } from './chunk-SS6QUKYY.mjs';
3
+ import { WeaverseRoot, Weaverse, WeaverseItemStore } from '@weaverse/react';
2
4
  export { isBrowser, isIframe, useChildInstances, useItemInstance, useParentInstance, useWeaverse } from '@weaverse/react';
3
5
  import { createSchema } from '@weaverse/schema';
4
6
  export { createSchema } from '@weaverse/schema';
5
- import { jsx } from 'react/jsx-runtime';
6
- import { createContext, memo, useContext, useMemo } from 'react';
7
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
8
+ import { createContext, memo, useContext, useMemo, useRef, useEffect, useSyncExternalStore } from 'react';
7
9
 
8
- // src/index.ts
9
-
10
- // src/utils.ts
11
- function generateDataFromSchema(schema) {
12
- let data = {};
13
- if (!schema) {
14
- return data;
15
- }
16
- let groups = schema.settings ?? schema.inspector ?? [];
17
- for (let group of groups) {
18
- for (let input of group?.inputs ?? []) {
19
- let { name, defaultValue } = input;
20
- if (name && defaultValue !== null && defaultValue !== void 0) {
21
- data[name] = defaultValue;
22
- }
23
- }
24
- }
25
- return data;
26
- }
27
-
28
- // src/item.ts
29
10
  var WeaverseNextItem = class extends WeaverseItemStore {
30
11
  constructor(initialData, weaverse) {
31
12
  super(initialData, weaverse);
@@ -132,18 +113,53 @@ var NextClient = class {
132
113
  function createWeaverseNextClient(config) {
133
114
  return new NextClient(config);
134
115
  }
116
+
117
+ // src/theme-settings-store.ts
118
+ function createWeaverseNextThemeSettingsStore(options = {}) {
119
+ let listeners = /* @__PURE__ */ new Set();
120
+ let store = {
121
+ publicEnv: options.publicEnv,
122
+ schema: options.schema,
123
+ settings: { ...options.settings ?? {} },
124
+ getServerSnapshot: () => store.settings,
125
+ getSnapshot: () => store.settings,
126
+ subscribe: (listener) => {
127
+ listeners.add(listener);
128
+ return () => {
129
+ listeners.delete(listener);
130
+ };
131
+ },
132
+ updateThemeSettings: (next) => {
133
+ store.settings = { ...store.settings, ...next };
134
+ for (let listener of listeners) {
135
+ listener();
136
+ }
137
+ }
138
+ };
139
+ return store;
140
+ }
141
+ var EMPTY_THEME_SETTINGS = {};
135
142
  var WeaverseNextContext = createContext(null);
136
143
  function WeaverseNextProvider(props) {
137
144
  let { children, client, rootData, pageData, commerce, themeSettings } = props;
145
+ let themeSettingsValue = themeSettings ?? client?.themeSettings ?? EMPTY_THEME_SETTINGS;
146
+ let themeSettingsStore = useMemo(
147
+ () => createWeaverseNextThemeSettingsStore({
148
+ schema: client?.themeSchema,
149
+ settings: themeSettingsValue
150
+ }),
151
+ [client?.themeSchema, themeSettingsValue]
152
+ );
138
153
  let value = useMemo(
139
154
  () => ({
140
155
  client,
141
156
  rootData,
142
157
  pageData,
143
158
  commerce: commerce ?? client?.commerce,
144
- themeSettings: themeSettings ?? client?.themeSettings ?? {}
159
+ themeSettings: themeSettingsStore.getSnapshot(),
160
+ themeSettingsStore
145
161
  }),
146
- [client, rootData, pageData, commerce, themeSettings]
162
+ [client, rootData, pageData, commerce, themeSettingsStore]
147
163
  );
148
164
  return /* @__PURE__ */ jsx(WeaverseNextContext.Provider, { value, children });
149
165
  }
@@ -167,87 +183,216 @@ function useWeaverseCommerce() {
167
183
  return useWeaverseNextContext("useWeaverseCommerce").commerce;
168
184
  }
169
185
  function useThemeSettings() {
170
- return useWeaverseNextContext("useThemeSettings").themeSettings;
186
+ let { themeSettingsStore } = useWeaverseNextContext("useThemeSettings");
187
+ return useSyncExternalStore(
188
+ themeSettingsStore.subscribe,
189
+ themeSettingsStore.getSnapshot,
190
+ themeSettingsStore.getServerSnapshot
191
+ );
171
192
  }
172
-
173
- // src/loader.ts
174
- function isItemNode(node) {
175
- return typeof node === "object" && node !== null && typeof node.type === "string";
193
+ function getRuntimeWindow() {
194
+ return typeof window === "undefined" ? void 0 : window;
176
195
  }
177
- function cloneItem(item) {
178
- return {
179
- ...item,
180
- children: Array.isArray(item.children) ? item.children.map(
181
- (child) => isItemNode(child) ? cloneItem(child) : { ...child }
182
- ) : item.children
183
- };
196
+ function getRenderablePage(data) {
197
+ let page = data.page;
198
+ let rootId = page.rootId ?? page.items.find((item) => item.type === "main")?.id ?? page.items[0]?.id ?? page.id;
199
+ return { ...page, rootId };
184
200
  }
185
- async function runWeaverseComponentLoaders(args) {
186
- let { client, context, commerce } = args;
187
- let sourceData = args.data ?? client.data;
188
- if (!sourceData) {
189
- return null;
201
+ function getConfigString(configs, key) {
202
+ let value = configs?.[key];
203
+ return typeof value === "string" ? value : void 0;
204
+ }
205
+ function getRecordString(record, key) {
206
+ let value = record?.[key];
207
+ return typeof value === "string" ? value : void 0;
208
+ }
209
+ function resolveProjectId(client, data, fallback) {
210
+ let projectId = client?.projectId;
211
+ if (projectId) {
212
+ return projectId;
190
213
  }
191
- if (!sourceData.page.items.length) {
192
- return sourceData;
214
+ let configProjectId = getConfigString(data.configs, "projectId");
215
+ if (configProjectId) {
216
+ return configProjectId;
193
217
  }
194
- let data = {
195
- ...sourceData,
196
- page: {
197
- ...sourceData.page,
198
- items: sourceData.page.items.map(cloneItem)
199
- }
218
+ return getRecordString(data.project, "id") ?? fallback;
219
+ }
220
+ function resolveDataContext(config) {
221
+ if (config.dataContext) {
222
+ return config.dataContext;
223
+ }
224
+ if (config.client?.dataContext) {
225
+ return config.client.dataContext;
226
+ }
227
+ return {};
228
+ }
229
+ function getRuntimeKey(pageId, requestInfo) {
230
+ return `${pageId}:${requestInfo.pathname}:${requestInfo.search}`;
231
+ }
232
+ function registerRuntime(runtime) {
233
+ let runtimeWindow = getRuntimeWindow();
234
+ if (!runtimeWindow) {
235
+ return;
236
+ }
237
+ runtimeWindow.__weaverses ??= {};
238
+ runtimeWindow.__weaverses[runtime.pageId] = runtime;
239
+ runtimeWindow.__weaverse = runtime;
240
+ }
241
+ var WeaverseNextRuntime = class extends Weaverse {
242
+ pageId;
243
+ internal;
244
+ requestInfo;
245
+ isPreviewMode;
246
+ isRevisionPreview;
247
+ sectionType;
248
+ translationMap = {};
249
+ translationLocale = "";
250
+ translationLanguageId = "";
251
+ constructor(config) {
252
+ let { client, data } = config;
253
+ let page = getRenderablePage(data);
254
+ let configs = data.configs;
255
+ let requestContext = client?.requestContext;
256
+ let projectId = resolveProjectId(client, data, page.id);
257
+ ensureNextItemConstructor();
258
+ super({
259
+ projectId,
260
+ data: page,
261
+ isDesignMode: requestContext?.isDesignMode ?? false,
262
+ weaverseHost: getConfigString(configs, "weaverseHost"),
263
+ weaverseVersion: getConfigString(configs, "weaverseVersion")
264
+ });
265
+ this.pageId = page.id;
266
+ this.dataContext = resolveDataContext(config);
267
+ this.requestInfo = buildWeaverseNextRequestInfo(requestContext);
268
+ this.isDesignMode = requestContext?.isDesignMode ?? false;
269
+ this.isPreviewMode = requestContext?.isPreviewMode ?? false;
270
+ this.isRevisionPreview = requestContext?.isRevisionPreview ?? false;
271
+ this.sectionType = requestContext?.sectionType;
272
+ this.internal = {
273
+ navigate: config.navigate,
274
+ pageAssignment: data.pageAssignment,
275
+ project: data.project,
276
+ revalidate: config.revalidate,
277
+ themeSettingsStore: config.themeSettingsStore ?? createWeaverseNextThemeSettingsStore({
278
+ schema: client?.themeSchema,
279
+ settings: client?.themeSettings
280
+ })
281
+ };
282
+ }
283
+ extractTranslationSidecar = () => void 0;
284
+ setTranslationSidecar = (map = {}, locale = "", languageId = "") => {
285
+ this.translationMap = map;
286
+ this.translationLocale = locale;
287
+ this.translationLanguageId = languageId;
200
288
  };
201
- let items = data.page.items;
202
- let componentMap = /* @__PURE__ */ new Map();
203
- for (let component of client.components) {
204
- if (component?.schema?.type) {
205
- componentMap.set(component.schema.type, component);
289
+ updateTranslation = () => void 0;
290
+ getTranslationChanges = () => void 0;
291
+ };
292
+ function createWeaverseNextRuntime(config) {
293
+ let requestInfo = buildWeaverseNextRequestInfo(config.client?.requestContext);
294
+ let page = getRenderablePage(config.data);
295
+ let runtimeWindow = getRuntimeWindow();
296
+ let existing = runtimeWindow?.__weaverses?.[page.id];
297
+ let requestKey = getRuntimeKey(page.id, requestInfo);
298
+ if (existing?.__weaverseNextRequestKey === requestKey) {
299
+ let nextIsDesignMode = config.client?.requestContext?.isDesignMode ?? false;
300
+ if (!(existing.isDesignMode || nextIsDesignMode)) {
301
+ existing.setProjectData(page);
302
+ }
303
+ existing.dataContext = resolveDataContext(config);
304
+ existing.requestInfo = requestInfo;
305
+ existing.projectId = resolveProjectId(config.client, config.data, page.id);
306
+ existing.isDesignMode = nextIsDesignMode;
307
+ existing.isPreviewMode = config.client?.requestContext?.isPreviewMode ?? false;
308
+ existing.isRevisionPreview = config.client?.requestContext?.isRevisionPreview ?? false;
309
+ existing.sectionType = config.client?.requestContext?.sectionType;
310
+ existing.weaverseHost = getConfigString(config.data.configs, "weaverseHost") ?? existing.weaverseHost;
311
+ existing.weaverseVersion = getConfigString(config.data.configs, "weaverseVersion") ?? existing.weaverseVersion;
312
+ existing.internal.pageAssignment = config.data.pageAssignment;
313
+ existing.internal.project = config.data.project;
314
+ if (config.navigate) {
315
+ existing.internal.navigate = config.navigate;
316
+ }
317
+ if (config.revalidate) {
318
+ existing.internal.revalidate = config.revalidate;
206
319
  }
320
+ if (config.themeSettingsStore) {
321
+ existing.internal.themeSettingsStore = config.themeSettingsStore;
322
+ }
323
+ registerRuntime(existing);
324
+ return existing;
207
325
  }
208
- let visited = /* @__PURE__ */ new Set();
209
- let resolvedContext = context ?? client.requestContext;
210
- let resolvedCommerce = commerce ?? client.commerce;
211
- let resolvedWeaverse = resolvedCommerce === client.commerce ? client : {
212
- ...client,
213
- commerce: resolvedCommerce,
214
- storefront: resolvedCommerce?.storefront
215
- };
216
- async function walk(nodes) {
217
- for (let item of nodes) {
218
- if (!item || visited.has(item.id)) {
219
- continue;
220
- }
221
- visited.add(item.id);
222
- let component = componentMap.get(item.type);
223
- if (component?.loader) {
224
- try {
225
- item.loaderData = await component.loader({
226
- data: { ...generateDataFromSchema(component.schema), ...item.data },
227
- weaverse: resolvedWeaverse,
228
- context: resolvedContext,
229
- commerce: resolvedCommerce
230
- });
231
- } catch (error) {
232
- console.warn("\u274C Item loader run failed.", item.type, item.id, error);
233
- }
234
- }
235
- let children = item.children;
236
- if (Array.isArray(children)) {
237
- let inlineChildren = children.filter(
238
- isItemNode
239
- );
240
- if (inlineChildren.length) {
241
- await walk(inlineChildren);
242
- }
326
+ let runtime = new WeaverseNextRuntime(config);
327
+ runtime.__weaverseNextRequestKey = requestKey;
328
+ registerRuntime(runtime);
329
+ return runtime;
330
+ }
331
+ function bindWeaverseNextStudioRuntime(runtime) {
332
+ if (!runtime.isDesignMode) {
333
+ return false;
334
+ }
335
+ let studio = getRuntimeWindow()?.weaverseStudio;
336
+ if (!studio) {
337
+ return false;
338
+ }
339
+ let boundRuntime = runtime;
340
+ if (!boundRuntime.__weaverseNextStudioBound) {
341
+ studio.init?.(runtime);
342
+ boundRuntime.__weaverseNextStudioBound = true;
343
+ return true;
344
+ }
345
+ studio.refreshStudio?.({
346
+ data: runtime.data,
347
+ pageId: runtime.pageId,
348
+ requestInfo: runtime.requestInfo
349
+ });
350
+ return true;
351
+ }
352
+ var STUDIO_BIND_RETRY_INTERVAL_MS = 50;
353
+ var STUDIO_BIND_MAX_ATTEMPTS = 100;
354
+ function WeaverseNextStudioBridge(props) {
355
+ let { runtime, navigate, revalidate } = props;
356
+ let lastRuntimeRef = useRef(runtime);
357
+ let lastRuntimeDataRef = useRef(runtime.data);
358
+ let lastRequestInfoRef = useRef(runtime.requestInfo);
359
+ useEffect(() => {
360
+ if (navigate) {
361
+ runtime.internal.navigate = navigate;
362
+ }
363
+ if (revalidate) {
364
+ runtime.internal.revalidate = revalidate;
365
+ }
366
+ if (bindWeaverseNextStudioRuntime(runtime) || !runtime.isDesignMode) {
367
+ return;
368
+ }
369
+ let attempts = 0;
370
+ let interval = window.setInterval(() => {
371
+ attempts += 1;
372
+ if (bindWeaverseNextStudioRuntime(runtime) || attempts >= STUDIO_BIND_MAX_ATTEMPTS) {
373
+ window.clearInterval(interval);
243
374
  }
375
+ }, STUDIO_BIND_RETRY_INTERVAL_MS);
376
+ return () => window.clearInterval(interval);
377
+ }, [runtime, navigate, revalidate]);
378
+ useEffect(() => {
379
+ if (lastRuntimeRef.current !== runtime) {
380
+ lastRuntimeRef.current = runtime;
381
+ lastRuntimeDataRef.current = runtime.data;
382
+ lastRequestInfoRef.current = runtime.requestInfo;
383
+ return;
244
384
  }
245
- }
246
- await walk(items);
247
- return data;
385
+ if (lastRuntimeDataRef.current === runtime.data && lastRequestInfoRef.current === runtime.requestInfo) {
386
+ return;
387
+ }
388
+ lastRuntimeDataRef.current = runtime.data;
389
+ lastRequestInfoRef.current = runtime.requestInfo;
390
+ bindWeaverseNextStudioRuntime(runtime);
391
+ }, [runtime, runtime.data, runtime.requestInfo]);
392
+ return null;
248
393
  }
249
394
  var EMPTY_DATA_CONTEXT = {};
250
- function getRenderablePage(data) {
395
+ function getRenderablePage2(data) {
251
396
  let page = data?.page;
252
397
  if (!page) {
253
398
  return null;
@@ -260,30 +405,136 @@ var WeaverseNextRenderer = memo(function WeaverseNextRendererComponent(props) {
260
405
  let client = props.client ?? context?.client;
261
406
  let data = props.data ?? client?.data ?? null;
262
407
  let dataContext = props.dataContext ?? client?.dataContext ?? EMPTY_DATA_CONTEXT;
263
- let page = useMemo(() => getRenderablePage(data), [data]);
408
+ let page = useMemo(() => getRenderablePage2(data), [data]);
264
409
  let weaverse = useMemo(() => {
265
410
  if (!page) {
266
411
  return null;
267
412
  }
268
- let instance = new Weaverse({
269
- projectId: client?.projectId || page.id || "weaverse-next",
270
- data: page
413
+ return createWeaverseNextRuntime({
414
+ client,
415
+ data: { ...data, page },
416
+ dataContext,
417
+ themeSettingsStore: context?.themeSettingsStore
271
418
  });
272
- instance.dataContext = dataContext;
273
- instance.isDesignMode = client?.requestContext?.isDesignMode ?? false;
274
- return instance;
275
- }, [
276
- client?.projectId,
277
- client?.requestContext?.isDesignMode,
278
- page,
279
- dataContext
280
- ]);
419
+ }, [client, page, data, dataContext, context?.themeSettingsStore]);
281
420
  if (!weaverse) {
282
421
  return null;
283
422
  }
284
- return /* @__PURE__ */ jsx(WeaverseRoot, { context: weaverse });
423
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
424
+ /* @__PURE__ */ jsx(WeaverseRoot, { context: weaverse }),
425
+ /* @__PURE__ */ jsx(WeaverseNextStudioBridge, { runtime: weaverse })
426
+ ] });
285
427
  });
286
428
 
287
- export { WeaverseNextProvider, WeaverseNextRenderer, createWeaverseNextClient, runWeaverseComponentLoaders, useThemeSettings, useWeaverseCommerce, useWeaversePageData, useWeaverseRootData };
429
+ // src/studio-script-src.ts
430
+ var TRUSTED_STUDIO_DOMAINS = ["weaverse.io", "weaverse.dev"];
431
+ function isLoopbackHostname(hostname) {
432
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".localhost");
433
+ }
434
+ function normalizeOrigin(value) {
435
+ if (!value) {
436
+ return null;
437
+ }
438
+ try {
439
+ return new URL(value).origin;
440
+ } catch {
441
+ return null;
442
+ }
443
+ }
444
+ function isTrustedStudioOrigin(origin, storefrontHostname) {
445
+ let parsed;
446
+ try {
447
+ parsed = new URL(origin);
448
+ } catch {
449
+ return false;
450
+ }
451
+ let { hostname, protocol } = parsed;
452
+ if (isLoopbackHostname(storefrontHostname) && isLoopbackHostname(hostname)) {
453
+ return protocol === "http:" || protocol === "https:";
454
+ }
455
+ if (protocol !== "https:") {
456
+ return false;
457
+ }
458
+ return TRUSTED_STUDIO_DOMAINS.some(
459
+ (domain) => hostname === domain || hostname.endsWith(`.${domain}`)
460
+ );
461
+ }
462
+ function toSearchParams(context) {
463
+ if (context?.searchParams) {
464
+ return new URLSearchParams(context.searchParams);
465
+ }
466
+ if (context?.url) {
467
+ let url = typeof context.url === "string" ? new URL(context.url, "http://localhost") : context.url;
468
+ return new URLSearchParams(url.searchParams);
469
+ }
470
+ return new URLSearchParams();
471
+ }
472
+ function last(searchParams, key) {
473
+ let values = searchParams.getAll(key);
474
+ return values.length ? values.at(-1) ?? null : null;
475
+ }
476
+ function resolveWeaverseNextStudioScriptSrc(context, options = {}) {
477
+ let searchParams = toSearchParams(context);
478
+ let isDesignMode = context?.isDesignMode ?? last(searchParams, "isDesignMode") === "true";
479
+ let isPreviewMode = context?.isPreviewMode ?? last(searchParams, "isPreviewMode") === "true";
480
+ let isRevisionPreview = context?.isRevisionPreview ?? Boolean(last(searchParams, "__revisionId"));
481
+ if (!(isDesignMode || isPreviewMode || isRevisionPreview)) {
482
+ return null;
483
+ }
484
+ let origin = normalizeOrigin(last(searchParams, "weaverseHost"));
485
+ if (!origin) {
486
+ return null;
487
+ }
488
+ let storefrontHostname = options.storefrontHostname ?? "";
489
+ if (!isTrustedStudioOrigin(origin, storefrontHostname)) {
490
+ return null;
491
+ }
492
+ let framework = options.framework ?? "hydrogen";
493
+ let bundle = isDesignMode ? "index.js" : "preview.js";
494
+ let version = last(searchParams, "weaverseVersion");
495
+ let url = `${origin}/static/studio/${framework}/${bundle}`;
496
+ return version ? `${url}?v=${encodeURIComponent(version)}` : url;
497
+ }
498
+
499
+ // src/studio-connect.tsx
500
+ var loadedScriptSrcs = /* @__PURE__ */ new Set();
501
+ function loadScript(src) {
502
+ if (typeof document === "undefined") {
503
+ return;
504
+ }
505
+ if (loadedScriptSrcs.has(src)) {
506
+ return;
507
+ }
508
+ let existing = document.querySelector(`script[src="${src}"]`);
509
+ if (existing) {
510
+ loadedScriptSrcs.add(src);
511
+ return;
512
+ }
513
+ let script = document.createElement("script");
514
+ script.async = true;
515
+ script.src = src;
516
+ document.head.appendChild(script);
517
+ loadedScriptSrcs.add(src);
518
+ }
519
+ function WeaverseNextStudioConnect(props) {
520
+ useEffect(() => {
521
+ let context = props.context;
522
+ if (!context && typeof window !== "undefined") {
523
+ context = {
524
+ searchParams: new URLSearchParams(window.location.search),
525
+ url: window.location.href
526
+ };
527
+ }
528
+ let src = resolveWeaverseNextStudioScriptSrc(context, {
529
+ storefrontHostname: props.storefrontHostname ?? (typeof window === "undefined" ? void 0 : window.location.hostname)
530
+ });
531
+ if (src) {
532
+ loadScript(src);
533
+ }
534
+ }, [props.context, props.storefrontHostname]);
535
+ return null;
536
+ }
537
+
538
+ export { WeaverseNextProvider, WeaverseNextRenderer, WeaverseNextRuntime, WeaverseNextStudioBridge, WeaverseNextStudioConnect, bindWeaverseNextStudioRuntime, createWeaverseNextClient, createWeaverseNextRuntime, createWeaverseNextThemeSettingsStore, resolveWeaverseNextStudioScriptSrc, useThemeSettings, useWeaverseCommerce, useWeaversePageData, useWeaverseRootData };
288
539
  //# sourceMappingURL=index.mjs.map
289
540
  //# sourceMappingURL=index.mjs.map