@weaverse/next 0.1.0-alpha.0 → 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # @weaverse/next
2
+
3
+ Next.js App Router integration for building Weaverse-powered storefronts.
4
+
5
+ This package adapts the Weaverse runtime (component registry, page-tree
6
+ rendering, Studio bridge) to Next.js without depending on React Router or
7
+ Hydrogen. It mirrors the `@weaverse/hydrogen` contract where it makes sense, but
8
+ takes plain, framework-neutral inputs (URL, headers, search params) instead of a
9
+ React Router `Request`/`LoaderFunctionArgs`.
10
+
11
+ ## Entrypoints
12
+
13
+ | Import | Use in | Purpose |
14
+ | --- | --- | --- |
15
+ | `@weaverse/next` | Client & server | Registry, renderer, provider, hooks, Studio runtime/bridge. |
16
+ | `@weaverse/next/server` | Server only | Server-side Weaverse client that fetches page/theme data from the Weaverse API. |
17
+
18
+ > The server-side data-loading API lives under **`@weaverse/next/server`** so the
19
+ > root entry stays free of server-only fetch assumptions and safe to import from
20
+ > client components.
21
+
22
+ ## Package structure
23
+
24
+ ```text
25
+ packages/next/src/
26
+ index.ts # root (@weaverse/next) public exports
27
+ server.ts # server (@weaverse/next/server) public exports
28
+ client.ts # createWeaverseNextClient (injected-fetcher client)
29
+ loader.ts # runWeaverseComponentLoaders (server-side section loaders)
30
+ registry.ts # register components with the Weaverse element registry
31
+ renderer.tsx # WeaverseNextRenderer (page-tree → React)
32
+ provider.tsx # WeaverseNextProvider (client context for hooks)
33
+ hooks.ts # useThemeSettings / useWeaversePageData / ...
34
+ runtime.ts # WeaverseNextRuntime + Studio binding
35
+ studio-bridge.tsx # Studio script injection / connect
36
+ request-info.ts # buildWeaverseNextRequestInfo (Studio-compatible)
37
+ theme-settings-store.ts
38
+ types.ts # shared types (root + server)
39
+ server/
40
+ server-client.ts # createWeaverseNextServerClient
41
+ configs.ts # getWeaverseNextConfigs (host/api-base/flags/env)
42
+ normalize-page-url.ts
43
+ ```
44
+
45
+ ## Root API (`@weaverse/next`)
46
+
47
+ - `createWeaverseNextClient(config)` — request-safe client that delegates network
48
+ I/O to injected `fetchPage` / `fetchThemeSettings`.
49
+ - `WeaverseNextProvider` / `WeaverseNextRenderer` — client provider + page-tree
50
+ renderer.
51
+ - `runWeaverseComponentLoaders(args)` — walk a page tree and run each registered
52
+ component's `loader` server-side.
53
+ - Hooks: `useThemeSettings`, `useWeaversePageData`, `useWeaverseRootData`,
54
+ `useWeaverseCommerce` (plus re-exported `@weaverse/react` hooks).
55
+ - Studio helpers: `WeaverseNextRuntime`, `createWeaverseNextRuntime`,
56
+ `bindWeaverseNextStudioRuntime`, `WeaverseNextStudioBridge`,
57
+ `WeaverseNextStudioConnect`, `resolveWeaverseNextStudioScriptSrc`,
58
+ `buildWeaverseNextRequestInfo`, `createWeaverseNextThemeSettingsStore`.
59
+ - `createSchema` (re-exported from `@weaverse/schema`).
60
+
61
+ ## Server API (`@weaverse/next/server`)
62
+
63
+ `createWeaverseNextServerClient(config)` returns a client that performs the real
64
+ Weaverse public API fetch — the equivalent of Hydrogen's
65
+ `context.weaverse.loadPage(...)`.
66
+
67
+ ```ts
68
+ import { createWeaverseNextServerClient } from '@weaverse/next/server'
69
+ import { components } from '@/weaverse/components'
70
+
71
+ export async function loadWeaverse(request: Request, storefront: Storefront) {
72
+ let url = new URL(request.url)
73
+ let weaverse = createWeaverseNextServerClient({
74
+ projectId: process.env.WEAVERSE_PROJECT_ID, // string | (ctx) => string | Promise<string>
75
+ components,
76
+ themeSchema,
77
+ commerce: { storefront },
78
+ env: process.env,
79
+ requestContext: {
80
+ url,
81
+ headers: request.headers,
82
+ searchParams: url.searchParams,
83
+ i18n: storefront.i18n,
84
+ },
85
+ cache: { revalidate: 60, tags: ['weaverse'] },
86
+ })
87
+
88
+ let [page, theme] = await Promise.all([
89
+ weaverse.loadPage({ type: 'INDEX' }),
90
+ weaverse.loadThemeSettings(),
91
+ ])
92
+ return { page, theme, weaverse }
93
+ }
94
+ ```
95
+
96
+ ### Returned client
97
+
98
+ | Member | Description |
99
+ | --- | --- |
100
+ | `loadPage(input?)` | POSTs `{ projectId, url, i18n, params, isDesignMode }` to `/api/public/project`, runs component loaders, returns `WeaverseNextLoaderData` (`page`, `project`, `pageAssignment`, `configs.requestInfo`). In Studio preview mode (`?isPreviewMode=true`) it short-circuits to a synthetic single-section page built from the `sectionType` presets — no fetch, no `projectId` required. A route-level `projectId` override is propagated to `client.projectId`, loaders, and the returned `configs`. |
101
+ | `loadThemeSettings(options?)` | POSTs to `/api/public/project_configs`, merges `themeSchema` defaults under `theme`, includes `staticContent`; falls back to defaults on failure. |
102
+ | `fetchWithCache<T>(url, options?)` | Mode-aware fetch: `cache: 'no-store'` in design/revision, `next: { revalidate, tags }` in published mode. |
103
+ | `projectId` / `resolveProjectId()` | Resolved project id (query → function → string → env). |
104
+ | `components`, `commerce`, `storefront`, `requestContext`, `themeSchema`, `themeSettings`, `data`, `dataContext`, `configs` | Readonly request state. `storefront` aliases `commerce.storefront`. |
105
+
106
+ ### Config resolution
107
+
108
+ Close to Hydrogen's `getWeaverseConfigs`:
109
+
110
+ - **projectId** — `?weaverseProjectId=` query → config function → config string →
111
+ `WEAVERSE_PROJECT_ID` env.
112
+ - **weaverseHost** — trusted request `?weaverseHost=` (only `weaverse.io` /
113
+ `weaverse.dev` over https) → `WEAVERSE_HOST` env → `https://studio.weaverse.io`.
114
+ - **API base** — trusted request host → `WEAVERSE_PUBLIC_API_BASE` env → custom
115
+ non-production `WEAVERSE_HOST` → `https://api.weaverse.io`.
116
+ - **publicEnv** — `PUBLIC_STORE_DOMAIN`, `PUBLIC_STOREFRONT_API_TOKEN`.
117
+
118
+ `WEAVERSE_API_KEY` is read from the request query / env and kept on the internal
119
+ base configs, but the current server client does **not** attach it to the
120
+ page/theme API requests. It is also **never** serialized into client-facing
121
+ `configs` / loader data, so it cannot leak to the browser.
122
+
123
+ ## Studio helpers
124
+
125
+ Design/preview detection, the Studio bridge script, and the in-page runtime are
126
+ shared with the root entry: `createWeaverseNextRuntime` + `WeaverseNextProvider`
127
+ hydrate Studio, and `bindWeaverseNextStudioRuntime` wires live updates. The
128
+ server client emits `configs.requestInfo` and design-mode flags the bridge
129
+ expects.
130
+
131
+ > **Mount `WeaverseNextStudioConnect` at your root/client shell.**
132
+ > `WeaverseNextRenderer` mounts the in-page runtime bridge but does **not**
133
+ > inject the Studio bridge script by itself. The script (which lets Studio's
134
+ > `checkWeaversePage()` handshake answer on every route, including content-less
135
+ > ones) is loaded by `WeaverseNextStudioConnect`, so it must be rendered once at
136
+ > the app root/client shell for the Studio editor to connect.
137
+
138
+ ## Current limitations
139
+
140
+ - Server fetch has a timeout + single attempt; it does not yet replicate
141
+ Hydrogen's subrequest cache, retry/backoff, or merchant-overrides translation
142
+ fetch.
143
+ - Next caching is expressed only via `cache: 'no-store'` and
144
+ `next: { revalidate, tags }`; on-demand `revalidatePath`/`revalidateTag` is left
145
+ to the host app.
146
+ - `loadCustomPages` (sitemap) and SEO helpers are not ported yet.
147
+ - The Studio bridge currently reuses the Hydrogen bundle until a Next-specific
148
+ bundle is required.
149
+ - Package is pre-release (`alpha`); the API may still change.
@@ -0,0 +1,149 @@
1
+ // src/utils.ts
2
+ function generateDataFromSchema(schema) {
3
+ let data = {};
4
+ if (!schema) {
5
+ return data;
6
+ }
7
+ let groups = schema.settings ?? schema.inspector ?? [];
8
+ for (let group of groups) {
9
+ for (let input of group?.inputs ?? []) {
10
+ let { name, defaultValue } = input;
11
+ if (name && defaultValue !== null && defaultValue !== void 0) {
12
+ data[name] = defaultValue;
13
+ }
14
+ }
15
+ }
16
+ return data;
17
+ }
18
+
19
+ // src/loader.ts
20
+ function isItemNode(node) {
21
+ return typeof node === "object" && node !== null && typeof node.type === "string";
22
+ }
23
+ function cloneItem(item) {
24
+ return {
25
+ ...item,
26
+ children: Array.isArray(item.children) ? item.children.map(
27
+ (child) => isItemNode(child) ? cloneItem(child) : { ...child }
28
+ ) : item.children
29
+ };
30
+ }
31
+ async function runWeaverseComponentLoaders(args) {
32
+ let { client, context, commerce } = args;
33
+ let sourceData = args.data ?? client.data;
34
+ if (!sourceData) {
35
+ return null;
36
+ }
37
+ if (!sourceData.page.items.length) {
38
+ return sourceData;
39
+ }
40
+ let data = {
41
+ ...sourceData,
42
+ page: {
43
+ ...sourceData.page,
44
+ items: sourceData.page.items.map(cloneItem)
45
+ }
46
+ };
47
+ let items = data.page.items;
48
+ let componentMap = /* @__PURE__ */ new Map();
49
+ for (let component of client.components) {
50
+ if (component?.schema?.type) {
51
+ componentMap.set(component.schema.type, component);
52
+ }
53
+ }
54
+ let visited = /* @__PURE__ */ new Set();
55
+ let resolvedContext = context ?? client.requestContext;
56
+ let resolvedCommerce = commerce ?? client.commerce;
57
+ let resolvedWeaverse = resolvedCommerce === client.commerce ? client : {
58
+ ...client,
59
+ commerce: resolvedCommerce,
60
+ storefront: resolvedCommerce?.storefront
61
+ };
62
+ async function walk(nodes) {
63
+ for (let item of nodes) {
64
+ if (!item || visited.has(item.id)) {
65
+ continue;
66
+ }
67
+ visited.add(item.id);
68
+ let component = componentMap.get(item.type);
69
+ if (component?.loader) {
70
+ try {
71
+ item.loaderData = await component.loader({
72
+ data: { ...generateDataFromSchema(component.schema), ...item.data },
73
+ weaverse: resolvedWeaverse,
74
+ context: resolvedContext,
75
+ commerce: resolvedCommerce
76
+ });
77
+ } catch (error) {
78
+ console.warn("\u274C Item loader run failed.", item.type, item.id, error);
79
+ }
80
+ }
81
+ let children = item.children;
82
+ if (Array.isArray(children)) {
83
+ let inlineChildren = children.filter(
84
+ isItemNode
85
+ );
86
+ if (inlineChildren.length) {
87
+ await walk(inlineChildren);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ await walk(items);
93
+ return data;
94
+ }
95
+
96
+ // src/request-info.ts
97
+ function toUrl(value) {
98
+ return typeof value === "string" ? new URL(value, "http://localhost") : value;
99
+ }
100
+ function getSearchParamsFromContext(context) {
101
+ if (context?.searchParams) {
102
+ return new URLSearchParams(context.searchParams);
103
+ }
104
+ if (context?.url) {
105
+ let url = toUrl(context.url);
106
+ return new URLSearchParams(url.searchParams);
107
+ }
108
+ return new URLSearchParams();
109
+ }
110
+ function getPathnameFromContext(context) {
111
+ if (context?.pathname) {
112
+ return context.pathname;
113
+ }
114
+ if (context?.url) {
115
+ let url = toUrl(context.url);
116
+ return url.pathname || "/";
117
+ }
118
+ return "/";
119
+ }
120
+ function coerceQueryValue(value) {
121
+ if (value === "true") {
122
+ return true;
123
+ }
124
+ if (value === "false") {
125
+ return false;
126
+ }
127
+ return value;
128
+ }
129
+ function toQueryRecord(searchParams) {
130
+ let queries = {};
131
+ for (let [key, value] of searchParams.entries()) {
132
+ queries[key] = coerceQueryValue(value);
133
+ }
134
+ return queries;
135
+ }
136
+ function buildWeaverseNextRequestInfo(context) {
137
+ let searchParams = getSearchParamsFromContext(context);
138
+ let search = searchParams.toString();
139
+ return {
140
+ pathname: getPathnameFromContext(context),
141
+ search: search ? `?${search}` : "",
142
+ queries: toQueryRecord(searchParams),
143
+ ...context?.i18n ? { i18n: context.i18n } : {}
144
+ };
145
+ }
146
+
147
+ export { buildWeaverseNextRequestInfo, generateDataFromSchema, runWeaverseComponentLoaders };
148
+ //# sourceMappingURL=chunk-SS6QUKYY.mjs.map
149
+ //# sourceMappingURL=chunk-SS6QUKYY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/loader.ts","../src/request-info.ts"],"names":[],"mappings":"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","file":"chunk-SS6QUKYY.mjs","sourcesContent":["import type { SchemaType } from '@weaverse/schema'\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 | 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\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 = getSearchParamsFromContext(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"]}
package/dist/index.d.mts CHANGED
@@ -1,163 +1,11 @@
1
- import { WeaverseElement, WeaverseResourcePickerData } from '@weaverse/react';
1
+ import { Weaverse } from '@weaverse/react';
2
2
  export { isBrowser, isIframe, useChildInstances, useItemInstance, useParentInstance, useWeaverse } from '@weaverse/react';
3
- import { PageType, SchemaType } from '@weaverse/schema';
4
3
  export { InspectorGroup, PageType, SchemaType, createSchema } from '@weaverse/schema';
5
- import * as react from 'react';
6
- import { ReactNode, ComponentType, ForwardRefExoticComponent } from 'react';
4
+ import { d as WeaverseNextClientConfig, c as WeaverseNextClient, e as WeaverseNextCommerceContext, q as WeaverseNextRequestContext, n as WeaverseNextLoaderData, r as WeaverseNextRequestInfo, t as WeaverseNextRuntimeInternal, s as WeaverseNextRuntimeConfig, A as WeaverseNextThemeSettingsStore } from './types-DvEeLX6L.mjs';
5
+ export { W as WeaverseCollection, f as WeaverseNextComponent, g as WeaverseNextComponentData, h as WeaverseNextComponentLoaderArgs, i as WeaverseNextComponentProps, l as WeaverseNextI18n, m as WeaverseNextLoadPageInput, o as WeaverseNextPageData, x as WeaverseNextStorefront, B as WeaverseProduct } from './types-DvEeLX6L.mjs';
7
6
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
-
9
- /**
10
- * Locale/market info passed through to component loaders and the Storefront
11
- * client. Kept structurally close to Hydrogen's `WeaverseI18n` but without the
12
- * `@shopify/hydrogen` `I18nBase` dependency so the adapter stays framework
13
- * neutral.
14
- */
15
- interface WeaverseNextI18n {
16
- country?: string;
17
- label?: string;
18
- language?: string;
19
- locale?: string;
20
- pathPrefix?: string;
21
- [key: string]: unknown;
22
- }
23
- /**
24
- * Minimal Storefront client shape required by component loaders. The host app
25
- * provides this (public or private token mode); Weaverse never owns it.
26
- */
27
- interface WeaverseNextStorefront {
28
- i18n?: WeaverseNextI18n;
29
- query: (query: string, options?: unknown) => Promise<unknown>;
30
- [key: string]: unknown;
31
- }
32
- /**
33
- * App-provided commerce context. `storefront` is the only v0 must-have; cart
34
- * and customer-account stay app-owned and are passed through untouched.
35
- */
36
- interface WeaverseNextCommerceContext {
37
- cart?: unknown;
38
- customerAccount?: unknown;
39
- storefront?: WeaverseNextStorefront;
40
- [key: string]: unknown;
41
- }
42
- /**
43
- * Explicit, framework-neutral replacement for React Router's
44
- * `LoaderFunctionArgs`. Built by the Next route/page from params + headers +
45
- * searchParams instead of an implicit loader context.
46
- */
47
- interface WeaverseNextRequestContext {
48
- handle?: string;
49
- headers?: Headers;
50
- i18n?: WeaverseNextI18n;
51
- isDesignMode?: boolean;
52
- isPreviewMode?: boolean;
53
- isRevisionPreview?: boolean;
54
- pageType?: PageType;
55
- pathname?: string;
56
- searchParams?: URLSearchParams;
57
- sectionType?: string;
58
- url?: URL | string;
59
- [key: string]: unknown;
60
- }
61
- /** A single serialized item in a Weaverse page tree. */
62
- interface WeaverseNextComponentData {
63
- children?: WeaverseNextComponentData[] | {
64
- id: string;
65
- }[];
66
- data?: Record<string, unknown>;
67
- id: string;
68
- /** Attached by {@link runWeaverseComponentLoaders} after a loader runs. */
69
- loaderData?: unknown;
70
- type: string;
71
- [key: string]: unknown;
72
- }
73
- /** Serialized page tree fetched from Weaverse. */
74
- interface WeaverseNextPageData {
75
- id: string;
76
- items: WeaverseNextComponentData[];
77
- name?: string;
78
- rootId?: string;
79
- [key: string]: unknown;
80
- }
81
- /**
82
- * Top-level payload returned by `client.loadPage`. Kept close to Hydrogen's
83
- * `WeaverseLoaderData` so registered components stay portable.
84
- */
85
- interface WeaverseNextLoaderData {
86
- configs?: Record<string, unknown>;
87
- page: WeaverseNextPageData;
88
- pageAssignment?: Record<string, unknown>;
89
- project?: Record<string, unknown>;
90
- [key: string]: unknown;
91
- }
92
- /** Props injected into every registered Weaverse component. */
93
- interface WeaverseNextComponentProps<L = unknown> extends Partial<WeaverseElement> {
94
- children?: ReactNode;
95
- className?: string;
96
- loaderData?: L;
97
- [key: string]: unknown;
98
- }
99
- /**
100
- * Arguments passed to a component `loader`. `commerce.storefront` is the
101
- * explicit contract; `weaverse.storefront` exists as a compatibility alias for
102
- * Pilot-style loaders that call `weaverse.storefront.query(...)`.
103
- */
104
- interface WeaverseNextComponentLoaderArgs<TData = unknown> {
105
- commerce?: WeaverseNextCommerceContext;
106
- context?: WeaverseNextRequestContext;
107
- data: TData;
108
- weaverse: WeaverseNextClient;
109
- }
110
- /** Registered component module shape: `default` + `schema` + optional `loader`. */
111
- interface WeaverseNextComponent<TProps extends WeaverseNextComponentProps = WeaverseNextComponentProps> {
112
- default: ComponentType<TProps> | ForwardRefExoticComponent<TProps>;
113
- loader?: (args: WeaverseNextComponentLoaderArgs) => Promise<unknown>;
114
- schema: SchemaType;
115
- }
116
- /** Input for `client.loadPage`. The Next route maps `params` → `type/handle`. */
117
- interface WeaverseNextLoadPageInput {
118
- handle?: string;
119
- locale?: string;
120
- type?: PageType;
121
- [key: string]: unknown;
122
- }
123
- interface WeaverseNextClientConfig {
124
- commerce?: WeaverseNextCommerceContext;
125
- components: WeaverseNextComponent[];
126
- /**
127
- * Injected page fetcher. v0 delegates network I/O to the host app so the
128
- * adapter can be tested without real Weaverse API calls.
129
- */
130
- fetchPage?: (input: WeaverseNextLoadPageInput) => Promise<WeaverseNextLoaderData | null>;
131
- /** Injected theme-settings fetcher. */
132
- fetchThemeSettings?: (context?: WeaverseNextRequestContext) => Promise<unknown>;
133
- projectId: string;
134
- requestContext?: WeaverseNextRequestContext;
135
- themeSchema?: unknown;
136
- themeSettings?: Record<string, unknown>;
137
- }
138
- /**
139
- * Request-safe client returned by {@link createWeaverseNextClient}. Holds the
140
- * registry/config plus the currently loaded page data, and is the object passed
141
- * to component loaders as `weaverse`.
142
- */
143
- interface WeaverseNextClient {
144
- commerce?: WeaverseNextCommerceContext;
145
- components: WeaverseNextComponent[];
146
- /** Currently loaded page payload (set by `loadPage`). */
147
- data: WeaverseNextLoaderData | null;
148
- /** Data-connector context handed to the React renderer. */
149
- dataContext: Record<string, unknown>;
150
- loadPage: (input?: WeaverseNextLoadPageInput) => Promise<WeaverseNextLoaderData | null>;
151
- loadThemeSettings: (context?: WeaverseNextRequestContext) => Promise<unknown>;
152
- projectId: string;
153
- requestContext?: WeaverseNextRequestContext;
154
- /** Compatibility alias for `commerce.storefront`. */
155
- storefront?: WeaverseNextStorefront;
156
- themeSchema?: unknown;
157
- themeSettings: Record<string, unknown>;
158
- }
159
- type WeaverseProduct = WeaverseResourcePickerData;
160
- type WeaverseCollection = WeaverseResourcePickerData;
7
+ import * as react from 'react';
8
+ import { ReactNode } from 'react';
161
9
 
162
10
  /**
163
11
  * Create a {@link WeaverseNextClient} from explicit config.
@@ -187,8 +35,9 @@ declare function useWeaversePageData<T = unknown>(): T;
187
35
  /** Read the app-provided commerce context (storefront/cart/customer). */
188
36
  declare function useWeaverseCommerce<T = WeaverseNextCommerceContext>(): T;
189
37
  /**
190
- * Read Weaverse theme settings. Next version backed by provider root/theme
191
- * data (`client.themeSettings`), not the Hydrogen theme-settings store.
38
+ * Read live Weaverse theme settings. The Studio bridge updates the provider's
39
+ * external store via `updateThemeSettings`, so components re-render during live
40
+ * design-mode edits.
192
41
  */
193
42
  declare function useThemeSettings<T = Record<string, unknown>>(): T;
194
43
 
@@ -262,4 +111,74 @@ interface WeaverseNextRendererProps {
262
111
  */
263
112
  declare const WeaverseNextRenderer: react.MemoExoticComponent<(props: WeaverseNextRendererProps) => react_jsx_runtime.JSX.Element | null>;
264
113
 
265
- export { type RunComponentLoadersArgs, type WeaverseCollection, type WeaverseNextClient, type WeaverseNextClientConfig, type WeaverseNextCommerceContext, type WeaverseNextComponent, type WeaverseNextComponentData, type WeaverseNextComponentLoaderArgs, type WeaverseNextComponentProps, type WeaverseNextI18n, type WeaverseNextLoadPageInput, type WeaverseNextLoaderData, type WeaverseNextPageData, WeaverseNextProvider, type WeaverseNextProviderProps, WeaverseNextRenderer, type WeaverseNextRendererProps, type WeaverseNextRequestContext, type WeaverseNextStorefront, type WeaverseProduct, createWeaverseNextClient, runWeaverseComponentLoaders, useThemeSettings, useWeaverseCommerce, useWeaversePageData, useWeaverseRootData };
114
+ /**
115
+ * Build the Hydrogen-like request info shape consumed by the Studio bridge.
116
+ * Duplicate query keys keep the last value, matching `URLSearchParams` iteration
117
+ * assignment and Hydrogen's practical query-object behavior.
118
+ */
119
+ declare function buildWeaverseNextRequestInfo(context?: WeaverseNextRequestContext): WeaverseNextRequestInfo;
120
+
121
+ declare global {
122
+ interface Window {
123
+ __weaverse?: WeaverseNextRuntime;
124
+ __weaverses?: Record<string, WeaverseNextRuntime>;
125
+ }
126
+ }
127
+ declare class WeaverseNextRuntime extends Weaverse {
128
+ pageId: string;
129
+ internal: WeaverseNextRuntimeInternal;
130
+ requestInfo: WeaverseNextRequestInfo;
131
+ isPreviewMode: boolean;
132
+ isRevisionPreview: boolean;
133
+ sectionType?: string;
134
+ translationMap: Record<string, unknown>;
135
+ translationLocale: string;
136
+ translationLanguageId: string;
137
+ constructor(config: WeaverseNextRuntimeConfig);
138
+ extractTranslationSidecar: () => undefined;
139
+ setTranslationSidecar: (map?: Record<string, unknown>, locale?: string, languageId?: string) => void;
140
+ updateTranslation: () => undefined;
141
+ getTranslationChanges: () => undefined;
142
+ }
143
+ declare function createWeaverseNextRuntime(config: WeaverseNextRuntimeConfig): WeaverseNextRuntime;
144
+ declare function bindWeaverseNextStudioRuntime(runtime: WeaverseNextRuntime): boolean;
145
+
146
+ interface WeaverseNextStudioBridgeProps {
147
+ navigate?: WeaverseNextRuntimeInternal['navigate'];
148
+ revalidate?: WeaverseNextRuntimeInternal['revalidate'];
149
+ runtime: WeaverseNextRuntime;
150
+ }
151
+ /** Page-level Studio runtime binder. */
152
+ declare function WeaverseNextStudioBridge(props: WeaverseNextStudioBridgeProps): null;
153
+
154
+ interface WeaverseNextStudioConnectProps {
155
+ context?: WeaverseNextRequestContext;
156
+ storefrontHostname?: string;
157
+ }
158
+ /** Root-level Studio script connector for Next client boundaries. */
159
+ declare function WeaverseNextStudioConnect(props: WeaverseNextStudioConnectProps): null;
160
+
161
+ interface ResolveWeaverseNextStudioScriptSrcOptions {
162
+ framework?: 'hydrogen' | 'next';
163
+ storefrontHostname?: string;
164
+ }
165
+ /**
166
+ * Resolve the Studio script URL from request/search context. v0 intentionally
167
+ * reuses the existing structural Hydrogen bridge script until a concrete Builder
168
+ * incompatibility requires a dedicated Next bundle.
169
+ */
170
+ declare function resolveWeaverseNextStudioScriptSrc(context?: WeaverseNextRequestContext, options?: ResolveWeaverseNextStudioScriptSrcOptions): string | null;
171
+
172
+ interface CreateWeaverseNextThemeSettingsStoreOptions {
173
+ publicEnv?: Record<string, string | undefined>;
174
+ schema?: unknown;
175
+ settings?: Record<string, unknown>;
176
+ }
177
+ /**
178
+ * Small external store shape compatible with the Studio bridge and React's
179
+ * `useSyncExternalStore` contract. The bridge calls `updateThemeSettings` during
180
+ * live theme edits, so keep that method name stable.
181
+ */
182
+ declare function createWeaverseNextThemeSettingsStore(options?: CreateWeaverseNextThemeSettingsStoreOptions): WeaverseNextThemeSettingsStore;
183
+
184
+ export { type CreateWeaverseNextThemeSettingsStoreOptions, type ResolveWeaverseNextStudioScriptSrcOptions, type RunComponentLoadersArgs, WeaverseNextClient, WeaverseNextClientConfig, WeaverseNextCommerceContext, WeaverseNextLoaderData, WeaverseNextProvider, type WeaverseNextProviderProps, WeaverseNextRenderer, type WeaverseNextRendererProps, WeaverseNextRequestContext, WeaverseNextRequestInfo, WeaverseNextRuntime, WeaverseNextRuntimeConfig, WeaverseNextRuntimeInternal, WeaverseNextStudioBridge, type WeaverseNextStudioBridgeProps, WeaverseNextStudioConnect, type WeaverseNextStudioConnectProps, WeaverseNextThemeSettingsStore, bindWeaverseNextStudioRuntime, buildWeaverseNextRequestInfo, createWeaverseNextClient, createWeaverseNextRuntime, createWeaverseNextThemeSettingsStore, resolveWeaverseNextStudioScriptSrc, runWeaverseComponentLoaders, useThemeSettings, useWeaverseCommerce, useWeaversePageData, useWeaverseRootData };