@open-mercato/shared 0.6.2-develop.3467.1.2a1818709d → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +24 -0
- package/dist/lib/bootstrap/factory.js +3 -1
- package/dist/lib/bootstrap/factory.js.map +2 -2
- package/dist/lib/commands/command-interceptor-store.js +3 -1
- package/dist/lib/commands/command-interceptor-store.js.map +2 -2
- package/dist/lib/crud/enricher-registry.js +3 -1
- package/dist/lib/crud/enricher-registry.js.map +2 -2
- package/dist/lib/crud/factory.js.map +1 -1
- package/dist/lib/crud/interceptor-registry.js +3 -1
- package/dist/lib/crud/interceptor-registry.js.map +2 -2
- package/dist/lib/crud/sync-event-runner.js +2 -8
- package/dist/lib/crud/sync-event-runner.js.map +2 -2
- package/dist/lib/db/mikro.js +27 -3
- package/dist/lib/db/mikro.js.map +2 -2
- package/dist/lib/di/container.js +16 -0
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/events/patterns.js +2 -10
- package/dist/lib/events/patterns.js.map +2 -2
- package/dist/lib/middleware/page-executor.js +2 -1
- package/dist/lib/middleware/page-executor.js.map +2 -2
- package/dist/lib/modules/registry.js +2 -1
- package/dist/lib/modules/registry.js.map +2 -2
- package/dist/lib/notifications/handler-registry.js +3 -1
- package/dist/lib/notifications/handler-registry.js.map +2 -2
- package/dist/lib/patterns/wildcard.js +42 -0
- package/dist/lib/patterns/wildcard.js.map +7 -0
- package/dist/lib/regex/linear.js +31 -0
- package/dist/lib/regex/linear.js.map +7 -0
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/entities/validation.js +15 -7
- package/dist/modules/entities/validation.js.map +2 -2
- package/dist/modules/overrides.js +857 -0
- package/dist/modules/overrides.js.map +2 -2
- package/dist/modules/registry.js +17 -4
- package/dist/modules/registry.js.map +2 -2
- package/dist/modules/widgets/injection-loader.js +12 -6
- package/dist/modules/widgets/injection-loader.js.map +2 -2
- package/jest.config.cjs +3 -1
- package/package.json +8 -8
- package/src/lib/bootstrap/factory.ts +3 -1
- package/src/lib/commands/command-interceptor-store.ts +3 -1
- package/src/lib/crud/__tests__/sync-event-runner.test.ts +5 -0
- package/src/lib/crud/enricher-registry.ts +3 -1
- package/src/lib/crud/factory.ts +2 -2
- package/src/lib/crud/interceptor-registry.ts +3 -1
- package/src/lib/crud/sync-event-runner.ts +2 -11
- package/src/lib/db/mikro.ts +27 -2
- package/src/lib/di/container.ts +24 -4
- package/src/lib/events/__tests__/patterns.test.ts +5 -0
- package/src/lib/events/patterns.ts +3 -16
- package/src/lib/middleware/page-executor.ts +2 -1
- package/src/lib/modules/registry.ts +2 -1
- package/src/lib/notifications/handler-registry.ts +3 -1
- package/src/lib/patterns/__tests__/wildcard.test.ts +27 -0
- package/src/lib/patterns/wildcard.ts +59 -0
- package/src/lib/regex/linear.ts +48 -0
- package/src/modules/__tests__/contract-overrides.test.ts +332 -0
- package/src/modules/__tests__/overrides.test.ts +23 -22
- package/src/modules/__tests__/route-overrides.test.ts +441 -0
- package/src/modules/entities/__tests__/validation.test.ts +97 -2
- package/src/modules/entities/validation.ts +14 -9
- package/src/modules/overrides.ts +1297 -27
- package/src/modules/registry.ts +27 -4
- package/src/modules/widgets/__tests__/injection-loader.required-modules.test.ts +21 -0
- package/src/modules/widgets/injection-loader.ts +12 -6
|
@@ -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 type {\n InjectionAnyWidgetModule,\n InjectionDataWidgetModule,\n InjectionWidgetMetadata,\n InjectionWidgetModule,\n InjectionSpotId,\n ModuleInjectionSlot,\n ModuleInjectionTable,\n InjectionWidgetPlacement,\n} from './injection'\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 _coreInjectionWidgetEntries = entries\n writeGlobalInjectionWidgets(entries)\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 _coreInjectionTables = tables\n writeGlobalInjectionTables(tables)\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 const pattern = new RegExp(`^${candidateSpotId.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*')}$`)\n if (!pattern.test(spotId)) 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": "
|
|
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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/jest.config.cjs
CHANGED
|
@@ -14,12 +14,14 @@ module.exports = {
|
|
|
14
14
|
{
|
|
15
15
|
tsconfig: {
|
|
16
16
|
jsx: 'react-jsx',
|
|
17
|
+
rootDir: '.',
|
|
18
|
+
ignoreDeprecations: '6.0',
|
|
17
19
|
},
|
|
18
20
|
},
|
|
19
21
|
],
|
|
20
22
|
},
|
|
21
23
|
transformIgnorePatterns: [
|
|
22
|
-
'node_modules/(?!(@mikro-orm)/)',
|
|
24
|
+
'node_modules/(?!(@mikro-orm|kysely)/)',
|
|
23
25
|
],
|
|
24
26
|
testMatch: ['<rootDir>/src/**/__tests__/**/*.test.(ts|tsx)'],
|
|
25
27
|
passWithNoTests: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/shared",
|
|
3
|
-
"version": "0.6.2
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -89,15 +89,16 @@
|
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@mikro-orm/core": "^7.0.
|
|
93
|
-
"@mikro-orm/decorators": "^7.0.
|
|
94
|
-
"@mikro-orm/postgresql": "^7.0.
|
|
95
|
-
"@open-mercato/cache": "0.6.2
|
|
92
|
+
"@mikro-orm/core": "^7.0.17",
|
|
93
|
+
"@mikro-orm/decorators": "^7.0.17",
|
|
94
|
+
"@mikro-orm/postgresql": "^7.0.17",
|
|
95
|
+
"@open-mercato/cache": "0.6.2",
|
|
96
96
|
"dotenv": "^17.4.2",
|
|
97
97
|
"rate-limiter-flexible": "^11.1.0",
|
|
98
|
+
"re2js": "2.8.2",
|
|
98
99
|
"reflect-metadata": "^0.2.2",
|
|
99
100
|
"sanitize-html": "^2.17.4",
|
|
100
|
-
"undici": "^
|
|
101
|
+
"undici": "^8.3.0"
|
|
101
102
|
},
|
|
102
103
|
"devDependencies": {
|
|
103
104
|
"@types/jest": "^30.0.0",
|
|
@@ -112,6 +113,5 @@
|
|
|
112
113
|
"type": "git",
|
|
113
114
|
"url": "https://github.com/open-mercato/open-mercato",
|
|
114
115
|
"directory": "packages/shared"
|
|
115
|
-
}
|
|
116
|
-
"stableVersion": "0.6.1"
|
|
116
|
+
}
|
|
117
117
|
}
|
|
@@ -13,6 +13,7 @@ import { registerMutationGuards } from '../crud/mutation-guard-store'
|
|
|
13
13
|
import { registerCommandInterceptors } from '../commands/command-interceptor-store'
|
|
14
14
|
import { registerNotificationHandlers } from '../notifications/handler-registry'
|
|
15
15
|
import { clearRegisteredIntegrations, registerBundles, registerIntegrations } from '../../modules/integrations/types'
|
|
16
|
+
import { applyComponentOverridesToEntries } from '../../modules/overrides'
|
|
16
17
|
|
|
17
18
|
let _bootstrapped = false
|
|
18
19
|
|
|
@@ -82,7 +83,8 @@ export function createBootstrap(data: BootstrapData, options: BootstrapOptions =
|
|
|
82
83
|
|
|
83
84
|
// === 6d. Component overrides (for page/component replacement) ===
|
|
84
85
|
if (data.componentOverrideEntries) {
|
|
85
|
-
const
|
|
86
|
+
const finalEntries = applyComponentOverridesToEntries(data.componentOverrideEntries)
|
|
87
|
+
const allOverrides = finalEntries.flatMap((entry) => entry.componentOverrides ?? [])
|
|
86
88
|
registerComponentOverrides(allOverrides)
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { CommandInterceptor } from './command-interceptor'
|
|
9
|
+
import { applyCommandInterceptorOverridesToEntries } from '../../modules/overrides'
|
|
9
10
|
|
|
10
11
|
export interface CommandInterceptorRegistryEntry {
|
|
11
12
|
moduleId: string
|
|
@@ -41,8 +42,9 @@ function writeGlobal(entries: CommandInterceptorRegistryEntry[]) {
|
|
|
41
42
|
export function registerCommandInterceptors(
|
|
42
43
|
entries: Array<{ moduleId: string; interceptors: CommandInterceptor[] }>,
|
|
43
44
|
) {
|
|
45
|
+
const finalEntries = applyCommandInterceptorOverridesToEntries(entries)
|
|
44
46
|
const flat: CommandInterceptorRegistryEntry[] = []
|
|
45
|
-
for (const entry of
|
|
47
|
+
for (const entry of finalEntries) {
|
|
46
48
|
for (const interceptor of entry.interceptors) {
|
|
47
49
|
flat.push({ moduleId: entry.moduleId, interceptor })
|
|
48
50
|
}
|
|
@@ -22,6 +22,11 @@ describe('matchesEventPattern', () => {
|
|
|
22
22
|
expect(matchesEventPattern('example.*', 'example.todo.created')).toBe(true)
|
|
23
23
|
expect(matchesEventPattern('example.*', 'example.item.updated')).toBe(true)
|
|
24
24
|
})
|
|
25
|
+
|
|
26
|
+
it('treats regex metacharacters as literals', () => {
|
|
27
|
+
expect(matchesEventPattern('example.(todo).*', 'example.(todo).created')).toBe(true)
|
|
28
|
+
expect(matchesEventPattern('example.+.*', 'example.todo.created')).toBe(false)
|
|
29
|
+
})
|
|
25
30
|
})
|
|
26
31
|
|
|
27
32
|
describe('collectSyncSubscribers', () => {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { EnricherRegistryEntry, EnricherQueryEngineConfig, ResponseEnricher } from './response-enricher'
|
|
9
|
+
import { applyResponseEnricherOverridesToEntries } from '../../modules/overrides'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Selector for filtering enrichers by execution surface.
|
|
@@ -47,8 +48,9 @@ function writeGlobalEnrichers(entries: EnricherRegistryEntry[]) {
|
|
|
47
48
|
export function registerResponseEnrichers(
|
|
48
49
|
entries: Array<{ moduleId: string; enrichers: ResponseEnricher[] }>,
|
|
49
50
|
) {
|
|
51
|
+
const finalEntries = applyResponseEnricherOverridesToEntries(entries)
|
|
50
52
|
const flat: EnricherRegistryEntry[] = []
|
|
51
|
-
for (const entry of
|
|
53
|
+
for (const entry of finalEntries) {
|
|
52
54
|
for (const enricher of entry.enrichers) {
|
|
53
55
|
flat.push({ moduleId: entry.moduleId, enricher })
|
|
54
56
|
}
|
package/src/lib/crud/factory.ts
CHANGED
|
@@ -979,7 +979,7 @@ export function makeCrudRoute<TCreate = any, TUpdate = any, TList = any>(opts: C
|
|
|
979
979
|
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId ?? '',
|
|
980
980
|
tenantId: ctx.auth.tenantId ?? '',
|
|
981
981
|
userId: ctx.auth.sub,
|
|
982
|
-
em: ctx.container.resolve('em'),
|
|
982
|
+
em: ctx.container.resolve('em') as EntityManager,
|
|
983
983
|
container: ctx.container,
|
|
984
984
|
userFeatures,
|
|
985
985
|
}
|
|
@@ -1009,7 +1009,7 @@ export function makeCrudRoute<TCreate = any, TUpdate = any, TList = any>(opts: C
|
|
|
1009
1009
|
userId: ctx.auth.sub,
|
|
1010
1010
|
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId ?? '',
|
|
1011
1011
|
tenantId: ctx.auth.tenantId ?? '',
|
|
1012
|
-
em: ctx.container.resolve('em'),
|
|
1012
|
+
em: ctx.container.resolve('em') as EntityManager,
|
|
1013
1013
|
container: ctx.container,
|
|
1014
1014
|
userFeatures: await resolveUserFeatures(ctx),
|
|
1015
1015
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ApiInterceptor, ApiInterceptorMethod, ApiInterceptorRegistryEntry } from './api-interceptor'
|
|
2
|
+
import { applyApiInterceptorOverridesToEntries } from '../../modules/overrides'
|
|
2
3
|
|
|
3
4
|
let _interceptorEntries: ApiInterceptorRegistryEntry[] | null = null
|
|
4
5
|
const GLOBAL_INTERCEPTOR_KEY = '__openMercatoApiInterceptors__'
|
|
@@ -21,8 +22,9 @@ function writeGlobalInterceptors(entries: ApiInterceptorRegistryEntry[]) {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export function registerApiInterceptors(entries: Array<{ moduleId: string; interceptors: ApiInterceptor[] }>) {
|
|
25
|
+
const finalEntries = applyApiInterceptorOverridesToEntries(entries)
|
|
24
26
|
const flat: ApiInterceptorRegistryEntry[] = []
|
|
25
|
-
|
|
27
|
+
finalEntries.forEach((entry, moduleOrder) => {
|
|
26
28
|
entry.interceptors.forEach((interceptor, interceptorOrder) => {
|
|
27
29
|
flat.push({
|
|
28
30
|
moduleId: entry.moduleId,
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import type { SyncCrudEventPayload } from './sync-event-types'
|
|
2
2
|
import type { SyncSubscriberEntry } from './sync-subscriber-store'
|
|
3
|
+
import { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'
|
|
3
4
|
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Event pattern matching
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
|
|
8
|
-
function escapeRegex(input: string): string {
|
|
9
|
-
// Escape all characters with special meaning in regular expressions
|
|
10
|
-
return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
11
|
-
}
|
|
12
|
-
|
|
13
9
|
export function matchesEventPattern(pattern: string, eventId: string): boolean {
|
|
14
|
-
|
|
15
|
-
if (pattern === '*') return true
|
|
16
|
-
// Escape the pattern for literal use in a RegExp, then turn "\*" into ".*" for wildcard support
|
|
17
|
-
const escaped = escapeRegex(pattern).replace(/\\\*/g, '.*')
|
|
18
|
-
const regex = new RegExp('^' + escaped + '$')
|
|
19
|
-
return regex.test(eventId)
|
|
10
|
+
return matchWildcardPattern(eventId, pattern)
|
|
20
11
|
}
|
|
21
12
|
|
|
22
13
|
// ---------------------------------------------------------------------------
|
package/src/lib/db/mikro.ts
CHANGED
|
@@ -70,6 +70,18 @@ export async function getOrm() {
|
|
|
70
70
|
|
|
71
71
|
const sslConfig = getSslConfig()
|
|
72
72
|
|
|
73
|
+
if (process.env.OM_DB_POOL_DEBUG === '1' || process.env.OM_INTEGRATION_TEST === 'true') {
|
|
74
|
+
console.log('[orm] pool config', {
|
|
75
|
+
poolMin,
|
|
76
|
+
poolMax,
|
|
77
|
+
poolIdleTimeout,
|
|
78
|
+
poolAcquireTimeout,
|
|
79
|
+
idleSessionTimeoutMs,
|
|
80
|
+
idleInTransactionTimeoutMs,
|
|
81
|
+
nodeEnv: process.env.NODE_ENV,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
73
85
|
ormInstance = await MikroORM.init<PostgreSqlDriver, PostgreSqlEntityManager<PostgreSqlDriver>>({
|
|
74
86
|
driver: PostgreSqlDriver,
|
|
75
87
|
clientUrl,
|
|
@@ -82,18 +94,31 @@ export async function getOrm() {
|
|
|
82
94
|
metadataProvider: ReflectMetadataProvider,
|
|
83
95
|
// MikroORM v7 pool shape (min/max/idleTimeoutMillis). Knex-era `acquireTimeoutMillis` /
|
|
84
96
|
// `destroyTimeoutMillis` were removed; acquire wait maps to pg `connectionTimeoutMillis`
|
|
85
|
-
// below under `driverOptions`.
|
|
97
|
+
// below under `driverOptions`. Mirror `connectionTimeoutMillis` here too — older Mikro
|
|
98
|
+
// versions read it from `pool`; v7 reads from `driverOptions` but accepting both
|
|
99
|
+
// costs nothing and protects us from upstream config-merge regressions.
|
|
86
100
|
pool: {
|
|
87
101
|
min: poolMin,
|
|
88
102
|
max: poolMax,
|
|
89
103
|
idleTimeoutMillis: poolIdleTimeout,
|
|
90
|
-
|
|
104
|
+
acquireTimeoutMillis: poolAcquireTimeout,
|
|
105
|
+
} as any,
|
|
91
106
|
// Driver options are merged into pg.PoolConfig (ClientConfig + pg-pool).
|
|
92
107
|
driverOptions: {
|
|
93
108
|
connectionTimeoutMillis: poolAcquireTimeout,
|
|
94
109
|
idle_in_transaction_session_timeout: idleInTransactionTimeoutMs,
|
|
95
110
|
options: connectionOptions,
|
|
96
111
|
ssl: sslConfig,
|
|
112
|
+
onPoolCreated: (pool: any) => {
|
|
113
|
+
if (process.env.OM_DB_POOL_DEBUG === '1' || process.env.OM_INTEGRATION_TEST === 'true') {
|
|
114
|
+
console.log('[orm] pg pool created with options', {
|
|
115
|
+
max: pool.options?.max,
|
|
116
|
+
min: pool.options?.min,
|
|
117
|
+
idleTimeoutMillis: pool.options?.idleTimeoutMillis,
|
|
118
|
+
connectionTimeoutMillis: pool.options?.connectionTimeoutMillis,
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
},
|
|
97
122
|
},
|
|
98
123
|
})
|
|
99
124
|
|
package/src/lib/di/container.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { createContainer, asValue, AwilixContainer, InjectionMode } from 'awilix'
|
|
1
|
+
import { createContainer, asValue, AwilixContainer, InjectionMode, type Resolver } from 'awilix'
|
|
2
2
|
import { RequestContext } from '@mikro-orm/core'
|
|
3
3
|
import { getOrm } from '@open-mercato/shared/lib/db/mikro'
|
|
4
4
|
import { EntityManager } from '@mikro-orm/postgresql'
|
|
5
5
|
import { BasicQueryEngine } from '@open-mercato/shared/lib/query/engine'
|
|
6
6
|
import { DefaultDataEngine } from '@open-mercato/shared/lib/data/engine'
|
|
7
7
|
import { commandRegistry, CommandBus } from '@open-mercato/shared/lib/commands'
|
|
8
|
+
import { applyDiOverridesToContainer } from '@open-mercato/shared/modules/overrides'
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
type DynamicCradle = Record<string, any>
|
|
11
|
+
|
|
12
|
+
export type AppContainer = AwilixContainer<DynamicCradle>
|
|
13
|
+
export type DiRegistrar = (container: AppContainer) => void
|
|
11
14
|
|
|
12
15
|
// Registration pattern for publishable packages
|
|
13
16
|
// Use globalThis to survive tsx/esbuild module duplication issue where the same
|
|
@@ -38,13 +41,26 @@ export function getDiRegistrars(): DiRegistrar[] {
|
|
|
38
41
|
return registrars
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
function isAwilixResolver(value: unknown): value is Resolver<unknown> {
|
|
45
|
+
return Boolean(value && typeof value === 'object' && typeof (value as { resolve?: unknown }).resolve === 'function')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function toAwilixRegistrations(registrations: Record<string, unknown>): Record<string, Resolver<any>> {
|
|
49
|
+
return Object.fromEntries(
|
|
50
|
+
Object.entries(registrations).map(([key, value]) => [
|
|
51
|
+
key,
|
|
52
|
+
isAwilixResolver(value) ? value : asValue(value),
|
|
53
|
+
]),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
export async function createRequestContainer(): Promise<AppContainer> {
|
|
42
58
|
const diRegistrars = getDiRegistrars()
|
|
43
59
|
const orm = await getOrm()
|
|
44
60
|
// Use a fresh event manager so request-level subscribers (e.g., encryption) don't pile up globally
|
|
45
61
|
const baseEm = (RequestContext.getEntityManager() as any) ?? orm.em
|
|
46
62
|
const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true }) as unknown as EntityManager
|
|
47
|
-
const container = createContainer({ injectionMode: InjectionMode.CLASSIC })
|
|
63
|
+
const container = createContainer<DynamicCradle>({ injectionMode: InjectionMode.CLASSIC })
|
|
48
64
|
// Core registrations
|
|
49
65
|
container.register({
|
|
50
66
|
em: asValue(em),
|
|
@@ -82,6 +98,10 @@ export async function createRequestContainer(): Promise<AppContainer> {
|
|
|
82
98
|
} catch {}
|
|
83
99
|
}
|
|
84
100
|
} catch {}
|
|
101
|
+
applyDiOverridesToContainer({
|
|
102
|
+
register: (registrations) => container.register(toAwilixRegistrations(registrations)),
|
|
103
|
+
unregister: (key) => container.register({ [key]: asValue(undefined) }),
|
|
104
|
+
})
|
|
85
105
|
// Ensure tenant encryption subscriber is always registered on the fresh request-scoped EM
|
|
86
106
|
try {
|
|
87
107
|
const emForEnc = container.resolve('em') as any
|
|
@@ -19,6 +19,11 @@ describe('matchEventPattern', () => {
|
|
|
19
19
|
expect(matchEventPattern('customers.person', 'customers.*')).toBe(true)
|
|
20
20
|
expect(matchEventPattern('customers.person.deleted', 'customers.*')).toBe(false)
|
|
21
21
|
})
|
|
22
|
+
|
|
23
|
+
it('treats regex metacharacters as literal pattern characters', () => {
|
|
24
|
+
expect(matchEventPattern('customers.(person).created', 'customers.(person).*')).toBe(true)
|
|
25
|
+
expect(matchEventPattern('customers.person.created', 'customers.+.*')).toBe(false)
|
|
26
|
+
})
|
|
22
27
|
})
|
|
23
28
|
|
|
24
29
|
describe('prefix mode', () => {
|
|
@@ -1,24 +1,11 @@
|
|
|
1
|
+
import { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'
|
|
2
|
+
|
|
1
3
|
export type EventPatternMode = 'single-segment' | 'prefix'
|
|
2
4
|
|
|
3
5
|
type MatchEventPatternOptions = {
|
|
4
6
|
mode?: EventPatternMode
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
const singleSegmentPatternCache = new Map<string, RegExp>()
|
|
8
|
-
|
|
9
|
-
function getSingleSegmentPatternRegex(pattern: string): RegExp {
|
|
10
|
-
const existing = singleSegmentPatternCache.get(pattern)
|
|
11
|
-
if (existing) return existing
|
|
12
|
-
|
|
13
|
-
const regexPattern = pattern
|
|
14
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
15
|
-
.replace(/\*/g, '[^.]+')
|
|
16
|
-
|
|
17
|
-
const compiled = new RegExp(`^${regexPattern}$`)
|
|
18
|
-
singleSegmentPatternCache.set(pattern, compiled)
|
|
19
|
-
return compiled
|
|
20
|
-
}
|
|
21
|
-
|
|
22
9
|
export function matchEventPattern(
|
|
23
10
|
eventName: string,
|
|
24
11
|
pattern: string,
|
|
@@ -44,7 +31,7 @@ export function matchEventPattern(
|
|
|
44
31
|
return false
|
|
45
32
|
}
|
|
46
33
|
|
|
47
|
-
return
|
|
34
|
+
return matchWildcardPattern(eventName, pattern, { singleSegmentWildcard: true })
|
|
48
35
|
}
|
|
49
36
|
|
|
50
37
|
export function matchAnyEventPattern(
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
PageRouteMiddleware,
|
|
7
7
|
} from '@open-mercato/shared/modules/middleware/page'
|
|
8
8
|
import { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'
|
|
9
|
+
import { applyPageGuardOverridesToEntries } from '@open-mercato/shared/modules/overrides'
|
|
9
10
|
|
|
10
11
|
type ExecutePageMiddlewareArgs = {
|
|
11
12
|
entries: PageMiddlewareRegistryEntry[]
|
|
@@ -31,7 +32,7 @@ function flattenAndSortMiddleware(
|
|
|
31
32
|
mode: PageMiddlewareMode,
|
|
32
33
|
pathname: string
|
|
33
34
|
): PageRouteMiddleware[] {
|
|
34
|
-
return entries
|
|
35
|
+
return applyPageGuardOverridesToEntries(entries)
|
|
35
36
|
.flatMap((entry) => entry.middleware)
|
|
36
37
|
.filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))
|
|
37
38
|
.sort(compareMiddleware)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Module } from '@open-mercato/shared/modules/registry'
|
|
2
|
+
import { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'
|
|
2
3
|
|
|
3
4
|
// Registration pattern for publishable packages
|
|
4
5
|
let _modules: Module[] | null = null
|
|
@@ -7,7 +8,7 @@ export function registerModules(modules: Module[]) {
|
|
|
7
8
|
if (_modules !== null && process.env.NODE_ENV === 'development') {
|
|
8
9
|
console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')
|
|
9
10
|
}
|
|
10
|
-
_modules = modules
|
|
11
|
+
_modules = applyModuleOverridesToModules(modules)
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function getModules(): Module[] {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'
|
|
2
|
+
import { applyNotificationHandlerOverridesToEntries } from '@open-mercato/shared/modules/overrides'
|
|
2
3
|
|
|
3
4
|
const GLOBAL_NOTIFICATION_HANDLERS_KEY = '__openMercatoNotificationHandlers__'
|
|
4
5
|
|
|
@@ -29,8 +30,9 @@ function writeGlobalEntries(entries: NotificationHandlerRegistryEntry[]) {
|
|
|
29
30
|
export function registerNotificationHandlers(
|
|
30
31
|
entries: Array<{ moduleId: string; handlers: NotificationHandler[] }>,
|
|
31
32
|
) {
|
|
33
|
+
const finalEntries = applyNotificationHandlerOverridesToEntries(entries)
|
|
32
34
|
const flat: NotificationHandlerRegistryEntry[] = []
|
|
33
|
-
for (const entry of
|
|
35
|
+
for (const entry of finalEntries) {
|
|
34
36
|
for (const handler of entry.handlers ?? []) {
|
|
35
37
|
flat.push({ moduleId: entry.moduleId, handler })
|
|
36
38
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { matchWildcardPattern } from '../wildcard'
|
|
2
|
+
|
|
3
|
+
describe('matchWildcardPattern', () => {
|
|
4
|
+
it('matches exact values and the global wildcard', () => {
|
|
5
|
+
expect(matchWildcardPattern('customers.person.created', 'customers.person.created')).toBe(true)
|
|
6
|
+
expect(matchWildcardPattern('customers.person.created', '*')).toBe(true)
|
|
7
|
+
expect(matchWildcardPattern('customers.person.created', 'sales.order.created')).toBe(false)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('matches multi-segment wildcard patterns without regex execution', () => {
|
|
11
|
+
expect(matchWildcardPattern('example.todo.created', 'example.*')).toBe(true)
|
|
12
|
+
expect(matchWildcardPattern('example.todo.created', 'example.*.created')).toBe(true)
|
|
13
|
+
expect(matchWildcardPattern('example.todo.created', '*.todo.*')).toBe(true)
|
|
14
|
+
expect(matchWildcardPattern('example.todo.created', 'customers.*')).toBe(false)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('can restrict wildcards to a single dotted segment', () => {
|
|
18
|
+
expect(matchWildcardPattern('customers.person', 'customers.*', { singleSegmentWildcard: true })).toBe(true)
|
|
19
|
+
expect(matchWildcardPattern('customers.person.deleted', 'customers.*', { singleSegmentWildcard: true })).toBe(false)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('treats regex metacharacters as literals', () => {
|
|
23
|
+
expect(matchWildcardPattern('cache:(tenant)[1]', 'cache:(tenant)[*]')).toBe(true)
|
|
24
|
+
expect(matchWildcardPattern('cache:(tenant)[1]', 'cache:.+')).toBe(false)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type WildcardMatchOptions = {
|
|
2
|
+
singleSegmentWildcard?: boolean
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function matchWildcardSegment(pattern: string, value: string, options: WildcardMatchOptions): boolean {
|
|
6
|
+
let patternIndex = 0
|
|
7
|
+
let valueIndex = 0
|
|
8
|
+
let starIndex = -1
|
|
9
|
+
let valueRetryIndex = 0
|
|
10
|
+
|
|
11
|
+
while (valueIndex < value.length) {
|
|
12
|
+
const patternChar = pattern[patternIndex]
|
|
13
|
+
const valueChar = value[valueIndex]
|
|
14
|
+
|
|
15
|
+
if (patternChar === '*') {
|
|
16
|
+
starIndex = patternIndex
|
|
17
|
+
valueRetryIndex = valueIndex
|
|
18
|
+
patternIndex += 1
|
|
19
|
+
continue
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (patternChar === valueChar) {
|
|
23
|
+
patternIndex += 1
|
|
24
|
+
valueIndex += 1
|
|
25
|
+
continue
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
starIndex !== -1
|
|
30
|
+
&& (!options.singleSegmentWildcard || value[valueRetryIndex] !== '.')
|
|
31
|
+
) {
|
|
32
|
+
patternIndex = starIndex + 1
|
|
33
|
+
valueRetryIndex += 1
|
|
34
|
+
valueIndex = valueRetryIndex
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
while (pattern[patternIndex] === '*') {
|
|
42
|
+
patternIndex += 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return patternIndex === pattern.length
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function matchWildcardPattern(
|
|
49
|
+
value: string,
|
|
50
|
+
pattern: string,
|
|
51
|
+
options: WildcardMatchOptions = {},
|
|
52
|
+
): boolean {
|
|
53
|
+
if (pattern === value) return true
|
|
54
|
+
if (pattern === '*') return true
|
|
55
|
+
if (!pattern.includes('*')) return false
|
|
56
|
+
|
|
57
|
+
return matchWildcardSegment(pattern, value, options)
|
|
58
|
+
}
|
|
59
|
+
|