@open-mercato/shared 0.6.7-develop.6828.1.ab1620a63e → 0.6.7-develop.6834.1.e76f4b8cbc

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 254 entry points
1
+ [build:shared] found 256 entry points
2
2
  [build:shared] built successfully
package/AGENTS.md CHANGED
@@ -44,7 +44,7 @@ yarn workspace @open-mercato/shared build
44
44
  | `custom-fields/` | When handling custom field payloads | `@open-mercato/shared/lib/custom-fields` |
45
45
  | `data/` | When you need `DataEngine` or `QueryEngine` types | `@open-mercato/shared/lib/data/engine` |
46
46
  | `db/` | When resolving the ORM/connection-pool config (`resolvePoolConfig`, pool/timeout env knobs) | `@open-mercato/shared/lib/db/mikro` |
47
- | `di/` | When setting up dependency injection (Awilix) | `@open-mercato/shared/lib/di` |
47
+ | `di/` | When setting up dependency injection (Awilix). The app-level hook (`src/di.ts` → `register`) is wired explicitly in BOTH bootstrap paths — `src/bootstrap.ts` for the Next.js runtime, `bootstrapFromAppRoot()` for worker/scheduler/CLI processes. Never rely on the legacy `import('@/di')` fallback: the alias does not exist outside the app bundler | `@open-mercato/shared/lib/di` |
48
48
  | `encryption/` | When querying encrypted entities (MUST use instead of raw `em.find`) | `@open-mercato/shared/lib/encryption/find` |
49
49
  | `i18n/` | When translating strings — `useT()` client-side, `resolveTranslations()` server-side | `@open-mercato/shared/lib/i18n/context` or `/server` |
50
50
  | `indexers/` | When building query index helpers | `@open-mercato/shared/lib/indexers` |
@@ -24,14 +24,19 @@ function createCliBundlePlugins(appRoot) {
24
24
  name: "alias-resolver",
25
25
  setup(build) {
26
26
  build.onResolve({ filter: /^@\// }, (args) => {
27
- const resolved = path.join(appRoot, args.path.slice(2));
28
- if (!fs.existsSync(resolved) && fs.existsSync(resolved + ".ts")) {
29
- return { path: resolved + ".ts" };
27
+ const rest = args.path.slice("@/".length);
28
+ const bases = rest.startsWith(".mercato/") ? [path.join(appRoot, rest)] : [path.join(appRoot, "src", rest), path.join(appRoot, rest)];
29
+ for (const base of bases) {
30
+ if (fs.existsSync(base) && fs.statSync(base).isFile()) {
31
+ return { path: base };
32
+ }
33
+ for (const suffix of [".ts", ".tsx", "/index.ts", "/index.tsx"]) {
34
+ if (fs.existsSync(base + suffix)) {
35
+ return { path: base + suffix };
36
+ }
37
+ }
30
38
  }
31
- if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() && fs.existsSync(path.join(resolved, "index.ts"))) {
32
- return { path: path.join(resolved, "index.ts") };
33
- }
34
- return { path: resolved };
39
+ return { path: path.join(appRoot, rest) };
35
40
  });
36
41
  }
37
42
  };
@@ -197,9 +202,10 @@ function cacheIsValid(appRoot, jsPath, metadataPath, expectedInputHash) {
197
202
  if (!metadata || metadata.inputHash !== expectedInputHash) return false;
198
203
  return contentHash(fs.readFileSync(jsPath)) === metadata.outputHash && dependenciesAreValid(appRoot, metadata.dependencies);
199
204
  }
200
- async function compileAndImport(tsPath, allowRecovery = true) {
201
- const jsPath = tsPath.replace(/\.ts$/, ".mjs");
202
- const appRoot = path.dirname(path.dirname(path.dirname(tsPath)));
205
+ async function compileAndImport(tsPath, options = {}) {
206
+ const allowRecovery = options.allowRecovery ?? true;
207
+ const jsPath = options.outFile ?? tsPath.replace(/\.ts$/, ".mjs");
208
+ const appRoot = options.appRoot ?? path.dirname(path.dirname(path.dirname(tsPath)));
203
209
  const appTsconfig = path.join(appRoot, "tsconfig.json");
204
210
  const metadataPath = cacheMetadataPath(jsPath);
205
211
  const tsExists = fs.existsSync(tsPath);
@@ -214,6 +220,7 @@ async function compileAndImport(tsPath, allowRecovery = true) {
214
220
  const expectedInputHash = cacheInputHash(tsPath, appRoot, tsconfigPaths);
215
221
  const needsCompile = !cacheIsValid(appRoot, jsPath, metadataPath, expectedInputHash);
216
222
  if (needsCompile) {
223
+ fs.mkdirSync(path.dirname(jsPath), { recursive: true });
217
224
  const esbuild = await import("esbuild");
218
225
  const result = await esbuild.build({
219
226
  entryPoints: [tsPath],
@@ -252,7 +259,7 @@ async function compileAndImport(tsPath, allowRecovery = true) {
252
259
  if (!recovered.applied) {
253
260
  throw error;
254
261
  }
255
- return compileAndImport(tsPath, false);
262
+ return compileAndImport(tsPath, { ...options, allowRecovery: false });
256
263
  }
257
264
  }
258
265
  async function loadOptionalGeneratedModule(tsPath, fallback) {
@@ -273,7 +280,7 @@ async function loadOptionalGeneratedModule(tsPath, fallback) {
273
280
  return fallback;
274
281
  }
275
282
  }
276
- async function loadBootstrapData(appRoot) {
283
+ function resolveAppRootOrThrow(appRoot) {
277
284
  const resolved = appRoot ? {
278
285
  generatedDir: path.join(appRoot, ".mercato", "generated"),
279
286
  appDir: appRoot,
@@ -284,6 +291,37 @@ async function loadBootstrapData(appRoot) {
284
291
  'Could not find app root with .mercato/generated directory. Make sure you run this command from within a Next.js app directory, or run "yarn mercato generate" first to create the generated files.'
285
292
  );
286
293
  }
294
+ return resolved;
295
+ }
296
+ async function loadAppDiRegistrar(appDir) {
297
+ const tsPath = path.join(appDir, "src", "di.ts");
298
+ if (!fs.existsSync(tsPath)) {
299
+ logger.debug("App-level DI module not present, skipping its registrations", { filePath: tsPath });
300
+ return null;
301
+ }
302
+ try {
303
+ const appDiModule = await compileAndImport(tsPath, {
304
+ appRoot: appDir,
305
+ outFile: path.join(appDir, ".mercato", "generated", "app-di.compiled.mjs")
306
+ });
307
+ const register = appDiModule.register;
308
+ if (typeof register !== "function") {
309
+ logger.error("App-level DI module exports no register(); its registrations are skipped", {
310
+ filePath: tsPath
311
+ });
312
+ return null;
313
+ }
314
+ return register;
315
+ } catch (error) {
316
+ logger.error("Failed to load the app-level DI module; its registrations are skipped", {
317
+ filePath: tsPath,
318
+ err: error
319
+ });
320
+ return null;
321
+ }
322
+ }
323
+ async function loadBootstrapData(appRoot) {
324
+ const resolved = resolveAppRootOrThrow(appRoot);
287
325
  const { generatedDir } = resolved;
288
326
  ensureMikroOrmV7GeneratedCacheCompatibility(resolved.appDir);
289
327
  const entityIdsModule = await compileAndImport(path.join(generatedDir, "entities.ids.generated.ts"));
@@ -332,8 +370,10 @@ async function loadBootstrapData(appRoot) {
332
370
  }
333
371
  async function bootstrapFromAppRoot(appRoot) {
334
372
  const { createBootstrap, waitForAsyncRegistration } = await import("./factory.js");
335
- const data = await loadBootstrapData(appRoot);
336
- const bootstrap = createBootstrap(data);
373
+ const resolved = resolveAppRootOrThrow(appRoot);
374
+ const data = await loadBootstrapData(resolved.appDir);
375
+ const appDiRegistrar = await loadAppDiRegistrar(resolved.appDir);
376
+ const bootstrap = createBootstrap(data, appDiRegistrar ? { appDiRegistrar } : {});
337
377
  bootstrap();
338
378
  await waitForAsyncRegistration();
339
379
  return data;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/bootstrap/dynamicLoader.ts"],
4
- "sourcesContent": ["import type { BootstrapData } from './types'\nimport { findAppRoot, type AppRoot } from './appResolver'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport { createLogger } from '../logger'\nimport {\n ensureMikroOrmV7GeneratedCacheCompatibility,\n recoverMikroOrmV7GeneratedCacheFromImportError,\n} from './generatedCacheRecovery'\nimport { CLIENT_ONLY_STUB_NAMESPACE, createClientOnlyStubPlugin } from './clientOnlyModules'\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport crypto from 'node:crypto'\nimport { createRequire } from 'node:module'\nimport { pathToFileURL } from 'node:url'\n\nconst logger = createLogger('shared').child({ component: 'bootstrap' })\n\n/**\n * Thrown when an expected generated source file is absent.\n *\n * Optional registries treat this as the supported compatibility case (an app\n * that never generated the file), which is what makes it distinguishable from\n * a file that exists but fails to compile or import.\n */\nclass GeneratedFileNotFoundError extends Error {\n readonly filePath: string\n\n constructor(filePath: string) {\n super(`Generated file not found: ${filePath}`)\n this.name = 'GeneratedFileNotFoundError'\n this.filePath = filePath\n }\n}\n\n/**\n * esbuild plugins for the CLI bundle, in resolution order. The client-only stub must come\n * first so it wins over the alias and external plugins for `*.client` dynamic imports.\n *\n * Exported so the wiring itself is testable: a test that only exercises\n * `createClientOnlyStubPlugin` in isolation stays green if the plugin is dropped from this\n * list, which would silently reintroduce #4623.\n */\nexport function createCliBundlePlugins(appRoot: string): import('esbuild').Plugin[] {\n // Plugin to resolve @/ alias to app root (works for @app modules)\n const aliasPlugin: import('esbuild').Plugin = {\n name: 'alias-resolver',\n setup(build) {\n // Resolve @/ alias to app root\n build.onResolve({ filter: /^@\\// }, (args) => {\n const resolved = path.join(appRoot, args.path.slice(2))\n // Try with .ts extension if base path doesn't exist\n if (!fs.existsSync(resolved) && fs.existsSync(resolved + '.ts')) {\n return { path: resolved + '.ts' }\n }\n // Also check for /index.ts if it's a directory\n if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() && fs.existsSync(path.join(resolved, 'index.ts'))) {\n return { path: path.join(resolved, 'index.ts') }\n }\n return { path: resolved }\n })\n },\n }\n\n // Plugin to mark non-JSON package imports as external\n const externalNonJsonPlugin: import('esbuild').Plugin = {\n name: 'external-non-json',\n setup(build) {\n // Mark all package imports as external EXCEPT JSON files\n // Filter matches paths that don't start with . or / (package imports like @open-mercato/shared)\n build.onResolve({ filter: /^[^./]/ }, (args) => {\n // Skip Windows absolute paths (e.g., C:\\...) - they're local files, not packages\n if (/^[a-zA-Z]:/.test(args.path)) {\n return null // Let esbuild handle it\n }\n // If it's a JSON file, let esbuild bundle it\n if (args.path.endsWith('.json')) {\n return null // Let esbuild handle it\n }\n // Otherwise mark as external\n return { path: args.path, external: true }\n })\n },\n }\n\n return [createClientOnlyStubPlugin(), aliasPlugin, externalNonJsonPlugin]\n}\n\nconst DYNAMIC_LOADER_CACHE_VERSION = 4\n\ntype DynamicLoaderCacheMetadata = {\n version: number\n inputHash: string\n outputHash: string\n dependencies: Record<string, string>\n}\n\nfunction cacheMetadataPath(jsPath: string): string {\n return `${jsPath}.cache.json`\n}\n\nfunction contentHash(content: Buffer | string): string {\n return crypto.createHash('sha256').update(content).digest('hex')\n}\n\nfunction parseJsonConfig(content: string): unknown {\n let normalized = ''\n let inString = false\n let escaped = false\n\n for (let index = 0; index < content.length; index += 1) {\n const character = content[index]\n const nextCharacter = content[index + 1]\n\n if (inString) {\n normalized += character\n if (escaped) {\n escaped = false\n } else if (character === '\\\\') {\n escaped = true\n } else if (character === '\"') {\n inString = false\n }\n continue\n }\n\n if (character === '\"') {\n inString = true\n normalized += character\n continue\n }\n\n if (character === '/' && nextCharacter === '/') {\n while (index < content.length && content[index] !== '\\n') index += 1\n normalized += '\\n'\n continue\n }\n\n if (character === '/' && nextCharacter === '*') {\n index += 2\n while (index < content.length && !(content[index] === '*' && content[index + 1] === '/')) {\n index += 1\n }\n index += 1\n continue\n }\n\n if (character === ',') {\n let lookahead = index + 1\n while (lookahead < content.length && /\\s/.test(content[lookahead])) lookahead += 1\n if (content[lookahead] === '}' || content[lookahead] === ']') continue\n }\n\n normalized += character\n }\n\n return JSON.parse(normalized)\n}\n\nfunction resolveExistingConfigPath(candidate: string): string | null {\n for (const configPath of [candidate, `${candidate}.json`, path.join(candidate, 'tsconfig.json')]) {\n if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) return configPath\n }\n return null\n}\n\nfunction resolvePackageConfig(configPath: string, reference: string): string | null {\n try {\n const resolved = createRequire(pathToFileURL(configPath)).resolve(reference)\n return path.extname(resolved) === '.json' ? resolved : null\n } catch {\n return null\n }\n}\n\nfunction resolveExtendedConfig(configPath: string, reference: string): string {\n if (path.isAbsolute(reference) || reference.startsWith('.')) {\n const resolved = resolveExistingConfigPath(path.resolve(path.dirname(configPath), reference))\n if (resolved) return resolved\n } else {\n for (const packageReference of [reference, `${reference}/tsconfig.json`]) {\n const resolved = resolvePackageConfig(configPath, packageReference)\n if (resolved) return resolved\n }\n }\n\n throw new Error(`[internal] TypeScript config extends target not found: ${reference}`)\n}\n\nfunction collectTsconfigPaths(entryPath: string, visited: Set<string> = new Set()): string[] {\n const configPath = path.resolve(entryPath)\n if (visited.has(configPath)) return []\n visited.add(configPath)\n\n const parsed = parseJsonConfig(fs.readFileSync(configPath, 'utf8'))\n if (typeof parsed !== 'object' || parsed === null || !('extends' in parsed)) return [configPath]\n\n const extendsValue = parsed.extends\n const references = typeof extendsValue === 'string'\n ? [extendsValue]\n : Array.isArray(extendsValue) && extendsValue.every((value) => typeof value === 'string')\n ? extendsValue\n : []\n\n return [\n ...references.flatMap((reference) => collectTsconfigPaths(\n resolveExtendedConfig(configPath, reference),\n visited,\n )),\n configPath,\n ]\n}\n\nfunction hashFilesRelativeTo(appRoot: string, filePaths: string[]): Record<string, string> {\n return Object.fromEntries(filePaths.map((filePath) => [\n path.relative(appRoot, filePath).split(path.sep).join('/'),\n contentHash(fs.readFileSync(filePath)),\n ]))\n}\n\nfunction cacheInputHash(tsPath: string, appRoot: string, tsconfigPaths: string[]): string {\n const hash = crypto.createHash('sha256')\n hash.update(JSON.stringify({\n version: DYNAMIC_LOADER_CACHE_VERSION,\n sourceHash: contentHash(fs.readFileSync(tsPath)),\n tsconfigHashes: hashFilesRelativeTo(appRoot, tsconfigPaths),\n }))\n return hash.digest('hex')\n}\n\nfunction dependenciesAreValid(appRoot: string, dependencies: Record<string, string>): boolean {\n return Object.entries(dependencies).every(([relativePath, expectedHash]) => {\n const dependencyPath = path.resolve(appRoot, relativePath)\n return fs.existsSync(dependencyPath)\n && contentHash(fs.readFileSync(dependencyPath)) === expectedHash\n })\n}\n\nfunction collectDependencyHashes(\n appRoot: string,\n inputs: Record<string, unknown>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.keys(inputs)\n .filter((inputPath) => !inputPath.startsWith(`${CLIENT_ONLY_STUB_NAMESPACE}:`))\n .map((inputPath) => {\n const absolutePath = path.isAbsolute(inputPath)\n ? inputPath\n : path.resolve(appRoot, inputPath)\n const relativePath = path.relative(appRoot, absolutePath).split(path.sep).join('/')\n return [relativePath, contentHash(fs.readFileSync(absolutePath))]\n })\n .sort(([left], [right]) => left.localeCompare(right)),\n )\n}\n\nfunction readCacheMetadata(metadataPath: string): DynamicLoaderCacheMetadata | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(metadataPath, 'utf8'))\n if (\n typeof parsed === 'object'\n && parsed !== null\n && 'version' in parsed\n && parsed.version === DYNAMIC_LOADER_CACHE_VERSION\n && 'inputHash' in parsed\n && typeof parsed.inputHash === 'string'\n && 'outputHash' in parsed\n && typeof parsed.outputHash === 'string'\n && 'dependencies' in parsed\n && typeof parsed.dependencies === 'object'\n && parsed.dependencies !== null\n && Object.values(parsed.dependencies).every((hash) => typeof hash === 'string')\n ) {\n return {\n version: parsed.version,\n inputHash: parsed.inputHash,\n outputHash: parsed.outputHash,\n dependencies: parsed.dependencies as Record<string, string>,\n }\n }\n } catch {\n return null\n }\n return null\n}\n\nfunction cacheIsValid(\n appRoot: string,\n jsPath: string,\n metadataPath: string,\n expectedInputHash: string,\n): boolean {\n if (!fs.existsSync(jsPath)) return false\n const metadata = readCacheMetadata(metadataPath)\n if (!metadata || metadata.inputHash !== expectedInputHash) return false\n return contentHash(fs.readFileSync(jsPath)) === metadata.outputHash\n && dependenciesAreValid(appRoot, metadata.dependencies)\n}\n\n/**\n * Compile a TypeScript file to JavaScript using esbuild bundler.\n * This bundles the file and all its dependencies, handling JSON imports properly.\n * The compiled file is written next to the source file with a .mjs extension.\n */\nasync function compileAndImport(tsPath: string, allowRecovery: boolean = true): Promise<Record<string, unknown>> {\n const jsPath = tsPath.replace(/\\.ts$/, '.mjs')\n const appRoot = path.dirname(path.dirname(path.dirname(tsPath)))\n const appTsconfig = path.join(appRoot, 'tsconfig.json')\n const metadataPath = cacheMetadataPath(jsPath)\n\n const tsExists = fs.existsSync(tsPath)\n const tsconfigExists = fs.existsSync(appTsconfig)\n\n if (!tsExists) {\n throw new GeneratedFileNotFoundError(tsPath)\n }\n if (!tsconfigExists) {\n throw new Error(`App TypeScript config not found: ${appTsconfig}`)\n }\n\n const tsconfigPaths = collectTsconfigPaths(appTsconfig)\n const expectedInputHash = cacheInputHash(tsPath, appRoot, tsconfigPaths)\n const needsCompile = !cacheIsValid(appRoot, jsPath, metadataPath, expectedInputHash)\n\n if (needsCompile) {\n // Dynamically import esbuild only when needed\n const esbuild = await import('esbuild')\n\n // Use esbuild.build with bundling to handle JSON imports\n const result = await esbuild.build({\n entryPoints: [tsPath],\n outfile: jsPath,\n absWorkingDir: appRoot,\n bundle: true,\n metafile: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n tsconfig: appTsconfig,\n plugins: createCliBundlePlugins(appRoot),\n // Allow JSON imports\n loader: { '.json': 'json' },\n })\n const metadata: DynamicLoaderCacheMetadata = {\n version: DYNAMIC_LOADER_CACHE_VERSION,\n inputHash: expectedInputHash,\n outputHash: contentHash(fs.readFileSync(jsPath)),\n dependencies: {\n ...collectDependencyHashes(appRoot, result.metafile.inputs),\n ...hashFilesRelativeTo(appRoot, tsconfigPaths),\n },\n }\n fs.writeFileSync(metadataPath, JSON.stringify(metadata))\n }\n\n // Import the compiled JavaScript\n try {\n const outputHash = contentHash(fs.readFileSync(jsPath))\n const fileUrl = `${pathToFileURL(jsPath).href}?cache=${outputHash}`\n return await import(fileUrl)\n } catch (error) {\n if (!allowRecovery) {\n throw error\n }\n\n const recovered = recoverMikroOrmV7GeneratedCacheFromImportError(appRoot, error)\n if (!recovered.applied) {\n throw error\n }\n\n return compileAndImport(tsPath, false)\n }\n}\n\n\n/**\n * Load a generated registry that older apps may not have generated yet.\n *\n * An absent source file is the supported compatibility case and resolves to\n * `fallback` quietly. Any other failure \u2014 a compile error, a broken import, a\n * runtime throw at module scope \u2014 still resolves to `fallback` so bootstrap\n * keeps working, but is reported at error level: a registry that silently\n * degrades to nothing is exactly how command interceptors stopped applying in\n * worker/CLI processes (#4327, #4491).\n */\nasync function loadOptionalGeneratedModule(\n tsPath: string,\n fallback: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n try {\n return await compileAndImport(tsPath)\n } catch (error) {\n if (error instanceof GeneratedFileNotFoundError) {\n logger.debug('Optional generated registry not present, using empty fallback', {\n file: path.basename(tsPath),\n })\n return fallback\n }\n\n logger.error('Failed to load generated registry, continuing without its entries', {\n file: path.basename(tsPath),\n filePath: tsPath,\n err: error,\n })\n return fallback\n }\n}\n\n/**\n * Dynamically load bootstrap data from a resolved app directory.\n *\n * IMPORTANT: This only works in unbundled contexts (CLI, tsx).\n * Do NOT use this in Next.js bundled code - use static imports instead.\n *\n * For CLI context, we skip loading modules.generated.ts which has Next.js dependencies.\n * CLI commands are discovered separately via the CLI module system.\n *\n * @param appRoot - Optional explicit app root path. If not provided, will search from cwd.\n * @returns The loaded bootstrap data\n * @throws Error if app root cannot be found or generated files are missing\n */\nexport async function loadBootstrapData(appRoot?: string): Promise<BootstrapData> {\n const resolved: AppRoot | null = appRoot\n ? {\n generatedDir: path.join(appRoot, '.mercato', 'generated'),\n appDir: appRoot,\n mercatoDir: path.join(appRoot, '.mercato'),\n }\n : findAppRoot()\n\n if (!resolved) {\n throw new Error(\n 'Could not find app root with .mercato/generated directory. ' +\n 'Make sure you run this command from within a Next.js app directory, ' +\n 'or run \"yarn mercato generate\" first to create the generated files.',\n )\n }\n\n const { generatedDir } = resolved\n\n ensureMikroOrmV7GeneratedCacheCompatibility(resolved.appDir)\n\n // IMPORTANT: Load entity IDs FIRST and register them before loading modules.\n // This is because modules (e.g., ce.ts files) use E.xxx.xxx at module scope,\n // and they need entity IDs to be available when they're imported.\n const entityIdsModule = await compileAndImport(path.join(generatedDir, 'entities.ids.generated.ts'))\n registerEntityIds(entityIdsModule.E as BootstrapData['entityIds'])\n\n // Now load the rest of the generated files.\n // modules.cli.generated.ts excludes Next.js-dependent code (routes, APIs, widgets)\n const [\n modulesModule,\n entitiesModule,\n diModule,\n searchModule,\n commandLoadersModule,\n commandInterceptorsModule,\n workflowsModule,\n ] = await Promise.all([\n compileAndImport(path.join(generatedDir, 'modules.cli.generated.ts')),\n compileAndImport(path.join(generatedDir, 'entities.generated.ts')),\n compileAndImport(path.join(generatedDir, 'di.generated.ts')),\n loadOptionalGeneratedModule(path.join(generatedDir, 'search.generated.ts'), { searchModuleConfigs: [] }),\n loadOptionalGeneratedModule(path.join(generatedDir, 'command-loaders.generated.ts'), { commandLoaderEntries: [] }),\n loadOptionalGeneratedModule(path.join(generatedDir, 'command-interceptors.generated.ts'), {\n commandInterceptorEntries: [],\n }),\n loadOptionalGeneratedModule(path.join(generatedDir, 'workflows.generated.ts'), { allCodeWorkflows: [] }),\n ])\n\n return {\n modules: modulesModule.modules as BootstrapData['modules'],\n entities: entitiesModule.entities as BootstrapData['entities'],\n diRegistrars: diModule.diRegistrars as BootstrapData['diRegistrars'],\n entityIds: entityIdsModule.E as BootstrapData['entityIds'],\n // Search configs are needed by workers for indexing\n searchModuleConfigs: (searchModule.searchModuleConfigs ?? []) as BootstrapData['searchModuleConfigs'],\n commandLoaderEntries: (commandLoadersModule.commandLoaderEntries ?? []) as BootstrapData['commandLoaderEntries'],\n // Command interceptors must apply in worker/CLI processes too \u2014 the\n // interceptor registry is per-process, so relying on the Next.js runtime's\n // registration silently no-ops every interceptor for queued/CLI commands\n // (#4327).\n commandInterceptorEntries: (commandInterceptorsModule.commandInterceptorEntries ??\n []) as BootstrapData['commandInterceptorEntries'],\n // Code workflow definitions are needed by workers to resume code-defined instances\n codeWorkflows: (workflowsModule.allCodeWorkflows ?? []) as BootstrapData['codeWorkflows'],\n // Empty UI-related data - not needed for CLI\n dashboardWidgetEntries: [],\n injectionWidgetEntries: [],\n injectionTables: [],\n interceptorEntries: [],\n componentOverrideEntries: [],\n }\n}\n\n/**\n * Create and execute bootstrap in CLI context.\n *\n * This is a convenience function that finds the app root, loads the generated\n * data dynamically, and runs bootstrap. Use this in CLI entry points.\n *\n * Returns the loaded bootstrap data so the CLI can register modules directly\n * (avoids module resolution issues when importing @open-mercato/cli/mercato).\n *\n * @param appRoot - Optional explicit app root path\n * @returns The loaded bootstrap data (modules, entities, etc.)\n */\nexport async function bootstrapFromAppRoot(appRoot?: string): Promise<BootstrapData> {\n const { createBootstrap, waitForAsyncRegistration } = await import('./factory.js')\n const data = await loadBootstrapData(appRoot)\n const bootstrap = createBootstrap(data)\n bootstrap()\n // In CLI context, wait for async registrations (UI widgets, search configs, etc.)\n await waitForAsyncRegistration()\n\n return data\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { BootstrapData } from './types'\nimport type { AppDiRegistrar } from '../di/container'\nimport { findAppRoot, type AppRoot } from './appResolver'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport { createLogger } from '../logger'\nimport {\n ensureMikroOrmV7GeneratedCacheCompatibility,\n recoverMikroOrmV7GeneratedCacheFromImportError,\n} from './generatedCacheRecovery'\nimport { CLIENT_ONLY_STUB_NAMESPACE, createClientOnlyStubPlugin } from './clientOnlyModules'\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport crypto from 'node:crypto'\nimport { createRequire } from 'node:module'\nimport { pathToFileURL } from 'node:url'\n\nconst logger = createLogger('shared').child({ component: 'bootstrap' })\n\n/**\n * Thrown when an expected generated source file is absent.\n *\n * Optional registries treat this as the supported compatibility case (an app\n * that never generated the file), which is what makes it distinguishable from\n * a file that exists but fails to compile or import.\n */\nclass GeneratedFileNotFoundError extends Error {\n readonly filePath: string\n\n constructor(filePath: string) {\n super(`Generated file not found: ${filePath}`)\n this.name = 'GeneratedFileNotFoundError'\n this.filePath = filePath\n }\n}\n\n/**\n * esbuild plugins for the CLI bundle, in resolution order. The client-only stub must come\n * first so it wins over the alias and external plugins for `*.client` dynamic imports.\n *\n * Exported so the wiring itself is testable: a test that only exercises\n * `createClientOnlyStubPlugin` in isolation stays green if the plugin is dropped from this\n * list, which would silently reintroduce #4623.\n */\nexport function createCliBundlePlugins(appRoot: string): import('esbuild').Plugin[] {\n // Plugin to resolve the @/ alias the way the app tsconfig maps it:\n // `@/.mercato/*` to the app root, every other `@/*` to the app's src/ directory.\n const aliasPlugin: import('esbuild').Plugin = {\n name: 'alias-resolver',\n setup(build) {\n build.onResolve({ filter: /^@\\// }, (args) => {\n const rest = args.path.slice('@/'.length)\n const bases = rest.startsWith('.mercato/')\n ? [path.join(appRoot, rest)]\n : [path.join(appRoot, 'src', rest), path.join(appRoot, rest)]\n for (const base of bases) {\n if (fs.existsSync(base) && fs.statSync(base).isFile()) {\n return { path: base }\n }\n for (const suffix of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + suffix)) {\n return { path: base + suffix }\n }\n }\n }\n // Nothing matched \u2014 hand esbuild the literal mapping so it reports the\n // missing file against the path the app author actually wrote.\n return { path: path.join(appRoot, rest) }\n })\n },\n }\n\n // Plugin to mark non-JSON package imports as external\n const externalNonJsonPlugin: import('esbuild').Plugin = {\n name: 'external-non-json',\n setup(build) {\n // Mark all package imports as external EXCEPT JSON files\n // Filter matches paths that don't start with . or / (package imports like @open-mercato/shared)\n build.onResolve({ filter: /^[^./]/ }, (args) => {\n // Skip Windows absolute paths (e.g., C:\\...) - they're local files, not packages\n if (/^[a-zA-Z]:/.test(args.path)) {\n return null // Let esbuild handle it\n }\n // If it's a JSON file, let esbuild bundle it\n if (args.path.endsWith('.json')) {\n return null // Let esbuild handle it\n }\n // Otherwise mark as external\n return { path: args.path, external: true }\n })\n },\n }\n\n return [createClientOnlyStubPlugin(), aliasPlugin, externalNonJsonPlugin]\n}\n\nconst DYNAMIC_LOADER_CACHE_VERSION = 4\n\ntype DynamicLoaderCacheMetadata = {\n version: number\n inputHash: string\n outputHash: string\n dependencies: Record<string, string>\n}\n\nfunction cacheMetadataPath(jsPath: string): string {\n return `${jsPath}.cache.json`\n}\n\nfunction contentHash(content: Buffer | string): string {\n return crypto.createHash('sha256').update(content).digest('hex')\n}\n\nfunction parseJsonConfig(content: string): unknown {\n let normalized = ''\n let inString = false\n let escaped = false\n\n for (let index = 0; index < content.length; index += 1) {\n const character = content[index]\n const nextCharacter = content[index + 1]\n\n if (inString) {\n normalized += character\n if (escaped) {\n escaped = false\n } else if (character === '\\\\') {\n escaped = true\n } else if (character === '\"') {\n inString = false\n }\n continue\n }\n\n if (character === '\"') {\n inString = true\n normalized += character\n continue\n }\n\n if (character === '/' && nextCharacter === '/') {\n while (index < content.length && content[index] !== '\\n') index += 1\n normalized += '\\n'\n continue\n }\n\n if (character === '/' && nextCharacter === '*') {\n index += 2\n while (index < content.length && !(content[index] === '*' && content[index + 1] === '/')) {\n index += 1\n }\n index += 1\n continue\n }\n\n if (character === ',') {\n let lookahead = index + 1\n while (lookahead < content.length && /\\s/.test(content[lookahead])) lookahead += 1\n if (content[lookahead] === '}' || content[lookahead] === ']') continue\n }\n\n normalized += character\n }\n\n return JSON.parse(normalized)\n}\n\nfunction resolveExistingConfigPath(candidate: string): string | null {\n for (const configPath of [candidate, `${candidate}.json`, path.join(candidate, 'tsconfig.json')]) {\n if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) return configPath\n }\n return null\n}\n\nfunction resolvePackageConfig(configPath: string, reference: string): string | null {\n try {\n const resolved = createRequire(pathToFileURL(configPath)).resolve(reference)\n return path.extname(resolved) === '.json' ? resolved : null\n } catch {\n return null\n }\n}\n\nfunction resolveExtendedConfig(configPath: string, reference: string): string {\n if (path.isAbsolute(reference) || reference.startsWith('.')) {\n const resolved = resolveExistingConfigPath(path.resolve(path.dirname(configPath), reference))\n if (resolved) return resolved\n } else {\n for (const packageReference of [reference, `${reference}/tsconfig.json`]) {\n const resolved = resolvePackageConfig(configPath, packageReference)\n if (resolved) return resolved\n }\n }\n\n throw new Error(`[internal] TypeScript config extends target not found: ${reference}`)\n}\n\nfunction collectTsconfigPaths(entryPath: string, visited: Set<string> = new Set()): string[] {\n const configPath = path.resolve(entryPath)\n if (visited.has(configPath)) return []\n visited.add(configPath)\n\n const parsed = parseJsonConfig(fs.readFileSync(configPath, 'utf8'))\n if (typeof parsed !== 'object' || parsed === null || !('extends' in parsed)) return [configPath]\n\n const extendsValue = parsed.extends\n const references = typeof extendsValue === 'string'\n ? [extendsValue]\n : Array.isArray(extendsValue) && extendsValue.every((value) => typeof value === 'string')\n ? extendsValue\n : []\n\n return [\n ...references.flatMap((reference) => collectTsconfigPaths(\n resolveExtendedConfig(configPath, reference),\n visited,\n )),\n configPath,\n ]\n}\n\nfunction hashFilesRelativeTo(appRoot: string, filePaths: string[]): Record<string, string> {\n return Object.fromEntries(filePaths.map((filePath) => [\n path.relative(appRoot, filePath).split(path.sep).join('/'),\n contentHash(fs.readFileSync(filePath)),\n ]))\n}\n\nfunction cacheInputHash(tsPath: string, appRoot: string, tsconfigPaths: string[]): string {\n const hash = crypto.createHash('sha256')\n hash.update(JSON.stringify({\n version: DYNAMIC_LOADER_CACHE_VERSION,\n sourceHash: contentHash(fs.readFileSync(tsPath)),\n tsconfigHashes: hashFilesRelativeTo(appRoot, tsconfigPaths),\n }))\n return hash.digest('hex')\n}\n\nfunction dependenciesAreValid(appRoot: string, dependencies: Record<string, string>): boolean {\n return Object.entries(dependencies).every(([relativePath, expectedHash]) => {\n const dependencyPath = path.resolve(appRoot, relativePath)\n return fs.existsSync(dependencyPath)\n && contentHash(fs.readFileSync(dependencyPath)) === expectedHash\n })\n}\n\nfunction collectDependencyHashes(\n appRoot: string,\n inputs: Record<string, unknown>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.keys(inputs)\n .filter((inputPath) => !inputPath.startsWith(`${CLIENT_ONLY_STUB_NAMESPACE}:`))\n .map((inputPath) => {\n const absolutePath = path.isAbsolute(inputPath)\n ? inputPath\n : path.resolve(appRoot, inputPath)\n const relativePath = path.relative(appRoot, absolutePath).split(path.sep).join('/')\n return [relativePath, contentHash(fs.readFileSync(absolutePath))]\n })\n .sort(([left], [right]) => left.localeCompare(right)),\n )\n}\n\nfunction readCacheMetadata(metadataPath: string): DynamicLoaderCacheMetadata | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(metadataPath, 'utf8'))\n if (\n typeof parsed === 'object'\n && parsed !== null\n && 'version' in parsed\n && parsed.version === DYNAMIC_LOADER_CACHE_VERSION\n && 'inputHash' in parsed\n && typeof parsed.inputHash === 'string'\n && 'outputHash' in parsed\n && typeof parsed.outputHash === 'string'\n && 'dependencies' in parsed\n && typeof parsed.dependencies === 'object'\n && parsed.dependencies !== null\n && Object.values(parsed.dependencies).every((hash) => typeof hash === 'string')\n ) {\n return {\n version: parsed.version,\n inputHash: parsed.inputHash,\n outputHash: parsed.outputHash,\n dependencies: parsed.dependencies as Record<string, string>,\n }\n }\n } catch {\n return null\n }\n return null\n}\n\nfunction cacheIsValid(\n appRoot: string,\n jsPath: string,\n metadataPath: string,\n expectedInputHash: string,\n): boolean {\n if (!fs.existsSync(jsPath)) return false\n const metadata = readCacheMetadata(metadataPath)\n if (!metadata || metadata.inputHash !== expectedInputHash) return false\n return contentHash(fs.readFileSync(jsPath)) === metadata.outputHash\n && dependenciesAreValid(appRoot, metadata.dependencies)\n}\n\n/**\n * Options for `compileAndImport`.\n *\n * Both paths default to the generated-registry layout (`<appRoot>/.mercato/generated/<file>.ts`\n * compiled to a `.mjs` sibling). Sources that live elsewhere in the app \u2014 `src/di.ts` \u2014 MUST pass\n * both explicitly: the default app root is derived by walking three directories up from the source,\n * which only holds inside `.mercato/generated`.\n */\ntype CompileAndImportOptions = {\n appRoot?: string\n outFile?: string\n allowRecovery?: boolean\n}\n\n/**\n * Compile a TypeScript file to JavaScript using esbuild bundler.\n * This bundles the file and all its dependencies, handling JSON imports properly.\n * The compiled file is written next to the source file with a .mjs extension unless\n * `outFile` says otherwise.\n */\nasync function compileAndImport(\n tsPath: string,\n options: CompileAndImportOptions = {},\n): Promise<Record<string, unknown>> {\n const allowRecovery = options.allowRecovery ?? true\n const jsPath = options.outFile ?? tsPath.replace(/\\.ts$/, '.mjs')\n const appRoot = options.appRoot ?? path.dirname(path.dirname(path.dirname(tsPath)))\n const appTsconfig = path.join(appRoot, 'tsconfig.json')\n const metadataPath = cacheMetadataPath(jsPath)\n\n const tsExists = fs.existsSync(tsPath)\n const tsconfigExists = fs.existsSync(appTsconfig)\n\n if (!tsExists) {\n throw new GeneratedFileNotFoundError(tsPath)\n }\n if (!tsconfigExists) {\n throw new Error(`App TypeScript config not found: ${appTsconfig}`)\n }\n\n const tsconfigPaths = collectTsconfigPaths(appTsconfig)\n const expectedInputHash = cacheInputHash(tsPath, appRoot, tsconfigPaths)\n const needsCompile = !cacheIsValid(appRoot, jsPath, metadataPath, expectedInputHash)\n\n if (needsCompile) {\n fs.mkdirSync(path.dirname(jsPath), { recursive: true })\n // Dynamically import esbuild only when needed\n const esbuild = await import('esbuild')\n\n // Use esbuild.build with bundling to handle JSON imports\n const result = await esbuild.build({\n entryPoints: [tsPath],\n outfile: jsPath,\n absWorkingDir: appRoot,\n bundle: true,\n metafile: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n tsconfig: appTsconfig,\n plugins: createCliBundlePlugins(appRoot),\n // Allow JSON imports\n loader: { '.json': 'json' },\n })\n const metadata: DynamicLoaderCacheMetadata = {\n version: DYNAMIC_LOADER_CACHE_VERSION,\n inputHash: expectedInputHash,\n outputHash: contentHash(fs.readFileSync(jsPath)),\n dependencies: {\n ...collectDependencyHashes(appRoot, result.metafile.inputs),\n ...hashFilesRelativeTo(appRoot, tsconfigPaths),\n },\n }\n fs.writeFileSync(metadataPath, JSON.stringify(metadata))\n }\n\n // Import the compiled JavaScript\n try {\n const outputHash = contentHash(fs.readFileSync(jsPath))\n const fileUrl = `${pathToFileURL(jsPath).href}?cache=${outputHash}`\n return await import(fileUrl)\n } catch (error) {\n if (!allowRecovery) {\n throw error\n }\n\n const recovered = recoverMikroOrmV7GeneratedCacheFromImportError(appRoot, error)\n if (!recovered.applied) {\n throw error\n }\n\n return compileAndImport(tsPath, { ...options, allowRecovery: false })\n }\n}\n\n\n/**\n * Load a generated registry that older apps may not have generated yet.\n *\n * An absent source file is the supported compatibility case and resolves to\n * `fallback` quietly. Any other failure \u2014 a compile error, a broken import, a\n * runtime throw at module scope \u2014 still resolves to `fallback` so bootstrap\n * keeps working, but is reported at error level: a registry that silently\n * degrades to nothing is exactly how command interceptors stopped applying in\n * worker/CLI processes (#4327, #4491).\n */\nasync function loadOptionalGeneratedModule(\n tsPath: string,\n fallback: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n try {\n return await compileAndImport(tsPath)\n } catch (error) {\n if (error instanceof GeneratedFileNotFoundError) {\n logger.debug('Optional generated registry not present, using empty fallback', {\n file: path.basename(tsPath),\n })\n return fallback\n }\n\n logger.error('Failed to load generated registry, continuing without its entries', {\n file: path.basename(tsPath),\n filePath: tsPath,\n err: error,\n })\n return fallback\n }\n}\n\nfunction resolveAppRootOrThrow(appRoot?: string): AppRoot {\n const resolved: AppRoot | null = appRoot\n ? {\n generatedDir: path.join(appRoot, '.mercato', 'generated'),\n appDir: appRoot,\n mercatoDir: path.join(appRoot, '.mercato'),\n }\n : findAppRoot()\n\n if (!resolved) {\n throw new Error(\n 'Could not find app root with .mercato/generated directory. ' +\n 'Make sure you run this command from within a Next.js app directory, ' +\n 'or run \"yarn mercato generate\" first to create the generated files.',\n )\n }\n\n return resolved\n}\n\n/**\n * Load the app-level DI registrar (`src/di.ts`) for the dynamic bootstrap path.\n *\n * The Next.js runtime imports `@/di` statically from its own `src/bootstrap.ts` and hands the\n * registrar to `createBootstrap`. Worker, scheduler and CLI processes bootstrap through\n * `bootstrapFromAppRoot` instead, where the `@/` alias does not exist \u2014 so without this the app's\n * DI registrations silently never ran there, and every request container paid a failed\n * `import('@/di')` resolution (the compatibility fallback in `lib/di/container.ts`).\n *\n * An absent `src/di.ts` is the supported case and resolves to `null` quietly. A file that exists\n * but cannot be compiled, imported, or does not export `register` is reported at error level and\n * still resolves to `null`, so a broken app DI module degrades the same way a broken generated\n * registry does (#4327, #4491) instead of taking the whole process down.\n */\nasync function loadAppDiRegistrar(appDir: string): Promise<AppDiRegistrar | null> {\n const tsPath = path.join(appDir, 'src', 'di.ts')\n if (!fs.existsSync(tsPath)) {\n logger.debug('App-level DI module not present, skipping its registrations', { filePath: tsPath })\n return null\n }\n\n try {\n const appDiModule = await compileAndImport(tsPath, {\n appRoot: appDir,\n outFile: path.join(appDir, '.mercato', 'generated', 'app-di.compiled.mjs'),\n })\n const register = appDiModule.register\n if (typeof register !== 'function') {\n logger.error('App-level DI module exports no register(); its registrations are skipped', {\n filePath: tsPath,\n })\n return null\n }\n return register as AppDiRegistrar\n } catch (error) {\n logger.error('Failed to load the app-level DI module; its registrations are skipped', {\n filePath: tsPath,\n err: error,\n })\n return null\n }\n}\n\n/**\n * Dynamically load bootstrap data from a resolved app directory.\n *\n * IMPORTANT: This only works in unbundled contexts (CLI, tsx).\n * Do NOT use this in Next.js bundled code - use static imports instead.\n *\n * For CLI context, we skip loading modules.generated.ts which has Next.js dependencies.\n * CLI commands are discovered separately via the CLI module system.\n *\n * @param appRoot - Optional explicit app root path. If not provided, will search from cwd.\n * @returns The loaded bootstrap data\n * @throws Error if app root cannot be found or generated files are missing\n */\nexport async function loadBootstrapData(appRoot?: string): Promise<BootstrapData> {\n const resolved = resolveAppRootOrThrow(appRoot)\n\n const { generatedDir } = resolved\n\n ensureMikroOrmV7GeneratedCacheCompatibility(resolved.appDir)\n\n // IMPORTANT: Load entity IDs FIRST and register them before loading modules.\n // This is because modules (e.g., ce.ts files) use E.xxx.xxx at module scope,\n // and they need entity IDs to be available when they're imported.\n const entityIdsModule = await compileAndImport(path.join(generatedDir, 'entities.ids.generated.ts'))\n registerEntityIds(entityIdsModule.E as BootstrapData['entityIds'])\n\n // Now load the rest of the generated files.\n // modules.cli.generated.ts excludes Next.js-dependent code (routes, APIs, widgets)\n const [\n modulesModule,\n entitiesModule,\n diModule,\n searchModule,\n commandLoadersModule,\n commandInterceptorsModule,\n workflowsModule,\n ] = await Promise.all([\n compileAndImport(path.join(generatedDir, 'modules.cli.generated.ts')),\n compileAndImport(path.join(generatedDir, 'entities.generated.ts')),\n compileAndImport(path.join(generatedDir, 'di.generated.ts')),\n loadOptionalGeneratedModule(path.join(generatedDir, 'search.generated.ts'), { searchModuleConfigs: [] }),\n loadOptionalGeneratedModule(path.join(generatedDir, 'command-loaders.generated.ts'), { commandLoaderEntries: [] }),\n loadOptionalGeneratedModule(path.join(generatedDir, 'command-interceptors.generated.ts'), {\n commandInterceptorEntries: [],\n }),\n loadOptionalGeneratedModule(path.join(generatedDir, 'workflows.generated.ts'), { allCodeWorkflows: [] }),\n ])\n\n return {\n modules: modulesModule.modules as BootstrapData['modules'],\n entities: entitiesModule.entities as BootstrapData['entities'],\n diRegistrars: diModule.diRegistrars as BootstrapData['diRegistrars'],\n entityIds: entityIdsModule.E as BootstrapData['entityIds'],\n // Search configs are needed by workers for indexing\n searchModuleConfigs: (searchModule.searchModuleConfigs ?? []) as BootstrapData['searchModuleConfigs'],\n commandLoaderEntries: (commandLoadersModule.commandLoaderEntries ?? []) as BootstrapData['commandLoaderEntries'],\n // Command interceptors must apply in worker/CLI processes too \u2014 the\n // interceptor registry is per-process, so relying on the Next.js runtime's\n // registration silently no-ops every interceptor for queued/CLI commands\n // (#4327).\n commandInterceptorEntries: (commandInterceptorsModule.commandInterceptorEntries ??\n []) as BootstrapData['commandInterceptorEntries'],\n // Code workflow definitions are needed by workers to resume code-defined instances\n codeWorkflows: (workflowsModule.allCodeWorkflows ?? []) as BootstrapData['codeWorkflows'],\n // Empty UI-related data - not needed for CLI\n dashboardWidgetEntries: [],\n injectionWidgetEntries: [],\n injectionTables: [],\n interceptorEntries: [],\n componentOverrideEntries: [],\n }\n}\n\n/**\n * Create and execute bootstrap in CLI context.\n *\n * This is a convenience function that finds the app root, loads the generated\n * data dynamically, and runs bootstrap. Use this in CLI entry points.\n *\n * Returns the loaded bootstrap data so the CLI can register modules directly\n * (avoids module resolution issues when importing @open-mercato/cli/mercato).\n *\n * @param appRoot - Optional explicit app root path\n * @returns The loaded bootstrap data (modules, entities, etc.)\n */\nexport async function bootstrapFromAppRoot(appRoot?: string): Promise<BootstrapData> {\n const { createBootstrap, waitForAsyncRegistration } = await import('./factory.js')\n const resolved = resolveAppRootOrThrow(appRoot)\n const data = await loadBootstrapData(resolved.appDir)\n const appDiRegistrar = await loadAppDiRegistrar(resolved.appDir)\n const bootstrap = createBootstrap(data, appDiRegistrar ? { appDiRegistrar } : {})\n bootstrap()\n // In CLI context, wait for async registrations (UI widgets, search configs, etc.)\n await waitForAsyncRegistration()\n\n return data\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -14,6 +14,7 @@ const GLOBAL_KEY = "__openMercatoDiRegistrars__";
14
14
  const APP_DI_REGISTRAR_KEY = "__openMercatoAppDiRegistrar__";
15
15
  const APP_DI_LOAD_WARNING_KEY = "__openMercatoAppDiLoadWarningEmitted__";
16
16
  const APP_DI_REGISTER_WARNING_KEY = "__openMercatoAppDiRegisterWarningEmitted__";
17
+ const APP_DI_MODULE_UNRESOLVABLE_KEY = "__openMercatoAppDiModuleUnresolvable__";
17
18
  const BOOTSTRAP_CACHE_KEY = "__openMercatoBootstrapCache__";
18
19
  const ENCRYPTION_ENABLED_KEY = "__openMercatoEncryptionEnabledCache__";
19
20
  const BOOTSTRAP_CACHE_KEYS = [
@@ -79,6 +80,7 @@ function registerDiRegistrars(registrars) {
79
80
  setGlobalRegistrars(registrars);
80
81
  globalThis[BOOTSTRAP_CACHE_KEY] = null;
81
82
  globalThis[ENCRYPTION_ENABLED_KEY] = void 0;
83
+ globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] = void 0;
82
84
  }
83
85
  function getDiRegistrars() {
84
86
  const registrars = getGlobalRegistrars();
@@ -100,6 +102,14 @@ function resetBootstrapCache() {
100
102
  globalThis[ENCRYPTION_ENABLED_KEY] = void 0;
101
103
  globalThis[APP_DI_LOAD_WARNING_KEY] = void 0;
102
104
  globalThis[APP_DI_REGISTER_WARNING_KEY] = void 0;
105
+ globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] = void 0;
106
+ }
107
+ function isAppDiModuleUnresolvable() {
108
+ return globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] === true;
109
+ }
110
+ function markAppDiModuleUnresolvable() {
111
+ ;
112
+ globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] = true;
103
113
  }
104
114
  function isAppDiModuleNotFound(error) {
105
115
  if (!error || typeof error !== "object") return false;
@@ -202,7 +212,7 @@ async function createRequestContainer() {
202
212
  } catch (error) {
203
213
  logger.error("App-level DI registrar failed", { err: error });
204
214
  }
205
- } else {
215
+ } else if (!isAppDiModuleUnresolvable()) {
206
216
  try {
207
217
  const appDi = await import("@/di");
208
218
  if (appDi?.register) {
@@ -219,6 +229,7 @@ async function createRequestContainer() {
219
229
  }
220
230
  } catch (err) {
221
231
  if (isAppDiModuleNotFound(err)) {
232
+ markAppDiModuleUnresolvable();
222
233
  logger.debug("App-level DI override module (@/di) not resolvable; skipping", { err });
223
234
  } else {
224
235
  warnAppDiFailureOnce(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/di/container.ts"],
4
- "sourcesContent": ["import { asFunction, 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'\nimport { createOptimisticLockGuardService } from '@open-mercato/shared/lib/crud/optimistic-lock'\nimport { getAllOptimisticLockReaders } from '@open-mercato/shared/lib/crud/optimistic-lock-store'\nimport { createCommandOptimisticLockGuardService } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'di' })\n\ntype DynamicCradle = Record<string, any>\n\nexport type AppContainer = AwilixContainer<DynamicCradle>\nexport type DiRegistrar = (container: AppContainer) => void\nexport type AppDiRegistrar = (container: AppContainer) => void | Promise<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__'\nconst APP_DI_REGISTRAR_KEY = '__openMercatoAppDiRegistrar__'\nconst APP_DI_LOAD_WARNING_KEY = '__openMercatoAppDiLoadWarningEmitted__'\nconst APP_DI_REGISTER_WARNING_KEY = '__openMercatoAppDiRegisterWarningEmitted__'\n// Phase 5 \u2014 process-scoped bootstrap cache. The cache/event-bus/encryption\n// services bootstrap() creates are inherently process-scoped (they hold\n// state across requests). Caching them on globalThis after the first\n// successful bootstrap call lets every subsequent request skip the\n// `await bootstrap(container)` body and just re-register the cached\n// instances. Same globalThis pattern as registerDiRegistrars so HMR\n// keeps working.\nconst BOOTSTRAP_CACHE_KEY = '__openMercatoBootstrapCache__'\nconst ENCRYPTION_ENABLED_KEY = '__openMercatoEncryptionEnabledCache__'\n\nconst BOOTSTRAP_CACHE_KEYS = [\n 'cache',\n 'eventBus',\n 'kmsService',\n 'tenantEncryptionService',\n 'rateLimiterService',\n 'searchModuleConfigs',\n 'searchIndexer',\n] as const\n\ntype BootstrapCacheEntry = Partial<Record<(typeof BOOTSTRAP_CACHE_KEYS)[number], unknown>>\n\n// Phase 5 is opt-in. Some bootstrap services close over per-request state\n// (e.g. tenantEncryptionService captures the first request's `em.fork`, the\n// event-bus's resolver closes over the first container) so naively replaying\n// them on later requests yields stale references \u2014 observed as a 500 from\n// CRUD list endpoints in `next start`. Default OFF preserves develop's\n// per-request bootstrap. Set `OM_BOOTSTRAP_CACHE=1` to opt in once each\n// cached service is verified safe for cross-request reuse.\nfunction isBootstrapCacheEnabled(): boolean {\n const raw = process.env.OM_BOOTSTRAP_CACHE\n if (raw === undefined) return false\n const normalized = raw.trim().toLowerCase()\n if (!normalized.length) return false\n if (normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'no') return false\n return true\n}\n\nfunction getBootstrapCache(): BootstrapCacheEntry | null {\n if (!isBootstrapCacheEnabled()) return null\n const existing = (globalThis as any)[BOOTSTRAP_CACHE_KEY]\n return existing && typeof existing === 'object' ? (existing as BootstrapCacheEntry) : null\n}\n\nfunction setBootstrapCache(entry: BootstrapCacheEntry): void {\n if (!isBootstrapCacheEnabled()) return\n ;(globalThis as any)[BOOTSTRAP_CACHE_KEY] = entry\n}\n\nfunction harvestBootstrapCache(container: AwilixContainer): BootstrapCacheEntry {\n const entry: BootstrapCacheEntry = {}\n for (const key of BOOTSTRAP_CACHE_KEYS) {\n try {\n const value: unknown = container.resolve(key as never)\n if (value !== undefined && value !== null) entry[key] = value\n } catch {\n // not registered \u2014 skip\n }\n }\n return entry\n}\n\ntype EncryptionEnabledProbe = { isEnabled?: () => boolean } | null | undefined\n\nfunction getCachedEncryptionEnabled(service: EncryptionEnabledProbe): boolean | null {\n if (!service || typeof service.isEnabled !== 'function') return false\n const cached = (globalThis as Record<string, unknown>)[ENCRYPTION_ENABLED_KEY]\n if (typeof cached === 'boolean') return cached\n try {\n const result = !!service.isEnabled()\n ;(globalThis as Record<string, unknown>)[ENCRYPTION_ENABLED_KEY] = result\n return result\n } catch {\n return null\n }\n}\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 logger.debug('DI registrars re-registered (this may occur during HMR)')\n }\n setGlobalRegistrars(registrars)\n // Force re-bootstrap on HMR \u2014 module subscribers may have changed.\n ;(globalThis as any)[BOOTSTRAP_CACHE_KEY] = null\n ;(globalThis as any)[ENCRYPTION_ENABLED_KEY] = undefined\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 getAppDiRegistrar(): AppDiRegistrar | null {\n const registrar = (globalThis as Record<string, unknown>)[APP_DI_REGISTRAR_KEY]\n return typeof registrar === 'function' ? registrar as AppDiRegistrar : null\n}\n\nexport function registerAppDiRegistrar(registrar: AppDiRegistrar | null): void {\n ;(globalThis as Record<string, unknown>)[APP_DI_REGISTRAR_KEY] = registrar\n}\n\n/** Test-only helper to drop process-scoped request-container state. */\nexport function resetBootstrapCache(): void {\n (globalThis as any)[BOOTSTRAP_CACHE_KEY] = null\n ;(globalThis as any)[ENCRYPTION_ENABLED_KEY] = undefined\n ;(globalThis as Record<string, unknown>)[APP_DI_LOAD_WARNING_KEY] = undefined\n ;(globalThis as Record<string, unknown>)[APP_DI_REGISTER_WARNING_KEY] = undefined\n}\n\nfunction isAppDiModuleNotFound(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const { code, message } = error as { code?: unknown; message?: unknown }\n const text = typeof message === 'string' ? message : ''\n const moduleNotFound =\n code === 'MODULE_NOT_FOUND'\n || code === 'ERR_MODULE_NOT_FOUND'\n || text.startsWith('Cannot find module')\n || text.startsWith('Cannot find package')\n return moduleNotFound && /(?:module|package) ['\"]@\\/di['\"]/.test(text)\n}\n\nfunction warnAppDiFailureOnce(\n key: typeof APP_DI_LOAD_WARNING_KEY | typeof APP_DI_REGISTER_WARNING_KEY,\n message: string,\n error: unknown,\n): void {\n const globalScope = globalThis as Record<string, unknown>\n if (globalScope[key] === true) return\n globalScope[key] = true\n logger.warn(message, { err: error })\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 // Default OSS optimistic-lock guard. Reads from the global reader store\n // (populated by `makeCrudRoute` auto-registration + any module-DI\n // hand-wired calls to `registerOptimisticLockReaders`). Service is\n // strictly additive: when `OM_OPTIMISTIC_LOCK=off` (or no header is\n // sent) it short-circuits at validateMutation. Module-level di.ts\n // registrations override this default via Awilix replace semantics \u2014\n // see the enterprise `record_locks` module for the canonical override.\n // Spec: .ai/specs/implemented/2026-05-25-oss-optimistic-locking.md\n crudMutationGuardService: asFunction((em: EntityManager) =>\n createOptimisticLockGuardService({\n getEm: () => em,\n readers: getAllOptimisticLockReaders(),\n }),\n ).scoped(),\n // Default OSS command-level optimistic-lock guard, awaited by\n // `enforceCommandOptimisticLockWithGuards` for Command-pattern writes.\n // Header/explicit-token compare only (no `resolveExpected`), so it is\n // behaviourally identical to calling `enforceCommandOptimisticLock`\n // directly. The enterprise `record_locks` module overrides this DI key\n // with a lock-backed `resolveExpected` via Awilix replace semantics.\n // Spec: .ai/specs/enterprise/2026-06-09-record-locks-unified-coverage.md (Phase 0)\n commandOptimisticLockGuardService: asFunction(() =>\n createCommandOptimisticLockGuardService(),\n ).scoped(),\n })\n // Allow modules to override/extend. Fail-open by design (one broken module's\n // di.ts must not take down every request container), but never silently: the\n // module's services would otherwise vanish with no trace until an unrelated\n // Awilix resolution error surfaces much later.\n for (const [registrarIndex, reg] of diRegistrars.entries()) {\n try { reg?.(container) } catch (error) {\n logger.error('Module DI registrar failed', { registrarIndex, err: error })\n }\n }\n // Core bootstrap (cache, event bus, encryption subscriber/KMS, module subscribers)\n // Phase 5 \u2014 process-scoped once-guard. The first request runs the full\n // bootstrap() body; later requests re-register the cached services\n // directly on this request's container without re-importing or\n // re-initializing anything. HMR clears the cache (see\n // registerDiRegistrars). Skippable if a caller already wired eventBus.\n const alreadyBootstrappedOnThisContainer = !!container.registrations?.eventBus\n if (!alreadyBootstrappedOnThisContainer) {\n const cached = getBootstrapCache()\n if (cached) {\n const replay: Record<string, any> = {}\n for (const [key, value] of Object.entries(cached)) {\n if (value !== undefined && value !== null) replay[key] = asValue(value)\n }\n if (Object.keys(replay).length > 0) container.register(replay)\n } else {\n try {\n const { bootstrap } = await import('@open-mercato/core/bootstrap') as any\n if (bootstrap && typeof bootstrap === 'function') {\n await bootstrap(container)\n setBootstrapCache(harvestBootstrapCache(container))\n }\n } catch { /* optional */ }\n }\n }\n // App-level DI override. Scaffolded apps register this callback explicitly\n // from their own bootstrap module so app aliases resolve in app context.\n const appDiRegistrar = getAppDiRegistrar()\n if (appDiRegistrar) {\n try {\n await appDiRegistrar(container)\n } catch (error) {\n logger.error('App-level DI registrar failed', { err: error })\n }\n } else {\n // Backward-compatible fallback for apps that have not adopted explicit wiring.\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 (err) {\n warnAppDiFailureOnce(\n APP_DI_REGISTER_WARNING_KEY,\n 'App-level DI override (src/di.ts register()) threw; its registrations are skipped',\n err,\n )\n }\n }\n } catch (err) {\n if (isAppDiModuleNotFound(err)) {\n logger.debug('App-level DI override module (@/di) not resolvable; skipping', { err })\n } else {\n warnAppDiFailureOnce(\n APP_DI_LOAD_WARNING_KEY,\n 'App-level DI override module (@/di) failed to load; its registrations are skipped',\n err,\n )\n }\n }\n }\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 // Phase 5 \u2014 cache `tenantEncryptionService.isEnabled()` for the process\n // lifetime. The result depends only on config that does not change at\n // runtime, so reading it once skips a config lookup per request.\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 && getCachedEncryptionEnabled(tenantEncryptionService) === true) {\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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",
4
+ "sourcesContent": ["import { asFunction, 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'\nimport { createOptimisticLockGuardService } from '@open-mercato/shared/lib/crud/optimistic-lock'\nimport { getAllOptimisticLockReaders } from '@open-mercato/shared/lib/crud/optimistic-lock-store'\nimport { createCommandOptimisticLockGuardService } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'di' })\n\ntype DynamicCradle = Record<string, any>\n\nexport type AppContainer = AwilixContainer<DynamicCradle>\nexport type DiRegistrar = (container: AppContainer) => void\nexport type AppDiRegistrar = (container: AppContainer) => void | Promise<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__'\nconst APP_DI_REGISTRAR_KEY = '__openMercatoAppDiRegistrar__'\nconst APP_DI_LOAD_WARNING_KEY = '__openMercatoAppDiLoadWarningEmitted__'\nconst APP_DI_REGISTER_WARNING_KEY = '__openMercatoAppDiRegisterWarningEmitted__'\n// Set once the legacy `@/di` fallback proves the specifier is unresolvable in this process.\n// Worker/CLI processes run against built package output where the app's `@/` alias does not\n// exist, so retrying the import per request container only repeats a failed module resolution\n// (and its log line) once per job.\nconst APP_DI_MODULE_UNRESOLVABLE_KEY = '__openMercatoAppDiModuleUnresolvable__'\n// Phase 5 \u2014 process-scoped bootstrap cache. The cache/event-bus/encryption\n// services bootstrap() creates are inherently process-scoped (they hold\n// state across requests). Caching them on globalThis after the first\n// successful bootstrap call lets every subsequent request skip the\n// `await bootstrap(container)` body and just re-register the cached\n// instances. Same globalThis pattern as registerDiRegistrars so HMR\n// keeps working.\nconst BOOTSTRAP_CACHE_KEY = '__openMercatoBootstrapCache__'\nconst ENCRYPTION_ENABLED_KEY = '__openMercatoEncryptionEnabledCache__'\n\nconst BOOTSTRAP_CACHE_KEYS = [\n 'cache',\n 'eventBus',\n 'kmsService',\n 'tenantEncryptionService',\n 'rateLimiterService',\n 'searchModuleConfigs',\n 'searchIndexer',\n] as const\n\ntype BootstrapCacheEntry = Partial<Record<(typeof BOOTSTRAP_CACHE_KEYS)[number], unknown>>\n\n// Phase 5 is opt-in. Some bootstrap services close over per-request state\n// (e.g. tenantEncryptionService captures the first request's `em.fork`, the\n// event-bus's resolver closes over the first container) so naively replaying\n// them on later requests yields stale references \u2014 observed as a 500 from\n// CRUD list endpoints in `next start`. Default OFF preserves develop's\n// per-request bootstrap. Set `OM_BOOTSTRAP_CACHE=1` to opt in once each\n// cached service is verified safe for cross-request reuse.\nfunction isBootstrapCacheEnabled(): boolean {\n const raw = process.env.OM_BOOTSTRAP_CACHE\n if (raw === undefined) return false\n const normalized = raw.trim().toLowerCase()\n if (!normalized.length) return false\n if (normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'no') return false\n return true\n}\n\nfunction getBootstrapCache(): BootstrapCacheEntry | null {\n if (!isBootstrapCacheEnabled()) return null\n const existing = (globalThis as any)[BOOTSTRAP_CACHE_KEY]\n return existing && typeof existing === 'object' ? (existing as BootstrapCacheEntry) : null\n}\n\nfunction setBootstrapCache(entry: BootstrapCacheEntry): void {\n if (!isBootstrapCacheEnabled()) return\n ;(globalThis as any)[BOOTSTRAP_CACHE_KEY] = entry\n}\n\nfunction harvestBootstrapCache(container: AwilixContainer): BootstrapCacheEntry {\n const entry: BootstrapCacheEntry = {}\n for (const key of BOOTSTRAP_CACHE_KEYS) {\n try {\n const value: unknown = container.resolve(key as never)\n if (value !== undefined && value !== null) entry[key] = value\n } catch {\n // not registered \u2014 skip\n }\n }\n return entry\n}\n\ntype EncryptionEnabledProbe = { isEnabled?: () => boolean } | null | undefined\n\nfunction getCachedEncryptionEnabled(service: EncryptionEnabledProbe): boolean | null {\n if (!service || typeof service.isEnabled !== 'function') return false\n const cached = (globalThis as Record<string, unknown>)[ENCRYPTION_ENABLED_KEY]\n if (typeof cached === 'boolean') return cached\n try {\n const result = !!service.isEnabled()\n ;(globalThis as Record<string, unknown>)[ENCRYPTION_ENABLED_KEY] = result\n return result\n } catch {\n return null\n }\n}\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 logger.debug('DI registrars re-registered (this may occur during HMR)')\n }\n setGlobalRegistrars(registrars)\n // Force re-bootstrap on HMR \u2014 module subscribers may have changed.\n ;(globalThis as any)[BOOTSTRAP_CACHE_KEY] = null\n ;(globalThis as any)[ENCRYPTION_ENABLED_KEY] = undefined\n // An app that gains a src/di.ts mid-session reloads through here, so the negative\n // resolution result must not outlive the reload.\n ;(globalThis as Record<string, unknown>)[APP_DI_MODULE_UNRESOLVABLE_KEY] = undefined\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 getAppDiRegistrar(): AppDiRegistrar | null {\n const registrar = (globalThis as Record<string, unknown>)[APP_DI_REGISTRAR_KEY]\n return typeof registrar === 'function' ? registrar as AppDiRegistrar : null\n}\n\nexport function registerAppDiRegistrar(registrar: AppDiRegistrar | null): void {\n ;(globalThis as Record<string, unknown>)[APP_DI_REGISTRAR_KEY] = registrar\n}\n\n/** Test-only helper to drop process-scoped request-container state. */\nexport function resetBootstrapCache(): void {\n (globalThis as any)[BOOTSTRAP_CACHE_KEY] = null\n ;(globalThis as any)[ENCRYPTION_ENABLED_KEY] = undefined\n ;(globalThis as Record<string, unknown>)[APP_DI_LOAD_WARNING_KEY] = undefined\n ;(globalThis as Record<string, unknown>)[APP_DI_REGISTER_WARNING_KEY] = undefined\n ;(globalThis as Record<string, unknown>)[APP_DI_MODULE_UNRESOLVABLE_KEY] = undefined\n}\n\nfunction isAppDiModuleUnresolvable(): boolean {\n return (globalThis as Record<string, unknown>)[APP_DI_MODULE_UNRESOLVABLE_KEY] === true\n}\n\nfunction markAppDiModuleUnresolvable(): void {\n ;(globalThis as Record<string, unknown>)[APP_DI_MODULE_UNRESOLVABLE_KEY] = true\n}\n\nfunction isAppDiModuleNotFound(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const { code, message } = error as { code?: unknown; message?: unknown }\n const text = typeof message === 'string' ? message : ''\n const moduleNotFound =\n code === 'MODULE_NOT_FOUND'\n || code === 'ERR_MODULE_NOT_FOUND'\n || text.startsWith('Cannot find module')\n || text.startsWith('Cannot find package')\n return moduleNotFound && /(?:module|package) ['\"]@\\/di['\"]/.test(text)\n}\n\nfunction warnAppDiFailureOnce(\n key: typeof APP_DI_LOAD_WARNING_KEY | typeof APP_DI_REGISTER_WARNING_KEY,\n message: string,\n error: unknown,\n): void {\n const globalScope = globalThis as Record<string, unknown>\n if (globalScope[key] === true) return\n globalScope[key] = true\n logger.warn(message, { err: error })\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 // Default OSS optimistic-lock guard. Reads from the global reader store\n // (populated by `makeCrudRoute` auto-registration + any module-DI\n // hand-wired calls to `registerOptimisticLockReaders`). Service is\n // strictly additive: when `OM_OPTIMISTIC_LOCK=off` (or no header is\n // sent) it short-circuits at validateMutation. Module-level di.ts\n // registrations override this default via Awilix replace semantics \u2014\n // see the enterprise `record_locks` module for the canonical override.\n // Spec: .ai/specs/implemented/2026-05-25-oss-optimistic-locking.md\n crudMutationGuardService: asFunction((em: EntityManager) =>\n createOptimisticLockGuardService({\n getEm: () => em,\n readers: getAllOptimisticLockReaders(),\n }),\n ).scoped(),\n // Default OSS command-level optimistic-lock guard, awaited by\n // `enforceCommandOptimisticLockWithGuards` for Command-pattern writes.\n // Header/explicit-token compare only (no `resolveExpected`), so it is\n // behaviourally identical to calling `enforceCommandOptimisticLock`\n // directly. The enterprise `record_locks` module overrides this DI key\n // with a lock-backed `resolveExpected` via Awilix replace semantics.\n // Spec: .ai/specs/enterprise/2026-06-09-record-locks-unified-coverage.md (Phase 0)\n commandOptimisticLockGuardService: asFunction(() =>\n createCommandOptimisticLockGuardService(),\n ).scoped(),\n })\n // Allow modules to override/extend. Fail-open by design (one broken module's\n // di.ts must not take down every request container), but never silently: the\n // module's services would otherwise vanish with no trace until an unrelated\n // Awilix resolution error surfaces much later.\n for (const [registrarIndex, reg] of diRegistrars.entries()) {\n try { reg?.(container) } catch (error) {\n logger.error('Module DI registrar failed', { registrarIndex, err: error })\n }\n }\n // Core bootstrap (cache, event bus, encryption subscriber/KMS, module subscribers)\n // Phase 5 \u2014 process-scoped once-guard. The first request runs the full\n // bootstrap() body; later requests re-register the cached services\n // directly on this request's container without re-importing or\n // re-initializing anything. HMR clears the cache (see\n // registerDiRegistrars). Skippable if a caller already wired eventBus.\n const alreadyBootstrappedOnThisContainer = !!container.registrations?.eventBus\n if (!alreadyBootstrappedOnThisContainer) {\n const cached = getBootstrapCache()\n if (cached) {\n const replay: Record<string, any> = {}\n for (const [key, value] of Object.entries(cached)) {\n if (value !== undefined && value !== null) replay[key] = asValue(value)\n }\n if (Object.keys(replay).length > 0) container.register(replay)\n } else {\n try {\n const { bootstrap } = await import('@open-mercato/core/bootstrap') as any\n if (bootstrap && typeof bootstrap === 'function') {\n await bootstrap(container)\n setBootstrapCache(harvestBootstrapCache(container))\n }\n } catch { /* optional */ }\n }\n }\n // App-level DI override. Scaffolded apps register this callback explicitly\n // from their own bootstrap module so app aliases resolve in app context.\n const appDiRegistrar = getAppDiRegistrar()\n if (appDiRegistrar) {\n try {\n await appDiRegistrar(container)\n } catch (error) {\n logger.error('App-level DI registrar failed', { err: error })\n }\n } else if (!isAppDiModuleUnresolvable()) {\n // Backward-compatible fallback for apps that have not adopted explicit wiring.\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 (err) {\n warnAppDiFailureOnce(\n APP_DI_REGISTER_WARNING_KEY,\n 'App-level DI override (src/di.ts register()) threw; its registrations are skipped',\n err,\n )\n }\n }\n } catch (err) {\n if (isAppDiModuleNotFound(err)) {\n markAppDiModuleUnresolvable()\n logger.debug('App-level DI override module (@/di) not resolvable; skipping', { err })\n } else {\n warnAppDiFailureOnce(\n APP_DI_LOAD_WARNING_KEY,\n 'App-level DI override module (@/di) failed to load; its registrations are skipped',\n err,\n )\n }\n }\n }\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 // Phase 5 \u2014 cache `tenantEncryptionService.isEnabled()` for the process\n // lifetime. The result depends only on config that does not change at\n // runtime, so reading it once skips a config lookup per request.\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 && getCachedEncryptionEnabled(tenantEncryptionService) === true) {\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": ["em"]
7
7
  }
@@ -1,10 +1,10 @@
1
1
  import { defaultLocale, locales } from "./config.js";
2
2
  import { resolveForcedLocale, resolveLocaleFromAcceptLanguage } from "./locale.js";
3
3
  import { createFallbackTranslator, createTranslator } from "./translate.js";
4
- import { getModules } from "../modules/registry.js";
4
+ import { tryGetModules } from "../modules/registry.js";
5
5
  import { loadAppDictionary } from "./app-dictionaries.js";
6
6
  import { getCachedDictionary, setCachedDictionary } from "./dictionary-cache.js";
7
- import { registerModules, getModules as getModules2 } from "../modules/registry.js";
7
+ import { registerModules, getModules } from "../modules/registry.js";
8
8
  import { registerAppDictionaryLoader } from "./app-dictionaries.js";
9
9
  import { invalidateDictionaryCache } from "./dictionary-cache.js";
10
10
  function flattenDictionary(source, prefix = "") {
@@ -46,7 +46,7 @@ async function loadDictionary(locale) {
46
46
  if (cached) return cached;
47
47
  const baseRaw = await loadAppDictionary(locale);
48
48
  const merged = { ...flattenDictionary(baseRaw) };
49
- const modules = getModules();
49
+ const modules = tryGetModules() ?? [];
50
50
  for (const m of modules) {
51
51
  const dict = m.translations?.[locale];
52
52
  if (dict) Object.assign(merged, flattenDictionary(dict));
@@ -67,7 +67,7 @@ try {
67
67
  }
68
68
  export {
69
69
  detectLocale,
70
- getModules2 as getModules,
70
+ getModules,
71
71
  invalidateDictionaryCache,
72
72
  loadDictionary,
73
73
  registerAppDictionaryLoader,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/i18n/server.ts"],
4
- "sourcesContent": ["import { defaultLocale, locales, type Locale } from './config'\nimport type { Dict } from './context'\nimport { resolveForcedLocale, resolveLocaleFromAcceptLanguage } from './locale'\nimport { createFallbackTranslator, createTranslator } from './translate'\nimport { getModules } from '../modules/registry'\nimport { loadAppDictionary } from './app-dictionaries'\nimport { getCachedDictionary, setCachedDictionary } from './dictionary-cache'\n\n// Re-export for backwards compatibility\nexport { registerModules, getModules } from '../modules/registry'\nexport { registerAppDictionaryLoader } from './app-dictionaries'\nexport { invalidateDictionaryCache } from './dictionary-cache'\n\nfunction flattenDictionary(source: unknown, prefix = ''): Dict {\n if (!source || typeof source !== 'object' || Array.isArray(source)) return {}\n const result: Dict = {}\n for (const [key, value] of Object.entries(source as Record<string, unknown>)) {\n if (!key) continue\n const nextKey = prefix ? `${prefix}.${key}` : key\n if (typeof value === 'string') {\n result[nextKey] = value\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n Object.assign(result, flattenDictionary(value, nextKey))\n }\n }\n return result\n}\n\nexport async function detectLocale(): Promise<Locale> {\n // Ops-level override: pin the whole app to one locale (default: unset).\n const forced = resolveForcedLocale(process.env)\n if (forced) return forced\n // Dynamic import to avoid requiring Next.js in non-Next.js contexts (CLI, tests)\n try {\n const { cookies, headers } = await import('next/headers')\n try {\n const c = (await cookies()).get('locale')?.value\n if (c && locales.includes(c as Locale)) return c as Locale\n } catch {\n // cookies() may not be available outside request context (e.g., in tests)\n }\n try {\n const accept = (await headers()).get('accept-language') || ''\n const match = resolveLocaleFromAcceptLanguage(accept)\n if (match) return match\n } catch {\n // headers() may not be available outside request context (e.g., in tests)\n }\n } catch {\n // next/headers not available (CLI context)\n }\n return defaultLocale\n}\n\nexport async function loadDictionary(locale: Locale): Promise<Dict> {\n // Locale dictionaries are immutable at runtime, so the flatten+merge below\n // only needs to run once per locale. The cache is invalidated whenever\n // modules or the app dictionary loader are (re)registered.\n const cached = getCachedDictionary(locale)\n if (cached) return cached\n // Load from registry instead of @/ import (works in standalone packages)\n const baseRaw = await loadAppDictionary(locale)\n const merged: Dict = { ...flattenDictionary(baseRaw) }\n const modules = getModules()\n for (const m of modules) {\n const dict = m.translations?.[locale]\n if (dict) Object.assign(merged, flattenDictionary(dict))\n }\n setCachedDictionary(locale, merged)\n return merged\n}\n\nexport async function resolveTranslations() {\n const locale = await detectLocale()\n const dict = await loadDictionary(locale)\n const t = createTranslator(dict)\n const translate = createFallbackTranslator(dict)\n return { locale, dict, t, translate }\n}\n// Hint Next.js to keep this server-only; ignore if unavailable when running scripts outside Next.\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // noop: allows running generator scripts without Next's server-only package\n}\n"],
5
- "mappings": "AAAA,SAAS,eAAe,eAA4B;AAEpD,SAAS,qBAAqB,uCAAuC;AACrE,SAAS,0BAA0B,wBAAwB;AAC3D,SAAS,kBAAkB;AAC3B,SAAS,yBAAyB;AAClC,SAAS,qBAAqB,2BAA2B;AAGzD,SAAS,iBAAiB,cAAAA,mBAAkB;AAC5C,SAAS,mCAAmC;AAC5C,SAAS,iCAAiC;AAE1C,SAAS,kBAAkB,QAAiB,SAAS,IAAU;AAC7D,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AAC5E,QAAM,SAAe,CAAC;AACtB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,CAAC,IAAK;AACV,UAAM,UAAU,SAAS,GAAG,MAAM,IAAI,GAAG,KAAK;AAC9C,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,OAAO,IAAI;AAAA,IACpB,WAAW,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,aAAO,OAAO,QAAQ,kBAAkB,OAAO,OAAO,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,eAAgC;AAEpD,QAAM,SAAS,oBAAoB,QAAQ,GAAG;AAC9C,MAAI,OAAQ,QAAO;AAEnB,MAAI;AACF,UAAM,EAAE,SAAS,QAAQ,IAAI,MAAM,OAAO,cAAc;AACxD,QAAI;AACF,YAAM,KAAK,MAAM,QAAQ,GAAG,IAAI,QAAQ,GAAG;AAC3C,UAAI,KAAK,QAAQ,SAAS,CAAW,EAAG,QAAO;AAAA,IACjD,QAAQ;AAAA,IAER;AACA,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,GAAG,IAAI,iBAAiB,KAAK;AAC3D,YAAM,QAAQ,gCAAgC,MAAM;AACpD,UAAI,MAAO,QAAO;AAAA,IACpB,QAAQ;AAAA,IAER;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,eAAsB,eAAe,QAA+B;AAIlE,QAAM,SAAS,oBAAoB,MAAM;AACzC,MAAI,OAAQ,QAAO;AAEnB,QAAM,UAAU,MAAM,kBAAkB,MAAM;AAC9C,QAAM,SAAe,EAAE,GAAG,kBAAkB,OAAO,EAAE;AACrD,QAAM,UAAU,WAAW;AAC3B,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,EAAE,eAAe,MAAM;AACpC,QAAI,KAAM,QAAO,OAAO,QAAQ,kBAAkB,IAAI,CAAC;AAAA,EACzD;AACA,sBAAoB,QAAQ,MAAM;AAClC,SAAO;AACT;AAEA,eAAsB,sBAAsB;AAC1C,QAAM,SAAS,MAAM,aAAa;AAClC,QAAM,OAAO,MAAM,eAAe,MAAM;AACxC,QAAM,IAAI,iBAAiB,IAAI;AAC/B,QAAM,YAAY,yBAAyB,IAAI;AAC/C,SAAO,EAAE,QAAQ,MAAM,GAAG,UAAU;AACtC;AAEA,IAAI;AAEF,UAAQ,aAAa;AACvB,QAAQ;AAER;",
6
- "names": ["getModules"]
4
+ "sourcesContent": ["import { defaultLocale, locales, type Locale } from './config'\nimport type { Dict } from './context'\nimport { resolveForcedLocale, resolveLocaleFromAcceptLanguage } from './locale'\nimport { createFallbackTranslator, createTranslator } from './translate'\nimport { tryGetModules } from '../modules/registry'\nimport { loadAppDictionary } from './app-dictionaries'\nimport { getCachedDictionary, setCachedDictionary } from './dictionary-cache'\n\n// Re-export for backwards compatibility\nexport { registerModules, getModules } from '../modules/registry'\nexport { registerAppDictionaryLoader } from './app-dictionaries'\nexport { invalidateDictionaryCache } from './dictionary-cache'\n\nfunction flattenDictionary(source: unknown, prefix = ''): Dict {\n if (!source || typeof source !== 'object' || Array.isArray(source)) return {}\n const result: Dict = {}\n for (const [key, value] of Object.entries(source as Record<string, unknown>)) {\n if (!key) continue\n const nextKey = prefix ? `${prefix}.${key}` : key\n if (typeof value === 'string') {\n result[nextKey] = value\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n Object.assign(result, flattenDictionary(value, nextKey))\n }\n }\n return result\n}\n\nexport async function detectLocale(): Promise<Locale> {\n // Ops-level override: pin the whole app to one locale (default: unset).\n const forced = resolveForcedLocale(process.env)\n if (forced) return forced\n // Dynamic import to avoid requiring Next.js in non-Next.js contexts (CLI, tests)\n try {\n const { cookies, headers } = await import('next/headers')\n try {\n const c = (await cookies()).get('locale')?.value\n if (c && locales.includes(c as Locale)) return c as Locale\n } catch {\n // cookies() may not be available outside request context (e.g., in tests)\n }\n try {\n const accept = (await headers()).get('accept-language') || ''\n const match = resolveLocaleFromAcceptLanguage(accept)\n if (match) return match\n } catch {\n // headers() may not be available outside request context (e.g., in tests)\n }\n } catch {\n // next/headers not available (CLI context)\n }\n return defaultLocale\n}\n\nexport async function loadDictionary(locale: Locale): Promise<Dict> {\n // Locale dictionaries are immutable at runtime, so the flatten+merge below\n // only needs to run once per locale. The cache is invalidated whenever\n // modules or the app dictionary loader are (re)registered.\n const cached = getCachedDictionary(locale)\n if (cached) return cached\n // Load from registry instead of @/ import (works in standalone packages)\n const baseRaw = await loadAppDictionary(locale)\n const merged: Dict = { ...flattenDictionary(baseRaw) }\n // Route handlers translate their responses, so they resolve a dictionary even\n // when they are exercised in isolation without a bootstrapped registry. The\n // app dictionary alone is the right degraded answer there \u2014 `registerModules`\n // invalidates this cache, so a later bootstrap still gets the merged result.\n const modules = tryGetModules() ?? []\n for (const m of modules) {\n const dict = m.translations?.[locale]\n if (dict) Object.assign(merged, flattenDictionary(dict))\n }\n setCachedDictionary(locale, merged)\n return merged\n}\n\nexport async function resolveTranslations() {\n const locale = await detectLocale()\n const dict = await loadDictionary(locale)\n const t = createTranslator(dict)\n const translate = createFallbackTranslator(dict)\n return { locale, dict, t, translate }\n}\n// Hint Next.js to keep this server-only; ignore if unavailable when running scripts outside Next.\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // noop: allows running generator scripts without Next's server-only package\n}\n"],
5
+ "mappings": "AAAA,SAAS,eAAe,eAA4B;AAEpD,SAAS,qBAAqB,uCAAuC;AACrE,SAAS,0BAA0B,wBAAwB;AAC3D,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,qBAAqB,2BAA2B;AAGzD,SAAS,iBAAiB,kBAAkB;AAC5C,SAAS,mCAAmC;AAC5C,SAAS,iCAAiC;AAE1C,SAAS,kBAAkB,QAAiB,SAAS,IAAU;AAC7D,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AAC5E,QAAM,SAAe,CAAC;AACtB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,CAAC,IAAK;AACV,UAAM,UAAU,SAAS,GAAG,MAAM,IAAI,GAAG,KAAK;AAC9C,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,OAAO,IAAI;AAAA,IACpB,WAAW,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,aAAO,OAAO,QAAQ,kBAAkB,OAAO,OAAO,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,eAAgC;AAEpD,QAAM,SAAS,oBAAoB,QAAQ,GAAG;AAC9C,MAAI,OAAQ,QAAO;AAEnB,MAAI;AACF,UAAM,EAAE,SAAS,QAAQ,IAAI,MAAM,OAAO,cAAc;AACxD,QAAI;AACF,YAAM,KAAK,MAAM,QAAQ,GAAG,IAAI,QAAQ,GAAG;AAC3C,UAAI,KAAK,QAAQ,SAAS,CAAW,EAAG,QAAO;AAAA,IACjD,QAAQ;AAAA,IAER;AACA,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,GAAG,IAAI,iBAAiB,KAAK;AAC3D,YAAM,QAAQ,gCAAgC,MAAM;AACpD,UAAI,MAAO,QAAO;AAAA,IACpB,QAAQ;AAAA,IAER;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,eAAsB,eAAe,QAA+B;AAIlE,QAAM,SAAS,oBAAoB,MAAM;AACzC,MAAI,OAAQ,QAAO;AAEnB,QAAM,UAAU,MAAM,kBAAkB,MAAM;AAC9C,QAAM,SAAe,EAAE,GAAG,kBAAkB,OAAO,EAAE;AAKrD,QAAM,UAAU,cAAc,KAAK,CAAC;AACpC,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,EAAE,eAAe,MAAM;AACpC,QAAI,KAAM,QAAO,OAAO,QAAQ,kBAAkB,IAAI,CAAC;AAAA,EACzD;AACA,sBAAoB,QAAQ,MAAM;AAClC,SAAO;AACT;AAEA,eAAsB,sBAAsB;AAC1C,QAAM,SAAS,MAAM,aAAa;AAClC,QAAM,OAAO,MAAM,eAAe,MAAM;AACxC,QAAM,IAAI,iBAAiB,IAAI;AAC/B,QAAM,YAAY,yBAAyB,IAAI;AAC/C,SAAO,EAAE,QAAQ,MAAM,GAAG,UAAU;AACtC;AAEA,IAAI;AAEF,UAAQ,aAAa;AACvB,QAAQ;AAER;",
6
+ "names": []
7
7
  }
@@ -0,0 +1,29 @@
1
+ const GLOBAL_KEY = /* @__PURE__ */ Symbol.for("@open-mercato/shared.loggerExtension");
2
+ function store() {
3
+ const globalStore = globalThis;
4
+ let current = globalStore[GLOBAL_KEY];
5
+ if (!current) {
6
+ current = {};
7
+ globalStore[GLOBAL_KEY] = current;
8
+ }
9
+ return current;
10
+ }
11
+ function registerLoggerExtension(extension) {
12
+ store().active = extension;
13
+ return () => {
14
+ const current = store();
15
+ if (current.active === extension) current.active = void 0;
16
+ };
17
+ }
18
+ function getLoggerExtension() {
19
+ return store().active;
20
+ }
21
+ function resetLoggerExtension() {
22
+ store().active = void 0;
23
+ }
24
+ export {
25
+ getLoggerExtension,
26
+ registerLoggerExtension,
27
+ resetLoggerExtension
28
+ };
29
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/logger/extension.ts"],
4
+ "sourcesContent": ["import type { LogLevel } from './level'\n\nexport type LoggerExtensionRecord = {\n level: LogLevel\n namespace: string\n message: string\n fields: Record<string, unknown>\n time: number\n}\n\nexport type LoggerExtension = {\n /**\n * Add process-local context (for example trace/span ids) to both the local\n * line and the optional remote sink.\n */\n enrich?(): Record<string, unknown> | undefined\n /**\n * Observe a record after the local transport writes it. Implementations must\n * never throw into application code.\n */\n emit?(record: LoggerExtensionRecord): void\n}\n\nconst GLOBAL_KEY = Symbol.for('@open-mercato/shared.loggerExtension')\n\ntype LoggerExtensionStore = {\n active?: LoggerExtension\n}\n\nfunction store(): LoggerExtensionStore {\n const globalStore = globalThis as unknown as Record<symbol, LoggerExtensionStore | undefined>\n let current = globalStore[GLOBAL_KEY]\n if (!current) {\n current = {}\n globalStore[GLOBAL_KEY] = current\n }\n return current\n}\n\n/**\n * Register the single process-wide logger extension. The returned disposer only\n * clears the extension when it is still the active registration.\n */\nexport function registerLoggerExtension(extension: LoggerExtension): () => void {\n store().active = extension\n return () => {\n const current = store()\n if (current.active === extension) current.active = undefined\n }\n}\n\nexport function getLoggerExtension(): LoggerExtension | undefined {\n return store().active\n}\n\n/** Test-only: clear the process-wide extension. */\nexport function resetLoggerExtension(): void {\n store().active = undefined\n}\n"],
5
+ "mappings": "AAuBA,MAAM,aAAa,uBAAO,IAAI,sCAAsC;AAMpE,SAAS,QAA8B;AACrC,QAAM,cAAc;AACpB,MAAI,UAAU,YAAY,UAAU;AACpC,MAAI,CAAC,SAAS;AACZ,cAAU,CAAC;AACX,gBAAY,UAAU,IAAI;AAAA,EAC5B;AACA,SAAO;AACT;AAMO,SAAS,wBAAwB,WAAwC;AAC9E,QAAM,EAAE,SAAS;AACjB,SAAO,MAAM;AACX,UAAM,UAAU,MAAM;AACtB,QAAI,QAAQ,WAAW,UAAW,SAAQ,SAAS;AAAA,EACrD;AACF;AAEO,SAAS,qBAAkD;AAChE,SAAO,MAAM,EAAE;AACjB;AAGO,SAAS,uBAA6B;AAC3C,QAAM,EAAE,SAAS;AACnB;",
6
+ "names": []
7
+ }