@weaverse/hydrogen 6.1.2 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -6
- package/dist/index.mjs +1 -2
- package/dist/internal.js +18 -29
- package/dist/internal.mjs +3 -14
- package/dist/next-client-boundary.js +1088 -0
- package/dist/next-client-boundary.js.map +1 -0
- package/dist/next-client-boundary.mjs +1038 -0
- package/dist/next-client-boundary.mjs.map +1 -0
- package/dist/next-server.js +669 -13
- package/dist/next-server.js.map +1 -0
- package/dist/next-server.mjs +666 -2
- package/dist/next-server.mjs.map +1 -0
- package/dist/next.js +33 -41
- package/dist/next.mjs +5 -3
- package/dist/react-router.js +6 -7
- package/dist/react-router.mjs +2 -3
- package/dist/seo-B8u2r3rX.mjs +205 -0
- package/dist/seo-B8u2r3rX.mjs.map +1 -0
- package/dist/seo-D9A9uRQ4.js +264 -0
- package/dist/seo-D9A9uRQ4.js.map +1 -0
- package/dist/translate-Celryv8j.mjs +26 -0
- package/dist/translate-Celryv8j.mjs.map +1 -0
- package/dist/translate-Kt1BYL1n.js +37 -0
- package/dist/translate-Kt1BYL1n.js.map +1 -0
- package/dist/{nested-key-CR3-YMFi.js → translation-store-BmGxBALf.js} +46 -137
- package/dist/translation-store-BmGxBALf.js.map +1 -0
- package/dist/{nested-key-_k9aW_Wd.mjs → translation-store-JFs-4lvf.mjs} +35 -138
- package/dist/translation-store-JFs-4lvf.mjs.map +1 -0
- package/dist/types/hooks/translation-context.d.ts +2 -36
- package/dist/types/hooks/translation-context.d.ts.map +1 -1
- package/dist/types/next/client-boundary.d.ts +25 -0
- package/dist/types/next/client-boundary.d.ts.map +1 -0
- package/dist/types/next/client.d.ts +16 -0
- package/dist/types/next/client.d.ts.map +1 -0
- package/dist/types/next/hooks.d.ts +20 -0
- package/dist/types/next/hooks.d.ts.map +1 -0
- package/dist/types/next/index.d.ts +34 -0
- package/dist/types/next/index.d.ts.map +1 -0
- package/dist/types/next/item.d.ts +60 -0
- package/dist/types/next/item.d.ts.map +1 -0
- package/dist/types/next/loader.d.ts +25 -0
- package/dist/types/next/loader.d.ts.map +1 -0
- package/dist/types/next/main.d.ts +11 -0
- package/dist/types/next/main.d.ts.map +1 -0
- package/dist/types/next/provider.d.ts +85 -0
- package/dist/types/next/provider.d.ts.map +1 -0
- package/dist/types/next/registry.d.ts +15 -0
- package/dist/types/next/registry.d.ts.map +1 -0
- package/dist/types/next/renderer.d.ts +21 -0
- package/dist/types/next/renderer.d.ts.map +1 -0
- package/dist/types/next/request-info.d.ts +19 -0
- package/dist/types/next/request-info.d.ts.map +1 -0
- package/dist/types/next/revalidate-item.d.ts +82 -0
- package/dist/types/next/revalidate-item.d.ts.map +1 -0
- package/dist/types/next/root-provider.d.ts +65 -0
- package/dist/types/next/root-provider.d.ts.map +1 -0
- package/dist/types/next/runtime.d.ts +113 -0
- package/dist/types/next/runtime.d.ts.map +1 -0
- package/dist/types/next/seo.d.ts +55 -0
- package/dist/types/next/seo.d.ts.map +1 -0
- package/dist/types/next/server/configs.d.ts +27 -0
- package/dist/types/next/server/configs.d.ts.map +1 -0
- package/dist/types/next/server/normalize-page-url.d.ts +19 -0
- package/dist/types/next/server/normalize-page-url.d.ts.map +1 -0
- package/dist/types/next/server/revalidate-handler.d.ts +55 -0
- package/dist/types/next/server/revalidate-handler.d.ts.map +1 -0
- package/dist/types/next/server/server-client.d.ts +21 -0
- package/dist/types/next/server/server-client.d.ts.map +1 -0
- package/dist/types/next/server.d.ts +19 -0
- package/dist/types/next/server.d.ts.map +1 -0
- package/dist/types/next/studio-bridge.d.ts +16 -0
- package/dist/types/next/studio-bridge.d.ts.map +1 -0
- package/dist/types/next/studio-connect.d.ts +27 -0
- package/dist/types/next/studio-connect.d.ts.map +1 -0
- package/dist/types/next/studio-router.d.ts +50 -0
- package/dist/types/next/studio-router.d.ts.map +1 -0
- package/dist/types/next/studio-script-src.d.ts +12 -0
- package/dist/types/next/studio-script-src.d.ts.map +1 -0
- package/dist/types/next/theme-settings-store.d.ts +18 -0
- package/dist/types/next/theme-settings-store.d.ts.map +1 -0
- package/dist/types/next/translation-context.d.ts +109 -0
- package/dist/types/next/translation-context.d.ts.map +1 -0
- package/dist/types/next/types.d.ts +674 -0
- package/dist/types/next/types.d.ts.map +1 -0
- package/dist/types/next/use-weaverse-next-studio.d.ts +34 -0
- package/dist/types/next/use-weaverse-next-studio.d.ts.map +1 -0
- package/dist/types/next/utils.d.ts +21 -0
- package/dist/types/next/utils.d.ts.map +1 -0
- package/dist/types/next-server.d.ts +3 -1
- package/dist/types/next-server.d.ts.map +1 -1
- package/dist/types/next.d.ts +4 -3
- package/dist/types/next.d.ts.map +1 -1
- package/dist/types/utils/translate.d.ts +51 -0
- package/dist/types/utils/translate.d.ts.map +1 -0
- package/dist/{weaverse-client-C9IgIH7O.js → weaverse-client-BmceZc5q.js} +49 -771
- package/dist/weaverse-client-BmceZc5q.js.map +1 -0
- package/dist/{weaverse-client-D59mBW0j.mjs → weaverse-client-CXnM9ayH.mjs} +4 -720
- package/dist/weaverse-client-CXnM9ayH.mjs.map +1 -0
- package/package.json +13 -7
- package/src/hooks/translation-context.tsx +7 -84
- package/src/next/client-boundary.ts +51 -0
- package/src/next/client.ts +102 -0
- package/src/next/hooks.ts +52 -0
- package/src/next/index.ts +145 -0
- package/src/next/item.ts +167 -0
- package/src/next/loader.ts +135 -0
- package/src/next/main.tsx +45 -0
- package/src/next/provider.tsx +241 -0
- package/src/next/registry.ts +63 -0
- package/src/next/renderer.tsx +102 -0
- package/src/next/request-info.ts +124 -0
- package/src/next/revalidate-item.ts +211 -0
- package/src/next/root-provider.tsx +201 -0
- package/src/next/runtime.ts +559 -0
- package/src/next/seo.ts +157 -0
- package/src/next/server/configs.ts +178 -0
- package/src/next/server/normalize-page-url.ts +73 -0
- package/src/next/server/revalidate-handler.ts +357 -0
- package/src/next/server/server-client.ts +728 -0
- package/src/next/server.ts +67 -0
- package/src/next/studio-bridge.tsx +82 -0
- package/src/next/studio-connect.tsx +88 -0
- package/src/next/studio-router.ts +85 -0
- package/src/next/studio-script-src.ts +128 -0
- package/src/next/theme-settings-store.ts +47 -0
- package/src/next/translation-context.tsx +182 -0
- package/src/next/types.ts +764 -0
- package/src/next/use-weaverse-next-studio.tsx +106 -0
- package/src/next/utils.ts +48 -0
- package/src/next-server.ts +41 -1
- package/src/next.ts +51 -15
- package/src/utils/translate.ts +101 -0
- package/dist/internal.js.map +0 -1
- package/dist/internal.mjs.map +0 -1
- package/dist/nested-key-CR3-YMFi.js.map +0 -1
- package/dist/nested-key-_k9aW_Wd.mjs.map +0 -1
- package/dist/translation-store-4fAV771j.js +0 -69
- package/dist/translation-store-4fAV771j.js.map +0 -1
- package/dist/translation-store-BBrw0FOk.mjs +0 -58
- package/dist/translation-store-BBrw0FOk.mjs.map +0 -1
- package/dist/weaverse-client-C9IgIH7O.js.map +0 -1
- package/dist/weaverse-client-D59mBW0j.mjs.map +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { SchemaType } from '@weaverse/schema'
|
|
2
|
+
import { registerWeaverseNextComponents } from './registry'
|
|
3
|
+
import type {
|
|
4
|
+
WeaverseNextClient,
|
|
5
|
+
WeaverseNextClientConfig,
|
|
6
|
+
WeaverseNextCommerceContext,
|
|
7
|
+
WeaverseNextComponent,
|
|
8
|
+
WeaverseNextLoaderData,
|
|
9
|
+
WeaverseNextLoadPageInput,
|
|
10
|
+
WeaverseNextRequestContext,
|
|
11
|
+
WeaverseNextStorefront,
|
|
12
|
+
WeaverseNextThemeSchema,
|
|
13
|
+
} from './types'
|
|
14
|
+
import { generateDataFromSchema } from './utils'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Request-safe Weaverse client for Next.js. Holds the component registry, theme
|
|
18
|
+
* data, request/commerce context, and the currently loaded page payload.
|
|
19
|
+
*
|
|
20
|
+
* Unlike Hydrogen's `WeaverseClient`, it never receives a React Router
|
|
21
|
+
* `Request`/`LoaderFunctionArgs`. Network I/O is delegated to `fetchPage` /
|
|
22
|
+
* `fetchThemeSettings` injected through config, keeping v0 testable without
|
|
23
|
+
* real Weaverse API calls.
|
|
24
|
+
*/
|
|
25
|
+
class NextClient implements WeaverseNextClient {
|
|
26
|
+
projectId: string
|
|
27
|
+
components: WeaverseNextComponent[]
|
|
28
|
+
themeSchema?: SchemaType | WeaverseNextThemeSchema
|
|
29
|
+
themeSettings: Record<string, unknown>
|
|
30
|
+
requestContext?: WeaverseNextRequestContext
|
|
31
|
+
commerce?: WeaverseNextCommerceContext
|
|
32
|
+
data: WeaverseNextLoaderData | null = null
|
|
33
|
+
dataContext: Record<string, unknown> = {}
|
|
34
|
+
|
|
35
|
+
private readonly _fetchPage?: WeaverseNextClientConfig['fetchPage']
|
|
36
|
+
private readonly _fetchThemeSettings?: WeaverseNextClientConfig['fetchThemeSettings']
|
|
37
|
+
|
|
38
|
+
constructor(config: WeaverseNextClientConfig) {
|
|
39
|
+
this.projectId = config.projectId
|
|
40
|
+
this.components = config.components
|
|
41
|
+
registerWeaverseNextComponents(this.components)
|
|
42
|
+
this.themeSchema = config.themeSchema
|
|
43
|
+
// Seed `themeSchema` defaults before merchant-provided overrides, mirroring
|
|
44
|
+
// Hydrogen's `loadThemeSettings` (schema defaults under remote `theme`).
|
|
45
|
+
// Without this, `useThemeSettings()` would miss any setting the merchant
|
|
46
|
+
// never explicitly overrode.
|
|
47
|
+
this.themeSettings = {
|
|
48
|
+
...generateDataFromSchema(config.themeSchema),
|
|
49
|
+
...config.themeSettings,
|
|
50
|
+
}
|
|
51
|
+
this.requestContext = config.requestContext
|
|
52
|
+
this.commerce = config.commerce
|
|
53
|
+
this._fetchPage = config.fetchPage
|
|
54
|
+
this._fetchThemeSettings = config.fetchThemeSettings
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Compatibility alias so loaders can call `weaverse.storefront.query(...)`. */
|
|
58
|
+
get storefront(): WeaverseNextStorefront | undefined {
|
|
59
|
+
return this.commerce?.storefront
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
loadPage = (
|
|
63
|
+
input: WeaverseNextLoadPageInput = {}
|
|
64
|
+
): Promise<WeaverseNextLoaderData | null> => {
|
|
65
|
+
if (!this._fetchPage) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
'[WeaverseNextClient] loadPage requires a `fetchPage` function in client config.'
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
return this._fetchPage(input)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
loadThemeSettings = (
|
|
74
|
+
context?: WeaverseNextRequestContext
|
|
75
|
+
): Promise<unknown> => {
|
|
76
|
+
if (!this._fetchThemeSettings) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
'[WeaverseNextClient] loadThemeSettings requires a `fetchThemeSettings` function in client config.'
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
return this._fetchThemeSettings(context ?? this.requestContext)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create a {@link WeaverseNextClient} from explicit config.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* let client = createWeaverseNextClient({
|
|
91
|
+
* projectId: process.env.WEAVERSE_PROJECT_ID!,
|
|
92
|
+
* components,
|
|
93
|
+
* commerce: { storefront },
|
|
94
|
+
* fetchPage: async ({ type, handle }) => fetchWeaversePage({ type, handle }),
|
|
95
|
+
* })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export function createWeaverseNextClient(
|
|
99
|
+
config: WeaverseNextClientConfig
|
|
100
|
+
): WeaverseNextClient {
|
|
101
|
+
return new NextClient(config)
|
|
102
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useContext, useSyncExternalStore } from 'react'
|
|
4
|
+
import { WeaverseNextContext } from './provider'
|
|
5
|
+
import type { WeaverseNextCommerceContext } from './types'
|
|
6
|
+
|
|
7
|
+
const OUTSIDE_PROVIDER_ERROR = 'must be used inside a <WeaverseNextProvider>.'
|
|
8
|
+
|
|
9
|
+
function useWeaverseNextContext(hookName: string) {
|
|
10
|
+
let context = useContext(WeaverseNextContext)
|
|
11
|
+
if (!context) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`[@weaverse/hydrogen/next] ${hookName} ${OUTSIDE_PROVIDER_ERROR}`
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
return context
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Read explicit root/global data passed to the provider. Next-native
|
|
21
|
+
* replacement for `useRouteLoaderData('root')`.
|
|
22
|
+
*/
|
|
23
|
+
export function useWeaverseRootData<T = unknown>(): T {
|
|
24
|
+
return useWeaverseNextContext('useWeaverseRootData').rootData as T
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read explicit page/route commerce data passed to the provider. Next-native
|
|
29
|
+
* replacement for section-level `useLoaderData()`.
|
|
30
|
+
*/
|
|
31
|
+
export function useWeaversePageData<T = unknown>(): T {
|
|
32
|
+
return useWeaverseNextContext('useWeaversePageData').pageData as T
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Read the app-provided commerce context (storefront/cart/customer). */
|
|
36
|
+
export function useWeaverseCommerce<T = WeaverseNextCommerceContext>(): T {
|
|
37
|
+
return useWeaverseNextContext('useWeaverseCommerce').commerce as T
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Read live Weaverse theme settings. The Studio bridge updates the provider's
|
|
42
|
+
* external store via `updateThemeSettings`, so components re-render during live
|
|
43
|
+
* design-mode edits.
|
|
44
|
+
*/
|
|
45
|
+
export function useThemeSettings<T = Record<string, unknown>>(): T {
|
|
46
|
+
let { themeSettingsStore } = useWeaverseNextContext('useThemeSettings')
|
|
47
|
+
return useSyncExternalStore(
|
|
48
|
+
themeSettingsStore.subscribe,
|
|
49
|
+
themeSettingsStore.getSnapshot,
|
|
50
|
+
themeSettingsStore.getServerSnapshot
|
|
51
|
+
) as T
|
|
52
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js App Router rendering, provider, Studio, translation, and schema APIs.
|
|
3
|
+
*
|
|
4
|
+
* When migrating from `@weaverse/hydrogen`, replace route-loader context with
|
|
5
|
+
* {@link WeaverseNextProvider} props and render page data with
|
|
6
|
+
* {@link WeaverseNextRenderer}. Import server-only fetching and route-handler
|
|
7
|
+
* helpers from `@weaverse/hydrogen/next/server`.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type { StudioSelection } from '@weaverse/react'
|
|
13
|
+
// Re-export schema authoring for migration ergonomics.
|
|
14
|
+
export * from '@weaverse/schema'
|
|
15
|
+
export {
|
|
16
|
+
type CreateTranslateSources,
|
|
17
|
+
createTranslate,
|
|
18
|
+
getNestedKey,
|
|
19
|
+
interpolate,
|
|
20
|
+
type TranslateFunction,
|
|
21
|
+
} from '../utils/translate'
|
|
22
|
+
export { ThemeTextStore, TranslationStore } from '../utils/translation-store'
|
|
23
|
+
// Client code (components, hooks, runtime, and the `@weaverse/react`
|
|
24
|
+
// re-exports) comes by name from `./client-boundary`, which the build emits as
|
|
25
|
+
// its own `'use client'` file. Every other value export below is server-safe,
|
|
26
|
+
// so a Server Component can import this entry.
|
|
27
|
+
export {
|
|
28
|
+
bindWeaverseNextStudioRuntime,
|
|
29
|
+
createWeaverseNextClient,
|
|
30
|
+
createWeaverseNextRuntime,
|
|
31
|
+
loadWeaverseNextStudioScript,
|
|
32
|
+
ThemeTextProvider,
|
|
33
|
+
TranslationProvider,
|
|
34
|
+
useChildInstances,
|
|
35
|
+
useItemInstance,
|
|
36
|
+
useParentInstance,
|
|
37
|
+
useStudioSelection,
|
|
38
|
+
useThemeSettings,
|
|
39
|
+
useThemeText,
|
|
40
|
+
useTranslation,
|
|
41
|
+
useWeaverse,
|
|
42
|
+
useWeaverseCommerce,
|
|
43
|
+
useWeaverseNextStudioInternals,
|
|
44
|
+
useWeaversePageData,
|
|
45
|
+
useWeaverseRootData,
|
|
46
|
+
WeaverseNextProvider,
|
|
47
|
+
WeaverseNextRenderer,
|
|
48
|
+
WeaverseNextRootProvider,
|
|
49
|
+
WeaverseNextRuntime,
|
|
50
|
+
WeaverseNextStudio,
|
|
51
|
+
WeaverseNextStudioBridge,
|
|
52
|
+
WeaverseNextStudioConnect,
|
|
53
|
+
} from './client-boundary'
|
|
54
|
+
export type { WeaverseNextItem } from './item'
|
|
55
|
+
export {
|
|
56
|
+
type RunComponentLoadersArgs,
|
|
57
|
+
runWeaverseComponentLoaders,
|
|
58
|
+
} from './loader'
|
|
59
|
+
export type { WeaverseNextProviderProps } from './provider'
|
|
60
|
+
export type { WeaverseNextRendererProps } from './renderer'
|
|
61
|
+
export { buildWeaverseNextRequestInfo } from './request-info'
|
|
62
|
+
export {
|
|
63
|
+
buildWeaverseNextRevalidateRouteContext,
|
|
64
|
+
DEFAULT_REVALIDATE_ENDPOINT,
|
|
65
|
+
type RevalidateItemRuntimeLike,
|
|
66
|
+
revalidateWeaverseNextItem,
|
|
67
|
+
type WeaverseNextRevalidateRouteContext,
|
|
68
|
+
} from './revalidate-item'
|
|
69
|
+
export type {
|
|
70
|
+
WeaverseNextRootContextValue,
|
|
71
|
+
WeaverseNextRootProviderProps,
|
|
72
|
+
} from './root-provider'
|
|
73
|
+
export type { WeaverseNextRenderablePage } from './runtime'
|
|
74
|
+
export {
|
|
75
|
+
formatWeaverseNextSeoMetadata,
|
|
76
|
+
getWeaverseNextSeoMetadata,
|
|
77
|
+
type NextOpenGraph,
|
|
78
|
+
type NextTwitter,
|
|
79
|
+
type WeaverseNextSeoInput,
|
|
80
|
+
type WeaverseNextSeoMetadata,
|
|
81
|
+
type WeaverseNextSeoPageContainer,
|
|
82
|
+
} from './seo'
|
|
83
|
+
export type { WeaverseNextStudioBridgeProps } from './studio-bridge'
|
|
84
|
+
export type {
|
|
85
|
+
LoadWeaverseNextStudioScriptOptions,
|
|
86
|
+
WeaverseNextStudioConnectProps,
|
|
87
|
+
} from './studio-connect'
|
|
88
|
+
export {
|
|
89
|
+
type CreateWeaverseNextStudioInternalsOptions,
|
|
90
|
+
createWeaverseNextStudioInternals,
|
|
91
|
+
type WeaverseNextRouterLike,
|
|
92
|
+
type WeaverseNextStudioInternals,
|
|
93
|
+
} from './studio-router'
|
|
94
|
+
export {
|
|
95
|
+
type ResolveWeaverseNextStudioScriptSrcOptions,
|
|
96
|
+
resolveWeaverseNextStudioScriptSrc,
|
|
97
|
+
} from './studio-script-src'
|
|
98
|
+
export {
|
|
99
|
+
type CreateWeaverseNextThemeSettingsStoreOptions,
|
|
100
|
+
createWeaverseNextThemeSettingsStore,
|
|
101
|
+
} from './theme-settings-store'
|
|
102
|
+
export type {
|
|
103
|
+
ThemeTextProviderProps,
|
|
104
|
+
ThemeTextValue,
|
|
105
|
+
TranslationProviderProps,
|
|
106
|
+
TranslationValue,
|
|
107
|
+
} from './translation-context'
|
|
108
|
+
export type {
|
|
109
|
+
WeaverseArticle,
|
|
110
|
+
WeaverseBlog,
|
|
111
|
+
WeaverseCollection,
|
|
112
|
+
WeaverseMetaObject,
|
|
113
|
+
WeaverseNextCacheConfig,
|
|
114
|
+
WeaverseNextClient,
|
|
115
|
+
WeaverseNextClientConfig,
|
|
116
|
+
WeaverseNextCommerceContext,
|
|
117
|
+
WeaverseNextComponent,
|
|
118
|
+
WeaverseNextComponentData,
|
|
119
|
+
WeaverseNextComponentLoaderArgs,
|
|
120
|
+
WeaverseNextComponentProps,
|
|
121
|
+
WeaverseNextCustomPageEntry,
|
|
122
|
+
WeaverseNextFetchCustomPagesOptions,
|
|
123
|
+
WeaverseNextI18n,
|
|
124
|
+
WeaverseNextLoaderData,
|
|
125
|
+
WeaverseNextLoadPageInput,
|
|
126
|
+
WeaverseNextPageAssignment,
|
|
127
|
+
WeaverseNextPageData,
|
|
128
|
+
WeaverseNextRequestContext,
|
|
129
|
+
WeaverseNextRequestInfo,
|
|
130
|
+
WeaverseNextRuntimeConfig,
|
|
131
|
+
WeaverseNextRuntimeInternal,
|
|
132
|
+
WeaverseNextStorefront,
|
|
133
|
+
WeaverseNextThemeSchema,
|
|
134
|
+
WeaverseNextThemeSchemaGroup,
|
|
135
|
+
WeaverseNextThemeSchemaInput,
|
|
136
|
+
WeaverseNextThemeSettingsStore,
|
|
137
|
+
WeaverseNextTranslationChanges,
|
|
138
|
+
WeaverseNextTranslationEntry,
|
|
139
|
+
WeaverseNextTranslationItemEntry,
|
|
140
|
+
WeaverseNextTranslationMap,
|
|
141
|
+
WeaverseNextTranslationMapEntry,
|
|
142
|
+
WeaverseProduct,
|
|
143
|
+
} from './types'
|
|
144
|
+
export type { WeaverseNextStudioProps } from './use-weaverse-next-studio'
|
|
145
|
+
export { generateDataFromSchema, isBrowser, isIframe } from './utils'
|
package/src/next/item.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { ElementData } from '@weaverse/react'
|
|
2
|
+
import { Weaverse, WeaverseItemStore } from '@weaverse/react'
|
|
3
|
+
import type { WeaverseNextRuntime } from './runtime'
|
|
4
|
+
import type {
|
|
5
|
+
WeaverseNextComponentData,
|
|
6
|
+
WeaverseNextTranslationItemEntry,
|
|
7
|
+
} from './types'
|
|
8
|
+
import { generateDataFromSchema } from './utils'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Spread a serialized item's nested `data` field into top-level props — the
|
|
12
|
+
* shape the shared `@weaverse/react` renderer expects. Top-level fields win
|
|
13
|
+
* over nested ones, matching the constructor's original
|
|
14
|
+
* `Object.assign(store, schemaDefaults, data, rest)` merge order. Flat payloads
|
|
15
|
+
* with no nested `data` pass through unchanged.
|
|
16
|
+
*/
|
|
17
|
+
function flattenItemData(update: Omit<ElementData, 'id' | 'type'>) {
|
|
18
|
+
let { data, ...rest } = update
|
|
19
|
+
if (!(data && typeof data === 'object' && !Array.isArray(data))) {
|
|
20
|
+
return rest
|
|
21
|
+
}
|
|
22
|
+
return { ...data, ...rest }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* While set, item `setData()` queues its subscriber notification here instead
|
|
27
|
+
* of emitting synchronously. `createWeaverseNextRuntime()` runs during React
|
|
28
|
+
* render (the renderer's `useMemo`), and constructing a runtime makes core's
|
|
29
|
+
* `initProject()` call `setData()` on reused item instances (locale/client
|
|
30
|
+
* navigation) — emitting to `useSyncExternalStore` subscribers mid-render
|
|
31
|
+
* trips React's setState-in-render warning. Data is still applied
|
|
32
|
+
* synchronously; only the emit is deferred.
|
|
33
|
+
*/
|
|
34
|
+
let deferredItemUpdates: Set<WeaverseNextItem> | null = null
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run `create` with item update emissions deferred, returning its result and
|
|
38
|
+
* the items whose `setData()` fired meanwhile. The caller is responsible for
|
|
39
|
+
* calling `triggerUpdate()` on them once React has committed.
|
|
40
|
+
*/
|
|
41
|
+
export function collectDeferredItemUpdates<T>(
|
|
42
|
+
create: () => T
|
|
43
|
+
): [T, WeaverseNextItem[]] {
|
|
44
|
+
let previousQueue = deferredItemUpdates
|
|
45
|
+
let queue = new Set<WeaverseNextItem>()
|
|
46
|
+
deferredItemUpdates = queue
|
|
47
|
+
try {
|
|
48
|
+
return [create(), [...queue]]
|
|
49
|
+
} finally {
|
|
50
|
+
deferredItemUpdates = previousQueue
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Item store that flattens the serialized item's nested `data` field onto the
|
|
56
|
+
* store and seeds schema defaults — same shape Hydrogen produces, so the
|
|
57
|
+
* shared `@weaverse/react` renderer can spread props at the top level.
|
|
58
|
+
*/
|
|
59
|
+
export class WeaverseNextItem extends WeaverseItemStore {
|
|
60
|
+
/** Browser runtime that owns this item store. */
|
|
61
|
+
declare weaverse: WeaverseNextRuntime
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Cached merged snapshot (base `_store` + translations). Reset whenever the
|
|
65
|
+
* `_store` ref or the item's translation entry ref changes, so
|
|
66
|
+
* `useSyncExternalStore` sees a stable object between unrelated re-renders.
|
|
67
|
+
*/
|
|
68
|
+
private _cachedSnapshot: ElementData | null = null
|
|
69
|
+
private _snapshotStoreRef: ElementData | null = null
|
|
70
|
+
private _snapshotTranslationRef: WeaverseNextTranslationItemEntry | null =
|
|
71
|
+
null
|
|
72
|
+
|
|
73
|
+
/** Create an item store from serialized component data and its runtime. */
|
|
74
|
+
constructor(initialData: WeaverseNextComponentData, weaverse: Weaverse) {
|
|
75
|
+
super(initialData as ElementData, weaverse)
|
|
76
|
+
let schemaData = generateDataFromSchema(this.Element?.schema)
|
|
77
|
+
Object.assign(this._store, schemaData, flattenItemData(initialData))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Flatten nested `data` on updates exactly like the constructor does. Core's
|
|
82
|
+
* `initProject()` calls `setData(item)` when a new runtime serves a page
|
|
83
|
+
* whose item instances already exist (e.g. locale/client navigation).
|
|
84
|
+
*
|
|
85
|
+
* A complete serialized item (it carries `id` and `type`; core's
|
|
86
|
+
* `initProject()` always passes one) replaces the store the way the
|
|
87
|
+
* constructor builds it: the schema defaults of the item's own `type` — the
|
|
88
|
+
* Builder can replace a section in place, keeping its id — then the item's
|
|
89
|
+
* settings. A payload omits a setting — or the whole optional `data` — when
|
|
90
|
+
* its value equals the schema default, so merging into the previous store
|
|
91
|
+
* would keep the previous locale's value visible. Partial updates still
|
|
92
|
+
* merge. Either way the `_store` ref changes, which invalidates the memoized
|
|
93
|
+
* snapshot below. The subscriber emit is queued instead of fired when a
|
|
94
|
+
* render-phase deferral is active (see `collectDeferredItemUpdates`).
|
|
95
|
+
*/
|
|
96
|
+
setData = (update: Omit<ElementData, 'id' | 'type'>) => {
|
|
97
|
+
if ('id' in update && 'type' in update) {
|
|
98
|
+
let { data, type } = update as ElementData
|
|
99
|
+
let schema = this.weaverse.elementRegistry.get(type)?.schema
|
|
100
|
+
this._store = {
|
|
101
|
+
...generateDataFromSchema(schema),
|
|
102
|
+
...(data === undefined ? {} : { data }),
|
|
103
|
+
...flattenItemData(update),
|
|
104
|
+
} as ElementData
|
|
105
|
+
} else {
|
|
106
|
+
this.data = flattenItemData(update)
|
|
107
|
+
}
|
|
108
|
+
if (deferredItemUpdates) {
|
|
109
|
+
deferredItemUpdates.add(this)
|
|
110
|
+
} else {
|
|
111
|
+
this.triggerUpdate()
|
|
112
|
+
}
|
|
113
|
+
return this.data
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Overlay item-level translations onto the base store when the runtime is in
|
|
118
|
+
* translation mode. Mirrors Hydrogen's `WeaverseHydrogenItem.getSnapShot()`:
|
|
119
|
+
* memoized by `_store` ref + translation entry ref so the returned object is
|
|
120
|
+
* referentially stable, and a fast path that returns the base store untouched
|
|
121
|
+
* when the item has no translations.
|
|
122
|
+
*/
|
|
123
|
+
getSnapShot = (): ElementData => {
|
|
124
|
+
let base = this._store
|
|
125
|
+
let translations = this.weaverse.translationMap[this._id]
|
|
126
|
+
|
|
127
|
+
// No translations → return the base store directly (fast path).
|
|
128
|
+
if (!translations) {
|
|
129
|
+
return base
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Reuse the cached merge while both the store and translation refs hold.
|
|
133
|
+
if (
|
|
134
|
+
this._cachedSnapshot &&
|
|
135
|
+
this._snapshotStoreRef === base &&
|
|
136
|
+
this._snapshotTranslationRef === translations
|
|
137
|
+
) {
|
|
138
|
+
return this._cachedSnapshot
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let merged = { ...base }
|
|
142
|
+
for (let [key, entry] of Object.entries(translations)) {
|
|
143
|
+
if (entry.translatedValue !== undefined) {
|
|
144
|
+
merged[key] = entry.translatedValue
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this._cachedSnapshot = merged
|
|
149
|
+
this._snapshotStoreRef = base
|
|
150
|
+
this._snapshotTranslationRef = translations
|
|
151
|
+
return merged
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Register the Next item constructor globally. Idempotent: calling it more than
|
|
157
|
+
* once is safe. Invoked by the renderer before it builds a Weaverse instance.
|
|
158
|
+
*/
|
|
159
|
+
export function ensureNextItemConstructor() {
|
|
160
|
+
if (
|
|
161
|
+
!Weaverse.ItemConstructor ||
|
|
162
|
+
Weaverse.ItemConstructor === WeaverseItemStore ||
|
|
163
|
+
Weaverse.ItemConstructor === WeaverseNextItem
|
|
164
|
+
) {
|
|
165
|
+
Weaverse.ItemConstructor = WeaverseNextItem
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WeaverseNextClient,
|
|
3
|
+
WeaverseNextCommerceContext,
|
|
4
|
+
WeaverseNextComponent,
|
|
5
|
+
WeaverseNextComponentData,
|
|
6
|
+
WeaverseNextLoaderData,
|
|
7
|
+
WeaverseNextRequestContext,
|
|
8
|
+
} from './types'
|
|
9
|
+
import { generateDataFromSchema } from './utils'
|
|
10
|
+
|
|
11
|
+
/** Inputs for running registered component loaders over a page tree. */
|
|
12
|
+
export interface RunComponentLoadersArgs {
|
|
13
|
+
/** Weaverse client containing the component registry. */
|
|
14
|
+
client: WeaverseNextClient
|
|
15
|
+
/** Request-scoped commerce override; defaults to `client.commerce`. */
|
|
16
|
+
commerce?: WeaverseNextCommerceContext
|
|
17
|
+
/** Request-context override; defaults to `client.requestContext`. */
|
|
18
|
+
context?: WeaverseNextRequestContext
|
|
19
|
+
/** Page payload to walk; defaults to `client.data`. */
|
|
20
|
+
data?: WeaverseNextLoaderData | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isItemNode(node: unknown): node is WeaverseNextComponentData {
|
|
24
|
+
return (
|
|
25
|
+
typeof node === 'object' &&
|
|
26
|
+
node !== null &&
|
|
27
|
+
typeof (node as WeaverseNextComponentData).type === 'string'
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function cloneItem(item: WeaverseNextComponentData): WeaverseNextComponentData {
|
|
32
|
+
return {
|
|
33
|
+
...item,
|
|
34
|
+
children: Array.isArray(item.children)
|
|
35
|
+
? item.children.map((child) =>
|
|
36
|
+
isItemNode(child) ? cloneItem(child) : { ...child }
|
|
37
|
+
)
|
|
38
|
+
: item.children,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Walk a serialized page tree and run each registered component's `loader`,
|
|
44
|
+
* attaching the result to the item as `loaderData`.
|
|
45
|
+
*
|
|
46
|
+
* Loaders receive explicit `commerce.storefront` (and `weaverse.storefront` as
|
|
47
|
+
* a compatibility alias via the client). Children are walked recursively when
|
|
48
|
+
* they are inline item objects; flat `items` arrays are covered by the
|
|
49
|
+
* top-level walk.
|
|
50
|
+
*
|
|
51
|
+
* @returns a cloned `data` object with `loaderData` attached to cloned items.
|
|
52
|
+
*/
|
|
53
|
+
export async function runWeaverseComponentLoaders(
|
|
54
|
+
args: RunComponentLoadersArgs
|
|
55
|
+
): Promise<WeaverseNextLoaderData | null> {
|
|
56
|
+
let { client, context, commerce } = args
|
|
57
|
+
let sourceData = args.data ?? client.data
|
|
58
|
+
if (!sourceData) {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
if (!sourceData.page.items.length) {
|
|
62
|
+
return sourceData
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let data: WeaverseNextLoaderData = {
|
|
66
|
+
...sourceData,
|
|
67
|
+
page: {
|
|
68
|
+
...sourceData.page,
|
|
69
|
+
items: sourceData.page.items.map(cloneItem),
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
let items = data.page.items
|
|
73
|
+
|
|
74
|
+
let componentMap = new Map<string, WeaverseNextComponent>()
|
|
75
|
+
for (let component of client.components) {
|
|
76
|
+
if (component?.schema?.type) {
|
|
77
|
+
componentMap.set(component.schema.type, component)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let visited = new Set<string>()
|
|
82
|
+
let resolvedContext = context ?? client.requestContext
|
|
83
|
+
let resolvedCommerce = commerce ?? client.commerce
|
|
84
|
+
|
|
85
|
+
// Per-call `weaverse` alias. When a request-scoped `commerce` is passed, the
|
|
86
|
+
// `weaverse.storefront` compatibility alias must resolve to *that* storefront,
|
|
87
|
+
// not the client's construction-time commerce. Pilot-style loaders call
|
|
88
|
+
// `weaverse.storefront.query(...)`, so a stale alias would query the wrong
|
|
89
|
+
// (or an undefined) storefront. When no override is given we reuse the client
|
|
90
|
+
// as-is to preserve its prototype getters.
|
|
91
|
+
let resolvedWeaverse: WeaverseNextClient =
|
|
92
|
+
resolvedCommerce === client.commerce
|
|
93
|
+
? client
|
|
94
|
+
: {
|
|
95
|
+
...client,
|
|
96
|
+
commerce: resolvedCommerce,
|
|
97
|
+
storefront: resolvedCommerce?.storefront,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function walk(nodes: WeaverseNextComponentData[]) {
|
|
101
|
+
for (let item of nodes) {
|
|
102
|
+
if (!item || visited.has(item.id)) {
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
visited.add(item.id)
|
|
106
|
+
|
|
107
|
+
let component = componentMap.get(item.type)
|
|
108
|
+
if (component?.loader) {
|
|
109
|
+
try {
|
|
110
|
+
item.loaderData = await component.loader({
|
|
111
|
+
data: { ...generateDataFromSchema(component.schema), ...item.data },
|
|
112
|
+
weaverse: resolvedWeaverse,
|
|
113
|
+
context: resolvedContext,
|
|
114
|
+
commerce: resolvedCommerce,
|
|
115
|
+
})
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.warn('❌ Item loader run failed.', item.type, item.id, error)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let children = item.children
|
|
122
|
+
if (Array.isArray(children)) {
|
|
123
|
+
let inlineChildren = children.filter(
|
|
124
|
+
isItemNode
|
|
125
|
+
) as WeaverseNextComponentData[]
|
|
126
|
+
if (inlineChildren.length) {
|
|
127
|
+
await walk(inlineChildren)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
await walk(items)
|
|
134
|
+
return data
|
|
135
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createSchema, type SchemaType } from '@weaverse/schema'
|
|
2
|
+
import type { HTMLAttributes, RefObject } from 'react'
|
|
3
|
+
import type { WeaverseNextComponent, WeaverseNextComponentProps } from './types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default `main` root component.
|
|
7
|
+
*
|
|
8
|
+
* A Weaverse page tree is a flat `items` array with a single root item of type
|
|
9
|
+
* `main` whose `children` are the page's top-level sections. Hydrogen ships its
|
|
10
|
+
* own `main`; the Next adapter provides this minimal, framework-neutral
|
|
11
|
+
* equivalent so consumers don't have to register one just to render a basic
|
|
12
|
+
* page tree (omitting it would leave child sections unrendered).
|
|
13
|
+
*
|
|
14
|
+
* It renders `children` and forwards the Weaverse DOM attributes the shared
|
|
15
|
+
* renderer injects (`data-wv-id`, `data-wv-type`, `ref`, `className`, ...rest)
|
|
16
|
+
* onto the root element. `loaderData` is adapter-internal and stripped so it
|
|
17
|
+
* never lands on the DOM node.
|
|
18
|
+
*/
|
|
19
|
+
function Main(
|
|
20
|
+
props: WeaverseNextComponentProps & {
|
|
21
|
+
ref?: RefObject<HTMLDivElement | null>
|
|
22
|
+
}
|
|
23
|
+
) {
|
|
24
|
+
let { children, loaderData, ...rest } = props
|
|
25
|
+
// `rest` carries the Weaverse-injected DOM attributes (data-wv-*, ref,
|
|
26
|
+
// className). They are all valid div attributes, so the cast is safe.
|
|
27
|
+
return <div {...(rest as HTMLAttributes<HTMLDivElement>)}>{children}</div>
|
|
28
|
+
}
|
|
29
|
+
Main.displayName = 'main'
|
|
30
|
+
|
|
31
|
+
/** Schema for the default {@link Main} root component. Type is exactly `main`. */
|
|
32
|
+
export const mainSchema: SchemaType = createSchema({
|
|
33
|
+
type: 'main',
|
|
34
|
+
title: 'Main',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Default `main` component module, registered ahead of user components by
|
|
39
|
+
* {@link registerWeaverseNextComponents}. Kept package-internal — consumers
|
|
40
|
+
* override it simply by registering their own component with `type: 'main'`.
|
|
41
|
+
*/
|
|
42
|
+
export const defaultMainComponent: WeaverseNextComponent = {
|
|
43
|
+
default: Main,
|
|
44
|
+
schema: mainSchema,
|
|
45
|
+
}
|