@pradip1995/framework-runtime 0.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/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@pradip1995/framework-runtime",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "sideEffects": false,
9
+ "files": [
10
+ "src"
11
+ ],
12
+ "exports": {
13
+ ".": "./src/index.ts",
14
+ "./render-page": "./src/render-page.tsx"
15
+ },
16
+ "dependencies": {
17
+ "@pradip1995/framework-core": "0.2.0",
18
+ "@pradip1995/plugin-sdk": "0.2.0",
19
+ "@pradip1995/medusa-connector": "0.2.0"
20
+ },
21
+ "peerDependencies": {
22
+ "next": ">=15",
23
+ "react": ">=19"
24
+ },
25
+ "devDependencies": {
26
+ "@types/react": "^19",
27
+ "next": "15.3.8",
28
+ "react": "19.0.3",
29
+ "typescript": "^5.7.2"
30
+ },
31
+ "scripts": {
32
+ "typecheck": "tsc --noEmit",
33
+ "lint": "tsc --noEmit"
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { renderPage, registerServices, resolveServices, getSegmentProps } from "./render-page"
2
+ export { renderConfiguredPage } from "./render-configured-page"
3
+ export type { RenderPageInput } from "./render-page"
4
+ export type { ConfiguredPage, RenderConfiguredPageInput, SegmentEntry } from "./render-configured-page"
@@ -0,0 +1,48 @@
1
+ import { container } from "@pradip1995/framework-core"
2
+
3
+ let registered = false
4
+
5
+ export async function registerServices() {
6
+ if (registered) return
7
+ registered = true
8
+
9
+ const {
10
+ HomeModule,
11
+ LayoutModule,
12
+ ProductModule,
13
+ StoreModule,
14
+ CartModule,
15
+ CheckoutModule,
16
+ AccountModule,
17
+ OrderModule,
18
+ WishlistModule,
19
+ HelpModule,
20
+ } = await import("@pradip1995/medusa-connector")
21
+
22
+ container.register("homeModule", () => new HomeModule())
23
+ container.register("layoutModule", () => new LayoutModule())
24
+ container.register("productModule", () => new ProductModule())
25
+ container.register("storeModule", () => new StoreModule())
26
+ container.register("cartModule", () => new CartModule())
27
+ container.register("checkoutModule", () => new CheckoutModule())
28
+ container.register("accountModule", () => new AccountModule())
29
+ container.register("orderModule", () => new OrderModule())
30
+ container.register("wishlistModule", () => new WishlistModule())
31
+ container.register("helpModule", () => new HelpModule())
32
+ }
33
+
34
+ export async function resolveServices(): Promise<Record<string, unknown>> {
35
+ await registerServices()
36
+ return {
37
+ homeModule: container.resolve("homeModule"),
38
+ layoutModule: container.resolve("layoutModule"),
39
+ productModule: container.resolve("productModule"),
40
+ storeModule: container.resolve("storeModule"),
41
+ cartModule: container.resolve("cartModule"),
42
+ checkoutModule: container.resolve("checkoutModule"),
43
+ accountModule: container.resolve("accountModule"),
44
+ orderModule: container.resolve("orderModule"),
45
+ wishlistModule: container.resolve("wishlistModule"),
46
+ helpModule: container.resolve("helpModule"),
47
+ }
48
+ }
@@ -0,0 +1,77 @@
1
+ import { buildRouteContext } from "@pradip1995/framework-core"
2
+ import { resolveServices } from "./register-services"
3
+ import { getSegmentProps } from "./segment-props"
4
+ import type { ComponentType, ReactNode } from "react"
5
+
6
+ export type SegmentEntry = {
7
+ pkg: string
8
+ Component: ComponentType<Record<string, unknown>>
9
+ }
10
+
11
+ export type ConfiguredPage = {
12
+ workflow: {
13
+ execute: (ctx: {
14
+ route: ReturnType<typeof buildRouteContext>
15
+ services: Record<string, unknown>
16
+ }) => Promise<Record<string, unknown>>
17
+ }
18
+ layout: ComponentType<{ data: Record<string, unknown>; children: ReactNode }>
19
+ segments: SegmentEntry[]
20
+ }
21
+
22
+ export type RenderConfiguredPageInput = {
23
+ page: ConfiguredPage
24
+ countryCode: string
25
+ slug: string[]
26
+ searchParams: Record<string, string | string[] | undefined>
27
+ }
28
+
29
+ function NotFoundPanel({ message }: { message: string }) {
30
+ return (
31
+ <section className="mx-auto max-w-lg px-4 py-20 text-center">
32
+ <h1 className="section-heading mb-3">Not found</h1>
33
+ <p className="text-muted text-sm">{message}</p>
34
+ <a href="/store" className="btn-primary inline-block mt-8">
35
+ Continue shopping
36
+ </a>
37
+ </section>
38
+ )
39
+ }
40
+
41
+ export async function renderConfiguredPage(input: RenderConfiguredPageInput): Promise<ReactNode> {
42
+ try {
43
+ const route = buildRouteContext(input.countryCode, input.slug, input.searchParams)
44
+ const services = await resolveServices()
45
+ const data = await input.page.workflow.execute({ route, services })
46
+ const Layout = input.page.layout
47
+
48
+ if (data.notFound) {
49
+ const message =
50
+ data.emptyCart === true
51
+ ? "Your cart is empty or checkout is unavailable."
52
+ : "The page you're looking for doesn't exist or is temporarily unavailable."
53
+
54
+ return (
55
+ <Layout data={data}>
56
+ <NotFoundPanel message={message} />
57
+ </Layout>
58
+ )
59
+ }
60
+
61
+ const segmentElements = input.page.segments.map(({ pkg, Component: Segment }) => {
62
+ const props = getSegmentProps(pkg, data)
63
+ return <Segment key={pkg} {...props} />
64
+ })
65
+
66
+ return <Layout data={data}>{segmentElements}</Layout>
67
+ } catch (error) {
68
+ console.error("[storefront] renderConfiguredPage error:", error)
69
+ const message = error instanceof Error ? error.message : "Unknown error"
70
+ return (
71
+ <main className="min-h-screen p-8 bg-page-bg">
72
+ <h1 className="text-xl font-semibold text-red-600 mb-2">Storefront error</h1>
73
+ <p className="text-sm text-body mb-4">{message}</p>
74
+ </main>
75
+ )
76
+ }
77
+ }
@@ -0,0 +1,80 @@
1
+ import { matchRoute, buildRouteContext } from "@pradip1995/framework-core"
2
+ import { resolveServices } from "./register-services"
3
+ import { getSegmentProps } from "./segment-props"
4
+ import type { ComponentType, ReactNode } from "react"
5
+ import type { PageConfigEntry } from "@pradip1995/plugin-sdk"
6
+
7
+ type ImportMap = Record<string, () => Promise<{ default: unknown }>>
8
+
9
+ export type RenderPageInput = {
10
+ countryCode: string
11
+ slug: string[]
12
+ searchParams: Record<string, string | string[] | undefined>
13
+ routeManifest: PageConfigEntry[]
14
+ importMap: ImportMap
15
+ }
16
+
17
+ async function resolvePlugin(importMap: ImportMap, pkg: string) {
18
+ const loader = importMap[pkg]
19
+ if (!loader) throw new Error(`Plugin not in import map: ${pkg}`)
20
+ return loader()
21
+ }
22
+
23
+ export async function renderPage(input: RenderPageInput): Promise<ReactNode> {
24
+ try {
25
+ const pathname = input.slug.length > 0 ? `/${input.slug.join("/")}` : "/"
26
+ const page = matchRoute(input.routeManifest, pathname)
27
+
28
+ if (!page) {
29
+ const { notFound } = await import("next/navigation")
30
+ notFound()
31
+ }
32
+
33
+ const route = buildRouteContext(input.countryCode, input.slug, input.searchParams)
34
+ const services = await resolveServices()
35
+
36
+ const workflowMod = (await resolvePlugin(input.importMap, page!.workflow)) as {
37
+ default: {
38
+ execute: (ctx: {
39
+ route: typeof route
40
+ services: Record<string, unknown>
41
+ }) => Promise<Record<string, unknown>>
42
+ }
43
+ }
44
+
45
+ const data = await workflowMod.default.execute({ route, services })
46
+
47
+ const layoutMod = (await resolvePlugin(input.importMap, page!.layout)) as {
48
+ default: ComponentType<{ data: Record<string, unknown>; children: ReactNode }>
49
+ }
50
+ const Layout = layoutMod.default
51
+
52
+ const segmentElements = await Promise.all(
53
+ page!.segments.map(async (segmentPkg) => {
54
+ const mod = (await resolvePlugin(input.importMap, segmentPkg)) as {
55
+ default: ComponentType<Record<string, unknown>>
56
+ }
57
+ const props = getSegmentProps(segmentPkg, data)
58
+ const Segment = mod.default
59
+ return <Segment key={segmentPkg} {...props} />
60
+ })
61
+ )
62
+
63
+ return <Layout data={data}>{segmentElements}</Layout>
64
+ } catch (error) {
65
+ console.error("[storefront] renderPage error:", error)
66
+ const message = error instanceof Error ? error.message : "Unknown error"
67
+ return (
68
+ <main className="min-h-screen p-8 bg-page-bg">
69
+ <h1 className="text-xl font-semibold text-red-600 mb-2">Storefront error</h1>
70
+ <p className="text-sm text-body mb-4">{message}</p>
71
+ <p className="text-xs text-muted">
72
+ Check MEDUSA_BACKEND_URL is reachable and NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY is set.
73
+ </p>
74
+ </main>
75
+ )
76
+ }
77
+ }
78
+
79
+ export { registerServices, resolveServices } from "./register-services"
80
+ export { getSegmentProps } from "./segment-props"
@@ -0,0 +1,50 @@
1
+ /** Maps segment package name → workflow output key(s) */
2
+ const SEGMENT_DATA_KEYS: Record<string, string> = {
3
+ "@pradip1995/segment-hero": "hero",
4
+ "@pradip1995/segment-shop-by-age": "shopByAge",
5
+ "@pradip1995/segment-shop-by-category": "shopByCategory",
6
+ "@pradip1995/segment-why-choose-us": "whyChooseUs",
7
+ "@pradip1995/segment-new-arrivals": "newArrivals",
8
+ "@pradip1995/segment-loved-by-moms": "lovedByMoms",
9
+ "@pradip1995/segment-testimonials": "testimonials",
10
+ "@pradip1995/segment-features": "features",
11
+ "@pradip1995/segment-promotional-banners": "promotionalBanners",
12
+ "@pradip1995/segment-collections-showcase": "collectionsShowcase",
13
+ "@pradip1995/segment-bestsellers-carousel": "bestsellers",
14
+ "@pradip1995/segment-reviews-marquee": "testimonials",
15
+ "@pradip1995/segment-nav": "nav",
16
+ "@pradip1995/segment-footer": "footer",
17
+ "@pradip1995/segment-promo-bar": "promoBar",
18
+ "@pradip1995/segment-product-grid": "store",
19
+ "@pradip1995/segment-refinement-list": "store",
20
+ "@pradip1995/segment-mobile-filters": "store",
21
+ "@pradip1995/segment-product-gallery": "product",
22
+ "@pradip1995/segment-product-info": "product",
23
+ "@pradip1995/segment-product-actions": "product",
24
+ "@pradip1995/segment-related-products": "relatedProducts",
25
+ "@pradip1995/segment-cart-item": "cartPage",
26
+ "@pradip1995/segment-cart-summary": "cartPage",
27
+ "@pradip1995/segment-checkout-form": "checkout",
28
+ "@pradip1995/segment-checkout-summary": "checkout",
29
+ "@pradip1995/segment-login-template": "account",
30
+ "@pradip1995/segment-login": "account",
31
+ "@pradip1995/segment-register": "account",
32
+ "@pradip1995/segment-forgot-password": "account",
33
+ "@pradip1995/segment-google-login": "account",
34
+ "@pradip1995/segment-order-details": "orderPage",
35
+ "@pradip1995/segment-wishlist": "wishlistPage",
36
+ "@pradip1995/segment-help": "helpPage",
37
+ }
38
+
39
+ export function getSegmentProps(
40
+ segmentPkg: string,
41
+ data: Record<string, unknown>
42
+ ): Record<string, unknown> {
43
+ const key = SEGMENT_DATA_KEYS[segmentPkg]
44
+ if (!key) return data
45
+ const value = data[key]
46
+ if (value && typeof value === "object" && !Array.isArray(value)) {
47
+ return value as Record<string, unknown>
48
+ }
49
+ return { [key]: value }
50
+ }