@open-mercato/shared 0.9.1-develop.7575.1.8480826a9b → 0.10.1-develop.7577.1.477f2486e5

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 (125) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +4 -4
  3. package/dist/lib/auth/server.js +1 -1
  4. package/dist/lib/auth/server.js.map +2 -2
  5. package/dist/lib/bootstrap/dynamicLoader.js +46 -6
  6. package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
  7. package/dist/lib/bootstrap/factory.js +4 -3
  8. package/dist/lib/bootstrap/factory.js.map +2 -2
  9. package/dist/lib/bootstrap/next-subpath-resolve-hook-worker.js +6 -0
  10. package/dist/lib/bootstrap/next-subpath-resolve-hook-worker.js.map +7 -0
  11. package/dist/lib/bootstrap/next-subpath-resolve-hook.js +62 -0
  12. package/dist/lib/bootstrap/next-subpath-resolve-hook.js.map +7 -0
  13. package/dist/lib/catalog-visibility/canonicalizeEffectiveScope.js +33 -0
  14. package/dist/lib/catalog-visibility/canonicalizeEffectiveScope.js.map +7 -0
  15. package/dist/lib/catalog-visibility/hashEffectiveScope.js +9 -0
  16. package/dist/lib/catalog-visibility/hashEffectiveScope.js.map +7 -0
  17. package/dist/lib/catalog-visibility/index.js +4 -0
  18. package/dist/lib/catalog-visibility/index.js.map +2 -2
  19. package/dist/lib/commands/command-bus.js +45 -13
  20. package/dist/lib/commands/command-bus.js.map +2 -2
  21. package/dist/lib/commands/types.js.map +2 -2
  22. package/dist/lib/crud/exporters.js +1 -1
  23. package/dist/lib/crud/exporters.js.map +2 -2
  24. package/dist/lib/crud/ids.js +13 -1
  25. package/dist/lib/crud/ids.js.map +2 -2
  26. package/dist/lib/db/advisoryLock.js +172 -0
  27. package/dist/lib/db/advisoryLock.js.map +7 -0
  28. package/dist/lib/db/pg-errors.js +14 -0
  29. package/dist/lib/db/pg-errors.js.map +2 -2
  30. package/dist/lib/localization/index.js +9 -1
  31. package/dist/lib/localization/index.js.map +2 -2
  32. package/dist/lib/localization/translatable-fields.js +26 -0
  33. package/dist/lib/localization/translatable-fields.js.map +2 -2
  34. package/dist/lib/number.js +6 -0
  35. package/dist/lib/number.js.map +2 -2
  36. package/dist/lib/openapi/generator.js +84 -67
  37. package/dist/lib/openapi/generator.js.map +2 -2
  38. package/dist/lib/query/engine.js +45 -10
  39. package/dist/lib/query/engine.js.map +2 -2
  40. package/dist/lib/query/join-utils.js +1 -1
  41. package/dist/lib/query/join-utils.js.map +2 -2
  42. package/dist/lib/query/overlap.js +58 -0
  43. package/dist/lib/query/overlap.js.map +7 -0
  44. package/dist/lib/query/types.js.map +2 -2
  45. package/dist/lib/url.js +24 -0
  46. package/dist/lib/url.js.map +2 -2
  47. package/dist/lib/version.js +1 -1
  48. package/dist/lib/version.js.map +1 -1
  49. package/dist/lib/webhooks/body.js +7 -3
  50. package/dist/lib/webhooks/body.js.map +2 -2
  51. package/dist/lib/webhooks/index.js +2 -0
  52. package/dist/lib/webhooks/index.js.map +2 -2
  53. package/dist/modules/integrations/types.js.map +2 -2
  54. package/dist/modules/notifications/safeHref.js +20 -0
  55. package/dist/modules/notifications/safeHref.js.map +7 -0
  56. package/dist/modules/notifications/types.js +4 -0
  57. package/dist/modules/notifications/types.js.map +3 -3
  58. package/dist/modules/payment_gateways/types.js +14 -1
  59. package/dist/modules/payment_gateways/types.js.map +2 -2
  60. package/dist/modules/registry.js.map +2 -2
  61. package/dist/modules/search.js.map +2 -2
  62. package/dist/modules/widgets/injection-loader.js +14 -3
  63. package/dist/modules/widgets/injection-loader.js.map +2 -2
  64. package/package.json +10 -3
  65. package/src/lib/__tests__/number.test.ts +24 -1
  66. package/src/lib/__tests__/url.cross-site.test.ts +140 -0
  67. package/src/lib/auth/__tests__/server.test.ts +14 -0
  68. package/src/lib/auth/server.ts +1 -1
  69. package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +2 -2
  70. package/src/lib/bootstrap/__tests__/dynamicLoader.appModuleOverrides.test.ts +2 -2
  71. package/src/lib/bootstrap/__tests__/dynamicLoader.cacheRecovery.test.ts +2 -2
  72. package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +2 -2
  73. package/src/lib/bootstrap/__tests__/dynamicLoader.externalSubpath.test.ts +141 -0
  74. package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +2 -2
  75. package/src/lib/bootstrap/__tests__/factory.test.ts +18 -6
  76. package/src/lib/bootstrap/__tests__/next-subpath-resolve-hook.test.ts +174 -0
  77. package/src/lib/bootstrap/dynamicLoader.ts +63 -5
  78. package/src/lib/bootstrap/factory.ts +8 -3
  79. package/src/lib/bootstrap/next-subpath-resolve-hook-worker.ts +3 -0
  80. package/src/lib/bootstrap/next-subpath-resolve-hook.ts +130 -0
  81. package/src/lib/bootstrap/types.ts +5 -1
  82. package/src/lib/catalog-visibility/__tests__/hashEffectiveScope.test.ts +121 -0
  83. package/src/lib/catalog-visibility/canonicalizeEffectiveScope.ts +41 -0
  84. package/src/lib/catalog-visibility/hashEffectiveScope.ts +8 -0
  85. package/src/lib/catalog-visibility/index.ts +2 -0
  86. package/src/lib/commands/__tests__/command-bus.test.ts +100 -1
  87. package/src/lib/commands/command-bus.ts +42 -10
  88. package/src/lib/commands/command-interceptor.ts +4 -0
  89. package/src/lib/commands/types.ts +14 -0
  90. package/src/lib/crud/__tests__/exporters.test.ts +18 -0
  91. package/src/lib/crud/__tests__/ids.test.ts +34 -1
  92. package/src/lib/crud/exporters.ts +1 -1
  93. package/src/lib/crud/ids.ts +24 -0
  94. package/src/lib/db/__tests__/advisoryLock.integration.test.ts +343 -0
  95. package/src/lib/db/__tests__/advisoryLock.test.ts +564 -0
  96. package/src/lib/db/__tests__/pg-errors.test.ts +68 -1
  97. package/src/lib/db/advisoryLock.ts +271 -0
  98. package/src/lib/db/pg-errors.ts +35 -0
  99. package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +23 -0
  100. package/src/lib/localization/__tests__/translatable-fields.test.ts +17 -0
  101. package/src/lib/localization/index.ts +8 -1
  102. package/src/lib/localization/translatable-fields.ts +46 -0
  103. package/src/lib/number.ts +13 -0
  104. package/src/lib/openapi/__tests__/optional-path-params.test.ts +102 -0
  105. package/src/lib/openapi/generator.ts +106 -77
  106. package/src/lib/query/__tests__/empty-ids-filter.pg.test.ts +89 -0
  107. package/src/lib/query/__tests__/engine.empty-in-filter.test.ts +79 -0
  108. package/src/lib/query/__tests__/overlap.test.ts +219 -0
  109. package/src/lib/query/engine.ts +58 -8
  110. package/src/lib/query/join-utils.ts +1 -1
  111. package/src/lib/query/overlap.ts +106 -0
  112. package/src/lib/query/types.ts +13 -1
  113. package/src/lib/url.ts +30 -0
  114. package/src/lib/webhooks/__tests__/body.test.ts +48 -0
  115. package/src/lib/webhooks/body.ts +11 -4
  116. package/src/lib/webhooks/index.ts +1 -0
  117. package/src/modules/integrations/types.ts +6 -0
  118. package/src/modules/notifications/safeHref.ts +19 -0
  119. package/src/modules/notifications/types.ts +7 -0
  120. package/src/modules/payment_gateways/__tests__/types.test.ts +44 -0
  121. package/src/modules/payment_gateways/types.ts +52 -6
  122. package/src/modules/registry.ts +5 -0
  123. package/src/modules/search.ts +39 -0
  124. package/src/modules/widgets/__tests__/injection-loader.partitioned-registration.test.ts +78 -0
  125. package/src/modules/widgets/injection-loader.ts +33 -3
@@ -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, 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": "AACA,SAAS,2BAA2B;AACpC,SAAS,wBAAwB,4BAA4B;AAC7D,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,mCAAmC;AAC5C,SAAS,sCAAsC;AAC/C,SAAS,mCAAmC;AAC5C,SAAS,iCAAiC;AAC1C,SAAS,+BAA+B;AACxC,SAAS,kCAAkC;AAC3C,SAAS,8BAA8B;AACvC,SAAS,mCAAmC;AAC5C,SAAS,8BAA8B;AACvC,SAAS,oCAAoC;AAC7C,SAAS,6BAA6B,iBAAiB,4BAA4B;AACnF,SAAS,wCAAwC;AAEjD,MAAM,oBAAoB,oBAAI,IAAY;AAG1C,IAAI,4BAAkD;AAY/C,SAAS,gBAAgB,MAAqB,UAA4B,CAAC,GAAG;AACnF,SAAO,SAAS,YAAkB;AAChC,UAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAI,QAAQ,gBAAgB;AAC1B,6BAAuB,QAAQ,cAAc;AAAA,IAC/C;AAGA,QAAI,kBAAkB,IAAI,eAAe,KAAK,QAAQ,IAAI,aAAa,cAAe;AACtF,sBAAkB,IAAI,eAAe;AAGrC,wBAAoB,KAAK,QAAQ;AACjC,yBAAqB,KAAK,aAAa,OAAO,CAAC,MAAkC,KAAK,IAAI,CAAC;AAG3F,oBAAgB,KAAK,OAAO;AAC5B,gCAA4B;AAC5B,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,cAAc,QAAQ;AAC/B,6BAAqB,OAAO,YAAY;AAAA,MAC1C;AACA,UAAI,OAAO,SAAS,QAAQ;AAC1B,wBAAgB,OAAO,OAAO;AAAA,MAChC;AAAA,IACF;AAGA,sBAAkB,KAAK,SAAS;AAGhC,QAAI,KAAK,sBAAsB;AAC7B,2BAAqB,KAAK,oBAAoB;AAAA,IAChD;AAGA,QAAI,KAAK,qBAAqB;AAC5B,kCAA4B,KAAK,mBAAmB;AAAA,IACtD;AAGA,QAAI,KAAK,wBAAwB;AAC/B,qCAA+B,KAAK,sBAAsB;AAAA,IAC5D;AAGA,QAAI,KAAK,eAAe,QAAQ;AAC9B,kCAA4B,KAAK,aAAa;AAAA,IAChD;AAGA,QAAI,KAAK,iBAAiB;AACxB,gCAA0B,KAAK,eAAe;AAAA,IAChD;AAGA,QAAI,KAAK,oBAAoB;AAC3B,8BAAwB,KAAK,kBAAkB;AAAA,IACjD;AAGA,QAAI,KAAK,0BAA0B;AACjC,YAAM,eAAe,iCAAiC,KAAK,wBAAwB;AACnF,YAAM,eAAe,aAAa,QAAQ,CAAC,UAAU,MAAM,sBAAsB,CAAC,CAAC;AACnF,iCAA2B,YAAY;AAAA,IACzC;AAGA,QAAI,KAAK,cAAc;AACrB,6BAAuB,KAAK,YAAY;AAAA,IAC1C;AAGA,QAAI,KAAK,2BAA2B;AAClC,kCAA4B,KAAK,yBAAyB;AAAA,IAC5D;AAGA,QAAI,KAAK,sBAAsB;AAC7B,6BAAuB,KAAK,oBAAoB;AAAA,IAClD;AAGA,QAAI,KAAK,4BAA4B;AACnC,mCAA6B,KAAK,0BAA0B;AAAA,IAC9D;AAIA,gCAA4B,mCAAmC,MAAM,OAAO;AAC5E,SAAK;AAEL,YAAQ,yBAAyB;AAAA,EACnC;AACF;AAMA,eAAsB,2BAA0C;AAC9D,MAAI,2BAA2B;AAC7B,UAAM;AAAA,EACR;AACF;AAEA,eAAe,mCAAmC,MAAqB,SAA0C;AAG/G,MAAI;AACF,UAAM,gBAAgB,MAAM,OAAO,kDAAkD;AACrF,QAAI,CAAC,QAAQ,0BAA0B;AACrC,oBAAc,6BAA6B,KAAK,sBAAsB;AAAA,IACxE;AACA,kBAAc,4BAA4B,KAAK,iBAAiB,KAAK,sBAAsB;AAC3F,kBAAc;AAAA,MACZ,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,EAAE,EAAE,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AAAA,IAC9G;AAEA,QAAI,CAAC,QAAQ,kBAAkB;AAC7B,YAAM,CAAC,mBAAmB,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC/D,OAAO,mDAAmD;AAAA,QAC1D,OAAO,mDAAmD;AAAA,MAC5D,CAAC;AACD,wBAAkB,yBAAyB,KAAK,sBAAsB;AACtE,wBAAkB,yBAAyB,KAAK,sBAAsB;AAAA,IACxE;AAAA,EACF,QAAQ;AAAA,EAER;AASF;AAKO,SAAS,iBAA0B;AACxC,SAAO,kBAAkB,OAAO;AAClC;AAKO,SAAS,sBAA4B;AAC1C,oBAAkB,MAAM;AAC1B;",
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 // A partitioned runtime still needs its own entries \u2014 server-side resolvers read this\n // registry on request paths \u2014 it just must not shrink what a fuller partition already\n // published in the same process. Merge does both; skipping registration altogether left\n // API-only processes with an unregistered registry that throws on first read.\n coreInjection.registerCoreInjectionWidgets(\n data.injectionWidgetEntries,\n options.skipCoreInjectionWidgets ? { mode: 'merge' } : undefined,\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,6 @@
1
+ import { resolveNextSubpathAsync } from "./next-subpath-resolve-hook.js";
2
+ const resolve = resolveNextSubpathAsync;
3
+ export {
4
+ resolve
5
+ };
6
+ //# sourceMappingURL=next-subpath-resolve-hook-worker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/bootstrap/next-subpath-resolve-hook-worker.ts"],
4
+ "sourcesContent": ["import { resolveNextSubpathAsync } from './next-subpath-resolve-hook'\n\nexport const resolve = resolveNextSubpathAsync\n"],
5
+ "mappings": "AAAA,SAAS,+BAA+B;AAEjC,MAAM,UAAU;",
6
+ "names": []
7
+ }
@@ -0,0 +1,62 @@
1
+ import * as nodeModule from "node:module";
2
+ const NEXT_BARE_SUBPATH = /^next\/[A-Za-z0-9_-]+$/;
3
+ let installed = false;
4
+ function isModuleNotFound(error) {
5
+ return Boolean(error) && typeof error === "object" && error.code === "ERR_MODULE_NOT_FOUND";
6
+ }
7
+ function resolveNextSubpath(specifier, context, nextResolve) {
8
+ if (!NEXT_BARE_SUBPATH.test(specifier)) return nextResolve(specifier, context);
9
+ try {
10
+ return nextResolve(specifier, context);
11
+ } catch (error) {
12
+ if (!isModuleNotFound(error)) throw error;
13
+ try {
14
+ return nextResolve(`${specifier}.js`, context);
15
+ } catch {
16
+ throw error;
17
+ }
18
+ }
19
+ }
20
+ async function resolveNextSubpathAsync(specifier, context, nextResolve) {
21
+ if (!NEXT_BARE_SUBPATH.test(specifier)) return nextResolve(specifier, context);
22
+ try {
23
+ return await nextResolve(specifier, context);
24
+ } catch (error) {
25
+ if (!isModuleNotFound(error)) throw error;
26
+ try {
27
+ return await nextResolve(`${specifier}.js`, context);
28
+ } catch {
29
+ throw error;
30
+ }
31
+ }
32
+ }
33
+ function runtimeRegisterHooks() {
34
+ const candidate = nodeModule.registerHooks;
35
+ return typeof candidate === "function" ? candidate : void 0;
36
+ }
37
+ function runtimeRegisterHookModule() {
38
+ const candidate = nodeModule.register;
39
+ return typeof candidate === "function" ? candidate : void 0;
40
+ }
41
+ function installNextSubpathResolveHook(registerHooks = runtimeRegisterHooks(), registerHookModule = runtimeRegisterHookModule()) {
42
+ if (installed) return false;
43
+ if (typeof registerHooks === "function") {
44
+ registerHooks({ resolve: resolveNextSubpath });
45
+ } else if (typeof registerHookModule === "function") {
46
+ registerHookModule(new URL("./next-subpath-resolve-hook-worker.js", import.meta.url), import.meta.url);
47
+ } else {
48
+ return false;
49
+ }
50
+ installed = true;
51
+ return true;
52
+ }
53
+ function resetNextSubpathResolveHookForTests() {
54
+ installed = false;
55
+ }
56
+ export {
57
+ installNextSubpathResolveHook,
58
+ resetNextSubpathResolveHookForTests,
59
+ resolveNextSubpath,
60
+ resolveNextSubpathAsync
61
+ };
62
+ //# sourceMappingURL=next-subpath-resolve-hook.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/bootstrap/next-subpath-resolve-hook.ts"],
4
+ "sourcesContent": ["/**\n * Node ESM resolution for `next/<subpath>` outside the Next bundler (#6118, #7102).\n *\n * Installed by the `mercato` CLI entry, so every CLI command and worker it\n * runs can load package dists that import `next/dynamic`, `next/link`,\n * `next/server` \u2026 The `next/dynamic` imports in particular MUST keep the bare\n * specifier: Next's `next_dynamic` compiler transform only matches it exactly.\n *\n * `mercato ai_assistant mcp:serve-http` (and `mcp:serve`, `mcp:dev`,\n * `mcp:list-tools`) run in plain Node, and the AI API operation runner loads\n * API route modules with a runtime `import()`. Those modules import\n * `NextResponse` from `'next/server'`: a bare package subpath that only a\n * bundler resolves, because `next` ships no `exports` map and Node's ESM\n * resolver does no extension guessing. Every route with that import (287 of\n * them on the 0.7.0 packages) therefore fails to load with\n * `ERR_MODULE_NOT_FOUND \u2026 node_modules/next/server`, and every tool backed by\n * such a route returns \"Failed to load route module\".\n *\n * `next/server.js`, `next/headers.js`, `next/navigation.js` \u2026 do exist as files,\n * which is why `@open-mercato/shared` imports `'next/headers.js'`. Rather than\n * rewriting several hundred imports across every package (and every\n * third-party module), this hook retries a failed bare `next/<subpath>` with\n * the `.js` suffix. It only runs in the processes that install it, only for\n * `next/*` specifiers, and only after the normal resolution failed, so it\n * becomes a no-op the day `next` publishes an `exports` map.\n */\nimport * as nodeModule from 'node:module'\n\ntype ResolveContext = Record<string, unknown>\ntype ResolveResult = { url: string; format?: string | null | undefined; shortCircuit?: boolean }\ntype NextResolve = (specifier: string, context?: ResolveContext) => ResolveResult\ntype AsyncNextResolve = (specifier: string, context?: ResolveContext) => ResolveResult | Promise<ResolveResult>\n\n/** Bare `next/<one segment>` specifiers, e.g. `next/server`, `next/headers`. */\nconst NEXT_BARE_SUBPATH = /^next\\/[A-Za-z0-9_-]+$/\n\nlet installed = false\n\nfunction isModuleNotFound(error: unknown): boolean {\n return Boolean(error) && typeof error === 'object' && (error as { code?: unknown }).code === 'ERR_MODULE_NOT_FOUND'\n}\n\n/**\n * The resolve hook itself. Exported so it can be tested without touching the\n * process-global hook chain.\n */\nexport function resolveNextSubpath(\n specifier: string,\n context: ResolveContext,\n nextResolve: NextResolve,\n): ResolveResult {\n if (!NEXT_BARE_SUBPATH.test(specifier)) return nextResolve(specifier, context)\n try {\n return nextResolve(specifier, context)\n } catch (error) {\n if (!isModuleNotFound(error)) throw error\n try {\n return nextResolve(`${specifier}.js`, context)\n } catch {\n // Neither form resolves (Next is not installed at all): surface the\n // original error, which names the specifier the caller wrote.\n throw error\n }\n }\n}\n\nexport async function resolveNextSubpathAsync(\n specifier: string,\n context: ResolveContext,\n nextResolve: AsyncNextResolve,\n): Promise<ResolveResult> {\n if (!NEXT_BARE_SUBPATH.test(specifier)) return nextResolve(specifier, context)\n try {\n return await nextResolve(specifier, context)\n } catch (error) {\n if (!isModuleNotFound(error)) throw error\n try {\n return await nextResolve(`${specifier}.js`, context)\n } catch {\n throw error\n }\n }\n}\n\ntype RegisterHooks = (hooks: { resolve: typeof resolveNextSubpath }) => unknown\ntype RegisterHookModule = (specifier: string | URL, parentURL?: string | URL) => unknown\n\n/**\n * `module.registerHooks` exists from Node 22.15. It is read off the namespace\n * rather than imported by name: in the published ESM build a named import of an\n * export the running Node lacks fails while the module links, before the check\n * in `installNextSubpathResolveHook` can run, and the package still declares\n * `node >= 22.0.0`.\n */\nfunction runtimeRegisterHooks(): RegisterHooks | undefined {\n const candidate = (nodeModule as unknown as { registerHooks?: unknown }).registerHooks\n return typeof candidate === 'function' ? (candidate as RegisterHooks) : undefined\n}\n\nfunction runtimeRegisterHookModule(): RegisterHookModule | undefined {\n const candidate = (nodeModule as unknown as { register?: unknown }).register\n return typeof candidate === 'function' ? (candidate as RegisterHookModule) : undefined\n}\n\n/**\n * Install the hook once per process. Returns `true` when it was installed by\n * this call, `false` when it already was or when the runtime has neither\n * registration API. Node 22.15+ uses the synchronous hook API; earlier\n * supported Node 22 releases use the asynchronous hook API.\n */\nexport function installNextSubpathResolveHook(\n registerHooks: RegisterHooks | null | undefined = runtimeRegisterHooks(),\n registerHookModule: RegisterHookModule | null | undefined = runtimeRegisterHookModule(),\n): boolean {\n if (installed) return false\n if (typeof registerHooks === 'function') {\n registerHooks({ resolve: resolveNextSubpath })\n } else if (typeof registerHookModule === 'function') {\n registerHookModule(new URL('./next-subpath-resolve-hook-worker.js', import.meta.url), import.meta.url)\n } else {\n return false\n }\n installed = true\n return true\n}\n\n/** Test seam: forget that the hook was installed. */\nexport function resetNextSubpathResolveHookForTests(): void {\n installed = false\n}\n"],
5
+ "mappings": "AA0BA,YAAY,gBAAgB;AAQ5B,MAAM,oBAAoB;AAE1B,IAAI,YAAY;AAEhB,SAAS,iBAAiB,OAAyB;AACjD,SAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAa,MAA6B,SAAS;AAC/F;AAMO,SAAS,mBACd,WACA,SACA,aACe;AACf,MAAI,CAAC,kBAAkB,KAAK,SAAS,EAAG,QAAO,YAAY,WAAW,OAAO;AAC7E,MAAI;AACF,WAAO,YAAY,WAAW,OAAO;AAAA,EACvC,SAAS,OAAO;AACd,QAAI,CAAC,iBAAiB,KAAK,EAAG,OAAM;AACpC,QAAI;AACF,aAAO,YAAY,GAAG,SAAS,OAAO,OAAO;AAAA,IAC/C,QAAQ;AAGN,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,wBACpB,WACA,SACA,aACwB;AACxB,MAAI,CAAC,kBAAkB,KAAK,SAAS,EAAG,QAAO,YAAY,WAAW,OAAO;AAC7E,MAAI;AACF,WAAO,MAAM,YAAY,WAAW,OAAO;AAAA,EAC7C,SAAS,OAAO;AACd,QAAI,CAAC,iBAAiB,KAAK,EAAG,OAAM;AACpC,QAAI;AACF,aAAO,MAAM,YAAY,GAAG,SAAS,OAAO,OAAO;AAAA,IACrD,QAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAYA,SAAS,uBAAkD;AACzD,QAAM,YAAa,WAAsD;AACzE,SAAO,OAAO,cAAc,aAAc,YAA8B;AAC1E;AAEA,SAAS,4BAA4D;AACnE,QAAM,YAAa,WAAiD;AACpE,SAAO,OAAO,cAAc,aAAc,YAAmC;AAC/E;AAQO,SAAS,8BACd,gBAAkD,qBAAqB,GACvE,qBAA4D,0BAA0B,GAC7E;AACT,MAAI,UAAW,QAAO;AACtB,MAAI,OAAO,kBAAkB,YAAY;AACvC,kBAAc,EAAE,SAAS,mBAAmB,CAAC;AAAA,EAC/C,WAAW,OAAO,uBAAuB,YAAY;AACnD,uBAAmB,IAAI,IAAI,yCAAyC,YAAY,GAAG,GAAG,YAAY,GAAG;AAAA,EACvG,OAAO;AACL,WAAO;AAAA,EACT;AACA,cAAY;AACZ,SAAO;AACT;AAGO,SAAS,sCAA4C;AAC1D,cAAY;AACd;",
6
+ "names": []
7
+ }
@@ -0,0 +1,33 @@
1
+ const ID_LIST_KEYS = [
2
+ "categoryIds",
3
+ "excludeCategoryIds",
4
+ "excludeProductIds",
5
+ "excludeTagIds",
6
+ "tagIds"
7
+ ];
8
+ function compareStrings(left, right) {
9
+ if (left < right) return -1;
10
+ if (left > right) return 1;
11
+ return 0;
12
+ }
13
+ function sortedUnique(values) {
14
+ return Array.from(new Set(values)).sort(compareStrings);
15
+ }
16
+ function canonicalizeBranch(scope) {
17
+ const members = [];
18
+ const nested = (scope.allOf ?? []).map(canonicalizeBranch);
19
+ if (nested.length > 0) members.push(`"allOf":[${sortedUnique(nested).join(",")}]`);
20
+ for (const key of ID_LIST_KEYS) {
21
+ const ids = scope[key] ?? [];
22
+ if (ids.length > 0) members.push(`${JSON.stringify(key)}:${JSON.stringify(sortedUnique(ids))}`);
23
+ }
24
+ return `{${members.join(",")}}`;
25
+ }
26
+ function canonicalizeEffectiveScope(scope) {
27
+ if (scope === null) return "null";
28
+ return `[${sortedUnique(scope.map(canonicalizeBranch)).join(",")}]`;
29
+ }
30
+ export {
31
+ canonicalizeEffectiveScope
32
+ };
33
+ //# sourceMappingURL=canonicalizeEffectiveScope.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/catalog-visibility/canonicalizeEffectiveScope.ts"],
4
+ "sourcesContent": ["import type { AssortmentScope, EffectiveAssortmentScope } from './types'\n\nconst ID_LIST_KEYS = [\n 'categoryIds',\n 'excludeCategoryIds',\n 'excludeProductIds',\n 'excludeTagIds',\n 'tagIds',\n] as const\n\nfunction compareStrings(left: string, right: string): number {\n if (left < right) return -1\n if (left > right) return 1\n return 0\n}\n\nfunction sortedUnique(values: string[]): string[] {\n return Array.from(new Set(values)).sort(compareStrings)\n}\n\nfunction canonicalizeBranch(scope: AssortmentScope): string {\n const members: string[] = []\n const nested = (scope.allOf ?? []).map(canonicalizeBranch)\n if (nested.length > 0) members.push(`\"allOf\":[${sortedUnique(nested).join(',')}]`)\n for (const key of ID_LIST_KEYS) {\n const ids = scope[key] ?? []\n if (ids.length > 0) members.push(`${JSON.stringify(key)}:${JSON.stringify(sortedUnique(ids))}`)\n }\n return `{${members.join(',')}}`\n}\n\n/**\n * Deterministic serialization of a resolved scope. `null` (unrestricted) serializes as `null`\n * and `[]` (deny-all) as `[]`, so the two never collide. Empty and absent arrays are\n * equivalent, id lists are sorted and deduplicated, and OR-branches and `allOf` entries are\n * ordered by their own canonical form with identical ones collapsed.\n */\nexport function canonicalizeEffectiveScope(scope: EffectiveAssortmentScope): string {\n if (scope === null) return 'null'\n return `[${sortedUnique(scope.map(canonicalizeBranch)).join(',')}]`\n}\n"],
5
+ "mappings": "AAEA,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,MAAc,OAAuB;AAC3D,MAAI,OAAO,MAAO,QAAO;AACzB,MAAI,OAAO,MAAO,QAAO;AACzB,SAAO;AACT;AAEA,SAAS,aAAa,QAA4B;AAChD,SAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK,cAAc;AACxD;AAEA,SAAS,mBAAmB,OAAgC;AAC1D,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAU,MAAM,SAAS,CAAC,GAAG,IAAI,kBAAkB;AACzD,MAAI,OAAO,SAAS,EAAG,SAAQ,KAAK,YAAY,aAAa,MAAM,EAAE,KAAK,GAAG,CAAC,GAAG;AACjF,aAAW,OAAO,cAAc;AAC9B,UAAM,MAAM,MAAM,GAAG,KAAK,CAAC;AAC3B,QAAI,IAAI,SAAS,EAAG,SAAQ,KAAK,GAAG,KAAK,UAAU,GAAG,CAAC,IAAI,KAAK,UAAU,aAAa,GAAG,CAAC,CAAC,EAAE;AAAA,EAChG;AACA,SAAO,IAAI,QAAQ,KAAK,GAAG,CAAC;AAC9B;AAQO,SAAS,2BAA2B,OAAyC;AAClF,MAAI,UAAU,KAAM,QAAO;AAC3B,SAAO,IAAI,aAAa,MAAM,IAAI,kBAAkB,CAAC,EAAE,KAAK,GAAG,CAAC;AAClE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ import { createHash } from "node:crypto";
2
+ import { canonicalizeEffectiveScope } from "./canonicalizeEffectiveScope.js";
3
+ function hashEffectiveScope(scope) {
4
+ return createHash("sha256").update(canonicalizeEffectiveScope(scope)).digest("hex").slice(0, 16);
5
+ }
6
+ export {
7
+ hashEffectiveScope
8
+ };
9
+ //# sourceMappingURL=hashEffectiveScope.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/catalog-visibility/hashEffectiveScope.ts"],
4
+ "sourcesContent": ["import { createHash } from 'node:crypto'\nimport { canonicalizeEffectiveScope } from './canonicalizeEffectiveScope'\nimport type { EffectiveAssortmentScope } from './types'\n\n/** 16-hex-character sha256 digest of the canonical scope (SPEC-029 \u00A76.1 truncation). Server-only: uses node:crypto. */\nexport function hashEffectiveScope(scope: EffectiveAssortmentScope): string {\n return createHash('sha256').update(canonicalizeEffectiveScope(scope)).digest('hex').slice(0, 16)\n}\n"],
5
+ "mappings": "AAAA,SAAS,kBAAkB;AAC3B,SAAS,kCAAkC;AAIpC,SAAS,mBAAmB,OAAyC;AAC1E,SAAO,WAAW,QAAQ,EAAE,OAAO,2BAA2B,KAAK,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACjG;",
6
+ "names": []
7
+ }
@@ -2,7 +2,11 @@ import { matchesOne } from "./matchesOne.js";
2
2
  import { matchesScope } from "./matchesScope.js";
3
3
  import { unionScopes } from "./unionScopes.js";
4
4
  import { intersectScopes } from "./intersectScopes.js";
5
+ import { canonicalizeEffectiveScope } from "./canonicalizeEffectiveScope.js";
6
+ import { hashEffectiveScope } from "./hashEffectiveScope.js";
5
7
  export {
8
+ canonicalizeEffectiveScope,
9
+ hashEffectiveScope,
6
10
  intersectScopes,
7
11
  matchesOne,
8
12
  matchesScope,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/catalog-visibility/index.ts"],
4
- "sourcesContent": ["export type { AssortmentScope, EffectiveAssortmentScope, ScopedProduct } from './types'\nexport { matchesOne } from './matchesOne'\nexport { matchesScope } from './matchesScope'\nexport { unionScopes } from './unionScopes'\nexport { intersectScopes } from './intersectScopes'\n"],
5
- "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;",
4
+ "sourcesContent": ["export type { AssortmentScope, EffectiveAssortmentScope, ScopedProduct } from './types'\nexport { matchesOne } from './matchesOne'\nexport { matchesScope } from './matchesScope'\nexport { unionScopes } from './unionScopes'\nexport { intersectScopes } from './intersectScopes'\nexport { canonicalizeEffectiveScope } from './canonicalizeEffectiveScope'\nexport { hashEffectiveScope } from './hashEffectiveScope'\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,kCAAkC;AAC3C,SAAS,0BAA0B;",
6
6
  "names": []
7
7
  }
@@ -220,7 +220,9 @@ class CommandBus {
220
220
  commandId,
221
221
  auth: baseOptions.ctx.auth ?? null,
222
222
  selectedOrganizationId: baseOptions.ctx.selectedOrganizationId ?? baseOptions.ctx.auth?.orgId ?? null,
223
- container: baseOptions.ctx.container
223
+ container: baseOptions.ctx.container,
224
+ organizationScope: baseOptions.ctx.organizationScope ?? null,
225
+ request: baseOptions.ctx.request ?? null
224
226
  };
225
227
  const beforeResult = await runCommandInterceptorsBefore(
226
228
  allInterceptors,
@@ -261,13 +263,37 @@ class CommandBus {
261
263
  userFeatures = prepared.userFeatures;
262
264
  }
263
265
  const executeCore = async (coreOptions) => {
264
- const sourceLog = coreOptions.redoLogEntry ?? null;
265
- const snapshots = await this.prepareSnapshots(handler, coreOptions);
266
- const result2 = sourceLog && typeof handler.redo === "function" ? await handler.redo({ input: coreOptions.input, ctx: coreOptions.ctx, logEntry: sourceLog }) : await handler.execute(coreOptions.input, coreOptions.ctx);
267
- const afterSnapshot = await this.captureAfter(handler, coreOptions, result2);
266
+ const snapshots = {};
267
+ let executionOptions = coreOptions;
268
+ if (handler.prepareSnapshotInsideTransaction && handler.prepare) {
269
+ const prepare = handler.prepare;
270
+ const originalCtx = coreOptions.ctx;
271
+ const originalBeforeTransactionalWrite = originalCtx.beforeTransactionalWrite;
272
+ let prepared = false;
273
+ let transactionalCtx;
274
+ transactionalCtx = {
275
+ ...originalCtx,
276
+ beforeTransactionalWrite: async (em, input) => {
277
+ await originalBeforeTransactionalWrite?.(em, input);
278
+ if (prepared) return;
279
+ Object.assign(
280
+ snapshots,
281
+ await prepare(coreOptions.input, { ...transactionalCtx, transactionalEm: em }) ?? {}
282
+ );
283
+ prepared = true;
284
+ }
285
+ };
286
+ executionOptions = { ...coreOptions, ctx: transactionalCtx };
287
+ } else {
288
+ Object.assign(snapshots, await this.prepareSnapshots(handler, coreOptions));
289
+ }
290
+ effectiveOptions = executionOptions;
291
+ const sourceLog = executionOptions.redoLogEntry ?? null;
292
+ const result2 = sourceLog && typeof handler.redo === "function" ? await handler.redo({ input: executionOptions.input, ctx: executionOptions.ctx, logEntry: sourceLog }) : await handler.execute(executionOptions.input, executionOptions.ctx);
293
+ const afterSnapshot = await this.captureAfter(handler, executionOptions, result2);
268
294
  const snapshotsWithAfter = { ...snapshots, after: afterSnapshot };
269
- const logMeta = await this.buildLog(handler, coreOptions, result2, snapshotsWithAfter);
270
- let mergedMeta2 = this.mergeMetadata(coreOptions.metadata, logMeta);
295
+ const logMeta = await this.buildLog(handler, executionOptions, result2, snapshotsWithAfter);
296
+ let mergedMeta2 = this.mergeMetadata(executionOptions.metadata, logMeta);
271
297
  let interceptorContextMerged = {};
272
298
  for (const meta of interceptorMetadata.values()) {
273
299
  const logContextRecord = asRecord(asRecord(meta)?.logContext);
@@ -277,7 +303,7 @@ class CommandBus {
277
303
  ...logContextRecord
278
304
  };
279
305
  }
280
- const baseContext = asRecord(coreOptions.metadata?.context) ?? {};
306
+ const baseContext = asRecord(executionOptions.metadata?.context) ?? {};
281
307
  const logMetaContext = asRecord(logMeta?.context) ?? {};
282
308
  if (Object.keys(interceptorContextMerged).length > 0 || Object.keys(baseContext).length > 0 || Object.keys(logMetaContext).length > 0) {
283
309
  mergedMeta2 = mergedMeta2 ?? {};
@@ -291,7 +317,7 @@ class CommandBus {
291
317
  if (undoable && mergedMeta2?.replayable !== false) {
292
318
  mergedMeta2 = mergedMeta2 ?? {};
293
319
  if (!mergedMeta2.undoToken) mergedMeta2.undoToken = defaultUndoToken();
294
- if (mergedMeta2.actorUserId === void 0) mergedMeta2.actorUserId = coreOptions.ctx.auth?.sub ?? null;
320
+ if (mergedMeta2.actorUserId === void 0) mergedMeta2.actorUserId = executionOptions.ctx.auth?.sub ?? null;
295
321
  } else if (mergedMeta2?.replayable === false) {
296
322
  mergedMeta2.undoToken = null;
297
323
  }
@@ -317,7 +343,7 @@ class CommandBus {
317
343
  if (inferred) mergedMeta2.changes = inferred;
318
344
  }
319
345
  }
320
- const logEntry2 = await this.persistLog(commandId, coreOptions, mergedMeta2);
346
+ const logEntry2 = await this.persistLog(commandId, executionOptions, mergedMeta2);
321
347
  return { result: result2, mergedMeta: mergedMeta2, logEntry: logEntry2 };
322
348
  };
323
349
  let replaySourceFinalized = false;
@@ -383,7 +409,9 @@ class CommandBus {
383
409
  commandId,
384
410
  auth: effectiveOptions.ctx.auth ?? null,
385
411
  selectedOrganizationId: effectiveOptions.ctx.selectedOrganizationId ?? effectiveOptions.ctx.auth?.orgId ?? null,
386
- container: effectiveOptions.ctx.container
412
+ container: effectiveOptions.ctx.container,
413
+ organizationScope: effectiveOptions.ctx.organizationScope ?? null,
414
+ request: effectiveOptions.ctx.request ?? null
387
415
  };
388
416
  const afterResult = await runCommandInterceptorsAfter(
389
417
  allInterceptors,
@@ -448,7 +476,9 @@ class CommandBus {
448
476
  commandId: log.commandId,
449
477
  auth: replayCtx.auth ?? null,
450
478
  selectedOrganizationId: replayCtx.selectedOrganizationId ?? replayCtx.auth?.orgId ?? null,
451
- container: replayCtx.container
479
+ container: replayCtx.container,
480
+ organizationScope: replayCtx.organizationScope ?? null,
481
+ request: replayCtx.request ?? null
452
482
  };
453
483
  const beforeResult = await runCommandInterceptorsBeforeUndo(
454
484
  allInterceptors,
@@ -552,7 +582,9 @@ class CommandBus {
552
582
  commandId: log.commandId,
553
583
  auth: ctx.auth ?? null,
554
584
  selectedOrganizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
555
- container: ctx.container
585
+ container: ctx.container,
586
+ organizationScope: ctx.organizationScope ?? null,
587
+ request: ctx.request ?? null
556
588
  };
557
589
  await runCommandInterceptorsAfterUndo(
558
590
  allInterceptors,