@netsapiens/horizon-sdk 0.1.5 → 0.1.7
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 +292 -294
- package/dist/index.cjs +31 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -111
- package/dist/index.d.ts +43 -111
- package/dist/index.js +31 -57
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,11 @@ if (typeof window !== "undefined") {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// src/appId.ts
|
|
42
|
+
function deriveAppId(webpackModule) {
|
|
43
|
+
return webpackModule.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
// src/RemoteAppSDK.ts
|
|
42
47
|
var log = createLogger("RemoteAppSDK");
|
|
43
48
|
var RemoteAppSDK = class {
|
|
@@ -51,10 +56,15 @@ var RemoteAppSDK = class {
|
|
|
51
56
|
dynamicColumns = /* @__PURE__ */ new Set();
|
|
52
57
|
callEventsSubscribed = false;
|
|
53
58
|
sidePanelOpen = false;
|
|
54
|
-
|
|
59
|
+
/**
|
|
60
|
+
* @param webpackModule Your Module Federation container name (the `name` in
|
|
61
|
+
* ModuleFederationPlugin, camelCase). The app's `appId` is derived from it
|
|
62
|
+
* via {@link deriveAppId} so it matches the host/API registry `id`.
|
|
63
|
+
*/
|
|
64
|
+
constructor(eventBus, webpackModule) {
|
|
55
65
|
this.eventBus = eventBus;
|
|
56
|
-
this.appId =
|
|
57
|
-
log.debug(`Initialized for app: ${appId}`);
|
|
66
|
+
this.appId = deriveAppId(webpackModule);
|
|
67
|
+
log.debug(`Initialized for app: ${this.appId}`);
|
|
58
68
|
}
|
|
59
69
|
// -------------------------------------------------------------------------
|
|
60
70
|
// Routes
|
|
@@ -213,8 +223,8 @@ var RemoteAppSDK = class {
|
|
|
213
223
|
return Array.from(this.dynamicColumns);
|
|
214
224
|
}
|
|
215
225
|
};
|
|
216
|
-
function createRemoteAppSDK(eventBus,
|
|
217
|
-
return new RemoteAppSDK(eventBus,
|
|
226
|
+
function createRemoteAppSDK(eventBus, webpackModule) {
|
|
227
|
+
return new RemoteAppSDK(eventBus, webpackModule);
|
|
218
228
|
}
|
|
219
229
|
var HorizonContextReact = react.createContext(null);
|
|
220
230
|
function HorizonContextProvider({
|
|
@@ -303,18 +313,18 @@ function useSidePanel(eventBus, appId) {
|
|
|
303
313
|
[bus, appId]
|
|
304
314
|
);
|
|
305
315
|
}
|
|
306
|
-
function useRemoteApp(horizonContext,
|
|
316
|
+
function useRemoteApp(horizonContext, webpackModule) {
|
|
307
317
|
const sdkRef = react.useRef(null);
|
|
308
318
|
if (!sdkRef.current) {
|
|
309
|
-
sdkRef.current = createRemoteAppSDK(horizonContext.eventBus,
|
|
319
|
+
sdkRef.current = createRemoteAppSDK(horizonContext.eventBus, webpackModule);
|
|
310
320
|
}
|
|
311
321
|
const sdk = sdkRef.current;
|
|
312
322
|
return { sdk, ...horizonContext };
|
|
313
323
|
}
|
|
314
|
-
function useRoute(eventBus,
|
|
324
|
+
function useRoute(eventBus, webpackModule, config) {
|
|
315
325
|
const sdk = react.useMemo(
|
|
316
|
-
() => createRemoteAppSDK(eventBus,
|
|
317
|
-
[eventBus,
|
|
326
|
+
() => createRemoteAppSDK(eventBus, webpackModule),
|
|
327
|
+
[eventBus, webpackModule]
|
|
318
328
|
);
|
|
319
329
|
react.useEffect(() => {
|
|
320
330
|
void sdk.registerRoute(config);
|
|
@@ -322,10 +332,10 @@ function useRoute(eventBus, appId, config) {
|
|
|
322
332
|
}, [sdk, config]);
|
|
323
333
|
return sdk;
|
|
324
334
|
}
|
|
325
|
-
function useRouteFromModule(eventBus,
|
|
335
|
+
function useRouteFromModule(eventBus, webpackModule, routeConfig, moduleConfig) {
|
|
326
336
|
const sdk = react.useMemo(
|
|
327
|
-
() => createRemoteAppSDK(eventBus,
|
|
328
|
-
[eventBus,
|
|
337
|
+
() => createRemoteAppSDK(eventBus, webpackModule),
|
|
338
|
+
[eventBus, webpackModule]
|
|
329
339
|
);
|
|
330
340
|
const [loading, setLoading] = react.useState(true);
|
|
331
341
|
const [error, setError] = react.useState(null);
|
|
@@ -346,10 +356,10 @@ function useRouteFromModule(eventBus, appId, routeConfig, moduleConfig) {
|
|
|
346
356
|
}, [sdk, moduleConfig, routeConfig]);
|
|
347
357
|
return { loading, error, sdk };
|
|
348
358
|
}
|
|
349
|
-
function useDynamicExtension(eventBus,
|
|
359
|
+
function useDynamicExtension(eventBus, webpackModule, config) {
|
|
350
360
|
const sdk = react.useMemo(
|
|
351
|
-
() => createRemoteAppSDK(eventBus,
|
|
352
|
-
[eventBus,
|
|
361
|
+
() => createRemoteAppSDK(eventBus, webpackModule),
|
|
362
|
+
[eventBus, webpackModule]
|
|
353
363
|
);
|
|
354
364
|
react.useEffect(() => {
|
|
355
365
|
sdk.registerDynamicExtension(config);
|
|
@@ -360,10 +370,10 @@ function useDynamicExtension(eventBus, appId, config) {
|
|
|
360
370
|
function usePageContext(context) {
|
|
361
371
|
return context.pageContext;
|
|
362
372
|
}
|
|
363
|
-
function useDynamicColumn(eventBus,
|
|
373
|
+
function useDynamicColumn(eventBus, webpackModule, config) {
|
|
364
374
|
const sdk = react.useMemo(
|
|
365
|
-
() => createRemoteAppSDK(eventBus,
|
|
366
|
-
[eventBus,
|
|
375
|
+
() => createRemoteAppSDK(eventBus, webpackModule),
|
|
376
|
+
[eventBus, webpackModule]
|
|
367
377
|
);
|
|
368
378
|
react.useEffect(() => {
|
|
369
379
|
sdk.registerDynamicColumn(config);
|
|
@@ -512,64 +522,24 @@ function moduleLoadError(appId, url, cause) {
|
|
|
512
522
|
});
|
|
513
523
|
}
|
|
514
524
|
|
|
515
|
-
// src/anchors.ts
|
|
516
|
-
var MANAGE_ANCHORS = {
|
|
517
|
-
dashboard: "manage-dashboard",
|
|
518
|
-
users: "manage-users",
|
|
519
|
-
contacts: "manage-contacts",
|
|
520
|
-
devices: "manage-devices",
|
|
521
|
-
phoneNumbers: "manage-phone-numbers",
|
|
522
|
-
callLogs: "manage-call-logs",
|
|
523
|
-
voicemail: "manage-voicemail",
|
|
524
|
-
fax: "manage-fax",
|
|
525
|
-
settings: "manage-settings"
|
|
526
|
-
};
|
|
527
|
-
var PLATFORM_ANCHORS = {
|
|
528
|
-
dashboard: "platform-dashboard",
|
|
529
|
-
codeManagement: "platform-code-management",
|
|
530
|
-
configManagement: "platform-config-management",
|
|
531
|
-
sdkManagement: "platform-ui-sdk",
|
|
532
|
-
branding: "platform-branding",
|
|
533
|
-
recording: "platform-recording",
|
|
534
|
-
logsAndDiagnostics: "platform-logs-and-diagnostics"
|
|
535
|
-
};
|
|
536
|
-
var APPS_ANCHORS = {
|
|
537
|
-
home: "apps-home"
|
|
538
|
-
};
|
|
539
|
-
var MY_ACCOUNT_ANCHORS = {
|
|
540
|
-
profile: "my-account-profile",
|
|
541
|
-
preferences: "my-account-preferences",
|
|
542
|
-
security: "my-account-security"
|
|
543
|
-
};
|
|
544
|
-
var ANCHORS = {
|
|
545
|
-
manage: MANAGE_ANCHORS,
|
|
546
|
-
platform: PLATFORM_ANCHORS,
|
|
547
|
-
apps: APPS_ANCHORS,
|
|
548
|
-
myAccount: MY_ACCOUNT_ANCHORS
|
|
549
|
-
};
|
|
550
|
-
|
|
551
525
|
// package.json
|
|
552
526
|
var package_default = {
|
|
553
|
-
version: "0.1.
|
|
527
|
+
version: "0.1.7"};
|
|
554
528
|
|
|
555
529
|
// src/index.ts
|
|
556
530
|
var VERSION = package_default.version;
|
|
557
531
|
var log2 = createLogger("FederationSDK");
|
|
558
532
|
log2.info(`SDK v${VERSION} loaded`);
|
|
559
533
|
|
|
560
|
-
exports.ANCHORS = ANCHORS;
|
|
561
|
-
exports.APPS_ANCHORS = APPS_ANCHORS;
|
|
562
534
|
exports.HorizonContextProvider = HorizonContextProvider;
|
|
563
535
|
exports.HorizonSDKError = HorizonSDKError;
|
|
564
|
-
exports.MANAGE_ANCHORS = MANAGE_ANCHORS;
|
|
565
|
-
exports.MY_ACCOUNT_ANCHORS = MY_ACCOUNT_ANCHORS;
|
|
566
|
-
exports.PLATFORM_ANCHORS = PLATFORM_ANCHORS;
|
|
567
536
|
exports.RemoteAppSDK = RemoteAppSDK;
|
|
568
537
|
exports.VERSION = VERSION;
|
|
569
538
|
exports.apiError = apiError;
|
|
570
539
|
exports.createHorizonSDKError = createHorizonSDKError;
|
|
571
540
|
exports.createLogger = createLogger;
|
|
572
541
|
exports.createRemoteAppSDK = createRemoteAppSDK;
|
|
542
|
+
exports.deriveAppId = deriveAppId;
|
|
573
543
|
exports.getLogLevel = getLogLevel;
|
|
574
544
|
exports.invalidExtensionPointError = invalidExtensionPointError;
|
|
575
545
|
exports.moduleLoadError = moduleLoadError;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/logger.ts","../src/RemoteAppSDK.ts","../src/hooks.tsx","../src/errors/HorizonSDKError.ts","../src/anchors.ts","../package.json","../src/index.ts"],"names":["loglevel","createContext","useState","useEffect","useMemo","createElement","useContext","useRef","log"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Logging utility using loglevel for Horizon SDK\n * Consistent with netsapiens-monorepo logging pattern\n *\n * Usage:\n * import { createLogger } from '../utils/logger';\n * const log = createLogger('ModuleLoader');\n * log.info('Loading module...');\n * log.error('Failed to load:', error);\n */\nimport loglevel from \"loglevel\";\n\n// Configure default log level based on environment\nconst isDevelopment = process.env.NODE_ENV === \"development\";\n\n// Set default level\nloglevel.setDefaultLevel(\n isDevelopment ? loglevel.levels.DEBUG : loglevel.levels.WARN,\n);\n\n/**\n * Create a namespaced logger for a specific module\n * Follows the monorepo pattern from @netsapiens/ns-sdk\n *\n * @example\n * const log = createLogger('ModuleLoader');\n * log.debug('Starting load...');\n * log.info('Module loaded successfully');\n * log.warn('Slow load detected');\n * log.error('Load failed:', error);\n */\nexport const createLogger = (namespace: string) => {\n const moduleLogger = loglevel.getLogger(`HorizonSDK:${namespace}`);\n\n if (isDevelopment) {\n moduleLogger.setLevel(loglevel.levels.DEBUG);\n } else {\n moduleLogger.setLevel(loglevel.levels.WARN);\n }\n\n return moduleLogger;\n};\n\n/**\n * Default logger instance for the SDK\n */\nexport const logger = loglevel.getLogger(\"horizon-sdk\");\n\n/**\n * Set log level at runtime\n * Useful for debugging\n *\n * @example\n * import { setLogLevel } from './utils/logger';\n * setLogLevel('DEBUG'); // Enable all logs\n * setLogLevel('WARN'); // Only warnings and errors\n */\nexport const setLogLevel = (\n level: \"TRACE\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\" | \"SILENT\",\n) => {\n loglevel.setLevel(level);\n};\n\n/**\n * Get current log level\n */\nexport const getLogLevel = () => {\n return loglevel.getLevel();\n};\n\n// Extend Window interface for debug tools\ndeclare global {\n interface Window {\n __horizonSDKLogger__: typeof logger;\n __setHorizonSDKLogLevel__: typeof setLogLevel;\n }\n}\n\n// Make logger available in browser console for debugging\nif (typeof window !== \"undefined\") {\n window.__horizonSDKLogger__ = logger;\n window.__setHorizonSDKLogLevel__ = setLogLevel;\n\n if (isDevelopment) {\n logger.info(\n 'Horizon SDK logger initialized. Use window.__setHorizonSDKLogLevel__(\"DEBUG\") to adjust log level.',\n );\n }\n}\n","/**\n * Remote App SDK.\n *\n * Wraps Horizon's event bus with a typed, app-scoped API for registering routes\n * and dynamic extensions. Tracks every registration so `cleanup()` can tear\n * everything down on unmount — essential when a remote app is reloaded or its\n * webpack module is replaced during HMR.\n */\nimport type {\n CallEvent,\n CallEventType,\n DynamicColumnConfig,\n DynamicExtensionConfig,\n HorizonEventBus,\n RemoteModuleConfig,\n RouteConfig,\n SidePanelConfig,\n} from \"./types\";\nimport { createLogger } from \"./utils/logger\";\n\nconst log = createLogger(\"RemoteAppSDK\");\n\nexport class RemoteAppSDK {\n private eventBus: HorizonEventBus;\n private appId: string;\n\n // Registrations are tracked separately so cleanup() emits the right\n // unregister event for each. (A single set keyed by id-only would let dynamic\n // registrations leak — they're unregistered on a different channel.)\n private routes = new Set<string>();\n private dynamicExtensions = new Set<string>();\n private dynamicColumns = new Set<string>();\n private callEventsSubscribed = false;\n private sidePanelOpen = false;\n\n constructor(eventBus: HorizonEventBus, appId: string) {\n this.eventBus = eventBus;\n this.appId = appId;\n log.debug(`Initialized for app: ${appId}`);\n }\n\n // -------------------------------------------------------------------------\n // Routes\n // -------------------------------------------------------------------------\n\n async registerRoute(config: Omit<RouteConfig, \"appId\">): Promise<void> {\n const route: RouteConfig = { ...config, appId: this.appId };\n this.eventBus.emit(\"route:register\", route);\n this.routes.add(route.id);\n log.info(\n `[${this.appId}] Route registered: ${route.id} at ${route.parentPath}/${route.path}`,\n );\n }\n\n unregisterRoute(routeId: string): void {\n this.eventBus.emit(\"route:unregister\", { id: routeId });\n this.routes.delete(routeId);\n log.info(`[${this.appId}] Route unregistered: ${routeId}`);\n }\n\n /**\n * Convenience: load a component out of a federated module's webpack\n * container and register it as a route in one step. Useful when the route\n * component lives in a sibling exposed module rather than the entry App.\n */\n async registerRouteFromModule(\n routeConfig: Omit<RouteConfig, \"appId\" | \"component\">,\n moduleConfig: RemoteModuleConfig,\n ): Promise<void> {\n const container = (\n window as unknown as Record<string, ModuleFederationContainer>\n )[moduleConfig.scope];\n if (!container) {\n throw new Error(`Remote container not found: ${moduleConfig.scope}`);\n }\n\n const factory = await container.get(moduleConfig.module);\n const moduleExports = factory();\n const component = (moduleExports.default ??\n moduleExports) as React.ComponentType<unknown>;\n\n await this.registerRoute({ ...routeConfig, component });\n }\n\n // -------------------------------------------------------------------------\n // Dynamic extensions (pattern-based UI injection)\n // -------------------------------------------------------------------------\n\n registerDynamicExtension(\n config: Omit<DynamicExtensionConfig, \"appId\">,\n ): void {\n const extension = { ...config, appId: this.appId };\n this.eventBus.emit(\"dynamic-extension:register\", extension);\n this.dynamicExtensions.add(extension.id);\n log.info(\n `[${this.appId}] Dynamic extension registered: ${extension.id} → zone ${extension.zone}`,\n );\n }\n\n unregisterDynamicExtension(extensionId: string): void {\n this.eventBus.emit(\"dynamic-extension:unregister\", { id: extensionId });\n this.dynamicExtensions.delete(extensionId);\n log.info(`[${this.appId}] Dynamic extension unregistered: ${extensionId}`);\n }\n\n // -------------------------------------------------------------------------\n // Dynamic columns (table column injection)\n // -------------------------------------------------------------------------\n\n registerDynamicColumn(config: Omit<DynamicColumnConfig, \"appId\">): void {\n const column = { ...config, appId: this.appId };\n this.eventBus.emit(\"dynamic-column:register\", column);\n this.dynamicColumns.add(column.id);\n log.info(\n `[${this.appId}] Dynamic column registered: ${column.id} → zone ${column.zone}`,\n );\n }\n\n unregisterDynamicColumn(columnId: string): void {\n this.eventBus.emit(\"dynamic-column:unregister\", { id: columnId });\n this.dynamicColumns.delete(columnId);\n log.info(`[${this.appId}] Dynamic column unregistered: ${columnId}`);\n }\n\n // -------------------------------------------------------------------------\n // Call events (capability-gated, app-scoped)\n // -------------------------------------------------------------------------\n\n /**\n * Subscribe to live SIP call events. Routes through the host's\n * `CallEventsManager`, which enforces the `call-events:listen` capability and\n * records the subscription against this app — so the platform knows which\n * apps consume call events and can disable the capability platform-wide.\n *\n * Prefer this over listening to `eventBus.on('call-event')` directly: the raw\n * bus is neither gated nor attributed to your app.\n *\n * @returns an unsubscribe function (also torn down by `cleanup()`).\n */\n subscribeToCallEvents(\n eventTypes: CallEventType[],\n callback: (event: CallEvent) => void,\n ): () => void {\n this.eventBus.emit(\"call-events:subscribe\", {\n appId: this.appId,\n eventTypes,\n callback,\n });\n this.callEventsSubscribed = true;\n log.info(\n `[${this.appId}] Subscribed to call events: ${eventTypes.join(\", \")}`,\n );\n return () => this.unsubscribeFromCallEvents();\n }\n\n unsubscribeFromCallEvents(): void {\n if (!this.callEventsSubscribed) return;\n this.eventBus.emit(\"call-events:unsubscribe\", { appId: this.appId });\n this.callEventsSubscribed = false;\n log.info(`[${this.appId}] Unsubscribed from call events`);\n }\n\n // -------------------------------------------------------------------------\n // Side panel (imperative, app-driven)\n // -------------------------------------------------------------------------\n\n /**\n * Open Horizon's shared side panel with app-provided content. The host renders\n * `config.component` inside the drawer, passing it `{ context, close }`.\n *\n * Can be called from anywhere with an SDK instance; remote components without\n * one (extensions, pages) can use the `useSidePanel()` hook instead. Calling\n * again replaces the current content. Closed automatically by `cleanup()`.\n */\n openSidePanel(config: SidePanelConfig): void {\n this.eventBus.emit(\"side-panel:open\", { ...config, appId: this.appId });\n this.sidePanelOpen = true;\n log.info(\n `[${this.appId}] Side panel opened: ${config.title ?? \"(untitled)\"}`,\n );\n }\n\n /** Close the side panel if this app opened it. */\n closeSidePanel(): void {\n if (!this.sidePanelOpen) return;\n this.eventBus.emit(\"side-panel:close\", { appId: this.appId });\n this.sidePanelOpen = false;\n log.info(`[${this.appId}] Side panel closed`);\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n /**\n * Unregister everything this SDK instance has registered. Call from your\n * remote app's unmount/cleanup path — `useRemoteApp` does this for you.\n */\n cleanup(): void {\n log.info(\n `[${this.appId}] Cleanup: ${this.routes.size} routes, ${this.dynamicExtensions.size} extensions, ${this.dynamicColumns.size} columns`,\n );\n this.routes.forEach((id) => this.eventBus.emit(\"route:unregister\", { id }));\n this.dynamicExtensions.forEach((id) =>\n this.eventBus.emit(\"dynamic-extension:unregister\", { id }),\n );\n this.dynamicColumns.forEach((id) =>\n this.eventBus.emit(\"dynamic-column:unregister\", { id }),\n );\n this.unsubscribeFromCallEvents();\n this.closeSidePanel();\n this.routes.clear();\n this.dynamicExtensions.clear();\n this.dynamicColumns.clear();\n }\n\n getAppId(): string {\n return this.appId;\n }\n\n getRegisteredRoutes(): string[] {\n return Array.from(this.routes);\n }\n\n getRegisteredDynamicExtensions(): string[] {\n return Array.from(this.dynamicExtensions);\n }\n\n getRegisteredDynamicColumns(): string[] {\n return Array.from(this.dynamicColumns);\n }\n}\n\n/** Factory: equivalent to `new RemoteAppSDK(...)`. */\nexport function createRemoteAppSDK(\n eventBus: HorizonEventBus,\n appId: string,\n): RemoteAppSDK {\n return new RemoteAppSDK(eventBus, appId);\n}\n\ninterface ModuleFederationContainer {\n get: (\n module: string,\n ) => Promise<() => { default?: unknown; [key: string]: unknown }>;\n}\n","/**\n * React hooks for remote apps.\n *\n * Each hook handles its own cleanup so remote apps don't have to track\n * unregistration manually.\n */\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createElement } from \"react\";\nimport type {\n DynamicColumnConfig,\n DynamicExtensionConfig,\n ExtensionContext,\n HorizonContext,\n RemoteModuleConfig,\n RouteConfig,\n SidePanelConfig,\n} from \"./types\";\nimport { RemoteAppSDK, createRemoteAppSDK } from \"./RemoteAppSDK\";\n\n// ---------------------------------------------------------------------------\n// HorizonContextProvider + useHorizonContext\n// ---------------------------------------------------------------------------\n\n/**\n * Internal React context that carries the live HorizonContext.\n * Updated automatically when dark mode changes — remote pages never need to\n * subscribe to theme events manually.\n */\nconst HorizonContextReact = createContext<HorizonContext | null>(null);\n\n/**\n * Wrap your registered page components with this provider so they receive a\n * reactive HorizonContext without any extra event-listener boilerplate.\n *\n * The component memoization pattern in App.tsx captures `horizonContext` once\n * (to keep stable component identity), but `eventBus` is a singleton — the\n * Provider subscribes to it and pushes theme updates to all descendant pages.\n *\n * @example\n * // In App.tsx, inside useMemo with empty deps:\n * return (\n * <HorizonContextProvider context={horizonContext}>\n * <MyPage />\n * </HorizonContextProvider>\n * );\n */\nexport function HorizonContextProvider({\n context,\n children,\n}: {\n context: HorizonContext;\n children: ReactNode;\n}) {\n const [theme, setTheme] = useState<\"light\" | \"dark\">(\n context.theme ?? \"light\",\n );\n const [locale, setLocale] = useState<string>(context.locale ?? \"en-US\");\n\n useEffect(() => {\n if (!context.eventBus) return;\n\n const themeHandler = (data: unknown) => {\n const payload = data as { theme: \"light\" | \"dark\" };\n if (payload?.theme === \"light\" || payload?.theme === \"dark\")\n setTheme(payload.theme);\n };\n const localeHandler = (data: unknown) => {\n const payload = data as { locale: string };\n if (payload?.locale) setLocale(payload.locale);\n };\n\n context.eventBus.on(\"theme:changed\", themeHandler);\n context.eventBus.on(\"locale:changed\", localeHandler);\n return () => {\n context.eventBus.off(\"theme:changed\", themeHandler);\n context.eventBus.off(\"locale:changed\", localeHandler);\n };\n }, [context.eventBus]);\n\n const liveContext = useMemo<HorizonContext>(\n () => ({ ...context, theme, locale }),\n // Intentionally omit `context` — eventBus is stable and subscriptions keep\n // theme/locale live. context spread gives access to t, user, api, navigate, etc.\n [theme, locale],\n );\n\n return createElement(\n HorizonContextReact.Provider,\n { value: liveContext },\n children,\n );\n}\n\n/**\n * Read the live HorizonContext inside any page registered via the SDK.\n * Returns a context that updates automatically when dark mode changes.\n *\n * Must be called inside a component rendered within `HorizonContextProvider`.\n *\n * @example\n * export default function MyPage() {\n * const { ui, theme, user, navigate } = useHorizonContext();\n * const { PageTemplate, DatagridTemplate } = ui.templates;\n * // `theme` is always 'light' | 'dark', reactive to system/user toggle\n * }\n */\nexport function useHorizonContext(): HorizonContext {\n const ctx = useContext(HorizonContextReact);\n if (!ctx) {\n throw new Error(\n \"[Horizon SDK] useHorizonContext() must be called inside a component wrapped by HorizonContextProvider.\",\n );\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// useTheme — reactive theme for pages and standalone extensions\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current color scheme, reactive to host theme changes.\n *\n * Works in **both** contexts with the same import — no manual event wiring:\n *\n * - **Page components** (inside `HorizonContextProvider`): reads directly from\n * the provider's React context. No subscription overhead.\n * - **Standalone extension components** (rendered by the host via\n * `registerDynamicExtension`): falls back to `eventBus` subscription.\n * Pass `context.eventBus` as the argument.\n *\n * @example\n * // In a page component (HorizonContextProvider ancestor required)\n * export default function MyPage() {\n * const { theme } = useTheme();\n * return <div style={{ color: theme === 'dark' ? '#fff' : '#000' }}>...</div>;\n * }\n *\n * @example\n * // In a standalone extension component\n * export default function MyButton({ context }: ExtensionComponentProps) {\n * const { theme } = useTheme(context.eventBus);\n * const { Button } = context.ui ?? {};\n * return <Button>{theme === 'dark' ? '🌙' : '☀️'} Export</Button>;\n * }\n */\n/**\n * Returns the current color scheme, reactive to host theme changes.\n *\n * @param eventBus - Pass `context.eventBus` when called from a standalone\n * extension component (outside a HorizonContextProvider).\n * @param initialTheme - Pass `context.theme` when called from a standalone\n * extension component so the hook initialises with the correct value on\n * first render rather than defaulting to `'light'`.\n *\n * Inside a page component wrapped by `HorizonContextProvider`, both params\n * can be omitted — the provider context is used automatically.\n */\nexport function useTheme(\n eventBus?: HorizonContext[\"eventBus\"],\n initialTheme?: \"light\" | \"dark\",\n): { theme: \"light\" | \"dark\" } {\n const providerTheme = useContext(HorizonContextReact)?.theme;\n\n // Prefer provider > caller-supplied initial > safe default.\n // Passing initialTheme = context.theme from an extension component ensures\n // the correct value is used on the very first render.\n const [localTheme, setLocalTheme] = useState<\"light\" | \"dark\">(\n providerTheme ?? initialTheme ?? \"light\",\n );\n\n useEffect(() => {\n // Inside a HorizonContextProvider the provider re-renders with the new\n // theme value — no subscription needed here.\n if (providerTheme !== undefined) return;\n if (!eventBus) return;\n\n const handler = (data: unknown) => {\n const payload = data as { theme: \"light\" | \"dark\" };\n if (payload?.theme === \"light\" || payload?.theme === \"dark\") {\n setLocalTheme(payload.theme);\n }\n };\n eventBus.on(\"theme:changed\", handler);\n return () => {\n eventBus.off(\"theme:changed\", handler);\n };\n }, [providerTheme, eventBus]);\n\n return { theme: providerTheme ?? localTheme };\n}\n\n// ---------------------------------------------------------------------------\n// useLocale — access host translations and current locale\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the host's translation function and current locale.\n *\n * Because `i18next` is shared as a singleton, this hook reads directly from\n * the host's already-initialized i18next instance — all host translations\n * (common, telecom, admin, etc.) are immediately available with no extra\n * fetch or setup required.\n *\n * Reactivity is handled internally by react-i18next: the returned `t` and\n * `locale` update automatically when the user switches language.\n *\n * @param ns - Optional namespace(s). Defaults to `'common'` (the host\n * namespace that contains all standard UI strings). Pass your app's own\n * registered namespace to access remote-app-specific keys.\n *\n * @example\n * // In a page component — access any host string\n * export default function MyPage() {\n * const { t, locale } = useLocale();\n * return <Button>{t('SAVE')}</Button>;\n * }\n *\n * @example\n * // In an extension component — same API\n * export function MyButton({ context }: ExtensionComponentProps) {\n * const { t } = useLocale();\n * const { Button } = context.ui ?? {};\n * return <Button>{t('EXPORT')}</Button>;\n * }\n */\n/**\n * Returns the host's `t` translation function and current locale string.\n *\n * The `t` function is the host's i18next instance — all 1,375+ host strings\n * across the common, telecom, admin, and validation namespaces are immediately\n * available. No i18next dependency, no package install, no init required in\n * the remote app.\n *\n * Both `t` and `locale` update automatically when the user switches language.\n *\n * @example\n * export default function MyPage() {\n * const { t, locale } = useLocale();\n * return <Button>{t('SAVE')}</Button>;\n * }\n *\n * @example\n * // In a standalone extension component\n * export function MyButton({ context }: ExtensionComponentProps) {\n * const { t } = useLocale();\n * return <span>{t('EXPORT')}</span>;\n * }\n */\nexport function useLocale(): { t: HorizonContext[\"t\"]; locale: string } {\n const ctx = useContext(HorizonContextReact);\n return {\n t: ctx?.t,\n locale: ctx?.locale ?? \"en-US\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// useSidePanel — open the shared side panel from anywhere\n// ---------------------------------------------------------------------------\n\n/**\n * Open / close Horizon's shared side panel with app-provided content, from any\n * remote component — a page, an extension (row action, header button), or a\n * call-event handler.\n *\n * Works in **both** contexts like {@link useTheme}:\n * - **Page components** (inside `HorizonContextProvider`): call `useSidePanel()`\n * with no argument — the eventBus is read from the provider.\n * - **Standalone extension components** (rendered via `registerDynamicExtension`):\n * pass `context.eventBus`, e.g. `useSidePanel(context.eventBus)`.\n *\n * @example\n * // In an extension component (e.g. a table row action)\n * export function RowAction({ context }: ExtensionComponentProps) {\n * const { open } = useSidePanel(context.eventBus);\n * const { IconButton } = context.ui ?? {};\n * return (\n * <IconButton icon=\"mdi:information\" onClick={() =>\n * open({ title: 'Details', component: DetailsPanel })\n * } />\n * );\n * }\n *\n * // The content component\n * function DetailsPanel({ context, close }: SidePanelContentProps) {\n * const { Stack, Typography, Button } = context.ui ?? {};\n * return <Stack><Typography>…</Typography><Button onClick={close}>Done</Button></Stack>;\n * }\n *\n * @param eventBus - Pass `context.eventBus` from a standalone extension component.\n * @param appId - Optional owner id. When provided, the host scopes\n * programmatic closes to the owner, so this app's `close()` only dismisses a\n * panel it opened — one app can never close another's content. Omit it and\n * `close()` is unscoped (closes whatever is open). The instance API\n * `sdk.openSidePanel()` always stamps the owner for you.\n */\nexport function useSidePanel(\n eventBus?: HorizonContext[\"eventBus\"],\n appId?: string,\n): {\n open: (config: SidePanelConfig) => void;\n close: () => void;\n} {\n const ctxBus = useContext(HorizonContextReact)?.eventBus;\n const bus = eventBus ?? ctxBus;\n\n return useMemo(\n () => ({\n open: (config: SidePanelConfig) =>\n bus?.emit(\"side-panel:open\", { ...config, appId }),\n close: () => bus?.emit(\"side-panel:close\", { appId }),\n }),\n [bus, appId],\n );\n}\n\n// ---------------------------------------------------------------------------\n// useRemoteApp\n// ---------------------------------------------------------------------------\n\n/**\n * Get an SDK instance bound to your app, plus a flat view of the Horizon\n * context. Returned `sdk.cleanup()` is called automatically on unmount.\n *\n * @example\n * export default function MyApp(horizonContext: HorizonContext) {\n * const { sdk, user } = useRemoteApp(horizonContext, 'my-app');\n *\n * useEffect(() => {\n * sdk.registerDynamicExtension({\n * id: 'my-app.button',\n * zone: 'page-header-actions',\n * routes: [{ pattern: '/manage/*\\/users' }],\n * component: MyButton,\n * });\n * }, [sdk]);\n *\n * return <div>Hello {user.displayName}</div>;\n * }\n */\nexport function useRemoteApp(horizonContext: HorizonContext, appId: string) {\n const sdkRef = useRef<RemoteAppSDK | null>(null);\n if (!sdkRef.current) {\n sdkRef.current = createRemoteAppSDK(horizonContext.eventBus, appId);\n }\n const sdk = sdkRef.current;\n\n return { sdk, ...horizonContext };\n}\n\n/**\n * Register a route for the lifetime of the calling component.\n */\nexport function useRoute(\n eventBus: HorizonContext[\"eventBus\"],\n appId: string,\n config: Omit<RouteConfig, \"appId\">,\n): RemoteAppSDK {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, appId),\n [eventBus, appId],\n );\n\n useEffect(() => {\n void sdk.registerRoute(config);\n return () => sdk.unregisterRoute(config.id);\n }, [sdk, config]);\n\n return sdk;\n}\n\n/**\n * Register a route by pulling its component out of a federated module's\n * webpack container.\n */\nexport function useRouteFromModule(\n eventBus: HorizonContext[\"eventBus\"],\n appId: string,\n routeConfig: Omit<RouteConfig, \"appId\" | \"component\">,\n moduleConfig: RemoteModuleConfig,\n) {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, appId),\n [eventBus, appId],\n );\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n let mounted = true;\n sdk\n .registerRouteFromModule(routeConfig, moduleConfig)\n .then(() => {\n if (mounted) setLoading(false);\n })\n .catch((err: Error) => {\n if (mounted) {\n setError(err);\n setLoading(false);\n }\n });\n\n return () => {\n mounted = false;\n sdk.unregisterRoute(routeConfig.id);\n };\n }, [sdk, moduleConfig, routeConfig]);\n\n return { loading, error, sdk };\n}\n\n/**\n * Register a dynamic extension (pattern-based UI injection) for the lifetime\n * of the calling component.\n */\nexport function useDynamicExtension(\n eventBus: HorizonContext[\"eventBus\"],\n appId: string,\n config: Omit<DynamicExtensionConfig, \"appId\">,\n): RemoteAppSDK {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, appId),\n [eventBus, appId],\n );\n\n useEffect(() => {\n sdk.registerDynamicExtension(config);\n return () => sdk.unregisterDynamicExtension(config.id);\n }, [sdk, config]);\n\n return sdk;\n}\n\n// ---------------------------------------------------------------------------\n// usePageContext — typed access to host-supplied page context\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current page context as a typed value.\n *\n * The host passes page-specific data (e.g. active filters, date range, selected\n * row) through `ExtensionContext.pageContext`. This hook casts it to the caller's\n * expected shape and keeps it reactive — when the host updates `pageContext` the\n * extension re-renders automatically because it flows in as a React prop.\n *\n * Define a typed interface for each page's context in your remote app and pass\n * it as the type parameter. Returns `undefined` when the host has not populated\n * any context for the current zone.\n *\n * @example\n * interface CallLogsPageContext {\n * dateRange?: [Date | null, Date | null];\n * }\n *\n * export function AnalyticsWidget({ context }: ExtensionComponentProps) {\n * const pageCtx = usePageContext<CallLogsPageContext>(context);\n * const [start, end] = pageCtx?.dateRange ?? [null, null];\n * // ...\n * }\n */\nexport function usePageContext<T = unknown>(\n context: ExtensionContext,\n): T | undefined {\n return context.pageContext as T | undefined;\n}\n\n/**\n * Register a dynamic table column for the lifetime of the calling component.\n */\nexport function useDynamicColumn(\n eventBus: HorizonContext[\"eventBus\"],\n appId: string,\n config: Omit<DynamicColumnConfig, \"appId\">,\n): RemoteAppSDK {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, appId),\n [eventBus, appId],\n );\n\n useEffect(() => {\n sdk.registerDynamicColumn(config);\n return () => sdk.unregisterDynamicColumn(config.id);\n }, [sdk, config]);\n\n return sdk;\n}\n","/**\n * Federation Error\n * Structured error class with error codes for better error handling\n */\n\nexport type HorizonSDKErrorCode =\n | \"PERMISSION_DENIED\"\n | \"RATE_LIMIT_EXCEEDED\"\n | \"INVALID_MESSAGE\"\n | \"SIGNATURE_VERIFICATION_FAILED\"\n | \"API_ERROR\"\n | \"NETWORK_ERROR\"\n | \"INVALID_EXTENSION_POINT\"\n | \"INVALID_CONFIGURATION\"\n | \"APP_NOT_FOUND\"\n | \"MODULE_LOAD_FAILED\"\n | \"INITIALIZATION_FAILED\"\n | \"UNKNOWN_ERROR\";\n\nexport interface HorizonSDKErrorOptions {\n code: HorizonSDKErrorCode;\n message: string;\n details?: Record<string, unknown>;\n cause?: Error;\n statusCode?: number;\n}\n\n/**\n * HorizonSDKError class\n * Provides structured errors with error codes and additional context\n */\nexport class HorizonSDKError extends Error {\n public readonly code: HorizonSDKErrorCode;\n public readonly details?: Record<string, unknown>;\n public readonly cause?: Error;\n public readonly statusCode?: number;\n public readonly timestamp: string;\n\n constructor(options: HorizonSDKErrorOptions) {\n super(options.message);\n\n this.name = \"HorizonSDKError\";\n this.code = options.code;\n this.details = options.details;\n this.cause = options.cause;\n this.statusCode = options.statusCode;\n this.timestamp = new Date().toISOString();\n\n // Maintain proper stack trace (only for V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, HorizonSDKError);\n }\n }\n\n /**\n * Check if error is a HorizonSDKError\n */\n static isHorizonSDKError(error: unknown): error is HorizonSDKError {\n return error instanceof HorizonSDKError;\n }\n\n /**\n * Get user-friendly error message\n */\n getUserMessage(): string {\n switch (this.code) {\n case \"PERMISSION_DENIED\":\n return \"You do not have permission to access this resource.\";\n case \"RATE_LIMIT_EXCEEDED\":\n return \"Too many requests. Please try again later.\";\n case \"INVALID_MESSAGE\":\n return \"Invalid message format.\";\n case \"SIGNATURE_VERIFICATION_FAILED\":\n return \"Message signature verification failed.\";\n case \"API_ERROR\":\n return \"An error occurred while communicating with the API.\";\n case \"NETWORK_ERROR\":\n return \"Network error. Please check your connection.\";\n case \"INVALID_EXTENSION_POINT\":\n return \"Invalid extension point.\";\n case \"INVALID_CONFIGURATION\":\n return \"Invalid configuration provided.\";\n case \"APP_NOT_FOUND\":\n return \"Application not found.\";\n case \"MODULE_LOAD_FAILED\":\n return \"Failed to load application module.\";\n case \"INITIALIZATION_FAILED\":\n return \"Application initialization failed.\";\n default:\n return \"An unknown error occurred.\";\n }\n }\n\n /**\n * Serialize error to JSON\n */\n toJSON(): object {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n details: this.details,\n statusCode: this.statusCode,\n timestamp: this.timestamp,\n stack: this.stack,\n };\n }\n\n /**\n * Get formatted error message for logging\n */\n toLogString(): string {\n let log = `[${this.code}] ${this.message}`;\n\n if (this.statusCode) {\n log += ` (status: ${this.statusCode})`;\n }\n\n if (this.details) {\n log += `\\nDetails: ${JSON.stringify(this.details, null, 2)}`;\n }\n\n if (this.cause) {\n log += `\\nCaused by: ${this.cause.message}`;\n }\n\n return log;\n }\n}\n\n/**\n * Helper function to create HorizonSDKError instances\n */\nexport function createHorizonSDKError(\n code: HorizonSDKErrorCode,\n message: string,\n details?: Record<string, unknown>,\n cause?: Error,\n): HorizonSDKError {\n return new HorizonSDKError({ code, message, details, cause });\n}\n\n/**\n * Permission Denied Error\n */\nexport function permissionDeniedError(\n resource: string,\n details?: Record<string, unknown>,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"PERMISSION_DENIED\",\n message: `Permission denied: ${resource}`,\n details,\n statusCode: 403,\n });\n}\n\n/**\n * Rate Limit Exceeded Error\n */\nexport function rateLimitError(\n resetTime: number,\n details?: Record<string, unknown>,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"RATE_LIMIT_EXCEEDED\",\n message: `Rate limit exceeded. Resets at ${new Date(resetTime).toISOString()}`,\n details: { resetTime, ...details },\n statusCode: 429,\n });\n}\n\n/**\n * API Error\n */\nexport function apiError(\n message: string,\n statusCode: number,\n details?: Record<string, unknown>,\n cause?: Error,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"API_ERROR\",\n message,\n details,\n cause,\n statusCode,\n });\n}\n\n/**\n * Invalid Extension Point Error\n */\nexport function invalidExtensionPointError(\n pointId: string,\n validPoints: string[],\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"INVALID_EXTENSION_POINT\",\n message: `Invalid extension point: ${pointId}`,\n details: { pointId, validPoints },\n });\n}\n\n/**\n * Signature Verification Failed Error\n */\nexport function signatureVerificationError(reason: string): HorizonSDKError {\n return new HorizonSDKError({\n code: \"SIGNATURE_VERIFICATION_FAILED\",\n message: `Signature verification failed: ${reason}`,\n details: { reason },\n });\n}\n\n/**\n * Module Load Failed Error\n */\nexport function moduleLoadError(\n appId: string,\n url: string,\n cause?: Error,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"MODULE_LOAD_FAILED\",\n message: `Failed to load module for app: ${appId}`,\n details: { appId, url },\n cause,\n });\n}\n","/**\n * Stable Anchor IDs for Extension Placement\n *\n * These anchor IDs are guaranteed to be stable and can be used by extensions\n * to specify their menu placement using semantic anchors.\n *\n * @example\n * ```typescript\n * import { MANAGE_ANCHORS } from '@netsapiens/horizon-sdk';\n *\n * sdk.registerRoute({\n * id: 'my-crm',\n * parentPath: '/apps',\n * path: 'crm',\n * label: 'CRM',\n * placement: { after: MANAGE_ANCHORS.contacts }\n * });\n * ```\n */\n\n/**\n * Manage Menu Anchors\n * Available in both domain and no-domain contexts\n */\nexport const MANAGE_ANCHORS = {\n dashboard: \"manage-dashboard\",\n users: \"manage-users\",\n contacts: \"manage-contacts\",\n devices: \"manage-devices\",\n phoneNumbers: \"manage-phone-numbers\",\n callLogs: \"manage-call-logs\",\n voicemail: \"manage-voicemail\",\n fax: \"manage-fax\",\n settings: \"manage-settings\",\n} as const;\n\n/**\n * Platform Menu Anchors\n * Available to Admin, Super User, and Reseller scopes\n */\nexport const PLATFORM_ANCHORS = {\n dashboard: \"platform-dashboard\",\n codeManagement: \"platform-code-management\",\n configManagement: \"platform-config-management\",\n sdkManagement: \"platform-ui-sdk\",\n branding: \"platform-branding\",\n recording: \"platform-recording\",\n logsAndDiagnostics: \"platform-logs-and-diagnostics\",\n} as const;\n\n/**\n * Apps Menu Anchors\n * Extension apps typically register here\n */\nexport const APPS_ANCHORS = {\n home: \"apps-home\",\n} as const;\n\n/**\n * My Account Menu Anchors\n * User-specific settings and preferences\n */\nexport const MY_ACCOUNT_ANCHORS = {\n profile: \"my-account-profile\",\n preferences: \"my-account-preferences\",\n security: \"my-account-security\",\n} as const;\n\n/**\n * All anchor constants in one object for convenience\n */\nexport const ANCHORS = {\n manage: MANAGE_ANCHORS,\n platform: PLATFORM_ANCHORS,\n apps: APPS_ANCHORS,\n myAccount: MY_ACCOUNT_ANCHORS,\n} as const;\n\n/**\n * Type-safe anchor ID union\n */\nexport type AnchorId =\n | (typeof MANAGE_ANCHORS)[keyof typeof MANAGE_ANCHORS]\n | (typeof PLATFORM_ANCHORS)[keyof typeof PLATFORM_ANCHORS]\n | (typeof APPS_ANCHORS)[keyof typeof APPS_ANCHORS]\n | (typeof MY_ACCOUNT_ANCHORS)[keyof typeof MY_ACCOUNT_ANCHORS];\n","{\n \"name\": \"@netsapiens/horizon-sdk\",\n \"description\": \"SDK for building remote applications that integrate with NetSapiens Horizon\",\n \"version\": \"0.1.5\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"module\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"author\": \"NetSapiens Inc.\",\n \"license\": \"MIT\",\n \"keywords\": [\n \"netsapiens\",\n \"horizon\",\n \"remote-apps\",\n \"module-federation\",\n \"sdk\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"clean\": \"rimraf .turbo node_modules dist\",\n \"lint\": \"eslint . --max-warnings 0\",\n \"format\": \"prettier --check . --ignore-path ../../.gitignore\",\n \"format:fix\": \"prettier --write . --ignore-path ../../.gitignore\",\n \"typecheck\": \"tsc --noEmit\",\n \"validate\": \"pnpm run lint && pnpm run format && pnpm run typecheck\"\n },\n \"publishConfig\": {\n \"access\": \"restricted\"\n },\n \"peerDependencies\": {\n \"loglevel\": \"^1.9.2\",\n \"react\": \"^18.0.0 || ^19.0.0\",\n \"react-dom\": \"^18.0.0 || ^19.0.0\"\n },\n \"devDependencies\": {\n \"@repo/eslint-config\": \"workspace:*\",\n \"@repo/typescript-config\": \"workspace:*\",\n \"@types/node\": \"^22.7.5\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n \"eslint\": \"^8.57.0\",\n \"loglevel\": \"^1.9.2\",\n \"prettier\": \"3.3.3\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.4.5\"\n }\n}\n","/**\n * @netsapiens/horizon-sdk — public entry point.\n *\n * Single export surface. Remote app developers import everything from\n * `@netsapiens/horizon-sdk`.\n *\n * @example\n * import { useRemoteApp, type HorizonContext } from '@netsapiens/horizon-sdk';\n *\n * export default function MyApp(horizonContext: HorizonContext) {\n * const { sdk, user } = useRemoteApp(horizonContext, 'my-app');\n * useEffect(() => {\n * sdk.registerDynamicExtension({\n * id: 'my-app.export',\n * zone: 'page-header-actions',\n * routes: [{ pattern: '/manage/*\\/call-logs' }],\n * component: MyButton,\n * });\n * }, [sdk]);\n * return <div>Hello {user.displayName}</div>;\n * }\n */\nimport { createLogger } from \"./utils/logger\";\n\n// SDK\nexport { RemoteAppSDK, createRemoteAppSDK } from \"./RemoteAppSDK\";\nexport {\n useRemoteApp,\n useRoute,\n useRouteFromModule,\n useDynamicExtension,\n useDynamicColumn,\n usePageContext,\n // Dark-mode-aware context — use these instead of prop drilling\n HorizonContextProvider,\n useHorizonContext,\n // Reactive theme for pages and standalone extensions\n useTheme,\n // Host translations via shared i18next singleton\n useLocale,\n // Open the shared side panel from anywhere\n useSidePanel,\n} from \"./hooks\";\n\n// Types — runtime contract with Horizon\nexport type {\n HorizonContext,\n HorizonUser,\n HorizonAuth,\n HorizonApiClient,\n HorizonEventBus,\n HorizonUI,\n HorizonUITemplates,\n FormPanelStep,\n ThemeTokens,\n BreadcrumbItem,\n // Routes\n RouteConfig,\n RemoteModuleConfig,\n SemanticPlacement,\n // Dynamic extensions\n ExtensionZone,\n ExtensionZoneId,\n RoutePattern,\n ExtensionContext,\n ExtensionComponentProps,\n DynamicExtensionConfig,\n DynamicColumnConfig,\n DynamicColumnDefinition,\n TableFilterBarContext,\n // Side panel (imperative)\n SidePanelConfig,\n SidePanelContentProps,\n // Call events\n CallEvent,\n CallEventType,\n // Remote Authentication\n RemoteAuthRequest,\n RemoteAuthResponse,\n RemoteAuthError,\n RemoteAuthOptions,\n} from \"./types\";\n\n// Errors\nexport {\n HorizonSDKError,\n createHorizonSDKError,\n permissionDeniedError,\n rateLimitError,\n apiError,\n invalidExtensionPointError,\n signatureVerificationError,\n moduleLoadError,\n} from \"./errors\";\nexport type { HorizonSDKErrorCode, HorizonSDKErrorOptions } from \"./errors\";\n\n// Utilities\nexport { createLogger, setLogLevel, getLogLevel } from \"./utils/logger\";\n\n// Stable anchor IDs for menu placement\nexport {\n MANAGE_ANCHORS,\n PLATFORM_ANCHORS,\n APPS_ANCHORS,\n MY_ACCOUNT_ANCHORS,\n ANCHORS,\n type AnchorId,\n} from \"./anchors\";\n\n// @ts-ignore - This is resolved at build time by tsup\nimport pkg from \"../package.json\";\n\nexport const VERSION = pkg.version;\n\nconst log = createLogger(\"FederationSDK\");\nlog.info(`SDK v${VERSION} loaded`);\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/appId.ts","../src/RemoteAppSDK.ts","../src/hooks.tsx","../src/errors/HorizonSDKError.ts","../package.json","../src/index.ts"],"names":["loglevel","createContext","useState","useEffect","useMemo","createElement","useContext","useRef","log"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Logging utility using loglevel for Horizon SDK\n * Consistent with netsapiens-monorepo logging pattern\n *\n * Usage:\n * import { createLogger } from '../utils/logger';\n * const log = createLogger('ModuleLoader');\n * log.info('Loading module...');\n * log.error('Failed to load:', error);\n */\nimport loglevel from \"loglevel\";\n\n// Configure default log level based on environment\nconst isDevelopment = process.env.NODE_ENV === \"development\";\n\n// Set default level\nloglevel.setDefaultLevel(\n isDevelopment ? loglevel.levels.DEBUG : loglevel.levels.WARN,\n);\n\n/**\n * Create a namespaced logger for a specific module\n * Follows the monorepo pattern from @netsapiens/ns-sdk\n *\n * @example\n * const log = createLogger('ModuleLoader');\n * log.debug('Starting load...');\n * log.info('Module loaded successfully');\n * log.warn('Slow load detected');\n * log.error('Load failed:', error);\n */\nexport const createLogger = (namespace: string) => {\n const moduleLogger = loglevel.getLogger(`HorizonSDK:${namespace}`);\n\n if (isDevelopment) {\n moduleLogger.setLevel(loglevel.levels.DEBUG);\n } else {\n moduleLogger.setLevel(loglevel.levels.WARN);\n }\n\n return moduleLogger;\n};\n\n/**\n * Default logger instance for the SDK\n */\nexport const logger = loglevel.getLogger(\"horizon-sdk\");\n\n/**\n * Set log level at runtime\n * Useful for debugging\n *\n * @example\n * import { setLogLevel } from './utils/logger';\n * setLogLevel('DEBUG'); // Enable all logs\n * setLogLevel('WARN'); // Only warnings and errors\n */\nexport const setLogLevel = (\n level: \"TRACE\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\" | \"SILENT\",\n) => {\n loglevel.setLevel(level);\n};\n\n/**\n * Get current log level\n */\nexport const getLogLevel = () => {\n return loglevel.getLevel();\n};\n\n// Extend Window interface for debug tools\ndeclare global {\n interface Window {\n __horizonSDKLogger__: typeof logger;\n __setHorizonSDKLogLevel__: typeof setLogLevel;\n }\n}\n\n// Make logger available in browser console for debugging\nif (typeof window !== \"undefined\") {\n window.__horizonSDKLogger__ = logger;\n window.__setHorizonSDKLogLevel__ = setLogLevel;\n\n if (isDevelopment) {\n logger.info(\n 'Horizon SDK logger initialized. Use window.__setHorizonSDKLogLevel__(\"DEBUG\") to adjust log level.',\n );\n }\n}\n","/**\n * Derive a remote app's stable `appId` (the kebab-case registry `id`) from its\n * Module Federation container name (`webpack_module`, camelCase).\n *\n * This mirrors the derivation the Horizon host and the `/ui-extensions` API use\n * (`id = kebab(webpack_module)`), so a remote app specifies ONE identifier — the\n * `name` in its `ModuleFederationPlugin` config — and the SDK, host, and API all\n * agree on the resulting app id (used for registration attribution, audit, and\n * the `/ui-extensions/{id}` route).\n *\n * Idempotent: an already-kebab value is returned unchanged, so passing either\n * form works.\n *\n * @example\n * deriveAppId(\"ucaasExtensionDemo\"); // \"ucaas-extension-demo\"\n * deriveAppId(\"ucaas-extension-demo\"); // \"ucaas-extension-demo\" (unchanged)\n */\nexport function deriveAppId(webpackModule: string): string {\n return webpackModule.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n","/**\n * Remote App SDK.\n *\n * Wraps Horizon's event bus with a typed, app-scoped API for registering routes\n * and dynamic extensions. Tracks every registration so `cleanup()` can tear\n * everything down on unmount — essential when a remote app is reloaded or its\n * webpack module is replaced during HMR.\n */\nimport type {\n CallEvent,\n CallEventType,\n DynamicColumnConfig,\n DynamicExtensionConfig,\n HorizonEventBus,\n RemoteModuleConfig,\n RouteConfig,\n SidePanelConfig,\n} from \"./types\";\nimport { deriveAppId } from \"./appId\";\nimport { createLogger } from \"./utils/logger\";\n\nconst log = createLogger(\"RemoteAppSDK\");\n\nexport class RemoteAppSDK {\n private eventBus: HorizonEventBus;\n private appId: string;\n\n // Registrations are tracked separately so cleanup() emits the right\n // unregister event for each. (A single set keyed by id-only would let dynamic\n // registrations leak — they're unregistered on a different channel.)\n private routes = new Set<string>();\n private dynamicExtensions = new Set<string>();\n private dynamicColumns = new Set<string>();\n private callEventsSubscribed = false;\n private sidePanelOpen = false;\n\n /**\n * @param webpackModule Your Module Federation container name (the `name` in\n * ModuleFederationPlugin, camelCase). The app's `appId` is derived from it\n * via {@link deriveAppId} so it matches the host/API registry `id`.\n */\n constructor(eventBus: HorizonEventBus, webpackModule: string) {\n this.eventBus = eventBus;\n this.appId = deriveAppId(webpackModule);\n log.debug(`Initialized for app: ${this.appId}`);\n }\n\n // -------------------------------------------------------------------------\n // Routes\n // -------------------------------------------------------------------------\n\n async registerRoute(config: Omit<RouteConfig, \"appId\">): Promise<void> {\n const route: RouteConfig = { ...config, appId: this.appId };\n this.eventBus.emit(\"route:register\", route);\n this.routes.add(route.id);\n log.info(\n `[${this.appId}] Route registered: ${route.id} at ${route.parentPath}/${route.path}`,\n );\n }\n\n unregisterRoute(routeId: string): void {\n this.eventBus.emit(\"route:unregister\", { id: routeId });\n this.routes.delete(routeId);\n log.info(`[${this.appId}] Route unregistered: ${routeId}`);\n }\n\n /**\n * Convenience: load a component out of a federated module's webpack\n * container and register it as a route in one step. Useful when the route\n * component lives in a sibling exposed module rather than the entry App.\n */\n async registerRouteFromModule(\n routeConfig: Omit<RouteConfig, \"appId\" | \"component\">,\n moduleConfig: RemoteModuleConfig,\n ): Promise<void> {\n const container = (\n window as unknown as Record<string, ModuleFederationContainer>\n )[moduleConfig.scope];\n if (!container) {\n throw new Error(`Remote container not found: ${moduleConfig.scope}`);\n }\n\n const factory = await container.get(moduleConfig.module);\n const moduleExports = factory();\n const component = (moduleExports.default ??\n moduleExports) as React.ComponentType<unknown>;\n\n await this.registerRoute({ ...routeConfig, component });\n }\n\n // -------------------------------------------------------------------------\n // Dynamic extensions (pattern-based UI injection)\n // -------------------------------------------------------------------------\n\n registerDynamicExtension(\n config: Omit<DynamicExtensionConfig, \"appId\">,\n ): void {\n const extension = { ...config, appId: this.appId };\n this.eventBus.emit(\"dynamic-extension:register\", extension);\n this.dynamicExtensions.add(extension.id);\n log.info(\n `[${this.appId}] Dynamic extension registered: ${extension.id} → zone ${extension.zone}`,\n );\n }\n\n unregisterDynamicExtension(extensionId: string): void {\n this.eventBus.emit(\"dynamic-extension:unregister\", { id: extensionId });\n this.dynamicExtensions.delete(extensionId);\n log.info(`[${this.appId}] Dynamic extension unregistered: ${extensionId}`);\n }\n\n // -------------------------------------------------------------------------\n // Dynamic columns (table column injection)\n // -------------------------------------------------------------------------\n\n registerDynamicColumn(config: Omit<DynamicColumnConfig, \"appId\">): void {\n const column = { ...config, appId: this.appId };\n this.eventBus.emit(\"dynamic-column:register\", column);\n this.dynamicColumns.add(column.id);\n log.info(\n `[${this.appId}] Dynamic column registered: ${column.id} → zone ${column.zone}`,\n );\n }\n\n unregisterDynamicColumn(columnId: string): void {\n this.eventBus.emit(\"dynamic-column:unregister\", { id: columnId });\n this.dynamicColumns.delete(columnId);\n log.info(`[${this.appId}] Dynamic column unregistered: ${columnId}`);\n }\n\n // -------------------------------------------------------------------------\n // Call events (capability-gated, app-scoped)\n // -------------------------------------------------------------------------\n\n /**\n * Subscribe to live SIP call events. Routes through the host's\n * `CallEventsManager`, which enforces the `call-events:listen` capability and\n * records the subscription against this app — so the platform knows which\n * apps consume call events and can disable the capability platform-wide.\n *\n * Prefer this over listening to `eventBus.on('call-event')` directly: the raw\n * bus is neither gated nor attributed to your app.\n *\n * @returns an unsubscribe function (also torn down by `cleanup()`).\n */\n subscribeToCallEvents(\n eventTypes: CallEventType[],\n callback: (event: CallEvent) => void,\n ): () => void {\n this.eventBus.emit(\"call-events:subscribe\", {\n appId: this.appId,\n eventTypes,\n callback,\n });\n this.callEventsSubscribed = true;\n log.info(\n `[${this.appId}] Subscribed to call events: ${eventTypes.join(\", \")}`,\n );\n return () => this.unsubscribeFromCallEvents();\n }\n\n unsubscribeFromCallEvents(): void {\n if (!this.callEventsSubscribed) return;\n this.eventBus.emit(\"call-events:unsubscribe\", { appId: this.appId });\n this.callEventsSubscribed = false;\n log.info(`[${this.appId}] Unsubscribed from call events`);\n }\n\n // -------------------------------------------------------------------------\n // Side panel (imperative, app-driven)\n // -------------------------------------------------------------------------\n\n /**\n * Open Horizon's shared side panel with app-provided content. The host renders\n * `config.component` inside the drawer, passing it `{ context, close }`.\n *\n * Can be called from anywhere with an SDK instance; remote components without\n * one (extensions, pages) can use the `useSidePanel()` hook instead. Calling\n * again replaces the current content. Closed automatically by `cleanup()`.\n */\n openSidePanel(config: SidePanelConfig): void {\n this.eventBus.emit(\"side-panel:open\", { ...config, appId: this.appId });\n this.sidePanelOpen = true;\n log.info(\n `[${this.appId}] Side panel opened: ${config.title ?? \"(untitled)\"}`,\n );\n }\n\n /** Close the side panel if this app opened it. */\n closeSidePanel(): void {\n if (!this.sidePanelOpen) return;\n this.eventBus.emit(\"side-panel:close\", { appId: this.appId });\n this.sidePanelOpen = false;\n log.info(`[${this.appId}] Side panel closed`);\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n /**\n * Unregister everything this SDK instance has registered. Call from your\n * remote app's unmount/cleanup path — `useRemoteApp` does this for you.\n */\n cleanup(): void {\n log.info(\n `[${this.appId}] Cleanup: ${this.routes.size} routes, ${this.dynamicExtensions.size} extensions, ${this.dynamicColumns.size} columns`,\n );\n this.routes.forEach((id) => this.eventBus.emit(\"route:unregister\", { id }));\n this.dynamicExtensions.forEach((id) =>\n this.eventBus.emit(\"dynamic-extension:unregister\", { id }),\n );\n this.dynamicColumns.forEach((id) =>\n this.eventBus.emit(\"dynamic-column:unregister\", { id }),\n );\n this.unsubscribeFromCallEvents();\n this.closeSidePanel();\n this.routes.clear();\n this.dynamicExtensions.clear();\n this.dynamicColumns.clear();\n }\n\n getAppId(): string {\n return this.appId;\n }\n\n getRegisteredRoutes(): string[] {\n return Array.from(this.routes);\n }\n\n getRegisteredDynamicExtensions(): string[] {\n return Array.from(this.dynamicExtensions);\n }\n\n getRegisteredDynamicColumns(): string[] {\n return Array.from(this.dynamicColumns);\n }\n}\n\n/**\n * Factory: equivalent to `new RemoteAppSDK(...)`. Pass your Module Federation\n * container name (`webpack_module`, camelCase); the appId is derived from it.\n */\nexport function createRemoteAppSDK(\n eventBus: HorizonEventBus,\n webpackModule: string,\n): RemoteAppSDK {\n return new RemoteAppSDK(eventBus, webpackModule);\n}\n\ninterface ModuleFederationContainer {\n get: (\n module: string,\n ) => Promise<() => { default?: unknown; [key: string]: unknown }>;\n}\n","/**\n * React hooks for remote apps.\n *\n * Each hook handles its own cleanup so remote apps don't have to track\n * unregistration manually.\n */\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createElement } from \"react\";\nimport type {\n DynamicColumnConfig,\n DynamicExtensionConfig,\n ExtensionContext,\n HorizonContext,\n RemoteModuleConfig,\n RouteConfig,\n SidePanelConfig,\n} from \"./types\";\nimport { RemoteAppSDK, createRemoteAppSDK } from \"./RemoteAppSDK\";\n\n// ---------------------------------------------------------------------------\n// HorizonContextProvider + useHorizonContext\n// ---------------------------------------------------------------------------\n\n/**\n * Internal React context that carries the live HorizonContext.\n * Updated automatically when dark mode changes — remote pages never need to\n * subscribe to theme events manually.\n */\nconst HorizonContextReact = createContext<HorizonContext | null>(null);\n\n/**\n * Wrap your registered page components with this provider so they receive a\n * reactive HorizonContext without any extra event-listener boilerplate.\n *\n * The component memoization pattern in App.tsx captures `horizonContext` once\n * (to keep stable component identity), but `eventBus` is a singleton — the\n * Provider subscribes to it and pushes theme updates to all descendant pages.\n *\n * @example\n * // In App.tsx, inside useMemo with empty deps:\n * return (\n * <HorizonContextProvider context={horizonContext}>\n * <MyPage />\n * </HorizonContextProvider>\n * );\n */\nexport function HorizonContextProvider({\n context,\n children,\n}: {\n context: HorizonContext;\n children: ReactNode;\n}) {\n const [theme, setTheme] = useState<\"light\" | \"dark\">(\n context.theme ?? \"light\",\n );\n const [locale, setLocale] = useState<string>(context.locale ?? \"en-US\");\n\n useEffect(() => {\n if (!context.eventBus) return;\n\n const themeHandler = (data: unknown) => {\n const payload = data as { theme: \"light\" | \"dark\" };\n if (payload?.theme === \"light\" || payload?.theme === \"dark\")\n setTheme(payload.theme);\n };\n const localeHandler = (data: unknown) => {\n const payload = data as { locale: string };\n if (payload?.locale) setLocale(payload.locale);\n };\n\n context.eventBus.on(\"theme:changed\", themeHandler);\n context.eventBus.on(\"locale:changed\", localeHandler);\n return () => {\n context.eventBus.off(\"theme:changed\", themeHandler);\n context.eventBus.off(\"locale:changed\", localeHandler);\n };\n }, [context.eventBus]);\n\n const liveContext = useMemo<HorizonContext>(\n () => ({ ...context, theme, locale }),\n // Intentionally omit `context` — eventBus is stable and subscriptions keep\n // theme/locale live. context spread gives access to t, user, api, navigate, etc.\n [theme, locale],\n );\n\n return createElement(\n HorizonContextReact.Provider,\n { value: liveContext },\n children,\n );\n}\n\n/**\n * Read the live HorizonContext inside any page registered via the SDK.\n * Returns a context that updates automatically when dark mode changes.\n *\n * Must be called inside a component rendered within `HorizonContextProvider`.\n *\n * @example\n * export default function MyPage() {\n * const { ui, theme, user, navigate } = useHorizonContext();\n * const { PageTemplate, DatagridTemplate } = ui.templates;\n * // `theme` is always 'light' | 'dark', reactive to system/user toggle\n * }\n */\nexport function useHorizonContext(): HorizonContext {\n const ctx = useContext(HorizonContextReact);\n if (!ctx) {\n throw new Error(\n \"[Horizon SDK] useHorizonContext() must be called inside a component wrapped by HorizonContextProvider.\",\n );\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// useTheme — reactive theme for pages and standalone extensions\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current color scheme, reactive to host theme changes.\n *\n * Works in **both** contexts with the same import — no manual event wiring:\n *\n * - **Page components** (inside `HorizonContextProvider`): reads directly from\n * the provider's React context. No subscription overhead.\n * - **Standalone extension components** (rendered by the host via\n * `registerDynamicExtension`): falls back to `eventBus` subscription.\n * Pass `context.eventBus` as the argument.\n *\n * @example\n * // In a page component (HorizonContextProvider ancestor required)\n * export default function MyPage() {\n * const { theme } = useTheme();\n * return <div style={{ color: theme === 'dark' ? '#fff' : '#000' }}>...</div>;\n * }\n *\n * @example\n * // In a standalone extension component\n * export default function MyButton({ context }: ExtensionComponentProps) {\n * const { theme } = useTheme(context.eventBus);\n * const { Button } = context.ui ?? {};\n * return <Button>{theme === 'dark' ? '🌙' : '☀️'} Export</Button>;\n * }\n */\n/**\n * Returns the current color scheme, reactive to host theme changes.\n *\n * @param eventBus - Pass `context.eventBus` when called from a standalone\n * extension component (outside a HorizonContextProvider).\n * @param initialTheme - Pass `context.theme` when called from a standalone\n * extension component so the hook initialises with the correct value on\n * first render rather than defaulting to `'light'`.\n *\n * Inside a page component wrapped by `HorizonContextProvider`, both params\n * can be omitted — the provider context is used automatically.\n */\nexport function useTheme(\n eventBus?: HorizonContext[\"eventBus\"],\n initialTheme?: \"light\" | \"dark\",\n): { theme: \"light\" | \"dark\" } {\n const providerTheme = useContext(HorizonContextReact)?.theme;\n\n // Prefer provider > caller-supplied initial > safe default.\n // Passing initialTheme = context.theme from an extension component ensures\n // the correct value is used on the very first render.\n const [localTheme, setLocalTheme] = useState<\"light\" | \"dark\">(\n providerTheme ?? initialTheme ?? \"light\",\n );\n\n useEffect(() => {\n // Inside a HorizonContextProvider the provider re-renders with the new\n // theme value — no subscription needed here.\n if (providerTheme !== undefined) return;\n if (!eventBus) return;\n\n const handler = (data: unknown) => {\n const payload = data as { theme: \"light\" | \"dark\" };\n if (payload?.theme === \"light\" || payload?.theme === \"dark\") {\n setLocalTheme(payload.theme);\n }\n };\n eventBus.on(\"theme:changed\", handler);\n return () => {\n eventBus.off(\"theme:changed\", handler);\n };\n }, [providerTheme, eventBus]);\n\n return { theme: providerTheme ?? localTheme };\n}\n\n// ---------------------------------------------------------------------------\n// useLocale — access host translations and current locale\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the host's translation function and current locale.\n *\n * Because `i18next` is shared as a singleton, this hook reads directly from\n * the host's already-initialized i18next instance — all host translations\n * (common, telecom, admin, etc.) are immediately available with no extra\n * fetch or setup required.\n *\n * Reactivity is handled internally by react-i18next: the returned `t` and\n * `locale` update automatically when the user switches language.\n *\n * @param ns - Optional namespace(s). Defaults to `'common'` (the host\n * namespace that contains all standard UI strings). Pass your app's own\n * registered namespace to access remote-app-specific keys.\n *\n * @example\n * // In a page component — access any host string\n * export default function MyPage() {\n * const { t, locale } = useLocale();\n * return <Button>{t('SAVE')}</Button>;\n * }\n *\n * @example\n * // In an extension component — same API\n * export function MyButton({ context }: ExtensionComponentProps) {\n * const { t } = useLocale();\n * const { Button } = context.ui ?? {};\n * return <Button>{t('EXPORT')}</Button>;\n * }\n */\n/**\n * Returns the host's `t` translation function and current locale string.\n *\n * The `t` function is the host's i18next instance — all 1,375+ host strings\n * across the common, telecom, admin, and validation namespaces are immediately\n * available. No i18next dependency, no package install, no init required in\n * the remote app.\n *\n * Both `t` and `locale` update automatically when the user switches language.\n *\n * @example\n * export default function MyPage() {\n * const { t, locale } = useLocale();\n * return <Button>{t('SAVE')}</Button>;\n * }\n *\n * @example\n * // In a standalone extension component\n * export function MyButton({ context }: ExtensionComponentProps) {\n * const { t } = useLocale();\n * return <span>{t('EXPORT')}</span>;\n * }\n */\nexport function useLocale(): { t: HorizonContext[\"t\"]; locale: string } {\n const ctx = useContext(HorizonContextReact);\n return {\n t: ctx?.t,\n locale: ctx?.locale ?? \"en-US\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// useSidePanel — open the shared side panel from anywhere\n// ---------------------------------------------------------------------------\n\n/**\n * Open / close Horizon's shared side panel with app-provided content, from any\n * remote component — a page, an extension (row action, header button), or a\n * call-event handler.\n *\n * Works in **both** contexts like {@link useTheme}:\n * - **Page components** (inside `HorizonContextProvider`): call `useSidePanel()`\n * with no argument — the eventBus is read from the provider.\n * - **Standalone extension components** (rendered via `registerDynamicExtension`):\n * pass `context.eventBus`, e.g. `useSidePanel(context.eventBus)`.\n *\n * @example\n * // In an extension component (e.g. a table row action)\n * export function RowAction({ context }: ExtensionComponentProps) {\n * const { open } = useSidePanel(context.eventBus);\n * const { IconButton } = context.ui ?? {};\n * return (\n * <IconButton icon=\"mdi:information\" onClick={() =>\n * open({ title: 'Details', component: DetailsPanel })\n * } />\n * );\n * }\n *\n * // The content component\n * function DetailsPanel({ context, close }: SidePanelContentProps) {\n * const { Stack, Typography, Button } = context.ui ?? {};\n * return <Stack><Typography>…</Typography><Button onClick={close}>Done</Button></Stack>;\n * }\n *\n * @param eventBus - Pass `context.eventBus` from a standalone extension component.\n * @param appId - Optional owner id. When provided, the host scopes\n * programmatic closes to the owner, so this app's `close()` only dismisses a\n * panel it opened — one app can never close another's content. Omit it and\n * `close()` is unscoped (closes whatever is open). The instance API\n * `sdk.openSidePanel()` always stamps the owner for you.\n */\nexport function useSidePanel(\n eventBus?: HorizonContext[\"eventBus\"],\n appId?: string,\n): {\n open: (config: SidePanelConfig) => void;\n close: () => void;\n} {\n const ctxBus = useContext(HorizonContextReact)?.eventBus;\n const bus = eventBus ?? ctxBus;\n\n return useMemo(\n () => ({\n open: (config: SidePanelConfig) =>\n bus?.emit(\"side-panel:open\", { ...config, appId }),\n close: () => bus?.emit(\"side-panel:close\", { appId }),\n }),\n [bus, appId],\n );\n}\n\n// ---------------------------------------------------------------------------\n// useRemoteApp\n// ---------------------------------------------------------------------------\n\n/**\n * Get an SDK instance bound to your app, plus a flat view of the Horizon\n * context. Returned `sdk.cleanup()` is called automatically on unmount.\n *\n * Pass your **Module Federation container name** — the `name` in your\n * `ModuleFederationPlugin` config (a.k.a. `webpack_module`, camelCase). That's\n * the single identifier you maintain; the SDK derives the kebab-case app id\n * (matching the host/API registry `id`) for you via {@link deriveAppId}.\n *\n * @example\n * export default function MyApp(horizonContext: HorizonContext) {\n * // 'myApp' === the name in your ModuleFederationPlugin config\n * const { sdk, user } = useRemoteApp(horizonContext, 'myApp');\n *\n * useEffect(() => {\n * sdk.registerDynamicExtension({\n * id: 'my-app.button',\n * zone: 'page-header-actions',\n * routes: [{ pattern: '/manage/*\\/users' }],\n * component: MyButton,\n * });\n * }, [sdk]);\n *\n * return <div>Hello {user.displayName}</div>;\n * }\n */\nexport function useRemoteApp(\n horizonContext: HorizonContext,\n webpackModule: string,\n) {\n const sdkRef = useRef<RemoteAppSDK | null>(null);\n if (!sdkRef.current) {\n sdkRef.current = createRemoteAppSDK(horizonContext.eventBus, webpackModule);\n }\n const sdk = sdkRef.current;\n\n return { sdk, ...horizonContext };\n}\n\n/**\n * Register a route for the lifetime of the calling component.\n */\nexport function useRoute(\n eventBus: HorizonContext[\"eventBus\"],\n webpackModule: string,\n config: Omit<RouteConfig, \"appId\">,\n): RemoteAppSDK {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, webpackModule),\n [eventBus, webpackModule],\n );\n\n useEffect(() => {\n void sdk.registerRoute(config);\n return () => sdk.unregisterRoute(config.id);\n }, [sdk, config]);\n\n return sdk;\n}\n\n/**\n * Register a route by pulling its component out of a federated module's\n * webpack container.\n */\nexport function useRouteFromModule(\n eventBus: HorizonContext[\"eventBus\"],\n webpackModule: string,\n routeConfig: Omit<RouteConfig, \"appId\" | \"component\">,\n moduleConfig: RemoteModuleConfig,\n) {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, webpackModule),\n [eventBus, webpackModule],\n );\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n let mounted = true;\n sdk\n .registerRouteFromModule(routeConfig, moduleConfig)\n .then(() => {\n if (mounted) setLoading(false);\n })\n .catch((err: Error) => {\n if (mounted) {\n setError(err);\n setLoading(false);\n }\n });\n\n return () => {\n mounted = false;\n sdk.unregisterRoute(routeConfig.id);\n };\n }, [sdk, moduleConfig, routeConfig]);\n\n return { loading, error, sdk };\n}\n\n/**\n * Register a dynamic extension (pattern-based UI injection) for the lifetime\n * of the calling component.\n */\nexport function useDynamicExtension(\n eventBus: HorizonContext[\"eventBus\"],\n webpackModule: string,\n config: Omit<DynamicExtensionConfig, \"appId\">,\n): RemoteAppSDK {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, webpackModule),\n [eventBus, webpackModule],\n );\n\n useEffect(() => {\n sdk.registerDynamicExtension(config);\n return () => sdk.unregisterDynamicExtension(config.id);\n }, [sdk, config]);\n\n return sdk;\n}\n\n// ---------------------------------------------------------------------------\n// usePageContext — typed access to host-supplied page context\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current page context as a typed value.\n *\n * The host passes page-specific data (e.g. active filters, date range, selected\n * row) through `ExtensionContext.pageContext`. This hook casts it to the caller's\n * expected shape and keeps it reactive — when the host updates `pageContext` the\n * extension re-renders automatically because it flows in as a React prop.\n *\n * Define a typed interface for each page's context in your remote app and pass\n * it as the type parameter. Returns `undefined` when the host has not populated\n * any context for the current zone.\n *\n * @example\n * interface CallLogsPageContext {\n * dateRange?: [Date | null, Date | null];\n * }\n *\n * export function AnalyticsWidget({ context }: ExtensionComponentProps) {\n * const pageCtx = usePageContext<CallLogsPageContext>(context);\n * const [start, end] = pageCtx?.dateRange ?? [null, null];\n * // ...\n * }\n */\nexport function usePageContext<T = unknown>(\n context: ExtensionContext,\n): T | undefined {\n return context.pageContext as T | undefined;\n}\n\n/**\n * Register a dynamic table column for the lifetime of the calling component.\n */\nexport function useDynamicColumn(\n eventBus: HorizonContext[\"eventBus\"],\n webpackModule: string,\n config: Omit<DynamicColumnConfig, \"appId\">,\n): RemoteAppSDK {\n const sdk = useMemo(\n () => createRemoteAppSDK(eventBus, webpackModule),\n [eventBus, webpackModule],\n );\n\n useEffect(() => {\n sdk.registerDynamicColumn(config);\n return () => sdk.unregisterDynamicColumn(config.id);\n }, [sdk, config]);\n\n return sdk;\n}\n","/**\n * Federation Error\n * Structured error class with error codes for better error handling\n */\n\nexport type HorizonSDKErrorCode =\n | \"PERMISSION_DENIED\"\n | \"RATE_LIMIT_EXCEEDED\"\n | \"INVALID_MESSAGE\"\n | \"SIGNATURE_VERIFICATION_FAILED\"\n | \"API_ERROR\"\n | \"NETWORK_ERROR\"\n | \"INVALID_EXTENSION_POINT\"\n | \"INVALID_CONFIGURATION\"\n | \"APP_NOT_FOUND\"\n | \"MODULE_LOAD_FAILED\"\n | \"INITIALIZATION_FAILED\"\n | \"UNKNOWN_ERROR\";\n\nexport interface HorizonSDKErrorOptions {\n code: HorizonSDKErrorCode;\n message: string;\n details?: Record<string, unknown>;\n cause?: Error;\n statusCode?: number;\n}\n\n/**\n * HorizonSDKError class\n * Provides structured errors with error codes and additional context\n */\nexport class HorizonSDKError extends Error {\n public readonly code: HorizonSDKErrorCode;\n public readonly details?: Record<string, unknown>;\n public readonly cause?: Error;\n public readonly statusCode?: number;\n public readonly timestamp: string;\n\n constructor(options: HorizonSDKErrorOptions) {\n super(options.message);\n\n this.name = \"HorizonSDKError\";\n this.code = options.code;\n this.details = options.details;\n this.cause = options.cause;\n this.statusCode = options.statusCode;\n this.timestamp = new Date().toISOString();\n\n // Maintain proper stack trace (only for V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, HorizonSDKError);\n }\n }\n\n /**\n * Check if error is a HorizonSDKError\n */\n static isHorizonSDKError(error: unknown): error is HorizonSDKError {\n return error instanceof HorizonSDKError;\n }\n\n /**\n * Get user-friendly error message\n */\n getUserMessage(): string {\n switch (this.code) {\n case \"PERMISSION_DENIED\":\n return \"You do not have permission to access this resource.\";\n case \"RATE_LIMIT_EXCEEDED\":\n return \"Too many requests. Please try again later.\";\n case \"INVALID_MESSAGE\":\n return \"Invalid message format.\";\n case \"SIGNATURE_VERIFICATION_FAILED\":\n return \"Message signature verification failed.\";\n case \"API_ERROR\":\n return \"An error occurred while communicating with the API.\";\n case \"NETWORK_ERROR\":\n return \"Network error. Please check your connection.\";\n case \"INVALID_EXTENSION_POINT\":\n return \"Invalid extension point.\";\n case \"INVALID_CONFIGURATION\":\n return \"Invalid configuration provided.\";\n case \"APP_NOT_FOUND\":\n return \"Application not found.\";\n case \"MODULE_LOAD_FAILED\":\n return \"Failed to load application module.\";\n case \"INITIALIZATION_FAILED\":\n return \"Application initialization failed.\";\n default:\n return \"An unknown error occurred.\";\n }\n }\n\n /**\n * Serialize error to JSON\n */\n toJSON(): object {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n details: this.details,\n statusCode: this.statusCode,\n timestamp: this.timestamp,\n stack: this.stack,\n };\n }\n\n /**\n * Get formatted error message for logging\n */\n toLogString(): string {\n let log = `[${this.code}] ${this.message}`;\n\n if (this.statusCode) {\n log += ` (status: ${this.statusCode})`;\n }\n\n if (this.details) {\n log += `\\nDetails: ${JSON.stringify(this.details, null, 2)}`;\n }\n\n if (this.cause) {\n log += `\\nCaused by: ${this.cause.message}`;\n }\n\n return log;\n }\n}\n\n/**\n * Helper function to create HorizonSDKError instances\n */\nexport function createHorizonSDKError(\n code: HorizonSDKErrorCode,\n message: string,\n details?: Record<string, unknown>,\n cause?: Error,\n): HorizonSDKError {\n return new HorizonSDKError({ code, message, details, cause });\n}\n\n/**\n * Permission Denied Error\n */\nexport function permissionDeniedError(\n resource: string,\n details?: Record<string, unknown>,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"PERMISSION_DENIED\",\n message: `Permission denied: ${resource}`,\n details,\n statusCode: 403,\n });\n}\n\n/**\n * Rate Limit Exceeded Error\n */\nexport function rateLimitError(\n resetTime: number,\n details?: Record<string, unknown>,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"RATE_LIMIT_EXCEEDED\",\n message: `Rate limit exceeded. Resets at ${new Date(resetTime).toISOString()}`,\n details: { resetTime, ...details },\n statusCode: 429,\n });\n}\n\n/**\n * API Error\n */\nexport function apiError(\n message: string,\n statusCode: number,\n details?: Record<string, unknown>,\n cause?: Error,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"API_ERROR\",\n message,\n details,\n cause,\n statusCode,\n });\n}\n\n/**\n * Invalid Extension Point Error\n */\nexport function invalidExtensionPointError(\n pointId: string,\n validPoints: string[],\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"INVALID_EXTENSION_POINT\",\n message: `Invalid extension point: ${pointId}`,\n details: { pointId, validPoints },\n });\n}\n\n/**\n * Signature Verification Failed Error\n */\nexport function signatureVerificationError(reason: string): HorizonSDKError {\n return new HorizonSDKError({\n code: \"SIGNATURE_VERIFICATION_FAILED\",\n message: `Signature verification failed: ${reason}`,\n details: { reason },\n });\n}\n\n/**\n * Module Load Failed Error\n */\nexport function moduleLoadError(\n appId: string,\n url: string,\n cause?: Error,\n): HorizonSDKError {\n return new HorizonSDKError({\n code: \"MODULE_LOAD_FAILED\",\n message: `Failed to load module for app: ${appId}`,\n details: { appId, url },\n cause,\n });\n}\n","{\n \"name\": \"@netsapiens/horizon-sdk\",\n \"description\": \"SDK for building remote applications that integrate with NetSapiens Horizon\",\n \"version\": \"0.1.7\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"module\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"author\": \"NetSapiens Inc.\",\n \"license\": \"MIT\",\n \"keywords\": [\n \"netsapiens\",\n \"horizon\",\n \"remote-apps\",\n \"module-federation\",\n \"sdk\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"clean\": \"rimraf .turbo node_modules dist\",\n \"lint\": \"eslint . --max-warnings 0\",\n \"format\": \"prettier --check . --ignore-path ../../.gitignore\",\n \"format:fix\": \"prettier --write . --ignore-path ../../.gitignore\",\n \"typecheck\": \"tsc --noEmit\",\n \"validate\": \"pnpm run lint && pnpm run format && pnpm run typecheck\"\n },\n \"publishConfig\": {\n \"access\": \"restricted\"\n },\n \"peerDependencies\": {\n \"loglevel\": \"^1.9.2\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\"\n },\n \"devDependencies\": {\n \"@repo/eslint-config\": \"workspace:*\",\n \"@repo/typescript-config\": \"workspace:*\",\n \"@types/node\": \"^22.7.5\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n \"eslint\": \"^8.57.0\",\n \"loglevel\": \"^1.9.2\",\n \"prettier\": \"3.3.3\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.4.5\"\n }\n}\n","/**\n * @netsapiens/horizon-sdk — public entry point.\n *\n * Single export surface. Remote app developers import everything from\n * `@netsapiens/horizon-sdk`.\n *\n * @example\n * import { useRemoteApp, type HorizonContext } from '@netsapiens/horizon-sdk';\n *\n * export default function MyApp(horizonContext: HorizonContext) {\n * const { sdk, user } = useRemoteApp(horizonContext, 'my-app');\n * useEffect(() => {\n * sdk.registerDynamicExtension({\n * id: 'my-app.export',\n * zone: 'page-header-actions',\n * routes: [{ pattern: '/manage/*\\/call-logs' }],\n * component: MyButton,\n * });\n * }, [sdk]);\n * return <div>Hello {user.displayName}</div>;\n * }\n */\nimport { createLogger } from \"./utils/logger\";\n\n// SDK\nexport { RemoteAppSDK, createRemoteAppSDK } from \"./RemoteAppSDK\";\n// Derive the kebab app id from a Module Federation name (host/API parity)\nexport { deriveAppId } from \"./appId\";\nexport {\n useRemoteApp,\n useRoute,\n useRouteFromModule,\n useDynamicExtension,\n useDynamicColumn,\n usePageContext,\n // Dark-mode-aware context — use these instead of prop drilling\n HorizonContextProvider,\n useHorizonContext,\n // Reactive theme for pages and standalone extensions\n useTheme,\n // Host translations via shared i18next singleton\n useLocale,\n // Open the shared side panel from anywhere\n useSidePanel,\n} from \"./hooks\";\n\n// Types — runtime contract with Horizon\nexport type {\n HorizonContext,\n HorizonUser,\n HorizonAuth,\n HorizonApiClient,\n HorizonEventBus,\n HorizonUI,\n HorizonUITemplates,\n FormPanelStep,\n ThemeTokens,\n BreadcrumbItem,\n // Routes\n RouteConfig,\n RemoteModuleConfig,\n SemanticPlacement,\n // Dynamic extensions\n ExtensionZone,\n ExtensionZoneId,\n RoutePattern,\n ExtensionContext,\n ExtensionComponentProps,\n DynamicExtensionConfig,\n DynamicColumnConfig,\n DynamicColumnDefinition,\n TableFilterBarContext,\n // Side panel (imperative)\n SidePanelConfig,\n SidePanelContentProps,\n // Call events\n CallEvent,\n CallEventType,\n // Remote Authentication\n RemoteAuthRequest,\n RemoteAuthResponse,\n RemoteAuthError,\n RemoteAuthOptions,\n} from \"./types\";\n\n// Errors\nexport {\n HorizonSDKError,\n createHorizonSDKError,\n permissionDeniedError,\n rateLimitError,\n apiError,\n invalidExtensionPointError,\n signatureVerificationError,\n moduleLoadError,\n} from \"./errors\";\nexport type { HorizonSDKErrorCode, HorizonSDKErrorOptions } from \"./errors\";\n\n// Utilities\nexport { createLogger, setLogLevel, getLogLevel } from \"./utils/logger\";\n\n// @ts-ignore - This is resolved at build time by tsup\nimport pkg from \"../package.json\";\n\nexport const VERSION = pkg.version;\n\nconst log = createLogger(\"FederationSDK\");\nlog.info(`SDK v${VERSION} loaded`);\n"]}
|