@kaena1/ecomm-remote-utils 1.0.0-f-EC-4239.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.
@@ -0,0 +1,144 @@
1
+ import { default as default_2 } from 'react';
2
+ import { JSX as JSX_2 } from 'react/jsx-runtime';
3
+ import { ReactNode } from 'react';
4
+
5
+ declare interface ComponentManifest {
6
+ bundleUrl: string;
7
+ styleUrl?: string;
8
+ /** Rollup chunk files that the entry imports. Pre-declared so consumers can add
9
+ * modulepreload hints before the entry is evaluated, eliminating cascade fetches. */
10
+ chunkUrls?: string[];
11
+ skeletonSpec?: SkeletonSpec;
12
+ }
13
+
14
+ export declare class ModuleLoader {
15
+ private loadedModules;
16
+ private importMapInjected;
17
+ private mmUiComponents;
18
+ /**
19
+ * Register the mm-ui-components module to expose on `window` before any
20
+ * remote component is loaded. Call this once at app startup, before the
21
+ * first render. Must match the `mmUiComponents` prop passed to `ImportMapScripts`.
22
+ */
23
+ configure(mmUiComponents: Record<string, unknown>): void;
24
+ private prepareGlobalDependencies;
25
+ /**
26
+ * Ensure import map is injected before loading any modules
27
+ * Import maps MUST be added before any module scripts
28
+ */
29
+ private ensureImportMap;
30
+ /**
31
+ * Load remote React component from ES module URL
32
+ *
33
+ * @param options - Configuration object with bundleUrl and componentName
34
+ * @returns Promise that resolves to the loaded React component
35
+ * @throws Error if component loading fails or times out (10s)
36
+ */
37
+ loadComponent(options: ModuleLoadOptions): Promise<RemoteComponentType>;
38
+ /**
39
+ * Load ES module from external URL using dynamic script injection
40
+ * This bypasses Webpack's module resolution and works with import maps
41
+ */
42
+ private loadModuleViaScript;
43
+ /**
44
+ * Preload a module without extracting components
45
+ * Useful for warming up cache
46
+ *
47
+ * @param bundleUrl - URL of the module to preload
48
+ */
49
+ preloadModule(bundleUrl: string): Promise<void>;
50
+ /**
51
+ * Check if a module has already been loaded
52
+ *
53
+ * @param bundleUrl - URL of the module to check
54
+ * @returns true if module is already loaded, false otherwise
55
+ */
56
+ isLoaded(bundleUrl: string): boolean;
57
+ /**
58
+ * Clear all loaded modules from cache
59
+ * Useful for development/testing
60
+ */
61
+ clearCache(): void;
62
+ }
63
+
64
+ export declare const moduleLoader: ModuleLoader;
65
+
66
+ declare interface ModuleLoadOptions {
67
+ bundleUrl: string;
68
+ componentName: string;
69
+ }
70
+
71
+ /**
72
+ * Configures the module loader with the host app's mm-ui-components instance
73
+ * so remote components share the same ThemeProvider / ConfigProvider contexts.
74
+ *
75
+ * Place this once near the root of the app (e.g. in _app.tsx), at the same
76
+ * level as ThemeProvider and ConfigProvider. Also pass the same `mmUiComponents`
77
+ * object to `<ImportMapScripts>` in the document head.
78
+ */
79
+ export declare const RemoteComponentsProvider: ({ mmUiComponents, children, }: RemoteComponentsProviderProps) => JSX_2.Element;
80
+
81
+ declare interface RemoteComponentsProviderProps {
82
+ /**
83
+ * Pass the mm-ui-components module (or a subset of its exports) here.
84
+ * The provider exposes it on `window.mmUiComponents` so remote components
85
+ * can consume it via the import map, sharing the same React context instances
86
+ * as the host app.
87
+ *
88
+ * @example
89
+ * import { ThemeProvider, ConfigProvider } from '@kaena1/mm-ui-components';
90
+ * <RemoteComponentsProvider mmUiComponents={{ ThemeProvider, ConfigProvider }}>
91
+ *
92
+ * @example full namespace
93
+ * import * as mmUiComponents from '@kaena1/mm-ui-components';
94
+ * <RemoteComponentsProvider mmUiComponents={mmUiComponents}>
95
+ */
96
+ mmUiComponents: Record<string, unknown>;
97
+ children: ReactNode;
98
+ }
99
+
100
+ declare interface RemoteComponentState {
101
+ component: RemoteComponentType | null;
102
+ manifest: ComponentManifest | null;
103
+ loading: boolean;
104
+ error: string | null;
105
+ loaded: boolean;
106
+ }
107
+
108
+ declare type RemoteComponentType = React.ComponentType<any>;
109
+
110
+ export declare const RemoteRenderer: default_2.FC<RemoteRendererProps>;
111
+
112
+ declare interface RemoteRendererProps {
113
+ manifest: ComponentManifest;
114
+ componentName: string;
115
+ componentProps?: any;
116
+ autoLoad?: boolean;
117
+ children?: default_2.ReactNode;
118
+ errorFallback?: (error: string) => default_2.ReactNode;
119
+ }
120
+
121
+ declare interface SkeletonSpec {
122
+ shimmerStyle: string;
123
+ componentStyle: string;
124
+ containerClass: string;
125
+ minHeight: string;
126
+ barWidth: string;
127
+ barHeight: number;
128
+ }
129
+
130
+ export declare function useRemoteComponent(options: UseRemoteComponentOptions): UseRemoteComponentReturn;
131
+
132
+ declare interface UseRemoteComponentOptions {
133
+ manifest: ComponentManifest;
134
+ componentName: string;
135
+ autoLoad?: boolean;
136
+ }
137
+
138
+ declare interface UseRemoteComponentReturn extends RemoteComponentState {
139
+ loadComponent: () => Promise<void>;
140
+ reload: () => Promise<void>;
141
+ clearError: () => void;
142
+ }
143
+
144
+ export { }
package/dist/client.js ADDED
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { useRemoteComponent as r } from "./hooks/useRemoteComponent.js";
3
+ import { RemoteRenderer as t } from "./components/RemoteRenderer.js";
4
+ import { RemoteComponentsProvider as n } from "./components/RemoteComponentsProvider.js";
5
+ import { ModuleLoader as f, moduleLoader as u } from "./services/moduleLoader.js";
6
+ export {
7
+ f as ModuleLoader,
8
+ n as RemoteComponentsProvider,
9
+ t as RemoteRenderer,
10
+ u as moduleLoader,
11
+ r as useRemoteComponent
12
+ };
@@ -0,0 +1,51 @@
1
+ import { jsxs as t, Fragment as r, jsx as e } from "react/jsx-runtime";
2
+ import n from "../node_modules/es-module-shims/dist/es-module-shims.js";
3
+ import { generateImportMapJson as o } from "../services/ImportMapService.js";
4
+ const l = ({
5
+ mmUiComponentsExports: s
6
+ } = {}) => /* @__PURE__ */ t(r, { children: [
7
+ /* @__PURE__ */ e(
8
+ "script",
9
+ {
10
+ type: "esms-options",
11
+ dangerouslySetInnerHTML: { __html: JSON.stringify({ shimMode: !0 }) }
12
+ }
13
+ ),
14
+ /* @__PURE__ */ e(
15
+ "script",
16
+ {
17
+ suppressHydrationWarning: !0,
18
+ dangerouslySetInnerHTML: { __html: n }
19
+ }
20
+ ),
21
+ /* @__PURE__ */ e(
22
+ "link",
23
+ {
24
+ rel: "preconnect",
25
+ href: "https://assets-qa.mintmobile.com",
26
+ crossOrigin: "anonymous"
27
+ }
28
+ ),
29
+ /* @__PURE__ */ e(
30
+ "link",
31
+ {
32
+ rel: "preconnect",
33
+ href: "https://assets.mintmobile.com",
34
+ crossOrigin: "anonymous"
35
+ }
36
+ ),
37
+ /* @__PURE__ */ e("link", { rel: "dns-prefetch", href: "https://assets-qa.mintmobile.com" }),
38
+ /* @__PURE__ */ e("link", { rel: "dns-prefetch", href: "https://assets.mintmobile.com" }),
39
+ /* @__PURE__ */ e(
40
+ "script",
41
+ {
42
+ type: "importmap-shim",
43
+ dangerouslySetInnerHTML: {
44
+ __html: o(void 0, s)
45
+ }
46
+ }
47
+ )
48
+ ] });
49
+ export {
50
+ l as ImportMapScripts
51
+ };
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as t, Fragment as f } from "react/jsx-runtime";
3
+ import { useEffect as u } from "react";
4
+ import { moduleLoader as e } from "../services/moduleLoader.js";
5
+ const n = ({
6
+ mmUiComponents: r,
7
+ children: o
8
+ }) => (e.configure(r), u(() => {
9
+ e.configure(r);
10
+ }, [r]), /* @__PURE__ */ t(f, { children: o }));
11
+ export {
12
+ n as RemoteComponentsProvider
13
+ };
@@ -0,0 +1,24 @@
1
+ import { jsxs as s, Fragment as n, jsx as e } from "react/jsx-runtime";
2
+ import { Fragment as m } from "react";
3
+ const h = ({ manifest: r }) => {
4
+ var o;
5
+ return /* @__PURE__ */ s(n, { children: [
6
+ r.styleUrl && /* @__PURE__ */ e("link", { rel: "stylesheet", href: r.styleUrl }),
7
+ /* @__PURE__ */ e("link", { rel: "modulepreload-shim", href: r.bundleUrl }),
8
+ (o = r.chunkUrls) == null ? void 0 : o.map((l) => /* @__PURE__ */ s(m, { children: [
9
+ /* @__PURE__ */ e(
10
+ "link",
11
+ {
12
+ rel: "preload",
13
+ as: "fetch",
14
+ crossOrigin: "anonymous",
15
+ href: l
16
+ }
17
+ ),
18
+ /* @__PURE__ */ e("link", { rel: "modulepreload-shim", href: l })
19
+ ] }, l))
20
+ ] });
21
+ };
22
+ export {
23
+ h as RemoteLinks
24
+ };
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import { jsx as t, Fragment as l, jsxs as c } from "react/jsx-runtime";
3
+ import { useRemoteComponent as _ } from "../hooks/useRemoteComponent.js";
4
+ import { useState as k, useEffect as C, useRef as g, useLayoutEffect as v } from "react";
5
+ import { createPortal as N } from "react-dom";
6
+ function R({ node: e }) {
7
+ const n = g(null);
8
+ return v(() => {
9
+ const o = n.current;
10
+ if (o)
11
+ return o.appendChild(e), () => {
12
+ e.parentNode === o && e.remove();
13
+ };
14
+ }, [e]), /* @__PURE__ */ t("div", { style: { display: "contents" }, ref: n });
15
+ }
16
+ function x(e) {
17
+ const n = e.containerClass.replace(/[^a-zA-Z0-9-_]/g, "") || "__mm-component-skeleton";
18
+ return /* @__PURE__ */ c(l, { children: [
19
+ /* @__PURE__ */ t("style", { id: "__mm-skeleton", children: e.shimmerStyle }),
20
+ /* @__PURE__ */ t("style", { id: `__mm-${n}-skeleton`, children: e.componentStyle }),
21
+ /* @__PURE__ */ t(
22
+ "div",
23
+ {
24
+ className: e.containerClass,
25
+ style: { minHeight: e.minHeight },
26
+ children: /* @__PURE__ */ t(
27
+ "div",
28
+ {
29
+ className: "__mm-skel",
30
+ style: { width: e.barWidth, height: e.barHeight }
31
+ }
32
+ )
33
+ }
34
+ )
35
+ ] });
36
+ }
37
+ const w = ({
38
+ manifest: e,
39
+ componentName: n,
40
+ componentProps: o,
41
+ autoLoad: p = !0,
42
+ children: i,
43
+ errorFallback: m
44
+ }) => {
45
+ const {
46
+ component: r,
47
+ error: d,
48
+ loaded: u
49
+ } = _({
50
+ manifest: e,
51
+ componentName: n,
52
+ autoLoad: p
53
+ }), [s, y] = k(null);
54
+ if (C(() => {
55
+ const h = document.createElement("div");
56
+ h.style.display = "contents", y(h);
57
+ }, []), d) return m ? m(d) : null;
58
+ const a = e.skeletonSpec ? x(e.skeletonSpec) : null;
59
+ if (!s)
60
+ return !u || !r ? /* @__PURE__ */ t(l, { children: a ?? i ?? null }) : /* @__PURE__ */ t(r, { ...o, children: i });
61
+ const f = /* @__PURE__ */ t(R, { node: s }), S = N(i, s);
62
+ return !u || !r ? /* @__PURE__ */ c(l, { children: [
63
+ a,
64
+ f
65
+ ] }) : /* @__PURE__ */ c(l, { children: [
66
+ S,
67
+ /* @__PURE__ */ t(r, { ...o, children: f })
68
+ ] });
69
+ };
70
+ export {
71
+ w as RemoteRenderer
72
+ };
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { useState as f, useCallback as l, useEffect as p } from "react";
3
+ import { moduleLoader as g } from "../services/moduleLoader.js";
4
+ function b(m) {
5
+ const {
6
+ manifest: t,
7
+ componentName: s,
8
+ autoLoad: c = !0
9
+ } = m, [d, r] = f({
10
+ component: null,
11
+ manifest: null,
12
+ loading: !1,
13
+ error: null,
14
+ loaded: !1
15
+ }), e = l(async () => {
16
+ try {
17
+ r((n) => ({
18
+ ...n,
19
+ loading: !0,
20
+ error: null,
21
+ manifest: t
22
+ }));
23
+ const o = {
24
+ bundleUrl: t.bundleUrl,
25
+ componentName: s
26
+ }, a = await g.loadComponent(o);
27
+ r((n) => ({
28
+ ...n,
29
+ component: a,
30
+ loading: !1,
31
+ loaded: !0
32
+ }));
33
+ } catch (o) {
34
+ console.error("Error loading remote component:", o);
35
+ const a = o instanceof Error ? o.message : "Unknown error loading component";
36
+ r((n) => ({
37
+ ...n,
38
+ error: a,
39
+ loading: !1,
40
+ loaded: !1
41
+ }));
42
+ }
43
+ }, [t, s]), i = l(async () => {
44
+ await e();
45
+ }, [e]), u = l(() => {
46
+ r((o) => ({ ...o, error: null }));
47
+ }, []);
48
+ return p(() => {
49
+ c && e();
50
+ }, [c, e]), {
51
+ ...d,
52
+ loadComponent: e,
53
+ reload: i,
54
+ clearError: u
55
+ };
56
+ }
57
+ export {
58
+ b as useRemoteComponent
59
+ };
@@ -0,0 +1,25 @@
1
+ "use server";
2
+ import { fetchManifest as t } from "../../../../services/fetchManifest.js";
3
+ import "../../../../services/moduleLoader.js";
4
+ import { isEnvironment as i } from "../../../../types/environment.js";
5
+ import { mapManifestUrl as o } from "../../../mappers/mapManifestUrl.js";
6
+ async function f(r, n) {
7
+ try {
8
+ let e = "production";
9
+ i(r) ? e = r : console.warn(
10
+ `Invalid environment "${r}" specified in environment variables. Defaulting to "production".`
11
+ );
12
+ try {
13
+ return await t(o(e, n));
14
+ } catch {
15
+ return await t(o("production", void 0));
16
+ }
17
+ } catch (e) {
18
+ return console.warn(
19
+ `Failed to fetch remote manifest from all sources. Returning empty manifest. Error: ${e instanceof Error ? e.message : e}`
20
+ ), { components: {}, version: "", generatedAt: "" };
21
+ }
22
+ }
23
+ export {
24
+ f as getRemoteManifest
25
+ };
@@ -0,0 +1,27 @@
1
+ const a = "manifest.json", n = "https://assets.mintmobile.com", c = "https://assets-qa.mintmobile.com", m = (e, t) => {
2
+ const s = r(e), o = p(e, t);
3
+ return `${s}/remote-components/manifest-poc/${o}/${a}?_ts=${Date.now()}`;
4
+ }, r = (e) => {
5
+ switch (e) {
6
+ case "production":
7
+ return n;
8
+ case "develop":
9
+ case "development":
10
+ case "staging":
11
+ case "qa1":
12
+ return c;
13
+ }
14
+ }, p = (e, t) => {
15
+ switch (e) {
16
+ case "production":
17
+ return t || "f-poc-live-update";
18
+ case "develop":
19
+ case "development":
20
+ case "staging":
21
+ case "qa1":
22
+ return t || "f-poc-live-update";
23
+ }
24
+ };
25
+ export {
26
+ m as mapManifestUrl
27
+ };
@@ -0,0 +1,9 @@
1
+ const r = (t) => {
2
+ let e = 5381;
3
+ for (let o = 0; o < t.length; o++)
4
+ e = ((e << 5) + e ^ t.charCodeAt(o)) >>> 0;
5
+ return e.toString(36);
6
+ }, h = (t) => `__rm_${r(t)}`;
7
+ export {
8
+ h as getModulePreloadKey
9
+ };
@@ -0,0 +1,17 @@
1
+ import { cookies as n } from "next/headers.js";
2
+ import { getRemoteManifest as a } from "../api/server/getRemoteManifest/getRemoteManifest.js";
3
+ const s = async ({
4
+ NEXT_PUBLIC_ENVIRONMENT: t
5
+ }) => {
6
+ var o;
7
+ const e = (o = (await n()).get("cont-comp")) == null ? void 0 : o.value;
8
+ return {
9
+ remoteManifest: await a(
10
+ t,
11
+ e
12
+ )
13
+ };
14
+ };
15
+ export {
16
+ s as getRemoteComponentsData
17
+ };
@@ -0,0 +1,20 @@
1
+ import { getRemoteManifest as p } from "../api/server/getRemoteManifest/getRemoteManifest.js";
2
+ function u(n, t) {
3
+ return async (e) => {
4
+ const { NEXT_PUBLIC_ENVIRONMENT: r } = n, { req: s } = e, c = s.cookies["cont-comp"], i = {
5
+ remoteManifest: await p(
6
+ r,
7
+ c
8
+ )
9
+ }, o = t ? await t(e) : { props: {} };
10
+ return "props" in o ? {
11
+ props: {
12
+ ...i,
13
+ ...o.props
14
+ }
15
+ } : o;
16
+ };
17
+ }
18
+ export {
19
+ u as withRemoteComponentsGSSP
20
+ };