@weaverse/next 0.1.0-alpha.15 → 0.1.0-alpha.17

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 CHANGED
@@ -45,30 +45,7 @@ Implementation lives in [`src/server/server-client.ts`](./src/server/server-clie
45
45
  - forces `no-store` for design/revision preview reads;
46
46
  - returns safe client-facing configs including `requestInfo`.
47
47
 
48
- ### 3. Is the POC bootstrapped from Shopify's Hydrogen preview docs?
49
-
50
- Yes. The POC started as a Next App Router app plus Shopify Hydrogen preview setup:
51
-
52
- ```bash
53
- npx create-next-app@latest weaverse-hydrogen-next-poc --ts --app --eslint --tailwind --no-src-dir --import-alias "@/*" --use-npm --yes
54
- npx @shopify/hydrogen@preview setup
55
- ```
56
-
57
- See the POC findings doc:
58
-
59
- - https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/findings.md
60
-
61
- ### 4. Where does the POC load page + Weaverse data?
62
-
63
- Reference POC files:
64
-
65
- - Server client helper: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/weaverse-next/server.ts
66
- - Home route load: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/page.tsx
67
- - Product route load: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/products/%5Bhandle%5D/page.tsx
68
- - Collection route load: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/collections/%5Bhandle%5D/page.tsx
69
- - Client wrapper/renderer: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/weaverse-next/wrapper.tsx
70
- - Studio script connector: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/weaverse-next/studio-connect.tsx
71
- - Per-item revalidation route: https://github.com/Weaverse/weaverse-hydrogen-next-poc/blob/main/app/api/weaverse/revalidate/route.ts
48
+ ### 3. Where does a route load page + Weaverse data?
72
49
 
73
50
  The route pattern is:
74
51
 
@@ -85,13 +62,13 @@ export default async function Home(props: {
85
62
  }
86
63
  ```
87
64
 
88
- ### 5. Is there a global app load context like Hydrogen/Pilot?
65
+ ### 4. Is there a global app load context like Hydrogen/Pilot?
89
66
 
90
67
  Not automatically from the framework.
91
68
 
92
69
  Hydrogen/React Router injects `context.weaverse` into route loaders. Next App Router does not have that loader-context mechanism. The Next equivalent is a small app-owned server helper that creates the request-scoped Weaverse server client at the route boundary.
93
70
 
94
- In the POC that helper is `getWeaverseServerClient(...)` in `app/weaverse-next/server.ts`. It plays the same role as Hydrogen's global load context, but explicitly:
71
+ Name it whatever you like — `getWeaverseServerClient(...)` in `app/weaverse-next/server.ts` below. It plays the same role as Hydrogen's global load context, but explicitly:
95
72
 
96
73
  ```ts
97
74
  let weaverse = await getWeaverseServerClient(searchParams, pathname)
@@ -555,7 +532,7 @@ import {
555
532
 
556
533
  export const { POST } = createWeaverseNextRevalidateHandler({
557
534
  // `requestContext` is validated browser input for route identity only.
558
- // Project ID, Studio host, API base/key, and env must still come from
535
+ // Project ID, Studio host, API base, and env must still come from
559
536
  // server config — never from this context — and it is `undefined` for
560
537
  // legacy request bodies that carry no route context.
561
538
  getClient: (_request, requestContext) =>
@@ -602,13 +579,13 @@ handler is the security boundary and re-validates independently:
602
579
 
603
580
  - Only `pathname`, sanitized `search`, a narrow i18n subset, a
604
581
  `PageTypeSchema`-valid `pageType`, and a bounded `handle` cross the boundary.
605
- Headers, cookies, auth, env, project ID, Studio host, API base/key, commerce
582
+ Headers, cookies, auth, env, project ID, Studio host, API base, commerce
606
583
  clients, the runtime, and the client are never serialized.
607
- - Server-owned controls (`weaverseProjectId`, `weaverseHost`, `weaverseApiKey`,
608
- `weaverseApiBase`, `weaversePublicApiBase`, `weaverseVersion`, `projectId`) and
609
- transient transport controls (`weaverseDraftItem`, `__weaverseDraftItem`,
610
- `_rsc`) are stripped case-insensitively on both sides, so a crafted body cannot
611
- influence server config resolution.
584
+ - Server-owned controls (`weaverseProjectId`, `weaverseHost`, `weaverseApiBase`,
585
+ `weaversePublicApiBase`, `weaverseVersion`, `projectId`) and transient transport
586
+ controls (`weaverseDraftItem`, `__weaverseDraftItem`, `_rsc`) are stripped
587
+ case-insensitively on both sides, so a crafted body cannot influence server
588
+ config resolution.
612
589
  - The origin is fixed from the endpoint request before assigning any
613
590
  browser-provided pathname/search, so input cannot change protocol, host, port,
614
591
  or credentials. `pathname` and `url.pathname` share one URL-canonicalized value.
@@ -629,30 +606,6 @@ Server config resolution intentionally mirrors Hydrogen where possible:
629
606
  - API base: trusted request host → `WEAVERSE_PUBLIC_API_BASE` → non-production `WEAVERSE_HOST` → `https://api.weaverse.io`.
630
607
  - public env: `PUBLIC_STORE_DOMAIN`, `PUBLIC_STOREFRONT_API_TOKEN`.
631
608
 
632
- `WEAVERSE_API_KEY` may be read into internal base configs but is not attached to page/theme API requests and is never serialized into client-facing loader data.
633
-
634
- ## POC reference
635
-
636
- Live POC:
637
-
638
- - https://weaverse-hydrogen-next-poc.vercel.app
639
-
640
- Repo:
641
-
642
- - https://github.com/Weaverse/weaverse-hydrogen-next-poc
643
-
644
- Important POC paths:
645
-
646
- ```text
647
- app/weaverse-next/server.ts # explicit Next server context helper
648
- app/weaverse-next/wrapper.tsx # client provider + renderer
649
- app/weaverse-next/studio-connect.tsx # root Studio script connector
650
- app/api/weaverse/revalidate/route.ts # per-item loader revalidation route
651
- app/page.tsx # home page Weaverse load
652
- app/products/[handle]/page.tsx # product route Weaverse load
653
- app/collections/[handle]/page.tsx # collection route Weaverse load
654
- ```
655
-
656
609
  ## Alpha migration notes
657
610
 
658
611
  ### After `0.1.0-alpha.9`
@@ -313,5 +313,5 @@ function getWeaverseNextSeoMetadata(data) {
313
313
  }
314
314
 
315
315
  export { DEFAULT_REVALIDATE_ENDPOINT, ROUTE_CONTEXT_I18N_KEYS, buildWeaverseNextRequestInfo, buildWeaverseNextRevalidateRouteContext, formatWeaverseNextSeoMetadata, generateDataFromSchema, getWeaverseNextSeoMetadata, revalidateWeaverseNextItem, runWeaverseComponentLoaders, sanitizeRouteContextSearch };
316
- //# sourceMappingURL=chunk-NL22OVD7.mjs.map
317
- //# sourceMappingURL=chunk-NL22OVD7.mjs.map
316
+ //# sourceMappingURL=chunk-HRNAW6CA.mjs.map
317
+ //# sourceMappingURL=chunk-HRNAW6CA.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/loader.ts","../src/request-info.ts","../src/revalidate-item.ts","../src/seo.ts"],"names":[],"mappings":";;;AAWO,SAAS,uBACd,MAAA,EACyB;AACzB,EAAA,IAAI,OAAgC,EAAC;AACrC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA,GACD,MAAA,CAAO,QAAA,IAGP,MAAA,CAAyC,aAC1C,EAAC;AAEH,EAAA,KAAA,IAAS,SAAS,MAAA,EAAQ;AACxB,IAAA,KAAA,IAAS,KAAA,IAAS,KAAA,EAAO,MAAA,IAAU,EAAC,EAAG;AACrC,MAAA,IAAI,EAAE,IAAA,EAAM,YAAA,EAAa,GAAI,KAAA;AAC7B,MAAA,IAAI,IAAA,IAAQ,YAAA,KAAiB,IAAA,IAAQ,YAAA,KAAiB,MAAA,EAAW;AAC/D,QAAA,IAAA,CAAK,IAAI,CAAA,GAAI,YAAA;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACdA,SAAS,WAAW,IAAA,EAAkD;AACpE,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,SAAS,IAAA,IACT,OAAQ,KAAmC,IAAA,KAAS,QAAA;AAExD;AAEA,SAAS,UAAU,IAAA,EAA4D;AAC7E,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,UAAU,KAAA,CAAM,OAAA,CAAQ,KAAK,QAAQ,CAAA,GACjC,KAAK,QAAA,CAAS,GAAA;AAAA,MAAI,CAAC,KAAA,KACjB,UAAA,CAAW,KAAK,CAAA,GAAI,UAAU,KAAK,CAAA,GAAI,EAAE,GAAG,KAAA;AAAM,QAEpD,IAAA,CAAK;AAAA,GACX;AACF;AAaA,eAAsB,4BACpB,IAAA,EACwC;AACxC,EAAA,IAAI,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAS,GAAI,IAAA;AACpC,EAAA,IAAI,UAAA,GAAa,IAAA,CAAK,IAAA,IAAQ,MAAA,CAAO,IAAA;AACrC,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ;AACjC,IAAA,OAAO,UAAA;AAAA,EACT;AAEA,EAAA,IAAI,IAAA,GAA+B;AAAA,IACjC,GAAG,UAAA;AAAA,IACH,IAAA,EAAM;AAAA,MACJ,GAAG,UAAA,CAAW,IAAA;AAAA,MACd,KAAA,EAAO,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,IAAI,SAAS;AAAA;AAC5C,GACF;AACA,EAAA,IAAI,KAAA,GAAQ,KAAK,IAAA,CAAK,KAAA;AAEtB,EAAA,IAAI,YAAA,uBAAmB,GAAA,EAAmC;AAC1D,EAAA,KAAA,IAAS,SAAA,IAAa,OAAO,UAAA,EAAY;AACvC,IAAA,IAAI,SAAA,EAAW,QAAQ,IAAA,EAAM;AAC3B,MAAA,YAAA,CAAa,GAAA,CAAI,SAAA,CAAU,MAAA,CAAO,IAAA,EAAM,SAAS,CAAA;AAAA,IACnD;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,uBAAc,GAAA,EAAY;AAC9B,EAAA,IAAI,eAAA,GAAkB,WAAW,MAAA,CAAO,cAAA;AACxC,EAAA,IAAI,gBAAA,GAAmB,YAAY,MAAA,CAAO,QAAA;AAQ1C,EAAA,IAAI,gBAAA,GACF,gBAAA,KAAqB,MAAA,CAAO,QAAA,GACxB,MAAA,GACA;AAAA,IACE,GAAG,MAAA;AAAA,IACH,QAAA,EAAU,gBAAA;AAAA,IACV,YAAY,gBAAA,EAAkB;AAAA,GAChC;AAEN,EAAA,eAAe,KAAK,KAAA,EAAoC;AACtD,IAAA,KAAA,IAAS,QAAQ,KAAA,EAAO;AACtB,MAAA,IAAI,CAAC,IAAA,IAAQ,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG;AACjC,QAAA;AAAA,MACF;AACA,MAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,EAAE,CAAA;AAEnB,MAAA,IAAI,SAAA,GAAY,YAAA,CAAa,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA;AAC1C,MAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,QAAA,IAAI;AACF,UAAA,IAAA,CAAK,UAAA,GAAa,MAAM,SAAA,CAAU,MAAA,CAAO;AAAA,YACvC,IAAA,EAAM,EAAE,GAAG,sBAAA,CAAuB,UAAU,MAAM,CAAA,EAAG,GAAG,IAAA,CAAK,IAAA,EAAK;AAAA,YAClE,QAAA,EAAU,gBAAA;AAAA,YACV,OAAA,EAAS,eAAA;AAAA,YACT,QAAA,EAAU;AAAA,WACX,CAAA;AAAA,QACH,SAAS,KAAA,EAAO;AACd,UAAA,OAAA,CAAQ,KAAK,gCAAA,EAA6B,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,IAAI,KAAK,CAAA;AAAA,QACrE;AAAA,MACF;AAEA,MAAA,IAAI,WAAW,IAAA,CAAK,QAAA;AACpB,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC3B,QAAA,IAAI,iBAAiB,QAAA,CAAS,MAAA;AAAA,UAC5B;AAAA,SACF;AACA,QAAA,IAAI,eAAe,MAAA,EAAQ;AACzB,UAAA,MAAM,KAAK,cAAc,CAAA;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,KAAK,KAAK,CAAA;AAChB,EAAA,OAAO,IAAA;AACT;;;ACjIA,SAAS,MAAM,KAAA,EAA0B;AACvC,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,IAAI,GAAA,CAAI,KAAA,EAAO,kBAAkB,CAAA,GAAI,KAAA;AAC1E;AAEA,SAAS,2BACP,OAAA,EACiB;AACjB,EAAA,IAAI,SAAS,YAAA,EAAc;AACzB,IAAA,OAAO,IAAI,eAAA,CAAgB,OAAA,CAAQ,YAAY,CAAA;AAAA,EACjD;AAEA,EAAA,IAAI,SAAS,GAAA,EAAK;AAChB,IAAA,IAAI,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AAC3B,IAAA,OAAO,IAAI,eAAA,CAAgB,GAAA,CAAI,YAAY,CAAA;AAAA,EAC7C;AAEA,EAAA,OAAO,IAAI,eAAA,EAAgB;AAC7B;AAEA,SAAS,uBAAuB,OAAA,EAA8C;AAC5E,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,OAAO,OAAA,CAAQ,QAAA;AAAA,EACjB;AAEA,EAAA,IAAI,SAAS,GAAA,EAAK;AAChB,IAAA,IAAI,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AAC3B,IAAA,OAAO,IAAI,QAAA,IAAY,GAAA;AAAA,EACzB;AAEA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,iBAAiB,KAAA,EAAiC;AACzD,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,UAAU,OAAA,EAAS;AACrB,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,cACP,YAAA,EACkC;AAClC,EAAA,IAAI,UAA4C,EAAC;AACjD,EAAA,KAAA,IAAS,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,YAAA,CAAa,SAAQ,EAAG;AAC/C,IAAA,OAAA,CAAQ,GAAG,CAAA,GAAI,gBAAA,CAAiB,KAAK,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,OAAA;AACT;AAEA,IAAM,uBAAA,uBAA8B,GAAA,CAAI;AAAA,EACtC,mBAAA;AAAA;AAAA;AAAA;AAAA,EAIA;AACF,CAAC,CAAA;AAED,SAAS,2BACP,OAAA,EACiB;AACjB,EAAA,IAAI,YAAA,GAAe,IAAI,eAAA,CAAgB,0BAAA,CAA2B,OAAO,CAAC,CAAA;AAC1E,EAAA,KAAA,IAAS,OAAO,uBAAA,EAAyB;AACvC,IAAA,YAAA,CAAa,OAAO,GAAG,CAAA;AAAA,EACzB;AACA,EAAA,OAAO,YAAA;AACT;AAOO,SAAS,6BACd,OAAA,EACyB;AACzB,EAAA,IAAI,YAAA,GAAe,2BAA2B,OAAO,CAAA;AACrD,EAAA,IAAI,MAAA,GAAS,aAAa,QAAA,EAAS;AAEnC,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,uBAAuB,OAAO,CAAA;AAAA,IACxC,MAAA,EAAQ,MAAA,GAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,GAAK,EAAA;AAAA,IAChC,OAAA,EAAS,cAAc,YAAY,CAAA;AAAA,IACnC,GAAI,SAAS,IAAA,GAAO,EAAE,MAAM,OAAA,CAAQ,IAAA,KAAS,EAAC;AAAA,IAC9C,GAAI,SAAS,QAAA,GAAW,EAAE,UAAU,OAAA,CAAQ,QAAA,KAAa,EAAC;AAAA,IAC1D,GAAI,SAAS,MAAA,GAAS,EAAE,QAAQ,OAAA,CAAQ,MAAA,KAAW;AAAC,GACtD;AACF;ACrFO,IAAM,2BAAA,GAA8B;AAE3C,IAAM,+BAAA,GAAkC,GAAA;AAYjC,IAAM,2BAAA,GAA8B;AAAA,EACzC,mBAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EACA,iBAAA;AAAA,EACA,uBAAA;AAAA,EACA,iBAAA;AAAA,EACA,WAAA;AAAA,EACA,mBAAA;AAAA,EACA,qBAAA;AAAA,EACA;AACF,CAAA;AAGO,IAAM,uBAAA,GAA0B;AAAA,EACrC,SAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF;AAmDO,SAAS,2BAA2B,MAAA,EAAwB;AACjE,EAAA,IAAI,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAM,CAAA;AACvC,EAAA,IAAI,MAAA,GAAS,IAAI,GAAA,CAAI,2BAA2B,CAAA;AAChD,EAAA,KAAA,IAAS,GAAA,IAAO,CAAC,GAAG,IAAI,IAAI,MAAA,CAAO,IAAA,EAAM,CAAC,CAAA,EAAG;AAC3C,IAAA,IAAI,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,WAAA,EAAa,CAAA,EAAG;AACjC,MAAA,MAAA,CAAO,OAAO,GAAG,CAAA;AAAA,IACnB;AAAA,EACF;AACA,EAAA,IAAI,UAAA,GAAa,OAAO,QAAA,EAAS;AACjC,EAAA,OAAO,UAAA,GAAa,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,GAAK,EAAA;AACzC;AAQO,SAAS,wCACd,WAAA,EACgD;AAChD,EAAA,IACE,CAAC,eACD,OAAO,WAAA,CAAY,aAAa,QAAA,IAChC,CAAC,YAAY,QAAA,EACb;AACA,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,YAAA,GAAmD;AAAA,IACrD,UAAU,WAAA,CAAY,QAAA;AAAA,IACtB,MAAA,EAAQ,0BAAA,CAA2B,WAAA,CAAY,MAAA,IAAU,EAAE;AAAA,GAC7D;AAEA,EAAA,IAAI,EAAE,MAAK,GAAI,WAAA;AACf,EAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACpC,IAAA,IAAI,SAAiC,EAAC;AACtC,IAAA,KAAA,IAAS,OAAO,uBAAA,EAAyB;AACvC,MAAA,IAAI,KAAA,GAAQ,KAAK,GAAG,CAAA;AACpB,MAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,KAAA;AAAA,MAChB;AAAA,IACF;AACA,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,MAAA,EAAQ;AAC9B,MAAA,YAAA,CAAa,IAAA,GAAO,MAAA;AAAA,IACtB;AAAA,EACF;AAEA,EAAA,IACE,YAAY,QAAA,IACZ,cAAA,CAAe,UAAU,WAAA,CAAY,QAAQ,EAAE,OAAA,EAC/C;AACA,IAAA,YAAA,CAAa,WAAW,WAAA,CAAY,QAAA;AAAA,EACtC;AAEA,EAAA,IACE,OAAO,YAAY,MAAA,KAAW,QAAA,IAC9B,YAAY,MAAA,IACZ,WAAA,CAAY,MAAA,CAAO,MAAA,IAAU,+BAAA,EAC7B;AACA,IAAA,YAAA,CAAa,SAAS,WAAA,CAAY,MAAA;AAAA,EACpC;AAEA,EAAA,OAAO,YAAA;AACT;AAeA,eAAsB,0BAAA,CACpB,OAAA,EACA,SAAA,EACA,QAAA,GAAmB,2BAAA,EACJ;AACf,EAAA,IAAI,QAAA,GAAW,OAAA,CAAQ,aAAA,CAAc,GAAA,CAAI,UAAU,EAAE,CAAA;AACrD,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,2CAAA,EAA8C,UAAU,EAAE,CAAA,EAAA;AAAA,KAC5D;AAAA,EACF;AAIA,EAAA,IAAI,YAAA,GAAe,uCAAA;AAAA,IACjB,OAAA,CAAQ;AAAA,GACV;AACA,EAAA,IAAI,OAAO,YAAA,GAAe,EAAE,WAAW,YAAA,EAAa,GAAI,EAAE,SAAA,EAAU;AAEpE,EAAA,IAAI,QAAA,GAAW,MAAM,KAAA,CAAM,QAAA,EAAU;AAAA,IACnC,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,IAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI;AAAA,GAC1B,CAAA;AACD,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,8CAAA,EAAiD,SAAS,MAAM,CAAA,CAAA;AAAA,KAClE;AAAA,EACF;AAEA,EAAA,IAAI,EAAE,UAAA,EAAW,GAAK,MAAM,SAAS,IAAA,EAAK;AAK1C,EAAA,QAAA,CAAS,OAAA,CAAQ,EAAE,UAAA,EAAY,UAAA,IAAc,MAAM,CAAA;AACrD;;;AC5JA,SAAS,oBAAoB,GAAA,EAAwC;AACnE,EAAA,OAAO,OAAA,CAAQ,KAAK,KAAA,IAAS,GAAA,EAAK,eAAe,GAAA,EAAK,KAAA,IAAS,KAAK,IAAI,CAAA;AAC1E;AAEA,SAAS,kBAAkB,GAAA,EAAsC;AAC/D,EAAA,OAAO,OAAA,CAAQ,KAAK,KAAA,IAAS,GAAA,EAAK,eAAe,GAAA,EAAK,KAAA,IAAS,KAAK,QAAQ,CAAA;AAC9E;AAEA,SAAS,qBACP,QAAA,EACmC;AACnC,EAAA,OAAO,QAAA,KAAa,wBAAwB,qBAAA,GAAwB,SAAA;AACtE;AAQA,SAAS,uBACP,IAAA,EACmD;AACnD,EAAA,IAAI,IAAA,KAAS,SAAA,IAAa,IAAA,KAAS,SAAA,IAAa,SAAS,aAAA,EAAe;AACtE,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,SAAA;AACT;AAQO,SAAS,8BACd,GAAA,EACyB;AACzB,EAAA,IAAI,KAAA,GAAQ,GAAA,EAAK,MAAA,EAAQ,KAAA,KAAU,KAAA;AACnC,EAAA,IAAI,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,MAAA,KAAW,KAAA;AACrC,EAAA,IAAI,QAAA,GAAoC;AAAA,IACtC,MAAA,EAAQ,EAAE,KAAA,EAAO,MAAA;AAAO,GAC1B;AAEA,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,IAAI,IAAI,KAAA,EAAO;AACb,IAAA,QAAA,CAAS,QAAQ,GAAA,CAAI,KAAA;AAAA,EACvB;AACA,EAAA,IAAI,IAAI,WAAA,EAAa;AACnB,IAAA,QAAA,CAAS,cAAc,GAAA,CAAI,WAAA;AAAA,EAC7B;AACA,EAAA,IAAI,IAAI,QAAA,EAAU;AAChB,IAAA,QAAA,CAAS,WAAW,GAAA,CAAI,QAAA;AAAA,EAC1B;AACA,EAAA,IAAI,IAAI,YAAA,EAAc;AACpB,IAAA,QAAA,CAAS,UAAA,GAAa,EAAE,SAAA,EAAW,GAAA,CAAI,YAAA,EAAa;AAAA,EACtD;AAEA,EAAA,IAAI,YAAY,GAAA,CAAI,SAAA;AACpB,EAAA,IAAI,mBAAA,CAAoB,SAAS,CAAA,EAAG;AAClC,IAAA,QAAA,CAAS,SAAA,GAAY;AAAA,MACnB,OAAO,SAAA,EAAW,KAAA;AAAA,MAClB,aAAa,SAAA,EAAW,WAAA;AAAA,MACxB,QAAQ,SAAA,EAAW,KAAA,GAAQ,CAAC,SAAA,CAAU,KAAK,CAAA,GAAI,MAAA;AAAA,MAC/C,IAAA,EAAM,sBAAA,CAAuB,SAAA,EAAW,IAAI;AAAA,KAC9C;AAAA,EACF;AAEA,EAAA,IAAI,UAAU,GAAA,CAAI,OAAA;AAClB,EAAA,IAAI,iBAAA,CAAkB,OAAO,CAAA,EAAG;AAK9B,IAAA,QAAA,CAAS,OAAA,GAAU;AAAA,MACjB,IAAA,EAAM,oBAAA,CAAqB,OAAA,EAAS,QAAQ,CAAA;AAAA,MAC5C,OAAO,OAAA,EAAS,KAAA;AAAA,MAChB,aAAa,OAAA,EAAS,WAAA;AAAA,MACtB,QAAQ,OAAA,EAAS,KAAA,GAAQ,CAAC,OAAA,CAAQ,KAAK,CAAA,GAAI;AAAA,KAC7C;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAMO,SAAS,2BACd,IAAA,EACyB;AACzB,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,8BAA8B,IAAI,CAAA;AAAA,EAC3C;AACA,EAAA,IAAI,OACF,OAAA,IAAW,IAAA,IAAQ,QAAQ,IAAA,GACtB,IAAA,GACC,KAAsC,IAAA,IAAQ,IAAA;AACtD,EAAA,OAAO,6BAAA,CAA8B,IAAA,EAAM,GAAA,IAAO,IAAI,CAAA;AACxD","file":"chunk-NL22OVD7.mjs","sourcesContent":["import type { SchemaType } from '@weaverse/schema'\nimport type { WeaverseNextThemeSchema } from './types'\n\n/**\n * Generate default component data from a schema's `settings` inputs.\n *\n * Mirrors Hydrogen's `generateDataFromSchema` (settings → `{ [name]:\n * defaultValue }`) but is kept local so `@weaverse/next` doesn't depend on\n * `@weaverse/hydrogen`. Falls back to the deprecated `inspector` array when a\n * schema predates `settings`.\n */\nexport function generateDataFromSchema(\n schema: SchemaType | WeaverseNextThemeSchema | undefined\n): Record<string, unknown> {\n let data: Record<string, unknown> = {}\n if (!schema) {\n return data\n }\n\n let groups =\n (schema.settings as {\n inputs?: { name?: string; defaultValue?: unknown }[]\n }[]) ??\n (schema as { inspector?: typeof groups }).inspector ??\n []\n\n for (let group of groups) {\n for (let input of group?.inputs ?? []) {\n let { name, defaultValue } = input\n if (name && defaultValue !== null && defaultValue !== undefined) {\n data[name] = defaultValue\n }\n }\n }\n\n return data\n}\n","import type {\n WeaverseNextClient,\n WeaverseNextCommerceContext,\n WeaverseNextComponent,\n WeaverseNextComponentData,\n WeaverseNextLoaderData,\n WeaverseNextRequestContext,\n} from './types'\nimport { generateDataFromSchema } from './utils'\n\n/** Inputs for running registered component loaders over a page tree. */\nexport interface RunComponentLoadersArgs {\n /** Weaverse client containing the component registry. */\n client: WeaverseNextClient\n /** Request-scoped commerce override; defaults to `client.commerce`. */\n commerce?: WeaverseNextCommerceContext\n /** Request-context override; defaults to `client.requestContext`. */\n context?: WeaverseNextRequestContext\n /** Page payload to walk; defaults to `client.data`. */\n data?: WeaverseNextLoaderData | null\n}\n\nfunction isItemNode(node: unknown): node is WeaverseNextComponentData {\n return (\n typeof node === 'object' &&\n node !== null &&\n typeof (node as WeaverseNextComponentData).type === 'string'\n )\n}\n\nfunction cloneItem(item: WeaverseNextComponentData): WeaverseNextComponentData {\n return {\n ...item,\n children: Array.isArray(item.children)\n ? item.children.map((child) =>\n isItemNode(child) ? cloneItem(child) : { ...child }\n )\n : item.children,\n }\n}\n\n/**\n * Walk a serialized page tree and run each registered component's `loader`,\n * attaching the result to the item as `loaderData`.\n *\n * Loaders receive explicit `commerce.storefront` (and `weaverse.storefront` as\n * a compatibility alias via the client). Children are walked recursively when\n * they are inline item objects; flat `items` arrays are covered by the\n * top-level walk.\n *\n * @returns a cloned `data` object with `loaderData` attached to cloned items.\n */\nexport async function runWeaverseComponentLoaders(\n args: RunComponentLoadersArgs\n): Promise<WeaverseNextLoaderData | null> {\n let { client, context, commerce } = args\n let sourceData = args.data ?? client.data\n if (!sourceData) {\n return null\n }\n if (!sourceData.page.items.length) {\n return sourceData\n }\n\n let data: WeaverseNextLoaderData = {\n ...sourceData,\n page: {\n ...sourceData.page,\n items: sourceData.page.items.map(cloneItem),\n },\n }\n let items = data.page.items\n\n let componentMap = new Map<string, WeaverseNextComponent>()\n for (let component of client.components) {\n if (component?.schema?.type) {\n componentMap.set(component.schema.type, component)\n }\n }\n\n let visited = new Set<string>()\n let resolvedContext = context ?? client.requestContext\n let resolvedCommerce = commerce ?? client.commerce\n\n // Per-call `weaverse` alias. When a request-scoped `commerce` is passed, the\n // `weaverse.storefront` compatibility alias must resolve to *that* storefront,\n // not the client's construction-time commerce. Pilot-style loaders call\n // `weaverse.storefront.query(...)`, so a stale alias would query the wrong\n // (or an undefined) storefront. When no override is given we reuse the client\n // as-is to preserve its prototype getters.\n let resolvedWeaverse: WeaverseNextClient =\n resolvedCommerce === client.commerce\n ? client\n : {\n ...client,\n commerce: resolvedCommerce,\n storefront: resolvedCommerce?.storefront,\n }\n\n async function walk(nodes: WeaverseNextComponentData[]) {\n for (let item of nodes) {\n if (!item || visited.has(item.id)) {\n continue\n }\n visited.add(item.id)\n\n let component = componentMap.get(item.type)\n if (component?.loader) {\n try {\n item.loaderData = await component.loader({\n data: { ...generateDataFromSchema(component.schema), ...item.data },\n weaverse: resolvedWeaverse,\n context: resolvedContext,\n commerce: resolvedCommerce,\n })\n } catch (error) {\n console.warn('❌ Item loader run failed.', item.type, item.id, error)\n }\n }\n\n let children = item.children\n if (Array.isArray(children)) {\n let inlineChildren = children.filter(\n isItemNode\n ) as WeaverseNextComponentData[]\n if (inlineChildren.length) {\n await walk(inlineChildren)\n }\n }\n }\n }\n\n await walk(items)\n return data\n}\n","import type {\n WeaverseNextRequestContext,\n WeaverseNextRequestInfo,\n} from './types'\n\nfunction toUrl(value: string | URL): URL {\n return typeof value === 'string' ? new URL(value, 'http://localhost') : value\n}\n\nfunction getSearchParamsFromContext(\n context?: WeaverseNextRequestContext\n): URLSearchParams {\n if (context?.searchParams) {\n return new URLSearchParams(context.searchParams)\n }\n\n if (context?.url) {\n let url = toUrl(context.url)\n return new URLSearchParams(url.searchParams)\n }\n\n return new URLSearchParams()\n}\n\nfunction getPathnameFromContext(context?: WeaverseNextRequestContext): string {\n if (context?.pathname) {\n return context.pathname\n }\n\n if (context?.url) {\n let url = toUrl(context.url)\n return url.pathname || '/'\n }\n\n return '/'\n}\n\nfunction coerceQueryValue(value: string): string | boolean {\n if (value === 'true') {\n return true\n }\n if (value === 'false') {\n return false\n }\n return value\n}\n\nfunction toQueryRecord(\n searchParams: URLSearchParams\n): Record<string, string | boolean> {\n let queries: Record<string, string | boolean> = {}\n for (let [key, value] of searchParams.entries()) {\n queries[key] = coerceQueryValue(value)\n }\n return queries\n}\n\nconst TRANSIENT_STUDIO_PARAMS = new Set([\n 'weaverseDraftItem',\n // Legacy name from the original design-mode draft-item spike. Keep it out of\n // runtime requestInfo too, in case an older Builder bundle is in front of a\n // newer Next storefront.\n '__weaverseDraftItem',\n])\n\nfunction getRequestInfoSearchParams(\n context?: WeaverseNextRequestContext\n): URLSearchParams {\n let searchParams = new URLSearchParams(getSearchParamsFromContext(context))\n for (let key of TRANSIENT_STUDIO_PARAMS) {\n searchParams.delete(key)\n }\n return searchParams\n}\n\n/**\n * Build the Hydrogen-like request info shape consumed by the Studio bridge.\n * Duplicate query keys keep the last value, matching `URLSearchParams` iteration\n * assignment and Hydrogen's practical query-object behavior.\n */\nexport function buildWeaverseNextRequestInfo(\n context?: WeaverseNextRequestContext\n): WeaverseNextRequestInfo {\n let searchParams = getRequestInfoSearchParams(context)\n let search = searchParams.toString()\n\n return {\n pathname: getPathnameFromContext(context),\n search: search ? `?${search}` : '',\n queries: toQueryRecord(searchParams),\n ...(context?.i18n ? { i18n: context.i18n } : {}),\n ...(context?.pageType ? { pageType: context.pageType } : {}),\n ...(context?.handle ? { handle: context.handle } : {}),\n }\n}\n","import type { PageType } from '@weaverse/schema'\nimport { PageTypeSchema } from '@weaverse/schema'\nimport type {\n WeaverseNextComponentData,\n WeaverseNextI18n,\n WeaverseNextRequestInfo,\n} from './types'\n\n/** Default App Router endpoint used for Studio item-loader revalidation. */\nexport const DEFAULT_REVALIDATE_ENDPOINT = '/api/weaverse/revalidate'\n\nconst MAX_ROUTE_CONTEXT_HANDLE_LENGTH = 512\n\n/**\n * Server-owned and transient controls stripped from the revalidation route\n * context on both sides of the trust boundary. Compared case-insensitively, so\n * every entry is stored lowercased. Client sanitization keeps normal traffic\n * clean; the route handler repeats this deny-list as the security boundary.\n *\n * - Server-owned: a crafted `?weaverseProjectId=`/`?weaverseHost=`/`?weaverseApiKey=`\n * must never influence server config resolution (see `getWeaverseNextConfigs`).\n * - Transient: draft-item and RSC transport params never describe route identity.\n */\nexport const DENIED_ROUTE_CONTEXT_PARAMS = [\n 'weaverseprojectid',\n 'weaversehost',\n 'weaverseapikey',\n 'weaverseapibase',\n 'weaversepublicapibase',\n 'weaverseversion',\n 'projectid',\n 'weaversedraftitem',\n '__weaversedraftitem',\n '_rsc',\n]\n\n/** i18n fields allowed to cross the browser boundary. */\nexport const ROUTE_CONTEXT_I18N_KEYS = [\n 'country',\n 'label',\n 'language',\n 'locale',\n 'pathPrefix',\n] as const\n\n/**\n * JSON-safe route subset that may cross the browser boundary alongside the\n * draft item. Never carries headers, cookies, auth, env, project ID, Studio\n * host, API keys/bases, commerce clients, the request context, runtime, or\n * client. `search` is canonical (preserves duplicate keys); the derived\n * `queries` record is intentionally omitted.\n */\nexport interface WeaverseNextRevalidateRouteContext {\n /** Route handle used for page assignment. */\n handle?: string\n /** Public locale and market identity allowed across the browser boundary. */\n i18n?: Pick<\n WeaverseNextI18n,\n 'country' | 'label' | 'language' | 'locale' | 'pathPrefix'\n >\n /** Weaverse page type resolved by the active route. */\n pageType?: PageType\n /** Canonical same-origin route pathname. */\n pathname: string\n /** Sanitized serialized query string, including the leading `?` when set. */\n search: string\n}\n\n/**\n * Minimal runtime surface the in-place apply needs. Structural (not the\n * concrete `WeaverseNextRuntime` class) so unit tests can drive it with a\n * plain mock. `requestInfo` stays optional: a real `WeaverseNextRuntime`\n * always supplies it, but when absent the client omits `routeContext` and the\n * legacy `{ draftItem }` request is preserved.\n */\nexport interface RevalidateItemRuntimeLike {\n /** Live item stores keyed by Weaverse item ID. */\n itemInstances: Map<\n string,\n {\n /** Merge refreshed loader data into the live item store. */\n setData: (update: Record<string, unknown>) => unknown\n }\n >\n /** Active route identity used to construct the optional revalidation payload. */\n requestInfo?: WeaverseNextRequestInfo\n}\n\n/**\n * Strip denied (server-owned/transient) controls from a `search` string and\n * re-serialize it. Preserves ordinary application queries, duplicate keys,\n * encoded Unicode, and mode controls (`isDesignMode`, `isPreviewMode`,\n * `__revisionId`, `sectionType`). Returns `''` or a `?`-prefixed string.\n */\nexport function sanitizeRouteContextSearch(search: string): string {\n let params = new URLSearchParams(search)\n let denied = new Set(DENIED_ROUTE_CONTEXT_PARAMS)\n for (let key of [...new Set(params.keys())]) {\n if (denied.has(key.toLowerCase())) {\n params.delete(key)\n }\n }\n let serialized = params.toString()\n return serialized ? `?${serialized}` : ''\n}\n\n/**\n * Build the sanitized {@link WeaverseNextRevalidateRouteContext} from the\n * active runtime's request info. Returns `undefined` without a usable pathname\n * so the caller omits `routeContext` and keeps the legacy request. This is not\n * the security boundary — the route handler re-validates and re-sanitizes.\n */\nexport function buildWeaverseNextRevalidateRouteContext(\n requestInfo?: WeaverseNextRequestInfo\n): WeaverseNextRevalidateRouteContext | undefined {\n if (\n !requestInfo ||\n typeof requestInfo.pathname !== 'string' ||\n !requestInfo.pathname\n ) {\n return\n }\n\n let routeContext: WeaverseNextRevalidateRouteContext = {\n pathname: requestInfo.pathname,\n search: sanitizeRouteContextSearch(requestInfo.search ?? ''),\n }\n\n let { i18n } = requestInfo\n if (i18n && typeof i18n === 'object') {\n let picked: Record<string, string> = {}\n for (let key of ROUTE_CONTEXT_I18N_KEYS) {\n let value = i18n[key]\n if (typeof value === 'string') {\n picked[key] = value\n }\n }\n if (Object.keys(picked).length) {\n routeContext.i18n = picked\n }\n }\n\n if (\n requestInfo.pageType &&\n PageTypeSchema.safeParse(requestInfo.pageType).success\n ) {\n routeContext.pageType = requestInfo.pageType\n }\n\n if (\n typeof requestInfo.handle === 'string' &&\n requestInfo.handle &&\n requestInfo.handle.length <= MAX_ROUTE_CONTEXT_HANDLE_LENGTH\n ) {\n routeContext.handle = requestInfo.handle\n }\n\n return routeContext\n}\n\n/**\n * Studio per-item loader revalidation: POST the unsaved draft item (and, when\n * the runtime exposes route context, a sanitized `routeContext`) to the app's\n * revalidate route (see `createWeaverseNextRevalidateHandler`) and apply the\n * returned `loaderData` to the live item instance in place.\n *\n * No `router.refresh()` is involved, so the RSC tree is never re-applied — no\n * page remount, no scroll reset, and the draft payload never touches the URL.\n *\n * Throws on any failure (missing route, non-OK response, unknown item) so the\n * Builder Studio bridge can detect the miss and fall back to its legacy\n * refresh-based revalidation for this window.\n */\nexport async function revalidateWeaverseNextItem(\n runtime: RevalidateItemRuntimeLike,\n draftItem: WeaverseNextComponentData,\n endpoint: string = DEFAULT_REVALIDATE_ENDPOINT\n): Promise<void> {\n let instance = runtime.itemInstances.get(draftItem.id)\n if (!instance) {\n throw new Error(\n `[weaverse-next] No live item instance for \"${draftItem.id}\".`\n )\n }\n\n // Snapshot route identity at revalidation start so a later navigation cannot\n // retarget this in-flight request. Omit `routeContext` for legacy runtimes.\n let routeContext = buildWeaverseNextRevalidateRouteContext(\n runtime.requestInfo\n )\n let body = routeContext ? { draftItem, routeContext } : { draftItem }\n\n let response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error(\n `[weaverse-next] Revalidate endpoint responded ${response.status}.`\n )\n }\n\n let { loaderData } = (await response.json()) as { loaderData?: unknown }\n // The item's settings were already mutated by the Studio edit; only the\n // loader payload needs applying. `setData` merges and notifies subscribers,\n // and the new `loaderData` identity is what Builder's revalidation poll\n // observes for completion.\n instance.setData({ loaderData: loaderData ?? null })\n}\n","import type { PageSEOData } from '@weaverse/schema'\nimport type { Metadata } from 'next'\nimport type { WeaverseNextLoaderData, WeaverseNextPageData } from './types'\n\n/** Object containing an optional Weaverse page for SEO extraction. */\nexport interface WeaverseNextSeoPageContainer {\n /** Page whose published SEO fields should be formatted. */\n page?: WeaverseNextPageData | null\n}\n\n/** Input accepted by {@link getWeaverseNextSeoMetadata}. */\nexport type WeaverseNextSeoInput =\n | WeaverseNextLoaderData\n | WeaverseNextPageData\n | WeaverseNextSeoPageContainer\n | null\n | undefined\n\n/** Open Graph metadata accepted by Next.js's `Metadata` API. */\nexport type NextOpenGraph = NonNullable<Metadata['openGraph']>\n\n/** Twitter card metadata accepted by Next.js's `Metadata` API. */\nexport type NextTwitter = NonNullable<Metadata['twitter']>\n\n/**\n * Weaverse page SEO expressed as a subset of Next's `Metadata`. Assignable to\n * `Metadata` as-is: `const metadata: Metadata = getWeaverseNextSeoMetadata(data)`.\n */\nexport interface WeaverseNextSeoMetadata {\n /** Canonical URL settings for Next's metadata API. */\n alternates?: {\n /** Preferred URL for search indexing. */\n canonical?: string\n }\n /** Page meta description. */\n description?: string\n /** Page meta keywords. */\n keywords?: string\n /** Open Graph metadata normalized to Next's supported union. */\n openGraph?: NextOpenGraph\n /** Search crawler indexing and link-following directives. */\n robots: {\n /** Whether crawlers may follow links on the page. */\n follow: boolean\n /** Whether crawlers may index the page. */\n index: boolean\n }\n /** Page title. */\n title?: string\n /** Twitter card metadata normalized for Next's metadata API. */\n twitter?: NextTwitter\n}\n\nfunction hasOpenGraphContent(seo: PageSEOData['openGraph']): boolean {\n return Boolean(seo?.title || seo?.description || seo?.image || seo?.type)\n}\n\nfunction hasTwitterContent(seo: PageSEOData['twitter']): boolean {\n return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType)\n}\n\nfunction normalizeTwitterCard(\n cardType: NonNullable<PageSEOData['twitter']>['cardType']\n): 'summary' | 'summary_large_image' {\n return cardType === 'summary_large_image' ? 'summary_large_image' : 'summary'\n}\n\n/**\n * Map a Builder Open Graph type onto Next's `OpenGraphType` union. Every\n * Builder value except `product` exists in Next's union and is preserved\n * verbatim; `product` is not modeled by Next and makes `generateMetadata()`\n * throw, so it degrades to `website` — the closest renderable equivalent.\n */\nfunction normalizeOpenGraphType(\n type: NonNullable<PageSEOData['openGraph']>['type']\n): 'article' | 'profile' | 'video.other' | 'website' {\n if (type === 'article' || type === 'profile' || type === 'video.other') {\n return type\n }\n return 'website'\n}\n\n/**\n * Convert Builder SEO fields into metadata accepted by Next.js.\n *\n * Unsupported Open Graph product types and incomplete Twitter app/player\n * cards are safely downgraded to renderable Next.js variants.\n */\nexport function formatWeaverseNextSeoMetadata(\n seo: PageSEOData | null | undefined\n): WeaverseNextSeoMetadata {\n let index = seo?.robots?.index !== false\n let follow = seo?.robots?.follow !== false\n let metadata: WeaverseNextSeoMetadata = {\n robots: { index, follow },\n }\n\n if (!seo) {\n return metadata\n }\n\n if (seo.title) {\n metadata.title = seo.title\n }\n if (seo.description) {\n metadata.description = seo.description\n }\n if (seo.keywords) {\n metadata.keywords = seo.keywords\n }\n if (seo.canonicalUrl) {\n metadata.alternates = { canonical: seo.canonicalUrl }\n }\n\n let openGraph = seo.openGraph\n if (hasOpenGraphContent(openGraph)) {\n metadata.openGraph = {\n title: openGraph?.title,\n description: openGraph?.description,\n images: openGraph?.image ? [openGraph.image] : undefined,\n type: normalizeOpenGraphType(openGraph?.type),\n }\n }\n\n let twitter = seo.twitter\n if (hasTwitterContent(twitter)) {\n // Builder does not collect the descriptors Next requires for `app` cards\n // (Next throws when they are missing) or `player` cards (which render an\n // empty, useless card without a player URL and dimensions). Downgrade both\n // to `summary` rather than emitting metadata Next cannot render.\n metadata.twitter = {\n card: normalizeTwitterCard(twitter?.cardType),\n title: twitter?.title,\n description: twitter?.description,\n images: twitter?.image ? [twitter.image] : undefined,\n }\n }\n\n return metadata\n}\n\n/**\n * Read page SEO from loader data or a page payload and format it for Next.js\n * `generateMetadata`. Missing data returns indexable/followable robot defaults.\n */\nexport function getWeaverseNextSeoMetadata(\n data: WeaverseNextSeoInput\n): WeaverseNextSeoMetadata {\n if (!data) {\n return formatWeaverseNextSeoMetadata(null)\n }\n let page =\n 'items' in data && 'id' in data\n ? (data as WeaverseNextPageData)\n : ((data as WeaverseNextSeoPageContainer).page ?? null)\n return formatWeaverseNextSeoMetadata(page?.seo ?? null)\n}\n"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/loader.ts","../src/request-info.ts","../src/revalidate-item.ts","../src/seo.ts"],"names":[],"mappings":"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","file":"chunk-HRNAW6CA.mjs","sourcesContent":["import type { SchemaType } from '@weaverse/schema'\nimport type { WeaverseNextThemeSchema } from './types'\n\n/**\n * Generate default component data from a schema's `settings` inputs.\n *\n * Mirrors Hydrogen's `generateDataFromSchema` (settings → `{ [name]:\n * defaultValue }`) but is kept local so `@weaverse/next` doesn't depend on\n * `@weaverse/hydrogen`. Falls back to the deprecated `inspector` array when a\n * schema predates `settings`.\n */\nexport function generateDataFromSchema(\n schema: SchemaType | WeaverseNextThemeSchema | undefined\n): Record<string, unknown> {\n let data: Record<string, unknown> = {}\n if (!schema) {\n return data\n }\n\n let groups =\n (schema.settings as {\n inputs?: { name?: string; defaultValue?: unknown }[]\n }[]) ??\n (schema as { inspector?: typeof groups }).inspector ??\n []\n\n for (let group of groups) {\n for (let input of group?.inputs ?? []) {\n let { name, defaultValue } = input\n if (name && defaultValue !== null && defaultValue !== undefined) {\n data[name] = defaultValue\n }\n }\n }\n\n return data\n}\n","import type {\n WeaverseNextClient,\n WeaverseNextCommerceContext,\n WeaverseNextComponent,\n WeaverseNextComponentData,\n WeaverseNextLoaderData,\n WeaverseNextRequestContext,\n} from './types'\nimport { generateDataFromSchema } from './utils'\n\n/** Inputs for running registered component loaders over a page tree. */\nexport interface RunComponentLoadersArgs {\n /** Weaverse client containing the component registry. */\n client: WeaverseNextClient\n /** Request-scoped commerce override; defaults to `client.commerce`. */\n commerce?: WeaverseNextCommerceContext\n /** Request-context override; defaults to `client.requestContext`. */\n context?: WeaverseNextRequestContext\n /** Page payload to walk; defaults to `client.data`. */\n data?: WeaverseNextLoaderData | null\n}\n\nfunction isItemNode(node: unknown): node is WeaverseNextComponentData {\n return (\n typeof node === 'object' &&\n node !== null &&\n typeof (node as WeaverseNextComponentData).type === 'string'\n )\n}\n\nfunction cloneItem(item: WeaverseNextComponentData): WeaverseNextComponentData {\n return {\n ...item,\n children: Array.isArray(item.children)\n ? item.children.map((child) =>\n isItemNode(child) ? cloneItem(child) : { ...child }\n )\n : item.children,\n }\n}\n\n/**\n * Walk a serialized page tree and run each registered component's `loader`,\n * attaching the result to the item as `loaderData`.\n *\n * Loaders receive explicit `commerce.storefront` (and `weaverse.storefront` as\n * a compatibility alias via the client). Children are walked recursively when\n * they are inline item objects; flat `items` arrays are covered by the\n * top-level walk.\n *\n * @returns a cloned `data` object with `loaderData` attached to cloned items.\n */\nexport async function runWeaverseComponentLoaders(\n args: RunComponentLoadersArgs\n): Promise<WeaverseNextLoaderData | null> {\n let { client, context, commerce } = args\n let sourceData = args.data ?? client.data\n if (!sourceData) {\n return null\n }\n if (!sourceData.page.items.length) {\n return sourceData\n }\n\n let data: WeaverseNextLoaderData = {\n ...sourceData,\n page: {\n ...sourceData.page,\n items: sourceData.page.items.map(cloneItem),\n },\n }\n let items = data.page.items\n\n let componentMap = new Map<string, WeaverseNextComponent>()\n for (let component of client.components) {\n if (component?.schema?.type) {\n componentMap.set(component.schema.type, component)\n }\n }\n\n let visited = new Set<string>()\n let resolvedContext = context ?? client.requestContext\n let resolvedCommerce = commerce ?? client.commerce\n\n // Per-call `weaverse` alias. When a request-scoped `commerce` is passed, the\n // `weaverse.storefront` compatibility alias must resolve to *that* storefront,\n // not the client's construction-time commerce. Pilot-style loaders call\n // `weaverse.storefront.query(...)`, so a stale alias would query the wrong\n // (or an undefined) storefront. When no override is given we reuse the client\n // as-is to preserve its prototype getters.\n let resolvedWeaverse: WeaverseNextClient =\n resolvedCommerce === client.commerce\n ? client\n : {\n ...client,\n commerce: resolvedCommerce,\n storefront: resolvedCommerce?.storefront,\n }\n\n async function walk(nodes: WeaverseNextComponentData[]) {\n for (let item of nodes) {\n if (!item || visited.has(item.id)) {\n continue\n }\n visited.add(item.id)\n\n let component = componentMap.get(item.type)\n if (component?.loader) {\n try {\n item.loaderData = await component.loader({\n data: { ...generateDataFromSchema(component.schema), ...item.data },\n weaverse: resolvedWeaverse,\n context: resolvedContext,\n commerce: resolvedCommerce,\n })\n } catch (error) {\n console.warn('❌ Item loader run failed.', item.type, item.id, error)\n }\n }\n\n let children = item.children\n if (Array.isArray(children)) {\n let inlineChildren = children.filter(\n isItemNode\n ) as WeaverseNextComponentData[]\n if (inlineChildren.length) {\n await walk(inlineChildren)\n }\n }\n }\n }\n\n await walk(items)\n return data\n}\n","import type {\n WeaverseNextRequestContext,\n WeaverseNextRequestInfo,\n} from './types'\n\nfunction toUrl(value: string | URL): URL {\n return typeof value === 'string' ? new URL(value, 'http://localhost') : value\n}\n\nfunction getSearchParamsFromContext(\n context?: WeaverseNextRequestContext\n): URLSearchParams {\n if (context?.searchParams) {\n return new URLSearchParams(context.searchParams)\n }\n\n if (context?.url) {\n let url = toUrl(context.url)\n return new URLSearchParams(url.searchParams)\n }\n\n return new URLSearchParams()\n}\n\nfunction getPathnameFromContext(context?: WeaverseNextRequestContext): string {\n if (context?.pathname) {\n return context.pathname\n }\n\n if (context?.url) {\n let url = toUrl(context.url)\n return url.pathname || '/'\n }\n\n return '/'\n}\n\nfunction coerceQueryValue(value: string): string | boolean {\n if (value === 'true') {\n return true\n }\n if (value === 'false') {\n return false\n }\n return value\n}\n\nfunction toQueryRecord(\n searchParams: URLSearchParams\n): Record<string, string | boolean> {\n let queries: Record<string, string | boolean> = {}\n for (let [key, value] of searchParams.entries()) {\n queries[key] = coerceQueryValue(value)\n }\n return queries\n}\n\nconst TRANSIENT_STUDIO_PARAMS = new Set([\n 'weaverseDraftItem',\n // Legacy name from the original design-mode draft-item spike. Keep it out of\n // runtime requestInfo too, in case an older Builder bundle is in front of a\n // newer Next storefront.\n '__weaverseDraftItem',\n])\n\nfunction getRequestInfoSearchParams(\n context?: WeaverseNextRequestContext\n): URLSearchParams {\n let searchParams = new URLSearchParams(getSearchParamsFromContext(context))\n for (let key of TRANSIENT_STUDIO_PARAMS) {\n searchParams.delete(key)\n }\n return searchParams\n}\n\n/**\n * Build the Hydrogen-like request info shape consumed by the Studio bridge.\n * Duplicate query keys keep the last value, matching `URLSearchParams` iteration\n * assignment and Hydrogen's practical query-object behavior.\n */\nexport function buildWeaverseNextRequestInfo(\n context?: WeaverseNextRequestContext\n): WeaverseNextRequestInfo {\n let searchParams = getRequestInfoSearchParams(context)\n let search = searchParams.toString()\n\n return {\n pathname: getPathnameFromContext(context),\n search: search ? `?${search}` : '',\n queries: toQueryRecord(searchParams),\n ...(context?.i18n ? { i18n: context.i18n } : {}),\n ...(context?.pageType ? { pageType: context.pageType } : {}),\n ...(context?.handle ? { handle: context.handle } : {}),\n }\n}\n","import type { PageType } from '@weaverse/schema'\nimport { PageTypeSchema } from '@weaverse/schema'\nimport type {\n WeaverseNextComponentData,\n WeaverseNextI18n,\n WeaverseNextRequestInfo,\n} from './types'\n\n/** Default App Router endpoint used for Studio item-loader revalidation. */\nexport const DEFAULT_REVALIDATE_ENDPOINT = '/api/weaverse/revalidate'\n\nconst MAX_ROUTE_CONTEXT_HANDLE_LENGTH = 512\n\n/**\n * Server-owned and transient controls stripped from the revalidation route\n * context on both sides of the trust boundary. Compared case-insensitively, so\n * every entry is stored lowercased. Client sanitization keeps normal traffic\n * clean; the route handler repeats this deny-list as the security boundary.\n *\n * - Server-owned: a crafted `?weaverseProjectId=`/`?weaverseHost=` must never\n * influence server config resolution (see `getWeaverseNextConfigs`), and\n * credential-shaped params never ride along in route context.\n * - Transient: draft-item and RSC transport params never describe route identity.\n */\nexport const DENIED_ROUTE_CONTEXT_PARAMS = [\n 'weaverseprojectid',\n 'weaversehost',\n 'weaverseapikey',\n 'weaverseapibase',\n 'weaversepublicapibase',\n 'weaverseversion',\n 'projectid',\n 'weaversedraftitem',\n '__weaversedraftitem',\n '_rsc',\n]\n\n/** i18n fields allowed to cross the browser boundary. */\nexport const ROUTE_CONTEXT_I18N_KEYS = [\n 'country',\n 'label',\n 'language',\n 'locale',\n 'pathPrefix',\n] as const\n\n/**\n * JSON-safe route subset that may cross the browser boundary alongside the\n * draft item. Never carries headers, cookies, auth, env, project ID, Studio\n * host, API keys/bases, commerce clients, the request context, runtime, or\n * client. `search` is canonical (preserves duplicate keys); the derived\n * `queries` record is intentionally omitted.\n */\nexport interface WeaverseNextRevalidateRouteContext {\n /** Route handle used for page assignment. */\n handle?: string\n /** Public locale and market identity allowed across the browser boundary. */\n i18n?: Pick<\n WeaverseNextI18n,\n 'country' | 'label' | 'language' | 'locale' | 'pathPrefix'\n >\n /** Weaverse page type resolved by the active route. */\n pageType?: PageType\n /** Canonical same-origin route pathname. */\n pathname: string\n /** Sanitized serialized query string, including the leading `?` when set. */\n search: string\n}\n\n/**\n * Minimal runtime surface the in-place apply needs. Structural (not the\n * concrete `WeaverseNextRuntime` class) so unit tests can drive it with a\n * plain mock. `requestInfo` stays optional: a real `WeaverseNextRuntime`\n * always supplies it, but when absent the client omits `routeContext` and the\n * legacy `{ draftItem }` request is preserved.\n */\nexport interface RevalidateItemRuntimeLike {\n /** Live item stores keyed by Weaverse item ID. */\n itemInstances: Map<\n string,\n {\n /** Merge refreshed loader data into the live item store. */\n setData: (update: Record<string, unknown>) => unknown\n }\n >\n /** Active route identity used to construct the optional revalidation payload. */\n requestInfo?: WeaverseNextRequestInfo\n}\n\n/**\n * Strip denied (server-owned/transient) controls from a `search` string and\n * re-serialize it. Preserves ordinary application queries, duplicate keys,\n * encoded Unicode, and mode controls (`isDesignMode`, `isPreviewMode`,\n * `__revisionId`, `sectionType`). Returns `''` or a `?`-prefixed string.\n */\nexport function sanitizeRouteContextSearch(search: string): string {\n let params = new URLSearchParams(search)\n let denied = new Set(DENIED_ROUTE_CONTEXT_PARAMS)\n for (let key of [...new Set(params.keys())]) {\n if (denied.has(key.toLowerCase())) {\n params.delete(key)\n }\n }\n let serialized = params.toString()\n return serialized ? `?${serialized}` : ''\n}\n\n/**\n * Build the sanitized {@link WeaverseNextRevalidateRouteContext} from the\n * active runtime's request info. Returns `undefined` without a usable pathname\n * so the caller omits `routeContext` and keeps the legacy request. This is not\n * the security boundary — the route handler re-validates and re-sanitizes.\n */\nexport function buildWeaverseNextRevalidateRouteContext(\n requestInfo?: WeaverseNextRequestInfo\n): WeaverseNextRevalidateRouteContext | undefined {\n if (\n !requestInfo ||\n typeof requestInfo.pathname !== 'string' ||\n !requestInfo.pathname\n ) {\n return\n }\n\n let routeContext: WeaverseNextRevalidateRouteContext = {\n pathname: requestInfo.pathname,\n search: sanitizeRouteContextSearch(requestInfo.search ?? ''),\n }\n\n let { i18n } = requestInfo\n if (i18n && typeof i18n === 'object') {\n let picked: Record<string, string> = {}\n for (let key of ROUTE_CONTEXT_I18N_KEYS) {\n let value = i18n[key]\n if (typeof value === 'string') {\n picked[key] = value\n }\n }\n if (Object.keys(picked).length) {\n routeContext.i18n = picked\n }\n }\n\n if (\n requestInfo.pageType &&\n PageTypeSchema.safeParse(requestInfo.pageType).success\n ) {\n routeContext.pageType = requestInfo.pageType\n }\n\n if (\n typeof requestInfo.handle === 'string' &&\n requestInfo.handle &&\n requestInfo.handle.length <= MAX_ROUTE_CONTEXT_HANDLE_LENGTH\n ) {\n routeContext.handle = requestInfo.handle\n }\n\n return routeContext\n}\n\n/**\n * Studio per-item loader revalidation: POST the unsaved draft item (and, when\n * the runtime exposes route context, a sanitized `routeContext`) to the app's\n * revalidate route (see `createWeaverseNextRevalidateHandler`) and apply the\n * returned `loaderData` to the live item instance in place.\n *\n * No `router.refresh()` is involved, so the RSC tree is never re-applied — no\n * page remount, no scroll reset, and the draft payload never touches the URL.\n *\n * Throws on any failure (missing route, non-OK response, unknown item) so the\n * Builder Studio bridge can detect the miss and fall back to its legacy\n * refresh-based revalidation for this window.\n */\nexport async function revalidateWeaverseNextItem(\n runtime: RevalidateItemRuntimeLike,\n draftItem: WeaverseNextComponentData,\n endpoint: string = DEFAULT_REVALIDATE_ENDPOINT\n): Promise<void> {\n let instance = runtime.itemInstances.get(draftItem.id)\n if (!instance) {\n throw new Error(\n `[weaverse-next] No live item instance for \"${draftItem.id}\".`\n )\n }\n\n // Snapshot route identity at revalidation start so a later navigation cannot\n // retarget this in-flight request. Omit `routeContext` for legacy runtimes.\n let routeContext = buildWeaverseNextRevalidateRouteContext(\n runtime.requestInfo\n )\n let body = routeContext ? { draftItem, routeContext } : { draftItem }\n\n let response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error(\n `[weaverse-next] Revalidate endpoint responded ${response.status}.`\n )\n }\n\n let { loaderData } = (await response.json()) as { loaderData?: unknown }\n // The item's settings were already mutated by the Studio edit; only the\n // loader payload needs applying. `setData` merges and notifies subscribers,\n // and the new `loaderData` identity is what Builder's revalidation poll\n // observes for completion.\n instance.setData({ loaderData: loaderData ?? null })\n}\n","import type { PageSEOData } from '@weaverse/schema'\nimport type { Metadata } from 'next'\nimport type { WeaverseNextLoaderData, WeaverseNextPageData } from './types'\n\n/** Object containing an optional Weaverse page for SEO extraction. */\nexport interface WeaverseNextSeoPageContainer {\n /** Page whose published SEO fields should be formatted. */\n page?: WeaverseNextPageData | null\n}\n\n/** Input accepted by {@link getWeaverseNextSeoMetadata}. */\nexport type WeaverseNextSeoInput =\n | WeaverseNextLoaderData\n | WeaverseNextPageData\n | WeaverseNextSeoPageContainer\n | null\n | undefined\n\n/** Open Graph metadata accepted by Next.js's `Metadata` API. */\nexport type NextOpenGraph = NonNullable<Metadata['openGraph']>\n\n/** Twitter card metadata accepted by Next.js's `Metadata` API. */\nexport type NextTwitter = NonNullable<Metadata['twitter']>\n\n/**\n * Weaverse page SEO expressed as a subset of Next's `Metadata`. Assignable to\n * `Metadata` as-is: `const metadata: Metadata = getWeaverseNextSeoMetadata(data)`.\n */\nexport interface WeaverseNextSeoMetadata {\n /** Canonical URL settings for Next's metadata API. */\n alternates?: {\n /** Preferred URL for search indexing. */\n canonical?: string\n }\n /** Page meta description. */\n description?: string\n /** Page meta keywords. */\n keywords?: string\n /** Open Graph metadata normalized to Next's supported union. */\n openGraph?: NextOpenGraph\n /** Search crawler indexing and link-following directives. */\n robots: {\n /** Whether crawlers may follow links on the page. */\n follow: boolean\n /** Whether crawlers may index the page. */\n index: boolean\n }\n /** Page title. */\n title?: string\n /** Twitter card metadata normalized for Next's metadata API. */\n twitter?: NextTwitter\n}\n\nfunction hasOpenGraphContent(seo: PageSEOData['openGraph']): boolean {\n return Boolean(seo?.title || seo?.description || seo?.image || seo?.type)\n}\n\nfunction hasTwitterContent(seo: PageSEOData['twitter']): boolean {\n return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType)\n}\n\nfunction normalizeTwitterCard(\n cardType: NonNullable<PageSEOData['twitter']>['cardType']\n): 'summary' | 'summary_large_image' {\n return cardType === 'summary_large_image' ? 'summary_large_image' : 'summary'\n}\n\n/**\n * Map a Builder Open Graph type onto Next's `OpenGraphType` union. Every\n * Builder value except `product` exists in Next's union and is preserved\n * verbatim; `product` is not modeled by Next and makes `generateMetadata()`\n * throw, so it degrades to `website` — the closest renderable equivalent.\n */\nfunction normalizeOpenGraphType(\n type: NonNullable<PageSEOData['openGraph']>['type']\n): 'article' | 'profile' | 'video.other' | 'website' {\n if (type === 'article' || type === 'profile' || type === 'video.other') {\n return type\n }\n return 'website'\n}\n\n/**\n * Convert Builder SEO fields into metadata accepted by Next.js.\n *\n * Unsupported Open Graph product types and incomplete Twitter app/player\n * cards are safely downgraded to renderable Next.js variants.\n */\nexport function formatWeaverseNextSeoMetadata(\n seo: PageSEOData | null | undefined\n): WeaverseNextSeoMetadata {\n let index = seo?.robots?.index !== false\n let follow = seo?.robots?.follow !== false\n let metadata: WeaverseNextSeoMetadata = {\n robots: { index, follow },\n }\n\n if (!seo) {\n return metadata\n }\n\n if (seo.title) {\n metadata.title = seo.title\n }\n if (seo.description) {\n metadata.description = seo.description\n }\n if (seo.keywords) {\n metadata.keywords = seo.keywords\n }\n if (seo.canonicalUrl) {\n metadata.alternates = { canonical: seo.canonicalUrl }\n }\n\n let openGraph = seo.openGraph\n if (hasOpenGraphContent(openGraph)) {\n metadata.openGraph = {\n title: openGraph?.title,\n description: openGraph?.description,\n images: openGraph?.image ? [openGraph.image] : undefined,\n type: normalizeOpenGraphType(openGraph?.type),\n }\n }\n\n let twitter = seo.twitter\n if (hasTwitterContent(twitter)) {\n // Builder does not collect the descriptors Next requires for `app` cards\n // (Next throws when they are missing) or `player` cards (which render an\n // empty, useless card without a player URL and dimensions). Downgrade both\n // to `summary` rather than emitting metadata Next cannot render.\n metadata.twitter = {\n card: normalizeTwitterCard(twitter?.cardType),\n title: twitter?.title,\n description: twitter?.description,\n images: twitter?.image ? [twitter.image] : undefined,\n }\n }\n\n return metadata\n}\n\n/**\n * Read page SEO from loader data or a page payload and format it for Next.js\n * `generateMetadata`. Missing data returns indexable/followable robot defaults.\n */\nexport function getWeaverseNextSeoMetadata(\n data: WeaverseNextSeoInput\n): WeaverseNextSeoMetadata {\n if (!data) {\n return formatWeaverseNextSeoMetadata(null)\n }\n let page =\n 'items' in data && 'id' in data\n ? (data as WeaverseNextPageData)\n : ((data as WeaverseNextSeoPageContainer).page ?? null)\n return formatWeaverseNextSeoMetadata(page?.seo ?? null)\n}\n"]}
package/dist/index.js CHANGED
@@ -674,6 +674,12 @@ function getConfigString(configs, key) {
674
674
  let value = configs?.[key];
675
675
  return typeof value === "string" ? value : void 0;
676
676
  }
677
+ function resolveModeFlag(config, key) {
678
+ return config.client?.requestContext?.[key] === true || config.data.configs?.[key] === true;
679
+ }
680
+ function resolveSectionType(config) {
681
+ return config.client?.requestContext?.sectionType || getConfigString(config.data.configs, "sectionType") || void 0;
682
+ }
677
683
  function getRecordString(record, key) {
678
684
  let value = record?.[key];
679
685
  return typeof value === "string" ? value : void 0;
@@ -768,22 +774,26 @@ var WeaverseNextRuntime = class extends react.Weaverse {
768
774
  let page = getRenderablePage(data);
769
775
  let configs = data.configs;
770
776
  let requestContext = client?.requestContext;
777
+ let isDesignMode = resolveModeFlag(config, "isDesignMode");
778
+ let isPreviewMode = resolveModeFlag(config, "isPreviewMode");
779
+ let isRevisionPreview = resolveModeFlag(config, "isRevisionPreview");
780
+ let sectionType = resolveSectionType(config);
771
781
  let projectId = resolveProjectId(client, data, page.id);
772
782
  ensureNextItemConstructor();
773
783
  super({
774
784
  projectId,
775
785
  data: page,
776
- isDesignMode: requestContext?.isDesignMode ?? false,
786
+ isDesignMode,
777
787
  weaverseHost: getConfigString(configs, "weaverseHost"),
778
788
  weaverseVersion: getConfigString(configs, "weaverseVersion")
779
789
  });
780
790
  this.pageId = page.id;
781
791
  this.dataContext = resolveDataContext(config);
782
792
  this.requestInfo = buildWeaverseNextRequestInfo(requestContext);
783
- this.isDesignMode = requestContext?.isDesignMode ?? false;
784
- this.isPreviewMode = requestContext?.isPreviewMode ?? false;
785
- this.isRevisionPreview = requestContext?.isRevisionPreview ?? false;
786
- this.sectionType = requestContext?.sectionType;
793
+ this.isDesignMode = isDesignMode;
794
+ this.isPreviewMode = isPreviewMode;
795
+ this.isRevisionPreview = isRevisionPreview;
796
+ this.sectionType = sectionType;
787
797
  let translationStore = config.translationStore ?? new TranslationStore();
788
798
  this.internal = {
789
799
  merchantOverrides: config.merchantOverrides,
@@ -915,7 +925,11 @@ function createWeaverseNextRuntime(config) {
915
925
  let existing = runtimeWindow?.__weaverses?.[page.id];
916
926
  let requestKey = getRuntimeKey(page.id, requestInfo);
917
927
  if (existing?.__weaverseNextRequestKey === requestKey) {
918
- let nextIsDesignMode = config.client?.requestContext?.isDesignMode ?? false;
928
+ let configs = config.data.configs;
929
+ let nextIsDesignMode = resolveModeFlag(config, "isDesignMode");
930
+ let nextIsPreviewMode = resolveModeFlag(config, "isPreviewMode");
931
+ let nextIsRevisionPreview = resolveModeFlag(config, "isRevisionPreview");
932
+ let nextSectionType = resolveSectionType(config);
919
933
  if (existing.isDesignMode || nextIsDesignMode) {
920
934
  existing.pendingProjectData = void 0;
921
935
  } else {
@@ -926,11 +940,11 @@ function createWeaverseNextRuntime(config) {
926
940
  existing.requestInfo = requestInfo;
927
941
  existing.projectId = resolveProjectId(config.client, config.data, page.id);
928
942
  existing.isDesignMode = nextIsDesignMode;
929
- existing.isPreviewMode = config.client?.requestContext?.isPreviewMode ?? false;
930
- existing.isRevisionPreview = config.client?.requestContext?.isRevisionPreview ?? false;
931
- existing.sectionType = config.client?.requestContext?.sectionType;
932
- existing.weaverseHost = getConfigString(config.data.configs, "weaverseHost") ?? existing.weaverseHost;
933
- existing.weaverseVersion = getConfigString(config.data.configs, "weaverseVersion") ?? existing.weaverseVersion;
943
+ existing.isPreviewMode = nextIsPreviewMode;
944
+ existing.isRevisionPreview = nextIsRevisionPreview;
945
+ existing.sectionType = nextSectionType;
946
+ existing.weaverseHost = getConfigString(configs, "weaverseHost") ?? existing.weaverseHost;
947
+ existing.weaverseVersion = getConfigString(configs, "weaverseVersion") ?? existing.weaverseVersion;
934
948
  existing.internal.pageAssignment = config.data.pageAssignment;
935
949
  existing.internal.project = config.data.project;
936
950
  if (config.navigate) {