@weaverse/next 0.1.0-alpha.11 → 0.1.0-alpha.12

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
@@ -245,7 +245,7 @@ let theme = await weaverse.loadThemeSettings()
245
245
  | Member | Description |
246
246
  | --- | --- |
247
247
  | `loadPage(input?)` | Fetches the page from `/api/public/project`, builds `WeaverseNextLoaderData`, runs component loaders, and returns page/project/config data. In section preview mode it can synthesize a single-section preview page. |
248
- | `loadThemeSettings(options?)` | Fetches `/api/public/project_configs`, merges schema defaults under merchant settings, returns theme settings/static content/schema data for design mode. |
248
+ | `loadThemeSettings(options?)` | Fetches `/api/public/project_configs`, merges schema defaults under merchant settings, returns theme settings/static content/schema data for design mode. When the theme schema declares `i18n` and `requestContext.i18n` supplies both `language` and `country`, it also fetches locale static-text overrides from `/api/translation/static` in parallel and returns them as `merchantOverrides`. A failed or skipped overrides fetch never fails theme settings. |
249
249
  | `fetchCustomPages(options?)` | Fetches published custom pages from `/api/public/v1/projects/:projectId/custom-pages` for sitemap generation, paginating automatically and returning partial results if a later page fails. |
250
250
  | `fetchWithCache<T>(url, options?)` | Next-aware fetch helper. Uses `cache: 'no-store'` in design/revision preview and `next: { revalidate, tags }` in published mode. |
251
251
  | `resolveProjectId()` / `projectId` | Resolves project id from Studio query, config function/string, or env. |
@@ -284,12 +284,13 @@ Use the server entry in `generateMetadata()` to map Weaverse `page.seo` into a N
284
284
 
285
285
  ```ts
286
286
  // app/pages/[handle]/page.tsx
287
+ import type { Metadata } from 'next'
287
288
  import { getWeaverseNextSeoMetadata } from '@weaverse/next/server'
288
289
 
289
290
  export async function generateMetadata(props: {
290
291
  params: Promise<{ handle: string }>
291
292
  searchParams: Promise<Record<string, string | string[] | undefined>>
292
- }) {
293
+ }): Promise<Metadata> {
293
294
  let { handle } = await props.params
294
295
  let weaverse = await getWeaverseServerClient(props.searchParams, `/pages/${handle}`)
295
296
  let data = await weaverse.loadPage({ type: 'PAGE', handle })
@@ -297,7 +298,17 @@ export async function generateMetadata(props: {
297
298
  }
298
299
  ```
299
300
 
300
- The helper is pure and does not import `next/*`; its return shape is intentionally structural so the app can type it as `Metadata` if desired.
301
+ The helper is pure and has no runtime dependency on `next`; it only imports the
302
+ `Metadata` type. Its return value is directly assignable to Next `Metadata`.
303
+
304
+ Two Builder-only SEO values are normalized so Next can always render the result:
305
+
306
+ - Open Graph type `product` falls back to `website` (Next throws on `product`).
307
+ The other Builder types — `website`, `article`, `profile`, `video.other` — are
308
+ passed through unchanged.
309
+ - Twitter card types `app` and `player` fall back to `summary`, because Builder
310
+ does not collect the extra descriptors those cards need (Next throws without
311
+ the `app` fields, and a `player` card without a player URL renders empty).
301
312
 
302
313
  ### Custom pages for sitemap generation
303
314
 
@@ -165,6 +165,15 @@ function hasOpenGraphContent(seo) {
165
165
  function hasTwitterContent(seo) {
166
166
  return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType);
167
167
  }
168
+ function normalizeTwitterCard(cardType) {
169
+ return cardType === "summary_large_image" ? "summary_large_image" : "summary";
170
+ }
171
+ function normalizeOpenGraphType(type) {
172
+ if (type === "article" || type === "profile" || type === "video.other") {
173
+ return type;
174
+ }
175
+ return "website";
176
+ }
168
177
  function formatWeaverseNextSeoMetadata(seo) {
169
178
  let index = seo?.robots?.index !== false;
170
179
  let follow = seo?.robots?.follow !== false;
@@ -192,13 +201,13 @@ function formatWeaverseNextSeoMetadata(seo) {
192
201
  title: openGraph?.title,
193
202
  description: openGraph?.description,
194
203
  images: openGraph?.image ? [openGraph.image] : void 0,
195
- type: openGraph?.type || "website"
204
+ type: normalizeOpenGraphType(openGraph?.type)
196
205
  };
197
206
  }
198
207
  let twitter = seo.twitter;
199
208
  if (hasTwitterContent(twitter)) {
200
209
  metadata.twitter = {
201
- card: twitter?.cardType || "summary",
210
+ card: normalizeTwitterCard(twitter?.cardType),
202
211
  title: twitter?.title,
203
212
  description: twitter?.description,
204
213
  images: twitter?.image ? [twitter.image] : void 0
@@ -215,5 +224,5 @@ function getWeaverseNextSeoMetadata(data) {
215
224
  }
216
225
 
217
226
  export { buildWeaverseNextRequestInfo, formatWeaverseNextSeoMetadata, generateDataFromSchema, getWeaverseNextSeoMetadata, runWeaverseComponentLoaders };
218
- //# sourceMappingURL=chunk-XXGMPTWH.mjs.map
219
- //# sourceMappingURL=chunk-XXGMPTWH.mjs.map
227
+ //# sourceMappingURL=chunk-MQ6N5ODA.mjs.map
228
+ //# sourceMappingURL=chunk-MQ6N5ODA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/loader.ts","../src/request-info.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;;;AClBA,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;;;AC7HA,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;AAAC,GAChD;AACF;;;AClEA,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;AAEO,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;AAEO,SAAS,2BACd,IAAA,EAMyB;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,KAAgD,IAAA,IAAQ,IAAA;AAChE,EAAA,OAAO,6BAAA,CAA8B,IAAA,EAAM,GAAA,IAAO,IAAI,CAAA;AACxD","file":"chunk-MQ6N5ODA.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\nexport interface RunComponentLoadersArgs {\n client: WeaverseNextClient\n commerce?: WeaverseNextCommerceContext\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 }\n}\n","import type { PageSEOData } from '@weaverse/schema'\nimport type { Metadata } from 'next'\nimport type { WeaverseNextLoaderData, WeaverseNextPageData } from './types'\n\n/** Next's own `openGraph` / `twitter` unions, reused so the helper's result\n * is directly assignable to `Metadata` without a consumer-side adapter. */\ntype NextOpenGraph = NonNullable<Metadata['openGraph']>\ntype 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 alternates?: { canonical?: string }\n description?: string\n keywords?: string\n openGraph?: NextOpenGraph\n robots: {\n follow: boolean\n index: boolean\n }\n title?: string\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\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\nexport function getWeaverseNextSeoMetadata(\n data:\n | WeaverseNextLoaderData\n | WeaverseNextPageData\n | { page?: WeaverseNextPageData | null }\n | null\n | undefined\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 { page?: WeaverseNextPageData | null }).page ?? null)\n return formatWeaverseNextSeoMetadata(page?.seo ?? null)\n}\n"]}
package/dist/index.d.mts CHANGED
@@ -2,11 +2,12 @@ import { WeaverseItemStore, Weaverse, ElementData } from '@weaverse/react';
2
2
  export { isBrowser, isIframe, useChildInstances, useItemInstance, useParentInstance, useWeaverse } from '@weaverse/react';
3
3
  import { SchemaType } from '@weaverse/schema';
4
4
  export * from '@weaverse/schema';
5
- import { h as WeaverseNextClientConfig, g as WeaverseNextClient, i as WeaverseNextCommerceContext, x as WeaverseNextRequestContext, t as WeaverseNextLoaderData, a as TranslationStore, y as WeaverseNextRequestInfo, k as WeaverseNextComponentData, J as WeaverseNextThemeSettingsStore, G as WeaverseNextThemeSchema, A as WeaverseNextRuntimeInternal, N as WeaverseNextTranslationMap, v as WeaverseNextPageData, z as WeaverseNextRuntimeConfig, K as WeaverseNextTranslationChanges } from './seo-CgO21rLg.mjs';
6
- export { T as ThemeTextStore, W as WeaverseArticle, b as WeaverseBlog, c as WeaverseCollection, d as WeaverseMetaObject, j as WeaverseNextComponent, l as WeaverseNextComponentLoaderArgs, m as WeaverseNextComponentProps, o as WeaverseNextCustomPageEntry, p as WeaverseNextFetchCustomPagesOptions, r as WeaverseNextI18n, s as WeaverseNextLoadPageInput, u as WeaverseNextPageAssignment, B as WeaverseNextSeoMetadata, F as WeaverseNextStorefront, L as WeaverseNextTranslationEntry, M as WeaverseNextTranslationItemEntry, O as WeaverseNextTranslationMapEntry, P as WeaverseProduct, Q as formatWeaverseNextSeoMetadata, R as getWeaverseNextSeoMetadata } from './seo-CgO21rLg.mjs';
5
+ import { h as WeaverseNextClientConfig, g as WeaverseNextClient, i as WeaverseNextCommerceContext, x as WeaverseNextRequestContext, t as WeaverseNextLoaderData, a as TranslationStore, y as WeaverseNextRequestInfo, k as WeaverseNextComponentData, J as WeaverseNextThemeSettingsStore, G as WeaverseNextThemeSchema, A as WeaverseNextRuntimeInternal, N as WeaverseNextTranslationMap, v as WeaverseNextPageData, z as WeaverseNextRuntimeConfig, K as WeaverseNextTranslationChanges } from './seo-CrEFl_Up.mjs';
6
+ export { T as ThemeTextStore, W as WeaverseArticle, b as WeaverseBlog, c as WeaverseCollection, d as WeaverseMetaObject, j as WeaverseNextComponent, l as WeaverseNextComponentLoaderArgs, m as WeaverseNextComponentProps, o as WeaverseNextCustomPageEntry, p as WeaverseNextFetchCustomPagesOptions, r as WeaverseNextI18n, s as WeaverseNextLoadPageInput, u as WeaverseNextPageAssignment, B as WeaverseNextSeoMetadata, F as WeaverseNextStorefront, L as WeaverseNextTranslationEntry, M as WeaverseNextTranslationItemEntry, O as WeaverseNextTranslationMapEntry, P as WeaverseProduct, Q as formatWeaverseNextSeoMetadata, R as getWeaverseNextSeoMetadata } from './seo-CrEFl_Up.mjs';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
  import * as react from 'react';
9
9
  import { ReactNode } from 'react';
10
+ import 'next';
10
11
 
11
12
  /**
12
13
  * Create a {@link WeaverseNextClient} from explicit config.
package/dist/index.d.ts CHANGED
@@ -2,11 +2,12 @@ import { WeaverseItemStore, Weaverse, ElementData } from '@weaverse/react';
2
2
  export { isBrowser, isIframe, useChildInstances, useItemInstance, useParentInstance, useWeaverse } from '@weaverse/react';
3
3
  import { SchemaType } from '@weaverse/schema';
4
4
  export * from '@weaverse/schema';
5
- import { h as WeaverseNextClientConfig, g as WeaverseNextClient, i as WeaverseNextCommerceContext, x as WeaverseNextRequestContext, t as WeaverseNextLoaderData, a as TranslationStore, y as WeaverseNextRequestInfo, k as WeaverseNextComponentData, J as WeaverseNextThemeSettingsStore, G as WeaverseNextThemeSchema, A as WeaverseNextRuntimeInternal, N as WeaverseNextTranslationMap, v as WeaverseNextPageData, z as WeaverseNextRuntimeConfig, K as WeaverseNextTranslationChanges } from './seo-CgO21rLg.js';
6
- export { T as ThemeTextStore, W as WeaverseArticle, b as WeaverseBlog, c as WeaverseCollection, d as WeaverseMetaObject, j as WeaverseNextComponent, l as WeaverseNextComponentLoaderArgs, m as WeaverseNextComponentProps, o as WeaverseNextCustomPageEntry, p as WeaverseNextFetchCustomPagesOptions, r as WeaverseNextI18n, s as WeaverseNextLoadPageInput, u as WeaverseNextPageAssignment, B as WeaverseNextSeoMetadata, F as WeaverseNextStorefront, L as WeaverseNextTranslationEntry, M as WeaverseNextTranslationItemEntry, O as WeaverseNextTranslationMapEntry, P as WeaverseProduct, Q as formatWeaverseNextSeoMetadata, R as getWeaverseNextSeoMetadata } from './seo-CgO21rLg.js';
5
+ import { h as WeaverseNextClientConfig, g as WeaverseNextClient, i as WeaverseNextCommerceContext, x as WeaverseNextRequestContext, t as WeaverseNextLoaderData, a as TranslationStore, y as WeaverseNextRequestInfo, k as WeaverseNextComponentData, J as WeaverseNextThemeSettingsStore, G as WeaverseNextThemeSchema, A as WeaverseNextRuntimeInternal, N as WeaverseNextTranslationMap, v as WeaverseNextPageData, z as WeaverseNextRuntimeConfig, K as WeaverseNextTranslationChanges } from './seo-CrEFl_Up.js';
6
+ export { T as ThemeTextStore, W as WeaverseArticle, b as WeaverseBlog, c as WeaverseCollection, d as WeaverseMetaObject, j as WeaverseNextComponent, l as WeaverseNextComponentLoaderArgs, m as WeaverseNextComponentProps, o as WeaverseNextCustomPageEntry, p as WeaverseNextFetchCustomPagesOptions, r as WeaverseNextI18n, s as WeaverseNextLoadPageInput, u as WeaverseNextPageAssignment, B as WeaverseNextSeoMetadata, F as WeaverseNextStorefront, L as WeaverseNextTranslationEntry, M as WeaverseNextTranslationItemEntry, O as WeaverseNextTranslationMapEntry, P as WeaverseProduct, Q as formatWeaverseNextSeoMetadata, R as getWeaverseNextSeoMetadata } from './seo-CrEFl_Up.js';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
  import * as react from 'react';
9
9
  import { ReactNode } from 'react';
10
+ import 'next';
10
11
 
11
12
  /**
12
13
  * Create a {@link WeaverseNextClient} from explicit config.
package/dist/index.js CHANGED
@@ -1134,6 +1134,15 @@ function hasOpenGraphContent(seo) {
1134
1134
  function hasTwitterContent(seo) {
1135
1135
  return Boolean(seo?.title || seo?.description || seo?.image || seo?.cardType);
1136
1136
  }
1137
+ function normalizeTwitterCard(cardType) {
1138
+ return cardType === "summary_large_image" ? "summary_large_image" : "summary";
1139
+ }
1140
+ function normalizeOpenGraphType(type) {
1141
+ if (type === "article" || type === "profile" || type === "video.other") {
1142
+ return type;
1143
+ }
1144
+ return "website";
1145
+ }
1137
1146
  function formatWeaverseNextSeoMetadata(seo) {
1138
1147
  let index = seo?.robots?.index !== false;
1139
1148
  let follow = seo?.robots?.follow !== false;
@@ -1161,13 +1170,13 @@ function formatWeaverseNextSeoMetadata(seo) {
1161
1170
  title: openGraph?.title,
1162
1171
  description: openGraph?.description,
1163
1172
  images: openGraph?.image ? [openGraph.image] : void 0,
1164
- type: openGraph?.type || "website"
1173
+ type: normalizeOpenGraphType(openGraph?.type)
1165
1174
  };
1166
1175
  }
1167
1176
  let twitter = seo.twitter;
1168
1177
  if (hasTwitterContent(twitter)) {
1169
1178
  metadata.twitter = {
1170
- card: twitter?.cardType || "summary",
1179
+ card: normalizeTwitterCard(twitter?.cardType),
1171
1180
  title: twitter?.title,
1172
1181
  description: twitter?.description,
1173
1182
  images: twitter?.image ? [twitter.image] : void 0