@open-mercato/shared 0.7.1-develop.7136.1.0f76137a1d → 0.7.1-develop.7148.1.3076e5ccf7

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,2 +1,2 @@
1
- [build:shared] found 270 entry points
1
+ [build:shared] found 271 entry points
2
2
  [build:shared] built successfully
package/AGENTS.md CHANGED
@@ -296,10 +296,12 @@ MUST rules:
296
296
  - API-route override keys are `'METHOD /api/path'` (method case-insensitive, path leading slash optional). Trailing slashes are stripped.
297
297
  - Page-route override keys are `'/backend/path'` or `'/frontend/path'`.
298
298
  - `null` disables the matching method; `{ handler, metadata? }` replaces it. Disabling every method on an entry drops the entry.
299
- - The dispatcher SHOULD run from `bootstrap.ts` BEFORE any registry first-loads (`registerApiRouteManifests`, widget registries, notification registries, etc.) so the overrides take effect when the registry stores entries.
299
+ - The dispatcher SHOULD run from `bootstrap.ts` BEFORE any registry first-loads (`registerApiRouteManifests`, widget registries, notification registries, etc.) so the overrides take effect when the registry stores entries. It MUST also run in the browser before `ClientBootstrap` re-registers the injection/dashboard/notification registries — pass `{ domains: ['widgets', 'notifications'] }` there so server-only appliers are not reported as unwired — otherwise the raw generated registries overwrite the filtered ones and a disabled widget returns on hydration (#5152).
300
+ - `widgets.injection` keys accept EITHER identifier: the generated `entry.key` (`module:widget:file`) or the widget's own `entry.widgetId` (`module.injection.widget`). Both the entries registry and the injection tables resolve either spelling, so listing one is enough; listing both is accepted silently, and only genuinely conflicting values warn.
301
+ - An `enabledModules` entry gated on a **server-only** env var (anything not `NEXT_PUBLIC_*`) MUST NOT carry `widgets` or `notifications` overrides. The browser re-evaluates `modules.ts` with those reads `undefined`, so the entry is absent there: the server dispatches the override and the client does not, and the widget returns on hydration with nothing logged. Declare such an override on an ungated entry, or gate it on a `NEXT_PUBLIC_` var. Outside production `ClientBootstrap` warns for every module id in `enabled-module-ids.generated` that the browser-evaluated list is missing.
300
302
  - Adding a new override domain MUST follow the umbrella spec: typed sub-shape + composer + runtime hook + tests + AGENTS.md/docs update + status-table tick.
301
303
  - `nav.groupOrder` **prepends** group ids ahead of the built-in ordering; ids it does not name keep their current position. It is a default, resolved beneath role and per-user sidebar preferences, and an absent override MUST leave ordering byte-identical.
302
- - Nav ordering state lives on `globalThis` because its reader is `@open-mercato/core` while its writer is app bootstrap; a module-local variable would be invisible across duplicated module instances in standalone builds.
304
+ - Nav ordering state lives on `globalThis` because its reader is `@open-mercato/core` while its writer is app bootstrap; a module-local variable would be invisible across duplicated module instances in standalone builds. The injection-widget `key`⇄`widgetId` alias index is on `globalThis` for the same reason — `@open-mercato/ui` writes it while filtering entries, `@open-mercato/core` reads it while filtering tables.
303
305
 
304
306
  ### Query Engine Extensibility (UMES)
305
307
 
@@ -89,7 +89,7 @@ async function registerWidgetsAndOptionalPackages(data, options) {
89
89
  if (!options.skipCoreInjectionWidgets) {
90
90
  coreInjection.registerCoreInjectionWidgets(data.injectionWidgetEntries);
91
91
  }
92
- coreInjection.registerCoreInjectionTables(data.injectionTables);
92
+ coreInjection.registerCoreInjectionTables(data.injectionTables, data.injectionWidgetEntries);
93
93
  coreInjection.registerEnabledModuleIds(
94
94
  data.modules.map((module) => module.id).filter((id) => typeof id === "string" && id.length > 0)
95
95
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/bootstrap/factory.ts"],
4
- "sourcesContent": ["import type { BootstrapData, BootstrapOptions } from './types'\nimport { registerOrmEntities } from '../db/mikro'\nimport { registerAppDiRegistrar, registerDiRegistrars } from '../di/container'\nimport { registerModules } from '../modules/registry'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport { registerEntityFields } from '../encryption/entityFields'\nimport { registerSearchModuleConfigs } from '../../modules/search'\nimport { registerAnalyticsModuleConfigs } from '../../modules/analytics'\nimport { registerCodeWorkflowEntries } from '../../modules/workflows/code-registry'\nimport { registerResponseEnrichers } from '../crud/enricher-registry'\nimport { registerApiInterceptors } from '../crud/interceptor-registry'\nimport { registerComponentOverrides } from '../../modules/widgets/component-registry'\nimport { registerMutationGuards } from '../crud/mutation-guard-store'\nimport { registerCommandInterceptors } from '../commands/command-interceptor-store'\nimport { registerCommandLoaders } from '../commands/registry'\nimport { registerNotificationHandlers } from '../notifications/handler-registry'\nimport { clearRegisteredIntegrations, registerBundles, registerIntegrations } from '../../modules/integrations/types'\nimport { applyComponentOverridesToEntries } from '../../modules/overrides'\n\nconst _bootstrappedKeys = new Set<string>()\n\n// Store the async registration promise so callers can await it if needed\nlet _asyncRegistrationPromise: Promise<void> | null = null\n\n/**\n * Creates a bootstrap function that registers all application dependencies.\n *\n * The returned function should be called once at application startup.\n * In development mode, it can be called multiple times (for HMR).\n *\n * @param data - All generated registry data from .mercato/generated/\n * @param options - Optional configuration\n * @returns A bootstrap function to call at app startup\n */\nexport function createBootstrap(data: BootstrapData, options: BootstrapOptions = {}) {\n return function bootstrap(): void {\n const registrationKey = options.registrationKey ?? 'default'\n if (options.appDiRegistrar) {\n registerAppDiRegistrar(options.appDiRegistrar)\n }\n // In development, always re-run registrations to handle HMR\n // (Module state may be reset when Turbopack reloads packages)\n if (_bootstrappedKeys.has(registrationKey) && process.env.NODE_ENV !== 'development') return\n _bootstrappedKeys.add(registrationKey)\n\n // === 1. Foundation: ORM entities and DI registrars ===\n registerOrmEntities(data.entities)\n registerDiRegistrars(data.diRegistrars.filter((r): r is NonNullable<typeof r> => r != null))\n\n // === 2. Modules registry (required by i18n, query engine, dashboards, CLI) ===\n registerModules(data.modules)\n clearRegisteredIntegrations()\n for (const module of data.modules) {\n if (module.integrations?.length) {\n registerIntegrations(module.integrations)\n }\n if (module.bundles?.length) {\n registerBundles(module.bundles)\n }\n }\n\n // === 3. Entity IDs (required by encryption, indexing, entity links) ===\n registerEntityIds(data.entityIds)\n\n // === 4. Entity fields registry (for encryption manager, Turbopack compatibility) ===\n if (data.entityFieldsRegistry) {\n registerEntityFields(data.entityFieldsRegistry)\n }\n\n // === 5. Search module configs (for search service registration in DI) ===\n if (data.searchModuleConfigs) {\n registerSearchModuleConfigs(data.searchModuleConfigs)\n }\n\n // === 6. Analytics module configs (for dashboard widgets and analytics API) ===\n if (data.analyticsModuleConfigs) {\n registerAnalyticsModuleConfigs(data.analyticsModuleConfigs)\n }\n\n // === 6a. Code workflow definitions (so CLI/worker processes resolve them like the app runtime) ===\n if (data.codeWorkflows?.length) {\n registerCodeWorkflowEntries(data.codeWorkflows)\n }\n\n // === 6b. Response enrichers (for CRUD response enrichment) ===\n if (data.enricherEntries) {\n registerResponseEnrichers(data.enricherEntries)\n }\n\n // === 6c. API interceptors (for CRUD route interception) ===\n if (data.interceptorEntries) {\n registerApiInterceptors(data.interceptorEntries)\n }\n\n // === 6d. Component overrides (for page/component replacement) ===\n if (data.componentOverrideEntries) {\n const finalEntries = applyComponentOverridesToEntries(data.componentOverrideEntries)\n const allOverrides = finalEntries.flatMap((entry) => entry.componentOverrides ?? [])\n registerComponentOverrides(allOverrides)\n }\n\n // === 6e. Mutation guards (for CRUD mutation lifecycle) ===\n if (data.guardEntries) {\n registerMutationGuards(data.guardEntries)\n }\n\n // === 6f. Command interceptors (for command bus lifecycle) ===\n if (data.commandInterceptorEntries) {\n registerCommandInterceptors(data.commandInterceptorEntries)\n }\n\n // === 6f.1. Command loaders (for lazy command handler registration) ===\n if (data.commandLoaderEntries) {\n registerCommandLoaders(data.commandLoaderEntries)\n }\n\n // === 6g. Notification handlers (reactive notification side-effects) ===\n if (data.notificationHandlerEntries) {\n registerNotificationHandlers(data.notificationHandlerEntries)\n }\n\n // === 7-8. UI Widgets and Optional packages (async to avoid circular deps) ===\n // Store the promise so CLI context can await it\n _asyncRegistrationPromise = registerWidgetsAndOptionalPackages(data, options)\n void _asyncRegistrationPromise\n\n options.onRegistrationComplete?.()\n }\n}\n\n/**\n * Wait for async registrations (CLI modules, widgets, etc.) to complete.\n * Call this after bootstrap() in CLI context where you need modules immediately.\n */\nexport async function waitForAsyncRegistration(): Promise<void> {\n if (_asyncRegistrationPromise) {\n await _asyncRegistrationPromise\n }\n}\n\nasync function registerWidgetsAndOptionalPackages(data: BootstrapData, options: BootstrapOptions): Promise<void> {\n // Register widget data required by server-side injection independently from\n // browser-facing UI registries. API-only bootstraps avoid loading @open-mercato/ui.\n try {\n const coreInjection = await import('@open-mercato/core/modules/widgets/lib/injection')\n if (!options.skipCoreInjectionWidgets) {\n coreInjection.registerCoreInjectionWidgets(data.injectionWidgetEntries)\n }\n coreInjection.registerCoreInjectionTables(data.injectionTables)\n coreInjection.registerEnabledModuleIds(\n data.modules.map((module) => module.id).filter((id): id is string => typeof id === 'string' && id.length > 0),\n )\n\n if (!options.skipUiRegistries) {\n const [dashboardRegistry, injectionRegistry] = await Promise.all([\n import('@open-mercato/ui/backend/dashboard/widgetRegistry'),\n import('@open-mercato/ui/backend/injection/widgetRegistry'),\n ])\n dashboardRegistry.registerDashboardWidgets(data.dashboardWidgetEntries)\n injectionRegistry.registerInjectionWidgets(data.injectionWidgetEntries)\n }\n } catch {\n // UI packages may not be available in all contexts\n }\n\n // Note: Search module configs are registered synchronously in the main bootstrap.\n // The actual registerSearchModule() call happens in core/bootstrap.ts when the\n // DI container is created, using getSearchModuleConfigs() from the global registry.\n\n // Note: CLI module registration is handled separately in CLI context\n // via bootstrapFromAppRoot in dynamicLoader. We don't import CLI here\n // to avoid Turbopack tracing through the CLI package in Next.js context.\n}\n\n/**\n * Check if bootstrap has been called.\n */\nexport function isBootstrapped(): boolean {\n return _bootstrappedKeys.size > 0\n}\n\n/**\n * Reset bootstrap state. Useful for testing.\n */\nexport function resetBootstrapState(): void {\n _bootstrappedKeys.clear()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { BootstrapData, BootstrapOptions } from './types'\nimport { registerOrmEntities } from '../db/mikro'\nimport { registerAppDiRegistrar, registerDiRegistrars } from '../di/container'\nimport { registerModules } from '../modules/registry'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport { registerEntityFields } from '../encryption/entityFields'\nimport { registerSearchModuleConfigs } from '../../modules/search'\nimport { registerAnalyticsModuleConfigs } from '../../modules/analytics'\nimport { registerCodeWorkflowEntries } from '../../modules/workflows/code-registry'\nimport { registerResponseEnrichers } from '../crud/enricher-registry'\nimport { registerApiInterceptors } from '../crud/interceptor-registry'\nimport { registerComponentOverrides } from '../../modules/widgets/component-registry'\nimport { registerMutationGuards } from '../crud/mutation-guard-store'\nimport { registerCommandInterceptors } from '../commands/command-interceptor-store'\nimport { registerCommandLoaders } from '../commands/registry'\nimport { registerNotificationHandlers } from '../notifications/handler-registry'\nimport { clearRegisteredIntegrations, registerBundles, registerIntegrations } from '../../modules/integrations/types'\nimport { applyComponentOverridesToEntries } from '../../modules/overrides'\n\nconst _bootstrappedKeys = new Set<string>()\n\n// Store the async registration promise so callers can await it if needed\nlet _asyncRegistrationPromise: Promise<void> | null = null\n\n/**\n * Creates a bootstrap function that registers all application dependencies.\n *\n * The returned function should be called once at application startup.\n * In development mode, it can be called multiple times (for HMR).\n *\n * @param data - All generated registry data from .mercato/generated/\n * @param options - Optional configuration\n * @returns A bootstrap function to call at app startup\n */\nexport function createBootstrap(data: BootstrapData, options: BootstrapOptions = {}) {\n return function bootstrap(): void {\n const registrationKey = options.registrationKey ?? 'default'\n if (options.appDiRegistrar) {\n registerAppDiRegistrar(options.appDiRegistrar)\n }\n // In development, always re-run registrations to handle HMR\n // (Module state may be reset when Turbopack reloads packages)\n if (_bootstrappedKeys.has(registrationKey) && process.env.NODE_ENV !== 'development') return\n _bootstrappedKeys.add(registrationKey)\n\n // === 1. Foundation: ORM entities and DI registrars ===\n registerOrmEntities(data.entities)\n registerDiRegistrars(data.diRegistrars.filter((r): r is NonNullable<typeof r> => r != null))\n\n // === 2. Modules registry (required by i18n, query engine, dashboards, CLI) ===\n registerModules(data.modules)\n clearRegisteredIntegrations()\n for (const module of data.modules) {\n if (module.integrations?.length) {\n registerIntegrations(module.integrations)\n }\n if (module.bundles?.length) {\n registerBundles(module.bundles)\n }\n }\n\n // === 3. Entity IDs (required by encryption, indexing, entity links) ===\n registerEntityIds(data.entityIds)\n\n // === 4. Entity fields registry (for encryption manager, Turbopack compatibility) ===\n if (data.entityFieldsRegistry) {\n registerEntityFields(data.entityFieldsRegistry)\n }\n\n // === 5. Search module configs (for search service registration in DI) ===\n if (data.searchModuleConfigs) {\n registerSearchModuleConfigs(data.searchModuleConfigs)\n }\n\n // === 6. Analytics module configs (for dashboard widgets and analytics API) ===\n if (data.analyticsModuleConfigs) {\n registerAnalyticsModuleConfigs(data.analyticsModuleConfigs)\n }\n\n // === 6a. Code workflow definitions (so CLI/worker processes resolve them like the app runtime) ===\n if (data.codeWorkflows?.length) {\n registerCodeWorkflowEntries(data.codeWorkflows)\n }\n\n // === 6b. Response enrichers (for CRUD response enrichment) ===\n if (data.enricherEntries) {\n registerResponseEnrichers(data.enricherEntries)\n }\n\n // === 6c. API interceptors (for CRUD route interception) ===\n if (data.interceptorEntries) {\n registerApiInterceptors(data.interceptorEntries)\n }\n\n // === 6d. Component overrides (for page/component replacement) ===\n if (data.componentOverrideEntries) {\n const finalEntries = applyComponentOverridesToEntries(data.componentOverrideEntries)\n const allOverrides = finalEntries.flatMap((entry) => entry.componentOverrides ?? [])\n registerComponentOverrides(allOverrides)\n }\n\n // === 6e. Mutation guards (for CRUD mutation lifecycle) ===\n if (data.guardEntries) {\n registerMutationGuards(data.guardEntries)\n }\n\n // === 6f. Command interceptors (for command bus lifecycle) ===\n if (data.commandInterceptorEntries) {\n registerCommandInterceptors(data.commandInterceptorEntries)\n }\n\n // === 6f.1. Command loaders (for lazy command handler registration) ===\n if (data.commandLoaderEntries) {\n registerCommandLoaders(data.commandLoaderEntries)\n }\n\n // === 6g. Notification handlers (reactive notification side-effects) ===\n if (data.notificationHandlerEntries) {\n registerNotificationHandlers(data.notificationHandlerEntries)\n }\n\n // === 7-8. UI Widgets and Optional packages (async to avoid circular deps) ===\n // Store the promise so CLI context can await it\n _asyncRegistrationPromise = registerWidgetsAndOptionalPackages(data, options)\n void _asyncRegistrationPromise\n\n options.onRegistrationComplete?.()\n }\n}\n\n/**\n * Wait for async registrations (CLI modules, widgets, etc.) to complete.\n * Call this after bootstrap() in CLI context where you need modules immediately.\n */\nexport async function waitForAsyncRegistration(): Promise<void> {\n if (_asyncRegistrationPromise) {\n await _asyncRegistrationPromise\n }\n}\n\nasync function registerWidgetsAndOptionalPackages(data: BootstrapData, options: BootstrapOptions): Promise<void> {\n // Register widget data required by server-side injection independently from\n // browser-facing UI registries. API-only bootstraps avoid loading @open-mercato/ui.\n try {\n const coreInjection = await import('@open-mercato/core/modules/widgets/lib/injection')\n if (!options.skipCoreInjectionWidgets) {\n coreInjection.registerCoreInjectionWidgets(data.injectionWidgetEntries)\n }\n coreInjection.registerCoreInjectionTables(data.injectionTables, data.injectionWidgetEntries)\n coreInjection.registerEnabledModuleIds(\n data.modules.map((module) => module.id).filter((id): id is string => typeof id === 'string' && id.length > 0),\n )\n\n if (!options.skipUiRegistries) {\n const [dashboardRegistry, injectionRegistry] = await Promise.all([\n import('@open-mercato/ui/backend/dashboard/widgetRegistry'),\n import('@open-mercato/ui/backend/injection/widgetRegistry'),\n ])\n dashboardRegistry.registerDashboardWidgets(data.dashboardWidgetEntries)\n injectionRegistry.registerInjectionWidgets(data.injectionWidgetEntries)\n }\n } catch {\n // UI packages may not be available in all contexts\n }\n\n // Note: Search module configs are registered synchronously in the main bootstrap.\n // The actual registerSearchModule() call happens in core/bootstrap.ts when the\n // DI container is created, using getSearchModuleConfigs() from the global registry.\n\n // Note: CLI module registration is handled separately in CLI context\n // via bootstrapFromAppRoot in dynamicLoader. We don't import CLI here\n // to avoid Turbopack tracing through the CLI package in Next.js context.\n}\n\n/**\n * Check if bootstrap has been called.\n */\nexport function isBootstrapped(): boolean {\n return _bootstrappedKeys.size > 0\n}\n\n/**\n * Reset bootstrap state. Useful for testing.\n */\nexport function resetBootstrapState(): void {\n _bootstrappedKeys.clear()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,45 @@
1
+ const QUERY_INDEX_REINDEX_EXPORT = "queryIndexReindexEntityTypes";
2
+ const ENTITY_TYPE_PATTERN = /^[a-z0-9_]+:[a-z0-9_]+$/;
3
+ function isQueryIndexEntityType(value) {
4
+ return typeof value === "string" && ENTITY_TYPE_PATTERN.test(value);
5
+ }
6
+ function declareQueryIndexReindex(entityTypes) {
7
+ if (!Array.isArray(entityTypes) || entityTypes.length === 0) {
8
+ throw new Error("[internal] declareQueryIndexReindex requires at least one entity type");
9
+ }
10
+ const normalized = [];
11
+ for (const entityType of entityTypes) {
12
+ if (!isQueryIndexEntityType(entityType)) {
13
+ throw new Error(
14
+ `[internal] declareQueryIndexReindex expects "module:entity" identifiers, received: ${String(entityType)}`
15
+ );
16
+ }
17
+ if (!normalized.includes(entityType)) normalized.push(entityType);
18
+ }
19
+ return Object.freeze(normalized);
20
+ }
21
+ function readQueryIndexReindexDeclaration(moduleExports, onReject) {
22
+ if (!moduleExports || typeof moduleExports !== "object") return [];
23
+ const declared = moduleExports[QUERY_INDEX_REINDEX_EXPORT];
24
+ if (!Array.isArray(declared)) return [];
25
+ const collected = [];
26
+ for (const entityType of declared) {
27
+ if (!isQueryIndexEntityType(entityType)) {
28
+ onReject?.(entityType);
29
+ continue;
30
+ }
31
+ if (!collected.includes(entityType)) collected.push(entityType);
32
+ }
33
+ return collected;
34
+ }
35
+ function formatQueryIndexRebuildCommands(entityTypes) {
36
+ return entityTypes.map((entityType) => `mercato query_index rebuild --entity ${entityType} --global`);
37
+ }
38
+ export {
39
+ QUERY_INDEX_REINDEX_EXPORT,
40
+ declareQueryIndexReindex,
41
+ formatQueryIndexRebuildCommands,
42
+ isQueryIndexEntityType,
43
+ readQueryIndexReindexDeclaration
44
+ };
45
+ //# sourceMappingURL=migration-reindex.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/query/migration-reindex.ts"],
4
+ "sourcesContent": ["/**\n * Data migrations rewrite columns in raw SQL, so they bypass every CRUD/indexer helper that\n * would normally emit `query_index.upsert_one`. A migration also cannot emit from where it\n * stands: it runs inside its own transaction with no DI container, and the projection must\n * only be refreshed once the rewrite has committed.\n *\n * A migration therefore *declares* the entity types whose projections it invalidated, and\n * `mercato db migrate` discharges the obligation after the whole run commits.\n */\n\nexport const QUERY_INDEX_REINDEX_EXPORT = 'queryIndexReindexEntityTypes'\n\nconst ENTITY_TYPE_PATTERN = /^[a-z0-9_]+:[a-z0-9_]+$/\n\nexport function isQueryIndexEntityType(value: unknown): value is string {\n return typeof value === 'string' && ENTITY_TYPE_PATTERN.test(value)\n}\n\nexport function declareQueryIndexReindex(entityTypes: readonly string[]): readonly string[] {\n if (!Array.isArray(entityTypes) || entityTypes.length === 0) {\n throw new Error('[internal] declareQueryIndexReindex requires at least one entity type')\n }\n const normalized: string[] = []\n for (const entityType of entityTypes) {\n if (!isQueryIndexEntityType(entityType)) {\n throw new Error(\n `[internal] declareQueryIndexReindex expects \"module:entity\" identifiers, received: ${String(entityType)}`,\n )\n }\n if (!normalized.includes(entityType)) normalized.push(entityType)\n }\n return Object.freeze(normalized)\n}\n\n/**\n * The reader \u2014 not `declareQueryIndexReindex` \u2014 is the contract's real boundary: it accepts any\n * `queryIndexReindexEntityTypes` array, including one written as a plain literal. A rejected entry\n * is therefore reported through `onReject` rather than dropped silently, so a typo such as\n * `customers:customerDictionaryEntry` cannot leave a projection stale behind a green migrate run.\n */\nexport function readQueryIndexReindexDeclaration(\n moduleExports: unknown,\n onReject?: (value: unknown) => void,\n): string[] {\n if (!moduleExports || typeof moduleExports !== 'object') return []\n const declared = (moduleExports as Record<string, unknown>)[QUERY_INDEX_REINDEX_EXPORT]\n if (!Array.isArray(declared)) return []\n const collected: string[] = []\n for (const entityType of declared) {\n if (!isQueryIndexEntityType(entityType)) {\n onReject?.(entityType)\n continue\n }\n if (!collected.includes(entityType)) collected.push(entityType)\n }\n return collected\n}\n\nexport function formatQueryIndexRebuildCommands(entityTypes: readonly string[]): string[] {\n return entityTypes.map((entityType) => `mercato query_index rebuild --entity ${entityType} --global`)\n}\n"],
5
+ "mappings": "AAUO,MAAM,6BAA6B;AAE1C,MAAM,sBAAsB;AAErB,SAAS,uBAAuB,OAAiC;AACtE,SAAO,OAAO,UAAU,YAAY,oBAAoB,KAAK,KAAK;AACpE;AAEO,SAAS,yBAAyB,aAAmD;AAC1F,MAAI,CAAC,MAAM,QAAQ,WAAW,KAAK,YAAY,WAAW,GAAG;AAC3D,UAAM,IAAI,MAAM,uEAAuE;AAAA,EACzF;AACA,QAAM,aAAuB,CAAC;AAC9B,aAAW,cAAc,aAAa;AACpC,QAAI,CAAC,uBAAuB,UAAU,GAAG;AACvC,YAAM,IAAI;AAAA,QACR,sFAAsF,OAAO,UAAU,CAAC;AAAA,MAC1G;AAAA,IACF;AACA,QAAI,CAAC,WAAW,SAAS,UAAU,EAAG,YAAW,KAAK,UAAU;AAAA,EAClE;AACA,SAAO,OAAO,OAAO,UAAU;AACjC;AAQO,SAAS,iCACd,eACA,UACU;AACV,MAAI,CAAC,iBAAiB,OAAO,kBAAkB,SAAU,QAAO,CAAC;AACjE,QAAM,WAAY,cAA0C,0BAA0B;AACtF,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AACtC,QAAM,YAAsB,CAAC;AAC7B,aAAW,cAAc,UAAU;AACjC,QAAI,CAAC,uBAAuB,UAAU,GAAG;AACvC,iBAAW,UAAU;AACrB;AAAA,IACF;AACA,QAAI,CAAC,UAAU,SAAS,UAAU,EAAG,WAAU,KAAK,UAAU;AAAA,EAChE;AACA,SAAO;AACT;AAEO,SAAS,gCAAgC,aAA0C;AACxF,SAAO,YAAY,IAAI,CAAC,eAAe,wCAAwC,UAAU,WAAW;AACtG;",
6
+ "names": []
7
+ }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.7.1-develop.7136.1.0f76137a1d";
1
+ const APP_VERSION = "0.7.1-develop.7148.1.3076e5ccf7";
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.7.1-develop.7136.1.0f76137a1d';\nexport const appVersion = APP_VERSION;\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.7.1-develop.7148.1.3076e5ccf7';\nexport const appVersion = APP_VERSION;\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
@@ -27,14 +27,15 @@ const DOMAIN_KEYS = [
27
27
  "nav"
28
28
  ];
29
29
  const TRACKING_ISSUE_HINT = "See `.ai/specs/implemented/2026-05-04-modules-ts-unified-overrides.md` and tracking issue https://github.com/open-mercato/open-mercato/issues/1787.";
30
- function applyModuleOverridesFromEnabledModules(modules) {
30
+ function applyModuleOverridesFromEnabledModules(modules, options) {
31
31
  if (!Array.isArray(modules) || modules.length === 0) return;
32
+ const selectedDomains = options?.domains ? DOMAIN_KEYS.filter((domain) => options.domains.includes(domain)) : DOMAIN_KEYS;
32
33
  const buckets = /* @__PURE__ */ new Map();
33
34
  for (const entry of modules) {
34
35
  if (!entry || typeof entry.id !== "string" || !entry.id) continue;
35
36
  const overrides = entry.overrides;
36
37
  if (!overrides || typeof overrides !== "object") continue;
37
- for (const domain of DOMAIN_KEYS) {
38
+ for (const domain of selectedDomains) {
38
39
  const value = overrides[domain];
39
40
  if (value === void 0 || value === null) continue;
40
41
  if (typeof value !== "object") continue;
@@ -132,6 +133,15 @@ function composeStore(store) {
132
133
  }
133
134
  return { ...store.modules, ...store.programmatic };
134
135
  }
136
+ function resolveOverrideIds(value, options) {
137
+ const ids = [];
138
+ const primary = options.getId(value);
139
+ if (primary) ids.push(primary);
140
+ for (const alias of options.getAliasIds?.(value) ?? []) {
141
+ if (alias && !ids.includes(alias)) ids.push(alias);
142
+ }
143
+ return ids;
144
+ }
135
145
  function applyArrayOverrides(items, overrides, options) {
136
146
  if (!items || Object.keys(overrides).length === 0) {
137
147
  return { items: items ? Array.from(items) : items, consumed: /* @__PURE__ */ new Set(), changed: false };
@@ -140,12 +150,20 @@ function applyArrayOverrides(items, overrides, options) {
140
150
  const result = [];
141
151
  let changed = false;
142
152
  for (const item of items) {
143
- const id = options.getId(item);
144
- if (!id || !Object.prototype.hasOwnProperty.call(overrides, id)) {
153
+ const matched = resolveOverrideIds(item, options).filter((candidate) => Object.prototype.hasOwnProperty.call(overrides, candidate));
154
+ if (matched.length === 0) {
145
155
  result.push(item);
146
156
  continue;
147
157
  }
148
- consumed.add(id);
158
+ const id = matched[0];
159
+ for (const candidate of matched) consumed.add(candidate);
160
+ if (matched.some((candidate) => overrides[candidate] !== overrides[id])) {
161
+ const disablesAndReplaces = matched.some((candidate) => overrides[candidate] === null !== (overrides[id] === null));
162
+ logger.warn(
163
+ disablesAndReplaces ? "Conflicting overrides for the same entry \u2014 one key disables it while another replaces it; the first matching key wins" : "Duplicate replacement overrides for the same entry \u2014 only the first matching key takes effect",
164
+ { label: options.label, id, keys: matched }
165
+ );
166
+ }
149
167
  changed = true;
150
168
  const replacement = overrides[id];
151
169
  if (replacement === null) continue;
@@ -154,12 +172,22 @@ function applyArrayOverrides(items, overrides, options) {
154
172
  result.push(item);
155
173
  continue;
156
174
  }
157
- const replacementId = options.getId(replacement);
158
- if (replacementId !== id) {
175
+ const replacementIds = resolveOverrideIds(replacement, options);
176
+ if (!replacementIds.includes(id)) {
159
177
  logger.warn("Skipping malformed override \u2014 replacement id must match the override key", { label: options.label, id });
160
178
  result.push(item);
161
179
  continue;
162
180
  }
181
+ const itemIds = resolveOverrideIds(item, options);
182
+ const divergent = itemIds.filter((candidate) => !replacementIds.includes(candidate));
183
+ if (divergent.length > 0) {
184
+ logger.warn("Replacement override changes an identifier it was not matched on \u2014 references to the old value are not rewritten", {
185
+ label: options.label,
186
+ id,
187
+ replaced: divergent,
188
+ replacementIds
189
+ });
190
+ }
163
191
  result.push(replacement);
164
192
  }
165
193
  return { items: result, consumed, changed };
@@ -289,6 +317,7 @@ function resetModuleContractOverridesForTests() {
289
317
  clearStore(aclFeatureOverrideStore);
290
318
  clearStore(encryptionMapOverrideStore);
291
319
  clearStore(diOverrideStore);
320
+ getInjectionWidgetIdAliases().clear();
292
321
  for (const key of Object.keys(setupOverridesByModule)) delete setupOverridesByModule[key];
293
322
  const navState = getNavOverrideState();
294
323
  navState.modules = null;
@@ -528,10 +557,35 @@ function applyEntryListOverrides(entries, overrides, options) {
528
557
  warnStaleOverrides(options.label, overrides, consumed);
529
558
  return changed ? result : Array.from(entries);
530
559
  }
560
+ const GLOBAL_INJECTION_WIDGET_ID_ALIASES_KEY = "__openMercatoInjectionWidgetIdAliases__";
561
+ function getInjectionWidgetIdAliases() {
562
+ const existing = globalThis[GLOBAL_INJECTION_WIDGET_ID_ALIASES_KEY];
563
+ if (existing instanceof Map) return existing;
564
+ const initial = /* @__PURE__ */ new Map();
565
+ globalThis[GLOBAL_INJECTION_WIDGET_ID_ALIASES_KEY] = initial;
566
+ return initial;
567
+ }
568
+ function rememberInjectionWidgetIdAliases(entries) {
569
+ if (!entries) return;
570
+ const aliases = getInjectionWidgetIdAliases();
571
+ for (const entry of entries) {
572
+ const ids = [entry?.key, entry?.widgetId].filter((id) => typeof id === "string" && id.length > 0);
573
+ if (ids.length < 2) continue;
574
+ for (const id of ids) {
575
+ const siblings = aliases.get(id) ?? /* @__PURE__ */ new Set();
576
+ for (const sibling of ids) {
577
+ if (sibling !== id) siblings.add(sibling);
578
+ }
579
+ aliases.set(id, siblings);
580
+ }
581
+ }
582
+ }
531
583
  function applyInjectionWidgetOverridesToEntries(entries, overrides = composeInjectionWidgetOverrides()) {
584
+ rememberInjectionWidgetIdAliases(entries);
532
585
  const applied = applyArrayOverrides(entries, overrides, {
533
586
  label: "widgets.injection",
534
587
  getId: (entry) => entry.key,
588
+ getAliasIds: (entry) => [entry.widgetId],
535
589
  isReplacement: isInjectionWidgetEntry
536
590
  });
537
591
  warnStaleOverrides("widgets.injection", overrides, applied.consumed);
@@ -555,8 +609,14 @@ function applyWorkerOverridesToDescriptors(entries, overrides = composeWorkerOve
555
609
  warnStaleOverrides("workers", overrides, applied.consumed);
556
610
  return applied.items ?? [];
557
611
  }
558
- function applyInjectionWidgetOverridesToTables(tables, overrides = composeInjectionWidgetOverrides()) {
559
- const disabled = new Set(Object.entries(overrides).filter(([, value]) => value === null).map(([key]) => key));
612
+ function applyInjectionWidgetOverridesToTables(tables, overrides = composeInjectionWidgetOverrides(), entries = []) {
613
+ rememberInjectionWidgetIdAliases(entries);
614
+ const disabled = /* @__PURE__ */ new Set();
615
+ for (const [key, value] of Object.entries(overrides)) {
616
+ if (value !== null) continue;
617
+ disabled.add(key);
618
+ for (const alias of getInjectionWidgetIdAliases().get(key) ?? []) disabled.add(alias);
619
+ }
560
620
  if (disabled.size === 0) return Array.from(tables);
561
621
  const filterSlot = (slot) => {
562
622
  if (typeof slot === "string") return disabled.has(slot) ? null : slot;