@open-mercato/shared 0.6.6-develop.6551.1.1511bfa890 → 0.6.7-canary.316.1.e6c38e0bef

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +24 -0
  3. package/dist/lib/auth/server.js +19 -7
  4. package/dist/lib/auth/server.js.map +2 -2
  5. package/dist/lib/bootstrap/dynamicLoader.js +13 -2
  6. package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
  7. package/dist/lib/bootstrap/factory.js +8 -1
  8. package/dist/lib/bootstrap/factory.js.map +2 -2
  9. package/dist/lib/cache/maintenance.js +5 -0
  10. package/dist/lib/cache/maintenance.js.map +7 -0
  11. package/dist/lib/commands/helpers.js +4 -2
  12. package/dist/lib/commands/helpers.js.map +2 -2
  13. package/dist/lib/crud/cache.js +3 -5
  14. package/dist/lib/crud/cache.js.map +2 -2
  15. package/dist/lib/crud/errors.js.map +2 -2
  16. package/dist/lib/crud/factory.js +4 -3
  17. package/dist/lib/crud/factory.js.map +2 -2
  18. package/dist/lib/crud/ids.js +11 -2
  19. package/dist/lib/crud/ids.js.map +2 -2
  20. package/dist/lib/crud/mutation-guard-registry.js +2 -12
  21. package/dist/lib/crud/mutation-guard-registry.js.map +2 -2
  22. package/dist/lib/crud/mutation-guard-service.js +30 -0
  23. package/dist/lib/crud/mutation-guard-service.js.map +7 -0
  24. package/dist/lib/crud/mutation-guard.js +1 -11
  25. package/dist/lib/crud/mutation-guard.js.map +2 -2
  26. package/dist/lib/data/engine.js +6 -2
  27. package/dist/lib/data/engine.js.map +2 -2
  28. package/dist/lib/db/mikro.js +3 -0
  29. package/dist/lib/db/mikro.js.map +2 -2
  30. package/dist/lib/di/container.js +30 -10
  31. package/dist/lib/di/container.js.map +3 -3
  32. package/dist/lib/openapi/attach-docs.js +44 -0
  33. package/dist/lib/openapi/attach-docs.js.map +7 -0
  34. package/dist/lib/openapi/index.js +2 -0
  35. package/dist/lib/openapi/index.js.map +2 -2
  36. package/dist/lib/ratelimit/config.js +10 -1
  37. package/dist/lib/ratelimit/config.js.map +2 -2
  38. package/dist/lib/ratelimit/helpers.js +5 -3
  39. package/dist/lib/ratelimit/helpers.js.map +2 -2
  40. package/dist/lib/ratelimit/index.js +2 -1
  41. package/dist/lib/ratelimit/index.js.map +2 -2
  42. package/dist/lib/ratelimit/service.js +1 -1
  43. package/dist/lib/ratelimit/service.js.map +1 -1
  44. package/dist/lib/version.js +1 -1
  45. package/dist/lib/version.js.map +1 -1
  46. package/dist/modules/entities/validators.js +2 -1
  47. package/dist/modules/entities/validators.js.map +2 -2
  48. package/dist/modules/payment_gateways/types.js.map +2 -2
  49. package/dist/modules/search.js.map +1 -1
  50. package/dist/modules/workflows/code-registry.js +34 -0
  51. package/dist/modules/workflows/code-registry.js.map +7 -0
  52. package/package.json +6 -6
  53. package/src/lib/auth/__tests__/server.test.ts +39 -0
  54. package/src/lib/auth/server.ts +38 -8
  55. package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +76 -0
  56. package/src/lib/bootstrap/dynamicLoader.ts +12 -0
  57. package/src/lib/bootstrap/factory.ts +10 -1
  58. package/src/lib/bootstrap/types.ts +3 -1
  59. package/src/lib/cache/maintenance.ts +1 -0
  60. package/src/lib/commands/helpers.ts +6 -2
  61. package/src/lib/crud/__tests__/cache.test.ts +53 -1
  62. package/src/lib/crud/__tests__/errors.test.ts +47 -1
  63. package/src/lib/crud/__tests__/ids.test.ts +31 -1
  64. package/src/lib/crud/__tests__/route-mutation-guard.test.ts +23 -2
  65. package/src/lib/crud/cache.ts +8 -5
  66. package/src/lib/crud/errors.ts +23 -0
  67. package/src/lib/crud/factory.ts +4 -3
  68. package/src/lib/crud/ids.ts +22 -1
  69. package/src/lib/crud/mutation-guard-registry.ts +2 -38
  70. package/src/lib/crud/mutation-guard-service.ts +48 -0
  71. package/src/lib/crud/mutation-guard.ts +1 -17
  72. package/src/lib/crud/types.ts +1 -0
  73. package/src/lib/data/__tests__/engine.event-validation.test.ts +28 -0
  74. package/src/lib/data/engine.ts +9 -0
  75. package/src/lib/db/mikro.ts +12 -0
  76. package/src/lib/di/__tests__/bootstrap-cache.test.ts +54 -0
  77. package/src/lib/di/container.ts +36 -13
  78. package/src/lib/openapi/__tests__/attach-docs.test.ts +105 -0
  79. package/src/lib/openapi/attach-docs.ts +63 -0
  80. package/src/lib/openapi/index.ts +1 -0
  81. package/src/lib/ratelimit/__tests__/config.test.ts +12 -0
  82. package/src/lib/ratelimit/__tests__/helpers.test.ts +2 -2
  83. package/src/lib/ratelimit/__tests__/service.test.ts +1 -1
  84. package/src/lib/ratelimit/config.ts +11 -1
  85. package/src/lib/ratelimit/helpers.ts +9 -5
  86. package/src/lib/ratelimit/index.ts +1 -1
  87. package/src/lib/ratelimit/service.ts +1 -1
  88. package/src/lib/ratelimit/types.ts +1 -1
  89. package/src/modules/entities/validators.ts +2 -1
  90. package/src/modules/entities.ts +9 -1
  91. package/src/modules/payment_gateways/types.ts +3 -0
  92. package/src/modules/perspectives/types.ts +2 -0
  93. package/src/modules/search.ts +1 -1
  94. package/src/modules/workflows/code-registry.ts +54 -0
@@ -1,2 +1,2 @@
1
- [build:shared] found 236 entry points
1
+ [build:shared] found 240 entry points
2
2
  [build:shared] built successfully
package/AGENTS.md CHANGED
@@ -168,6 +168,30 @@ import { hasFeature, hasAllFeatures } from '@open-mercato/shared/security/featur
168
168
  - Use `hasAllFeatures(granted, required)` for arrays such as `features`, `requireFeatures`, or handler guard lists.
169
169
  - MUST NOT gate raw feature arrays with `includes(...)`, `Set.has(...)`, or ad hoc `every(...includes(...))` checks in shared registries or runners; wildcard grants like `module.*` and `*` are part of the RBAC contract.
170
170
 
171
+ ### CRUD HTTP Errors — MUST use the shared helpers instead of hand-rolling `CrudHttpError`
172
+
173
+ `@open-mercato/shared/lib/crud/errors` owns the standardized error shapes. Use the helper, not a raw `new CrudHttpError(...)`:
174
+
175
+ ```typescript
176
+ import { assertFound, notFound, badRequest, forbidden, conflict } from '@open-mercato/shared/lib/crud/errors'
177
+
178
+ const deal = assertFound(await em.findOne(Deal, { id }), translate('customers.errors.deal_not_found', 'Deal not found'))
179
+ ```
180
+
181
+ | Status | Helper |
182
+ |--------|--------|
183
+ | 400 | `badRequest(message)` |
184
+ | 403 | `forbidden(message?)` |
185
+ | 404 | `notFound(message?)` — or `assertFound(value, message)` when guarding a lookup |
186
+ | 409 | `conflict(message)` |
187
+
188
+ MUST rules:
189
+ - MUST NOT hand-roll `throw new CrudHttpError(404, { error: msg })`. Pick the helper that fits the call site:
190
+ - **Inline lookup** → `const deal = assertFound(await em.findOne(Deal, { id }), msg)`. It throws the standardized 404 and returns the value narrowed to `T`, so there is no nullable intermediate binding.
191
+ - **Guard statement** (multi-line lookup, or a compound condition such as `if (!entity || entity.tenantId !== auth.tenantId)`) → `throw notFound(msg)`. TypeScript already narrows the value after the throw, so this keeps tenant-scoping conditions explicit without losing type safety.
192
+ - `message` is passed through verbatim and is never derived from an entity name — keep routing 404 copy through `translate(...)` so it stays translatable.
193
+ - `assertFound` treats every falsy value as missing. Use it for entity/object lookups only, never to guard numbers or strings where `0`/`''` are valid results.
194
+
171
195
  ### CRUD Multi-ID Filtering
172
196
 
173
197
  - Use `parseIdsParam()` and `mergeIdFilter()` from `@open-mercato/shared/lib/crud/ids` for factory-level `ids` query support.
@@ -5,6 +5,13 @@ const TENANT_COOKIE_NAME = "om_selected_tenant";
5
5
  const ORGANIZATION_COOKIE_NAME = "om_selected_org";
6
6
  const ALL_ORGANIZATIONS_COOKIE_VALUE = "__all__";
7
7
  const SUPERADMIN_ROLE = "superadmin";
8
+ class AuthResolutionUnavailableError extends Error {
9
+ constructor(cause) {
10
+ super("Auth resolution unavailable");
11
+ this.name = "AuthResolutionUnavailableError";
12
+ if (cause !== void 0) this.cause = cause;
13
+ }
14
+ }
8
15
  const TRUSTED_AUTH_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("open-mercato.auth.trustedContext");
9
16
  function attachTrustedAuthContext(request, envelope) {
10
17
  ;
@@ -192,8 +199,8 @@ async function resolveCanonicalInteractiveAuthContext(auth) {
192
199
  const container = await createRequestContainer();
193
200
  const em = container.resolve("em");
194
201
  return await resolveCanonicalStaffAuthContext(em, auth);
195
- } catch {
196
- return null;
202
+ } catch (err) {
203
+ throw new AuthResolutionUnavailableError(err);
197
204
  }
198
205
  }
199
206
  async function resolveAuthFromCookiesDetailed() {
@@ -212,7 +219,8 @@ async function resolveAuthFromCookiesDetailed() {
212
219
  auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),
213
220
  status: "authenticated"
214
221
  };
215
- } catch {
222
+ } catch (err) {
223
+ if (err instanceof AuthResolutionUnavailableError) return { auth: null, status: "error" };
216
224
  return { auth: null, status: "invalid" };
217
225
  }
218
226
  }
@@ -233,6 +241,7 @@ async function resolveAuthFromRequestDetailed(req) {
233
241
  const authHeader = (req.headers.get("authorization") || "").trim();
234
242
  let token;
235
243
  let hadInvalidInteractiveToken = false;
244
+ let hadUnavailableResolution = false;
236
245
  if (authHeader.toLowerCase().startsWith("bearer ")) token = authHeader.slice(7).trim();
237
246
  if (!token) {
238
247
  const match = cookieHeader.match(/(?:^|;\s*)auth_token=([^;]+)/);
@@ -252,17 +261,19 @@ async function resolveAuthFromRequestDetailed(req) {
252
261
  }
253
262
  hadInvalidInteractiveToken = true;
254
263
  }
255
- } catch {
256
- hadInvalidInteractiveToken = true;
264
+ } catch (err) {
265
+ if (err instanceof AuthResolutionUnavailableError) hadUnavailableResolution = true;
266
+ else hadInvalidInteractiveToken = true;
257
267
  }
258
268
  }
269
+ const resolveUnauthenticatedStatus = () => hadUnavailableResolution ? "error" : hadInvalidInteractiveToken ? "invalid" : "missing";
259
270
  const apiKey = extractApiKey(req);
260
271
  if (!apiKey) {
261
- return { auth: null, status: hadInvalidInteractiveToken ? "invalid" : "missing" };
272
+ return { auth: null, status: resolveUnauthenticatedStatus() };
262
273
  }
263
274
  const apiAuth = await resolveApiKeyAuth(apiKey);
264
275
  if (!apiAuth) {
265
- return { auth: null, status: hadInvalidInteractiveToken ? "invalid" : "missing" };
276
+ return { auth: null, status: resolveUnauthenticatedStatus() };
266
277
  }
267
278
  return {
268
279
  auth: applySuperAdminScope(apiAuth, tenantCookie, orgCookie),
@@ -273,6 +284,7 @@ async function getAuthFromRequest(req) {
273
284
  return (await resolveAuthFromRequestDetailed(req)).auth;
274
285
  }
275
286
  export {
287
+ AuthResolutionUnavailableError,
276
288
  TRUSTED_AUTH_CONTEXT_SYMBOL,
277
289
  attachTrustedAuthContext,
278
290
  getAuthFromCookies,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/auth/server.ts"],
4
- "sourcesContent": ["import { cookies } from 'next/headers.js'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { verifyJwt } from './jwt'\nimport { getSharedApiKeyAuthCache } from './apiKeyAuthCache'\n\nconst TENANT_COOKIE_NAME = 'om_selected_tenant'\nconst ORGANIZATION_COOKIE_NAME = 'om_selected_org'\nconst ALL_ORGANIZATIONS_COOKIE_VALUE = '__all__'\nconst SUPERADMIN_ROLE = 'superadmin'\n\nexport type AuthContext = {\n sub: string\n sid?: string | null\n tenantId: string | null\n orgId: string | null\n email?: string\n roles?: string[]\n isApiKey?: boolean\n userId?: string\n keyId?: string\n keyName?: string\n [k: string]: unknown\n} | null\n\ntype CookieOverride = { applied: boolean; value: string | null }\ntype AuthResolutionStatus = 'authenticated' | 'missing' | 'invalid'\ntype AuthResolution = {\n auth: AuthContext\n status: AuthResolutionStatus\n}\n\n// Symbol-keyed trusted auth context. Set on synthetic Request objects by\n// callers that have already authenticated (e.g. the AI in-process operation\n// runner) so downstream auth resolution short-circuits without re-running\n// cookie/JWT/API-key parsing. The hook is fail-open: if absent the normal\n// resolution path runs unchanged.\nexport const TRUSTED_AUTH_CONTEXT_SYMBOL = Symbol.for('open-mercato.auth.trustedContext')\n\nexport type TrustedAuthContextEnvelope = {\n auth: AuthContext\n status?: AuthResolutionStatus\n}\n\nexport function attachTrustedAuthContext(\n request: Request,\n envelope: TrustedAuthContextEnvelope\n): Request {\n ;(request as unknown as Record<symbol, TrustedAuthContextEnvelope>)[TRUSTED_AUTH_CONTEXT_SYMBOL] = envelope\n return request\n}\n\nfunction readTrustedAuthContext(request: Request): TrustedAuthContextEnvelope | null {\n const carrier = request as unknown as Record<symbol, unknown>\n const envelope = carrier[TRUSTED_AUTH_CONTEXT_SYMBOL]\n if (!envelope || typeof envelope !== 'object') return null\n const candidate = envelope as TrustedAuthContextEnvelope\n if (!('auth' in candidate)) return null\n return candidate\n}\n\nfunction decodeCookieValue(raw: string | undefined): string | null {\n if (raw === undefined) return null\n try {\n const decoded = decodeURIComponent(raw)\n return decoded ?? null\n } catch {\n return raw ?? null\n }\n}\n\nfunction readCookieFromHeader(header: string | null | undefined, name: string): string | undefined {\n if (!header) return undefined\n const parts = header.split(';')\n for (const part of parts) {\n const trimmed = part.trim()\n if (trimmed.startsWith(`${name}=`)) {\n return trimmed.slice(name.length + 1)\n }\n }\n return undefined\n}\n\nfunction resolveTenantOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded) return { applied: true, value: null }\n const trimmed = decoded.trim()\n if (!trimmed) return { applied: true, value: null }\n return { applied: true, value: trimmed }\n}\n\nfunction resolveOrganizationOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded || decoded === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n const trimmed = decoded.trim()\n if (!trimmed || trimmed === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n return { applied: true, value: trimmed }\n}\n\nfunction isSuperAdminAuth(auth: AuthContext | null | undefined): boolean {\n if (!auth) return false\n return (auth as Record<string, unknown>).isSuperAdmin === true\n}\n\nfunction applySuperAdminScope(\n auth: AuthContext,\n tenantCookie: string | undefined,\n orgCookie: string | undefined\n): AuthContext {\n if (!auth || !isSuperAdminAuth(auth)) return auth\n\n const tenantOverride = resolveTenantOverride(tenantCookie)\n const orgOverride = resolveOrganizationOverride(orgCookie)\n if (!tenantOverride.applied && !orgOverride.applied) return auth\n\n type MutableAuthContext = Exclude<AuthContext, null> & {\n actorTenantId?: string | null\n actorOrgId?: string | null\n }\n const baseAuth = auth as Exclude<AuthContext, null>\n const next: MutableAuthContext = { ...baseAuth }\n if (tenantOverride.applied) {\n if (!('actorTenantId' in next)) next.actorTenantId = auth?.tenantId ?? null\n next.tenantId = tenantOverride.value\n }\n if (orgOverride.applied) {\n if (!('actorOrgId' in next)) next.actorOrgId = auth?.orgId ?? null\n next.orgId = orgOverride.value\n }\n next.isSuperAdmin = true\n const existingRoles = Array.isArray(next.roles) ? next.roles : []\n if (!existingRoles.some((role) => typeof role === 'string' && role.trim().toLowerCase() === SUPERADMIN_ROLE)) {\n next.roles = [...existingRoles, 'superadmin']\n }\n return next\n}\n\nasync function resolveApiKeyAuth(secret: string): Promise<AuthContext> {\n if (!secret) return null\n const cache = getSharedApiKeyAuthCache()\n const cached = cache.get(secret)\n if (cached !== undefined) return cached as AuthContext\n try {\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const { findApiKeyBySecret } = await import('@open-mercato/core/modules/api_keys/services/apiKeyService')\n const { Role, RoleAcl, User } = await import('@open-mercato/core/modules/auth/data/entities')\n const { Organization, Tenant } = await import('@open-mercato/core/modules/directory/data/entities')\n\n const record = await findApiKeyBySecret(em, secret)\n if (!record) {\n cache.setMiss(secret)\n return null\n }\n\n const roleIds = Array.isArray(record.rolesJson)\n ? record.rolesJson.filter((value): value is string => typeof value === 'string' && value.length > 0)\n : []\n const roles = roleIds.length\n ? await em.find(Role, { id: { $in: roleIds }, deletedAt: null })\n : []\n const roleNames = roles.map((role) => role.name).filter((name): name is string => typeof name === 'string' && name.length > 0)\n\n let keyIsSuperAdmin = false\n if (roleIds.length) {\n const superAcl = await em.findOne(\n RoleAcl,\n { role: { $in: roleIds } as any, isSuperAdmin: true, deletedAt: null } as any,\n )\n keyIsSuperAdmin = !!(superAcl && (superAcl as { isSuperAdmin?: boolean }).isSuperAdmin)\n }\n\n if (cache.shouldWriteLastUsed(record.id)) {\n try {\n record.lastUsedAt = new Date()\n await em.persist(record).flush()\n } catch {\n // best-effort update; ignore write failures\n }\n }\n\n // For session keys, use sessionUserId; for regular keys, use createdBy\n const actualUserId = record.sessionUserId ?? record.createdBy ?? null\n\n if (actualUserId) {\n const user = await em.findOne(User, { id: actualUserId, deletedAt: null })\n if (!user) {\n cache.setMiss(secret)\n return null\n }\n if ((user.tenantId ?? null) !== (record.tenantId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n if ((user.organizationId ?? null) !== (record.organizationId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n } else {\n if (record.tenantId) {\n const tenant = await em.findOne(Tenant, { id: record.tenantId, deletedAt: null, isActive: true })\n if (!tenant) {\n cache.setMiss(secret)\n return null\n }\n }\n if (record.organizationId) {\n const organization = await em.findOne(Organization, { id: record.organizationId, deletedAt: null, isActive: true })\n if (!organization) {\n cache.setMiss(secret)\n return null\n }\n if (record.tenantId && String(organization.tenant.id) !== record.tenantId) {\n cache.setMiss(secret)\n return null\n }\n }\n }\n\n const auth: Exclude<AuthContext, null> = {\n sub: `api_key:${record.id}`,\n tenantId: record.tenantId ?? null,\n orgId: record.organizationId ?? null,\n roles: roleNames,\n isApiKey: true,\n isSuperAdmin: keyIsSuperAdmin,\n keyId: record.id,\n keyName: record.name,\n ...(actualUserId ? { userId: actualUserId } : {}),\n }\n cache.setSuccess(secret, auth, record.expiresAt ? record.expiresAt.getTime() : null)\n return auth\n } catch {\n return null\n }\n}\n\nfunction extractApiKey(req: Request): string | null {\n const header = (req.headers.get('x-api-key') || '').trim()\n if (header) return header\n const authHeader = (req.headers.get('authorization') || '').trim()\n if (authHeader.toLowerCase().startsWith('apikey ')) {\n return authHeader.slice(7).trim()\n }\n return null\n}\n\nasync function resolveCanonicalInteractiveAuthContext(auth: AuthContext): Promise<AuthContext> {\n if (!auth || auth.isApiKey) return auth\n if (typeof auth.sub !== 'string' || auth.sub.trim().length === 0) return null\n\n try {\n const [{ createRequestContainer }, { resolveCanonicalStaffAuthContext }] = await Promise.all([\n import('@open-mercato/shared/lib/di/container'),\n import('@open-mercato/core/modules/auth/lib/sessionIntegrity'),\n ])\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n return await resolveCanonicalStaffAuthContext(em, auth)\n } catch {\n return null\n }\n}\n\nexport async function resolveAuthFromCookiesDetailed(): Promise<AuthResolution> {\n const cookieStore = await cookies()\n const token = cookieStore.get('auth_token')?.value\n if (!token) return { auth: null, status: 'missing' }\n try {\n const payload = verifyJwt(token) as AuthContext\n if (!payload) return { auth: null, status: 'invalid' }\n if (payload.type === 'customer') return { auth: null, status: 'invalid' }\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (!canonicalAuth) return { auth: null, status: 'invalid' }\n const tenantCookie = cookieStore.get(TENANT_COOKIE_NAME)?.value\n const orgCookie = cookieStore.get(ORGANIZATION_COOKIE_NAME)?.value\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n } catch {\n return { auth: null, status: 'invalid' }\n }\n}\n\nexport async function getAuthFromCookies(): Promise<AuthContext> {\n return (await resolveAuthFromCookiesDetailed()).auth\n}\n\nexport async function resolveAuthFromRequestDetailed(req: Request): Promise<AuthResolution> {\n const trusted = readTrustedAuthContext(req)\n if (trusted) {\n return {\n auth: trusted.auth,\n status: trusted.status ?? (trusted.auth ? 'authenticated' : 'missing'),\n }\n }\n const cookieHeader = req.headers.get('cookie') || ''\n const tenantCookie = readCookieFromHeader(cookieHeader, TENANT_COOKIE_NAME)\n const orgCookie = readCookieFromHeader(cookieHeader, ORGANIZATION_COOKIE_NAME)\n const authHeader = (req.headers.get('authorization') || '').trim()\n let token: string | undefined\n let hadInvalidInteractiveToken = false\n if (authHeader.toLowerCase().startsWith('bearer ')) token = authHeader.slice(7).trim()\n if (!token) {\n const match = cookieHeader.match(/(?:^|;\\s*)auth_token=([^;]+)/)\n if (match) token = decodeURIComponent(match[1])\n }\n if (token) {\n try {\n const payload = verifyJwt(token) as AuthContext\n if (payload && payload.type === 'customer') return { auth: null, status: 'invalid' }\n if (payload) {\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (canonicalAuth) {\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n }\n hadInvalidInteractiveToken = true\n }\n } catch {\n hadInvalidInteractiveToken = true\n }\n }\n\n const apiKey = extractApiKey(req)\n if (!apiKey) {\n return { auth: null, status: hadInvalidInteractiveToken ? 'invalid' : 'missing' }\n }\n const apiAuth = await resolveApiKeyAuth(apiKey)\n if (!apiAuth) {\n return { auth: null, status: hadInvalidInteractiveToken ? 'invalid' : 'missing' }\n }\n return {\n auth: applySuperAdminScope(apiAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n}\n\nexport async function getAuthFromRequest(req: Request): Promise<AuthContext> {\n return (await resolveAuthFromRequestDetailed(req)).auth\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { cookies } from 'next/headers.js'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { verifyJwt } from './jwt'\nimport { getSharedApiKeyAuthCache } from './apiKeyAuthCache'\n\nconst TENANT_COOKIE_NAME = 'om_selected_tenant'\nconst ORGANIZATION_COOKIE_NAME = 'om_selected_org'\nconst ALL_ORGANIZATIONS_COOKIE_VALUE = '__all__'\nconst SUPERADMIN_ROLE = 'superadmin'\n\nexport type AuthContext = {\n sub: string\n sid?: string | null\n tenantId: string | null\n orgId: string | null\n email?: string\n roles?: string[]\n isApiKey?: boolean\n userId?: string\n keyId?: string\n keyName?: string\n [k: string]: unknown\n} | null\n\ntype CookieOverride = { applied: boolean; value: string | null }\n// 'error' means auth could not be determined because of a transient/unexpected\n// failure (DB unavailable, connection-pool exhaustion, statement timeout, \u2026) \u2014\n// NOT because the token is genuinely invalid. Callers MUST NOT clear the user's\n// session cookies for 'error'; doing so would log everyone out at once during a\n// shared infrastructure blip (issue #4176).\ntype AuthResolutionStatus = 'authenticated' | 'missing' | 'invalid' | 'error'\ntype AuthResolution = {\n auth: AuthContext\n status: AuthResolutionStatus\n}\n\n// Thrown by `resolveCanonicalInteractiveAuthContext` when the canonical auth\n// lookup itself fails unexpectedly (as opposed to resolving to a genuine\n// \"not authorized\" null). Kept distinct so resolvers can map it to the\n// non-destructive 'error' status instead of the cookie-clearing 'invalid'.\nexport class AuthResolutionUnavailableError extends Error {\n constructor(cause?: unknown) {\n super('Auth resolution unavailable')\n this.name = 'AuthResolutionUnavailableError'\n if (cause !== undefined) (this as { cause?: unknown }).cause = cause\n }\n}\n\n// Symbol-keyed trusted auth context. Set on synthetic Request objects by\n// callers that have already authenticated (e.g. the AI in-process operation\n// runner) so downstream auth resolution short-circuits without re-running\n// cookie/JWT/API-key parsing. The hook is fail-open: if absent the normal\n// resolution path runs unchanged.\nexport const TRUSTED_AUTH_CONTEXT_SYMBOL = Symbol.for('open-mercato.auth.trustedContext')\n\nexport type TrustedAuthContextEnvelope = {\n auth: AuthContext\n status?: AuthResolutionStatus\n}\n\nexport function attachTrustedAuthContext(\n request: Request,\n envelope: TrustedAuthContextEnvelope\n): Request {\n ;(request as unknown as Record<symbol, TrustedAuthContextEnvelope>)[TRUSTED_AUTH_CONTEXT_SYMBOL] = envelope\n return request\n}\n\nfunction readTrustedAuthContext(request: Request): TrustedAuthContextEnvelope | null {\n const carrier = request as unknown as Record<symbol, unknown>\n const envelope = carrier[TRUSTED_AUTH_CONTEXT_SYMBOL]\n if (!envelope || typeof envelope !== 'object') return null\n const candidate = envelope as TrustedAuthContextEnvelope\n if (!('auth' in candidate)) return null\n return candidate\n}\n\nfunction decodeCookieValue(raw: string | undefined): string | null {\n if (raw === undefined) return null\n try {\n const decoded = decodeURIComponent(raw)\n return decoded ?? null\n } catch {\n return raw ?? null\n }\n}\n\nfunction readCookieFromHeader(header: string | null | undefined, name: string): string | undefined {\n if (!header) return undefined\n const parts = header.split(';')\n for (const part of parts) {\n const trimmed = part.trim()\n if (trimmed.startsWith(`${name}=`)) {\n return trimmed.slice(name.length + 1)\n }\n }\n return undefined\n}\n\nfunction resolveTenantOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded) return { applied: true, value: null }\n const trimmed = decoded.trim()\n if (!trimmed) return { applied: true, value: null }\n return { applied: true, value: trimmed }\n}\n\nfunction resolveOrganizationOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded || decoded === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n const trimmed = decoded.trim()\n if (!trimmed || trimmed === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n return { applied: true, value: trimmed }\n}\n\nfunction isSuperAdminAuth(auth: AuthContext | null | undefined): boolean {\n if (!auth) return false\n return (auth as Record<string, unknown>).isSuperAdmin === true\n}\n\nfunction applySuperAdminScope(\n auth: AuthContext,\n tenantCookie: string | undefined,\n orgCookie: string | undefined\n): AuthContext {\n if (!auth || !isSuperAdminAuth(auth)) return auth\n\n const tenantOverride = resolveTenantOverride(tenantCookie)\n const orgOverride = resolveOrganizationOverride(orgCookie)\n if (!tenantOverride.applied && !orgOverride.applied) return auth\n\n type MutableAuthContext = Exclude<AuthContext, null> & {\n actorTenantId?: string | null\n actorOrgId?: string | null\n }\n const baseAuth = auth as Exclude<AuthContext, null>\n const next: MutableAuthContext = { ...baseAuth }\n if (tenantOverride.applied) {\n if (!('actorTenantId' in next)) next.actorTenantId = auth?.tenantId ?? null\n next.tenantId = tenantOverride.value\n }\n if (orgOverride.applied) {\n if (!('actorOrgId' in next)) next.actorOrgId = auth?.orgId ?? null\n next.orgId = orgOverride.value\n }\n next.isSuperAdmin = true\n const existingRoles = Array.isArray(next.roles) ? next.roles : []\n if (!existingRoles.some((role) => typeof role === 'string' && role.trim().toLowerCase() === SUPERADMIN_ROLE)) {\n next.roles = [...existingRoles, 'superadmin']\n }\n return next\n}\n\nasync function resolveApiKeyAuth(secret: string): Promise<AuthContext> {\n if (!secret) return null\n const cache = getSharedApiKeyAuthCache()\n const cached = cache.get(secret)\n if (cached !== undefined) return cached as AuthContext\n try {\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const { findApiKeyBySecret } = await import('@open-mercato/core/modules/api_keys/services/apiKeyService')\n const { Role, RoleAcl, User } = await import('@open-mercato/core/modules/auth/data/entities')\n const { Organization, Tenant } = await import('@open-mercato/core/modules/directory/data/entities')\n\n const record = await findApiKeyBySecret(em, secret)\n if (!record) {\n cache.setMiss(secret)\n return null\n }\n\n const roleIds = Array.isArray(record.rolesJson)\n ? record.rolesJson.filter((value): value is string => typeof value === 'string' && value.length > 0)\n : []\n const roles = roleIds.length\n ? await em.find(Role, { id: { $in: roleIds }, deletedAt: null })\n : []\n const roleNames = roles.map((role) => role.name).filter((name): name is string => typeof name === 'string' && name.length > 0)\n\n let keyIsSuperAdmin = false\n if (roleIds.length) {\n const superAcl = await em.findOne(\n RoleAcl,\n { role: { $in: roleIds } as any, isSuperAdmin: true, deletedAt: null } as any,\n )\n keyIsSuperAdmin = !!(superAcl && (superAcl as { isSuperAdmin?: boolean }).isSuperAdmin)\n }\n\n if (cache.shouldWriteLastUsed(record.id)) {\n try {\n record.lastUsedAt = new Date()\n await em.persist(record).flush()\n } catch {\n // best-effort update; ignore write failures\n }\n }\n\n // For session keys, use sessionUserId; for regular keys, use createdBy\n const actualUserId = record.sessionUserId ?? record.createdBy ?? null\n\n if (actualUserId) {\n const user = await em.findOne(User, { id: actualUserId, deletedAt: null })\n if (!user) {\n cache.setMiss(secret)\n return null\n }\n if ((user.tenantId ?? null) !== (record.tenantId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n if ((user.organizationId ?? null) !== (record.organizationId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n } else {\n if (record.tenantId) {\n const tenant = await em.findOne(Tenant, { id: record.tenantId, deletedAt: null, isActive: true })\n if (!tenant) {\n cache.setMiss(secret)\n return null\n }\n }\n if (record.organizationId) {\n const organization = await em.findOne(Organization, { id: record.organizationId, deletedAt: null, isActive: true })\n if (!organization) {\n cache.setMiss(secret)\n return null\n }\n if (record.tenantId && String(organization.tenant.id) !== record.tenantId) {\n cache.setMiss(secret)\n return null\n }\n }\n }\n\n const auth: Exclude<AuthContext, null> = {\n sub: `api_key:${record.id}`,\n tenantId: record.tenantId ?? null,\n orgId: record.organizationId ?? null,\n roles: roleNames,\n isApiKey: true,\n isSuperAdmin: keyIsSuperAdmin,\n keyId: record.id,\n keyName: record.name,\n ...(actualUserId ? { userId: actualUserId } : {}),\n }\n cache.setSuccess(secret, auth, record.expiresAt ? record.expiresAt.getTime() : null)\n return auth\n } catch {\n return null\n }\n}\n\nfunction extractApiKey(req: Request): string | null {\n const header = (req.headers.get('x-api-key') || '').trim()\n if (header) return header\n const authHeader = (req.headers.get('authorization') || '').trim()\n if (authHeader.toLowerCase().startsWith('apikey ')) {\n return authHeader.slice(7).trim()\n }\n return null\n}\n\nasync function resolveCanonicalInteractiveAuthContext(auth: AuthContext): Promise<AuthContext> {\n if (!auth || auth.isApiKey) return auth\n if (typeof auth.sub !== 'string' || auth.sub.trim().length === 0) return null\n\n try {\n const [{ createRequestContainer }, { resolveCanonicalStaffAuthContext }] = await Promise.all([\n import('@open-mercato/shared/lib/di/container'),\n import('@open-mercato/core/modules/auth/lib/sessionIntegrity'),\n ])\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n return await resolveCanonicalStaffAuthContext(em, auth)\n } catch (err) {\n // A thrown error here means we could not check the token against the\n // database (DB down, pool exhausted, timeout). This is NOT the same as the\n // token being invalid \u2014 surface it so callers keep the session intact and\n // fail the request transiently instead of force-logging the user out.\n throw new AuthResolutionUnavailableError(err)\n }\n}\n\nexport async function resolveAuthFromCookiesDetailed(): Promise<AuthResolution> {\n const cookieStore = await cookies()\n const token = cookieStore.get('auth_token')?.value\n if (!token) return { auth: null, status: 'missing' }\n try {\n const payload = verifyJwt(token) as AuthContext\n if (!payload) return { auth: null, status: 'invalid' }\n if (payload.type === 'customer') return { auth: null, status: 'invalid' }\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (!canonicalAuth) return { auth: null, status: 'invalid' }\n const tenantCookie = cookieStore.get(TENANT_COOKIE_NAME)?.value\n const orgCookie = cookieStore.get(ORGANIZATION_COOKIE_NAME)?.value\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n } catch (err) {\n if (err instanceof AuthResolutionUnavailableError) return { auth: null, status: 'error' }\n return { auth: null, status: 'invalid' }\n }\n}\n\nexport async function getAuthFromCookies(): Promise<AuthContext> {\n return (await resolveAuthFromCookiesDetailed()).auth\n}\n\nexport async function resolveAuthFromRequestDetailed(req: Request): Promise<AuthResolution> {\n const trusted = readTrustedAuthContext(req)\n if (trusted) {\n return {\n auth: trusted.auth,\n status: trusted.status ?? (trusted.auth ? 'authenticated' : 'missing'),\n }\n }\n const cookieHeader = req.headers.get('cookie') || ''\n const tenantCookie = readCookieFromHeader(cookieHeader, TENANT_COOKIE_NAME)\n const orgCookie = readCookieFromHeader(cookieHeader, ORGANIZATION_COOKIE_NAME)\n const authHeader = (req.headers.get('authorization') || '').trim()\n let token: string | undefined\n let hadInvalidInteractiveToken = false\n let hadUnavailableResolution = false\n if (authHeader.toLowerCase().startsWith('bearer ')) token = authHeader.slice(7).trim()\n if (!token) {\n const match = cookieHeader.match(/(?:^|;\\s*)auth_token=([^;]+)/)\n if (match) token = decodeURIComponent(match[1])\n }\n if (token) {\n try {\n const payload = verifyJwt(token) as AuthContext\n if (payload && payload.type === 'customer') return { auth: null, status: 'invalid' }\n if (payload) {\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (canonicalAuth) {\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n }\n hadInvalidInteractiveToken = true\n }\n } catch (err) {\n // Transient/unexpected canonical-resolution failures must not invalidate\n // the token \u2014 surface them so we return 'error' (retryable) rather than\n // clearing the session cookies.\n if (err instanceof AuthResolutionUnavailableError) hadUnavailableResolution = true\n else hadInvalidInteractiveToken = true\n }\n }\n\n const resolveUnauthenticatedStatus = (): AuthResolutionStatus =>\n hadUnavailableResolution ? 'error' : hadInvalidInteractiveToken ? 'invalid' : 'missing'\n\n const apiKey = extractApiKey(req)\n if (!apiKey) {\n return { auth: null, status: resolveUnauthenticatedStatus() }\n }\n const apiAuth = await resolveApiKeyAuth(apiKey)\n if (!apiAuth) {\n return { auth: null, status: resolveUnauthenticatedStatus() }\n }\n return {\n auth: applySuperAdminScope(apiAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n}\n\nexport async function getAuthFromRequest(req: Request): Promise<AuthContext> {\n return (await resolveAuthFromRequestDetailed(req)).auth\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -93,13 +93,17 @@ async function loadBootstrapData(appRoot) {
93
93
  entitiesModule,
94
94
  diModule,
95
95
  searchModule,
96
- commandLoadersModule
96
+ commandLoadersModule,
97
+ commandInterceptorsModule,
98
+ workflowsModule
97
99
  ] = await Promise.all([
98
100
  compileAndImport(path.join(generatedDir, "modules.cli.generated.ts")),
99
101
  compileAndImport(path.join(generatedDir, "entities.generated.ts")),
100
102
  compileAndImport(path.join(generatedDir, "di.generated.ts")),
101
103
  compileAndImport(path.join(generatedDir, "search.generated.ts")).catch(() => ({ searchModuleConfigs: [] })),
102
- compileAndImport(path.join(generatedDir, "command-loaders.generated.ts")).catch(() => ({ commandLoaderEntries: [] }))
104
+ compileAndImport(path.join(generatedDir, "command-loaders.generated.ts")).catch(() => ({ commandLoaderEntries: [] })),
105
+ compileAndImport(path.join(generatedDir, "command-interceptors.generated.ts")).catch(() => ({ commandInterceptorEntries: [] })),
106
+ compileAndImport(path.join(generatedDir, "workflows.generated.ts")).catch(() => ({ allCodeWorkflows: [] }))
103
107
  ]);
104
108
  return {
105
109
  modules: modulesModule.modules,
@@ -109,6 +113,13 @@ async function loadBootstrapData(appRoot) {
109
113
  // Search configs are needed by workers for indexing
110
114
  searchModuleConfigs: searchModule.searchModuleConfigs ?? [],
111
115
  commandLoaderEntries: commandLoadersModule.commandLoaderEntries ?? [],
116
+ // Command interceptors must apply in worker/CLI processes too — the
117
+ // interceptor registry is per-process, so relying on the Next.js runtime's
118
+ // registration silently no-ops every interceptor for queued/CLI commands
119
+ // (#4327).
120
+ commandInterceptorEntries: commandInterceptorsModule.commandInterceptorEntries ?? [],
121
+ // Code workflow definitions are needed by workers to resume code-defined instances
122
+ codeWorkflows: workflowsModule.allCodeWorkflows ?? [],
112
123
  // Empty UI-related data - not needed for CLI
113
124
  dashboardWidgetEntries: [],
114
125
  injectionWidgetEntries: [],
@@ -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 {\n ensureMikroOrmV7GeneratedCacheCompatibility,\n recoverMikroOrmV7GeneratedCacheFromImportError,\n} from './generatedCacheRecovery'\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport { pathToFileURL } from 'node:url'\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\n // Check if we need to recompile (source newer than compiled)\n const tsExists = fs.existsSync(tsPath)\n const jsExists = fs.existsSync(jsPath)\n\n if (!tsExists) {\n throw new Error(`Generated file not found: ${tsPath}`)\n }\n\n const needsCompile = !jsExists ||\n fs.statSync(tsPath).mtimeMs > fs.statSync(jsPath).mtimeMs\n\n if (needsCompile) {\n // Dynamically import esbuild only when needed\n const esbuild = await import('esbuild')\n\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 // Use esbuild.build with bundling to handle JSON imports\n await esbuild.build({\n entryPoints: [tsPath],\n outfile: jsPath,\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n plugins: [aliasPlugin, externalNonJsonPlugin],\n // Allow JSON imports\n loader: { '.json': 'json' },\n })\n }\n\n // Import the compiled JavaScript\n try {\n const fileUrl = `${pathToFileURL(jsPath).href}?mtime=${fs.statSync(jsPath).mtimeMs}`\n return 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 * 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 ] = 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 compileAndImport(path.join(generatedDir, 'search.generated.ts')).catch(() => ({ searchModuleConfigs: [] })),\n compileAndImport(path.join(generatedDir, 'command-loaders.generated.ts')).catch(() => ({ commandLoaderEntries: [] })),\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 // 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 { findAppRoot, type AppRoot } from './appResolver'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport {\n ensureMikroOrmV7GeneratedCacheCompatibility,\n recoverMikroOrmV7GeneratedCacheFromImportError,\n} from './generatedCacheRecovery'\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport { pathToFileURL } from 'node:url'\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\n // Check if we need to recompile (source newer than compiled)\n const tsExists = fs.existsSync(tsPath)\n const jsExists = fs.existsSync(jsPath)\n\n if (!tsExists) {\n throw new Error(`Generated file not found: ${tsPath}`)\n }\n\n const needsCompile = !jsExists ||\n fs.statSync(tsPath).mtimeMs > fs.statSync(jsPath).mtimeMs\n\n if (needsCompile) {\n // Dynamically import esbuild only when needed\n const esbuild = await import('esbuild')\n\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 // Use esbuild.build with bundling to handle JSON imports\n await esbuild.build({\n entryPoints: [tsPath],\n outfile: jsPath,\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n plugins: [aliasPlugin, externalNonJsonPlugin],\n // Allow JSON imports\n loader: { '.json': 'json' },\n })\n }\n\n // Import the compiled JavaScript\n try {\n const fileUrl = `${pathToFileURL(jsPath).href}?mtime=${fs.statSync(jsPath).mtimeMs}`\n return 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 * 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 compileAndImport(path.join(generatedDir, 'search.generated.ts')).catch(() => ({ searchModuleConfigs: [] })),\n compileAndImport(path.join(generatedDir, 'command-loaders.generated.ts')).catch(() => ({ commandLoaderEntries: [] })),\n compileAndImport(path.join(generatedDir, 'command-interceptors.generated.ts')).catch(() => ({ commandInterceptorEntries: [] })),\n compileAndImport(path.join(generatedDir, 'workflows.generated.ts')).catch(() => ({ 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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",
6
6
  "names": []
7
7
  }
@@ -1,10 +1,11 @@
1
1
  import { registerOrmEntities } from "../db/mikro.js";
2
- import { registerDiRegistrars } from "../di/container.js";
2
+ import { registerAppDiRegistrar, registerDiRegistrars } from "../di/container.js";
3
3
  import { registerModules } from "../modules/registry.js";
4
4
  import { registerEntityIds } from "../encryption/entityIds.js";
5
5
  import { registerEntityFields } from "../encryption/entityFields.js";
6
6
  import { registerSearchModuleConfigs } from "../../modules/search.js";
7
7
  import { registerAnalyticsModuleConfigs } from "../../modules/analytics.js";
8
+ import { registerCodeWorkflowEntries } from "../../modules/workflows/code-registry.js";
8
9
  import { registerResponseEnrichers } from "../crud/enricher-registry.js";
9
10
  import { registerApiInterceptors } from "../crud/interceptor-registry.js";
10
11
  import { registerComponentOverrides } from "../../modules/widgets/component-registry.js";
@@ -18,6 +19,9 @@ let _bootstrapped = false;
18
19
  let _asyncRegistrationPromise = null;
19
20
  function createBootstrap(data, options = {}) {
20
21
  return function bootstrap() {
22
+ if (options.appDiRegistrar) {
23
+ registerAppDiRegistrar(options.appDiRegistrar);
24
+ }
21
25
  if (_bootstrapped && process.env.NODE_ENV !== "development") return;
22
26
  _bootstrapped = true;
23
27
  registerOrmEntities(data.entities);
@@ -42,6 +46,9 @@ function createBootstrap(data, options = {}) {
42
46
  if (data.analyticsModuleConfigs) {
43
47
  registerAnalyticsModuleConfigs(data.analyticsModuleConfigs);
44
48
  }
49
+ if (data.codeWorkflows?.length) {
50
+ registerCodeWorkflowEntries(data.codeWorkflows);
51
+ }
45
52
  if (data.enricherEntries) {
46
53
  registerResponseEnrichers(data.enricherEntries);
47
54
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/bootstrap/factory.ts"],
4
- "sourcesContent": ["import type { BootstrapData, BootstrapOptions } from './types'\nimport { registerOrmEntities } from '../db/mikro'\nimport { registerDiRegistrars } from '../di/container'\nimport { registerModules } from '../modules/registry'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport { registerEntityFields } from '../encryption/entityFields'\nimport { registerSearchModuleConfigs } from '../../modules/search'\nimport { registerAnalyticsModuleConfigs } from '../../modules/analytics'\nimport { registerResponseEnrichers } from '../crud/enricher-registry'\nimport { registerApiInterceptors } from '../crud/interceptor-registry'\nimport { registerComponentOverrides } from '../../modules/widgets/component-registry'\nimport { registerMutationGuards } from '../crud/mutation-guard-store'\nimport { registerCommandInterceptors } from '../commands/command-interceptor-store'\nimport { registerCommandLoaders } from '../commands/registry'\nimport { registerNotificationHandlers } from '../notifications/handler-registry'\nimport { clearRegisteredIntegrations, registerBundles, registerIntegrations } from '../../modules/integrations/types'\nimport { applyComponentOverridesToEntries } from '../../modules/overrides'\n\nlet _bootstrapped = false\n\n// Store the async registration promise so callers can await it if needed\nlet _asyncRegistrationPromise: Promise<void> | null = null\n\n/**\n * Creates a bootstrap function that registers all application dependencies.\n *\n * The returned function should be called once at application startup.\n * In development mode, it can be called multiple times (for HMR).\n *\n * @param data - All generated registry data from .mercato/generated/\n * @param options - Optional configuration\n * @returns A bootstrap function to call at app startup\n */\nexport function createBootstrap(data: BootstrapData, options: BootstrapOptions = {}) {\n return function bootstrap(): void {\n // In development, always re-run registrations to handle HMR\n // (Module state may be reset when Turbopack reloads packages)\n if (_bootstrapped && process.env.NODE_ENV !== 'development') return\n _bootstrapped = true\n\n // === 1. Foundation: ORM entities and DI registrars ===\n registerOrmEntities(data.entities)\n registerDiRegistrars(data.diRegistrars.filter((r): r is NonNullable<typeof r> => r != null))\n\n // === 2. Modules registry (required by i18n, query engine, dashboards, CLI) ===\n registerModules(data.modules)\n clearRegisteredIntegrations()\n for (const module of data.modules) {\n if (module.integrations?.length) {\n registerIntegrations(module.integrations)\n }\n if (module.bundles?.length) {\n registerBundles(module.bundles)\n }\n }\n\n // === 3. Entity IDs (required by encryption, indexing, entity links) ===\n registerEntityIds(data.entityIds)\n\n // === 4. Entity fields registry (for encryption manager, Turbopack compatibility) ===\n if (data.entityFieldsRegistry) {\n registerEntityFields(data.entityFieldsRegistry)\n }\n\n // === 5. Search module configs (for search service registration in DI) ===\n if (data.searchModuleConfigs) {\n registerSearchModuleConfigs(data.searchModuleConfigs)\n }\n\n // === 6. Analytics module configs (for dashboard widgets and analytics API) ===\n if (data.analyticsModuleConfigs) {\n registerAnalyticsModuleConfigs(data.analyticsModuleConfigs)\n }\n\n // === 6b. Response enrichers (for CRUD response enrichment) ===\n if (data.enricherEntries) {\n registerResponseEnrichers(data.enricherEntries)\n }\n\n // === 6c. API interceptors (for CRUD route interception) ===\n if (data.interceptorEntries) {\n registerApiInterceptors(data.interceptorEntries)\n }\n\n // === 6d. Component overrides (for page/component replacement) ===\n if (data.componentOverrideEntries) {\n const finalEntries = applyComponentOverridesToEntries(data.componentOverrideEntries)\n const allOverrides = finalEntries.flatMap((entry) => entry.componentOverrides ?? [])\n registerComponentOverrides(allOverrides)\n }\n\n // === 6e. Mutation guards (for CRUD mutation lifecycle) ===\n if (data.guardEntries) {\n registerMutationGuards(data.guardEntries)\n }\n\n // === 6f. Command interceptors (for command bus lifecycle) ===\n if (data.commandInterceptorEntries) {\n registerCommandInterceptors(data.commandInterceptorEntries)\n }\n\n // === 6f.1. Command loaders (for lazy command handler registration) ===\n if (data.commandLoaderEntries) {\n registerCommandLoaders(data.commandLoaderEntries)\n }\n\n // === 6g. Notification handlers (reactive notification side-effects) ===\n if (data.notificationHandlerEntries) {\n registerNotificationHandlers(data.notificationHandlerEntries)\n }\n\n // === 7-8. UI Widgets and Optional packages (async to avoid circular deps) ===\n // Store the promise so CLI context can await it\n _asyncRegistrationPromise = registerWidgetsAndOptionalPackages(data, options)\n void _asyncRegistrationPromise\n\n options.onRegistrationComplete?.()\n }\n}\n\n/**\n * Wait for async registrations (CLI modules, widgets, etc.) to complete.\n * Call this after bootstrap() in CLI context where you need modules immediately.\n */\nexport async function waitForAsyncRegistration(): Promise<void> {\n if (_asyncRegistrationPromise) {\n await _asyncRegistrationPromise\n }\n}\n\nasync function registerWidgetsAndOptionalPackages(data: BootstrapData, options: BootstrapOptions): Promise<void> {\n // Register UI widgets (dynamic imports to avoid circular deps with ui/core packages)\n try {\n const [dashboardRegistry, injectionRegistry, coreInjection] = await Promise.all([\n import('@open-mercato/ui/backend/dashboard/widgetRegistry'),\n import('@open-mercato/ui/backend/injection/widgetRegistry'),\n import('@open-mercato/core/modules/widgets/lib/injection'),\n ])\n\n dashboardRegistry.registerDashboardWidgets(data.dashboardWidgetEntries)\n injectionRegistry.registerInjectionWidgets(data.injectionWidgetEntries)\n coreInjection.registerCoreInjectionWidgets(data.injectionWidgetEntries)\n coreInjection.registerCoreInjectionTables(data.injectionTables)\n coreInjection.registerEnabledModuleIds(\n data.modules.map((module) => module.id).filter((id): id is string => typeof id === 'string' && id.length > 0),\n )\n } catch {\n // UI packages may not be available in all contexts\n }\n\n // Note: Search module configs are registered synchronously in the main bootstrap.\n // The actual registerSearchModule() call happens in core/bootstrap.ts when the\n // DI container is created, using getSearchModuleConfigs() from the global registry.\n\n // Note: CLI module registration is handled separately in CLI context\n // via bootstrapFromAppRoot in dynamicLoader. We don't import CLI here\n // to avoid Turbopack tracing through the CLI package in Next.js context.\n}\n\n/**\n * Check if bootstrap has been called.\n */\nexport function isBootstrapped(): boolean {\n return _bootstrapped\n}\n\n/**\n * Reset bootstrap state. Useful for testing.\n */\nexport function resetBootstrapState(): void {\n _bootstrapped = false\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { BootstrapData, BootstrapOptions } from './types'\nimport { registerOrmEntities } from '../db/mikro'\nimport { registerAppDiRegistrar, registerDiRegistrars } from '../di/container'\nimport { registerModules } from '../modules/registry'\nimport { registerEntityIds } from '../encryption/entityIds'\nimport { registerEntityFields } from '../encryption/entityFields'\nimport { registerSearchModuleConfigs } from '../../modules/search'\nimport { registerAnalyticsModuleConfigs } from '../../modules/analytics'\nimport { registerCodeWorkflowEntries } from '../../modules/workflows/code-registry'\nimport { registerResponseEnrichers } from '../crud/enricher-registry'\nimport { registerApiInterceptors } from '../crud/interceptor-registry'\nimport { registerComponentOverrides } from '../../modules/widgets/component-registry'\nimport { registerMutationGuards } from '../crud/mutation-guard-store'\nimport { registerCommandInterceptors } from '../commands/command-interceptor-store'\nimport { registerCommandLoaders } from '../commands/registry'\nimport { registerNotificationHandlers } from '../notifications/handler-registry'\nimport { clearRegisteredIntegrations, registerBundles, registerIntegrations } from '../../modules/integrations/types'\nimport { applyComponentOverridesToEntries } from '../../modules/overrides'\n\nlet _bootstrapped = false\n\n// Store the async registration promise so callers can await it if needed\nlet _asyncRegistrationPromise: Promise<void> | null = null\n\n/**\n * Creates a bootstrap function that registers all application dependencies.\n *\n * The returned function should be called once at application startup.\n * In development mode, it can be called multiple times (for HMR).\n *\n * @param data - All generated registry data from .mercato/generated/\n * @param options - Optional configuration\n * @returns A bootstrap function to call at app startup\n */\nexport function createBootstrap(data: BootstrapData, options: BootstrapOptions = {}) {\n return function bootstrap(): void {\n if (options.appDiRegistrar) {\n registerAppDiRegistrar(options.appDiRegistrar)\n }\n // In development, always re-run registrations to handle HMR\n // (Module state may be reset when Turbopack reloads packages)\n if (_bootstrapped && process.env.NODE_ENV !== 'development') return\n _bootstrapped = true\n\n // === 1. Foundation: ORM entities and DI registrars ===\n registerOrmEntities(data.entities)\n registerDiRegistrars(data.diRegistrars.filter((r): r is NonNullable<typeof r> => r != null))\n\n // === 2. Modules registry (required by i18n, query engine, dashboards, CLI) ===\n registerModules(data.modules)\n clearRegisteredIntegrations()\n for (const module of data.modules) {\n if (module.integrations?.length) {\n registerIntegrations(module.integrations)\n }\n if (module.bundles?.length) {\n registerBundles(module.bundles)\n }\n }\n\n // === 3. Entity IDs (required by encryption, indexing, entity links) ===\n registerEntityIds(data.entityIds)\n\n // === 4. Entity fields registry (for encryption manager, Turbopack compatibility) ===\n if (data.entityFieldsRegistry) {\n registerEntityFields(data.entityFieldsRegistry)\n }\n\n // === 5. Search module configs (for search service registration in DI) ===\n if (data.searchModuleConfigs) {\n registerSearchModuleConfigs(data.searchModuleConfigs)\n }\n\n // === 6. Analytics module configs (for dashboard widgets and analytics API) ===\n if (data.analyticsModuleConfigs) {\n registerAnalyticsModuleConfigs(data.analyticsModuleConfigs)\n }\n\n // === 6a. Code workflow definitions (so CLI/worker processes resolve them like the app runtime) ===\n if (data.codeWorkflows?.length) {\n registerCodeWorkflowEntries(data.codeWorkflows)\n }\n\n // === 6b. Response enrichers (for CRUD response enrichment) ===\n if (data.enricherEntries) {\n registerResponseEnrichers(data.enricherEntries)\n }\n\n // === 6c. API interceptors (for CRUD route interception) ===\n if (data.interceptorEntries) {\n registerApiInterceptors(data.interceptorEntries)\n }\n\n // === 6d. Component overrides (for page/component replacement) ===\n if (data.componentOverrideEntries) {\n const finalEntries = applyComponentOverridesToEntries(data.componentOverrideEntries)\n const allOverrides = finalEntries.flatMap((entry) => entry.componentOverrides ?? [])\n registerComponentOverrides(allOverrides)\n }\n\n // === 6e. Mutation guards (for CRUD mutation lifecycle) ===\n if (data.guardEntries) {\n registerMutationGuards(data.guardEntries)\n }\n\n // === 6f. Command interceptors (for command bus lifecycle) ===\n if (data.commandInterceptorEntries) {\n registerCommandInterceptors(data.commandInterceptorEntries)\n }\n\n // === 6f.1. Command loaders (for lazy command handler registration) ===\n if (data.commandLoaderEntries) {\n registerCommandLoaders(data.commandLoaderEntries)\n }\n\n // === 6g. Notification handlers (reactive notification side-effects) ===\n if (data.notificationHandlerEntries) {\n registerNotificationHandlers(data.notificationHandlerEntries)\n }\n\n // === 7-8. UI Widgets and Optional packages (async to avoid circular deps) ===\n // Store the promise so CLI context can await it\n _asyncRegistrationPromise = registerWidgetsAndOptionalPackages(data, options)\n void _asyncRegistrationPromise\n\n options.onRegistrationComplete?.()\n }\n}\n\n/**\n * Wait for async registrations (CLI modules, widgets, etc.) to complete.\n * Call this after bootstrap() in CLI context where you need modules immediately.\n */\nexport async function waitForAsyncRegistration(): Promise<void> {\n if (_asyncRegistrationPromise) {\n await _asyncRegistrationPromise\n }\n}\n\nasync function registerWidgetsAndOptionalPackages(data: BootstrapData, options: BootstrapOptions): Promise<void> {\n // Register UI widgets (dynamic imports to avoid circular deps with ui/core packages)\n try {\n const [dashboardRegistry, injectionRegistry, coreInjection] = await Promise.all([\n import('@open-mercato/ui/backend/dashboard/widgetRegistry'),\n import('@open-mercato/ui/backend/injection/widgetRegistry'),\n import('@open-mercato/core/modules/widgets/lib/injection'),\n ])\n\n dashboardRegistry.registerDashboardWidgets(data.dashboardWidgetEntries)\n injectionRegistry.registerInjectionWidgets(data.injectionWidgetEntries)\n coreInjection.registerCoreInjectionWidgets(data.injectionWidgetEntries)\n coreInjection.registerCoreInjectionTables(data.injectionTables)\n coreInjection.registerEnabledModuleIds(\n data.modules.map((module) => module.id).filter((id): id is string => typeof id === 'string' && id.length > 0),\n )\n } catch {\n // UI packages may not be available in all contexts\n }\n\n // Note: Search module configs are registered synchronously in the main bootstrap.\n // The actual registerSearchModule() call happens in core/bootstrap.ts when the\n // DI container is created, using getSearchModuleConfigs() from the global registry.\n\n // Note: CLI module registration is handled separately in CLI context\n // via bootstrapFromAppRoot in dynamicLoader. We don't import CLI here\n // to avoid Turbopack tracing through the CLI package in Next.js context.\n}\n\n/**\n * Check if bootstrap has been called.\n */\nexport function isBootstrapped(): boolean {\n return _bootstrapped\n}\n\n/**\n * Reset bootstrap state. Useful for testing.\n */\nexport function resetBootstrapState(): void {\n _bootstrapped = false\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,5 @@
1
+ import { purgeConfiguredCachePatternsAcrossTenantScopes } from "@open-mercato/cache/service";
2
+ export {
3
+ purgeConfiguredCachePatternsAcrossTenantScopes
4
+ };
5
+ //# sourceMappingURL=maintenance.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/cache/maintenance.ts"],
4
+ "sourcesContent": ["export { purgeConfiguredCachePatternsAcrossTenantScopes } from '@open-mercato/cache/service'\n"],
5
+ "mappings": "AAAA,SAAS,sDAAsD;",
6
+ "names": []
7
+ }
@@ -22,24 +22,26 @@ async function setCustomFieldsIfAny(opts) {
22
22
  });
23
23
  }
24
24
  async function emitCrudSideEffects(opts) {
25
- const { dataEngine, action, entity, identifiers, syncOrigin, events, indexer } = opts;
25
+ const { dataEngine, action, entity, identifiers, syncOrigin, actorUserId, events, indexer } = opts;
26
26
  dataEngine.markOrmEntityChange({
27
27
  action,
28
28
  entity,
29
29
  identifiers,
30
30
  syncOrigin,
31
+ actorUserId,
31
32
  events,
32
33
  indexer
33
34
  });
34
35
  }
35
36
  async function emitCrudUndoSideEffects(opts) {
36
- const { dataEngine, action, entity, identifiers, syncOrigin, events, indexer } = opts;
37
+ const { dataEngine, action, entity, identifiers, syncOrigin, actorUserId, events, indexer } = opts;
37
38
  if (!entity) return;
38
39
  dataEngine.markOrmEntityChange({
39
40
  action,
40
41
  entity,
41
42
  identifiers,
42
43
  syncOrigin,
44
+ actorUserId,
43
45
  events,
44
46
  indexer
45
47
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/commands/helpers.ts"],
4
- "sourcesContent": ["import { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { z } from 'zod'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { normalizeCustomFieldValues } from '../custom-fields/normalize'\nexport { normalizeCustomFieldValues } from '../custom-fields/normalize'\nimport type { CrudEventsConfig, CrudIndexerConfig, CrudEmitContext } from '@open-mercato/shared/lib/crud/types'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { CommandLogMetadata } from '@open-mercato/shared/lib/commands'\nimport type { BulkImportSuppression } from '@open-mercato/shared/lib/commands'\n\nexport type ParsedPayload<TSchema extends z.ZodTypeAny> = {\n parsed: z.infer<TSchema>\n custom: Record<string, unknown>\n}\n\nexport function parseWithCustomFields<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n raw: unknown\n): ParsedPayload<TSchema> {\n const { base, custom } = splitCustomFieldPayload(raw)\n const parsed = schema.parse(base)\n return { parsed, custom }\n}\n\nexport async function setCustomFieldsIfAny(opts: {\n dataEngine: DataEngine\n entityId: string\n recordId: string\n tenantId: string | null\n organizationId: string | null\n values: Record<string, unknown>\n notify?: boolean\n}) {\n const { values } = opts\n if (!values || !Object.keys(values).length) return\n const { dataEngine, entityId, recordId, tenantId, organizationId, notify = false } = opts\n const normalized = normalizeCustomFieldValues(values)\n await dataEngine.setCustomFields({\n entityId,\n recordId,\n tenantId,\n organizationId,\n values: normalized,\n notify,\n })\n}\n\nexport async function emitCrudSideEffects<TEntity>(opts: {\n dataEngine: DataEngine\n action: 'created' | 'updated' | 'deleted'\n entity: TEntity\n identifiers: CrudEmitContext<TEntity>['identifiers']\n syncOrigin?: string | null\n events?: CrudEventsConfig<any>\n indexer?: CrudIndexerConfig<any>\n}) {\n const { dataEngine, action, entity, identifiers, syncOrigin, events, indexer } = opts\n dataEngine.markOrmEntityChange({\n action,\n entity,\n identifiers,\n syncOrigin,\n events,\n indexer,\n })\n}\n\nexport async function emitCrudUndoSideEffects<TEntity>(opts: {\n dataEngine: DataEngine\n action: 'created' | 'updated' | 'deleted'\n entity: TEntity | null | undefined\n identifiers: CrudEmitContext<TEntity>['identifiers']\n syncOrigin?: string | null\n events?: CrudEventsConfig<any>\n indexer?: CrudIndexerConfig<any>\n}) {\n const { dataEngine, action, entity, identifiers, syncOrigin, events, indexer } = opts\n if (!entity) return\n dataEngine.markOrmEntityChange({\n action,\n entity,\n identifiers,\n syncOrigin,\n events,\n indexer,\n })\n}\n\nexport async function flushCrudSideEffects(dataEngine: DataEngine, suppress?: BulkImportSuppression): Promise<void> {\n // Direct-write bulk paths (those that flush here instead of going through the command bus) can\n // pass the same `bulkImport` suppression so a bulk run defers per-record events/reindex on this\n // path too. Omitted for normal writes \u2192 unchanged behavior. Mirrors the command bus's own flush.\n await dataEngine.flushOrmEntityChanges(suppress)\n}\n\nexport function buildChanges(\n before: Record<string, unknown> | null | undefined,\n after: Record<string, unknown>,\n keys: readonly string[]\n): Record<string, { from: unknown; to: unknown }> {\n if (!before) return {}\n const diff: Record<string, { from: unknown; to: unknown }> = {}\n const skipped = new Set(['updatedAt', 'updated_at'])\n for (const key of keys) {\n if (skipped.has(key)) continue\n const prev = before[key]\n const next = after[key]\n if (prev !== next) diff[key] = { from: prev, to: next }\n }\n return diff\n}\n\nexport function requireTenantScope(authTenantId: string | null, requested?: string | null): string {\n if (authTenantId && requested && requested !== authTenantId) {\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n const tenantId = requested || authTenantId\n if (!tenantId) throw new CrudHttpError(400, { error: 'Tenant scope required' })\n return tenantId\n}\n\nexport function requireId(value: unknown, message = 'ID is required'): string {\n if (typeof value === 'string' && value.trim()) return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n if (value && typeof value === 'object') {\n const source = value as Record<string, unknown>\n const candidates: unknown[] = [\n source.id,\n source.recordId,\n isRecord(source.body) ? source.body.id : undefined,\n isRecord(source.query) ? source.query.id : undefined,\n ]\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.trim()) return candidate\n if (typeof candidate === 'number' || typeof candidate === 'bigint') return String(candidate)\n }\n }\n throw new CrudHttpError(400, { error: message })\n}\n\nfunction isRecord(input: unknown): input is { [key: string]: unknown } {\n return !!input && typeof input === 'object'\n}\n\nexport type LogBuilderArgs<TInput, TResult> = {\n input: TInput\n result: TResult\n ctx: CommandRuntimeContext\n snapshots: { before?: unknown; after?: unknown }\n}\n\nexport type LogBuilder<TInput, TResult> = (args: LogBuilderArgs<TInput, TResult>) => CommandLogMetadata | null | Promise<CommandLogMetadata | null>\n\nexport function snapshotsEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (a == null || b == null) return a === b\n if (typeof a !== typeof b) return false\n if (typeof a !== 'object') return false\n if (Array.isArray(a) !== Array.isArray(b)) return false\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n return a.every((value, index) => snapshotsEqual(value, b[index]))\n }\n const keysA = Object.keys(a as Record<string, unknown>)\n const keysB = Object.keys(b as Record<string, unknown>)\n if (keysA.length !== keysB.length) return false\n return keysA.every((key) =>\n snapshotsEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n )\n}\n\nconst AUTHOR_UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n\nexport function normalizeAuthorUserId(\n explicitAuthorUserId: string | undefined | null,\n auth: { isApiKey?: boolean; isSuperAdmin?: boolean; sub?: string | null } | undefined | null\n): string | null {\n if (\n explicitAuthorUserId &&\n auth?.isSuperAdmin === true &&\n auth.isApiKey !== true &&\n AUTHOR_UUID_REGEX.test(explicitAuthorUserId)\n ) {\n return explicitAuthorUserId\n }\n const authSub = auth?.isApiKey ? null : auth?.sub ?? null\n if (!authSub) return null\n return AUTHOR_UUID_REGEX.test(authSub) ? authSub : null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { z } from 'zod'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { normalizeCustomFieldValues } from '../custom-fields/normalize'\nexport { normalizeCustomFieldValues } from '../custom-fields/normalize'\nimport type { CrudEventsConfig, CrudIndexerConfig, CrudEmitContext } from '@open-mercato/shared/lib/crud/types'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { CommandLogMetadata } from '@open-mercato/shared/lib/commands'\nimport type { BulkImportSuppression } from '@open-mercato/shared/lib/commands'\n\nexport type ParsedPayload<TSchema extends z.ZodTypeAny> = {\n parsed: z.infer<TSchema>\n custom: Record<string, unknown>\n}\n\nexport function parseWithCustomFields<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n raw: unknown\n): ParsedPayload<TSchema> {\n const { base, custom } = splitCustomFieldPayload(raw)\n const parsed = schema.parse(base)\n return { parsed, custom }\n}\n\nexport async function setCustomFieldsIfAny(opts: {\n dataEngine: DataEngine\n entityId: string\n recordId: string\n tenantId: string | null\n organizationId: string | null\n values: Record<string, unknown>\n notify?: boolean\n}) {\n const { values } = opts\n if (!values || !Object.keys(values).length) return\n const { dataEngine, entityId, recordId, tenantId, organizationId, notify = false } = opts\n const normalized = normalizeCustomFieldValues(values)\n await dataEngine.setCustomFields({\n entityId,\n recordId,\n tenantId,\n organizationId,\n values: normalized,\n notify,\n })\n}\n\nexport async function emitCrudSideEffects<TEntity>(opts: {\n dataEngine: DataEngine\n action: 'created' | 'updated' | 'deleted'\n entity: TEntity\n identifiers: CrudEmitContext<TEntity>['identifiers']\n syncOrigin?: string | null\n actorUserId?: string | null\n events?: CrudEventsConfig<any>\n indexer?: CrudIndexerConfig<any>\n}) {\n const { dataEngine, action, entity, identifiers, syncOrigin, actorUserId, events, indexer } = opts\n dataEngine.markOrmEntityChange({\n action,\n entity,\n identifiers,\n syncOrigin,\n actorUserId,\n events,\n indexer,\n })\n}\n\nexport async function emitCrudUndoSideEffects<TEntity>(opts: {\n dataEngine: DataEngine\n action: 'created' | 'updated' | 'deleted'\n entity: TEntity | null | undefined\n identifiers: CrudEmitContext<TEntity>['identifiers']\n syncOrigin?: string | null\n actorUserId?: string | null\n events?: CrudEventsConfig<any>\n indexer?: CrudIndexerConfig<any>\n}) {\n const { dataEngine, action, entity, identifiers, syncOrigin, actorUserId, events, indexer } = opts\n if (!entity) return\n dataEngine.markOrmEntityChange({\n action,\n entity,\n identifiers,\n syncOrigin,\n actorUserId,\n events,\n indexer,\n })\n}\n\nexport async function flushCrudSideEffects(dataEngine: DataEngine, suppress?: BulkImportSuppression): Promise<void> {\n // Direct-write bulk paths (those that flush here instead of going through the command bus) can\n // pass the same `bulkImport` suppression so a bulk run defers per-record events/reindex on this\n // path too. Omitted for normal writes \u2192 unchanged behavior. Mirrors the command bus's own flush.\n await dataEngine.flushOrmEntityChanges(suppress)\n}\n\nexport function buildChanges(\n before: Record<string, unknown> | null | undefined,\n after: Record<string, unknown>,\n keys: readonly string[]\n): Record<string, { from: unknown; to: unknown }> {\n if (!before) return {}\n const diff: Record<string, { from: unknown; to: unknown }> = {}\n const skipped = new Set(['updatedAt', 'updated_at'])\n for (const key of keys) {\n if (skipped.has(key)) continue\n const prev = before[key]\n const next = after[key]\n if (prev !== next) diff[key] = { from: prev, to: next }\n }\n return diff\n}\n\nexport function requireTenantScope(authTenantId: string | null, requested?: string | null): string {\n if (authTenantId && requested && requested !== authTenantId) {\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n const tenantId = requested || authTenantId\n if (!tenantId) throw new CrudHttpError(400, { error: 'Tenant scope required' })\n return tenantId\n}\n\nexport function requireId(value: unknown, message = 'ID is required'): string {\n if (typeof value === 'string' && value.trim()) return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n if (value && typeof value === 'object') {\n const source = value as Record<string, unknown>\n const candidates: unknown[] = [\n source.id,\n source.recordId,\n isRecord(source.body) ? source.body.id : undefined,\n isRecord(source.query) ? source.query.id : undefined,\n ]\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.trim()) return candidate\n if (typeof candidate === 'number' || typeof candidate === 'bigint') return String(candidate)\n }\n }\n throw new CrudHttpError(400, { error: message })\n}\n\nfunction isRecord(input: unknown): input is { [key: string]: unknown } {\n return !!input && typeof input === 'object'\n}\n\nexport type LogBuilderArgs<TInput, TResult> = {\n input: TInput\n result: TResult\n ctx: CommandRuntimeContext\n snapshots: { before?: unknown; after?: unknown }\n}\n\nexport type LogBuilder<TInput, TResult> = (args: LogBuilderArgs<TInput, TResult>) => CommandLogMetadata | null | Promise<CommandLogMetadata | null>\n\nexport function snapshotsEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (a == null || b == null) return a === b\n if (typeof a !== typeof b) return false\n if (typeof a !== 'object') return false\n if (Array.isArray(a) !== Array.isArray(b)) return false\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n return a.every((value, index) => snapshotsEqual(value, b[index]))\n }\n const keysA = Object.keys(a as Record<string, unknown>)\n const keysB = Object.keys(b as Record<string, unknown>)\n if (keysA.length !== keysB.length) return false\n return keysA.every((key) =>\n snapshotsEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n )\n}\n\nconst AUTHOR_UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n\nexport function normalizeAuthorUserId(\n explicitAuthorUserId: string | undefined | null,\n auth: { isApiKey?: boolean; isSuperAdmin?: boolean; sub?: string | null } | undefined | null\n): string | null {\n if (\n explicitAuthorUserId &&\n auth?.isSuperAdmin === true &&\n auth.isApiKey !== true &&\n AUTHOR_UUID_REGEX.test(explicitAuthorUserId)\n ) {\n return explicitAuthorUserId\n }\n const authSub = auth?.isApiKey ? null : auth?.sub ?? null\n if (!authSub) return null\n return AUTHOR_UUID_REGEX.test(authSub) ? authSub : null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["normalizeCustomFieldValues"]
7
7
  }
@@ -144,11 +144,9 @@ async function invalidateCrudCache(container, resource, identifiers, fallbackTen
144
144
  if (recordId) {
145
145
  tags.add(buildRecordTag(key, tenantId, recordId));
146
146
  }
147
- const organizationIds = [];
148
- if (identifiers.organizationId !== void 0) {
149
- organizationIds.push(identifiers.organizationId ?? null);
150
- }
151
- if (!organizationIds.length) organizationIds.push(null);
147
+ const organizationIds = [null];
148
+ const recordOrganizationId = identifiers.organizationId ?? null;
149
+ if (recordOrganizationId) organizationIds.push(recordOrganizationId);
152
150
  for (const tag of buildCollectionTags(key, tenantId, organizationIds)) {
153
151
  tags.add(tag);
154
152
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/crud/cache.ts"],
4
- "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { parseBooleanToken } from '../boolean'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'crud-cache' })\n\nexport type CrudCacheIdentifiers = {\n id?: string | null\n organizationId?: string | null\n tenantId?: string | null\n}\n\nlet crudCacheEnabledFlag: boolean | null = null\nexport function isCrudCacheEnabled(): boolean {\n if (crudCacheEnabledFlag !== null) return crudCacheEnabledFlag\n crudCacheEnabledFlag = parseBooleanToken(process.env.ENABLE_CRUD_API_CACHE ?? '') === true\n return crudCacheEnabledFlag\n}\n\nlet crudCacheDebugFlag: boolean | null = null\nexport function isCrudCacheDebugEnabled(): boolean {\n if (crudCacheDebugFlag !== null) return crudCacheDebugFlag\n crudCacheDebugFlag = parseBooleanToken(process.env.QUERY_ENGINE_DEBUG_SQL ?? '') === true\n return crudCacheDebugFlag\n}\n\nexport function debugCrudCache(event: string, context: Record<string, unknown>) {\n if (!isCrudCacheDebugEnabled()) return\n try {\n logger.debug(event, context)\n } catch {}\n}\n\nexport function resolveCrudCache(container: AwilixContainer): CacheStrategy | null {\n try {\n const cache = (container.resolve('cache') as CacheStrategy)\n if (cache && typeof cache.get === 'function' && typeof cache.set === 'function') {\n return cache\n }\n } catch {}\n return null\n}\n\nexport function normalizeTagSegment(value: string | null | undefined): string {\n if (value === null || value === undefined || value === '') return 'null'\n return value.toString().trim().replace(/[^a-zA-Z0-9._-]/g, '-')\n}\n\nexport function canonicalizeResourceTag(value: string | null | undefined): string | null {\n if (value === null || value === undefined) return null\n const trimmed = String(value).trim()\n if (!trimmed.length) return null\n const withSeparators = trimmed\n .replace(/::/g, '.')\n .replace(/[/\\\\]+/g, '.')\n .replace(/[\\s]+/g, '.')\n .replace(/_/g, '.')\n .replace(/-+/g, '.')\n const withCamelBreaks = withSeparators.replace(/([a-z0-9])([A-Z])/g, '$1.$2')\n const collapsed = withCamelBreaks.replace(/\\.{2,}/g, '.').replace(/(?:^\\.+|\\.+$)/g, '')\n const lowered = collapsed.toLowerCase()\n return lowered.length ? lowered : null\n}\n\nexport function buildRecordTag(resource: string, tenantId: string | null, recordId: string): string {\n return [\n 'crud',\n normalizeTagSegment(resource),\n 'tenant',\n normalizeTagSegment(tenantId),\n 'record',\n normalizeTagSegment(recordId),\n ].join(':')\n}\n\nexport function buildCollectionTags(\n resource: string,\n tenantId: string | null,\n organizationIds: Array<string | null>\n): string[] {\n const normalizedResource = normalizeTagSegment(resource)\n const normalizedTenant = normalizeTagSegment(tenantId)\n const tags = new Set<string>()\n if (!organizationIds.length) {\n tags.add(['crud', normalizedResource, 'tenant', normalizedTenant, 'org', 'null', 'collection'].join(':'))\n return Array.from(tags)\n }\n for (const orgId of organizationIds) {\n tags.add(['crud', normalizedResource, 'tenant', normalizedTenant, 'org', normalizeTagSegment(orgId), 'collection'].join(':'))\n }\n return Array.from(tags)\n}\n\nexport function normalizeIdentifierValue(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n if (typeof value === 'object') {\n if (value instanceof Date) return value.toISOString()\n if (value && typeof (value as { id?: unknown }).id !== 'undefined') {\n return normalizeIdentifierValue((value as { id?: unknown }).id)\n }\n }\n return String(value)\n}\n\nexport function pickFirstIdentifier(...values: Array<unknown>): string | null {\n for (const value of values) {\n const normalized = normalizeIdentifierValue(value)\n if (normalized) return normalized\n }\n return null\n}\n\nconst IRREGULAR_PLURALS: Record<string, string> = {\n people: 'person',\n children: 'child',\n mice: 'mouse',\n men: 'man',\n women: 'woman',\n geese: 'goose',\n feet: 'foot',\n teeth: 'tooth',\n oxen: 'ox',\n}\n\nfunction singularizeSegment(segment: string): string {\n const lower = segment.toLowerCase()\n const irregular = IRREGULAR_PLURALS[lower]\n if (irregular) return irregular\n if (lower.endsWith('ies') && lower.length > 3) return lower.slice(0, -3) + 'y'\n if (lower.endsWith('ses') && lower.length > 3) return lower.slice(0, -2)\n if (\n (lower.endsWith('xes') ||\n lower.endsWith('zes') ||\n lower.endsWith('ches') ||\n lower.endsWith('shes')) &&\n lower.length > 3\n ) {\n return lower.slice(0, -2)\n }\n if (lower.endsWith('s') && !lower.endsWith('ss') && lower.length > 1) return lower.slice(0, -1)\n return lower\n}\n\nfunction singularizeResourceSegment(segment: string): string {\n return segment\n .split('-')\n .map((part) => singularizeSegment(part))\n .join('-')\n}\n\nexport function deriveResourceFromCommandId(commandId: string | undefined | null): string | null {\n if (!commandId || typeof commandId !== 'string') return null\n const parts = commandId.split('.')\n if (parts.length < 2) return null\n const modulePart = parts[0]\n const entityPart = singularizeResourceSegment(parts[1])\n if (!modulePart || !entityPart) return null\n return `${modulePart}.${entityPart}`\n}\n\nexport function expandResourceAliases(resource: string, aliases?: string[]): string[] {\n const set = new Set<string>()\n const inputs = [resource, ...(aliases ?? [])]\n for (const candidate of inputs) {\n const canonical = canonicalizeResourceTag(candidate)\n if (canonical) set.add(canonical)\n }\n if (!set.size) return []\n return Array.from(set)\n}\n\nexport async function invalidateCrudCache(\n container: AwilixContainer,\n resource: string,\n identifiers: CrudCacheIdentifiers,\n fallbackTenant: string | null,\n reason: string,\n aliases?: string[]\n): Promise<void> {\n if (!isCrudCacheEnabled()) return\n const cache = resolveCrudCache(container)\n if (!cache || typeof cache.deleteByTags !== 'function') return\n const resources = expandResourceAliases(resource, aliases)\n const tenantId = identifiers.tenantId ?? fallbackTenant ?? null\n const recordId = normalizeIdentifierValue(identifiers.id)\n const tags = new Set<string>()\n for (const key of resources) {\n if (recordId) {\n tags.add(buildRecordTag(key, tenantId, recordId))\n }\n const organizationIds: Array<string | null> = []\n if (identifiers.organizationId !== undefined) {\n organizationIds.push(identifiers.organizationId ?? null)\n }\n if (!organizationIds.length) organizationIds.push(null)\n for (const tag of buildCollectionTags(key, tenantId, organizationIds)) {\n tags.add(tag)\n }\n }\n if (!tags.size) return\n const tagList = Array.from(tags)\n debugCrudCache('invalidate', {\n resource,\n aliases: resources,\n reason,\n tenantId: tenantId ?? 'null',\n tags: tagList,\n action: 'clearing',\n })\n const deleted = await runWithCacheTenant(tenantId, () => cache.deleteByTags(tagList))\n debugCrudCache('invalidate', {\n resource,\n reason,\n tenantId: tenantId ?? 'null',\n tags: tagList,\n action: 'cleared',\n deleted,\n })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { parseBooleanToken } from '../boolean'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'crud-cache' })\n\nexport type CrudCacheIdentifiers = {\n id?: string | null\n organizationId?: string | null\n tenantId?: string | null\n}\n\nlet crudCacheEnabledFlag: boolean | null = null\nexport function isCrudCacheEnabled(): boolean {\n if (crudCacheEnabledFlag !== null) return crudCacheEnabledFlag\n crudCacheEnabledFlag = parseBooleanToken(process.env.ENABLE_CRUD_API_CACHE ?? '') === true\n return crudCacheEnabledFlag\n}\n\nlet crudCacheDebugFlag: boolean | null = null\nexport function isCrudCacheDebugEnabled(): boolean {\n if (crudCacheDebugFlag !== null) return crudCacheDebugFlag\n crudCacheDebugFlag = parseBooleanToken(process.env.QUERY_ENGINE_DEBUG_SQL ?? '') === true\n return crudCacheDebugFlag\n}\n\nexport function debugCrudCache(event: string, context: Record<string, unknown>) {\n if (!isCrudCacheDebugEnabled()) return\n try {\n logger.debug(event, context)\n } catch {}\n}\n\nexport function resolveCrudCache(container: AwilixContainer): CacheStrategy | null {\n try {\n const cache = (container.resolve('cache') as CacheStrategy)\n if (cache && typeof cache.get === 'function' && typeof cache.set === 'function') {\n return cache\n }\n } catch {}\n return null\n}\n\nexport function normalizeTagSegment(value: string | null | undefined): string {\n if (value === null || value === undefined || value === '') return 'null'\n return value.toString().trim().replace(/[^a-zA-Z0-9._-]/g, '-')\n}\n\nexport function canonicalizeResourceTag(value: string | null | undefined): string | null {\n if (value === null || value === undefined) return null\n const trimmed = String(value).trim()\n if (!trimmed.length) return null\n const withSeparators = trimmed\n .replace(/::/g, '.')\n .replace(/[/\\\\]+/g, '.')\n .replace(/[\\s]+/g, '.')\n .replace(/_/g, '.')\n .replace(/-+/g, '.')\n const withCamelBreaks = withSeparators.replace(/([a-z0-9])([A-Z])/g, '$1.$2')\n const collapsed = withCamelBreaks.replace(/\\.{2,}/g, '.').replace(/(?:^\\.+|\\.+$)/g, '')\n const lowered = collapsed.toLowerCase()\n return lowered.length ? lowered : null\n}\n\nexport function buildRecordTag(resource: string, tenantId: string | null, recordId: string): string {\n return [\n 'crud',\n normalizeTagSegment(resource),\n 'tenant',\n normalizeTagSegment(tenantId),\n 'record',\n normalizeTagSegment(recordId),\n ].join(':')\n}\n\nexport function buildCollectionTags(\n resource: string,\n tenantId: string | null,\n organizationIds: Array<string | null>\n): string[] {\n const normalizedResource = normalizeTagSegment(resource)\n const normalizedTenant = normalizeTagSegment(tenantId)\n const tags = new Set<string>()\n if (!organizationIds.length) {\n tags.add(['crud', normalizedResource, 'tenant', normalizedTenant, 'org', 'null', 'collection'].join(':'))\n return Array.from(tags)\n }\n for (const orgId of organizationIds) {\n tags.add(['crud', normalizedResource, 'tenant', normalizedTenant, 'org', normalizeTagSegment(orgId), 'collection'].join(':'))\n }\n return Array.from(tags)\n}\n\nexport function normalizeIdentifierValue(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n if (typeof value === 'object') {\n if (value instanceof Date) return value.toISOString()\n if (value && typeof (value as { id?: unknown }).id !== 'undefined') {\n return normalizeIdentifierValue((value as { id?: unknown }).id)\n }\n }\n return String(value)\n}\n\nexport function pickFirstIdentifier(...values: Array<unknown>): string | null {\n for (const value of values) {\n const normalized = normalizeIdentifierValue(value)\n if (normalized) return normalized\n }\n return null\n}\n\nconst IRREGULAR_PLURALS: Record<string, string> = {\n people: 'person',\n children: 'child',\n mice: 'mouse',\n men: 'man',\n women: 'woman',\n geese: 'goose',\n feet: 'foot',\n teeth: 'tooth',\n oxen: 'ox',\n}\n\nfunction singularizeSegment(segment: string): string {\n const lower = segment.toLowerCase()\n const irregular = IRREGULAR_PLURALS[lower]\n if (irregular) return irregular\n if (lower.endsWith('ies') && lower.length > 3) return lower.slice(0, -3) + 'y'\n if (lower.endsWith('ses') && lower.length > 3) return lower.slice(0, -2)\n if (\n (lower.endsWith('xes') ||\n lower.endsWith('zes') ||\n lower.endsWith('ches') ||\n lower.endsWith('shes')) &&\n lower.length > 3\n ) {\n return lower.slice(0, -2)\n }\n if (lower.endsWith('s') && !lower.endsWith('ss') && lower.length > 1) return lower.slice(0, -1)\n return lower\n}\n\nfunction singularizeResourceSegment(segment: string): string {\n return segment\n .split('-')\n .map((part) => singularizeSegment(part))\n .join('-')\n}\n\nexport function deriveResourceFromCommandId(commandId: string | undefined | null): string | null {\n if (!commandId || typeof commandId !== 'string') return null\n const parts = commandId.split('.')\n if (parts.length < 2) return null\n const modulePart = parts[0]\n const entityPart = singularizeResourceSegment(parts[1])\n if (!modulePart || !entityPart) return null\n return `${modulePart}.${entityPart}`\n}\n\nexport function expandResourceAliases(resource: string, aliases?: string[]): string[] {\n const set = new Set<string>()\n const inputs = [resource, ...(aliases ?? [])]\n for (const candidate of inputs) {\n const canonical = canonicalizeResourceTag(candidate)\n if (canonical) set.add(canonical)\n }\n if (!set.size) return []\n return Array.from(set)\n}\n\nexport async function invalidateCrudCache(\n container: AwilixContainer,\n resource: string,\n identifiers: CrudCacheIdentifiers,\n fallbackTenant: string | null,\n reason: string,\n aliases?: string[]\n): Promise<void> {\n if (!isCrudCacheEnabled()) return\n const cache = resolveCrudCache(container)\n if (!cache || typeof cache.deleteByTags !== 'function') return\n const resources = expandResourceAliases(resource, aliases)\n const tenantId = identifiers.tenantId ?? fallbackTenant ?? null\n const recordId = normalizeIdentifierValue(identifiers.id)\n const tags = new Set<string>()\n for (const key of resources) {\n if (recordId) {\n tags.add(buildRecordTag(key, tenantId, recordId))\n }\n // Always flush the tenant-level (org:null) collection tag alongside the\n // write's own org. Tenant-scoped entities (orgField: null \u2014 e.g. auth roles)\n // cache their collections under org:null, but the command bus resolves a\n // write's organizationId from the actor's auth context, so the org-specific\n // tag alone never matches those entries and they only expire on TTL (#2919).\n const organizationIds: Array<string | null> = [null]\n const recordOrganizationId = identifiers.organizationId ?? null\n if (recordOrganizationId) organizationIds.push(recordOrganizationId)\n for (const tag of buildCollectionTags(key, tenantId, organizationIds)) {\n tags.add(tag)\n }\n }\n if (!tags.size) return\n const tagList = Array.from(tags)\n debugCrudCache('invalidate', {\n resource,\n aliases: resources,\n reason,\n tenantId: tenantId ?? 'null',\n tags: tagList,\n action: 'clearing',\n })\n const deleted = await runWithCacheTenant(tenantId, () => cache.deleteByTags(tagList))\n debugCrudCache('invalidate', {\n resource,\n reason,\n tenantId: tenantId ?? 'null',\n tags: tagList,\n action: 'cleared',\n deleted,\n })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }