@open-mercato/core 0.5.1-develop.3045.b4b3320cc2 → 0.6.0

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 (106) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +21 -1
  3. package/dist/modules/api_keys/api/keys/route.js +9 -0
  4. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  5. package/dist/modules/audit_logs/services/accessLogService.js +13 -0
  6. package/dist/modules/audit_logs/services/accessLogService.js.map +3 -3
  7. package/dist/modules/audit_logs/services/actionLogService.js +6 -5
  8. package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
  9. package/dist/modules/auth/api/roles/acl/route.js +27 -37
  10. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  11. package/dist/modules/auth/api/users/route.js +41 -28
  12. package/dist/modules/auth/api/users/route.js.map +3 -3
  13. package/dist/modules/auth/lib/grantChecks.js +160 -0
  14. package/dist/modules/auth/lib/grantChecks.js.map +7 -0
  15. package/dist/modules/configs/cli.js +11 -0
  16. package/dist/modules/configs/cli.js.map +2 -2
  17. package/dist/modules/configs/lib/touchGeneratedBarrels.js +46 -0
  18. package/dist/modules/configs/lib/touchGeneratedBarrels.js.map +7 -0
  19. package/dist/modules/customers/api/activities/route.js +1 -52
  20. package/dist/modules/customers/api/activities/route.js.map +2 -2
  21. package/dist/modules/customers/api/interactions/counts/route.js +2 -1
  22. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  23. package/dist/modules/customers/api/interactions/route.js +21 -1
  24. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  25. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +7 -3
  26. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  27. package/dist/modules/customers/backend/customers/deals/[id]/page.js +5 -1
  28. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  29. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +7 -3
  30. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  31. package/dist/modules/customers/components/detail/ActivitiesCard.js +62 -6
  32. package/dist/modules/customers/components/detail/ActivitiesCard.js.map +2 -2
  33. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +21 -6
  34. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
  35. package/dist/modules/customers/components/detail/ActivitiesSection.js +37 -5
  36. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
  37. package/dist/modules/customers/components/detail/ActivityCard.js +69 -17
  38. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  39. package/dist/modules/customers/components/detail/ActivityHistorySection.js +94 -34
  40. package/dist/modules/customers/components/detail/ActivityHistorySection.js.map +2 -2
  41. package/dist/modules/customers/components/detail/ActivityLogTab.js +3 -1
  42. package/dist/modules/customers/components/detail/ActivityLogTab.js.map +2 -2
  43. package/dist/modules/customers/components/detail/ActivityTimeline.js +41 -8
  44. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  45. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js +19 -6
  46. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js.map +2 -2
  47. package/dist/modules/customers/components/detail/ActivityTypeSelector.js +4 -3
  48. package/dist/modules/customers/components/detail/ActivityTypeSelector.js.map +2 -2
  49. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +80 -12
  50. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  51. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +65 -10
  52. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
  53. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +10 -5
  54. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  55. package/dist/modules/customers/data/validators.js +74 -2
  56. package/dist/modules/customers/data/validators.js.map +2 -2
  57. package/dist/modules/customers/lib/legacyActivityBridge.js +61 -0
  58. package/dist/modules/customers/lib/legacyActivityBridge.js.map +7 -0
  59. package/dist/modules/integrations/data/validators.js +2 -2
  60. package/dist/modules/integrations/data/validators.js.map +2 -2
  61. package/dist/modules/integrations/lib/credentials-service.js +12 -1
  62. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  63. package/dist/modules/messages/commands/actions.js +29 -14
  64. package/dist/modules/messages/commands/actions.js.map +2 -2
  65. package/dist/modules/messages/lib/actions.js +24 -4
  66. package/dist/modules/messages/lib/actions.js.map +2 -2
  67. package/dist/modules/sales/api/documents/factory.js +49 -36
  68. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  69. package/package.json +9 -10
  70. package/src/modules/api_keys/api/keys/route.ts +9 -0
  71. package/src/modules/audit_logs/services/accessLogService.ts +20 -0
  72. package/src/modules/audit_logs/services/actionLogService.ts +13 -5
  73. package/src/modules/auth/api/roles/acl/route.ts +32 -46
  74. package/src/modules/auth/api/users/route.ts +48 -33
  75. package/src/modules/auth/lib/grantChecks.ts +234 -0
  76. package/src/modules/configs/cli.ts +11 -0
  77. package/src/modules/configs/lib/touchGeneratedBarrels.ts +61 -0
  78. package/src/modules/customers/api/activities/route.ts +1 -76
  79. package/src/modules/customers/api/interactions/counts/route.ts +2 -1
  80. package/src/modules/customers/api/interactions/route.ts +28 -1
  81. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +13 -3
  82. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +14 -2
  83. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +13 -3
  84. package/src/modules/customers/components/detail/ActivitiesCard.tsx +92 -5
  85. package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +38 -6
  86. package/src/modules/customers/components/detail/ActivitiesSection.tsx +37 -3
  87. package/src/modules/customers/components/detail/ActivityCard.tsx +79 -14
  88. package/src/modules/customers/components/detail/ActivityHistorySection.tsx +102 -33
  89. package/src/modules/customers/components/detail/ActivityLogTab.tsx +7 -1
  90. package/src/modules/customers/components/detail/ActivityTimeline.tsx +39 -5
  91. package/src/modules/customers/components/detail/ActivityTimelineFilters.tsx +29 -7
  92. package/src/modules/customers/components/detail/ActivityTypeSelector.tsx +3 -2
  93. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +96 -13
  94. package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +50 -4
  95. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +21 -5
  96. package/src/modules/customers/data/validators.ts +85 -2
  97. package/src/modules/customers/i18n/de.json +11 -0
  98. package/src/modules/customers/i18n/en.json +11 -0
  99. package/src/modules/customers/i18n/es.json +11 -0
  100. package/src/modules/customers/i18n/pl.json +11 -0
  101. package/src/modules/customers/lib/legacyActivityBridge.ts +106 -0
  102. package/src/modules/integrations/data/validators.ts +8 -6
  103. package/src/modules/integrations/lib/credentials-service.ts +15 -1
  104. package/src/modules/messages/commands/actions.ts +28 -13
  105. package/src/modules/messages/lib/actions.ts +34 -3
  106. package/src/modules/sales/api/documents/factory.ts +55 -38
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/auth/lib/grantChecks.ts"],
4
+ "sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { CrudHttpError, forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Role, RoleAcl } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\n\ntype ActorAcl = {\n isSuperAdmin: boolean\n features: string[]\n organizations: string[] | null\n}\n\ntype GrantCheckContext = {\n em: EntityManager\n rbacService: RbacService\n actorUserId: string | null | undefined\n tenantId: string | null | undefined\n organizationId?: string | null | undefined\n}\n\ntype RoleGrantCheckInput = GrantCheckContext & {\n roles: Role[]\n}\n\ntype RoleTokenGrantCheckInput = GrantCheckContext & {\n roleTokens: unknown\n}\n\ntype FeatureGrantCheckInput = GrantCheckContext & {\n features: unknown\n isSuperAdmin?: boolean\n organizations?: string[] | null\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport async function assertActorCanGrantRoleTokens(input: RoleTokenGrantCheckInput): Promise<Role[]> {\n const tokens = normalizeStringList(input.roleTokens)\n if (!tokens.length) return []\n\n const tenantId = normalizeNullableString(input.tenantId)\n const roles = await resolveRolesForGrant(input.em, tokens, tenantId)\n await assertActorCanGrantRoles({ ...input, tenantId, roles })\n return roles\n}\n\nexport async function assertActorCanGrantRoles(input: RoleGrantCheckInput): Promise<void> {\n if (!input.roles.length) return\n\n const tenantId = normalizeNullableString(input.tenantId)\n const actorAcl = await loadActorAcl({ ...input, tenantId })\n if (actorAcl.isSuperAdmin) return\n\n if (!tenantId) {\n throw forbidden('Tenant context is required to grant roles.')\n }\n\n for (const role of input.roles) {\n const roleTenantId = normalizeNullableString(role.tenantId)\n if (roleTenantId !== tenantId) {\n throw forbidden('Cannot grant a role outside the target tenant.')\n }\n\n const acl = await findOneWithDecryption(\n input.em,\n RoleAcl,\n { role, tenantId } as FilterQuery<RoleAcl>,\n {},\n { tenantId, organizationId: null },\n )\n if (!acl) continue\n\n assertActorCanGrantAclSnapshot(actorAcl, {\n isSuperAdmin: !!acl.isSuperAdmin,\n features: normalizeStringList(acl.featuresJson),\n organizations: normalizeOrganizationList(acl.organizationsJson),\n })\n }\n}\n\nexport async function assertActorCanGrantAcl(input: FeatureGrantCheckInput): Promise<void> {\n const actorAcl = await loadActorAcl(input)\n if (actorAcl.isSuperAdmin) return\n\n const tenantId = normalizeNullableString(input.tenantId)\n if (!tenantId) {\n throw forbidden('Tenant context is required to grant ACL features.')\n }\n\n assertActorCanGrantAclSnapshot(actorAcl, {\n isSuperAdmin: !!input.isSuperAdmin,\n features: normalizeStringList(input.features),\n organizations: input.organizations === undefined ? undefined : normalizeOrganizationList(input.organizations),\n })\n}\n\nexport function normalizeGrantFeatureList(features: unknown): string[] {\n return normalizeStringList(features)\n}\n\nasync function loadActorAcl(input: GrantCheckContext): Promise<ActorAcl> {\n const actorUserId = normalizeNullableString(input.actorUserId)\n if (!actorUserId) throw forbidden('Not authorized to grant ACL privileges.')\n\n const acl = await input.rbacService.loadAcl(actorUserId, {\n tenantId: normalizeNullableString(input.tenantId),\n organizationId: normalizeNullableString(input.organizationId),\n })\n\n return {\n isSuperAdmin: !!acl?.isSuperAdmin,\n features: normalizeStringList(acl?.features),\n organizations: normalizeOrganizationList(acl?.organizations),\n }\n}\n\nasync function resolveRolesForGrant(\n em: EntityManager,\n roleTokens: string[],\n tenantId: string | null,\n): Promise<Role[]> {\n const roles: Role[] = []\n const missingRoles: string[] = []\n\n for (const token of roleTokens) {\n const role = await resolveRoleForGrant(em, token, tenantId)\n if (!role) {\n missingRoles.push(token)\n } else {\n roles.push(role)\n }\n }\n\n if (missingRoles.length) {\n const labels = missingRoles.map((role) => `\"${role}\"`).join(', ')\n throw new CrudHttpError(400, { error: `Role(s) not found: ${labels}` })\n }\n\n return roles\n}\n\nasync function resolveRoleForGrant(\n em: EntityManager,\n token: string,\n tenantId: string | null,\n): Promise<Role | null> {\n const where: Record<string, unknown> = UUID_RE.test(token)\n ? { id: token, deletedAt: null }\n : { name: token, deletedAt: null }\n if (tenantId) where.tenantId = tenantId\n return findOneWithDecryption(\n em,\n Role,\n where as FilterQuery<Role>,\n {},\n { tenantId, organizationId: null },\n )\n}\n\nfunction assertActorCanGrantAclSnapshot(\n actorAcl: ActorAcl,\n requested: {\n isSuperAdmin: boolean\n features: string[]\n organizations?: string[] | null\n },\n): void {\n if (requested.isSuperAdmin) {\n throw forbidden('Only super administrators can grant super admin access.')\n }\n\n const actorGrantableFeatures = actorAcl.features.filter((grant) => grant !== '*')\n for (const feature of requested.features) {\n if (feature === '*') {\n throw forbidden('Only super administrators can grant global wildcard access.')\n }\n if (isWildcardFeature(feature)) {\n if (!hasFeature(actorGrantableFeatures, feature)) {\n throw forbidden(`Cannot grant feature wildcard ${feature}.`)\n }\n continue\n }\n if (!hasFeature(actorGrantableFeatures, feature)) {\n throw forbidden(`Cannot grant feature ${feature}.`)\n }\n }\n\n if (requested.organizations !== undefined) {\n assertActorCanGrantOrganizations(actorAcl.organizations, requested.organizations)\n }\n}\n\nfunction assertActorCanGrantOrganizations(\n actorOrganizations: string[] | null,\n requestedOrganizations: string[] | null,\n): void {\n if (actorOrganizations === null || actorOrganizations.includes('__all__')) return\n\n if (requestedOrganizations === null || requestedOrganizations.includes('__all__')) {\n throw forbidden('Cannot grant unrestricted organization access.')\n }\n\n for (const organizationId of requestedOrganizations) {\n if (!actorOrganizations.includes(organizationId)) {\n throw forbidden('Cannot grant organization access outside actor scope.')\n }\n }\n}\n\nfunction normalizeStringList(values: unknown): string[] {\n if (!Array.isArray(values)) return []\n const dedup = new Set<string>()\n for (const value of values) {\n if (typeof value !== 'string') continue\n const trimmed = value.trim()\n if (!trimmed) continue\n dedup.add(trimmed)\n }\n return Array.from(dedup)\n}\n\nfunction normalizeOrganizationList(values: unknown): string[] | null {\n if (values === null || values === undefined) return null\n return normalizeStringList(values)\n}\n\nfunction normalizeNullableString(value: unknown): string | null {\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null\n}\n\nfunction isWildcardFeature(feature: string): boolean {\n return feature.endsWith('.*')\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -8,6 +8,7 @@ import {
8
8
  executeCachePurge,
9
9
  previewCachePurge
10
10
  } from "./lib/cache-cli.js";
11
+ import { touchGeneratedBarrels } from "./lib/touchGeneratedBarrels.js";
11
12
  function parseArgs(rest) {
12
13
  const args = {};
13
14
  for (let i = 0; i < rest.length; i += 1) {
@@ -215,6 +216,16 @@ async function runStructuralCachePurge(args) {
215
216
  pattern: "nav:*"
216
217
  };
217
218
  await runCachePurge(nextArgs);
219
+ const quiet = flagEnabled(args, "quiet");
220
+ try {
221
+ touchGeneratedBarrels({ quiet });
222
+ } catch (err) {
223
+ if (!quiet) {
224
+ console.warn(
225
+ `[structural] failed to touch generated barrels: ${err.message ?? err}`
226
+ );
227
+ }
228
+ }
218
229
  }
219
230
  function envDisablesAutoIndexing() {
220
231
  const raw = process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING ?? process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/configs/cli.ts"],
4
- "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ModuleConfigService } from './lib/module-config-service'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from '../notifications/lib/deliveryConfig'\nimport { Tenant } from '../directory/data/entities'\nimport {\n collectCacheStats,\n executeCachePurge,\n previewCachePurge,\n type CachePurgeRequest,\n} from './lib/cache-cli'\n\ntype ParsedArgs = Record<string, string | boolean>\n\ntype CacheScope = {\n label: string\n tenantId: string | null\n}\n\nfunction parseArgs(rest: string[]): ParsedArgs {\n const args: ParsedArgs = {}\n for (let i = 0; i < rest.length; i += 1) {\n const part = rest[i]\n if (!part?.startsWith('--')) continue\n const [rawKey, rawValue] = part.slice(2).split('=')\n if (!rawKey) continue\n if (rawValue !== undefined) {\n args[rawKey] = rawValue\n } else if (i + 1 < rest.length && !rest[i + 1]!.startsWith('--')) {\n args[rawKey] = rest[i + 1]!\n i += 1\n } else {\n args[rawKey] = true\n }\n }\n return args\n}\n\nfunction stringOption(args: ParsedArgs, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw !== 'string') continue\n const trimmed = raw.trim()\n if (trimmed.length > 0) return trimmed\n }\n return undefined\n}\n\nfunction flagEnabled(args: ParsedArgs, ...keys: string[]): boolean {\n for (const key of keys) {\n const raw = args[key]\n if (raw === undefined) continue\n if (raw === true) return true\n if (typeof raw === 'string') {\n const parsed = parseBooleanToken(raw)\n return parsed === null ? true : parsed\n }\n }\n return false\n}\n\nfunction splitListOption(raw: string | undefined): string[] {\n if (!raw) return []\n const seen = new Set<string>()\n const values: string[] = []\n for (const item of raw.split(',')) {\n const trimmed = item.trim()\n if (!trimmed || seen.has(trimmed)) continue\n seen.add(trimmed)\n values.push(trimmed)\n }\n return values\n}\n\nasync function resolveCacheScopes(\n em: EntityManager,\n args: ParsedArgs,\n): Promise<CacheScope[]> {\n const explicitTenantId = stringOption(args, 'tenant', 'tenantId')\n const globalOnly = flagEnabled(args, 'global')\n const allTenants = flagEnabled(args, 'all-tenants', 'allTenants')\n\n if (explicitTenantId && globalOnly) {\n throw new Error('Cannot combine `--tenant` with `--global`.')\n }\n if (explicitTenantId && allTenants) {\n throw new Error('Cannot combine `--tenant` with `--all-tenants`.')\n }\n if (globalOnly && allTenants) {\n throw new Error('Cannot combine `--global` with `--all-tenants`.')\n }\n\n if (explicitTenantId) {\n return [{ label: `tenant:${explicitTenantId}`, tenantId: explicitTenantId }]\n }\n\n if (globalOnly) {\n return [{ label: 'global', tenantId: null }]\n }\n\n if (!allTenants) {\n return [{ label: 'global', tenantId: null }]\n }\n\n const tenants = await em.find(Tenant, { deletedAt: null }, { orderBy: { name: 'asc' } })\n const scopes: CacheScope[] = [{ label: 'global', tenantId: null }]\n const seen = new Set<string>()\n for (const tenant of tenants) {\n const tenantId = typeof tenant.id === 'string' ? tenant.id : ''\n if (!tenantId || seen.has(tenantId)) continue\n seen.add(tenantId)\n scopes.push({ label: `tenant:${tenantId}`, tenantId })\n }\n return scopes\n}\n\nfunction resolveCachePurgeRequest(args: ParsedArgs): CachePurgeRequest {\n if (flagEnabled(args, 'all')) return { kind: 'all' }\n\n const segment = stringOption(args, 'segment')\n if (segment) return { kind: 'segment', segment }\n\n const tags = splitListOption(stringOption(args, 'tag', 'tags'))\n if (tags.length > 0) return { kind: 'tags', tags }\n\n const keys = splitListOption(stringOption(args, 'key', 'keys'))\n if (keys.length > 0) return { kind: 'keys', keys }\n\n const ids = splitListOption(stringOption(args, 'id', 'ids'))\n if (ids.length > 0) return { kind: 'ids', ids }\n\n const pattern = stringOption(args, 'pattern')\n if (pattern) return { kind: 'pattern', pattern }\n\n throw new Error(\n 'Choose a purge target: `--all`, `--segment <id>`, `--tag <tag1,tag2>`, `--key <key1,key2>`, `--id <token1,token2>`, or `--pattern <glob>`.',\n )\n}\n\nfunction printCacheHelp() {\n console.log('\uD83E\uDDF9 Cache CLI')\n console.log('')\n console.log('\uD83D\uDE80 Usage:')\n console.log(' yarn mercato configs cache stats [--tenant <id> | --global | --all-tenants] [--json]')\n console.log(' yarn mercato configs cache purge --all [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --segment <segment> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --tag <tag1,tag2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --key <key1,key2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --id <token1,token2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --pattern <glob> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache structural [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log('')\n console.log('\u2139\uFE0F Notes:')\n console.log(' `stats` mirrors the cache admin page segment overview for CRUD/widget caches.')\n console.log(' `purge --id` removes every key whose name contains the provided token (for example a user id or entity id).')\n console.log(' `structural` targets navigation caches (`nav:*`) and is the recommended post-step after module/sidebar structure changes.')\n console.log(' When no scope flag is supplied, this command uses the global cache scope only.')\n}\n\nasync function disposeContainer(container: unknown) {\n const disposable = container as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n}\n\nasync function runCacheStats(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n const cache = container.resolve('cache') as CacheStrategy\n const scopes = await resolveCacheScopes(em, args)\n const results = []\n for (const scope of scopes) {\n const stats = await runWithCacheTenant(scope.tenantId, async () => collectCacheStats(cache))\n results.push({ scope: scope.label, ...stats })\n }\n\n if (json) {\n console.log(JSON.stringify(results, null, 2))\n return\n }\n\n for (const result of results) {\n console.log(`\uD83D\uDD0E [cache] scope=${result.scope} totalKeys=${result.totalKeys} generatedAt=${result.generatedAt}`)\n if (result.segments.length === 0) {\n console.log(' \u2205 segments: none')\n continue\n }\n for (const segment of result.segments) {\n console.log(` \u2022 ${segment.segment} (${segment.keyCount})${segment.path ? ` ${segment.path}` : ''}`)\n }\n }\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runCachePurge(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const quiet = flagEnabled(args, 'quiet')\n const dryRun = flagEnabled(args, 'dry-run', 'dryRun')\n const request = resolveCachePurgeRequest(args)\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n const cache = container.resolve('cache') as CacheStrategy\n const scopes = await resolveCacheScopes(em, args)\n const results = []\n\n for (const scope of scopes) {\n const result = await runWithCacheTenant(scope.tenantId, async () =>\n dryRun ? previewCachePurge(cache, request) : executeCachePurge(cache, request)\n )\n results.push({\n scope: scope.label,\n dryRun,\n request,\n deleted: result.deleted,\n keyCount: result.keys.length,\n keys: result.keys,\n note: result.note,\n })\n }\n\n if (json) {\n console.log(JSON.stringify(results, null, 2))\n return\n }\n\n if (quiet) {\n return\n }\n\n for (const result of results) {\n console.log(`${result.dryRun ? '\uD83E\uDDEA' : '\uD83E\uDDF9'} [cache] scope=${result.scope} deleted=${result.deleted}${result.dryRun ? ' (dry-run)' : ''}`)\n if (result.note) console.log(` \u2139\uFE0F note: ${result.note}`)\n if (result.keys.length > 0) {\n for (const key of result.keys) {\n console.log(` \u2022 ${key}`)\n }\n }\n }\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runStructuralCachePurge(args: ParsedArgs) {\n const nextArgs: ParsedArgs = {\n ...args,\n pattern: 'nav:*',\n }\n await runCachePurge(nextArgs)\n}\n\nfunction envDisablesAutoIndexing(): boolean {\n const raw =\n process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING ??\n process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING\n if (!raw) return false\n return parseBooleanToken(raw) === true\n}\n\nconst restoreDefaults: ModuleCli = {\n command: 'restore-defaults',\n async run() {\n const container = await createRequestContainer()\n try {\n let service: ModuleConfigService\n try {\n service = (container.resolve('moduleConfigService') as ModuleConfigService)\n } catch {\n console.error('[configs] moduleConfigService is not registered in the container.')\n return\n }\n\n const disabledByEnv = envDisablesAutoIndexing()\n const defaultEnabled = !disabledByEnv\n await service.restoreDefaults(\n [\n {\n moduleId: 'vector',\n name: 'auto_index_enabled',\n value: defaultEnabled,\n },\n {\n moduleId: 'notifications',\n name: NOTIFICATIONS_DELIVERY_CONFIG_KEY,\n value: DEFAULT_NOTIFICATION_DELIVERY_CONFIG,\n },\n ],\n { force: true },\n )\n console.log(\n `[configs] Vector auto-indexing default set to ${defaultEnabled ? 'enabled' : 'disabled'}${\n disabledByEnv\n ? ' (forced by OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING or legacy DISABLE_VECTOR_SEARCH_AUTOINDEXING)'\n : ''\n }.`,\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n },\n}\n\nconst help: ModuleCli = {\n command: 'help',\n async run() {\n console.log('\u2699\uFE0F Configs CLI')\n console.log('')\n console.log('\uD83D\uDE80 Usage: yarn mercato configs restore-defaults')\n console.log(' Ensures global module configuration defaults exist.')\n console.log('')\n printCacheHelp()\n },\n}\n\nconst cacheCommand: ModuleCli = {\n command: 'cache',\n async run(rest) {\n const [subcommand, ...subRest] = rest\n const args = parseArgs(subRest)\n\n if (!subcommand || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {\n printCacheHelp()\n return\n }\n\n if (subcommand === 'stats') {\n await runCacheStats(args)\n return\n }\n\n if (subcommand === 'purge') {\n await runCachePurge(args)\n return\n }\n\n if (subcommand === 'structural') {\n await runStructuralCachePurge(args)\n return\n }\n\n throw new Error(`Unknown cache subcommand \"${subcommand}\".`)\n },\n}\n\nexport default [restoreDefaults, cacheCommand, help]\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ModuleConfigService } from './lib/module-config-service'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from '../notifications/lib/deliveryConfig'\nimport { Tenant } from '../directory/data/entities'\nimport {\n collectCacheStats,\n executeCachePurge,\n previewCachePurge,\n type CachePurgeRequest,\n} from './lib/cache-cli'\nimport { touchGeneratedBarrels } from './lib/touchGeneratedBarrels'\n\ntype ParsedArgs = Record<string, string | boolean>\n\ntype CacheScope = {\n label: string\n tenantId: string | null\n}\n\nfunction parseArgs(rest: string[]): ParsedArgs {\n const args: ParsedArgs = {}\n for (let i = 0; i < rest.length; i += 1) {\n const part = rest[i]\n if (!part?.startsWith('--')) continue\n const [rawKey, rawValue] = part.slice(2).split('=')\n if (!rawKey) continue\n if (rawValue !== undefined) {\n args[rawKey] = rawValue\n } else if (i + 1 < rest.length && !rest[i + 1]!.startsWith('--')) {\n args[rawKey] = rest[i + 1]!\n i += 1\n } else {\n args[rawKey] = true\n }\n }\n return args\n}\n\nfunction stringOption(args: ParsedArgs, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw !== 'string') continue\n const trimmed = raw.trim()\n if (trimmed.length > 0) return trimmed\n }\n return undefined\n}\n\nfunction flagEnabled(args: ParsedArgs, ...keys: string[]): boolean {\n for (const key of keys) {\n const raw = args[key]\n if (raw === undefined) continue\n if (raw === true) return true\n if (typeof raw === 'string') {\n const parsed = parseBooleanToken(raw)\n return parsed === null ? true : parsed\n }\n }\n return false\n}\n\nfunction splitListOption(raw: string | undefined): string[] {\n if (!raw) return []\n const seen = new Set<string>()\n const values: string[] = []\n for (const item of raw.split(',')) {\n const trimmed = item.trim()\n if (!trimmed || seen.has(trimmed)) continue\n seen.add(trimmed)\n values.push(trimmed)\n }\n return values\n}\n\nasync function resolveCacheScopes(\n em: EntityManager,\n args: ParsedArgs,\n): Promise<CacheScope[]> {\n const explicitTenantId = stringOption(args, 'tenant', 'tenantId')\n const globalOnly = flagEnabled(args, 'global')\n const allTenants = flagEnabled(args, 'all-tenants', 'allTenants')\n\n if (explicitTenantId && globalOnly) {\n throw new Error('Cannot combine `--tenant` with `--global`.')\n }\n if (explicitTenantId && allTenants) {\n throw new Error('Cannot combine `--tenant` with `--all-tenants`.')\n }\n if (globalOnly && allTenants) {\n throw new Error('Cannot combine `--global` with `--all-tenants`.')\n }\n\n if (explicitTenantId) {\n return [{ label: `tenant:${explicitTenantId}`, tenantId: explicitTenantId }]\n }\n\n if (globalOnly) {\n return [{ label: 'global', tenantId: null }]\n }\n\n if (!allTenants) {\n return [{ label: 'global', tenantId: null }]\n }\n\n const tenants = await em.find(Tenant, { deletedAt: null }, { orderBy: { name: 'asc' } })\n const scopes: CacheScope[] = [{ label: 'global', tenantId: null }]\n const seen = new Set<string>()\n for (const tenant of tenants) {\n const tenantId = typeof tenant.id === 'string' ? tenant.id : ''\n if (!tenantId || seen.has(tenantId)) continue\n seen.add(tenantId)\n scopes.push({ label: `tenant:${tenantId}`, tenantId })\n }\n return scopes\n}\n\nfunction resolveCachePurgeRequest(args: ParsedArgs): CachePurgeRequest {\n if (flagEnabled(args, 'all')) return { kind: 'all' }\n\n const segment = stringOption(args, 'segment')\n if (segment) return { kind: 'segment', segment }\n\n const tags = splitListOption(stringOption(args, 'tag', 'tags'))\n if (tags.length > 0) return { kind: 'tags', tags }\n\n const keys = splitListOption(stringOption(args, 'key', 'keys'))\n if (keys.length > 0) return { kind: 'keys', keys }\n\n const ids = splitListOption(stringOption(args, 'id', 'ids'))\n if (ids.length > 0) return { kind: 'ids', ids }\n\n const pattern = stringOption(args, 'pattern')\n if (pattern) return { kind: 'pattern', pattern }\n\n throw new Error(\n 'Choose a purge target: `--all`, `--segment <id>`, `--tag <tag1,tag2>`, `--key <key1,key2>`, `--id <token1,token2>`, or `--pattern <glob>`.',\n )\n}\n\nfunction printCacheHelp() {\n console.log('\uD83E\uDDF9 Cache CLI')\n console.log('')\n console.log('\uD83D\uDE80 Usage:')\n console.log(' yarn mercato configs cache stats [--tenant <id> | --global | --all-tenants] [--json]')\n console.log(' yarn mercato configs cache purge --all [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --segment <segment> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --tag <tag1,tag2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --key <key1,key2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --id <token1,token2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --pattern <glob> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache structural [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log('')\n console.log('\u2139\uFE0F Notes:')\n console.log(' `stats` mirrors the cache admin page segment overview for CRUD/widget caches.')\n console.log(' `purge --id` removes every key whose name contains the provided token (for example a user id or entity id).')\n console.log(' `structural` targets navigation caches (`nav:*`) and is the recommended post-step after module/sidebar structure changes.')\n console.log(' When no scope flag is supplied, this command uses the global cache scope only.')\n}\n\nasync function disposeContainer(container: unknown) {\n const disposable = container as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n}\n\nasync function runCacheStats(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n const cache = container.resolve('cache') as CacheStrategy\n const scopes = await resolveCacheScopes(em, args)\n const results = []\n for (const scope of scopes) {\n const stats = await runWithCacheTenant(scope.tenantId, async () => collectCacheStats(cache))\n results.push({ scope: scope.label, ...stats })\n }\n\n if (json) {\n console.log(JSON.stringify(results, null, 2))\n return\n }\n\n for (const result of results) {\n console.log(`\uD83D\uDD0E [cache] scope=${result.scope} totalKeys=${result.totalKeys} generatedAt=${result.generatedAt}`)\n if (result.segments.length === 0) {\n console.log(' \u2205 segments: none')\n continue\n }\n for (const segment of result.segments) {\n console.log(` \u2022 ${segment.segment} (${segment.keyCount})${segment.path ? ` ${segment.path}` : ''}`)\n }\n }\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runCachePurge(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const quiet = flagEnabled(args, 'quiet')\n const dryRun = flagEnabled(args, 'dry-run', 'dryRun')\n const request = resolveCachePurgeRequest(args)\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n const cache = container.resolve('cache') as CacheStrategy\n const scopes = await resolveCacheScopes(em, args)\n const results = []\n\n for (const scope of scopes) {\n const result = await runWithCacheTenant(scope.tenantId, async () =>\n dryRun ? previewCachePurge(cache, request) : executeCachePurge(cache, request)\n )\n results.push({\n scope: scope.label,\n dryRun,\n request,\n deleted: result.deleted,\n keyCount: result.keys.length,\n keys: result.keys,\n note: result.note,\n })\n }\n\n if (json) {\n console.log(JSON.stringify(results, null, 2))\n return\n }\n\n if (quiet) {\n return\n }\n\n for (const result of results) {\n console.log(`${result.dryRun ? '\uD83E\uDDEA' : '\uD83E\uDDF9'} [cache] scope=${result.scope} deleted=${result.deleted}${result.dryRun ? ' (dry-run)' : ''}`)\n if (result.note) console.log(` \u2139\uFE0F note: ${result.note}`)\n if (result.keys.length > 0) {\n for (const key of result.keys) {\n console.log(` \u2022 ${key}`)\n }\n }\n }\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runStructuralCachePurge(args: ParsedArgs) {\n const nextArgs: ParsedArgs = {\n ...args,\n pattern: 'nav:*',\n }\n await runCachePurge(nextArgs)\n const quiet = flagEnabled(args, 'quiet')\n try {\n touchGeneratedBarrels({ quiet })\n } catch (err) {\n if (!quiet) {\n console.warn(\n `[structural] failed to touch generated barrels: ${(err as Error).message ?? err}`,\n )\n }\n }\n}\n\nfunction envDisablesAutoIndexing(): boolean {\n const raw =\n process.env.OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING ??\n process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING\n if (!raw) return false\n return parseBooleanToken(raw) === true\n}\n\nconst restoreDefaults: ModuleCli = {\n command: 'restore-defaults',\n async run() {\n const container = await createRequestContainer()\n try {\n let service: ModuleConfigService\n try {\n service = (container.resolve('moduleConfigService') as ModuleConfigService)\n } catch {\n console.error('[configs] moduleConfigService is not registered in the container.')\n return\n }\n\n const disabledByEnv = envDisablesAutoIndexing()\n const defaultEnabled = !disabledByEnv\n await service.restoreDefaults(\n [\n {\n moduleId: 'vector',\n name: 'auto_index_enabled',\n value: defaultEnabled,\n },\n {\n moduleId: 'notifications',\n name: NOTIFICATIONS_DELIVERY_CONFIG_KEY,\n value: DEFAULT_NOTIFICATION_DELIVERY_CONFIG,\n },\n ],\n { force: true },\n )\n console.log(\n `[configs] Vector auto-indexing default set to ${defaultEnabled ? 'enabled' : 'disabled'}${\n disabledByEnv\n ? ' (forced by OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING or legacy DISABLE_VECTOR_SEARCH_AUTOINDEXING)'\n : ''\n }.`,\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n },\n}\n\nconst help: ModuleCli = {\n command: 'help',\n async run() {\n console.log('\u2699\uFE0F Configs CLI')\n console.log('')\n console.log('\uD83D\uDE80 Usage: yarn mercato configs restore-defaults')\n console.log(' Ensures global module configuration defaults exist.')\n console.log('')\n printCacheHelp()\n },\n}\n\nconst cacheCommand: ModuleCli = {\n command: 'cache',\n async run(rest) {\n const [subcommand, ...subRest] = rest\n const args = parseArgs(subRest)\n\n if (!subcommand || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {\n printCacheHelp()\n return\n }\n\n if (subcommand === 'stats') {\n await runCacheStats(args)\n return\n }\n\n if (subcommand === 'purge') {\n await runCachePurge(args)\n return\n }\n\n if (subcommand === 'structural') {\n await runStructuralCachePurge(args)\n return\n }\n\n throw new Error(`Unknown cache subcommand \"${subcommand}\".`)\n },\n}\n\nexport default [restoreDefaults, cacheCommand, help]\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,46 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const GENERATED_DIR_RELATIVE = path.join(".mercato", "generated");
4
+ const TOUCHABLE_PATTERN = /\.generated(?:\.[a-z0-9]+)?(?:\.ts|\.checksum)$/i;
5
+ const MAX_PARENT_WALK = 5;
6
+ function findGeneratedDir(startDir) {
7
+ let current = path.resolve(startDir);
8
+ for (let depth = 0; depth <= MAX_PARENT_WALK; depth += 1) {
9
+ const candidate = path.join(current, GENERATED_DIR_RELATIVE);
10
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
11
+ return candidate;
12
+ }
13
+ const parent = path.dirname(current);
14
+ if (parent === current) break;
15
+ current = parent;
16
+ }
17
+ return null;
18
+ }
19
+ function touchGeneratedBarrels(options = {}) {
20
+ const cwd = options.cwd ?? process.cwd();
21
+ const log = options.log ?? ((message) => console.log(message));
22
+ const quiet = options.quiet === true;
23
+ const generatedDir = findGeneratedDir(cwd);
24
+ if (!generatedDir) {
25
+ return { generatedDir: null, files: [] };
26
+ }
27
+ const touched = [];
28
+ const entries = fs.readdirSync(generatedDir, { withFileTypes: true });
29
+ for (const entry of entries) {
30
+ if (!entry.isFile()) continue;
31
+ if (!TOUCHABLE_PATTERN.test(entry.name)) continue;
32
+ const filePath = path.join(generatedDir, entry.name);
33
+ const contents = fs.readFileSync(filePath);
34
+ fs.writeFileSync(filePath, contents);
35
+ touched.push(filePath);
36
+ }
37
+ if (!quiet && touched.length > 0) {
38
+ log(`\u{1F501} [structural] touched ${touched.length} generated barrel(s) \u2192 ${generatedDir}`);
39
+ }
40
+ return { generatedDir, files: touched };
41
+ }
42
+ export {
43
+ findGeneratedDir,
44
+ touchGeneratedBarrels
45
+ };
46
+ //# sourceMappingURL=touchGeneratedBarrels.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/configs/lib/touchGeneratedBarrels.ts"],
4
+ "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\n\nconst GENERATED_DIR_RELATIVE = path.join('.mercato', 'generated')\nconst TOUCHABLE_PATTERN = /\\.generated(?:\\.[a-z0-9]+)?(?:\\.ts|\\.checksum)$/i\nconst MAX_PARENT_WALK = 5\n\nexport type TouchGeneratedBarrelsOptions = {\n cwd?: string\n quiet?: boolean\n log?: (message: string) => void\n}\n\nexport type TouchGeneratedBarrelsResult = {\n generatedDir: string | null\n files: string[]\n}\n\nexport function findGeneratedDir(startDir: string): string | null {\n let current = path.resolve(startDir)\n for (let depth = 0; depth <= MAX_PARENT_WALK; depth += 1) {\n const candidate = path.join(current, GENERATED_DIR_RELATIVE)\n if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {\n return candidate\n }\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n return null\n}\n\nexport function touchGeneratedBarrels(\n options: TouchGeneratedBarrelsOptions = {},\n): TouchGeneratedBarrelsResult {\n const cwd = options.cwd ?? process.cwd()\n const log = options.log ?? ((message: string) => console.log(message))\n const quiet = options.quiet === true\n\n const generatedDir = findGeneratedDir(cwd)\n if (!generatedDir) {\n return { generatedDir: null, files: [] }\n }\n\n const touched: string[] = []\n const entries = fs.readdirSync(generatedDir, { withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isFile()) continue\n if (!TOUCHABLE_PATTERN.test(entry.name)) continue\n const filePath = path.join(generatedDir, entry.name)\n const contents = fs.readFileSync(filePath)\n fs.writeFileSync(filePath, contents)\n touched.push(filePath)\n }\n\n if (!quiet && touched.length > 0) {\n log(`\uD83D\uDD01 [structural] touched ${touched.length} generated barrel(s) \u2192 ${generatedDir}`)\n }\n\n return { generatedDir, files: touched }\n}\n"],
5
+ "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,MAAM,yBAAyB,KAAK,KAAK,YAAY,WAAW;AAChE,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AAajB,SAAS,iBAAiB,UAAiC;AAChE,MAAI,UAAU,KAAK,QAAQ,QAAQ;AACnC,WAAS,QAAQ,GAAG,SAAS,iBAAiB,SAAS,GAAG;AACxD,UAAM,YAAY,KAAK,KAAK,SAAS,sBAAsB;AAC3D,QAAI,GAAG,WAAW,SAAS,KAAK,GAAG,SAAS,SAAS,EAAE,YAAY,GAAG;AACpE,aAAO;AAAA,IACT;AACA,UAAM,SAAS,KAAK,QAAQ,OAAO;AACnC,QAAI,WAAW,QAAS;AACxB,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAEO,SAAS,sBACd,UAAwC,CAAC,GACZ;AAC7B,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,MAAM,QAAQ,QAAQ,CAAC,YAAoB,QAAQ,IAAI,OAAO;AACpE,QAAM,QAAQ,QAAQ,UAAU;AAEhC,QAAM,eAAe,iBAAiB,GAAG;AACzC,MAAI,CAAC,cAAc;AACjB,WAAO,EAAE,cAAc,MAAM,OAAO,CAAC,EAAE;AAAA,EACzC;AAEA,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAU,GAAG,YAAY,cAAc,EAAE,eAAe,KAAK,CAAC;AACpE,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,OAAO,EAAG;AACrB,QAAI,CAAC,kBAAkB,KAAK,MAAM,IAAI,EAAG;AACzC,UAAM,WAAW,KAAK,KAAK,cAAc,MAAM,IAAI;AACnD,UAAM,WAAW,GAAG,aAAa,QAAQ;AACzC,OAAG,cAAc,UAAU,QAAQ;AACnC,YAAQ,KAAK,QAAQ;AAAA,EACvB;AAEA,MAAI,CAAC,SAAS,QAAQ,SAAS,GAAG;AAChC,QAAI,kCAA2B,QAAQ,MAAM,+BAA0B,YAAY,EAAE;AAAA,EACvF;AAEA,SAAO,EAAE,cAAc,OAAO,QAAQ;AACxC;",
6
+ "names": []
7
+ }
@@ -1,7 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
- import { loadCustomFieldValues } from "@open-mercato/shared/lib/crud/custom-fields";
5
4
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
6
5
  import {
7
6
  runCrudMutationGuardAfterSuccess,
@@ -20,6 +19,7 @@ import {
20
19
  import { resolveCustomerInteractionFeatureFlags } from "../../lib/interactionFeatureFlags.js";
21
20
  import { resolveCustomersRequestContext } from "../../lib/interactionRequestContext.js";
22
21
  import { hydrateCanonicalInteractions } from "../../lib/interactionReadModel.js";
22
+ import { resolveCanonicalActivityTargetId } from "../../lib/legacyActivityBridge.js";
23
23
  const listSchema = z.object({
24
24
  page: z.coerce.number().min(1).default(1),
25
25
  pageSize: z.coerce.number().min(1).max(100).default(50),
@@ -164,57 +164,6 @@ function mapLegacyActivity(activity) {
164
164
  customValues: null
165
165
  };
166
166
  }
167
- async function loadLegacyActivityCustomValues(em, activity) {
168
- const values = await loadCustomFieldValues({
169
- em,
170
- entityId: "customers:customer_activity",
171
- recordIds: [activity.id],
172
- tenantIdByRecord: { [activity.id]: activity.tenantId },
173
- organizationIdByRecord: { [activity.id]: activity.organizationId },
174
- tenantFallbacks: [activity.tenantId]
175
- });
176
- return values[activity.id] ?? null;
177
- }
178
- async function ensureCanonicalActivityBridge(em, commandBus, commandContext, activity) {
179
- const existing = await em.findOne(CustomerInteraction, { id: activity.id, tenantId: activity.tenantId });
180
- if (existing) return existing.id;
181
- const entityId = typeof activity.entity === "string" ? activity.entity : activity.entity.id;
182
- const dealId = activity.deal ? typeof activity.deal === "string" ? activity.deal : activity.deal.id : null;
183
- const customValues = await loadLegacyActivityCustomValues(em, activity);
184
- await commandBus.execute("customers.interactions.create", {
185
- input: {
186
- id: activity.id,
187
- tenantId: activity.tenantId,
188
- organizationId: activity.organizationId,
189
- entityId,
190
- interactionType: activity.activityType,
191
- title: activity.subject ?? null,
192
- body: activity.body ?? null,
193
- occurredAt: activity.occurredAt ?? null,
194
- status: activity.occurredAt ? "done" : "planned",
195
- dealId,
196
- authorUserId: activity.authorUserId ?? null,
197
- appearanceIcon: activity.appearanceIcon ?? null,
198
- appearanceColor: activity.appearanceColor ?? null,
199
- source: CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,
200
- ...customValues ? { customValues } : {}
201
- },
202
- ctx: commandContext
203
- });
204
- return activity.id;
205
- }
206
- async function resolveCanonicalActivityTargetId(em, commandBus, commandContext, targetId, tenantId) {
207
- const existing = await em.findOne(CustomerInteraction, { id: targetId, tenantId });
208
- if (existing) return existing.id;
209
- const legacy = await em.findOne(CustomerActivity, { id: targetId, tenantId }, { populate: ["entity", "deal"] });
210
- if (!legacy) return targetId;
211
- return ensureCanonicalActivityBridge(
212
- em,
213
- commandBus,
214
- commandContext,
215
- legacy
216
- );
217
- }
218
167
  async function listCanonicalActivities(em, container, auth, selectedOrganizationId, tenantId, organizationIds, query, options) {
219
168
  const where = {
220
169
  tenantId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/api/activities/route.ts"],
4
- "sourcesContent": ["/**\n * @deprecated Use /api/customers/interactions instead. This route is maintained\n * as a compatibility bridge per SPEC-046b and delegates writes to canonical\n * interaction commands.\n */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CustomerActivity, CustomerDeal, CustomerInteraction } from '../../data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { activityCreateSchema, activityUpdateSchema } from '../../data/validators'\nimport { createCustomersCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\nimport {\n mapInteractionRecordToActivitySummary,\n CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n} from '../../lib/interactionCompatibility'\nimport { resolveCustomerInteractionFeatureFlags } from '../../lib/interactionFeatureFlags'\nimport { resolveCustomersRequestContext } from '../../lib/interactionRequestContext'\nimport { hydrateCanonicalInteractions } from '../../lib/interactionReadModel'\n\nconst listSchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n entityId: z.string().uuid().optional(),\n dealId: z.string().uuid().optional(),\n activityType: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n}).passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.activities.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst activityCreateBodySchema = activityCreateSchema.omit({\n organizationId: true,\n tenantId: true,\n}).passthrough()\n\nconst activityUpdateBodySchema = activityUpdateSchema.omit({\n organizationId: true,\n tenantId: true,\n}).passthrough()\n\nconst activityDeleteBodySchema = z.object({\n id: z.string().uuid(),\n})\n\nconst ADAPTER_HEADERS = {\n Deprecation: 'true',\n Sunset: 'Tue, 30 Jun 2026 00:00:00 GMT',\n Link: '</api/customers/interactions>; rel=\"successor-version\"',\n}\n\n// Caps the per-source fetch window used by the deprecated merged (legacy +\n// canonical bridge) read path. Keeps memory bounded on tenants with large\n// activity history; deep-pagination beyond this window is not supported here \u2014\n// use /api/customers/interactions instead.\nconst MERGED_ACTIVITY_FETCH_CAP = 2000\n\ntype ActivityItem = {\n id: string\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n createdAt: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n entityId?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n customValues?: Record<string, unknown> | null\n activityTypeLabel?: string | null\n}\n\ntype CanonicalActivityListResult = {\n items: ActivityItem[]\n total: number\n bridgeIds: Set<string>\n}\n\nfunction resolveGuardUserId(auth: {\n sub?: string | null\n userId?: string | null\n keyId?: string | null\n}): string {\n if (typeof auth.sub === 'string' && auth.sub.trim().length > 0) return auth.sub\n if (typeof auth.userId === 'string' && auth.userId.trim().length > 0) return auth.userId\n if (typeof auth.keyId === 'string' && auth.keyId.trim().length > 0) return auth.keyId\n return 'system'\n}\n\nfunction withAdapterHeaders(response: Response): Response {\n const headers = new Headers(response.headers)\n Object.entries(ADAPTER_HEADERS).forEach(([key, value]) => headers.set(key, value))\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n}\n\nasync function legacyAdaptersDisabledResponse(): Promise<Response> {\n const { translate } = await resolveTranslations()\n return withAdapterHeaders(NextResponse.json(\n {\n error: translate(\n 'customers.interactions.legacyAdapters.disabled',\n 'This legacy adapter has been disabled. Use /api/customers/interactions instead.',\n ),\n },\n { status: 410 },\n ))\n}\n\nfunction buildLegacyOrderBy(sortField: string | undefined, sortDir: 'asc' | 'desc') {\n if (sortField === 'createdAt') {\n return { createdAt: sortDir }\n }\n return { occurredAt: sortDir, createdAt: sortDir } as const\n}\n\nfunction buildCanonicalOrderBy(sortField: string | undefined, sortDir: 'asc' | 'desc') {\n if (sortField === 'createdAt') {\n return { createdAt: sortDir }\n }\n return { occurredAt: sortDir, createdAt: sortDir } as const\n}\n\nfunction resolveActivitySortValue(item: ActivityItem, sortField: string | undefined): number {\n const raw =\n sortField === 'createdAt'\n ? item.createdAt\n : item.occurredAt ?? item.createdAt\n const timestamp = raw ? new Date(raw).getTime() : Number.NaN\n return Number.isNaN(timestamp) ? 0 : timestamp\n}\n\nfunction sortActivityItems(\n items: ActivityItem[],\n sortField: string | undefined,\n sortDir: 'asc' | 'desc',\n): ActivityItem[] {\n return [...items].sort((left, right) => {\n const leftValue = resolveActivitySortValue(left, sortField)\n const rightValue = resolveActivitySortValue(right, sortField)\n if (leftValue === rightValue) {\n return sortDir === 'asc'\n ? left.id.localeCompare(right.id)\n : right.id.localeCompare(left.id)\n }\n return sortDir === 'asc' ? leftValue - rightValue : rightValue - leftValue\n })\n}\n\nfunction paginateActivityItems(\n items: ActivityItem[],\n page: number,\n pageSize: number,\n): { items: ActivityItem[]; total: number } {\n const start = (page - 1) * pageSize\n return {\n items: items.slice(start, start + pageSize),\n total: items.length,\n }\n}\n\nasync function decorateActivityItems(\n em: EntityManager,\n items: ActivityItem[],\n decryptionScope?: { tenantId: string; organizationId: string },\n): Promise<ActivityItem[]> {\n if (items.length === 0) return items\n\n const authorIds = Array.from(\n new Set(\n items\n .map((item) => (typeof item.authorUserId === 'string' ? item.authorUserId : null))\n .filter((value): value is string => !!value),\n ),\n )\n const dealIds = Array.from(\n new Set(\n items\n .map((item) => (typeof item.dealId === 'string' ? item.dealId : null))\n .filter((value): value is string => !!value),\n ),\n )\n\n const [users, deals] = await Promise.all([\n authorIds.length > 0 ? em.find(User, { id: { $in: authorIds } }) : Promise.resolve([]),\n dealIds.length > 0\n ? decryptionScope\n ? findWithDecryption(em, CustomerDeal, { id: { $in: dealIds } }, undefined, decryptionScope)\n : em.find(CustomerDeal, { id: { $in: dealIds } })\n : Promise.resolve([]),\n ])\n\n const userMap = new Map(\n users.map((user) => [\n user.id,\n {\n name: user.name ?? null,\n email: user.email ?? null,\n },\n ]),\n )\n const dealMap = new Map(deals.map((deal) => [deal.id, deal.title]))\n\n return items.map((item) => ({\n ...item,\n activityTypeLabel: item.activityType,\n authorName: item.authorUserId ? userMap.get(item.authorUserId)?.name ?? null : null,\n authorEmail: item.authorUserId ? userMap.get(item.authorUserId)?.email ?? null : null,\n dealTitle: item.dealId ? dealMap.get(item.dealId) ?? null : null,\n }))\n}\n\nfunction mapLegacyActivity(activity: CustomerActivity): ActivityItem {\n return {\n id: activity.id,\n activityType: activity.activityType,\n subject: activity.subject ?? null,\n body: activity.body ?? null,\n occurredAt: activity.occurredAt ? activity.occurredAt.toISOString() : null,\n createdAt: activity.createdAt.toISOString(),\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n entityId: typeof activity.entity === 'string' ? activity.entity : activity.entity.id,\n authorUserId: activity.authorUserId ?? null,\n dealId: activity.deal ? (typeof activity.deal === 'string' ? activity.deal : activity.deal.id) : null,\n customValues: null,\n }\n}\n\nasync function loadLegacyActivityCustomValues(\n em: EntityManager,\n activity: CustomerActivity,\n): Promise<Record<string, unknown> | null> {\n const values = await loadCustomFieldValues({\n em,\n entityId: 'customers:customer_activity',\n recordIds: [activity.id],\n tenantIdByRecord: { [activity.id]: activity.tenantId },\n organizationIdByRecord: { [activity.id]: activity.organizationId },\n tenantFallbacks: [activity.tenantId],\n })\n return values[activity.id] ?? null\n}\n\nasync function ensureCanonicalActivityBridge(\n em: EntityManager,\n commandBus: CommandBus,\n commandContext: Parameters<CommandBus['execute']>[1]['ctx'],\n activity: CustomerActivity,\n): Promise<string> {\n const existing = await em.findOne(CustomerInteraction, { id: activity.id, tenantId: activity.tenantId })\n if (existing) return existing.id\n\n const entityId = typeof activity.entity === 'string' ? activity.entity : activity.entity.id\n const dealId = activity.deal\n ? (typeof activity.deal === 'string' ? activity.deal : activity.deal.id)\n : null\n const customValues = await loadLegacyActivityCustomValues(em, activity)\n\n await commandBus.execute('customers.interactions.create', {\n input: {\n id: activity.id,\n tenantId: activity.tenantId,\n organizationId: activity.organizationId,\n entityId,\n interactionType: activity.activityType,\n title: activity.subject ?? null,\n body: activity.body ?? null,\n occurredAt: activity.occurredAt ?? null,\n status: activity.occurredAt ? 'done' : 'planned',\n dealId,\n authorUserId: activity.authorUserId ?? null,\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n source: CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n ...(customValues ? { customValues } : {}),\n },\n ctx: commandContext,\n })\n\n return activity.id\n}\n\nasync function resolveCanonicalActivityTargetId(\n em: EntityManager,\n commandBus: CommandBus,\n commandContext: Parameters<CommandBus['execute']>[1]['ctx'],\n targetId: string,\n tenantId: string,\n): Promise<string> {\n const existing = await em.findOne(CustomerInteraction, { id: targetId, tenantId })\n if (existing) return existing.id\n\n const legacy = await em.findOne(CustomerActivity, { id: targetId, tenantId }, { populate: ['entity', 'deal'] })\n if (!legacy) return targetId\n\n return ensureCanonicalActivityBridge(\n em,\n commandBus,\n commandContext,\n legacy,\n )\n}\n\nasync function listCanonicalActivities(\n em: EntityManager,\n container: { resolve: (name: string) => unknown },\n auth: { tenantId: string | null; orgId: string | null; sub?: string | null; userId?: string | null; keyId?: string | null },\n selectedOrganizationId: string | null,\n tenantId: string,\n organizationIds: string[] | null,\n query: z.infer<typeof listSchema>,\n options?: { includeDeleted?: boolean; source?: string | string[] | null; paginate?: boolean },\n): Promise<CanonicalActivityListResult> {\n const where: Record<string, unknown> = {\n tenantId,\n interactionType: { $ne: 'task' },\n }\n if (!options?.includeDeleted) {\n where.deletedAt = null\n }\n if (organizationIds && organizationIds.length > 0) {\n where.organizationId = { $in: organizationIds }\n }\n if (query.entityId) where.entity = query.entityId\n if (query.dealId) where.dealId = query.dealId\n if (query.activityType) where.interactionType = query.activityType\n if (options?.source) {\n where.source = Array.isArray(options.source) ? { $in: options.source } : options.source\n }\n\n const findOptions = {\n orderBy: buildCanonicalOrderBy(query.sortField, query.sortDir ?? 'desc'),\n ...(options?.paginate === false\n ? {}\n : {\n offset: (query.page - 1) * query.pageSize,\n limit: query.pageSize,\n }),\n }\n\n const rows =\n options?.paginate === false\n ? await em.find(CustomerInteraction, where, findOptions)\n : (await em.findAndCount(CustomerInteraction, where, findOptions))[0]\n const total =\n options?.paginate === false\n ? rows.filter((row) => !row.deletedAt).length\n : await em.count(CustomerInteraction, where)\n\n const activeRows = rows.filter((row) => !row.deletedAt)\n const hydrated = await hydrateCanonicalInteractions({\n em,\n container,\n auth,\n selectedOrganizationId,\n interactions: activeRows,\n })\n const items = hydrated.map((row) => ({\n ...mapInteractionRecordToActivitySummary(row),\n customValues: row.customValues ?? null,\n activityTypeLabel: row.interactionType,\n }))\n\n return {\n items,\n total,\n bridgeIds: new Set(rows.map((row) => row.id)),\n }\n}\n\nasync function listLegacyActivities(\n em: EntityManager,\n tenantId: string,\n organizationIds: string[] | null,\n query: z.infer<typeof listSchema>,\n options?: { paginate?: boolean },\n selectedOrganizationId?: string | null,\n): Promise<{ items: ActivityItem[]; total: number }> {\n const where: Record<string, unknown> = { tenantId }\n if (organizationIds && organizationIds.length > 0) {\n where.organizationId = { $in: organizationIds }\n }\n if (query.entityId) where.entity = query.entityId\n if (query.dealId) where.deal = query.dealId\n if (query.activityType) where.activityType = query.activityType\n\n const findOptions = {\n populate: ['entity', 'deal'] as const,\n orderBy: buildLegacyOrderBy(query.sortField, query.sortDir ?? 'desc'),\n ...(options?.paginate === false\n ? {}\n : {\n offset: (query.page - 1) * query.pageSize,\n limit: query.pageSize,\n }),\n }\n\n const rows =\n options?.paginate === false\n ? await em.find(CustomerActivity, where, findOptions)\n : (await em.findAndCount(CustomerActivity, where, findOptions))[0]\n const total =\n options?.paginate === false\n ? rows.length\n : await em.count(CustomerActivity, where)\n\n return {\n items: await decorateActivityItems(\n em,\n rows.map(mapLegacyActivity),\n selectedOrganizationId ? { tenantId, organizationId: selectedOrganizationId } : undefined,\n ),\n total,\n }\n}\n\nexport async function GET(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const query = listSchema.parse(Object.fromEntries(url.searchParams))\n const {\n auth,\n em,\n organizationIds,\n container,\n selectedOrganizationId,\n } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n\n const sortDir = query.sortDir ?? 'desc'\n\n const result = flags.unified\n ? await listCanonicalActivities(\n em,\n container,\n auth,\n selectedOrganizationId,\n auth.tenantId,\n organizationIds,\n query,\n )\n : await (async () => {\n const windowSize = Math.min(\n MERGED_ACTIVITY_FETCH_CAP,\n Math.max(query.pageSize, query.page * query.pageSize + query.pageSize),\n )\n const windowedQuery = { ...query, page: 1, pageSize: windowSize }\n const [legacy, canonical] = await Promise.all([\n listLegacyActivities(\n em,\n auth.tenantId,\n organizationIds,\n windowedQuery,\n { paginate: true },\n selectedOrganizationId,\n ),\n listCanonicalActivities(\n em,\n container,\n auth,\n selectedOrganizationId,\n auth.tenantId,\n organizationIds,\n windowedQuery,\n {\n includeDeleted: true,\n paginate: true,\n source: CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n },\n ),\n ])\n const merged = sortActivityItems(\n [\n ...legacy.items.filter((item) => !canonical.bridgeIds.has(item.id)),\n ...canonical.items,\n ],\n query.sortField,\n sortDir,\n )\n const paged = paginateActivityItems(merged, query.page, query.pageSize)\n return {\n items: paged.items,\n total: paged.total,\n }\n })()\n\n return withAdapterHeaders(\n NextResponse.json({\n items: result.items,\n total: result.total,\n page: query.page,\n pageSize: query.pageSize,\n totalPages: Math.max(1, Math.ceil(result.total / query.pageSize)),\n }),\n )\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.get failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function POST(request: Request): Promise<Response> {\n try {\n const { commandContext, container, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const parsed = activityCreateBodySchema.parse(body)\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: parsed,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute('customers.interactions.create', {\n input: {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId ?? auth.orgId,\n entityId: parsed.entityId,\n interactionType: parsed.activityType,\n title: parsed.subject ?? null,\n body: parsed.body ?? null,\n occurredAt: parsed.occurredAt ?? null,\n status: parsed.occurredAt ? 'done' : 'planned',\n dealId: parsed.dealId ?? null,\n authorUserId: parsed.authorUserId ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n source: CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n customFields: (parsed as Record<string, unknown>).customFields,\n customValues: (parsed as Record<string, unknown>).customValues,\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(\n NextResponse.json(\n {\n id:\n result &&\n typeof result === 'object' &&\n 'interactionId' in result &&\n typeof result.interactionId === 'string'\n ? result.interactionId\n : result &&\n typeof result === 'object' &&\n 'id' in result &&\n typeof result.id === 'string'\n ? result.id\n : null,\n },\n { status: 201 },\n ),\n )\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.post failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function PUT(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const parsed = activityUpdateBodySchema.parse(body)\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: parsed,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const interactionId = flags.unified\n ? parsed.id\n : await resolveCanonicalActivityTargetId(em, commandBus, commandContext, parsed.id, auth.tenantId)\n\n await commandBus.execute('customers.interactions.update', {\n input: {\n id: interactionId,\n interactionType: parsed.activityType,\n title: parsed.subject ?? undefined,\n body: parsed.body ?? undefined,\n occurredAt: parsed.occurredAt ?? undefined,\n status: parsed.occurredAt ? 'done' : undefined,\n dealId: parsed.dealId ?? undefined,\n authorUserId: parsed.authorUserId ?? undefined,\n appearanceIcon: parsed.appearanceIcon ?? undefined,\n appearanceColor: parsed.appearanceColor ?? undefined,\n customFields: (parsed as Record<string, unknown>).customFields,\n customValues: (parsed as Record<string, unknown>).customValues,\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(NextResponse.json({ ok: true }))\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.put failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function DELETE(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const parsed = activityDeleteBodySchema.parse(body)\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: parsed,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const interactionId = flags.unified\n ? parsed.id\n : await resolveCanonicalActivityTargetId(em, commandBus, commandContext, parsed.id, auth.tenantId)\n await commandBus.execute('customers.interactions.delete', {\n input: { id: interactionId },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n return withAdapterHeaders(NextResponse.json({ ok: true }))\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.delete failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nconst activityListItemSchema = z\n .object({\n id: z.string().uuid(),\n activityType: z.string(),\n subject: z.string().nullable().optional(),\n body: z.string().nullable().optional(),\n occurredAt: z.string().nullable().optional(),\n createdAt: z.string(),\n appearanceIcon: z.string().nullable().optional(),\n appearanceColor: z.string().nullable().optional(),\n entityId: z.string().uuid().nullable().optional(),\n authorUserId: z.string().uuid().nullable().optional(),\n authorName: z.string().nullable().optional(),\n authorEmail: z.string().nullable().optional(),\n dealId: z.string().uuid().nullable().optional(),\n dealTitle: z.string().nullable().optional(),\n customValues: z.record(z.string(), z.unknown()).nullable().optional(),\n activityTypeLabel: z.string().nullable().optional(),\n })\n .passthrough()\n\nconst activityCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = createCustomersCrudOpenApi({\n resourceName: 'Activity',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(activityListItemSchema),\n create: {\n schema: activityCreateBodySchema,\n responseSchema: activityCreateResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Creates a timeline activity. Use POST /api/customers/interactions instead.',\n },\n update: {\n schema: activityUpdateBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Updates an activity. Use PUT /api/customers/interactions instead.',\n },\n del: {\n schema: activityDeleteBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Deletes an activity. Use DELETE /api/customers/interactions instead.',\n },\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * @deprecated Use /api/customers/interactions instead. This route is maintained\n * as a compatibility bridge per SPEC-046b and delegates writes to canonical\n * interaction commands.\n */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CustomerActivity, CustomerDeal, CustomerInteraction } from '../../data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { activityCreateSchema, activityUpdateSchema } from '../../data/validators'\nimport { createCustomersCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\nimport {\n mapInteractionRecordToActivitySummary,\n CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n} from '../../lib/interactionCompatibility'\nimport { resolveCustomerInteractionFeatureFlags } from '../../lib/interactionFeatureFlags'\nimport { resolveCustomersRequestContext } from '../../lib/interactionRequestContext'\nimport { hydrateCanonicalInteractions } from '../../lib/interactionReadModel'\nimport { resolveCanonicalActivityTargetId } from '../../lib/legacyActivityBridge'\n\nconst listSchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n entityId: z.string().uuid().optional(),\n dealId: z.string().uuid().optional(),\n activityType: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n}).passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.activities.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst activityCreateBodySchema = activityCreateSchema.omit({\n organizationId: true,\n tenantId: true,\n}).passthrough()\n\nconst activityUpdateBodySchema = activityUpdateSchema.omit({\n organizationId: true,\n tenantId: true,\n}).passthrough()\n\nconst activityDeleteBodySchema = z.object({\n id: z.string().uuid(),\n})\n\nconst ADAPTER_HEADERS = {\n Deprecation: 'true',\n Sunset: 'Tue, 30 Jun 2026 00:00:00 GMT',\n Link: '</api/customers/interactions>; rel=\"successor-version\"',\n}\n\n// Caps the per-source fetch window used by the deprecated merged (legacy +\n// canonical bridge) read path. Keeps memory bounded on tenants with large\n// activity history; deep-pagination beyond this window is not supported here \u2014\n// use /api/customers/interactions instead.\nconst MERGED_ACTIVITY_FETCH_CAP = 2000\n\ntype ActivityItem = {\n id: string\n activityType: string\n subject?: string | null\n body?: string | null\n occurredAt?: string | null\n createdAt: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n entityId?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n customValues?: Record<string, unknown> | null\n activityTypeLabel?: string | null\n}\n\ntype CanonicalActivityListResult = {\n items: ActivityItem[]\n total: number\n bridgeIds: Set<string>\n}\n\nfunction resolveGuardUserId(auth: {\n sub?: string | null\n userId?: string | null\n keyId?: string | null\n}): string {\n if (typeof auth.sub === 'string' && auth.sub.trim().length > 0) return auth.sub\n if (typeof auth.userId === 'string' && auth.userId.trim().length > 0) return auth.userId\n if (typeof auth.keyId === 'string' && auth.keyId.trim().length > 0) return auth.keyId\n return 'system'\n}\n\nfunction withAdapterHeaders(response: Response): Response {\n const headers = new Headers(response.headers)\n Object.entries(ADAPTER_HEADERS).forEach(([key, value]) => headers.set(key, value))\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n}\n\nasync function legacyAdaptersDisabledResponse(): Promise<Response> {\n const { translate } = await resolveTranslations()\n return withAdapterHeaders(NextResponse.json(\n {\n error: translate(\n 'customers.interactions.legacyAdapters.disabled',\n 'This legacy adapter has been disabled. Use /api/customers/interactions instead.',\n ),\n },\n { status: 410 },\n ))\n}\n\nfunction buildLegacyOrderBy(sortField: string | undefined, sortDir: 'asc' | 'desc') {\n if (sortField === 'createdAt') {\n return { createdAt: sortDir }\n }\n return { occurredAt: sortDir, createdAt: sortDir } as const\n}\n\nfunction buildCanonicalOrderBy(sortField: string | undefined, sortDir: 'asc' | 'desc') {\n if (sortField === 'createdAt') {\n return { createdAt: sortDir }\n }\n return { occurredAt: sortDir, createdAt: sortDir } as const\n}\n\nfunction resolveActivitySortValue(item: ActivityItem, sortField: string | undefined): number {\n const raw =\n sortField === 'createdAt'\n ? item.createdAt\n : item.occurredAt ?? item.createdAt\n const timestamp = raw ? new Date(raw).getTime() : Number.NaN\n return Number.isNaN(timestamp) ? 0 : timestamp\n}\n\nfunction sortActivityItems(\n items: ActivityItem[],\n sortField: string | undefined,\n sortDir: 'asc' | 'desc',\n): ActivityItem[] {\n return [...items].sort((left, right) => {\n const leftValue = resolveActivitySortValue(left, sortField)\n const rightValue = resolveActivitySortValue(right, sortField)\n if (leftValue === rightValue) {\n return sortDir === 'asc'\n ? left.id.localeCompare(right.id)\n : right.id.localeCompare(left.id)\n }\n return sortDir === 'asc' ? leftValue - rightValue : rightValue - leftValue\n })\n}\n\nfunction paginateActivityItems(\n items: ActivityItem[],\n page: number,\n pageSize: number,\n): { items: ActivityItem[]; total: number } {\n const start = (page - 1) * pageSize\n return {\n items: items.slice(start, start + pageSize),\n total: items.length,\n }\n}\n\nasync function decorateActivityItems(\n em: EntityManager,\n items: ActivityItem[],\n decryptionScope?: { tenantId: string; organizationId: string },\n): Promise<ActivityItem[]> {\n if (items.length === 0) return items\n\n const authorIds = Array.from(\n new Set(\n items\n .map((item) => (typeof item.authorUserId === 'string' ? item.authorUserId : null))\n .filter((value): value is string => !!value),\n ),\n )\n const dealIds = Array.from(\n new Set(\n items\n .map((item) => (typeof item.dealId === 'string' ? item.dealId : null))\n .filter((value): value is string => !!value),\n ),\n )\n\n const [users, deals] = await Promise.all([\n authorIds.length > 0 ? em.find(User, { id: { $in: authorIds } }) : Promise.resolve([]),\n dealIds.length > 0\n ? decryptionScope\n ? findWithDecryption(em, CustomerDeal, { id: { $in: dealIds } }, undefined, decryptionScope)\n : em.find(CustomerDeal, { id: { $in: dealIds } })\n : Promise.resolve([]),\n ])\n\n const userMap = new Map(\n users.map((user) => [\n user.id,\n {\n name: user.name ?? null,\n email: user.email ?? null,\n },\n ]),\n )\n const dealMap = new Map(deals.map((deal) => [deal.id, deal.title]))\n\n return items.map((item) => ({\n ...item,\n activityTypeLabel: item.activityType,\n authorName: item.authorUserId ? userMap.get(item.authorUserId)?.name ?? null : null,\n authorEmail: item.authorUserId ? userMap.get(item.authorUserId)?.email ?? null : null,\n dealTitle: item.dealId ? dealMap.get(item.dealId) ?? null : null,\n }))\n}\n\nfunction mapLegacyActivity(activity: CustomerActivity): ActivityItem {\n return {\n id: activity.id,\n activityType: activity.activityType,\n subject: activity.subject ?? null,\n body: activity.body ?? null,\n occurredAt: activity.occurredAt ? activity.occurredAt.toISOString() : null,\n createdAt: activity.createdAt.toISOString(),\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n entityId: typeof activity.entity === 'string' ? activity.entity : activity.entity.id,\n authorUserId: activity.authorUserId ?? null,\n dealId: activity.deal ? (typeof activity.deal === 'string' ? activity.deal : activity.deal.id) : null,\n customValues: null,\n }\n}\n\nasync function listCanonicalActivities(\n em: EntityManager,\n container: { resolve: (name: string) => unknown },\n auth: { tenantId: string | null; orgId: string | null; sub?: string | null; userId?: string | null; keyId?: string | null },\n selectedOrganizationId: string | null,\n tenantId: string,\n organizationIds: string[] | null,\n query: z.infer<typeof listSchema>,\n options?: { includeDeleted?: boolean; source?: string | string[] | null; paginate?: boolean },\n): Promise<CanonicalActivityListResult> {\n const where: Record<string, unknown> = {\n tenantId,\n interactionType: { $ne: 'task' },\n }\n if (!options?.includeDeleted) {\n where.deletedAt = null\n }\n if (organizationIds && organizationIds.length > 0) {\n where.organizationId = { $in: organizationIds }\n }\n if (query.entityId) where.entity = query.entityId\n if (query.dealId) where.dealId = query.dealId\n if (query.activityType) where.interactionType = query.activityType\n if (options?.source) {\n where.source = Array.isArray(options.source) ? { $in: options.source } : options.source\n }\n\n const findOptions = {\n orderBy: buildCanonicalOrderBy(query.sortField, query.sortDir ?? 'desc'),\n ...(options?.paginate === false\n ? {}\n : {\n offset: (query.page - 1) * query.pageSize,\n limit: query.pageSize,\n }),\n }\n\n const rows =\n options?.paginate === false\n ? await em.find(CustomerInteraction, where, findOptions)\n : (await em.findAndCount(CustomerInteraction, where, findOptions))[0]\n const total =\n options?.paginate === false\n ? rows.filter((row) => !row.deletedAt).length\n : await em.count(CustomerInteraction, where)\n\n const activeRows = rows.filter((row) => !row.deletedAt)\n const hydrated = await hydrateCanonicalInteractions({\n em,\n container,\n auth,\n selectedOrganizationId,\n interactions: activeRows,\n })\n const items = hydrated.map((row) => ({\n ...mapInteractionRecordToActivitySummary(row),\n customValues: row.customValues ?? null,\n activityTypeLabel: row.interactionType,\n }))\n\n return {\n items,\n total,\n bridgeIds: new Set(rows.map((row) => row.id)),\n }\n}\n\nasync function listLegacyActivities(\n em: EntityManager,\n tenantId: string,\n organizationIds: string[] | null,\n query: z.infer<typeof listSchema>,\n options?: { paginate?: boolean },\n selectedOrganizationId?: string | null,\n): Promise<{ items: ActivityItem[]; total: number }> {\n const where: Record<string, unknown> = { tenantId }\n if (organizationIds && organizationIds.length > 0) {\n where.organizationId = { $in: organizationIds }\n }\n if (query.entityId) where.entity = query.entityId\n if (query.dealId) where.deal = query.dealId\n if (query.activityType) where.activityType = query.activityType\n\n const findOptions = {\n populate: ['entity', 'deal'] as const,\n orderBy: buildLegacyOrderBy(query.sortField, query.sortDir ?? 'desc'),\n ...(options?.paginate === false\n ? {}\n : {\n offset: (query.page - 1) * query.pageSize,\n limit: query.pageSize,\n }),\n }\n\n const rows =\n options?.paginate === false\n ? await em.find(CustomerActivity, where, findOptions)\n : (await em.findAndCount(CustomerActivity, where, findOptions))[0]\n const total =\n options?.paginate === false\n ? rows.length\n : await em.count(CustomerActivity, where)\n\n return {\n items: await decorateActivityItems(\n em,\n rows.map(mapLegacyActivity),\n selectedOrganizationId ? { tenantId, organizationId: selectedOrganizationId } : undefined,\n ),\n total,\n }\n}\n\nexport async function GET(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const query = listSchema.parse(Object.fromEntries(url.searchParams))\n const {\n auth,\n em,\n organizationIds,\n container,\n selectedOrganizationId,\n } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n\n const sortDir = query.sortDir ?? 'desc'\n\n const result = flags.unified\n ? await listCanonicalActivities(\n em,\n container,\n auth,\n selectedOrganizationId,\n auth.tenantId,\n organizationIds,\n query,\n )\n : await (async () => {\n const windowSize = Math.min(\n MERGED_ACTIVITY_FETCH_CAP,\n Math.max(query.pageSize, query.page * query.pageSize + query.pageSize),\n )\n const windowedQuery = { ...query, page: 1, pageSize: windowSize }\n const [legacy, canonical] = await Promise.all([\n listLegacyActivities(\n em,\n auth.tenantId,\n organizationIds,\n windowedQuery,\n { paginate: true },\n selectedOrganizationId,\n ),\n listCanonicalActivities(\n em,\n container,\n auth,\n selectedOrganizationId,\n auth.tenantId,\n organizationIds,\n windowedQuery,\n {\n includeDeleted: true,\n paginate: true,\n source: CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n },\n ),\n ])\n const merged = sortActivityItems(\n [\n ...legacy.items.filter((item) => !canonical.bridgeIds.has(item.id)),\n ...canonical.items,\n ],\n query.sortField,\n sortDir,\n )\n const paged = paginateActivityItems(merged, query.page, query.pageSize)\n return {\n items: paged.items,\n total: paged.total,\n }\n })()\n\n return withAdapterHeaders(\n NextResponse.json({\n items: result.items,\n total: result.total,\n page: query.page,\n pageSize: query.pageSize,\n totalPages: Math.max(1, Math.ceil(result.total / query.pageSize)),\n }),\n )\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.get failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function POST(request: Request): Promise<Response> {\n try {\n const { commandContext, container, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const parsed = activityCreateBodySchema.parse(body)\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: parsed,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute('customers.interactions.create', {\n input: {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId ?? auth.orgId,\n entityId: parsed.entityId,\n interactionType: parsed.activityType,\n title: parsed.subject ?? null,\n body: parsed.body ?? null,\n occurredAt: parsed.occurredAt ?? null,\n status: parsed.occurredAt ? 'done' : 'planned',\n dealId: parsed.dealId ?? null,\n authorUserId: parsed.authorUserId ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n source: CUSTOMER_INTERACTION_ACTIVITY_ADAPTER_SOURCE,\n customFields: (parsed as Record<string, unknown>).customFields,\n customValues: (parsed as Record<string, unknown>).customValues,\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(\n NextResponse.json(\n {\n id:\n result &&\n typeof result === 'object' &&\n 'interactionId' in result &&\n typeof result.interactionId === 'string'\n ? result.interactionId\n : result &&\n typeof result === 'object' &&\n 'id' in result &&\n typeof result.id === 'string'\n ? result.id\n : null,\n },\n { status: 201 },\n ),\n )\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.post failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function PUT(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const parsed = activityUpdateBodySchema.parse(body)\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: parsed,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const interactionId = flags.unified\n ? parsed.id\n : await resolveCanonicalActivityTargetId(em, commandBus, commandContext, parsed.id, auth.tenantId)\n\n await commandBus.execute('customers.interactions.update', {\n input: {\n id: interactionId,\n interactionType: parsed.activityType,\n title: parsed.subject ?? undefined,\n body: parsed.body ?? undefined,\n occurredAt: parsed.occurredAt ?? undefined,\n status: parsed.occurredAt ? 'done' : undefined,\n dealId: parsed.dealId ?? undefined,\n authorUserId: parsed.authorUserId ?? undefined,\n appearanceIcon: parsed.appearanceIcon ?? undefined,\n appearanceColor: parsed.appearanceColor ?? undefined,\n customFields: (parsed as Record<string, unknown>).customFields,\n customValues: (parsed as Record<string, unknown>).customValues,\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(NextResponse.json({ ok: true }))\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.put failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function DELETE(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const parsed = activityDeleteBodySchema.parse(body)\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: parsed,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const interactionId = flags.unified\n ? parsed.id\n : await resolveCanonicalActivityTargetId(em, commandBus, commandContext, parsed.id, auth.tenantId)\n await commandBus.execute('customers.interactions.delete', {\n input: { id: interactionId },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.activity',\n resourceId: parsed.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n return withAdapterHeaders(NextResponse.json({ ok: true }))\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n console.error('customers.activities.delete failed', err)\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nconst activityListItemSchema = z\n .object({\n id: z.string().uuid(),\n activityType: z.string(),\n subject: z.string().nullable().optional(),\n body: z.string().nullable().optional(),\n occurredAt: z.string().nullable().optional(),\n createdAt: z.string(),\n appearanceIcon: z.string().nullable().optional(),\n appearanceColor: z.string().nullable().optional(),\n entityId: z.string().uuid().nullable().optional(),\n authorUserId: z.string().uuid().nullable().optional(),\n authorName: z.string().nullable().optional(),\n authorEmail: z.string().nullable().optional(),\n dealId: z.string().uuid().nullable().optional(),\n dealTitle: z.string().nullable().optional(),\n customValues: z.record(z.string(), z.unknown()).nullable().optional(),\n activityTypeLabel: z.string().nullable().optional(),\n })\n .passthrough()\n\nconst activityCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = createCustomersCrudOpenApi({\n resourceName: 'Activity',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(activityListItemSchema),\n create: {\n schema: activityCreateBodySchema,\n responseSchema: activityCreateResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Creates a timeline activity. Use POST /api/customers/interactions instead.',\n },\n update: {\n schema: activityUpdateBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Updates an activity. Use PUT /api/customers/interactions instead.',\n },\n del: {\n schema: activityDeleteBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Deletes an activity. Use DELETE /api/customers/interactions instead.',\n },\n})\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -17,6 +17,7 @@ const responseSchema = z.object({
17
17
  email: z.number(),
18
18
  meeting: z.number(),
19
19
  note: z.number(),
20
+ task: z.number(),
20
21
  total: z.number()
21
22
  })
22
23
  });
@@ -66,7 +67,7 @@ async function GET(req) {
66
67
  baseQuery = baseQuery.where("status", "=", query.status);
67
68
  }
68
69
  const rows = await baseQuery.select(["interaction_type", sql`count(*)`.as("count")]).groupBy("interaction_type").execute();
69
- const counts = { call: 0, email: 0, meeting: 0, note: 0 };
70
+ const counts = { call: 0, email: 0, meeting: 0, note: 0, task: 0 };
70
71
  let total = 0;
71
72
  for (const row of rows) {
72
73
  const count = typeof row.count === "string" ? parseInt(row.count, 10) : row.count;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/api/interactions/counts/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { sql } from 'kysely'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst querySchema = z.object({\n entityId: z.string().uuid(),\n status: z.enum(['done', 'planned']).optional(),\n})\n\nconst responseSchema = z.object({\n ok: z.literal(true),\n result: z.object({\n call: z.number(),\n email: z.number(),\n meeting: z.number(),\n note: z.number(),\n total: z.number(),\n }),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.interactions.view'] },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n GET: {\n summary: 'Get interaction counts by type',\n description: 'Returns per-type interaction counts scoped to an entity.',\n query: querySchema,\n responses: [\n {\n status: 200,\n description: 'Counts by interaction type',\n schema: responseSchema,\n },\n ],\n },\n },\n}\n\nexport async function GET(req: Request) {\n try {\n const queryUrl = new URL(req.url)\n const query = querySchema.parse(Object.fromEntries(queryUrl.searchParams))\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, {\n error: translate('customers.errors.unauthorized', 'Unauthorized'),\n })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationIds = Array.isArray(scope?.filterIds) && scope.filterIds.length > 0\n ? scope.filterIds\n : auth.orgId\n ? [auth.orgId]\n : []\n const em = (container.resolve('em') as EntityManager).fork()\n const kysely = em.getKysely<any>()\n\n let baseQuery = (kysely as any)\n .selectFrom('customer_interactions')\n .where('entity_id', '=', query.entityId)\n .where('tenant_id', '=', auth.tenantId)\n .where('deleted_at', 'is', null)\n\n if (organizationIds.length === 1) {\n baseQuery = baseQuery.where('organization_id', '=', organizationIds[0])\n } else if (organizationIds.length > 1) {\n baseQuery = baseQuery.where('organization_id', 'in', organizationIds)\n }\n\n if (query.status) {\n baseQuery = baseQuery.where('status', '=', query.status)\n }\n\n // Raw SELECT: reads only unencrypted columns (id, interaction_type); title/notes are excluded to avoid ciphertext leakage.\n const rows = await baseQuery\n .select(['interaction_type', sql<string>`count(*)`.as('count')])\n .groupBy('interaction_type')\n .execute() as Array<{ interaction_type: string; count: string | number }>\n\n const counts: Record<string, number> = { call: 0, email: 0, meeting: 0, note: 0 }\n let total = 0\n for (const row of rows) {\n const count = typeof row.count === 'string' ? parseInt(row.count, 10) : row.count\n const type = row.interaction_type\n if (type in counts) {\n counts[type] = count\n }\n total += count\n }\n\n return NextResponse.json({ ok: true, result: { ...counts, total } })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[customers/interactions/counts] GET failed', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { sql } from 'kysely'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst querySchema = z.object({\n entityId: z.string().uuid(),\n status: z.enum(['done', 'planned']).optional(),\n})\n\nconst responseSchema = z.object({\n ok: z.literal(true),\n result: z.object({\n call: z.number(),\n email: z.number(),\n meeting: z.number(),\n note: z.number(),\n task: z.number(),\n total: z.number(),\n }),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.interactions.view'] },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n GET: {\n summary: 'Get interaction counts by type',\n description: 'Returns per-type interaction counts scoped to an entity.',\n query: querySchema,\n responses: [\n {\n status: 200,\n description: 'Counts by interaction type',\n schema: responseSchema,\n },\n ],\n },\n },\n}\n\nexport async function GET(req: Request) {\n try {\n const queryUrl = new URL(req.url)\n const query = querySchema.parse(Object.fromEntries(queryUrl.searchParams))\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, {\n error: translate('customers.errors.unauthorized', 'Unauthorized'),\n })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationIds = Array.isArray(scope?.filterIds) && scope.filterIds.length > 0\n ? scope.filterIds\n : auth.orgId\n ? [auth.orgId]\n : []\n const em = (container.resolve('em') as EntityManager).fork()\n const kysely = em.getKysely<any>()\n\n let baseQuery = (kysely as any)\n .selectFrom('customer_interactions')\n .where('entity_id', '=', query.entityId)\n .where('tenant_id', '=', auth.tenantId)\n .where('deleted_at', 'is', null)\n\n if (organizationIds.length === 1) {\n baseQuery = baseQuery.where('organization_id', '=', organizationIds[0])\n } else if (organizationIds.length > 1) {\n baseQuery = baseQuery.where('organization_id', 'in', organizationIds)\n }\n\n if (query.status) {\n baseQuery = baseQuery.where('status', '=', query.status)\n }\n\n // Raw SELECT: reads only unencrypted columns (id, interaction_type); title/notes are excluded to avoid ciphertext leakage.\n const rows = await baseQuery\n .select(['interaction_type', sql<string>`count(*)`.as('count')])\n .groupBy('interaction_type')\n .execute() as Array<{ interaction_type: string; count: string | number }>\n\n const counts: Record<string, number> = { call: 0, email: 0, meeting: 0, note: 0, task: 0 }\n let total = 0\n for (const row of rows) {\n const count = typeof row.count === 'string' ? parseInt(row.count, 10) : row.count\n const type = row.interaction_type\n if (type in counts) {\n counts[type] = count\n }\n total += count\n }\n\n return NextResponse.json({ ok: true, result: { ...counts, total } })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('[customers/interactions/counts] GET failed', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -20,6 +20,7 @@ import {
20
20
  defaultOkResponseSchema
21
21
  } from "../openapi.js";
22
22
  import { CUSTOMER_INTERACTION_ENTITY_ID } from "../../lib/interactionCompatibility.js";
23
+ import { resolveCanonicalActivityTargetId } from "../../lib/legacyActivityBridge.js";
23
24
  const rawBodySchema = z.object({}).passthrough();
24
25
  const interactionSortFieldSchema = z.enum([
25
26
  "scheduledAt",
@@ -83,7 +84,26 @@ const crud = makeCrudRoute({
83
84
  schema: rawBodySchema,
84
85
  mapInput: async ({ raw, ctx }) => {
85
86
  const { translate } = await resolveTranslations();
86
- return parseScopedCommandInput(interactionUpdateSchema, raw ?? {}, ctx, translate);
87
+ const parsed = parseScopedCommandInput(interactionUpdateSchema, raw ?? {}, ctx, translate);
88
+ const tenantId = ctx.auth?.tenantId ?? null;
89
+ if (typeof parsed.id === "string" && tenantId) {
90
+ try {
91
+ const em = ctx.container.resolve("em");
92
+ const commandBus = ctx.container.resolve("commandBus");
93
+ const commandContext = {
94
+ container: ctx.container,
95
+ auth: ctx.auth ?? null,
96
+ organizationScope: ctx.organizationScope ?? null,
97
+ selectedOrganizationId: ctx.selectedOrganizationId ?? null,
98
+ organizationIds: ctx.organizationIds ?? null,
99
+ request: ctx.request
100
+ };
101
+ await resolveCanonicalActivityTargetId(em, commandBus, commandContext, parsed.id, tenantId);
102
+ } catch (err) {
103
+ console.warn("[customers.interactions.put] legacy bridge failed", { id: parsed.id, error: err });
104
+ }
105
+ }
106
+ return parsed;
87
107
  },
88
108
  response: () => ({ ok: true })
89
109
  },