@netsapiens/horizon-sdk 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -470,6 +470,36 @@ interface ExtensionComponentProps {
470
470
  /** Provided by modal/dialog hosts to allow extensions to dismiss themselves */
471
471
  close?: () => void;
472
472
  }
473
+ /**
474
+ * Props passed to a side-panel content component opened via
475
+ * `sdk.openSidePanel()` / `useSidePanel().open()`. Mirrors
476
+ * {@link ExtensionComponentProps}, but `close` is always provided so any panel
477
+ * body can dismiss itself.
478
+ */
479
+ interface SidePanelContentProps {
480
+ /** Same host-built context extensions receive (ui, eventBus, user, theme, t). */
481
+ context: ExtensionContext;
482
+ /** Dismiss the panel. */
483
+ close: () => void;
484
+ }
485
+ /**
486
+ * Configuration for the on-demand side panel. The host renders `component`
487
+ * inside Horizon's shared SidePanel drawer; the remaining fields control the
488
+ * drawer chrome. Open it from anywhere with `sdk.openSidePanel(config)` or the
489
+ * `useSidePanel()` hook.
490
+ */
491
+ interface SidePanelConfig {
492
+ /** Component rendered as the panel body. Receives `{ context, close }`. */
493
+ component: React.ComponentType<SidePanelContentProps>;
494
+ /** Header title. */
495
+ title?: string;
496
+ /** Optional subtitle under the title. */
497
+ subtitle?: string;
498
+ /** Drawer width at lg+ breakpoints (full width below). Default `md`. */
499
+ width?: "sm" | "md" | "lg" | "xl";
500
+ /** Optional Iconify icon name shown in the header. */
501
+ icon?: string;
502
+ }
473
503
  /**
474
504
  * A SIP call lifecycle event delivered to apps subscribed via
475
505
  * `sdk.subscribeToCallEvents`. The host enriches this further, but every event
@@ -561,6 +591,7 @@ declare class RemoteAppSDK {
561
591
  private dynamicExtensions;
562
592
  private dynamicColumns;
563
593
  private callEventsSubscribed;
594
+ private sidePanelOpen;
564
595
  constructor(eventBus: HorizonEventBus, appId: string);
565
596
  registerRoute(config: Omit<RouteConfig, "appId">): Promise<void>;
566
597
  unregisterRoute(routeId: string): void;
@@ -587,6 +618,17 @@ declare class RemoteAppSDK {
587
618
  */
588
619
  subscribeToCallEvents(eventTypes: CallEventType[], callback: (event: CallEvent) => void): () => void;
589
620
  unsubscribeFromCallEvents(): void;
621
+ /**
622
+ * Open Horizon's shared side panel with app-provided content. The host renders
623
+ * `config.component` inside the drawer, passing it `{ context, close }`.
624
+ *
625
+ * Can be called from anywhere with an SDK instance; remote components without
626
+ * one (extensions, pages) can use the `useSidePanel()` hook instead. Calling
627
+ * again replaces the current content. Closed automatically by `cleanup()`.
628
+ */
629
+ openSidePanel(config: SidePanelConfig): void;
630
+ /** Close the side panel if this app opened it. */
631
+ closeSidePanel(): void;
590
632
  /**
591
633
  * Unregister everything this SDK instance has registered. Call from your
592
634
  * remote app's unmount/cleanup path — `useRemoteApp` does this for you.
@@ -732,6 +774,46 @@ declare function useLocale(): {
732
774
  t: HorizonContext["t"];
733
775
  locale: string;
734
776
  };
777
+ /**
778
+ * Open / close Horizon's shared side panel with app-provided content, from any
779
+ * remote component — a page, an extension (row action, header button), or a
780
+ * call-event handler.
781
+ *
782
+ * Works in **both** contexts like {@link useTheme}:
783
+ * - **Page components** (inside `HorizonContextProvider`): call `useSidePanel()`
784
+ * with no argument — the eventBus is read from the provider.
785
+ * - **Standalone extension components** (rendered via `registerDynamicExtension`):
786
+ * pass `context.eventBus`, e.g. `useSidePanel(context.eventBus)`.
787
+ *
788
+ * @example
789
+ * // In an extension component (e.g. a table row action)
790
+ * export function RowAction({ context }: ExtensionComponentProps) {
791
+ * const { open } = useSidePanel(context.eventBus);
792
+ * const { IconButton } = context.ui ?? {};
793
+ * return (
794
+ * <IconButton icon="mdi:information" onClick={() =>
795
+ * open({ title: 'Details', component: DetailsPanel })
796
+ * } />
797
+ * );
798
+ * }
799
+ *
800
+ * // The content component
801
+ * function DetailsPanel({ context, close }: SidePanelContentProps) {
802
+ * const { Stack, Typography, Button } = context.ui ?? {};
803
+ * return <Stack><Typography>…</Typography><Button onClick={close}>Done</Button></Stack>;
804
+ * }
805
+ *
806
+ * @param eventBus - Pass `context.eventBus` from a standalone extension component.
807
+ * @param appId - Optional owner id. When provided, the host scopes
808
+ * programmatic closes to the owner, so this app's `close()` only dismisses a
809
+ * panel it opened — one app can never close another's content. Omit it and
810
+ * `close()` is unscoped (closes whatever is open). The instance API
811
+ * `sdk.openSidePanel()` always stamps the owner for you.
812
+ */
813
+ declare function useSidePanel(eventBus?: HorizonContext["eventBus"], appId?: string): {
814
+ open: (config: SidePanelConfig) => void;
815
+ close: () => void;
816
+ };
735
817
  /**
736
818
  * Get an SDK instance bound to your app, plus a flat view of the Horizon
737
819
  * context. Returned `sdk.cleanup()` is called automatically on unmount.
@@ -929,107 +1011,6 @@ declare global {
929
1011
  }
930
1012
  }
931
1013
 
932
- /**
933
- * Stable Anchor IDs for Extension Placement
934
- *
935
- * These anchor IDs are guaranteed to be stable and can be used by extensions
936
- * to specify their menu placement using semantic anchors.
937
- *
938
- * @example
939
- * ```typescript
940
- * import { MANAGE_ANCHORS } from '@netsapiens/horizon-sdk';
941
- *
942
- * sdk.registerRoute({
943
- * id: 'my-crm',
944
- * parentPath: '/apps',
945
- * path: 'crm',
946
- * label: 'CRM',
947
- * placement: { after: MANAGE_ANCHORS.contacts }
948
- * });
949
- * ```
950
- */
951
- /**
952
- * Manage Menu Anchors
953
- * Available in both domain and no-domain contexts
954
- */
955
- declare const MANAGE_ANCHORS: {
956
- readonly dashboard: "manage-dashboard";
957
- readonly users: "manage-users";
958
- readonly contacts: "manage-contacts";
959
- readonly devices: "manage-devices";
960
- readonly phoneNumbers: "manage-phone-numbers";
961
- readonly callLogs: "manage-call-logs";
962
- readonly voicemail: "manage-voicemail";
963
- readonly fax: "manage-fax";
964
- readonly settings: "manage-settings";
965
- };
966
- /**
967
- * Platform Menu Anchors
968
- * Available to Admin, Super User, and Reseller scopes
969
- */
970
- declare const PLATFORM_ANCHORS: {
971
- readonly dashboard: "platform-dashboard";
972
- readonly codeManagement: "platform-code-management";
973
- readonly configManagement: "platform-config-management";
974
- readonly sdkManagement: "platform-ui-sdk";
975
- readonly branding: "platform-branding";
976
- readonly recording: "platform-recording";
977
- readonly logsAndDiagnostics: "platform-logs-and-diagnostics";
978
- };
979
- /**
980
- * Apps Menu Anchors
981
- * Extension apps typically register here
982
- */
983
- declare const APPS_ANCHORS: {
984
- readonly home: "apps-home";
985
- };
986
- /**
987
- * My Account Menu Anchors
988
- * User-specific settings and preferences
989
- */
990
- declare const MY_ACCOUNT_ANCHORS: {
991
- readonly profile: "my-account-profile";
992
- readonly preferences: "my-account-preferences";
993
- readonly security: "my-account-security";
994
- };
995
- /**
996
- * All anchor constants in one object for convenience
997
- */
998
- declare const ANCHORS: {
999
- readonly manage: {
1000
- readonly dashboard: "manage-dashboard";
1001
- readonly users: "manage-users";
1002
- readonly contacts: "manage-contacts";
1003
- readonly devices: "manage-devices";
1004
- readonly phoneNumbers: "manage-phone-numbers";
1005
- readonly callLogs: "manage-call-logs";
1006
- readonly voicemail: "manage-voicemail";
1007
- readonly fax: "manage-fax";
1008
- readonly settings: "manage-settings";
1009
- };
1010
- readonly platform: {
1011
- readonly dashboard: "platform-dashboard";
1012
- readonly codeManagement: "platform-code-management";
1013
- readonly configManagement: "platform-config-management";
1014
- readonly sdkManagement: "platform-ui-sdk";
1015
- readonly branding: "platform-branding";
1016
- readonly recording: "platform-recording";
1017
- readonly logsAndDiagnostics: "platform-logs-and-diagnostics";
1018
- };
1019
- readonly apps: {
1020
- readonly home: "apps-home";
1021
- };
1022
- readonly myAccount: {
1023
- readonly profile: "my-account-profile";
1024
- readonly preferences: "my-account-preferences";
1025
- readonly security: "my-account-security";
1026
- };
1027
- };
1028
- /**
1029
- * Type-safe anchor ID union
1030
- */
1031
- type AnchorId = (typeof MANAGE_ANCHORS)[keyof typeof MANAGE_ANCHORS] | (typeof PLATFORM_ANCHORS)[keyof typeof PLATFORM_ANCHORS] | (typeof APPS_ANCHORS)[keyof typeof APPS_ANCHORS] | (typeof MY_ACCOUNT_ANCHORS)[keyof typeof MY_ACCOUNT_ANCHORS];
1032
-
1033
1014
  declare const VERSION: string;
1034
1015
 
1035
- export { ANCHORS, APPS_ANCHORS, type AnchorId, type BreadcrumbItem, type CallEvent, type CallEventType, type DynamicColumnConfig, type DynamicColumnDefinition, type DynamicExtensionConfig, type ExtensionComponentProps, type ExtensionContext, type ExtensionZone, type ExtensionZoneId, type FormPanelStep, type HorizonApiClient, type HorizonAuth, type HorizonContext, HorizonContextProvider, type HorizonEventBus, HorizonSDKError, type HorizonSDKErrorCode, type HorizonSDKErrorOptions, type HorizonUI, type HorizonUITemplates, type HorizonUser, MANAGE_ANCHORS, MY_ACCOUNT_ANCHORS, PLATFORM_ANCHORS, RemoteAppSDK, type RemoteAuthError, type RemoteAuthOptions, type RemoteAuthRequest, type RemoteAuthResponse, type RemoteModuleConfig, type RouteConfig, type RoutePattern, type SemanticPlacement, type TableFilterBarContext, type ThemeTokens, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, usePageContext, useRemoteApp, useRoute, useRouteFromModule, useTheme };
1016
+ export { type BreadcrumbItem, type CallEvent, type CallEventType, type DynamicColumnConfig, type DynamicColumnDefinition, type DynamicExtensionConfig, type ExtensionComponentProps, type ExtensionContext, type ExtensionZone, type ExtensionZoneId, type FormPanelStep, type HorizonApiClient, type HorizonAuth, type HorizonContext, HorizonContextProvider, type HorizonEventBus, HorizonSDKError, type HorizonSDKErrorCode, type HorizonSDKErrorOptions, type HorizonUI, type HorizonUITemplates, type HorizonUser, RemoteAppSDK, type RemoteAuthError, type RemoteAuthOptions, type RemoteAuthRequest, type RemoteAuthResponse, type RemoteModuleConfig, type RouteConfig, type RoutePattern, type SemanticPlacement, type SidePanelConfig, type SidePanelContentProps, type TableFilterBarContext, type ThemeTokens, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, usePageContext, useRemoteApp, useRoute, useRouteFromModule, useSidePanel, useTheme };
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ var RemoteAppSDK = class {
44
44
  dynamicExtensions = /* @__PURE__ */ new Set();
45
45
  dynamicColumns = /* @__PURE__ */ new Set();
46
46
  callEventsSubscribed = false;
47
+ sidePanelOpen = false;
47
48
  constructor(eventBus, appId) {
48
49
  this.eventBus = eventBus;
49
50
  this.appId = appId;
@@ -145,6 +146,31 @@ var RemoteAppSDK = class {
145
146
  log.info(`[${this.appId}] Unsubscribed from call events`);
146
147
  }
147
148
  // -------------------------------------------------------------------------
149
+ // Side panel (imperative, app-driven)
150
+ // -------------------------------------------------------------------------
151
+ /**
152
+ * Open Horizon's shared side panel with app-provided content. The host renders
153
+ * `config.component` inside the drawer, passing it `{ context, close }`.
154
+ *
155
+ * Can be called from anywhere with an SDK instance; remote components without
156
+ * one (extensions, pages) can use the `useSidePanel()` hook instead. Calling
157
+ * again replaces the current content. Closed automatically by `cleanup()`.
158
+ */
159
+ openSidePanel(config) {
160
+ this.eventBus.emit("side-panel:open", { ...config, appId: this.appId });
161
+ this.sidePanelOpen = true;
162
+ log.info(
163
+ `[${this.appId}] Side panel opened: ${config.title ?? "(untitled)"}`
164
+ );
165
+ }
166
+ /** Close the side panel if this app opened it. */
167
+ closeSidePanel() {
168
+ if (!this.sidePanelOpen) return;
169
+ this.eventBus.emit("side-panel:close", { appId: this.appId });
170
+ this.sidePanelOpen = false;
171
+ log.info(`[${this.appId}] Side panel closed`);
172
+ }
173
+ // -------------------------------------------------------------------------
148
174
  // Lifecycle
149
175
  // -------------------------------------------------------------------------
150
176
  /**
@@ -163,6 +189,7 @@ var RemoteAppSDK = class {
163
189
  (id) => this.eventBus.emit("dynamic-column:unregister", { id })
164
190
  );
165
191
  this.unsubscribeFromCallEvents();
192
+ this.closeSidePanel();
166
193
  this.routes.clear();
167
194
  this.dynamicExtensions.clear();
168
195
  this.dynamicColumns.clear();
@@ -259,6 +286,17 @@ function useLocale() {
259
286
  locale: ctx?.locale ?? "en-US"
260
287
  };
261
288
  }
289
+ function useSidePanel(eventBus, appId) {
290
+ const ctxBus = useContext(HorizonContextReact)?.eventBus;
291
+ const bus = eventBus ?? ctxBus;
292
+ return useMemo(
293
+ () => ({
294
+ open: (config) => bus?.emit("side-panel:open", { ...config, appId }),
295
+ close: () => bus?.emit("side-panel:close", { appId })
296
+ }),
297
+ [bus, appId]
298
+ );
299
+ }
262
300
  function useRemoteApp(horizonContext, appId) {
263
301
  const sdkRef = useRef(null);
264
302
  if (!sdkRef.current) {
@@ -468,51 +506,15 @@ function moduleLoadError(appId, url, cause) {
468
506
  });
469
507
  }
470
508
 
471
- // src/anchors.ts
472
- var MANAGE_ANCHORS = {
473
- dashboard: "manage-dashboard",
474
- users: "manage-users",
475
- contacts: "manage-contacts",
476
- devices: "manage-devices",
477
- phoneNumbers: "manage-phone-numbers",
478
- callLogs: "manage-call-logs",
479
- voicemail: "manage-voicemail",
480
- fax: "manage-fax",
481
- settings: "manage-settings"
482
- };
483
- var PLATFORM_ANCHORS = {
484
- dashboard: "platform-dashboard",
485
- codeManagement: "platform-code-management",
486
- configManagement: "platform-config-management",
487
- sdkManagement: "platform-ui-sdk",
488
- branding: "platform-branding",
489
- recording: "platform-recording",
490
- logsAndDiagnostics: "platform-logs-and-diagnostics"
491
- };
492
- var APPS_ANCHORS = {
493
- home: "apps-home"
494
- };
495
- var MY_ACCOUNT_ANCHORS = {
496
- profile: "my-account-profile",
497
- preferences: "my-account-preferences",
498
- security: "my-account-security"
499
- };
500
- var ANCHORS = {
501
- manage: MANAGE_ANCHORS,
502
- platform: PLATFORM_ANCHORS,
503
- apps: APPS_ANCHORS,
504
- myAccount: MY_ACCOUNT_ANCHORS
505
- };
506
-
507
509
  // package.json
508
510
  var package_default = {
509
- version: "0.1.4"};
511
+ version: "0.1.6"};
510
512
 
511
513
  // src/index.ts
512
514
  var VERSION = package_default.version;
513
515
  var log2 = createLogger("FederationSDK");
514
516
  log2.info(`SDK v${VERSION} loaded`);
515
517
 
516
- export { ANCHORS, APPS_ANCHORS, HorizonContextProvider, HorizonSDKError, MANAGE_ANCHORS, MY_ACCOUNT_ANCHORS, PLATFORM_ANCHORS, RemoteAppSDK, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, usePageContext, useRemoteApp, useRoute, useRouteFromModule, useTheme };
518
+ export { HorizonContextProvider, HorizonSDKError, RemoteAppSDK, VERSION, apiError, createHorizonSDKError, createLogger, createRemoteAppSDK, getLogLevel, invalidExtensionPointError, moduleLoadError, permissionDeniedError, rateLimitError, setLogLevel, signatureVerificationError, useDynamicColumn, useDynamicExtension, useHorizonContext, useLocale, usePageContext, useRemoteApp, useRoute, useRouteFromModule, useSidePanel, useTheme };
517
519
  //# sourceMappingURL=index.js.map
518
520
  //# sourceMappingURL=index.js.map
package/dist/index.js.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":["log"],"mappings":"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","file":"index.js","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} 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\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 // 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.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} 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// 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.4\",\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} 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 // 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/RemoteAppSDK.ts","../src/hooks.tsx","../src/errors/HorizonSDKError.ts","../package.json","../src/index.ts"],"names":["log"],"mappings":"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","file":"index.js","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 \"name\": \"@netsapiens/horizon-sdk\",\n \"description\": \"SDK for building remote applications that integrate with NetSapiens Horizon\",\n \"version\": \"0.1.6\",\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\";\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"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netsapiens/horizon-sdk",
3
3
  "description": "SDK for building remote applications that integrate with NetSapiens Horizon",
4
- "version": "0.1.4",
4
+ "version": "0.1.6",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -40,8 +40,8 @@
40
40
  },
41
41
  "peerDependencies": {
42
42
  "loglevel": "^1.9.2",
43
- "react": "^18.0.0 || ^19.0.0",
44
- "react-dom": "^18.0.0 || ^19.0.0"
43
+ "react": "^19.0.0",
44
+ "react-dom": "^19.0.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@repo/eslint-config": "workspace:*",