@open-mercato/shared 0.6.2-develop.3467.1.2a1818709d → 0.6.3-canary.3483.1.98d4b4ecc7

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.
Files changed (43) hide show
  1. package/AGENTS.md +24 -0
  2. package/dist/lib/bootstrap/factory.js +3 -1
  3. package/dist/lib/bootstrap/factory.js.map +2 -2
  4. package/dist/lib/commands/command-interceptor-store.js +3 -1
  5. package/dist/lib/commands/command-interceptor-store.js.map +2 -2
  6. package/dist/lib/crud/enricher-registry.js +3 -1
  7. package/dist/lib/crud/enricher-registry.js.map +2 -2
  8. package/dist/lib/crud/factory.js.map +1 -1
  9. package/dist/lib/crud/interceptor-registry.js +3 -1
  10. package/dist/lib/crud/interceptor-registry.js.map +2 -2
  11. package/dist/lib/di/container.js +16 -0
  12. package/dist/lib/di/container.js.map +2 -2
  13. package/dist/lib/middleware/page-executor.js +2 -1
  14. package/dist/lib/middleware/page-executor.js.map +2 -2
  15. package/dist/lib/modules/registry.js +2 -1
  16. package/dist/lib/modules/registry.js.map +2 -2
  17. package/dist/lib/notifications/handler-registry.js +3 -1
  18. package/dist/lib/notifications/handler-registry.js.map +2 -2
  19. package/dist/lib/version.js +1 -1
  20. package/dist/lib/version.js.map +1 -1
  21. package/dist/modules/overrides.js +857 -0
  22. package/dist/modules/overrides.js.map +2 -2
  23. package/dist/modules/registry.js +17 -4
  24. package/dist/modules/registry.js.map +2 -2
  25. package/dist/modules/widgets/injection-loader.js +10 -4
  26. package/dist/modules/widgets/injection-loader.js.map +2 -2
  27. package/jest.config.cjs +3 -1
  28. package/package.json +7 -7
  29. package/src/lib/bootstrap/factory.ts +3 -1
  30. package/src/lib/commands/command-interceptor-store.ts +3 -1
  31. package/src/lib/crud/enricher-registry.ts +3 -1
  32. package/src/lib/crud/factory.ts +2 -2
  33. package/src/lib/crud/interceptor-registry.ts +3 -1
  34. package/src/lib/di/container.ts +24 -4
  35. package/src/lib/middleware/page-executor.ts +2 -1
  36. package/src/lib/modules/registry.ts +2 -1
  37. package/src/lib/notifications/handler-registry.ts +3 -1
  38. package/src/modules/__tests__/contract-overrides.test.ts +332 -0
  39. package/src/modules/__tests__/overrides.test.ts +23 -22
  40. package/src/modules/__tests__/route-overrides.test.ts +441 -0
  41. package/src/modules/overrides.ts +1297 -27
  42. package/src/modules/registry.ts +27 -4
  43. package/src/modules/widgets/injection-loader.ts +10 -4
@@ -1,3 +1,4 @@
1
+ import { applyApiInterceptorOverridesToEntries } from "../../modules/overrides.js";
1
2
  let _interceptorEntries = null;
2
3
  const GLOBAL_INTERCEPTOR_KEY = "__openMercatoApiInterceptors__";
3
4
  function readGlobalInterceptors() {
@@ -16,8 +17,9 @@ function writeGlobalInterceptors(entries) {
16
17
  }
17
18
  }
18
19
  function registerApiInterceptors(entries) {
20
+ const finalEntries = applyApiInterceptorOverridesToEntries(entries);
19
21
  const flat = [];
20
- entries.forEach((entry, moduleOrder) => {
22
+ finalEntries.forEach((entry, moduleOrder) => {
21
23
  entry.interceptors.forEach((interceptor, interceptorOrder) => {
22
24
  flat.push({
23
25
  moduleId: entry.moduleId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/crud/interceptor-registry.ts"],
4
- "sourcesContent": ["import type { ApiInterceptor, ApiInterceptorMethod, ApiInterceptorRegistryEntry } from './api-interceptor'\n\nlet _interceptorEntries: ApiInterceptorRegistryEntry[] | null = null\nconst GLOBAL_INTERCEPTOR_KEY = '__openMercatoApiInterceptors__'\n\nfunction readGlobalInterceptors(): ApiInterceptorRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY]\n return Array.isArray(value) ? (value as ApiInterceptorRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInterceptors(entries: ApiInterceptorRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerApiInterceptors(entries: Array<{ moduleId: string; interceptors: ApiInterceptor[] }>) {\n const flat: ApiInterceptorRegistryEntry[] = []\n entries.forEach((entry, moduleOrder) => {\n entry.interceptors.forEach((interceptor, interceptorOrder) => {\n flat.push({\n moduleId: entry.moduleId,\n interceptor,\n moduleOrder,\n interceptorOrder,\n })\n })\n })\n _interceptorEntries = flat\n writeGlobalInterceptors(flat)\n}\n\nexport function getAllApiInterceptors(): ApiInterceptorRegistryEntry[] {\n const globalEntries = readGlobalInterceptors()\n if (globalEntries) return globalEntries\n if (!_interceptorEntries) return []\n return _interceptorEntries\n}\n\nfunction routeMatches(targetRoute: string, routePath: string): boolean {\n if (targetRoute === '*') return true\n if (targetRoute.endsWith('/*')) {\n const prefix = targetRoute.slice(0, -2)\n return routePath === prefix || routePath.startsWith(`${prefix}/`)\n }\n return targetRoute === routePath\n}\n\nconst collisionWarnings = new Set<string>()\n\nexport function getApiInterceptorsForRoute(routePath: string, method: ApiInterceptorMethod): ApiInterceptorRegistryEntry[] {\n const matching = getAllApiInterceptors().filter((entry) => {\n const methods = entry.interceptor.methods ?? []\n return methods.includes(method) && routeMatches(entry.interceptor.targetRoute, routePath)\n })\n\n const sorted = matching.sort((a, b) => {\n const byPriority = (b.interceptor.priority ?? 0) - (a.interceptor.priority ?? 0)\n if (byPriority !== 0) return byPriority\n const byModule = a.moduleOrder - b.moduleOrder\n if (byModule !== 0) return byModule\n return a.interceptorOrder - b.interceptorOrder\n })\n\n if (process.env.NODE_ENV !== 'production') {\n for (let i = 1; i < sorted.length; i++) {\n const prev = sorted[i - 1]\n const current = sorted[i]\n const prevPriority = prev.interceptor.priority ?? 0\n const currentPriority = current.interceptor.priority ?? 0\n if (prevPriority !== currentPriority) continue\n const warningKey = `${routePath}:${method}:${prev.interceptor.id}:${current.interceptor.id}:${currentPriority}`\n if (collisionWarnings.has(warningKey)) continue\n collisionWarnings.add(warningKey)\n console.warn(\n `[UMES] Interceptors \"${prev.interceptor.id}\" and \"${current.interceptor.id}\" have the same priority (${currentPriority}) for route \"${routePath}\". Execution order is based on module registration order.`\n )\n }\n }\n\n return sorted\n}\n"],
5
- "mappings": "AAEA,IAAI,sBAA4D;AAChE,MAAM,yBAAyB;AAE/B,SAAS,yBAA+D;AACtE,MAAI;AACF,UAAM,QAAS,WAAuC,sBAAsB;AAC5E,WAAO,MAAM,QAAQ,KAAK,IAAK,QAA0C;AAAA,EAC3E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,wBAAwB,SAAwC;AACvE,MAAI;AACF;AAAC,IAAC,WAAuC,sBAAsB,IAAI;AAAA,EACrE,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,wBAAwB,SAAsE;AAC5G,QAAM,OAAsC,CAAC;AAC7C,UAAQ,QAAQ,CAAC,OAAO,gBAAgB;AACtC,UAAM,aAAa,QAAQ,CAAC,aAAa,qBAAqB;AAC5D,WAAK,KAAK;AAAA,QACR,UAAU,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACD,wBAAsB;AACtB,0BAAwB,IAAI;AAC9B;AAEO,SAAS,wBAAuD;AACrE,QAAM,gBAAgB,uBAAuB;AAC7C,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,oBAAqB,QAAO,CAAC;AAClC,SAAO;AACT;AAEA,SAAS,aAAa,aAAqB,WAA4B;AACrE,MAAI,gBAAgB,IAAK,QAAO;AAChC,MAAI,YAAY,SAAS,IAAI,GAAG;AAC9B,UAAM,SAAS,YAAY,MAAM,GAAG,EAAE;AACtC,WAAO,cAAc,UAAU,UAAU,WAAW,GAAG,MAAM,GAAG;AAAA,EAClE;AACA,SAAO,gBAAgB;AACzB;AAEA,MAAM,oBAAoB,oBAAI,IAAY;AAEnC,SAAS,2BAA2B,WAAmB,QAA6D;AACzH,QAAM,WAAW,sBAAsB,EAAE,OAAO,CAAC,UAAU;AACzD,UAAM,UAAU,MAAM,YAAY,WAAW,CAAC;AAC9C,WAAO,QAAQ,SAAS,MAAM,KAAK,aAAa,MAAM,YAAY,aAAa,SAAS;AAAA,EAC1F,CAAC;AAED,QAAM,SAAS,SAAS,KAAK,CAAC,GAAG,MAAM;AACrC,UAAM,cAAc,EAAE,YAAY,YAAY,MAAM,EAAE,YAAY,YAAY;AAC9E,QAAI,eAAe,EAAG,QAAO;AAC7B,UAAM,WAAW,EAAE,cAAc,EAAE;AACnC,QAAI,aAAa,EAAG,QAAO;AAC3B,WAAO,EAAE,mBAAmB,EAAE;AAAA,EAChC,CAAC;AAED,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,OAAO,OAAO,IAAI,CAAC;AACzB,YAAM,UAAU,OAAO,CAAC;AACxB,YAAM,eAAe,KAAK,YAAY,YAAY;AAClD,YAAM,kBAAkB,QAAQ,YAAY,YAAY;AACxD,UAAI,iBAAiB,gBAAiB;AACtC,YAAM,aAAa,GAAG,SAAS,IAAI,MAAM,IAAI,KAAK,YAAY,EAAE,IAAI,QAAQ,YAAY,EAAE,IAAI,eAAe;AAC7G,UAAI,kBAAkB,IAAI,UAAU,EAAG;AACvC,wBAAkB,IAAI,UAAU;AAChC,cAAQ;AAAA,QACN,wBAAwB,KAAK,YAAY,EAAE,UAAU,QAAQ,YAAY,EAAE,6BAA6B,eAAe,gBAAgB,SAAS;AAAA,MAClJ;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { ApiInterceptor, ApiInterceptorMethod, ApiInterceptorRegistryEntry } from './api-interceptor'\nimport { applyApiInterceptorOverridesToEntries } from '../../modules/overrides'\n\nlet _interceptorEntries: ApiInterceptorRegistryEntry[] | null = null\nconst GLOBAL_INTERCEPTOR_KEY = '__openMercatoApiInterceptors__'\n\nfunction readGlobalInterceptors(): ApiInterceptorRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY]\n return Array.isArray(value) ? (value as ApiInterceptorRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInterceptors(entries: ApiInterceptorRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerApiInterceptors(entries: Array<{ moduleId: string; interceptors: ApiInterceptor[] }>) {\n const finalEntries = applyApiInterceptorOverridesToEntries(entries)\n const flat: ApiInterceptorRegistryEntry[] = []\n finalEntries.forEach((entry, moduleOrder) => {\n entry.interceptors.forEach((interceptor, interceptorOrder) => {\n flat.push({\n moduleId: entry.moduleId,\n interceptor,\n moduleOrder,\n interceptorOrder,\n })\n })\n })\n _interceptorEntries = flat\n writeGlobalInterceptors(flat)\n}\n\nexport function getAllApiInterceptors(): ApiInterceptorRegistryEntry[] {\n const globalEntries = readGlobalInterceptors()\n if (globalEntries) return globalEntries\n if (!_interceptorEntries) return []\n return _interceptorEntries\n}\n\nfunction routeMatches(targetRoute: string, routePath: string): boolean {\n if (targetRoute === '*') return true\n if (targetRoute.endsWith('/*')) {\n const prefix = targetRoute.slice(0, -2)\n return routePath === prefix || routePath.startsWith(`${prefix}/`)\n }\n return targetRoute === routePath\n}\n\nconst collisionWarnings = new Set<string>()\n\nexport function getApiInterceptorsForRoute(routePath: string, method: ApiInterceptorMethod): ApiInterceptorRegistryEntry[] {\n const matching = getAllApiInterceptors().filter((entry) => {\n const methods = entry.interceptor.methods ?? []\n return methods.includes(method) && routeMatches(entry.interceptor.targetRoute, routePath)\n })\n\n const sorted = matching.sort((a, b) => {\n const byPriority = (b.interceptor.priority ?? 0) - (a.interceptor.priority ?? 0)\n if (byPriority !== 0) return byPriority\n const byModule = a.moduleOrder - b.moduleOrder\n if (byModule !== 0) return byModule\n return a.interceptorOrder - b.interceptorOrder\n })\n\n if (process.env.NODE_ENV !== 'production') {\n for (let i = 1; i < sorted.length; i++) {\n const prev = sorted[i - 1]\n const current = sorted[i]\n const prevPriority = prev.interceptor.priority ?? 0\n const currentPriority = current.interceptor.priority ?? 0\n if (prevPriority !== currentPriority) continue\n const warningKey = `${routePath}:${method}:${prev.interceptor.id}:${current.interceptor.id}:${currentPriority}`\n if (collisionWarnings.has(warningKey)) continue\n collisionWarnings.add(warningKey)\n console.warn(\n `[UMES] Interceptors \"${prev.interceptor.id}\" and \"${current.interceptor.id}\" have the same priority (${currentPriority}) for route \"${routePath}\". Execution order is based on module registration order.`\n )\n }\n }\n\n return sorted\n}\n"],
5
+ "mappings": "AACA,SAAS,6CAA6C;AAEtD,IAAI,sBAA4D;AAChE,MAAM,yBAAyB;AAE/B,SAAS,yBAA+D;AACtE,MAAI;AACF,UAAM,QAAS,WAAuC,sBAAsB;AAC5E,WAAO,MAAM,QAAQ,KAAK,IAAK,QAA0C;AAAA,EAC3E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,wBAAwB,SAAwC;AACvE,MAAI;AACF;AAAC,IAAC,WAAuC,sBAAsB,IAAI;AAAA,EACrE,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,wBAAwB,SAAsE;AAC5G,QAAM,eAAe,sCAAsC,OAAO;AAClE,QAAM,OAAsC,CAAC;AAC7C,eAAa,QAAQ,CAAC,OAAO,gBAAgB;AAC3C,UAAM,aAAa,QAAQ,CAAC,aAAa,qBAAqB;AAC5D,WAAK,KAAK;AAAA,QACR,UAAU,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACD,wBAAsB;AACtB,0BAAwB,IAAI;AAC9B;AAEO,SAAS,wBAAuD;AACrE,QAAM,gBAAgB,uBAAuB;AAC7C,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,oBAAqB,QAAO,CAAC;AAClC,SAAO;AACT;AAEA,SAAS,aAAa,aAAqB,WAA4B;AACrE,MAAI,gBAAgB,IAAK,QAAO;AAChC,MAAI,YAAY,SAAS,IAAI,GAAG;AAC9B,UAAM,SAAS,YAAY,MAAM,GAAG,EAAE;AACtC,WAAO,cAAc,UAAU,UAAU,WAAW,GAAG,MAAM,GAAG;AAAA,EAClE;AACA,SAAO,gBAAgB;AACzB;AAEA,MAAM,oBAAoB,oBAAI,IAAY;AAEnC,SAAS,2BAA2B,WAAmB,QAA6D;AACzH,QAAM,WAAW,sBAAsB,EAAE,OAAO,CAAC,UAAU;AACzD,UAAM,UAAU,MAAM,YAAY,WAAW,CAAC;AAC9C,WAAO,QAAQ,SAAS,MAAM,KAAK,aAAa,MAAM,YAAY,aAAa,SAAS;AAAA,EAC1F,CAAC;AAED,QAAM,SAAS,SAAS,KAAK,CAAC,GAAG,MAAM;AACrC,UAAM,cAAc,EAAE,YAAY,YAAY,MAAM,EAAE,YAAY,YAAY;AAC9E,QAAI,eAAe,EAAG,QAAO;AAC7B,UAAM,WAAW,EAAE,cAAc,EAAE;AACnC,QAAI,aAAa,EAAG,QAAO;AAC3B,WAAO,EAAE,mBAAmB,EAAE;AAAA,EAChC,CAAC;AAED,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,OAAO,OAAO,IAAI,CAAC;AACzB,YAAM,UAAU,OAAO,CAAC;AACxB,YAAM,eAAe,KAAK,YAAY,YAAY;AAClD,YAAM,kBAAkB,QAAQ,YAAY,YAAY;AACxD,UAAI,iBAAiB,gBAAiB;AACtC,YAAM,aAAa,GAAG,SAAS,IAAI,MAAM,IAAI,KAAK,YAAY,EAAE,IAAI,QAAQ,YAAY,EAAE,IAAI,eAAe;AAC7G,UAAI,kBAAkB,IAAI,UAAU,EAAG;AACvC,wBAAkB,IAAI,UAAU;AAChC,cAAQ;AAAA,QACN,wBAAwB,KAAK,YAAY,EAAE,UAAU,QAAQ,YAAY,EAAE,6BAA6B,eAAe,gBAAgB,SAAS;AAAA,MAClJ;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import { getOrm } from "@open-mercato/shared/lib/db/mikro";
4
4
  import { BasicQueryEngine } from "@open-mercato/shared/lib/query/engine";
5
5
  import { DefaultDataEngine } from "@open-mercato/shared/lib/data/engine";
6
6
  import { commandRegistry, CommandBus } from "@open-mercato/shared/lib/commands";
7
+ import { applyDiOverridesToContainer } from "@open-mercato/shared/modules/overrides";
7
8
  const GLOBAL_KEY = "__openMercatoDiRegistrars__";
8
9
  function getGlobalRegistrars() {
9
10
  return globalThis[GLOBAL_KEY] ?? null;
@@ -25,6 +26,17 @@ function getDiRegistrars() {
25
26
  }
26
27
  return registrars;
27
28
  }
29
+ function isAwilixResolver(value) {
30
+ return Boolean(value && typeof value === "object" && typeof value.resolve === "function");
31
+ }
32
+ function toAwilixRegistrations(registrations) {
33
+ return Object.fromEntries(
34
+ Object.entries(registrations).map(([key, value]) => [
35
+ key,
36
+ isAwilixResolver(value) ? value : asValue(value)
37
+ ])
38
+ );
39
+ }
28
40
  async function createRequestContainer() {
29
41
  const diRegistrars = getDiRegistrars();
30
42
  const orm = await getOrm();
@@ -71,6 +83,10 @@ async function createRequestContainer() {
71
83
  }
72
84
  } catch {
73
85
  }
86
+ applyDiOverridesToContainer({
87
+ register: (registrations) => container.register(toAwilixRegistrations(registrations)),
88
+ unregister: (key) => container.register({ [key]: asValue(void 0) })
89
+ });
74
90
  try {
75
91
  const emForEnc = container.resolve("em");
76
92
  const tenantEncryptionService = container.hasRegistration("tenantEncryptionService") ? container.resolve("tenantEncryptionService") : null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/di/container.ts"],
4
- "sourcesContent": ["import { createContainer, asValue, AwilixContainer, InjectionMode } from 'awilix'\nimport { RequestContext } from '@mikro-orm/core'\nimport { getOrm } from '@open-mercato/shared/lib/db/mikro'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine } from '@open-mercato/shared/lib/query/engine'\nimport { DefaultDataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { commandRegistry, CommandBus } from '@open-mercato/shared/lib/commands'\n\nexport type AppContainer = AwilixContainer\nexport type DiRegistrar = (container: AwilixContainer) => void\n\n// Registration pattern for publishable packages\n// Use globalThis to survive tsx/esbuild module duplication issue where the same\n// file can be loaded as multiple module instances when mixing dynamic and static imports\nconst GLOBAL_KEY = '__openMercatoDiRegistrars__'\n\nfunction getGlobalRegistrars(): DiRegistrar[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalRegistrars(registrars: DiRegistrar[]): void {\n (globalThis as any)[GLOBAL_KEY] = registrars\n}\n\nexport function registerDiRegistrars(registrars: DiRegistrar[]) {\n const existing = getGlobalRegistrars()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] DI registrars re-registered (this may occur during HMR)')\n }\n setGlobalRegistrars(registrars)\n}\n\nexport function getDiRegistrars(): DiRegistrar[] {\n const registrars = getGlobalRegistrars()\n if (!registrars) {\n throw new Error('[Bootstrap] DI registrars not registered. Call registerDiRegistrars() at bootstrap.')\n }\n return registrars\n}\n\nexport async function createRequestContainer(): Promise<AppContainer> {\n const diRegistrars = getDiRegistrars()\n const orm = await getOrm()\n // Use a fresh event manager so request-level subscribers (e.g., encryption) don't pile up globally\n const baseEm = (RequestContext.getEntityManager() as any) ?? orm.em\n const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true }) as unknown as EntityManager\n const container = createContainer({ injectionMode: InjectionMode.CLASSIC })\n // Core registrations\n container.register({\n em: asValue(em),\n queryEngine: asValue(new BasicQueryEngine(em, undefined, () => {\n try { return container.resolve('tenantEncryptionService') as any } catch { return null }\n })),\n dataEngine: asValue(new DefaultDataEngine(em, container as any)),\n commandRegistry: asValue(commandRegistry),\n commandBus: asValue(new CommandBus()),\n })\n // Allow modules to override/extend\n for (const reg of diRegistrars) {\n try { reg?.(container) } catch {}\n }\n // Core bootstrap (cache, event bus, encryption subscriber/KMS, module subscribers)\n try {\n const { bootstrap } = await import('@open-mercato/core/bootstrap') as any\n if (bootstrap && typeof bootstrap === 'function') {\n // Avoid double bootstrap if caller already wired it\n const alreadyBootstrapped = !!container.registrations?.eventBus\n if (!alreadyBootstrapped) {\n await bootstrap(container)\n }\n }\n } catch { /* optional */ }\n // App-level DI override (last chance)\n // This import path resolves only in the app context, not in packages\n try {\n // @ts-ignore - @/di only exists in app context, not in packages\n const appDi = await import('@/di') as any\n if (appDi?.register) {\n try {\n const maybe = appDi.register(container)\n if (maybe && typeof maybe.then === 'function') await maybe\n } catch {}\n }\n } catch {}\n // Ensure tenant encryption subscriber is always registered on the fresh request-scoped EM\n try {\n const emForEnc = container.resolve('em') as any\n const tenantEncryptionService = container.hasRegistration('tenantEncryptionService')\n ? (container.resolve('tenantEncryptionService') as any)\n : null\n if (emForEnc && tenantEncryptionService?.isEnabled?.()) {\n const { registerTenantEncryptionSubscriber } = await import('@open-mercato/shared/lib/encryption/subscriber')\n registerTenantEncryptionSubscriber(emForEnc, tenantEncryptionService)\n }\n } catch {\n // best-effort; do not block container creation\n }\n return container\n}\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // allow CLI/generator usage where Next server-only is not present\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB,SAA0B,qBAAqB;AACzE,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AAEvB,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,iBAAiB,kBAAkB;AAQ5C,MAAM,aAAa;AAEnB,SAAS,sBAA4C;AACnD,SAAQ,WAAmB,UAAU,KAAK;AAC5C;AAEA,SAAS,oBAAoB,YAAiC;AAC5D,EAAC,WAAmB,UAAU,IAAI;AACpC;AAEO,SAAS,qBAAqB,YAA2B;AAC9D,QAAM,WAAW,oBAAoB;AACrC,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,qEAAqE;AAAA,EACrF;AACA,sBAAoB,UAAU;AAChC;AAEO,SAAS,kBAAiC;AAC/C,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,qFAAqF;AAAA,EACvG;AACA,SAAO;AACT;AAEA,eAAsB,yBAAgD;AACpE,QAAM,eAAe,gBAAgB;AACrC,QAAM,MAAM,MAAM,OAAO;AAEzB,QAAM,SAAU,eAAe,iBAAiB,KAAa,IAAI;AACjE,QAAM,KAAK,OAAO,KAAK,EAAE,OAAO,MAAM,mBAAmB,MAAM,YAAY,KAAK,CAAC;AACjF,QAAM,YAAY,gBAAgB,EAAE,eAAe,cAAc,QAAQ,CAAC;AAE1E,YAAU,SAAS;AAAA,IACjB,IAAI,QAAQ,EAAE;AAAA,IACd,aAAa,QAAQ,IAAI,iBAAiB,IAAI,QAAW,MAAM;AAC7D,UAAI;AAAE,eAAO,UAAU,QAAQ,yBAAyB;AAAA,MAAS,QAAQ;AAAE,eAAO;AAAA,MAAK;AAAA,IACzF,CAAC,CAAC;AAAA,IACF,YAAY,QAAQ,IAAI,kBAAkB,IAAI,SAAgB,CAAC;AAAA,IAC/D,iBAAiB,QAAQ,eAAe;AAAA,IACxC,YAAY,QAAQ,IAAI,WAAW,CAAC;AAAA,EACtC,CAAC;AAED,aAAW,OAAO,cAAc;AAC9B,QAAI;AAAE,YAAM,SAAS;AAAA,IAAE,QAAQ;AAAA,IAAC;AAAA,EAClC;AAEA,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,8BAA8B;AACjE,QAAI,aAAa,OAAO,cAAc,YAAY;AAEhD,YAAM,sBAAsB,CAAC,CAAC,UAAU,eAAe;AACvD,UAAI,CAAC,qBAAqB;AACxB,cAAM,UAAU,SAAS;AAAA,MAC3B;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAiB;AAGzB,MAAI;AAEF,UAAM,QAAQ,MAAM,OAAO,MAAM;AACjC,QAAI,OAAO,UAAU;AACnB,UAAI;AACF,cAAM,QAAQ,MAAM,SAAS,SAAS;AACtC,YAAI,SAAS,OAAO,MAAM,SAAS,WAAY,OAAM;AAAA,MACvD,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF,QAAQ;AAAA,EAAC;AAET,MAAI;AACF,UAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,UAAM,0BAA0B,UAAU,gBAAgB,yBAAyB,IAC9E,UAAU,QAAQ,yBAAyB,IAC5C;AACJ,QAAI,YAAY,yBAAyB,YAAY,GAAG;AACtD,YAAM,EAAE,mCAAmC,IAAI,MAAM,OAAO,gDAAgD;AAC5G,yCAAmC,UAAU,uBAAuB;AAAA,IACtE;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AACA,IAAI;AAEF,UAAQ,aAAa;AACvB,QAAQ;AAER;",
4
+ "sourcesContent": ["import { createContainer, asValue, AwilixContainer, InjectionMode, type Resolver } from 'awilix'\nimport { RequestContext } from '@mikro-orm/core'\nimport { getOrm } from '@open-mercato/shared/lib/db/mikro'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine } from '@open-mercato/shared/lib/query/engine'\nimport { DefaultDataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { commandRegistry, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { applyDiOverridesToContainer } from '@open-mercato/shared/modules/overrides'\n\ntype DynamicCradle = Record<string, any>\n\nexport type AppContainer = AwilixContainer<DynamicCradle>\nexport type DiRegistrar = (container: AppContainer) => void\n\n// Registration pattern for publishable packages\n// Use globalThis to survive tsx/esbuild module duplication issue where the same\n// file can be loaded as multiple module instances when mixing dynamic and static imports\nconst GLOBAL_KEY = '__openMercatoDiRegistrars__'\n\nfunction getGlobalRegistrars(): DiRegistrar[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalRegistrars(registrars: DiRegistrar[]): void {\n (globalThis as any)[GLOBAL_KEY] = registrars\n}\n\nexport function registerDiRegistrars(registrars: DiRegistrar[]) {\n const existing = getGlobalRegistrars()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] DI registrars re-registered (this may occur during HMR)')\n }\n setGlobalRegistrars(registrars)\n}\n\nexport function getDiRegistrars(): DiRegistrar[] {\n const registrars = getGlobalRegistrars()\n if (!registrars) {\n throw new Error('[Bootstrap] DI registrars not registered. Call registerDiRegistrars() at bootstrap.')\n }\n return registrars\n}\n\nfunction isAwilixResolver(value: unknown): value is Resolver<unknown> {\n return Boolean(value && typeof value === 'object' && typeof (value as { resolve?: unknown }).resolve === 'function')\n}\n\nfunction toAwilixRegistrations(registrations: Record<string, unknown>): Record<string, Resolver<any>> {\n return Object.fromEntries(\n Object.entries(registrations).map(([key, value]) => [\n key,\n isAwilixResolver(value) ? value : asValue(value),\n ]),\n )\n}\n\nexport async function createRequestContainer(): Promise<AppContainer> {\n const diRegistrars = getDiRegistrars()\n const orm = await getOrm()\n // Use a fresh event manager so request-level subscribers (e.g., encryption) don't pile up globally\n const baseEm = (RequestContext.getEntityManager() as any) ?? orm.em\n const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true }) as unknown as EntityManager\n const container = createContainer<DynamicCradle>({ injectionMode: InjectionMode.CLASSIC })\n // Core registrations\n container.register({\n em: asValue(em),\n queryEngine: asValue(new BasicQueryEngine(em, undefined, () => {\n try { return container.resolve('tenantEncryptionService') as any } catch { return null }\n })),\n dataEngine: asValue(new DefaultDataEngine(em, container as any)),\n commandRegistry: asValue(commandRegistry),\n commandBus: asValue(new CommandBus()),\n })\n // Allow modules to override/extend\n for (const reg of diRegistrars) {\n try { reg?.(container) } catch {}\n }\n // Core bootstrap (cache, event bus, encryption subscriber/KMS, module subscribers)\n try {\n const { bootstrap } = await import('@open-mercato/core/bootstrap') as any\n if (bootstrap && typeof bootstrap === 'function') {\n // Avoid double bootstrap if caller already wired it\n const alreadyBootstrapped = !!container.registrations?.eventBus\n if (!alreadyBootstrapped) {\n await bootstrap(container)\n }\n }\n } catch { /* optional */ }\n // App-level DI override (last chance)\n // This import path resolves only in the app context, not in packages\n try {\n // @ts-ignore - @/di only exists in app context, not in packages\n const appDi = await import('@/di') as any\n if (appDi?.register) {\n try {\n const maybe = appDi.register(container)\n if (maybe && typeof maybe.then === 'function') await maybe\n } catch {}\n }\n } catch {}\n applyDiOverridesToContainer({\n register: (registrations) => container.register(toAwilixRegistrations(registrations)),\n unregister: (key) => container.register({ [key]: asValue(undefined) }),\n })\n // Ensure tenant encryption subscriber is always registered on the fresh request-scoped EM\n try {\n const emForEnc = container.resolve('em') as any\n const tenantEncryptionService = container.hasRegistration('tenantEncryptionService')\n ? (container.resolve('tenantEncryptionService') as any)\n : null\n if (emForEnc && tenantEncryptionService?.isEnabled?.()) {\n const { registerTenantEncryptionSubscriber } = await import('@open-mercato/shared/lib/encryption/subscriber')\n registerTenantEncryptionSubscriber(emForEnc, tenantEncryptionService)\n }\n } catch {\n // best-effort; do not block container creation\n }\n return container\n}\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // allow CLI/generator usage where Next server-only is not present\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from "@open-mercato/shared/modules/middleware/page";
2
+ import { applyPageGuardOverridesToEntries } from "@open-mercato/shared/modules/overrides";
2
3
  const DEFAULT_PRIORITY = 100;
3
4
  function shouldRunMiddleware(middleware, mode, pathname) {
4
5
  if (middleware.mode && middleware.mode !== mode) return false;
@@ -10,7 +11,7 @@ function compareMiddleware(a, b) {
10
11
  return a.id.localeCompare(b.id);
11
12
  }
12
13
  function flattenAndSortMiddleware(entries, mode, pathname) {
13
- return entries.flatMap((entry) => entry.middleware).filter((middleware) => shouldRunMiddleware(middleware, mode, pathname)).sort(compareMiddleware);
14
+ return applyPageGuardOverridesToEntries(entries).flatMap((entry) => entry.middleware).filter((middleware) => shouldRunMiddleware(middleware, mode, pathname)).sort(compareMiddleware);
14
15
  }
15
16
  async function executePageMiddleware(args) {
16
17
  const { entries, context, onError } = args;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/middleware/page-executor.ts"],
4
- "sourcesContent": ["import type {\n PageMiddlewareContext,\n PageMiddlewareMode,\n PageMiddlewareRegistryEntry,\n PageMiddlewareResult,\n PageRouteMiddleware,\n} from '@open-mercato/shared/modules/middleware/page'\nimport { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'\n\ntype ExecutePageMiddlewareArgs = {\n entries: PageMiddlewareRegistryEntry[]\n context: PageMiddlewareContext\n onError?: (error: unknown, middleware: Pick<PageRouteMiddleware, 'id' | 'priority'>) => void\n}\n\nconst DEFAULT_PRIORITY = 100\n\nfunction shouldRunMiddleware(middleware: PageRouteMiddleware, mode: PageMiddlewareMode, pathname: string): boolean {\n if (middleware.mode && middleware.mode !== mode) return false\n return matchPageMiddlewareTarget(pathname, middleware.target)\n}\n\nfunction compareMiddleware(a: PageRouteMiddleware, b: PageRouteMiddleware): number {\n const priorityDiff = (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY)\n if (priorityDiff !== 0) return priorityDiff\n return a.id.localeCompare(b.id)\n}\n\nfunction flattenAndSortMiddleware(\n entries: PageMiddlewareRegistryEntry[],\n mode: PageMiddlewareMode,\n pathname: string\n): PageRouteMiddleware[] {\n return entries\n .flatMap((entry) => entry.middleware)\n .filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))\n .sort(compareMiddleware)\n}\n\nexport async function executePageMiddleware(args: ExecutePageMiddlewareArgs): Promise<PageMiddlewareResult> {\n const { entries, context, onError } = args\n const matchedMiddleware = flattenAndSortMiddleware(entries, context.mode, context.pathname)\n for (const middleware of matchedMiddleware) {\n try {\n const result = await middleware.run(context)\n if (result.action === 'redirect') return result\n } catch (error) {\n if (onError) {\n onError(error, { id: middleware.id, priority: middleware.priority })\n } else {\n console.error('[middleware:page] execution failed', { id: middleware.id, error })\n }\n throw error\n }\n }\n return CONTINUE_PAGE_MIDDLEWARE\n}\n\nexport async function resolvePageMiddlewareRedirect(args: ExecutePageMiddlewareArgs): Promise<string | null> {\n const result = await executePageMiddleware(args)\n if (result.action !== 'redirect') return null\n return result.location\n}\n"],
5
- "mappings": "AAOA,SAAS,0BAA0B,iCAAiC;AAQpE,MAAM,mBAAmB;AAEzB,SAAS,oBAAoB,YAAiC,MAA0B,UAA2B;AACjH,MAAI,WAAW,QAAQ,WAAW,SAAS,KAAM,QAAO;AACxD,SAAO,0BAA0B,UAAU,WAAW,MAAM;AAC9D;AAEA,SAAS,kBAAkB,GAAwB,GAAgC;AACjF,QAAM,gBAAgB,EAAE,YAAY,qBAAqB,EAAE,YAAY;AACvE,MAAI,iBAAiB,EAAG,QAAO;AAC/B,SAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAChC;AAEA,SAAS,yBACP,SACA,MACA,UACuB;AACvB,SAAO,QACJ,QAAQ,CAAC,UAAU,MAAM,UAAU,EACnC,OAAO,CAAC,eAAe,oBAAoB,YAAY,MAAM,QAAQ,CAAC,EACtE,KAAK,iBAAiB;AAC3B;AAEA,eAAsB,sBAAsB,MAAgE;AAC1G,QAAM,EAAE,SAAS,SAAS,QAAQ,IAAI;AACtC,QAAM,oBAAoB,yBAAyB,SAAS,QAAQ,MAAM,QAAQ,QAAQ;AAC1F,aAAW,cAAc,mBAAmB;AAC1C,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,IAAI,OAAO;AAC3C,UAAI,OAAO,WAAW,WAAY,QAAO;AAAA,IAC3C,SAAS,OAAO;AACd,UAAI,SAAS;AACX,gBAAQ,OAAO,EAAE,IAAI,WAAW,IAAI,UAAU,WAAW,SAAS,CAAC;AAAA,MACrE,OAAO;AACL,gBAAQ,MAAM,sCAAsC,EAAE,IAAI,WAAW,IAAI,MAAM,CAAC;AAAA,MAClF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,8BAA8B,MAAyD;AAC3G,QAAM,SAAS,MAAM,sBAAsB,IAAI;AAC/C,MAAI,OAAO,WAAW,WAAY,QAAO;AACzC,SAAO,OAAO;AAChB;",
4
+ "sourcesContent": ["import type {\n PageMiddlewareContext,\n PageMiddlewareMode,\n PageMiddlewareRegistryEntry,\n PageMiddlewareResult,\n PageRouteMiddleware,\n} from '@open-mercato/shared/modules/middleware/page'\nimport { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'\nimport { applyPageGuardOverridesToEntries } from '@open-mercato/shared/modules/overrides'\n\ntype ExecutePageMiddlewareArgs = {\n entries: PageMiddlewareRegistryEntry[]\n context: PageMiddlewareContext\n onError?: (error: unknown, middleware: Pick<PageRouteMiddleware, 'id' | 'priority'>) => void\n}\n\nconst DEFAULT_PRIORITY = 100\n\nfunction shouldRunMiddleware(middleware: PageRouteMiddleware, mode: PageMiddlewareMode, pathname: string): boolean {\n if (middleware.mode && middleware.mode !== mode) return false\n return matchPageMiddlewareTarget(pathname, middleware.target)\n}\n\nfunction compareMiddleware(a: PageRouteMiddleware, b: PageRouteMiddleware): number {\n const priorityDiff = (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY)\n if (priorityDiff !== 0) return priorityDiff\n return a.id.localeCompare(b.id)\n}\n\nfunction flattenAndSortMiddleware(\n entries: PageMiddlewareRegistryEntry[],\n mode: PageMiddlewareMode,\n pathname: string\n): PageRouteMiddleware[] {\n return applyPageGuardOverridesToEntries(entries)\n .flatMap((entry) => entry.middleware)\n .filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))\n .sort(compareMiddleware)\n}\n\nexport async function executePageMiddleware(args: ExecutePageMiddlewareArgs): Promise<PageMiddlewareResult> {\n const { entries, context, onError } = args\n const matchedMiddleware = flattenAndSortMiddleware(entries, context.mode, context.pathname)\n for (const middleware of matchedMiddleware) {\n try {\n const result = await middleware.run(context)\n if (result.action === 'redirect') return result\n } catch (error) {\n if (onError) {\n onError(error, { id: middleware.id, priority: middleware.priority })\n } else {\n console.error('[middleware:page] execution failed', { id: middleware.id, error })\n }\n throw error\n }\n }\n return CONTINUE_PAGE_MIDDLEWARE\n}\n\nexport async function resolvePageMiddlewareRedirect(args: ExecutePageMiddlewareArgs): Promise<string | null> {\n const result = await executePageMiddleware(args)\n if (result.action !== 'redirect') return null\n return result.location\n}\n"],
5
+ "mappings": "AAOA,SAAS,0BAA0B,iCAAiC;AACpE,SAAS,wCAAwC;AAQjD,MAAM,mBAAmB;AAEzB,SAAS,oBAAoB,YAAiC,MAA0B,UAA2B;AACjH,MAAI,WAAW,QAAQ,WAAW,SAAS,KAAM,QAAO;AACxD,SAAO,0BAA0B,UAAU,WAAW,MAAM;AAC9D;AAEA,SAAS,kBAAkB,GAAwB,GAAgC;AACjF,QAAM,gBAAgB,EAAE,YAAY,qBAAqB,EAAE,YAAY;AACvE,MAAI,iBAAiB,EAAG,QAAO;AAC/B,SAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAChC;AAEA,SAAS,yBACP,SACA,MACA,UACuB;AACvB,SAAO,iCAAiC,OAAO,EAC5C,QAAQ,CAAC,UAAU,MAAM,UAAU,EACnC,OAAO,CAAC,eAAe,oBAAoB,YAAY,MAAM,QAAQ,CAAC,EACtE,KAAK,iBAAiB;AAC3B;AAEA,eAAsB,sBAAsB,MAAgE;AAC1G,QAAM,EAAE,SAAS,SAAS,QAAQ,IAAI;AACtC,QAAM,oBAAoB,yBAAyB,SAAS,QAAQ,MAAM,QAAQ,QAAQ;AAC1F,aAAW,cAAc,mBAAmB;AAC1C,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,IAAI,OAAO;AAC3C,UAAI,OAAO,WAAW,WAAY,QAAO;AAAA,IAC3C,SAAS,OAAO;AACd,UAAI,SAAS;AACX,gBAAQ,OAAO,EAAE,IAAI,WAAW,IAAI,UAAU,WAAW,SAAS,CAAC;AAAA,MACrE,OAAO;AACL,gBAAQ,MAAM,sCAAsC,EAAE,IAAI,WAAW,IAAI,MAAM,CAAC;AAAA,MAClF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,8BAA8B,MAAyD;AAC3G,QAAM,SAAS,MAAM,sBAAsB,IAAI;AAC/C,MAAI,OAAO,WAAW,WAAY,QAAO;AACzC,SAAO,OAAO;AAChB;",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,10 @@
1
+ import { applyModuleOverridesToModules } from "@open-mercato/shared/modules/overrides";
1
2
  let _modules = null;
2
3
  function registerModules(modules) {
3
4
  if (_modules !== null && process.env.NODE_ENV === "development") {
4
5
  console.debug("[Bootstrap] Modules re-registered (this may occur during HMR)");
5
6
  }
6
- _modules = modules;
7
+ _modules = applyModuleOverridesToModules(modules);
7
8
  }
8
9
  function getModules() {
9
10
  if (!_modules) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/modules/registry.ts"],
4
- "sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\n\n// Registration pattern for publishable packages\nlet _modules: Module[] | null = null\n\nexport function registerModules(modules: Module[]) {\n if (_modules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')\n }\n _modules = modules\n}\n\nexport function getModules(): Module[] {\n if (!_modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return _modules\n}\n"],
5
- "mappings": "AAGA,IAAI,WAA4B;AAEzB,SAAS,gBAAgB,SAAmB;AACjD,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,+DAA+D;AAAA,EAC/E;AACA,aAAW;AACb;AAEO,SAAS,aAAuB;AACrC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\nimport { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'\n\n// Registration pattern for publishable packages\nlet _modules: Module[] | null = null\n\nexport function registerModules(modules: Module[]) {\n if (_modules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')\n }\n _modules = applyModuleOverridesToModules(modules)\n}\n\nexport function getModules(): Module[] {\n if (!_modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return _modules\n}\n"],
5
+ "mappings": "AACA,SAAS,qCAAqC;AAG9C,IAAI,WAA4B;AAEzB,SAAS,gBAAgB,SAAmB;AACjD,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,+DAA+D;AAAA,EAC/E;AACA,aAAW,8BAA8B,OAAO;AAClD;AAEO,SAAS,aAAuB;AACrC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { applyNotificationHandlerOverridesToEntries } from "@open-mercato/shared/modules/overrides";
1
2
  const GLOBAL_NOTIFICATION_HANDLERS_KEY = "__openMercatoNotificationHandlers__";
2
3
  let _handlerEntries = null;
3
4
  function readGlobalEntries() {
@@ -16,8 +17,9 @@ function writeGlobalEntries(entries) {
16
17
  }
17
18
  }
18
19
  function registerNotificationHandlers(entries) {
20
+ const finalEntries = applyNotificationHandlerOverridesToEntries(entries);
19
21
  const flat = [];
20
- for (const entry of entries) {
22
+ for (const entry of finalEntries) {
21
23
  for (const handler of entry.handlers ?? []) {
22
24
  flat.push({ moduleId: entry.moduleId, handler });
23
25
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/notifications/handler-registry.ts"],
4
- "sourcesContent": ["import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'\n\nconst GLOBAL_NOTIFICATION_HANDLERS_KEY = '__openMercatoNotificationHandlers__'\n\ntype NotificationHandlerRegistryEntry = {\n moduleId: string\n handler: NotificationHandler\n}\n\nlet _handlerEntries: NotificationHandlerRegistryEntry[] | null = null\n\nfunction readGlobalEntries(): NotificationHandlerRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY]\n return Array.isArray(value) ? (value as NotificationHandlerRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalEntries(entries: NotificationHandlerRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerNotificationHandlers(\n entries: Array<{ moduleId: string; handlers: NotificationHandler[] }>,\n) {\n const flat: NotificationHandlerRegistryEntry[] = []\n for (const entry of entries) {\n for (const handler of entry.handlers ?? []) {\n flat.push({ moduleId: entry.moduleId, handler })\n }\n }\n flat.sort((a, b) => (b.handler.priority ?? 50) - (a.handler.priority ?? 50))\n _handlerEntries = flat\n writeGlobalEntries(flat)\n}\n\nexport function getNotificationHandlerEntries(): NotificationHandlerRegistryEntry[] {\n const globalEntries = readGlobalEntries()\n if (globalEntries) return globalEntries\n if (!_handlerEntries) return []\n return _handlerEntries\n}\n"],
5
- "mappings": "AAEA,MAAM,mCAAmC;AAOzC,IAAI,kBAA6D;AAEjE,SAAS,oBAA+D;AACtE,MAAI;AACF,UAAM,QAAS,WAAuC,gCAAgC;AACtF,WAAO,MAAM,QAAQ,KAAK,IAAK,QAA+C;AAAA,EAChF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,SAA6C;AACvE,MAAI;AACF;AAAC,IAAC,WAAuC,gCAAgC,IAAI;AAAA,EAC/E,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,6BACd,SACA;AACA,QAAM,OAA2C,CAAC;AAClD,aAAW,SAAS,SAAS;AAC3B,eAAW,WAAW,MAAM,YAAY,CAAC,GAAG;AAC1C,WAAK,KAAK,EAAE,UAAU,MAAM,UAAU,QAAQ,CAAC;AAAA,IACjD;AAAA,EACF;AACA,OAAK,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,YAAY,OAAO,EAAE,QAAQ,YAAY,GAAG;AAC3E,oBAAkB;AAClB,qBAAmB,IAAI;AACzB;AAEO,SAAS,gCAAoE;AAClF,QAAM,gBAAgB,kBAAkB;AACxC,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,gBAAiB,QAAO,CAAC;AAC9B,SAAO;AACT;",
4
+ "sourcesContent": ["import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'\nimport { applyNotificationHandlerOverridesToEntries } from '@open-mercato/shared/modules/overrides'\n\nconst GLOBAL_NOTIFICATION_HANDLERS_KEY = '__openMercatoNotificationHandlers__'\n\ntype NotificationHandlerRegistryEntry = {\n moduleId: string\n handler: NotificationHandler\n}\n\nlet _handlerEntries: NotificationHandlerRegistryEntry[] | null = null\n\nfunction readGlobalEntries(): NotificationHandlerRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY]\n return Array.isArray(value) ? (value as NotificationHandlerRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalEntries(entries: NotificationHandlerRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerNotificationHandlers(\n entries: Array<{ moduleId: string; handlers: NotificationHandler[] }>,\n) {\n const finalEntries = applyNotificationHandlerOverridesToEntries(entries)\n const flat: NotificationHandlerRegistryEntry[] = []\n for (const entry of finalEntries) {\n for (const handler of entry.handlers ?? []) {\n flat.push({ moduleId: entry.moduleId, handler })\n }\n }\n flat.sort((a, b) => (b.handler.priority ?? 50) - (a.handler.priority ?? 50))\n _handlerEntries = flat\n writeGlobalEntries(flat)\n}\n\nexport function getNotificationHandlerEntries(): NotificationHandlerRegistryEntry[] {\n const globalEntries = readGlobalEntries()\n if (globalEntries) return globalEntries\n if (!_handlerEntries) return []\n return _handlerEntries\n}\n"],
5
+ "mappings": "AACA,SAAS,kDAAkD;AAE3D,MAAM,mCAAmC;AAOzC,IAAI,kBAA6D;AAEjE,SAAS,oBAA+D;AACtE,MAAI;AACF,UAAM,QAAS,WAAuC,gCAAgC;AACtF,WAAO,MAAM,QAAQ,KAAK,IAAK,QAA+C;AAAA,EAChF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,SAA6C;AACvE,MAAI;AACF;AAAC,IAAC,WAAuC,gCAAgC,IAAI;AAAA,EAC/E,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,6BACd,SACA;AACA,QAAM,eAAe,2CAA2C,OAAO;AACvE,QAAM,OAA2C,CAAC;AAClD,aAAW,SAAS,cAAc;AAChC,eAAW,WAAW,MAAM,YAAY,CAAC,GAAG;AAC1C,WAAK,KAAK,EAAE,UAAU,MAAM,UAAU,QAAQ,CAAC;AAAA,IACjD;AAAA,EACF;AACA,OAAK,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,YAAY,OAAO,EAAE,QAAQ,YAAY,GAAG;AAC3E,oBAAkB;AAClB,qBAAmB,IAAI;AACzB;AAEO,SAAS,gCAAoE;AAClF,QAAM,gBAAgB,kBAAkB;AACxC,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,gBAAiB,QAAO,CAAC;AAC9B,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.6.2-develop.3467.1.2a1818709d";
1
+ const APP_VERSION = "0.6.3-canary.3483.1.98d4b4ecc7";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.2-develop.3467.1.2a1818709d'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.3-canary.3483.1.98d4b4ecc7'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }