@pradip1995/framework-runtime 0.2.1 → 0.2.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pradip1995/framework-runtime",
3
- "version": "0.2.1",
3
+ "version": "0.2.5",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "@pradip1995/framework-core": "0.2.1",
18
18
  "@pradip1995/plugin-sdk": "0.2.1",
19
- "@pradip1995/medusa-connector": "0.2.1"
19
+ "@pradip1995/medusa-connector": "0.2.2"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "next": ">=15",
@@ -0,0 +1,37 @@
1
+ export type BuilderSegmentDataFile = {
2
+ version: string
3
+ updatedAt: string
4
+ data: Record<string, Record<string, unknown>>
5
+ }
6
+
7
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
8
+ return typeof value === "object" && value !== null && !Array.isArray(value)
9
+ }
10
+
11
+ /** Deep-merge builder CMS values over workflow output (builder wins on conflicts). */
12
+ export function mergeBuilderSegmentData(
13
+ workflowData: Record<string, unknown>,
14
+ builderData: BuilderSegmentDataFile | null | undefined
15
+ ): Record<string, unknown> {
16
+ if (!builderData?.data || Object.keys(builderData.data).length === 0) {
17
+ return workflowData
18
+ }
19
+
20
+ const merged = { ...workflowData }
21
+
22
+ for (const [key, overlay] of Object.entries(builderData.data)) {
23
+ if (!isPlainObject(overlay)) {
24
+ merged[key] = overlay
25
+ continue
26
+ }
27
+
28
+ const existing = merged[key]
29
+ if (isPlainObject(existing)) {
30
+ merged[key] = { ...existing, ...overlay }
31
+ } else {
32
+ merged[key] = { ...overlay }
33
+ }
34
+ }
35
+
36
+ return merged
37
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  export { renderPage, registerServices, resolveServices, getSegmentProps } from "./render-page"
2
2
  export { renderConfiguredPage } from "./render-configured-page"
3
+ export {
4
+ applyBuilderDataToWorkflow,
5
+ getSegmentDataKey,
6
+ SEGMENT_DATA_KEYS,
7
+ } from "./segment-props"
8
+ export { mergeBuilderSegmentData } from "./builder-data"
3
9
  export type { RenderPageInput } from "./render-page"
4
10
  export type { ConfiguredPage, RenderConfiguredPageInput, SegmentEntry } from "./render-configured-page"
11
+ export type { BuilderSegmentDataFile } from "./builder-data"
@@ -1,11 +1,19 @@
1
1
  import { buildRouteContext } from "@pradip1995/framework-core"
2
2
  import { resolveServices } from "./register-services"
3
- import { getSegmentProps } from "./segment-props"
3
+ import {
4
+ applyChromeSegmentData,
5
+ applyBuilderDataToWorkflow,
6
+ resolveSegmentProps,
7
+ type ChromeSegmentRef,
8
+ } from "./segment-props"
9
+ import type { BuilderSegmentDataFile } from "./builder-data"
4
10
  import type { ComponentType, ReactNode } from "react"
5
11
 
6
12
  export type SegmentEntry = {
7
13
  pkg: string
8
14
  Component: ComponentType<Record<string, unknown>>
15
+ /** CMS values from pages.config.json, shaped by segment builder.settings.json */
16
+ data?: Record<string, unknown>
9
17
  }
10
18
 
11
19
  export type ConfiguredPage = {
@@ -24,6 +32,12 @@ export type RenderConfiguredPageInput = {
24
32
  countryCode: string
25
33
  slug: string[]
26
34
  searchParams: Record<string, string | string[] | undefined>
35
+ /** Global layout chrome from pages.config.json chrome[] */
36
+ chrome?: ChromeSegmentRef[] | null
37
+ /** @deprecated legacy global overlay */
38
+ pageSegmentData?: BuilderSegmentDataFile | null
39
+ /** @deprecated use chrome + inline segment data */
40
+ builderSegmentData?: BuilderSegmentDataFile | null
27
41
  }
28
42
 
29
43
  function NotFoundPanel({ message }: { message: string }) {
@@ -42,7 +56,14 @@ export async function renderConfiguredPage(input: RenderConfiguredPageInput): Pr
42
56
  try {
43
57
  const route = buildRouteContext(input.countryCode, input.slug, input.searchParams)
44
58
  const services = await resolveServices()
45
- const data = await input.page.workflow.execute({ route, services })
59
+ const workflowData = await input.page.workflow.execute({ route, services })
60
+
61
+ const legacyOverlay = input.pageSegmentData ?? input.builderSegmentData
62
+ let data = legacyOverlay
63
+ ? applyBuilderDataToWorkflow(workflowData, legacyOverlay)
64
+ : workflowData
65
+ data = applyChromeSegmentData(data, input.chrome)
66
+
46
67
  const Layout = input.page.layout
47
68
 
48
69
  if (data.notFound) {
@@ -58,8 +79,8 @@ export async function renderConfiguredPage(input: RenderConfiguredPageInput): Pr
58
79
  )
59
80
  }
60
81
 
61
- const segmentElements = input.page.segments.map(({ pkg, Component: Segment }) => {
62
- const props = getSegmentProps(pkg, data)
82
+ const segmentElements = input.page.segments.map(({ pkg, Component: Segment, data: inline }) => {
83
+ const props = resolveSegmentProps(pkg, data, inline)
63
84
  return <Segment key={pkg} {...props} />
64
85
  })
65
86
 
@@ -1,6 +1,7 @@
1
1
  import { matchRoute, buildRouteContext } from "@pradip1995/framework-core"
2
2
  import { resolveServices } from "./register-services"
3
- import { getSegmentProps } from "./segment-props"
3
+ import { applyBuilderDataToWorkflow, getSegmentProps } from "./segment-props"
4
+ import type { BuilderSegmentDataFile } from "./builder-data"
4
5
  import type { ComponentType, ReactNode } from "react"
5
6
  import type { PageConfigEntry } from "@pradip1995/plugin-sdk"
6
7
 
@@ -12,6 +13,10 @@ export type RenderPageInput = {
12
13
  searchParams: Record<string, string | string[] | undefined>
13
14
  routeManifest: PageConfigEntry[]
14
15
  importMap: ImportMap
16
+ /** CMS values from pages.config.json segmentData */
17
+ pageSegmentData?: BuilderSegmentDataFile | null
18
+ /** @deprecated use pageSegmentData */
19
+ builderSegmentData?: BuilderSegmentDataFile | null
15
20
  }
16
21
 
17
22
  async function resolvePlugin(importMap: ImportMap, pkg: string) {
@@ -42,7 +47,9 @@ export async function renderPage(input: RenderPageInput): Promise<ReactNode> {
42
47
  }
43
48
  }
44
49
 
45
- const data = await workflowMod.default.execute({ route, services })
50
+ const workflowData = await workflowMod.default.execute({ route, services })
51
+ const pageSegmentData = input.pageSegmentData ?? input.builderSegmentData
52
+ const data = applyBuilderDataToWorkflow(workflowData, pageSegmentData)
46
53
 
47
54
  const layoutMod = (await resolvePlugin(input.importMap, page!.layout)) as {
48
55
  default: ComponentType<{ data: Record<string, unknown>; children: ReactNode }>
@@ -1,5 +1,7 @@
1
+ import { mergeBuilderSegmentData, type BuilderSegmentDataFile } from "./builder-data"
2
+
1
3
  /** Maps segment package name → workflow output key(s) */
2
- const SEGMENT_DATA_KEYS: Record<string, string> = {
4
+ export const SEGMENT_DATA_KEYS: Record<string, string> = {
3
5
  "@pradip1995/segment-hero": "hero",
4
6
  "@pradip1995/segment-shop-by-age": "shopByAge",
5
7
  "@pradip1995/segment-shop-by-category": "shopByCategory",
@@ -36,6 +38,10 @@ const SEGMENT_DATA_KEYS: Record<string, string> = {
36
38
  "@pradip1995/segment-help": "helpPage",
37
39
  }
38
40
 
41
+ export function getSegmentDataKey(segmentPkg: string): string | undefined {
42
+ return SEGMENT_DATA_KEYS[segmentPkg]
43
+ }
44
+
39
45
  export function getSegmentProps(
40
46
  segmentPkg: string,
41
47
  data: Record<string, unknown>
@@ -48,3 +54,93 @@ export function getSegmentProps(
48
54
  }
49
55
  return { [key]: value }
50
56
  }
57
+
58
+ function mergeSegmentRecords(
59
+ base: Record<string, unknown>,
60
+ overlay: Record<string, unknown>
61
+ ): Record<string, unknown> {
62
+ const result = { ...base }
63
+ for (const [key, value] of Object.entries(overlay)) {
64
+ const existing = result[key]
65
+ if (
66
+ value &&
67
+ typeof value === "object" &&
68
+ !Array.isArray(value) &&
69
+ existing &&
70
+ typeof existing === "object" &&
71
+ !Array.isArray(existing)
72
+ ) {
73
+ result[key] = mergeSegmentRecords(
74
+ existing as Record<string, unknown>,
75
+ value as Record<string, unknown>
76
+ )
77
+ } else {
78
+ result[key] = value
79
+ }
80
+ }
81
+ return result
82
+ }
83
+
84
+ export function resolveSegmentProps(
85
+ segmentPkg: string,
86
+ workflowData: Record<string, unknown>,
87
+ inlineData?: Record<string, unknown> | null
88
+ ): Record<string, unknown> {
89
+ const base = getSegmentProps(segmentPkg, workflowData)
90
+
91
+ const layoutCart = workflowData.cart
92
+ if (
93
+ layoutCart &&
94
+ typeof layoutCart === "object" &&
95
+ (segmentPkg === "@pradip1995/segment-product-actions" ||
96
+ segmentPkg === "@pradip1995/segment-product-info") &&
97
+ !("cart" in base)
98
+ ) {
99
+ base.cart = layoutCart
100
+ }
101
+
102
+ if (!inlineData || Object.keys(inlineData).length === 0) return base
103
+ return mergeSegmentRecords(base, inlineData)
104
+ }
105
+
106
+ export type ChromeSegmentRef = string | { package: string; data?: Record<string, unknown> }
107
+
108
+ function chromePackageName(ref: ChromeSegmentRef): string {
109
+ return typeof ref === "string" ? ref : ref.package
110
+ }
111
+
112
+ function chromeInlineData(ref: ChromeSegmentRef): Record<string, unknown> | undefined {
113
+ return typeof ref === "string" ? undefined : ref.data
114
+ }
115
+
116
+ /** Merge global chrome segment data (nav, footer, promo) into workflow output for layout. */
117
+ export function applyChromeSegmentData(
118
+ workflowData: Record<string, unknown>,
119
+ chrome: ChromeSegmentRef[] | null | undefined
120
+ ): Record<string, unknown> {
121
+ if (!chrome?.length) return workflowData
122
+ const merged = { ...workflowData }
123
+
124
+ for (const ref of chrome) {
125
+ const pkg = chromePackageName(ref)
126
+ const data = chromeInlineData(ref)
127
+ const key = SEGMENT_DATA_KEYS[pkg]
128
+ if (!key || !data || Object.keys(data).length === 0) continue
129
+
130
+ const existing = merged[key]
131
+ if (existing && typeof existing === "object" && !Array.isArray(existing)) {
132
+ merged[key] = mergeSegmentRecords(existing as Record<string, unknown>, data)
133
+ } else {
134
+ merged[key] = { ...data }
135
+ }
136
+ }
137
+
138
+ return merged
139
+ }
140
+
141
+ export function applyBuilderDataToWorkflow(
142
+ workflowData: Record<string, unknown>,
143
+ builderData: BuilderSegmentDataFile | null | undefined
144
+ ): Record<string, unknown> {
145
+ return mergeBuilderSegmentData(workflowData, builderData)
146
+ }