@open-mercato/shared 0.6.6-develop.6089.1.a7ed560528 → 0.6.6-develop.6121.1.5797a901b3

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.
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.6.6-develop.6089.1.a7ed560528";
1
+ const APP_VERSION = "0.6.6-develop.6121.1.5797a901b3";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.6-develop.6089.1.a7ed560528'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.6-develop.6121.1.5797a901b3'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/modules/registry.ts"],
4
- "sourcesContent": ["import type { ReactNode } from 'react'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi/types'\nimport type { SyncCrudEventResult } from '../lib/crud/sync-event-types'\nimport type { DashboardWidgetModule } from './dashboard/widgets'\nimport type { InjectionAnyWidgetModule, ModuleInjectionTable } from './widgets/injection'\nimport type { IntegrationBundle, IntegrationDefinition } from './integrations/types'\nimport {\n applyApiOverridesToManifests,\n applyModuleOverridesToModules,\n applyPageOverridesToManifests,\n composeApiRouteOverrides,\n composePageRouteOverrides,\n} from './overrides'\n\n// Context passed to dynamic metadata guards\nexport type RouteVisibilityContext = { path?: string; auth?: any }\n\n/**\n * Portal sidebar navigation hint. When declared on a portal page's metadata,\n * the page is auto-listed in the portal sidebar (subject to RBAC) by the\n * `/api/customer_accounts/portal/nav` endpoint.\n *\n * Absence of `nav` means the page is routable but not auto-listed (useful for\n * detail pages, create forms, etc.).\n */\nexport type PortalNavMetadata = {\n label: string\n labelKey?: string\n group?: 'main' | 'account'\n order?: number\n icon?: string\n}\n\n// Metadata you can export from page.meta.ts or directly from a server page\nexport type PageMetadata = {\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: readonly string[]\n // Optional fine-grained feature requirements\n requireFeatures?: readonly string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: readonly string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n // Titles and grouping (aliases supported)\n title?: string\n titleKey?: string\n pageTitle?: string\n pageTitleKey?: string\n group?: string\n groupKey?: string\n pageGroup?: string\n pageGroupKey?: string\n // Ordering and visuals\n order?: number\n pageOrder?: number\n icon?: ReactNode\n navHidden?: boolean\n // Dynamic flags\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n // Optional static breadcrumb trail for header\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n // Navigation context for tiered navigation:\n // - 'main' (default): Main sidebar business operations\n // - 'admin': Collapsible \"Settings & Admin\" section at bottom of sidebar\n // - 'settings': Hidden from sidebar, only accessible via Settings hub page\n // - 'profile': Profile dropdown items\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nexport type ApiHandler = (req: Request, ctx?: any) => Promise<Response> | Response\n\nexport type ModuleSubscriberHandler = (\n payload: any,\n ctx: any\n) => Promise<void | SyncCrudEventResult> | void | SyncCrudEventResult\n\nexport type ModuleWorkerHandler = (job: unknown, ctx: unknown) => Promise<void> | void\n\nexport type ModuleRoute = {\n pattern?: string\n path?: string\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements\n requireFeatures?: string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n title?: string\n titleKey?: string\n group?: string\n groupKey?: string\n icon?: ReactNode\n order?: number\n priority?: number\n navHidden?: boolean\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n Component: (props: any) => ReactNode | Promise<ReactNode>\n}\n\nexport type ModuleApiLegacy = {\n method: HttpMethod\n path: string\n handler: ApiHandler\n metadata?: Record<string, unknown>\n docs?: OpenApiMethodDoc\n}\n\nexport type ModuleApiRouteFile = {\n path: string\n handlers: Partial<Record<HttpMethod, ApiHandler>>\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements for the entire route file\n // Note: per-method feature requirements should be expressed inside metadata\n requireFeatures?: string[]\n docs?: OpenApiRouteDoc\n metadata?: Partial<Record<HttpMethod, unknown>>\n}\n\nexport type ModuleApi = ModuleApiLegacy | ModuleApiRouteFile\n\nexport type RouteMatchParams = Record<string, string | string[]>\n\nexport type FrontendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type BackendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type ApiRouteManifestEntry = {\n moduleId: string\n kind: 'route-file' | 'legacy'\n path: string\n methods: HttpMethod[]\n method?: HttpMethod\n load: () => Promise<Record<string, unknown>>\n}\n\nexport type ModuleCli = {\n command: string\n run: (argv: string[]) => Promise<void> | void\n}\n\nexport type ModuleSubscriber = {\n id: string\n event: string\n persistent?: boolean\n sync?: boolean\n priority?: number\n handler: ModuleSubscriberHandler\n}\n\nexport type ModuleWorker = {\n id: string\n queue: string\n concurrency: number\n handler: ModuleWorkerHandler\n}\n\nexport type ModuleInfo = {\n name?: string\n title?: string\n version?: string\n description?: string\n author?: string\n license?: string\n homepage?: string\n copyright?: string\n // Optional hard dependencies: module ids that must be enabled\n requires?: string[]\n // Whether this module can be ejected into the app's src/modules/ for customization\n ejectable?: boolean\n}\n\nexport type ModuleDashboardWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<DashboardWidgetModule<any>>\n}\n\nexport type ModuleInjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<InjectionAnyWidgetModule<any, any>>\n}\n\nexport type Module = {\n id: string\n info?: ModuleInfo\n backendRoutes?: ModuleRoute[]\n frontendRoutes?: ModuleRoute[]\n apis?: ModuleApi[]\n cli?: ModuleCli[]\n translations?: Record<string, Record<string, string>>\n // Optional: per-module feature declarations discovered from acl.ts (module root)\n features?: Array<{ id: string; title: string; module: string }>\n // Auto-discovered event subscribers\n subscribers?: ModuleSubscriber[]\n // Auto-discovered queue workers\n workers?: ModuleWorker[]\n // Optional: per-module declared entity extensions and custom fields (static)\n // Extensions discovered from data/extensions.ts; Custom fields discovered from ce.ts (entities[].fields)\n entityExtensions?: import('./entities').EntityExtension[]\n customFieldSets?: import('./entities').CustomFieldSet[]\n // Optional: per-module declared custom entities (virtual/logical entities)\n // Discovered from ce.ts (module root). Each entry represents an entityId with optional label/description.\n customEntities?: Array<{ id: string; label?: string; description?: string }>\n dashboardWidgets?: ModuleDashboardWidgetEntry[]\n injectionWidgets?: ModuleInjectionWidgetEntry[]\n injectionTable?: ModuleInjectionTable\n // Optional: per-module vector search configuration (discovered from vector.ts)\n vector?: import('./vector').VectorModuleConfig\n // Optional: module-specific tenant setup configuration (from setup.ts)\n setup?: import('./setup').ModuleSetupConfig\n // Optional: default encryption maps owned by the module (from encryption.ts)\n defaultEncryptionMaps?: import('./encryption').ModuleEncryptionMap[]\n // Optional: integration marketplace declarations discovered from integration.ts\n integrations?: IntegrationDefinition[]\n bundles?: IntegrationBundle[]\n}\n\nfunction normPath(s: string) {\n return (s.startsWith('/') ? s : '/' + s).replace(/\\/+$/, '') || '/'\n}\n\n// 0 = literal (most specific), 1 = dynamic [param], 2 = catch-all [...param] or [[...param]]\nfunction segmentSpecificity(seg: string): 0 | 1 | 2 {\n if (seg.startsWith('[[...') || seg.startsWith('[...')) return 2\n if (seg.startsWith('[')) return 1\n return 0\n}\n\nfunction compareRouteSpecificity(aPattern: string, bPattern: string): number {\n const aSegs = aPattern.split('/')\n const bSegs = bPattern.split('/')\n const len = Math.max(aSegs.length, bSegs.length)\n for (let i = 0; i < len; i++) {\n const av = i < aSegs.length ? segmentSpecificity(aSegs[i]) : -1\n const bv = i < bSegs.length ? segmentSpecificity(bSegs[i]) : -1\n if (av !== bv) return av - bv\n }\n return 0\n}\n\nexport function sortRoutesBySpecificity<T extends { pattern?: string; path?: string }>(routes: T[]): T[] {\n return [...routes].sort((a, b) =>\n compareRouteSpecificity(a.pattern ?? a.path ?? '/', b.pattern ?? b.path ?? '/'),\n )\n}\n\n// Memoized per-array sorted view, so direct callers (e.g., the Next.js catch-all\n// routes that import generated `frontendRoutes`/`backendRoutes`/`apiRoutes`\n// arrays) match against a specificity-sorted view even if they never call\n// `register*RouteManifests`. Keyed by array reference; generated arrays are\n// module-level constants so this caches once per process.\nconst sortedRoutesCache = new WeakMap<object, readonly unknown[]>()\n\nfunction ensureSortedRoutes<T extends { pattern?: string; path?: string }>(routes: readonly T[]): readonly T[] {\n const cached = sortedRoutesCache.get(routes) as readonly T[] | undefined\n if (cached) return cached\n const sorted = sortRoutesBySpecificity([...routes])\n sortedRoutesCache.set(routes, sorted)\n return sorted\n}\n\nexport function matchRoutePattern(pattern: string, pathname: string): RouteMatchParams | undefined {\n const p = normPath(pattern)\n const u = normPath(pathname)\n const pSegs = p.split('/').slice(1)\n const uSegs = u.split('/').slice(1)\n const params: Record<string, string | string[]> = {}\n let i = 0\n for (let j = 0; j < pSegs.length; j++, i++) {\n const seg = pSegs[j]\n const mCatchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/)\n const mOptCatch = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/)\n const mDyn = seg.match(/^\\[(.+)\\]$/)\n if (mCatchAll) {\n const key = mCatchAll[1]\n if (i >= uSegs.length) return undefined\n params[key] = uSegs.slice(i)\n i = uSegs.length\n return i === uSegs.length ? params : undefined\n } else if (mOptCatch) {\n const key = mOptCatch[1]\n params[key] = i < uSegs.length ? uSegs.slice(i) : []\n i = uSegs.length\n return params\n } else if (mDyn) {\n if (i >= uSegs.length) return undefined\n params[mDyn[1]] = uSegs[i]\n } else {\n if (i >= uSegs.length || uSegs[i].toLowerCase() !== seg.toLowerCase()) return undefined\n }\n }\n if (i !== uSegs.length) return undefined\n return params\n}\n\nfunction getPattern(r: ModuleRoute) {\n return r.pattern ?? r.path ?? '/'\n}\n\nexport function findFrontendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.frontendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findBackendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.backendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findApi(modules: Module[], method: HttpMethod, pathname: string): { handler: ApiHandler; params: Record<string, string | string[]>; requireAuth?: boolean; requireRoles?: string[]; metadata?: any } | undefined {\n for (const m of modules) {\n const apis = m.apis ?? []\n for (const a of apis) {\n if ('handlers' in a) {\n const params = matchRoutePattern(a.path, pathname)\n const handler = (a.handlers as any)[method]\n if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }\n } else {\n const al = a as ModuleApiLegacy\n if (al.method !== method) continue\n const params = matchRoutePattern(al.path, pathname)\n if (params) {\n return { handler: al.handler, params, metadata: al.metadata }\n }\n }\n }\n }\n}\n\nexport function findRouteManifestMatch<T extends { pattern?: string; path?: string }>(\n routes: T[],\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n const params = matchRoutePattern(route.pattern ?? route.path ?? '/', pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nexport function findApiRouteManifestMatch<T extends { path: string; methods: HttpMethod[] }>(\n routes: T[],\n method: HttpMethod,\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n if (!route.methods.includes(method)) continue\n const params = matchRoutePattern(route.path, pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nlet _backendRouteManifests: BackendRouteManifestEntry[] | null = null\n\nexport function registerBackendRouteManifests(routes: BackendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'backend')\n _backendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getBackendRouteManifests(): BackendRouteManifestEntry[] {\n return _backendRouteManifests ?? []\n}\n\nlet _frontendRouteManifests: FrontendRouteManifestEntry[] | null = null\n\nexport function registerFrontendRouteManifests(routes: FrontendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'frontend')\n _frontendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getFrontendRouteManifests(): FrontendRouteManifestEntry[] {\n return _frontendRouteManifests ?? []\n}\n\nlet _apiRouteManifests: ApiRouteManifestEntry[] | null = null\n\nexport function registerApiRouteManifests(routes: ApiRouteManifestEntry[]) {\n // Apply any `entry.overrides.routes.api` overrides registered through the\n // unified `modules.ts` dispatcher or programmatic API before storing the\n // manifest. The composer is cheap and returns an empty object when no\n // overrides exist, so this is a no-op for apps that do not opt in.\n const routeOverrides = composeApiRouteOverrides()\n const finalRoutes = Object.keys(routeOverrides).length === 0\n ? routes\n : applyApiOverridesToManifests(routes, routeOverrides)\n _apiRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getApiRouteManifests(): ApiRouteManifestEntry[] {\n return _apiRouteManifests ?? []\n}\n\n// CLI modules registry - shared between CLI and module workers\nlet _cliModules: Module[] | null = null\n\nexport function registerCliModules(modules: Module[]) {\n if (_cliModules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] CLI modules re-registered (this may occur during HMR)')\n }\n _cliModules = applyModuleOverridesToModules(modules)\n}\n\nexport function getCliModules(): Module[] {\n // Return empty array if not registered - allows generate command to work without bootstrap\n return _cliModules ?? []\n}\n\nexport function hasCliModules(): boolean {\n return _cliModules !== null && _cliModules.length > 0\n}\n\nexport function getDefaultEncryptionMaps(modules: Module[]): import('./encryption').ModuleEncryptionMap[] {\n const byEntityId = new Map<string, { moduleId: string; map: import('./encryption').ModuleEncryptionMap }>()\n\n for (const mod of modules) {\n for (const entry of mod.defaultEncryptionMaps ?? []) {\n const previous = byEntityId.get(entry.entityId)\n if (previous) {\n throw new Error(\n `[registry] Duplicate default encryption map for \"${entry.entityId}\" declared by \"${previous.moduleId}\" and \"${mod.id}\"`\n )\n }\n byEntityId.set(entry.entityId, {\n moduleId: mod.id,\n map: {\n entityId: entry.entityId,\n fields: entry.fields.map((field) => ({\n field: field.field,\n hashField: field.hashField ?? null,\n })),\n },\n })\n }\n }\n\n return Array.from(byEntityId.values(), ({ map }) => map)\n}\n\nfunction ensureLazyHandler<T extends (...args: any[]) => any>(\n loaded: unknown,\n kind: 'subscriber' | 'worker',\n id: string\n): T {\n const handler = typeof loaded === 'function'\n ? loaded\n : loaded && typeof loaded === 'object' && 'default' in loaded\n ? (loaded as Record<string, unknown>).default\n : null\n if (typeof handler !== 'function') {\n throw new Error(`[registry] Invalid ${kind} module \"${id}\" (missing default export handler)`)\n }\n return handler as T\n}\n\nexport function createLazyModuleSubscriber(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleSubscriberHandler {\n let handlerPromise: Promise<ModuleSubscriberHandler> | null = null\n return async (payload, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleSubscriberHandler>(loaded, 'subscriber', id)\n )\n const handler = await handlerPromise\n return handler(payload, ctx)\n }\n}\n\nexport function createLazyModuleWorker(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleWorkerHandler {\n let handlerPromise: Promise<ModuleWorkerHandler> | null = null\n return async (job, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleWorkerHandler>(loaded, 'worker', id)\n )\n const handler = await handlerPromise\n return handler(job, ctx)\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ReactNode } from 'react'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi/types'\nimport type { SyncCrudEventResult } from '../lib/crud/sync-event-types'\nimport type { DashboardWidgetModule } from './dashboard/widgets'\nimport type { InjectionAnyWidgetModule, ModuleInjectionTable } from './widgets/injection'\nimport type { IntegrationBundle, IntegrationDefinition } from './integrations/types'\nimport {\n applyApiOverridesToManifests,\n applyModuleOverridesToModules,\n applyPageOverridesToManifests,\n composeApiRouteOverrides,\n composePageRouteOverrides,\n} from './overrides'\n\n// Context passed to dynamic metadata guards\nexport type RouteVisibilityContext = { path?: string; auth?: any }\n\n/**\n * Portal sidebar navigation hint. When declared on a portal page's metadata,\n * the page is auto-listed in the portal sidebar (subject to RBAC) by the\n * `/api/customer_accounts/portal/nav` endpoint.\n *\n * Absence of `nav` means the page is routable but not auto-listed (useful for\n * detail pages, create forms, etc.).\n */\nexport type PortalNavMetadata = {\n label: string\n labelKey?: string\n group?: 'main' | 'account'\n order?: number\n icon?: string\n}\n\n// Metadata you can export from page.meta.ts or directly from a server page\nexport type PageMetadata = {\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: readonly string[]\n // Optional fine-grained feature requirements\n requireFeatures?: readonly string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: readonly string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n // Titles and grouping (aliases supported)\n title?: string\n titleKey?: string\n pageTitle?: string\n pageTitleKey?: string\n group?: string\n groupKey?: string\n pageGroup?: string\n pageGroupKey?: string\n // Ordering and visuals\n order?: number\n pageOrder?: number\n icon?: ReactNode\n navHidden?: boolean\n // Dynamic flags\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n // Optional static breadcrumb trail for header\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n // Navigation context for tiered navigation:\n // - 'main' (default): Main sidebar business operations\n // - 'admin': Collapsible \"Settings & Admin\" section at bottom of sidebar\n // - 'settings': Hidden from sidebar, only accessible via Settings hub page\n // - 'profile': Profile dropdown items\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nexport type ApiHandler = (req: Request, ctx?: any) => Promise<Response> | Response\n\nexport type ModuleSubscriberHandler = (\n payload: any,\n ctx: any\n) => Promise<void | SyncCrudEventResult> | void | SyncCrudEventResult\n\nexport type ModuleWorkerHandler = (job: unknown, ctx: unknown) => Promise<void> | void\n\nexport type ModuleRoute = {\n pattern?: string\n path?: string\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements\n requireFeatures?: string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n title?: string\n titleKey?: string\n group?: string\n groupKey?: string\n icon?: ReactNode\n order?: number\n priority?: number\n navHidden?: boolean\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n Component: (props: any) => ReactNode | Promise<ReactNode>\n}\n\nexport type ModuleApiLegacy = {\n method: HttpMethod\n path: string\n handler: ApiHandler\n metadata?: Record<string, unknown>\n docs?: OpenApiMethodDoc\n}\n\nexport type ModuleApiRouteFile = {\n path: string\n handlers: Partial<Record<HttpMethod, ApiHandler>>\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements for the entire route file\n // Note: per-method feature requirements should be expressed inside metadata\n requireFeatures?: string[]\n docs?: OpenApiRouteDoc\n metadata?: Partial<Record<HttpMethod, unknown>>\n}\n\nexport type ModuleApi = ModuleApiLegacy | ModuleApiRouteFile\n\nexport type RouteMatchParams = Record<string, string | string[]>\n\nexport type FrontendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type BackendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type ApiRouteManifestEntry = {\n moduleId: string\n kind: 'route-file' | 'legacy'\n path: string\n methods: HttpMethod[]\n method?: HttpMethod\n load: () => Promise<Record<string, unknown>>\n}\n\nexport type ModuleCli = {\n command: string\n run: (argv: string[]) => Promise<void> | void\n}\n\nexport type ModuleSubscriber = {\n id: string\n event: string\n persistent?: boolean\n sync?: boolean\n priority?: number\n handler: ModuleSubscriberHandler\n}\n\nexport type ModuleWorker = {\n id: string\n queue: string\n concurrency: number\n handler: ModuleWorkerHandler\n}\n\nexport type ModuleInfo = {\n name?: string\n title?: string\n version?: string\n description?: string\n author?: string\n license?: string\n homepage?: string\n copyright?: string\n // Optional hard dependencies: module ids that must be enabled\n requires?: string[]\n // Whether this module can be ejected into the app's src/modules/ for customization\n ejectable?: boolean\n}\n\nexport type ModuleDashboardWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<DashboardWidgetModule<any>>\n}\n\nexport type ModuleInjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n widgetId?: string\n loader: () => Promise<InjectionAnyWidgetModule<any, any>>\n}\n\nexport type Module = {\n id: string\n info?: ModuleInfo\n backendRoutes?: ModuleRoute[]\n frontendRoutes?: ModuleRoute[]\n apis?: ModuleApi[]\n cli?: ModuleCli[]\n translations?: Record<string, Record<string, string>>\n // Optional: per-module feature declarations discovered from acl.ts (module root)\n features?: Array<{ id: string; title: string; module: string }>\n // Auto-discovered event subscribers\n subscribers?: ModuleSubscriber[]\n // Auto-discovered queue workers\n workers?: ModuleWorker[]\n // Optional: per-module declared entity extensions and custom fields (static)\n // Extensions discovered from data/extensions.ts; Custom fields discovered from ce.ts (entities[].fields)\n entityExtensions?: import('./entities').EntityExtension[]\n customFieldSets?: import('./entities').CustomFieldSet[]\n // Optional: per-module declared custom entities (virtual/logical entities)\n // Discovered from ce.ts (module root). Each entry represents an entityId with optional label/description.\n customEntities?: Array<{ id: string; label?: string; description?: string }>\n dashboardWidgets?: ModuleDashboardWidgetEntry[]\n injectionWidgets?: ModuleInjectionWidgetEntry[]\n injectionTable?: ModuleInjectionTable\n // Optional: per-module vector search configuration (discovered from vector.ts)\n vector?: import('./vector').VectorModuleConfig\n // Optional: module-specific tenant setup configuration (from setup.ts)\n setup?: import('./setup').ModuleSetupConfig\n // Optional: default encryption maps owned by the module (from encryption.ts)\n defaultEncryptionMaps?: import('./encryption').ModuleEncryptionMap[]\n // Optional: integration marketplace declarations discovered from integration.ts\n integrations?: IntegrationDefinition[]\n bundles?: IntegrationBundle[]\n}\n\nfunction normPath(s: string) {\n return (s.startsWith('/') ? s : '/' + s).replace(/\\/+$/, '') || '/'\n}\n\n// 0 = literal (most specific), 1 = dynamic [param], 2 = catch-all [...param] or [[...param]]\nfunction segmentSpecificity(seg: string): 0 | 1 | 2 {\n if (seg.startsWith('[[...') || seg.startsWith('[...')) return 2\n if (seg.startsWith('[')) return 1\n return 0\n}\n\nfunction compareRouteSpecificity(aPattern: string, bPattern: string): number {\n const aSegs = aPattern.split('/')\n const bSegs = bPattern.split('/')\n const len = Math.max(aSegs.length, bSegs.length)\n for (let i = 0; i < len; i++) {\n const av = i < aSegs.length ? segmentSpecificity(aSegs[i]) : -1\n const bv = i < bSegs.length ? segmentSpecificity(bSegs[i]) : -1\n if (av !== bv) return av - bv\n }\n return 0\n}\n\nexport function sortRoutesBySpecificity<T extends { pattern?: string; path?: string }>(routes: T[]): T[] {\n return [...routes].sort((a, b) =>\n compareRouteSpecificity(a.pattern ?? a.path ?? '/', b.pattern ?? b.path ?? '/'),\n )\n}\n\n// Memoized per-array sorted view, so direct callers (e.g., the Next.js catch-all\n// routes that import generated `frontendRoutes`/`backendRoutes`/`apiRoutes`\n// arrays) match against a specificity-sorted view even if they never call\n// `register*RouteManifests`. Keyed by array reference; generated arrays are\n// module-level constants so this caches once per process.\nconst sortedRoutesCache = new WeakMap<object, readonly unknown[]>()\n\nfunction ensureSortedRoutes<T extends { pattern?: string; path?: string }>(routes: readonly T[]): readonly T[] {\n const cached = sortedRoutesCache.get(routes) as readonly T[] | undefined\n if (cached) return cached\n const sorted = sortRoutesBySpecificity([...routes])\n sortedRoutesCache.set(routes, sorted)\n return sorted\n}\n\nexport function matchRoutePattern(pattern: string, pathname: string): RouteMatchParams | undefined {\n const p = normPath(pattern)\n const u = normPath(pathname)\n const pSegs = p.split('/').slice(1)\n const uSegs = u.split('/').slice(1)\n const params: Record<string, string | string[]> = {}\n let i = 0\n for (let j = 0; j < pSegs.length; j++, i++) {\n const seg = pSegs[j]\n const mCatchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/)\n const mOptCatch = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/)\n const mDyn = seg.match(/^\\[(.+)\\]$/)\n if (mCatchAll) {\n const key = mCatchAll[1]\n if (i >= uSegs.length) return undefined\n params[key] = uSegs.slice(i)\n i = uSegs.length\n return i === uSegs.length ? params : undefined\n } else if (mOptCatch) {\n const key = mOptCatch[1]\n params[key] = i < uSegs.length ? uSegs.slice(i) : []\n i = uSegs.length\n return params\n } else if (mDyn) {\n if (i >= uSegs.length) return undefined\n params[mDyn[1]] = uSegs[i]\n } else {\n if (i >= uSegs.length || uSegs[i].toLowerCase() !== seg.toLowerCase()) return undefined\n }\n }\n if (i !== uSegs.length) return undefined\n return params\n}\n\nfunction getPattern(r: ModuleRoute) {\n return r.pattern ?? r.path ?? '/'\n}\n\nexport function findFrontendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.frontendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findBackendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.backendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findApi(modules: Module[], method: HttpMethod, pathname: string): { handler: ApiHandler; params: Record<string, string | string[]>; requireAuth?: boolean; requireRoles?: string[]; metadata?: any } | undefined {\n for (const m of modules) {\n const apis = m.apis ?? []\n for (const a of apis) {\n if ('handlers' in a) {\n const params = matchRoutePattern(a.path, pathname)\n const handler = (a.handlers as any)[method]\n if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }\n } else {\n const al = a as ModuleApiLegacy\n if (al.method !== method) continue\n const params = matchRoutePattern(al.path, pathname)\n if (params) {\n return { handler: al.handler, params, metadata: al.metadata }\n }\n }\n }\n }\n}\n\nexport function findRouteManifestMatch<T extends { pattern?: string; path?: string }>(\n routes: T[],\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n const params = matchRoutePattern(route.pattern ?? route.path ?? '/', pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nexport function findApiRouteManifestMatch<T extends { path: string; methods: HttpMethod[] }>(\n routes: T[],\n method: HttpMethod,\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n if (!route.methods.includes(method)) continue\n const params = matchRoutePattern(route.path, pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nlet _backendRouteManifests: BackendRouteManifestEntry[] | null = null\n\nexport function registerBackendRouteManifests(routes: BackendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'backend')\n _backendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getBackendRouteManifests(): BackendRouteManifestEntry[] {\n return _backendRouteManifests ?? []\n}\n\nlet _frontendRouteManifests: FrontendRouteManifestEntry[] | null = null\n\nexport function registerFrontendRouteManifests(routes: FrontendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'frontend')\n _frontendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getFrontendRouteManifests(): FrontendRouteManifestEntry[] {\n return _frontendRouteManifests ?? []\n}\n\nlet _apiRouteManifests: ApiRouteManifestEntry[] | null = null\n\nexport function registerApiRouteManifests(routes: ApiRouteManifestEntry[]) {\n // Apply any `entry.overrides.routes.api` overrides registered through the\n // unified `modules.ts` dispatcher or programmatic API before storing the\n // manifest. The composer is cheap and returns an empty object when no\n // overrides exist, so this is a no-op for apps that do not opt in.\n const routeOverrides = composeApiRouteOverrides()\n const finalRoutes = Object.keys(routeOverrides).length === 0\n ? routes\n : applyApiOverridesToManifests(routes, routeOverrides)\n _apiRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getApiRouteManifests(): ApiRouteManifestEntry[] {\n return _apiRouteManifests ?? []\n}\n\n// CLI modules registry - shared between CLI and module workers\nlet _cliModules: Module[] | null = null\n\nexport function registerCliModules(modules: Module[]) {\n if (_cliModules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] CLI modules re-registered (this may occur during HMR)')\n }\n _cliModules = applyModuleOverridesToModules(modules)\n}\n\nexport function getCliModules(): Module[] {\n // Return empty array if not registered - allows generate command to work without bootstrap\n return _cliModules ?? []\n}\n\nexport function hasCliModules(): boolean {\n return _cliModules !== null && _cliModules.length > 0\n}\n\nexport function getDefaultEncryptionMaps(modules: Module[]): import('./encryption').ModuleEncryptionMap[] {\n const byEntityId = new Map<string, { moduleId: string; map: import('./encryption').ModuleEncryptionMap }>()\n\n for (const mod of modules) {\n for (const entry of mod.defaultEncryptionMaps ?? []) {\n const previous = byEntityId.get(entry.entityId)\n if (previous) {\n throw new Error(\n `[registry] Duplicate default encryption map for \"${entry.entityId}\" declared by \"${previous.moduleId}\" and \"${mod.id}\"`\n )\n }\n byEntityId.set(entry.entityId, {\n moduleId: mod.id,\n map: {\n entityId: entry.entityId,\n fields: entry.fields.map((field) => ({\n field: field.field,\n hashField: field.hashField ?? null,\n })),\n },\n })\n }\n }\n\n return Array.from(byEntityId.values(), ({ map }) => map)\n}\n\nfunction ensureLazyHandler<T extends (...args: any[]) => any>(\n loaded: unknown,\n kind: 'subscriber' | 'worker',\n id: string\n): T {\n const handler = typeof loaded === 'function'\n ? loaded\n : loaded && typeof loaded === 'object' && 'default' in loaded\n ? (loaded as Record<string, unknown>).default\n : null\n if (typeof handler !== 'function') {\n throw new Error(`[registry] Invalid ${kind} module \"${id}\" (missing default export handler)`)\n }\n return handler as T\n}\n\nexport function createLazyModuleSubscriber(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleSubscriberHandler {\n let handlerPromise: Promise<ModuleSubscriberHandler> | null = null\n return async (payload, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleSubscriberHandler>(loaded, 'subscriber', id)\n )\n const handler = await handlerPromise\n return handler(payload, ctx)\n }\n}\n\nexport function createLazyModuleWorker(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleWorkerHandler {\n let handlerPromise: Promise<ModuleWorkerHandler> | null = null\n return async (job, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleWorkerHandler>(loaded, 'worker', id)\n )\n const handler = await handlerPromise\n return handler(job, ctx)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -155,12 +155,14 @@ function getCoreInjectionTables() {
155
155
  }
156
156
  let widgetEntriesPromise = null;
157
157
  let injectionTablePromise = null;
158
+ let widgetLookupIndexPromise = null;
158
159
  function isInjectionSlotObject(value) {
159
160
  return typeof value === "object" && value !== null && "widgetId" in value;
160
161
  }
161
162
  function invalidateInjectionWidgetCache() {
162
163
  widgetEntriesPromise = null;
163
164
  injectionTablePromise = null;
165
+ widgetLookupIndexPromise = null;
164
166
  widgetCache.clear();
165
167
  warnedRequiredModuleSkips.clear();
166
168
  }
@@ -278,11 +280,58 @@ function isLoadedInjectionDataWidget(module) {
278
280
  }
279
281
  async function loadEntry(entry) {
280
282
  if (!widgetCache.has(entry.key)) {
281
- const promise = entry.loader().then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId));
283
+ const promise = Promise.resolve().then(() => entry.loader()).then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId));
282
284
  widgetCache.set(entry.key, promise);
283
285
  }
284
286
  return widgetCache.get(entry.key);
285
287
  }
288
+ async function loadWidgetLookupIndex() {
289
+ const version = getInjectionRegistryVersion();
290
+ if (!widgetLookupIndexPromise || widgetLookupIndexPromise.version !== version) {
291
+ const promise = Promise.resolve().then(async () => {
292
+ const widgetEntries = await loadWidgetEntries();
293
+ const settled = await Promise.allSettled(widgetEntries.map((entry) => loadEntry(entry)));
294
+ const widgetsById = /* @__PURE__ */ new Map();
295
+ const dataWidgetsById = /* @__PURE__ */ new Map();
296
+ settled.forEach((result, index) => {
297
+ if (result.status !== "fulfilled") return;
298
+ const entry = widgetEntries[index];
299
+ const module = result.value;
300
+ if (isLoadedInjectionWidget(module)) {
301
+ if (!widgetsById.has(module.metadata.id)) {
302
+ widgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key });
303
+ }
304
+ return;
305
+ }
306
+ if (!dataWidgetsById.has(module.metadata.id)) {
307
+ dataWidgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key });
308
+ }
309
+ });
310
+ return { widgetsById, dataWidgetsById };
311
+ });
312
+ widgetLookupIndexPromise = { version, promise };
313
+ }
314
+ return widgetLookupIndexPromise.promise;
315
+ }
316
+ function applyRequiredModuleGate(widget, enabledModuleIds) {
317
+ const missing = widgetMissingRequiredModules(widget.metadata, enabledModuleIds);
318
+ if (missing.length > 0) {
319
+ warnSkippedWidget(widget.metadata.id, missing);
320
+ return null;
321
+ }
322
+ return widget;
323
+ }
324
+ async function tryLoadHintedWidgetById(widgetId, isExpectedKind, enabledModuleIds) {
325
+ const widgetEntries = await loadWidgetEntries();
326
+ const entry = widgetEntries.find((candidate) => candidate.widgetId === widgetId);
327
+ if (!entry) return { resolved: false };
328
+ const module = await loadEntry(entry).catch(() => null);
329
+ if (!module || module.metadata.id !== widgetId || !isExpectedKind(module)) {
330
+ return { resolved: false };
331
+ }
332
+ const widget = { ...module, moduleId: entry.moduleId, key: entry.key };
333
+ return { resolved: true, widget: applyRequiredModuleGate(widget, enabledModuleIds) };
334
+ }
286
335
  function getEnabledModuleIdsForInjection() {
287
336
  const explicit = readGlobalEnabledModuleIds() ?? _enabledModuleIds;
288
337
  if (explicit) return explicit;
@@ -363,38 +412,20 @@ async function loadAllInjectionWidgets() {
363
412
  return Array.from(byId.values());
364
413
  }
365
414
  async function loadInjectionWidgetById(widgetId) {
366
- const widgetEntries = await loadWidgetEntries();
367
415
  const enabledModuleIds = getEnabledModuleIdsForInjection();
368
- for (const entry of widgetEntries) {
369
- const module = await loadEntry(entry);
370
- if (!isLoadedInjectionWidget(module)) continue;
371
- if (module.metadata.id === widgetId) {
372
- const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds);
373
- if (missing.length > 0) {
374
- warnSkippedWidget(module.metadata.id, missing);
375
- return null;
376
- }
377
- return { ...module, moduleId: entry.moduleId, key: entry.key };
378
- }
379
- }
380
- return null;
416
+ const hinted = await tryLoadHintedWidgetById(widgetId, isLoadedInjectionWidget, enabledModuleIds);
417
+ if (hinted.resolved) return hinted.widget;
418
+ const index = await loadWidgetLookupIndex();
419
+ const widget = index.widgetsById.get(widgetId);
420
+ return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null;
381
421
  }
382
422
  async function loadInjectionDataWidgetById(widgetId) {
383
- const widgetEntries = await loadWidgetEntries();
384
423
  const enabledModuleIds = getEnabledModuleIdsForInjection();
385
- for (const entry of widgetEntries) {
386
- const module = await loadEntry(entry);
387
- if (!isLoadedInjectionDataWidget(module)) continue;
388
- if (module.metadata.id === widgetId) {
389
- const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds);
390
- if (missing.length > 0) {
391
- warnSkippedWidget(module.metadata.id, missing);
392
- return null;
393
- }
394
- return { ...module, moduleId: entry.moduleId, key: entry.key };
395
- }
396
- }
397
- return null;
424
+ const hinted = await tryLoadHintedWidgetById(widgetId, isLoadedInjectionDataWidget, enabledModuleIds);
425
+ if (hinted.resolved) return hinted.widget;
426
+ const index = await loadWidgetLookupIndex();
427
+ const widget = index.dataWidgetsById.get(widgetId);
428
+ return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null;
398
429
  }
399
430
  async function loadInjectionWidgetsForSpot(spotId) {
400
431
  const entries = await getResolvedEntriesForSpot(spotId);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/widgets/injection-loader.ts"],
4
- "sourcesContent": ["import type { ModuleInjectionWidgetEntry } from '../registry'\nimport { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'\nimport type {\n InjectionAnyWidgetModule,\n InjectionDataWidgetModule,\n InjectionWidgetMetadata,\n InjectionWidgetModule,\n InjectionSpotId,\n ModuleInjectionSlot,\n ModuleInjectionTable,\n InjectionWidgetPlacement,\n} from './injection'\nimport {\n applyInjectionWidgetOverridesToEntries,\n applyInjectionWidgetOverridesToTables,\n} from '../overrides'\n\ntype LoadedWidgetModule = InjectionWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }\ntype LoadedDataWidgetModule = InjectionDataWidgetModule & { metadata: InjectionWidgetMetadata }\n\nexport type LoadedInjectionWidget = LoadedWidgetModule & {\n moduleId: string\n key: string\n placement?: {\n groupId?: string\n groupLabel?: string\n groupDescription?: string\n column?: 1 | 2\n kind?: 'tab' | 'group' | 'stack'\n [k: string]: unknown\n }\n}\n\nexport type LoadedInjectionDataWidget = LoadedDataWidgetModule & {\n moduleId: string\n key: string\n placement?: {\n groupId?: string\n groupLabel?: string\n groupDescription?: string\n column?: 1 | 2\n kind?: 'tab' | 'group' | 'stack'\n [k: string]: unknown\n }\n}\n\ntype WidgetEntry = ModuleInjectionWidgetEntry & { moduleId: string }\n\n// Registration pattern for publishable packages\nlet _coreInjectionWidgetEntries: ModuleInjectionWidgetEntry[] | null = null\nlet _coreInjectionTables: Array<{ moduleId: string; table: ModuleInjectionTable }> | null = null\nlet _enabledModuleIds: ReadonlySet<string> | null = null\nlet _injectionRegistryVersion = 0\nconst GLOBAL_INJECTION_WIDGETS_KEY = '__openMercatoCoreInjectionWidgetEntries__'\nconst GLOBAL_INJECTION_TABLES_KEY = '__openMercatoCoreInjectionTables__'\nconst GLOBAL_ENABLED_MODULE_IDS_KEY = '__openMercatoEnabledModuleIds__'\nconst GLOBAL_INJECTION_REGISTRY_VERSION_KEY = '__openMercatoCoreInjectionRegistryVersion__'\nconst INJECTION_REGISTRY_CHANGED_EVENT = '__openMercatoInjectionRegistryChanged__'\n\nfunction readGlobalInjectionWidgets(): ModuleInjectionWidgetEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INJECTION_WIDGETS_KEY]\n return Array.isArray(value) ? (value as ModuleInjectionWidgetEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInjectionWidgets(entries: ModuleInjectionWidgetEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INJECTION_WIDGETS_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction readGlobalEnabledModuleIds(): ReadonlySet<string> | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_ENABLED_MODULE_IDS_KEY]\n if (value instanceof Set) return value as ReadonlySet<string>\n return null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalEnabledModuleIds(ids: ReadonlySet<string>) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_ENABLED_MODULE_IDS_KEY] = ids\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction readGlobalInjectionTables(): Array<{ moduleId: string; table: ModuleInjectionTable }> | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INJECTION_TABLES_KEY]\n return Array.isArray(value) ? (value as Array<{ moduleId: string; table: ModuleInjectionTable }>) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInjectionTables(tables: Array<{ moduleId: string; table: ModuleInjectionTable }>) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INJECTION_TABLES_KEY] = tables\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction readGlobalInjectionRegistryVersion(): number | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INJECTION_REGISTRY_VERSION_KEY]\n return typeof value === 'number' ? value : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInjectionRegistryVersion(version: number) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INJECTION_REGISTRY_VERSION_KEY] = version\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction notifyInjectionRegistryChanged() {\n _injectionRegistryVersion += 1\n writeGlobalInjectionRegistryVersion(_injectionRegistryVersion)\n invalidateInjectionWidgetCache()\n\n if (typeof window === 'undefined') return\n\n window.dispatchEvent(new CustomEvent(INJECTION_REGISTRY_CHANGED_EVENT, {\n detail: { version: _injectionRegistryVersion },\n }))\n}\n\nexport function registerCoreInjectionWidgets(entries: ModuleInjectionWidgetEntry[]) {\n if (_coreInjectionWidgetEntries !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Core injection widgets re-registered (this may occur during HMR)')\n }\n const finalEntries = applyInjectionWidgetOverridesToEntries(entries)\n _coreInjectionWidgetEntries = finalEntries\n writeGlobalInjectionWidgets(finalEntries)\n notifyInjectionRegistryChanged()\n}\n\nexport function getCoreInjectionWidgets(): ModuleInjectionWidgetEntry[] {\n const globalEntries = readGlobalInjectionWidgets()\n if (globalEntries) return globalEntries\n if (!_coreInjectionWidgetEntries) {\n // On client-side, bootstrap doesn't run - return empty array gracefully\n if (typeof window !== 'undefined') {\n return []\n }\n throw new Error('[Bootstrap] Core injection widgets not registered. Call registerCoreInjectionWidgets() at bootstrap.')\n }\n return _coreInjectionWidgetEntries\n}\n\nexport function registerCoreInjectionTables(tables: Array<{ moduleId: string; table: ModuleInjectionTable }>) {\n if (_coreInjectionTables !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Core injection tables re-registered (this may occur during HMR)')\n }\n const finalTables = applyInjectionWidgetOverridesToTables(tables)\n _coreInjectionTables = finalTables\n writeGlobalInjectionTables(finalTables)\n notifyInjectionRegistryChanged()\n}\n\n/**\n * Register the canonical set of enabled module IDs for the running app.\n *\n * This is the authoritative signal used by `requiredModules` widget gating \u2014\n * deriving \"enabled\" from injection tables or widget entries is unreliable\n * because modules without injection widgets (for example `ai_assistant`) do\n * not contribute entries to either source. Bootstrap callers should pass\n * every module ID present in the app's module registry.\n */\nexport function registerEnabledModuleIds(moduleIds: Iterable<string>) {\n const next = new Set<string>()\n for (const moduleId of moduleIds) {\n if (typeof moduleId === 'string' && moduleId.length > 0) next.add(moduleId)\n }\n if (_enabledModuleIds !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Enabled module IDs re-registered (this may occur during HMR)')\n }\n _enabledModuleIds = next\n writeGlobalEnabledModuleIds(next)\n notifyInjectionRegistryChanged()\n}\n\nexport function getEnabledModuleIds(): ReadonlySet<string> | null {\n return readGlobalEnabledModuleIds() ?? _enabledModuleIds\n}\n\nexport function getInjectionRegistryVersion(): number {\n const globalVersion = readGlobalInjectionRegistryVersion()\n if (globalVersion !== null) return globalVersion\n return _injectionRegistryVersion\n}\n\nexport function subscribeToInjectionRegistryChanges(listener: () => void): () => void {\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n const handler = () => listener()\n window.addEventListener(INJECTION_REGISTRY_CHANGED_EVENT, handler)\n return () => {\n window.removeEventListener(INJECTION_REGISTRY_CHANGED_EVENT, handler)\n }\n}\n\nexport function getCoreInjectionTables(): Array<{ moduleId: string; table: ModuleInjectionTable }> {\n const globalTables = readGlobalInjectionTables()\n if (globalTables) return globalTables\n if (!_coreInjectionTables) {\n // On client-side, bootstrap doesn't run - return empty array gracefully\n if (typeof window !== 'undefined') {\n return []\n }\n throw new Error('[Bootstrap] Core injection tables not registered. Call registerCoreInjectionTables() at bootstrap.')\n }\n return _coreInjectionTables\n}\n\nlet widgetEntriesPromise: Promise<WidgetEntry[]> | null = null\ntype TableEntry = {\n widgetId: string\n moduleId: string\n priority: number\n placement?: ModuleInjectionSlot extends infer S\n ? S extends { widgetId: string }\n ? Omit<S, 'widgetId' | 'priority'>\n : never\n : never\n}\nlet injectionTablePromise: Promise<Map<InjectionSpotId, TableEntry[]>> | null = null\n\nfunction isInjectionSlotObject(value: ModuleInjectionSlot): value is InjectionWidgetPlacement & { widgetId: string; priority?: number } {\n return typeof value === 'object' && value !== null && 'widgetId' in value\n}\n\n/**\n * Invalidate the widget entries and widget module cache.\n * Call this when the generated registry is updated or modules are reloaded.\n */\nexport function invalidateInjectionWidgetCache() {\n widgetEntriesPromise = null\n injectionTablePromise = null\n widgetCache.clear()\n warnedRequiredModuleSkips.clear()\n}\n\nasync function loadWidgetEntries(): Promise<WidgetEntry[]> {\n if (!widgetEntriesPromise) {\n const promise = Promise.resolve().then(() =>\n getCoreInjectionWidgets().map((entry) => ({\n ...entry,\n moduleId: entry.moduleId || 'unknown',\n }))\n )\n widgetEntriesPromise = promise.catch((err) => {\n if (widgetEntriesPromise === promise) {\n widgetEntriesPromise = null\n }\n throw err\n })\n }\n return widgetEntriesPromise\n}\n\nasync function loadInjectionTable(): Promise<Map<InjectionSpotId, TableEntry[]>> {\n if (!injectionTablePromise) {\n const promise = Promise.resolve().then(() => {\n const list = getCoreInjectionTables()\n const table = new Map<InjectionSpotId, TableEntry[]>()\n\n for (const entry of list) {\n const injectionTable = entry.table ?? {}\n for (const [spotId, widgetIds] of Object.entries(injectionTable)) {\n const widgets = Array.isArray(widgetIds) ? widgetIds : [widgetIds]\n const existing = table.get(spotId) ?? []\n for (const widgetEntry of widgets) {\n if (typeof widgetEntry === 'string') {\n existing.push({ widgetId: widgetEntry, moduleId: entry.moduleId, priority: 0 })\n continue\n }\n if (isInjectionSlotObject(widgetEntry)) {\n const { widgetId, priority = 0, ...placement } = widgetEntry\n existing.push({\n widgetId,\n moduleId: entry.moduleId,\n priority: typeof priority === 'number' ? priority : 0,\n placement,\n })\n continue\n }\n }\n table.set(spotId, existing)\n }\n }\n\n for (const [spotId, widgets] of table.entries()) {\n table.set(spotId, widgets.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)))\n }\n\n return table\n })\n injectionTablePromise = promise.catch((err) => {\n if (injectionTablePromise === promise) {\n injectionTablePromise = null\n }\n throw err\n })\n }\n return injectionTablePromise\n}\n\nconst widgetCache = new Map<string, Promise<InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }>>()\n\nfunction isDataWidgetModule(widget: Record<string, unknown>): widget is LoadedDataWidgetModule {\n const keys = [\n 'columns',\n 'rowActions',\n 'bulkActions',\n 'filters',\n 'fields',\n 'steps',\n 'badge',\n 'menuItems',\n ]\n return keys.some((key) => key in widget)\n}\n\nfunction ensureValidInjectionModule(mod: unknown, key: string, moduleId: string): (InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }) {\n if (!mod || typeof mod !== 'object') {\n throw new Error(`Invalid injection widget module \"${key}\" from \"${moduleId}\" (expected object export)`)\n }\n const widget = (mod as { default?: InjectionAnyWidgetModule<any, any> }).default ?? (mod as InjectionAnyWidgetModule<any, any>)\n if (!widget || typeof widget !== 'object') {\n throw new Error(`Invalid injection widget export \"${key}\" from \"${moduleId}\" (missing default export)`) \n }\n if (!('metadata' in widget) || !widget.metadata || typeof widget.metadata !== 'object') {\n throw new Error(`Injection widget \"${key}\" from \"${moduleId}\" is missing metadata`)\n }\n const metadata = widget.metadata\n if (typeof metadata.id !== 'string' || metadata.id.length === 0) {\n throw new Error(`Injection widget \"${key}\" from \"${moduleId}\" metadata.id must be a non-empty string`)\n }\n const normalized = {\n ...widget,\n metadata,\n }\n\n if ('Widget' in normalized && typeof normalized.Widget === 'function') {\n if (typeof metadata.title !== 'string' || metadata.title.length === 0) {\n throw new Error(`Injection widget \"${metadata.id}\" from \"${moduleId}\" must have a title`)\n }\n return normalized\n }\n\n if (!isDataWidgetModule(normalized as Record<string, unknown>)) {\n throw new Error(\n `Injection widget \"${metadata.id}\" from \"${moduleId}\" must export either Widget component or a declarative data payload`\n )\n }\n\n return normalized\n}\n\nfunction isLoadedInjectionWidget(\n module: InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }\n): module is LoadedWidgetModule {\n return 'Widget' in module && typeof module.Widget === 'function'\n}\n\nfunction isLoadedInjectionDataWidget(\n module: InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }\n): module is LoadedDataWidgetModule {\n return !isLoadedInjectionWidget(module)\n}\n\nasync function loadEntry(entry: WidgetEntry): Promise<InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }> {\n if (!widgetCache.has(entry.key)) {\n const promise = entry.loader().then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId))\n widgetCache.set(entry.key, promise)\n }\n return widgetCache.get(entry.key)!\n}\n\nfunction getEnabledModuleIdsForInjection(): ReadonlySet<string> {\n // Prefer the explicit enabled-modules registry populated by bootstrap.\n // This is the only signal that includes modules without injection widgets\n // (for example `ai_assistant`), so it is required for `requiredModules`\n // gating to be sound.\n const explicit = readGlobalEnabledModuleIds() ?? _enabledModuleIds\n if (explicit) return explicit\n\n // Fallback: derive from injection tables and widget entries. This keeps\n // older bootstrap paths (and callers that have not yet wired\n // `registerEnabledModuleIds`) working \u2014 at the cost of mis-classifying\n // dependency modules that ship no widgets. New apps MUST call\n // `registerEnabledModuleIds` to get accurate gating.\n const enabled = new Set<string>()\n const tables = readGlobalInjectionTables() ?? _coreInjectionTables ?? []\n for (const entry of tables) {\n if (entry?.moduleId) enabled.add(entry.moduleId)\n }\n const entries = readGlobalInjectionWidgets() ?? _coreInjectionWidgetEntries ?? []\n for (const entry of entries) {\n if (entry?.moduleId) enabled.add(entry.moduleId)\n }\n return enabled\n}\n\nfunction widgetMissingRequiredModules(\n metadata: InjectionWidgetMetadata,\n enabledModuleIds: ReadonlySet<string>,\n): string[] {\n const required = metadata.requiredModules\n if (!Array.isArray(required) || required.length === 0) return []\n const missing: string[] = []\n for (const moduleId of required) {\n if (typeof moduleId !== 'string' || moduleId.length === 0) continue\n if (!enabledModuleIds.has(moduleId)) missing.push(moduleId)\n }\n return missing\n}\n\nconst warnedRequiredModuleSkips = new Set<string>()\n\nfunction warnSkippedWidget(metadataId: string, missingModules: string[]) {\n const key = `${metadataId}:${missingModules.join(',')}`\n if (warnedRequiredModuleSkips.has(key)) return\n warnedRequiredModuleSkips.add(key)\n if (process.env.NODE_ENV === 'development') {\n console.debug(\n `[InjectionLoader] Skipping widget \"${metadataId}\" \u2014 required module(s) not enabled: ${missingModules.join(', ')}`,\n )\n }\n}\n\nasync function getResolvedEntriesForSpot(spotId: InjectionSpotId): Promise<TableEntry[]> {\n const table = await loadInjectionTable()\n const exactEntries = table.get(spotId) ?? []\n const wildcardEntries: TableEntry[] = []\n\n for (const [candidateSpotId, candidateEntries] of table.entries()) {\n if (candidateSpotId === spotId) continue\n if (!candidateSpotId.includes('*')) continue\n if (!matchWildcardPattern(spotId, candidateSpotId)) continue\n wildcardEntries.push(...candidateEntries)\n }\n\n const dedupedEntries = new Map<string, TableEntry>()\n for (const entry of [...exactEntries, ...wildcardEntries]) {\n const cacheKey = `${entry.moduleId}:${entry.widgetId}`\n const previous = dedupedEntries.get(cacheKey)\n if (!previous || (entry.priority ?? 0) > (previous.priority ?? 0)) {\n dedupedEntries.set(cacheKey, entry)\n }\n }\n\n return Array.from(dedupedEntries.values()).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n}\n\nexport async function loadAllInjectionWidgets(): Promise<LoadedInjectionWidget[]> {\n const widgetEntries = await loadWidgetEntries()\n const enabledModuleIds = getEnabledModuleIdsForInjection()\n const loaded = await Promise.all(\n widgetEntries.map(async (entry) => {\n const module = await loadEntry(entry)\n if (!isLoadedInjectionWidget(module)) return null\n const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds)\n if (missing.length > 0) {\n warnSkippedWidget(module.metadata.id, missing)\n return null\n }\n return { ...module, moduleId: entry.moduleId, key: entry.key }\n })\n )\n const byId = new Map<string, LoadedInjectionWidget>()\n for (const widget of loaded) {\n if (!widget) continue\n if (!byId.has(widget.metadata.id)) {\n byId.set(widget.metadata.id, widget)\n }\n }\n return Array.from(byId.values())\n}\n\nexport async function loadInjectionWidgetById(widgetId: string): Promise<LoadedInjectionWidget | null> {\n const widgetEntries = await loadWidgetEntries()\n const enabledModuleIds = getEnabledModuleIdsForInjection()\n for (const entry of widgetEntries) {\n const module = await loadEntry(entry)\n if (!isLoadedInjectionWidget(module)) continue\n if (module.metadata.id === widgetId) {\n const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds)\n if (missing.length > 0) {\n warnSkippedWidget(module.metadata.id, missing)\n return null\n }\n return { ...module, moduleId: entry.moduleId, key: entry.key }\n }\n }\n return null\n}\n\nexport async function loadInjectionDataWidgetById(widgetId: string): Promise<LoadedInjectionDataWidget | null> {\n const widgetEntries = await loadWidgetEntries()\n const enabledModuleIds = getEnabledModuleIdsForInjection()\n for (const entry of widgetEntries) {\n const module = await loadEntry(entry)\n if (!isLoadedInjectionDataWidget(module)) continue\n if (module.metadata.id === widgetId) {\n const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds)\n if (missing.length > 0) {\n warnSkippedWidget(module.metadata.id, missing)\n return null\n }\n return { ...module, moduleId: entry.moduleId, key: entry.key }\n }\n }\n return null\n}\n\nexport async function loadInjectionWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionWidget[]> {\n const entries = await getResolvedEntriesForSpot(spotId)\n const widgets: LoadedInjectionWidget[] = []\n for (const { widgetId, placement, priority } of entries) {\n const widget = await loadInjectionWidgetById(widgetId)\n if (!widget) continue\n const combinedPlacement = placement\n ? { ...placement, priority: typeof priority === 'number' ? priority : 0 }\n : { priority: typeof priority === 'number' ? priority : 0 }\n widgets.push({ ...widget, placement: combinedPlacement })\n }\n return widgets\n}\n\nexport async function loadInjectionDataWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionDataWidget[]> {\n const entries = await getResolvedEntriesForSpot(spotId)\n const widgets: LoadedInjectionDataWidget[] = []\n for (const { widgetId, placement, priority } of entries) {\n const widget = await loadInjectionDataWidgetById(widgetId)\n if (!widget) continue\n const combinedPlacement = placement\n ? { ...placement, priority: typeof priority === 'number' ? priority : 0 }\n : { priority: typeof priority === 'number' ? priority : 0 }\n widgets.push({ ...widget, placement: combinedPlacement })\n }\n return widgets\n}\n"],
5
- "mappings": "AACA,SAAS,4BAA4B;AAWrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAkCP,IAAI,8BAAmE;AACvE,IAAI,uBAAwF;AAC5F,IAAI,oBAAgD;AACpD,IAAI,4BAA4B;AAChC,MAAM,+BAA+B;AACrC,MAAM,8BAA8B;AACpC,MAAM,gCAAgC;AACtC,MAAM,wCAAwC;AAC9C,MAAM,mCAAmC;AAEzC,SAAS,6BAAkE;AACzE,MAAI;AACF,UAAM,QAAS,WAAuC,4BAA4B;AAClF,WAAO,MAAM,QAAQ,KAAK,IAAK,QAAyC;AAAA,EAC1E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,4BAA4B,SAAuC;AAC1E,MAAI;AACF;AAAC,IAAC,WAAuC,4BAA4B,IAAI;AAAA,EAC3E,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,6BAAyD;AAChE,MAAI;AACF,UAAM,QAAS,WAAuC,6BAA6B;AACnF,QAAI,iBAAiB,IAAK,QAAO;AACjC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,4BAA4B,KAA0B;AAC7D,MAAI;AACF;AAAC,IAAC,WAAuC,6BAA6B,IAAI;AAAA,EAC5E,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,4BAA6F;AACpG,MAAI;AACF,UAAM,QAAS,WAAuC,2BAA2B;AACjF,WAAO,MAAM,QAAQ,KAAK,IAAK,QAAqE;AAAA,EACtG,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,2BAA2B,QAAkE;AACpG,MAAI;AACF;AAAC,IAAC,WAAuC,2BAA2B,IAAI;AAAA,EAC1E,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,qCAAoD;AAC3D,MAAI;AACF,UAAM,QAAS,WAAuC,qCAAqC;AAC3F,WAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oCAAoC,SAAiB;AAC5D,MAAI;AACF;AAAC,IAAC,WAAuC,qCAAqC,IAAI;AAAA,EACpF,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,iCAAiC;AACxC,+BAA6B;AAC7B,sCAAoC,yBAAyB;AAC7D,iCAA+B;AAE/B,MAAI,OAAO,WAAW,YAAa;AAEnC,SAAO,cAAc,IAAI,YAAY,kCAAkC;AAAA,IACrE,QAAQ,EAAE,SAAS,0BAA0B;AAAA,EAC/C,CAAC,CAAC;AACJ;AAEO,SAAS,6BAA6B,SAAuC;AAClF,MAAI,gCAAgC,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAClF,YAAQ,MAAM,8EAA8E;AAAA,EAC9F;AACA,QAAM,eAAe,uCAAuC,OAAO;AACnE,gCAA8B;AAC9B,8BAA4B,YAAY;AACxC,iCAA+B;AACjC;AAEO,SAAS,0BAAwD;AACtE,QAAM,gBAAgB,2BAA2B;AACjD,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,6BAA6B;AAEhC,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,IAAI,MAAM,sGAAsG;AAAA,EACxH;AACA,SAAO;AACT;AAEO,SAAS,4BAA4B,QAAkE;AAC5G,MAAI,yBAAyB,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC3E,YAAQ,MAAM,6EAA6E;AAAA,EAC7F;AACA,QAAM,cAAc,sCAAsC,MAAM;AAChE,yBAAuB;AACvB,6BAA2B,WAAW;AACtC,iCAA+B;AACjC;AAWO,SAAS,yBAAyB,WAA6B;AACpE,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,YAAY,WAAW;AAChC,QAAI,OAAO,aAAa,YAAY,SAAS,SAAS,EAAG,MAAK,IAAI,QAAQ;AAAA,EAC5E;AACA,MAAI,sBAAsB,QAAQ,QAAQ,IAAI,aAAa,eAAe;AACxE,YAAQ,MAAM,0EAA0E;AAAA,EAC1F;AACA,sBAAoB;AACpB,8BAA4B,IAAI;AAChC,iCAA+B;AACjC;AAEO,SAAS,sBAAkD;AAChE,SAAO,2BAA2B,KAAK;AACzC;AAEO,SAAS,8BAAsC;AACpD,QAAM,gBAAgB,mCAAmC;AACzD,MAAI,kBAAkB,KAAM,QAAO;AACnC,SAAO;AACT;AAEO,SAAS,oCAAoC,UAAkC;AACpF,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,QAAM,UAAU,MAAM,SAAS;AAC/B,SAAO,iBAAiB,kCAAkC,OAAO;AACjE,SAAO,MAAM;AACX,WAAO,oBAAoB,kCAAkC,OAAO;AAAA,EACtE;AACF;AAEO,SAAS,yBAAmF;AACjG,QAAM,eAAe,0BAA0B;AAC/C,MAAI,aAAc,QAAO;AACzB,MAAI,CAAC,sBAAsB;AAEzB,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,IAAI,MAAM,oGAAoG;AAAA,EACtH;AACA,SAAO;AACT;AAEA,IAAI,uBAAsD;AAW1D,IAAI,wBAA4E;AAEhF,SAAS,sBAAsB,OAAyG;AACtI,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,cAAc;AACtE;AAMO,SAAS,iCAAiC;AAC/C,yBAAuB;AACvB,0BAAwB;AACxB,cAAY,MAAM;AAClB,4BAA0B,MAAM;AAClC;AAEA,eAAe,oBAA4C;AACzD,MAAI,CAAC,sBAAsB;AACzB,UAAM,UAAU,QAAQ,QAAQ,EAAE;AAAA,MAAK,MACrC,wBAAwB,EAAE,IAAI,CAAC,WAAW;AAAA,QACxC,GAAG;AAAA,QACH,UAAU,MAAM,YAAY;AAAA,MAC9B,EAAE;AAAA,IACJ;AACA,2BAAuB,QAAQ,MAAM,CAAC,QAAQ;AAC5C,UAAI,yBAAyB,SAAS;AACpC,+BAAuB;AAAA,MACzB;AACA,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,eAAe,qBAAkE;AAC/E,MAAI,CAAC,uBAAuB;AAC1B,UAAM,UAAU,QAAQ,QAAQ,EAAE,KAAK,MAAM;AAC3C,YAAM,OAAO,uBAAuB;AACpC,YAAM,QAAQ,oBAAI,IAAmC;AAErD,iBAAW,SAAS,MAAM;AACxB,cAAM,iBAAiB,MAAM,SAAS,CAAC;AACvC,mBAAW,CAAC,QAAQ,SAAS,KAAK,OAAO,QAAQ,cAAc,GAAG;AAChE,gBAAM,UAAU,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC,SAAS;AACjE,gBAAM,WAAW,MAAM,IAAI,MAAM,KAAK,CAAC;AACvC,qBAAW,eAAe,SAAS;AACjC,gBAAI,OAAO,gBAAgB,UAAU;AACnC,uBAAS,KAAK,EAAE,UAAU,aAAa,UAAU,MAAM,UAAU,UAAU,EAAE,CAAC;AAC9E;AAAA,YACF;AACA,gBAAI,sBAAsB,WAAW,GAAG;AACtC,oBAAM,EAAE,UAAU,WAAW,GAAG,GAAG,UAAU,IAAI;AACjD,uBAAS,KAAK;AAAA,gBACZ;AAAA,gBACA,UAAU,MAAM;AAAA,gBAChB,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,gBACpD;AAAA,cACF,CAAC;AACD;AAAA,YACF;AAAA,UACF;AACA,gBAAM,IAAI,QAAQ,QAAQ;AAAA,QAC5B;AAAA,MACF;AAEA,iBAAW,CAAC,QAAQ,OAAO,KAAK,MAAM,QAAQ,GAAG;AAC/C,cAAM,IAAI,QAAQ,QAAQ,KAAK,CAAC,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE,CAAC;AAAA,MACjF;AAEA,aAAO;AAAA,IACT,CAAC;AACD,4BAAwB,QAAQ,MAAM,CAAC,QAAQ;AAC7C,UAAI,0BAA0B,SAAS;AACrC,gCAAwB;AAAA,MAC1B;AACA,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,MAAM,cAAc,oBAAI,IAAiG;AAEzH,SAAS,mBAAmB,QAAmE;AAC7F,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,SAAO,KAAK,KAAK,CAAC,QAAQ,OAAO,MAAM;AACzC;AAEA,SAAS,2BAA2B,KAAc,KAAa,UAAgG;AAC7J,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,UAAM,IAAI,MAAM,oCAAoC,GAAG,WAAW,QAAQ,4BAA4B;AAAA,EACxG;AACA,QAAM,SAAU,IAAyD,WAAY;AACrF,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,MAAM,oCAAoC,GAAG,WAAW,QAAQ,4BAA4B;AAAA,EACxG;AACA,MAAI,EAAE,cAAc,WAAW,CAAC,OAAO,YAAY,OAAO,OAAO,aAAa,UAAU;AACtF,UAAM,IAAI,MAAM,qBAAqB,GAAG,WAAW,QAAQ,uBAAuB;AAAA,EACpF;AACA,QAAM,WAAW,OAAO;AACxB,MAAI,OAAO,SAAS,OAAO,YAAY,SAAS,GAAG,WAAW,GAAG;AAC/D,UAAM,IAAI,MAAM,qBAAqB,GAAG,WAAW,QAAQ,0CAA0C;AAAA,EACvG;AACA,QAAM,aAAa;AAAA,IACjB,GAAG;AAAA,IACH;AAAA,EACF;AAEA,MAAI,YAAY,cAAc,OAAO,WAAW,WAAW,YAAY;AACrE,QAAI,OAAO,SAAS,UAAU,YAAY,SAAS,MAAM,WAAW,GAAG;AACrE,YAAM,IAAI,MAAM,qBAAqB,SAAS,EAAE,WAAW,QAAQ,qBAAqB;AAAA,IAC1F;AACA,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,mBAAmB,UAAqC,GAAG;AAC9D,UAAM,IAAI;AAAA,MACR,qBAAqB,SAAS,EAAE,WAAW,QAAQ;AAAA,IACrD;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,wBACP,QAC8B;AAC9B,SAAO,YAAY,UAAU,OAAO,OAAO,WAAW;AACxD;AAEA,SAAS,4BACP,QACkC;AAClC,SAAO,CAAC,wBAAwB,MAAM;AACxC;AAEA,eAAe,UAAU,OAAyG;AAChI,MAAI,CAAC,YAAY,IAAI,MAAM,GAAG,GAAG;AAC/B,UAAM,UAAU,MAAM,OAAO,EAAE,KAAK,CAAC,QAAQ,2BAA2B,KAAK,MAAM,KAAK,MAAM,QAAQ,CAAC;AACvG,gBAAY,IAAI,MAAM,KAAK,OAAO;AAAA,EACpC;AACA,SAAO,YAAY,IAAI,MAAM,GAAG;AAClC;AAEA,SAAS,kCAAuD;AAK9D,QAAM,WAAW,2BAA2B,KAAK;AACjD,MAAI,SAAU,QAAO;AAOrB,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,SAAS,0BAA0B,KAAK,wBAAwB,CAAC;AACvE,aAAW,SAAS,QAAQ;AAC1B,QAAI,OAAO,SAAU,SAAQ,IAAI,MAAM,QAAQ;AAAA,EACjD;AACA,QAAM,UAAU,2BAA2B,KAAK,+BAA+B,CAAC;AAChF,aAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,SAAU,SAAQ,IAAI,MAAM,QAAQ;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,6BACP,UACA,kBACU;AACV,QAAM,WAAW,SAAS;AAC1B,MAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,EAAG,QAAO,CAAC;AAC/D,QAAM,UAAoB,CAAC;AAC3B,aAAW,YAAY,UAAU;AAC/B,QAAI,OAAO,aAAa,YAAY,SAAS,WAAW,EAAG;AAC3D,QAAI,CAAC,iBAAiB,IAAI,QAAQ,EAAG,SAAQ,KAAK,QAAQ;AAAA,EAC5D;AACA,SAAO;AACT;AAEA,MAAM,4BAA4B,oBAAI,IAAY;AAElD,SAAS,kBAAkB,YAAoB,gBAA0B;AACvE,QAAM,MAAM,GAAG,UAAU,IAAI,eAAe,KAAK,GAAG,CAAC;AACrD,MAAI,0BAA0B,IAAI,GAAG,EAAG;AACxC,4BAA0B,IAAI,GAAG;AACjC,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,YAAQ;AAAA,MACN,sCAAsC,UAAU,4CAAuC,eAAe,KAAK,IAAI,CAAC;AAAA,IAClH;AAAA,EACF;AACF;AAEA,eAAe,0BAA0B,QAAgD;AACvF,QAAM,QAAQ,MAAM,mBAAmB;AACvC,QAAM,eAAe,MAAM,IAAI,MAAM,KAAK,CAAC;AAC3C,QAAM,kBAAgC,CAAC;AAEvC,aAAW,CAAC,iBAAiB,gBAAgB,KAAK,MAAM,QAAQ,GAAG;AACjE,QAAI,oBAAoB,OAAQ;AAChC,QAAI,CAAC,gBAAgB,SAAS,GAAG,EAAG;AACpC,QAAI,CAAC,qBAAqB,QAAQ,eAAe,EAAG;AACpD,oBAAgB,KAAK,GAAG,gBAAgB;AAAA,EAC1C;AAEA,QAAM,iBAAiB,oBAAI,IAAwB;AACnD,aAAW,SAAS,CAAC,GAAG,cAAc,GAAG,eAAe,GAAG;AACzD,UAAM,WAAW,GAAG,MAAM,QAAQ,IAAI,MAAM,QAAQ;AACpD,UAAM,WAAW,eAAe,IAAI,QAAQ;AAC5C,QAAI,CAAC,aAAa,MAAM,YAAY,MAAM,SAAS,YAAY,IAAI;AACjE,qBAAe,IAAI,UAAU,KAAK;AAAA,IACpC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,eAAe,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;AACjG;AAEA,eAAsB,0BAA4D;AAChF,QAAM,gBAAgB,MAAM,kBAAkB;AAC9C,QAAM,mBAAmB,gCAAgC;AACzD,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,cAAc,IAAI,OAAO,UAAU;AACjC,YAAM,SAAS,MAAM,UAAU,KAAK;AACpC,UAAI,CAAC,wBAAwB,MAAM,EAAG,QAAO;AAC7C,YAAM,UAAU,6BAA6B,OAAO,UAAU,gBAAgB;AAC9E,UAAI,QAAQ,SAAS,GAAG;AACtB,0BAAkB,OAAO,SAAS,IAAI,OAAO;AAC7C,eAAO;AAAA,MACT;AACA,aAAO,EAAE,GAAG,QAAQ,UAAU,MAAM,UAAU,KAAK,MAAM,IAAI;AAAA,IAC/D,CAAC;AAAA,EACH;AACA,QAAM,OAAO,oBAAI,IAAmC;AACpD,aAAW,UAAU,QAAQ;AAC3B,QAAI,CAAC,OAAQ;AACb,QAAI,CAAC,KAAK,IAAI,OAAO,SAAS,EAAE,GAAG;AACjC,WAAK,IAAI,OAAO,SAAS,IAAI,MAAM;AAAA,IACrC;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;AAEA,eAAsB,wBAAwB,UAAyD;AACrG,QAAM,gBAAgB,MAAM,kBAAkB;AAC9C,QAAM,mBAAmB,gCAAgC;AACzD,aAAW,SAAS,eAAe;AACjC,UAAM,SAAS,MAAM,UAAU,KAAK;AACpC,QAAI,CAAC,wBAAwB,MAAM,EAAG;AACtC,QAAI,OAAO,SAAS,OAAO,UAAU;AACnC,YAAM,UAAU,6BAA6B,OAAO,UAAU,gBAAgB;AAC9E,UAAI,QAAQ,SAAS,GAAG;AACtB,0BAAkB,OAAO,SAAS,IAAI,OAAO;AAC7C,eAAO;AAAA,MACT;AACA,aAAO,EAAE,GAAG,QAAQ,UAAU,MAAM,UAAU,KAAK,MAAM,IAAI;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,4BAA4B,UAA6D;AAC7G,QAAM,gBAAgB,MAAM,kBAAkB;AAC9C,QAAM,mBAAmB,gCAAgC;AACzD,aAAW,SAAS,eAAe;AACjC,UAAM,SAAS,MAAM,UAAU,KAAK;AACpC,QAAI,CAAC,4BAA4B,MAAM,EAAG;AAC1C,QAAI,OAAO,SAAS,OAAO,UAAU;AACnC,YAAM,UAAU,6BAA6B,OAAO,UAAU,gBAAgB;AAC9E,UAAI,QAAQ,SAAS,GAAG;AACtB,0BAAkB,OAAO,SAAS,IAAI,OAAO;AAC7C,eAAO;AAAA,MACT;AACA,aAAO,EAAE,GAAG,QAAQ,UAAU,MAAM,UAAU,KAAK,MAAM,IAAI;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,4BAA4B,QAA2D;AAC3G,QAAM,UAAU,MAAM,0BAA0B,MAAM;AACtD,QAAM,UAAmC,CAAC;AAC1C,aAAW,EAAE,UAAU,WAAW,SAAS,KAAK,SAAS;AACvD,UAAM,SAAS,MAAM,wBAAwB,QAAQ;AACrD,QAAI,CAAC,OAAQ;AACb,UAAM,oBAAoB,YACtB,EAAE,GAAG,WAAW,UAAU,OAAO,aAAa,WAAW,WAAW,EAAE,IACtE,EAAE,UAAU,OAAO,aAAa,WAAW,WAAW,EAAE;AAC5D,YAAQ,KAAK,EAAE,GAAG,QAAQ,WAAW,kBAAkB,CAAC;AAAA,EAC1D;AACA,SAAO;AACT;AAEA,eAAsB,gCAAgC,QAA+D;AACnH,QAAM,UAAU,MAAM,0BAA0B,MAAM;AACtD,QAAM,UAAuC,CAAC;AAC9C,aAAW,EAAE,UAAU,WAAW,SAAS,KAAK,SAAS;AACvD,UAAM,SAAS,MAAM,4BAA4B,QAAQ;AACzD,QAAI,CAAC,OAAQ;AACb,UAAM,oBAAoB,YACtB,EAAE,GAAG,WAAW,UAAU,OAAO,aAAa,WAAW,WAAW,EAAE,IACtE,EAAE,UAAU,OAAO,aAAa,WAAW,WAAW,EAAE;AAC5D,YAAQ,KAAK,EAAE,GAAG,QAAQ,WAAW,kBAAkB,CAAC;AAAA,EAC1D;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { ModuleInjectionWidgetEntry } from '../registry'\nimport { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'\nimport type {\n InjectionAnyWidgetModule,\n InjectionDataWidgetModule,\n InjectionWidgetMetadata,\n InjectionWidgetModule,\n InjectionSpotId,\n ModuleInjectionSlot,\n ModuleInjectionTable,\n InjectionWidgetPlacement,\n} from './injection'\nimport {\n applyInjectionWidgetOverridesToEntries,\n applyInjectionWidgetOverridesToTables,\n} from '../overrides'\n\ntype LoadedWidgetModule = InjectionWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }\ntype LoadedDataWidgetModule = InjectionDataWidgetModule & { metadata: InjectionWidgetMetadata }\n\nexport type LoadedInjectionWidget = LoadedWidgetModule & {\n moduleId: string\n key: string\n placement?: {\n groupId?: string\n groupLabel?: string\n groupDescription?: string\n column?: 1 | 2\n kind?: 'tab' | 'group' | 'stack'\n [k: string]: unknown\n }\n}\n\nexport type LoadedInjectionDataWidget = LoadedDataWidgetModule & {\n moduleId: string\n key: string\n placement?: {\n groupId?: string\n groupLabel?: string\n groupDescription?: string\n column?: 1 | 2\n kind?: 'tab' | 'group' | 'stack'\n [k: string]: unknown\n }\n}\n\ntype WidgetEntry = ModuleInjectionWidgetEntry & { moduleId: string }\n\n// Registration pattern for publishable packages\nlet _coreInjectionWidgetEntries: ModuleInjectionWidgetEntry[] | null = null\nlet _coreInjectionTables: Array<{ moduleId: string; table: ModuleInjectionTable }> | null = null\nlet _enabledModuleIds: ReadonlySet<string> | null = null\nlet _injectionRegistryVersion = 0\nconst GLOBAL_INJECTION_WIDGETS_KEY = '__openMercatoCoreInjectionWidgetEntries__'\nconst GLOBAL_INJECTION_TABLES_KEY = '__openMercatoCoreInjectionTables__'\nconst GLOBAL_ENABLED_MODULE_IDS_KEY = '__openMercatoEnabledModuleIds__'\nconst GLOBAL_INJECTION_REGISTRY_VERSION_KEY = '__openMercatoCoreInjectionRegistryVersion__'\nconst INJECTION_REGISTRY_CHANGED_EVENT = '__openMercatoInjectionRegistryChanged__'\n\nfunction readGlobalInjectionWidgets(): ModuleInjectionWidgetEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INJECTION_WIDGETS_KEY]\n return Array.isArray(value) ? (value as ModuleInjectionWidgetEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInjectionWidgets(entries: ModuleInjectionWidgetEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INJECTION_WIDGETS_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction readGlobalEnabledModuleIds(): ReadonlySet<string> | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_ENABLED_MODULE_IDS_KEY]\n if (value instanceof Set) return value as ReadonlySet<string>\n return null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalEnabledModuleIds(ids: ReadonlySet<string>) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_ENABLED_MODULE_IDS_KEY] = ids\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction readGlobalInjectionTables(): Array<{ moduleId: string; table: ModuleInjectionTable }> | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INJECTION_TABLES_KEY]\n return Array.isArray(value) ? (value as Array<{ moduleId: string; table: ModuleInjectionTable }>) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInjectionTables(tables: Array<{ moduleId: string; table: ModuleInjectionTable }>) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INJECTION_TABLES_KEY] = tables\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction readGlobalInjectionRegistryVersion(): number | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INJECTION_REGISTRY_VERSION_KEY]\n return typeof value === 'number' ? value : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInjectionRegistryVersion(version: number) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INJECTION_REGISTRY_VERSION_KEY] = version\n } catch {\n // ignore global assignment failures\n }\n}\n\nfunction notifyInjectionRegistryChanged() {\n _injectionRegistryVersion += 1\n writeGlobalInjectionRegistryVersion(_injectionRegistryVersion)\n invalidateInjectionWidgetCache()\n\n if (typeof window === 'undefined') return\n\n window.dispatchEvent(new CustomEvent(INJECTION_REGISTRY_CHANGED_EVENT, {\n detail: { version: _injectionRegistryVersion },\n }))\n}\n\nexport function registerCoreInjectionWidgets(entries: ModuleInjectionWidgetEntry[]) {\n if (_coreInjectionWidgetEntries !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Core injection widgets re-registered (this may occur during HMR)')\n }\n const finalEntries = applyInjectionWidgetOverridesToEntries(entries)\n _coreInjectionWidgetEntries = finalEntries\n writeGlobalInjectionWidgets(finalEntries)\n notifyInjectionRegistryChanged()\n}\n\nexport function getCoreInjectionWidgets(): ModuleInjectionWidgetEntry[] {\n const globalEntries = readGlobalInjectionWidgets()\n if (globalEntries) return globalEntries\n if (!_coreInjectionWidgetEntries) {\n // On client-side, bootstrap doesn't run - return empty array gracefully\n if (typeof window !== 'undefined') {\n return []\n }\n throw new Error('[Bootstrap] Core injection widgets not registered. Call registerCoreInjectionWidgets() at bootstrap.')\n }\n return _coreInjectionWidgetEntries\n}\n\nexport function registerCoreInjectionTables(tables: Array<{ moduleId: string; table: ModuleInjectionTable }>) {\n if (_coreInjectionTables !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Core injection tables re-registered (this may occur during HMR)')\n }\n const finalTables = applyInjectionWidgetOverridesToTables(tables)\n _coreInjectionTables = finalTables\n writeGlobalInjectionTables(finalTables)\n notifyInjectionRegistryChanged()\n}\n\n/**\n * Register the canonical set of enabled module IDs for the running app.\n *\n * This is the authoritative signal used by `requiredModules` widget gating \u2014\n * deriving \"enabled\" from injection tables or widget entries is unreliable\n * because modules without injection widgets (for example `ai_assistant`) do\n * not contribute entries to either source. Bootstrap callers should pass\n * every module ID present in the app's module registry.\n */\nexport function registerEnabledModuleIds(moduleIds: Iterable<string>) {\n const next = new Set<string>()\n for (const moduleId of moduleIds) {\n if (typeof moduleId === 'string' && moduleId.length > 0) next.add(moduleId)\n }\n if (_enabledModuleIds !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Enabled module IDs re-registered (this may occur during HMR)')\n }\n _enabledModuleIds = next\n writeGlobalEnabledModuleIds(next)\n notifyInjectionRegistryChanged()\n}\n\nexport function getEnabledModuleIds(): ReadonlySet<string> | null {\n return readGlobalEnabledModuleIds() ?? _enabledModuleIds\n}\n\nexport function getInjectionRegistryVersion(): number {\n const globalVersion = readGlobalInjectionRegistryVersion()\n if (globalVersion !== null) return globalVersion\n return _injectionRegistryVersion\n}\n\nexport function subscribeToInjectionRegistryChanges(listener: () => void): () => void {\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n const handler = () => listener()\n window.addEventListener(INJECTION_REGISTRY_CHANGED_EVENT, handler)\n return () => {\n window.removeEventListener(INJECTION_REGISTRY_CHANGED_EVENT, handler)\n }\n}\n\nexport function getCoreInjectionTables(): Array<{ moduleId: string; table: ModuleInjectionTable }> {\n const globalTables = readGlobalInjectionTables()\n if (globalTables) return globalTables\n if (!_coreInjectionTables) {\n // On client-side, bootstrap doesn't run - return empty array gracefully\n if (typeof window !== 'undefined') {\n return []\n }\n throw new Error('[Bootstrap] Core injection tables not registered. Call registerCoreInjectionTables() at bootstrap.')\n }\n return _coreInjectionTables\n}\n\nlet widgetEntriesPromise: Promise<WidgetEntry[]> | null = null\ntype TableEntry = {\n widgetId: string\n moduleId: string\n priority: number\n placement?: ModuleInjectionSlot extends infer S\n ? S extends { widgetId: string }\n ? Omit<S, 'widgetId' | 'priority'>\n : never\n : never\n}\nlet injectionTablePromise: Promise<Map<InjectionSpotId, TableEntry[]>> | null = null\ntype WidgetLookupIndex = {\n widgetsById: Map<string, LoadedInjectionWidget>\n dataWidgetsById: Map<string, LoadedInjectionDataWidget>\n}\nlet widgetLookupIndexPromise: { version: number; promise: Promise<WidgetLookupIndex> } | null = null\n\nfunction isInjectionSlotObject(value: ModuleInjectionSlot): value is InjectionWidgetPlacement & { widgetId: string; priority?: number } {\n return typeof value === 'object' && value !== null && 'widgetId' in value\n}\n\n/**\n * Invalidate the widget entries and widget module cache.\n * Call this when the generated registry is updated or modules are reloaded.\n */\nexport function invalidateInjectionWidgetCache() {\n widgetEntriesPromise = null\n injectionTablePromise = null\n widgetLookupIndexPromise = null\n widgetCache.clear()\n warnedRequiredModuleSkips.clear()\n}\n\nasync function loadWidgetEntries(): Promise<WidgetEntry[]> {\n if (!widgetEntriesPromise) {\n const promise = Promise.resolve().then(() =>\n getCoreInjectionWidgets().map((entry) => ({\n ...entry,\n moduleId: entry.moduleId || 'unknown',\n }))\n )\n widgetEntriesPromise = promise.catch((err) => {\n if (widgetEntriesPromise === promise) {\n widgetEntriesPromise = null\n }\n throw err\n })\n }\n return widgetEntriesPromise\n}\n\nasync function loadInjectionTable(): Promise<Map<InjectionSpotId, TableEntry[]>> {\n if (!injectionTablePromise) {\n const promise = Promise.resolve().then(() => {\n const list = getCoreInjectionTables()\n const table = new Map<InjectionSpotId, TableEntry[]>()\n\n for (const entry of list) {\n const injectionTable = entry.table ?? {}\n for (const [spotId, widgetIds] of Object.entries(injectionTable)) {\n const widgets = Array.isArray(widgetIds) ? widgetIds : [widgetIds]\n const existing = table.get(spotId) ?? []\n for (const widgetEntry of widgets) {\n if (typeof widgetEntry === 'string') {\n existing.push({ widgetId: widgetEntry, moduleId: entry.moduleId, priority: 0 })\n continue\n }\n if (isInjectionSlotObject(widgetEntry)) {\n const { widgetId, priority = 0, ...placement } = widgetEntry\n existing.push({\n widgetId,\n moduleId: entry.moduleId,\n priority: typeof priority === 'number' ? priority : 0,\n placement,\n })\n continue\n }\n }\n table.set(spotId, existing)\n }\n }\n\n for (const [spotId, widgets] of table.entries()) {\n table.set(spotId, widgets.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)))\n }\n\n return table\n })\n injectionTablePromise = promise.catch((err) => {\n if (injectionTablePromise === promise) {\n injectionTablePromise = null\n }\n throw err\n })\n }\n return injectionTablePromise\n}\n\nconst widgetCache = new Map<string, Promise<InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }>>()\n\nfunction isDataWidgetModule(widget: Record<string, unknown>): widget is LoadedDataWidgetModule {\n const keys = [\n 'columns',\n 'rowActions',\n 'bulkActions',\n 'filters',\n 'fields',\n 'steps',\n 'badge',\n 'menuItems',\n ]\n return keys.some((key) => key in widget)\n}\n\nfunction ensureValidInjectionModule(mod: unknown, key: string, moduleId: string): (InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }) {\n if (!mod || typeof mod !== 'object') {\n throw new Error(`Invalid injection widget module \"${key}\" from \"${moduleId}\" (expected object export)`)\n }\n const widget = (mod as { default?: InjectionAnyWidgetModule<any, any> }).default ?? (mod as InjectionAnyWidgetModule<any, any>)\n if (!widget || typeof widget !== 'object') {\n throw new Error(`Invalid injection widget export \"${key}\" from \"${moduleId}\" (missing default export)`) \n }\n if (!('metadata' in widget) || !widget.metadata || typeof widget.metadata !== 'object') {\n throw new Error(`Injection widget \"${key}\" from \"${moduleId}\" is missing metadata`)\n }\n const metadata = widget.metadata\n if (typeof metadata.id !== 'string' || metadata.id.length === 0) {\n throw new Error(`Injection widget \"${key}\" from \"${moduleId}\" metadata.id must be a non-empty string`)\n }\n const normalized = {\n ...widget,\n metadata,\n }\n\n if ('Widget' in normalized && typeof normalized.Widget === 'function') {\n if (typeof metadata.title !== 'string' || metadata.title.length === 0) {\n throw new Error(`Injection widget \"${metadata.id}\" from \"${moduleId}\" must have a title`)\n }\n return normalized\n }\n\n if (!isDataWidgetModule(normalized as Record<string, unknown>)) {\n throw new Error(\n `Injection widget \"${metadata.id}\" from \"${moduleId}\" must export either Widget component or a declarative data payload`\n )\n }\n\n return normalized\n}\n\nfunction isLoadedInjectionWidget(\n module: InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }\n): module is LoadedWidgetModule {\n return 'Widget' in module && typeof module.Widget === 'function'\n}\n\nfunction isLoadedInjectionDataWidget(\n module: InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }\n): module is LoadedDataWidgetModule {\n return !isLoadedInjectionWidget(module)\n}\n\nasync function loadEntry(entry: WidgetEntry): Promise<InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }> {\n if (!widgetCache.has(entry.key)) {\n const promise = Promise.resolve()\n .then(() => entry.loader())\n .then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId))\n widgetCache.set(entry.key, promise)\n }\n return widgetCache.get(entry.key)!\n}\n\nasync function loadWidgetLookupIndex(): Promise<WidgetLookupIndex> {\n const version = getInjectionRegistryVersion()\n if (!widgetLookupIndexPromise || widgetLookupIndexPromise.version !== version) {\n const promise = Promise.resolve().then(async () => {\n const widgetEntries = await loadWidgetEntries()\n const settled = await Promise.allSettled(widgetEntries.map((entry) => loadEntry(entry)))\n const widgetsById = new Map<string, LoadedInjectionWidget>()\n const dataWidgetsById = new Map<string, LoadedInjectionDataWidget>()\n\n settled.forEach((result, index) => {\n if (result.status !== 'fulfilled') return\n const entry = widgetEntries[index]\n const module = result.value\n if (isLoadedInjectionWidget(module)) {\n if (!widgetsById.has(module.metadata.id)) {\n widgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key })\n }\n return\n }\n if (!dataWidgetsById.has(module.metadata.id)) {\n dataWidgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key })\n }\n })\n\n return { widgetsById, dataWidgetsById }\n })\n widgetLookupIndexPromise = { version, promise }\n }\n return widgetLookupIndexPromise.promise\n}\n\nfunction applyRequiredModuleGate<T extends LoadedInjectionWidget | LoadedInjectionDataWidget>(\n widget: T,\n enabledModuleIds: ReadonlySet<string>,\n): T | null {\n const missing = widgetMissingRequiredModules(widget.metadata, enabledModuleIds)\n if (missing.length > 0) {\n warnSkippedWidget(widget.metadata.id, missing)\n return null\n }\n return widget\n}\n\ntype HintedLookupResult<T> =\n | { resolved: true; widget: T | null }\n | { resolved: false }\n\nasync function tryLoadHintedWidgetById<T extends LoadedInjectionWidget | LoadedInjectionDataWidget>(\n widgetId: string,\n isExpectedKind: (module: InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }) => boolean,\n enabledModuleIds: ReadonlySet<string>,\n): Promise<HintedLookupResult<T>> {\n const widgetEntries = await loadWidgetEntries()\n const entry = widgetEntries.find((candidate) => candidate.widgetId === widgetId)\n if (!entry) return { resolved: false }\n\n const module = await loadEntry(entry).catch(() => null)\n if (!module || module.metadata.id !== widgetId || !isExpectedKind(module)) {\n return { resolved: false }\n }\n\n const widget = { ...module, moduleId: entry.moduleId, key: entry.key } as T\n return { resolved: true, widget: applyRequiredModuleGate(widget, enabledModuleIds) }\n}\n\nfunction getEnabledModuleIdsForInjection(): ReadonlySet<string> {\n // Prefer the explicit enabled-modules registry populated by bootstrap.\n // This is the only signal that includes modules without injection widgets\n // (for example `ai_assistant`), so it is required for `requiredModules`\n // gating to be sound.\n const explicit = readGlobalEnabledModuleIds() ?? _enabledModuleIds\n if (explicit) return explicit\n\n // Fallback: derive from injection tables and widget entries. This keeps\n // older bootstrap paths (and callers that have not yet wired\n // `registerEnabledModuleIds`) working \u2014 at the cost of mis-classifying\n // dependency modules that ship no widgets. New apps MUST call\n // `registerEnabledModuleIds` to get accurate gating.\n const enabled = new Set<string>()\n const tables = readGlobalInjectionTables() ?? _coreInjectionTables ?? []\n for (const entry of tables) {\n if (entry?.moduleId) enabled.add(entry.moduleId)\n }\n const entries = readGlobalInjectionWidgets() ?? _coreInjectionWidgetEntries ?? []\n for (const entry of entries) {\n if (entry?.moduleId) enabled.add(entry.moduleId)\n }\n return enabled\n}\n\nfunction widgetMissingRequiredModules(\n metadata: InjectionWidgetMetadata,\n enabledModuleIds: ReadonlySet<string>,\n): string[] {\n const required = metadata.requiredModules\n if (!Array.isArray(required) || required.length === 0) return []\n const missing: string[] = []\n for (const moduleId of required) {\n if (typeof moduleId !== 'string' || moduleId.length === 0) continue\n if (!enabledModuleIds.has(moduleId)) missing.push(moduleId)\n }\n return missing\n}\n\nconst warnedRequiredModuleSkips = new Set<string>()\n\nfunction warnSkippedWidget(metadataId: string, missingModules: string[]) {\n const key = `${metadataId}:${missingModules.join(',')}`\n if (warnedRequiredModuleSkips.has(key)) return\n warnedRequiredModuleSkips.add(key)\n if (process.env.NODE_ENV === 'development') {\n console.debug(\n `[InjectionLoader] Skipping widget \"${metadataId}\" \u2014 required module(s) not enabled: ${missingModules.join(', ')}`,\n )\n }\n}\n\nasync function getResolvedEntriesForSpot(spotId: InjectionSpotId): Promise<TableEntry[]> {\n const table = await loadInjectionTable()\n const exactEntries = table.get(spotId) ?? []\n const wildcardEntries: TableEntry[] = []\n\n for (const [candidateSpotId, candidateEntries] of table.entries()) {\n if (candidateSpotId === spotId) continue\n if (!candidateSpotId.includes('*')) continue\n if (!matchWildcardPattern(spotId, candidateSpotId)) continue\n wildcardEntries.push(...candidateEntries)\n }\n\n const dedupedEntries = new Map<string, TableEntry>()\n for (const entry of [...exactEntries, ...wildcardEntries]) {\n const cacheKey = `${entry.moduleId}:${entry.widgetId}`\n const previous = dedupedEntries.get(cacheKey)\n if (!previous || (entry.priority ?? 0) > (previous.priority ?? 0)) {\n dedupedEntries.set(cacheKey, entry)\n }\n }\n\n return Array.from(dedupedEntries.values()).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n}\n\nexport async function loadAllInjectionWidgets(): Promise<LoadedInjectionWidget[]> {\n const widgetEntries = await loadWidgetEntries()\n const enabledModuleIds = getEnabledModuleIdsForInjection()\n const loaded = await Promise.all(\n widgetEntries.map(async (entry) => {\n const module = await loadEntry(entry)\n if (!isLoadedInjectionWidget(module)) return null\n const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds)\n if (missing.length > 0) {\n warnSkippedWidget(module.metadata.id, missing)\n return null\n }\n return { ...module, moduleId: entry.moduleId, key: entry.key }\n })\n )\n const byId = new Map<string, LoadedInjectionWidget>()\n for (const widget of loaded) {\n if (!widget) continue\n if (!byId.has(widget.metadata.id)) {\n byId.set(widget.metadata.id, widget)\n }\n }\n return Array.from(byId.values())\n}\n\nexport async function loadInjectionWidgetById(widgetId: string): Promise<LoadedInjectionWidget | null> {\n const enabledModuleIds = getEnabledModuleIdsForInjection()\n const hinted = await tryLoadHintedWidgetById<LoadedInjectionWidget>(widgetId, isLoadedInjectionWidget, enabledModuleIds)\n if (hinted.resolved) return hinted.widget\n\n const index = await loadWidgetLookupIndex()\n const widget = index.widgetsById.get(widgetId)\n return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null\n}\n\nexport async function loadInjectionDataWidgetById(widgetId: string): Promise<LoadedInjectionDataWidget | null> {\n const enabledModuleIds = getEnabledModuleIdsForInjection()\n const hinted = await tryLoadHintedWidgetById<LoadedInjectionDataWidget>(widgetId, isLoadedInjectionDataWidget, enabledModuleIds)\n if (hinted.resolved) return hinted.widget\n\n const index = await loadWidgetLookupIndex()\n const widget = index.dataWidgetsById.get(widgetId)\n return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null\n}\n\nexport async function loadInjectionWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionWidget[]> {\n const entries = await getResolvedEntriesForSpot(spotId)\n const widgets: LoadedInjectionWidget[] = []\n for (const { widgetId, placement, priority } of entries) {\n const widget = await loadInjectionWidgetById(widgetId)\n if (!widget) continue\n const combinedPlacement = placement\n ? { ...placement, priority: typeof priority === 'number' ? priority : 0 }\n : { priority: typeof priority === 'number' ? priority : 0 }\n widgets.push({ ...widget, placement: combinedPlacement })\n }\n return widgets\n}\n\nexport async function loadInjectionDataWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionDataWidget[]> {\n const entries = await getResolvedEntriesForSpot(spotId)\n const widgets: LoadedInjectionDataWidget[] = []\n for (const { widgetId, placement, priority } of entries) {\n const widget = await loadInjectionDataWidgetById(widgetId)\n if (!widget) continue\n const combinedPlacement = placement\n ? { ...placement, priority: typeof priority === 'number' ? priority : 0 }\n : { priority: typeof priority === 'number' ? priority : 0 }\n widgets.push({ ...widget, placement: combinedPlacement })\n }\n return widgets\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.6.6-develop.6089.1.a7ed560528",
3
+ "version": "0.6.6-develop.6121.1.5797a901b3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -93,7 +93,7 @@
93
93
  "@mikro-orm/core": "^7.1.4",
94
94
  "@mikro-orm/decorators": "^7.1.4",
95
95
  "@mikro-orm/postgresql": "^7.1.4",
96
- "@open-mercato/cache": "0.6.6-develop.6089.1.a7ed560528",
96
+ "@open-mercato/cache": "0.6.6-develop.6121.1.5797a901b3",
97
97
  "dotenv": "^17.4.2",
98
98
  "rate-limiter-flexible": "^11.2.0",
99
99
  "re2js": "2.8.3",
@@ -214,6 +214,7 @@ export type ModuleInjectionWidgetEntry = {
214
214
  moduleId: string
215
215
  key: string
216
216
  source: 'app' | 'package'
217
+ widgetId?: string
217
218
  loader: () => Promise<InjectionAnyWidgetModule<any, any>>
218
219
  }
219
220
 
@@ -10,12 +10,14 @@
10
10
  */
11
11
  import { describe, it, expect, beforeEach } from '@jest/globals'
12
12
  import type {
13
+ InjectionDataWidgetModule,
13
14
  InjectionWidgetModule,
14
15
  ModuleInjectionTable,
15
16
  } from '@open-mercato/shared/modules/widgets/injection'
16
17
  import type { ModuleInjectionWidgetEntry } from '@open-mercato/shared/modules/registry'
17
18
  import {
18
19
  invalidateInjectionWidgetCache,
20
+ loadInjectionDataWidgetById,
19
21
  loadInjectionWidgetById,
20
22
  loadInjectionWidgetsForSpot,
21
23
  registerCoreInjectionTables,
@@ -26,6 +28,7 @@ import {
26
28
  const HOST_SPOT_ID = 'data-table:host.list:search-trailing'
27
29
  const ALWAYS_AVAILABLE_WIDGET_ID = 'host.injection.always-available'
28
30
  const REQUIRES_AI_WIDGET_ID = 'host.injection.requires-ai-assistant'
31
+ const DATA_WIDGET_ID = 'host.injection.data-menu'
29
32
 
30
33
  const PlaceholderComponent = () => null
31
34
 
@@ -52,17 +55,30 @@ const requiresAiAssistantWidget: InjectionWidgetModule<Record<string, unknown>,
52
55
  Widget: PlaceholderComponent,
53
56
  }
54
57
 
58
+ const dataWidget: InjectionDataWidgetModule = {
59
+ metadata: {
60
+ id: DATA_WIDGET_ID,
61
+ title: 'Data Menu',
62
+ description: 'Declarative menu widget.',
63
+ priority: 20,
64
+ enabled: true,
65
+ },
66
+ menuItems: [],
67
+ }
68
+
55
69
  function makeWidgetEntry(
56
70
  moduleId: string,
57
71
  key: string,
58
- loader: () => Promise<InjectionWidgetModule<any, any>>,
72
+ loader: () => Promise<InjectionWidgetModule<any, any> | InjectionDataWidgetModule>,
73
+ options: { widgetId?: string } = {},
59
74
  ): ModuleInjectionWidgetEntry {
60
75
  return {
61
76
  moduleId,
62
77
  key,
63
78
  source: 'package',
79
+ ...options,
64
80
  loader,
65
- }
81
+ } as ModuleInjectionWidgetEntry
66
82
  }
67
83
 
68
84
  function registerHostFixtures(includeAiAssistantModule: boolean) {
@@ -194,4 +210,64 @@ describe('Injection loader — requiredModules gating (#1849)', () => {
194
210
  const loaded = await loadInjectionWidgetsForSpot(HOST_SPOT_ID)
195
211
  expect(loaded.map((widget) => widget.metadata.id)).toEqual([ALWAYS_AVAILABLE_WIDGET_ID])
196
212
  })
213
+
214
+ it('uses a generated widgetId hint to avoid loading unrelated component widgets', async () => {
215
+ invalidateInjectionWidgetCache()
216
+ const unrelatedLoader = jest.fn(async () => alwaysAvailableWidget)
217
+ const targetLoader = jest.fn(async () => requiresAiAssistantWidget)
218
+ registerCoreInjectionWidgets([
219
+ makeWidgetEntry('host', 'host/widgets/injection/unrelated/widget.ts', unrelatedLoader, {
220
+ widgetId: ALWAYS_AVAILABLE_WIDGET_ID,
221
+ }),
222
+ makeWidgetEntry('host', 'host/widgets/injection/requires-ai/widget.ts', targetLoader, {
223
+ widgetId: REQUIRES_AI_WIDGET_ID,
224
+ }),
225
+ ])
226
+ registerCoreInjectionTables([])
227
+ registerEnabledModuleIds(['host', 'ai_assistant'])
228
+
229
+ const widget = await loadInjectionWidgetById(REQUIRES_AI_WIDGET_ID)
230
+
231
+ expect(widget?.metadata.id).toBe(REQUIRES_AI_WIDGET_ID)
232
+ expect(targetLoader).toHaveBeenCalledTimes(1)
233
+ expect(unrelatedLoader).not.toHaveBeenCalled()
234
+ })
235
+
236
+ it('uses a generated widgetId hint to avoid loading unrelated data widgets', async () => {
237
+ invalidateInjectionWidgetCache()
238
+ const unrelatedLoader = jest.fn(async () => alwaysAvailableWidget)
239
+ const targetLoader = jest.fn(async () => dataWidget)
240
+ registerCoreInjectionWidgets([
241
+ makeWidgetEntry('host', 'host/widgets/injection/unrelated/widget.ts', unrelatedLoader, {
242
+ widgetId: ALWAYS_AVAILABLE_WIDGET_ID,
243
+ }),
244
+ makeWidgetEntry('host', 'host/widgets/injection/data-menu/widget.ts', targetLoader, {
245
+ widgetId: DATA_WIDGET_ID,
246
+ }),
247
+ ])
248
+ registerCoreInjectionTables([])
249
+ registerEnabledModuleIds(['host'])
250
+
251
+ const widget = await loadInjectionDataWidgetById(DATA_WIDGET_ID)
252
+
253
+ expect(widget?.metadata.id).toBe(DATA_WIDGET_ID)
254
+ expect(targetLoader).toHaveBeenCalledTimes(1)
255
+ expect(unrelatedLoader).not.toHaveBeenCalled()
256
+ })
257
+
258
+ it('skips unrelated failing loaders when resolving by id without hints', async () => {
259
+ invalidateInjectionWidgetCache()
260
+ registerCoreInjectionWidgets([
261
+ makeWidgetEntry('host', 'host/widgets/injection/broken/widget.ts', () => {
262
+ throw new Error('unrelated widget failed to import')
263
+ }),
264
+ makeWidgetEntry('host', 'host/widgets/injection/requires-ai/widget.ts', async () => requiresAiAssistantWidget),
265
+ ])
266
+ registerCoreInjectionTables([])
267
+ registerEnabledModuleIds(['host', 'ai_assistant'])
268
+
269
+ const widget = await loadInjectionWidgetById(REQUIRES_AI_WIDGET_ID)
270
+
271
+ expect(widget?.metadata.id).toBe(REQUIRES_AI_WIDGET_ID)
272
+ })
197
273
  })
@@ -240,6 +240,11 @@ type TableEntry = {
240
240
  : never
241
241
  }
242
242
  let injectionTablePromise: Promise<Map<InjectionSpotId, TableEntry[]>> | null = null
243
+ type WidgetLookupIndex = {
244
+ widgetsById: Map<string, LoadedInjectionWidget>
245
+ dataWidgetsById: Map<string, LoadedInjectionDataWidget>
246
+ }
247
+ let widgetLookupIndexPromise: { version: number; promise: Promise<WidgetLookupIndex> } | null = null
243
248
 
244
249
  function isInjectionSlotObject(value: ModuleInjectionSlot): value is InjectionWidgetPlacement & { widgetId: string; priority?: number } {
245
250
  return typeof value === 'object' && value !== null && 'widgetId' in value
@@ -252,6 +257,7 @@ function isInjectionSlotObject(value: ModuleInjectionSlot): value is InjectionWi
252
257
  export function invalidateInjectionWidgetCache() {
253
258
  widgetEntriesPromise = null
254
259
  injectionTablePromise = null
260
+ widgetLookupIndexPromise = null
255
261
  widgetCache.clear()
256
262
  warnedRequiredModuleSkips.clear()
257
263
  }
@@ -387,12 +393,79 @@ function isLoadedInjectionDataWidget(
387
393
 
388
394
  async function loadEntry(entry: WidgetEntry): Promise<InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }> {
389
395
  if (!widgetCache.has(entry.key)) {
390
- const promise = entry.loader().then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId))
396
+ const promise = Promise.resolve()
397
+ .then(() => entry.loader())
398
+ .then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId))
391
399
  widgetCache.set(entry.key, promise)
392
400
  }
393
401
  return widgetCache.get(entry.key)!
394
402
  }
395
403
 
404
+ async function loadWidgetLookupIndex(): Promise<WidgetLookupIndex> {
405
+ const version = getInjectionRegistryVersion()
406
+ if (!widgetLookupIndexPromise || widgetLookupIndexPromise.version !== version) {
407
+ const promise = Promise.resolve().then(async () => {
408
+ const widgetEntries = await loadWidgetEntries()
409
+ const settled = await Promise.allSettled(widgetEntries.map((entry) => loadEntry(entry)))
410
+ const widgetsById = new Map<string, LoadedInjectionWidget>()
411
+ const dataWidgetsById = new Map<string, LoadedInjectionDataWidget>()
412
+
413
+ settled.forEach((result, index) => {
414
+ if (result.status !== 'fulfilled') return
415
+ const entry = widgetEntries[index]
416
+ const module = result.value
417
+ if (isLoadedInjectionWidget(module)) {
418
+ if (!widgetsById.has(module.metadata.id)) {
419
+ widgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key })
420
+ }
421
+ return
422
+ }
423
+ if (!dataWidgetsById.has(module.metadata.id)) {
424
+ dataWidgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key })
425
+ }
426
+ })
427
+
428
+ return { widgetsById, dataWidgetsById }
429
+ })
430
+ widgetLookupIndexPromise = { version, promise }
431
+ }
432
+ return widgetLookupIndexPromise.promise
433
+ }
434
+
435
+ function applyRequiredModuleGate<T extends LoadedInjectionWidget | LoadedInjectionDataWidget>(
436
+ widget: T,
437
+ enabledModuleIds: ReadonlySet<string>,
438
+ ): T | null {
439
+ const missing = widgetMissingRequiredModules(widget.metadata, enabledModuleIds)
440
+ if (missing.length > 0) {
441
+ warnSkippedWidget(widget.metadata.id, missing)
442
+ return null
443
+ }
444
+ return widget
445
+ }
446
+
447
+ type HintedLookupResult<T> =
448
+ | { resolved: true; widget: T | null }
449
+ | { resolved: false }
450
+
451
+ async function tryLoadHintedWidgetById<T extends LoadedInjectionWidget | LoadedInjectionDataWidget>(
452
+ widgetId: string,
453
+ isExpectedKind: (module: InjectionAnyWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }) => boolean,
454
+ enabledModuleIds: ReadonlySet<string>,
455
+ ): Promise<HintedLookupResult<T>> {
456
+ const widgetEntries = await loadWidgetEntries()
457
+ const entry = widgetEntries.find((candidate) => candidate.widgetId === widgetId)
458
+ if (!entry) return { resolved: false }
459
+
460
+ const module = await loadEntry(entry).catch(() => null)
461
+ if (!module || module.metadata.id !== widgetId || !isExpectedKind(module)) {
462
+ return { resolved: false }
463
+ }
464
+
465
+ const widget = { ...module, moduleId: entry.moduleId, key: entry.key } as T
466
+ return { resolved: true, widget: applyRequiredModuleGate(widget, enabledModuleIds) }
467
+ }
468
+
396
469
  function getEnabledModuleIdsForInjection(): ReadonlySet<string> {
397
470
  // Prefer the explicit enabled-modules registry populated by bootstrap.
398
471
  // This is the only signal that includes modules without injection widgets
@@ -495,39 +568,23 @@ export async function loadAllInjectionWidgets(): Promise<LoadedInjectionWidget[]
495
568
  }
496
569
 
497
570
  export async function loadInjectionWidgetById(widgetId: string): Promise<LoadedInjectionWidget | null> {
498
- const widgetEntries = await loadWidgetEntries()
499
571
  const enabledModuleIds = getEnabledModuleIdsForInjection()
500
- for (const entry of widgetEntries) {
501
- const module = await loadEntry(entry)
502
- if (!isLoadedInjectionWidget(module)) continue
503
- if (module.metadata.id === widgetId) {
504
- const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds)
505
- if (missing.length > 0) {
506
- warnSkippedWidget(module.metadata.id, missing)
507
- return null
508
- }
509
- return { ...module, moduleId: entry.moduleId, key: entry.key }
510
- }
511
- }
512
- return null
572
+ const hinted = await tryLoadHintedWidgetById<LoadedInjectionWidget>(widgetId, isLoadedInjectionWidget, enabledModuleIds)
573
+ if (hinted.resolved) return hinted.widget
574
+
575
+ const index = await loadWidgetLookupIndex()
576
+ const widget = index.widgetsById.get(widgetId)
577
+ return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null
513
578
  }
514
579
 
515
580
  export async function loadInjectionDataWidgetById(widgetId: string): Promise<LoadedInjectionDataWidget | null> {
516
- const widgetEntries = await loadWidgetEntries()
517
581
  const enabledModuleIds = getEnabledModuleIdsForInjection()
518
- for (const entry of widgetEntries) {
519
- const module = await loadEntry(entry)
520
- if (!isLoadedInjectionDataWidget(module)) continue
521
- if (module.metadata.id === widgetId) {
522
- const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds)
523
- if (missing.length > 0) {
524
- warnSkippedWidget(module.metadata.id, missing)
525
- return null
526
- }
527
- return { ...module, moduleId: entry.moduleId, key: entry.key }
528
- }
529
- }
530
- return null
582
+ const hinted = await tryLoadHintedWidgetById<LoadedInjectionDataWidget>(widgetId, isLoadedInjectionDataWidget, enabledModuleIds)
583
+ if (hinted.resolved) return hinted.widget
584
+
585
+ const index = await loadWidgetLookupIndex()
586
+ const widget = index.dataWidgetsById.get(widgetId)
587
+ return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null
531
588
  }
532
589
 
533
590
  export async function loadInjectionWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionWidget[]> {