@weaverse/next 0.1.0-alpha.8 → 0.1.0-alpha.9

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
@@ -5,9 +5,27 @@ export { isBrowser, isIframe, useChildInstances, useItemInstance, useParentInsta
5
5
  import { createSchema } from '@weaverse/schema';
6
6
  export { createSchema } from '@weaverse/schema';
7
7
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
8
- import { createContext, memo, useContext, useMemo, useRef, useEffect, useSyncExternalStore } from 'react';
8
+ import { createContext, memo, useContext, useMemo, useEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';
9
9
  import { useRouter } from 'next/navigation';
10
10
 
11
+ function flattenItemData(update) {
12
+ let { data, ...rest } = update;
13
+ if (!(data && typeof data === "object" && !Array.isArray(data))) {
14
+ return rest;
15
+ }
16
+ return { ...data, ...rest };
17
+ }
18
+ var deferredItemUpdates = null;
19
+ function collectDeferredItemUpdates(create) {
20
+ let previousQueue = deferredItemUpdates;
21
+ let queue = /* @__PURE__ */ new Set();
22
+ deferredItemUpdates = queue;
23
+ try {
24
+ return [create(), [...queue]];
25
+ } finally {
26
+ deferredItemUpdates = previousQueue;
27
+ }
28
+ }
11
29
  var WeaverseNextItem = class extends WeaverseItemStore {
12
30
  /**
13
31
  * Cached merged snapshot (base `_store` + translations). Reset whenever the
@@ -19,10 +37,28 @@ var WeaverseNextItem = class extends WeaverseItemStore {
19
37
  _snapshotTranslationRef = null;
20
38
  constructor(initialData, weaverse) {
21
39
  super(initialData, weaverse);
22
- let { data, ...rest } = initialData;
23
40
  let schemaData = generateDataFromSchema(this.Element?.schema);
24
- Object.assign(this._store, schemaData, data, rest);
41
+ Object.assign(this._store, schemaData, flattenItemData(initialData));
25
42
  }
43
+ /**
44
+ * Flatten nested `data` on updates exactly like the constructor does. Core's
45
+ * `initProject()` calls `setData(item)` when a new runtime serves a page
46
+ * whose item instances already exist (e.g. locale/client navigation), and
47
+ * the inherited shallow merge would leave stale top-level props behind.
48
+ * Assigning through the inherited `data` setter swaps the `_store` ref,
49
+ * which invalidates the memoized snapshot below. The subscriber emit is
50
+ * queued instead of fired when a render-phase deferral is active (see
51
+ * `collectDeferredItemUpdates`).
52
+ */
53
+ setData = (update) => {
54
+ this.data = flattenItemData(update);
55
+ if (deferredItemUpdates) {
56
+ deferredItemUpdates.add(this);
57
+ } else {
58
+ this.triggerUpdate();
59
+ }
60
+ return this.data;
61
+ };
26
62
  /**
27
63
  * Overlay item-level translations onto the base store when the runtime is in
28
64
  * translation mode. Mirrors Hydrogen's `WeaverseHydrogenItem.getSnapShot()`:
@@ -518,6 +554,22 @@ var WeaverseNextRuntime = class extends Weaverse {
518
554
  translationMap = {};
519
555
  translationLocale = String();
520
556
  translationLanguageId = String();
557
+ /**
558
+ * Fresh page data captured while React was rendering. Runtime reuse happens
559
+ * inside the renderer's render phase (`useMemo`), where applying it through
560
+ * `setProjectData()` would emit item updates to `useSyncExternalStore`
561
+ * subscribers mid-render. The renderer applies it post-commit via
562
+ * `flushRenderPhaseUpdates()`. Never set on design-mode runtimes — the live
563
+ * Studio tree owns the page data there.
564
+ */
565
+ pendingProjectData;
566
+ /**
567
+ * Reused item instances whose data was refreshed while this runtime was
568
+ * constructed during render (core `initProject()` calls `setData()` on
569
+ * them). Their data is already fresh; only the subscriber notification was
570
+ * deferred to `flushRenderPhaseUpdates()`.
571
+ */
572
+ pendingItemUpdates = [];
521
573
  constructor(config) {
522
574
  let { client, data } = config;
523
575
  let page = getRenderablePage(data);
@@ -641,6 +693,27 @@ var WeaverseNextRuntime = class extends Weaverse {
641
693
  this.extractTranslationSidecar();
642
694
  this.initProject();
643
695
  };
696
+ /**
697
+ * Apply updates deferred off the render phase: swap in any pending page data
698
+ * captured on runtime reuse, then notify items whose data was refreshed
699
+ * during construction. The renderer calls this from a layout effect right
700
+ * after commit, so subscribers re-render before paint (no content flash) and
701
+ * without React's setState-in-render warning.
702
+ */
703
+ flushRenderPhaseUpdates = () => {
704
+ let pendingData = this.pendingProjectData;
705
+ if (pendingData) {
706
+ this.pendingProjectData = void 0;
707
+ this.setProjectData(pendingData);
708
+ }
709
+ let pendingItems = this.pendingItemUpdates;
710
+ if (pendingItems.length > 0) {
711
+ this.pendingItemUpdates = [];
712
+ for (let item of pendingItems) {
713
+ item.triggerUpdate();
714
+ }
715
+ }
716
+ };
644
717
  };
645
718
  function createWeaverseNextRuntime(config) {
646
719
  let requestInfo = buildWeaverseNextRequestInfo(config.client?.requestContext);
@@ -650,8 +723,10 @@ function createWeaverseNextRuntime(config) {
650
723
  let requestKey = getRuntimeKey(page.id, requestInfo);
651
724
  if (existing?.__weaverseNextRequestKey === requestKey) {
652
725
  let nextIsDesignMode = config.client?.requestContext?.isDesignMode ?? false;
653
- if (!(existing.isDesignMode || nextIsDesignMode)) {
654
- existing.setProjectData(page);
726
+ if (existing.isDesignMode || nextIsDesignMode) {
727
+ existing.pendingProjectData = void 0;
728
+ } else {
729
+ existing.pendingProjectData = page;
655
730
  }
656
731
  existing.__weaverseNextLatestData = page;
657
732
  existing.dataContext = resolveDataContext(config);
@@ -682,7 +757,10 @@ function createWeaverseNextRuntime(config) {
682
757
  registerRuntime(existing);
683
758
  return existing;
684
759
  }
685
- let runtime = new WeaverseNextRuntime(config);
760
+ let [runtime, refreshedItems] = collectDeferredItemUpdates(
761
+ () => new WeaverseNextRuntime(config)
762
+ );
763
+ runtime.pendingItemUpdates = refreshedItems;
686
764
  runtime.__weaverseNextRequestKey = requestKey;
687
765
  runtime.__weaverseNextLatestData = page;
688
766
  registerRuntime(runtime);
@@ -835,6 +913,7 @@ function WeaverseNextStudio(props) {
835
913
  );
836
914
  }
837
915
  var EMPTY_DATA_CONTEXT = {};
916
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
838
917
  function getRenderablePage2(data) {
839
918
  let page = data?.page;
840
919
  if (!page) {
@@ -870,6 +949,9 @@ var WeaverseNextRenderer = memo(function WeaverseNextRendererComponent(props) {
870
949
  context?.themeSettingsStore,
871
950
  context?.translationStore
872
951
  ]);
952
+ useIsomorphicLayoutEffect(() => {
953
+ weaverse?.flushRenderPhaseUpdates();
954
+ }, [weaverse]);
873
955
  if (!weaverse) {
874
956
  return null;
875
957
  }