@open-mercato/core 0.6.3-develop.3876.1.d40fe4ec2d → 0.6.3-develop.3894.1.352abf4240
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/attachments/api/file/[id]/route.js +7 -2
- package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +7 -4
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/audit_logs/services/accessLogService.js +127 -8
- package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
- package/dist/modules/auth/backend/auth/profile/page.js +1 -1
- package/dist/modules/auth/backend/auth/profile/page.js.map +2 -2
- package/dist/modules/auth/backend/profile/change-password/page.js +1 -1
- package/dist/modules/auth/backend/profile/change-password/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +1 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +6 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/di.js +17 -3
- package/dist/modules/auth/di.js.map +2 -2
- package/dist/modules/auth/services/rbacDefaultCache.js +110 -0
- package/dist/modules/auth/services/rbacDefaultCache.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +8 -1
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +3 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js +3 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js.map +2 -2
- package/dist/modules/configs/cli.js +27 -14
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/currencies/api/currencies/route.js +3 -4
- package/dist/modules/currencies/api/currencies/route.js.map +2 -2
- package/dist/modules/currencies/api/exchange-rates/route.js +3 -4
- package/dist/modules/currencies/api/exchange-rates/route.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +26 -24
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +26 -0
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +7 -0
- package/dist/modules/directory/utils/organizationScope.js +85 -0
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -1
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +1 -1
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
- package/dist/modules/sales/components/channels/ChannelOfferForm.js +1 -1
- package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +2 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +4 -2
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +20 -3
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/ActivitiesEditor.js +34 -1
- package/dist/modules/workflows/components/ActivitiesEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +153 -17
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/StepsEditor.js +31 -0
- package/dist/modules/workflows/components/StepsEditor.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraph.js +3 -2
- package/dist/modules/workflows/components/WorkflowGraph.js.map +2 -2
- package/dist/modules/workflows/components/nodes/WaitForTimerNode.js +54 -0
- package/dist/modules/workflows/components/nodes/WaitForTimerNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/index.js +3 -1
- package/dist/modules/workflows/components/nodes/index.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +117 -0
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/di.js +5 -1
- package/dist/modules/workflows/di.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +42 -1
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-queue-types.js.map +2 -2
- package/dist/modules/workflows/lib/activity-worker-handler.js +24 -0
- package/dist/modules/workflows/lib/activity-worker-handler.js.map +2 -2
- package/dist/modules/workflows/lib/duration.js +32 -0
- package/dist/modules/workflows/lib/duration.js.map +7 -0
- package/dist/modules/workflows/lib/event-logger.js +1 -0
- package/dist/modules/workflows/lib/event-logger.js.map +2 -2
- package/dist/modules/workflows/lib/format-validation-error.js +12 -0
- package/dist/modules/workflows/lib/format-validation-error.js.map +7 -0
- package/dist/modules/workflows/lib/graph-utils.js +6 -3
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/node-type-icons.js +9 -5
- package/dist/modules/workflows/lib/node-type-icons.js.map +2 -2
- package/dist/modules/workflows/lib/signal-handler.js +55 -23
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +79 -29
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/timer-handler.js +159 -0
- package/dist/modules/workflows/lib/timer-handler.js.map +7 -0
- package/dist/modules/workflows/lib/workflow-executor.js +1 -1
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/workers/workflow-activities.worker.js +20 -4
- package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/attachments/api/file/[id]/route.ts +7 -2
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +7 -4
- package/src/modules/audit_logs/services/accessLogService.ts +179 -15
- package/src/modules/auth/backend/auth/profile/page.tsx +1 -1
- package/src/modules/auth/backend/profile/change-password/page.tsx +1 -1
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +1 -1
- package/src/modules/auth/backend/users/create/page.tsx +6 -1
- package/src/modules/auth/di.ts +26 -3
- package/src/modules/auth/services/rbacDefaultCache.ts +145 -0
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +8 -1
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +3 -2
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/create/page.tsx +3 -2
- package/src/modules/configs/cli.ts +34 -13
- package/src/modules/currencies/api/currencies/route.ts +3 -4
- package/src/modules/currencies/api/exchange-rates/route.ts +3 -4
- package/src/modules/customers/api/people/route.ts +27 -25
- package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +39 -0
- package/src/modules/directory/utils/organizationScope.ts +121 -0
- package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +1 -1
- package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +1 -1
- package/src/modules/sales/components/channels/ChannelOfferForm.tsx +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +3 -2
- package/src/modules/workflows/backend/definitions/create/page.tsx +4 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +18 -1
- package/src/modules/workflows/components/ActivitiesEditor.tsx +40 -0
- package/src/modules/workflows/components/NodeEditDialog.tsx +218 -30
- package/src/modules/workflows/components/StepsEditor.tsx +36 -0
- package/src/modules/workflows/components/WorkflowGraph.tsx +2 -1
- package/src/modules/workflows/components/nodes/WaitForTimerNode.tsx +70 -0
- package/src/modules/workflows/components/nodes/index.ts +3 -0
- package/src/modules/workflows/data/validators.ts +121 -0
- package/src/modules/workflows/di.ts +4 -0
- package/src/modules/workflows/i18n/de.json +10 -1
- package/src/modules/workflows/i18n/en.json +10 -1
- package/src/modules/workflows/i18n/es.json +10 -1
- package/src/modules/workflows/i18n/pl.json +10 -1
- package/src/modules/workflows/lib/activity-executor.ts +86 -2
- package/src/modules/workflows/lib/activity-queue-types.ts +18 -11
- package/src/modules/workflows/lib/activity-worker-handler.ts +29 -0
- package/src/modules/workflows/lib/duration.ts +51 -0
- package/src/modules/workflows/lib/event-logger.ts +1 -0
- package/src/modules/workflows/lib/format-validation-error.ts +30 -0
- package/src/modules/workflows/lib/graph-utils.ts +3 -0
- package/src/modules/workflows/lib/node-type-icons.ts +6 -2
- package/src/modules/workflows/lib/signal-handler.ts +62 -24
- package/src/modules/workflows/lib/step-handler.ts +107 -50
- package/src/modules/workflows/lib/timer-handler.ts +213 -0
- package/src/modules/workflows/lib/workflow-executor.ts +1 -1
- package/src/modules/workflows/workers/workflow-activities.worker.ts +33 -7
|
@@ -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'\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": "AAEA,SAAS,0BAA8C;AACvD,SAAS,8BAA8B;AAEvC,SAAS,yBAAyB;AAClC,SAAS,sCAAsC,yCAAyC;AACxF,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,6BAA6B;
|
|
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\nexport const STRUCTURAL_CACHE_REQUESTS: CachePurgeRequest[] = [\n { kind: 'pattern', pattern: 'nav:*' },\n { kind: 'segment', segment: 'admin-nav' },\n { kind: 'segment', segment: 'portal-nav' },\n]\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/sidebar caches 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 runCachePurgeRequest(\n args: ParsedArgs,\n request: CachePurgeRequest,\n emitOutput = true,\n) {\n const json = flagEnabled(args, 'json')\n const quiet = flagEnabled(args, 'quiet')\n const dryRun = flagEnabled(args, 'dry-run', 'dryRun')\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 && emitOutput) {\n console.log(JSON.stringify(results, null, 2))\n return results\n }\n\n if (quiet || !emitOutput) {\n return results\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 return results\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runCachePurge(args: ParsedArgs) {\n await runCachePurgeRequest(args, resolveCachePurgeRequest(args))\n}\n\nasync function runStructuralCachePurge(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const structuralResults: Array<{\n request: CachePurgeRequest\n results: Awaited<ReturnType<typeof runCachePurgeRequest>>\n }> = []\n for (const request of STRUCTURAL_CACHE_REQUESTS) {\n const results = await runCachePurgeRequest(args, request, !json)\n structuralResults.push({ request, results })\n }\n if (json) {\n console.log(JSON.stringify(structuralResults, null, 2))\n }\n const quiet = flagEnabled(args, 'quiet')\n try {\n touchGeneratedBarrels({ quiet: quiet || json })\n } catch (err) {\n if (!quiet && !json) {\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
|
}
|
|
@@ -140,10 +140,9 @@ async function GET(req) {
|
|
|
140
140
|
} else {
|
|
141
141
|
orderBy.code = "ASC";
|
|
142
142
|
}
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const items = paged.map(toRow);
|
|
143
|
+
const offset = (page - 1) * pageSize;
|
|
144
|
+
const [rows, total] = await em.findAndCount(Currency, filter, { orderBy, limit: pageSize, offset });
|
|
145
|
+
const items = rows.map(toRow);
|
|
147
146
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
148
147
|
return NextResponse.json({ items, total, page, pageSize, totalPages });
|
|
149
148
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/currencies/api/currencies/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { Currency } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { currencyCreateSchema, currencyUpdateSchema } from '../../data/validators'\nimport {\n createCurrenciesCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['currencies.view'] },\n POST: { requireAuth: true, requireFeatures: ['currencies.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['currencies.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['currencies.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.object({}).loose()\ntype CrudInput = Record<string, unknown>\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: Currency,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n events: {\n module: 'currencies',\n entity: 'currency',\n persistent: true,\n },\n actions: {\n create: {\n commandId: 'currencies.currencies.create',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: ({ result }) => ({ id: String(result.currencyId) }),\n status: 201,\n },\n update: {\n commandId: 'currencies.currencies.update',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'currencies.currencies.delete',\n schema: rawBodySchema,\n mapInput: ({ raw, ctx }) => ({\n id: ((raw as Record<string, unknown>).query as Record<string, unknown> | undefined)?.id as string | undefined,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? undefined,\n tenantId: ctx.auth?.tenantId ?? undefined,\n }),\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst listQuerySchema = z.object({\n id: z.uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n sortField: z.enum(['code', 'name', 'createdAt', 'updatedAt']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n isBase: z.enum(['true', 'false']).optional(),\n isActive: z.enum(['true', 'false']).optional(),\n code: z.string().optional(),\n}).loose()\n\ntype CurrencyRow = {\n id: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n createdAt: string | null\n updatedAt: string | null\n organizationId: string\n tenantId: string\n}\n\nconst toRow = (currency: Currency): CurrencyRow => ({\n id: String(currency.id),\n code: String(currency.code),\n name: String(currency.name),\n symbol: currency.symbol ?? null,\n decimalPlaces: currency.decimalPlaces,\n thousandsSeparator: currency.thousandsSeparator ?? null,\n decimalSeparator: currency.decimalSeparator ?? null,\n isBase: !!currency.isBase,\n isActive: !!currency.isActive,\n createdAt: currency.createdAt ? currency.createdAt.toISOString() : null,\n updatedAt: currency.updatedAt ? currency.updatedAt.toISOString() : null,\n organizationId: String(currency.organizationId),\n tenantId: String(currency.tenantId),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listQuerySchema.safeParse({\n id: url.searchParams.get('id') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n sortField: url.searchParams.get('sortField') ?? undefined,\n sortDir: url.searchParams.get('sortDir') ?? undefined,\n isBase: url.searchParams.get('isBase') ?? undefined,\n isActive: url.searchParams.get('isActive') ?? undefined,\n code: url.searchParams.get('code') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, search, sortField, sortDir, isBase, isActive, code } = parsed.data\n const filter: FilterQuery<Currency> = {\n tenantId: auth.tenantId,\n deletedAt: null,\n }\n if (auth.orgId) {\n filter.organizationId = auth.orgId\n }\n \n if (id) filter.id = id\n if (code) filter.code = code\n if (search) {\n filter.$or = [\n { code: { $ilike: `%${search}%` } },\n { name: { $ilike: `%${search}%` } },\n { symbol: { $ilike: `%${search}%` } },\n ]\n }\n if (isBase === 'true') filter.isBase = true\n if (isBase === 'false') filter.isBase = false\n if (isActive === 'true') filter.isActive = true\n if (isActive === 'false') filter.isActive = false\n\n const fieldMap: Record<string, string> = {\n code: 'code',\n name: 'name',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n const orderBy: Record<string, 'ASC' | 'DESC'> = {}\n if (sortField) {\n const mapped = fieldMap[sortField] || 'code'\n orderBy[mapped] = sortDir === 'desc' ? 'DESC' : 'ASC'\n } else {\n orderBy.code = 'ASC'\n }\n\n const [
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { Currency } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { currencyCreateSchema, currencyUpdateSchema } from '../../data/validators'\nimport {\n createCurrenciesCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['currencies.view'] },\n POST: { requireAuth: true, requireFeatures: ['currencies.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['currencies.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['currencies.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.object({}).loose()\ntype CrudInput = Record<string, unknown>\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: Currency,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n events: {\n module: 'currencies',\n entity: 'currency',\n persistent: true,\n },\n actions: {\n create: {\n commandId: 'currencies.currencies.create',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: ({ result }) => ({ id: String(result.currencyId) }),\n status: 201,\n },\n update: {\n commandId: 'currencies.currencies.update',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'currencies.currencies.delete',\n schema: rawBodySchema,\n mapInput: ({ raw, ctx }) => ({\n id: ((raw as Record<string, unknown>).query as Record<string, unknown> | undefined)?.id as string | undefined,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? undefined,\n tenantId: ctx.auth?.tenantId ?? undefined,\n }),\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst listQuerySchema = z.object({\n id: z.uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n sortField: z.enum(['code', 'name', 'createdAt', 'updatedAt']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n isBase: z.enum(['true', 'false']).optional(),\n isActive: z.enum(['true', 'false']).optional(),\n code: z.string().optional(),\n}).loose()\n\ntype CurrencyRow = {\n id: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n createdAt: string | null\n updatedAt: string | null\n organizationId: string\n tenantId: string\n}\n\nconst toRow = (currency: Currency): CurrencyRow => ({\n id: String(currency.id),\n code: String(currency.code),\n name: String(currency.name),\n symbol: currency.symbol ?? null,\n decimalPlaces: currency.decimalPlaces,\n thousandsSeparator: currency.thousandsSeparator ?? null,\n decimalSeparator: currency.decimalSeparator ?? null,\n isBase: !!currency.isBase,\n isActive: !!currency.isActive,\n createdAt: currency.createdAt ? currency.createdAt.toISOString() : null,\n updatedAt: currency.updatedAt ? currency.updatedAt.toISOString() : null,\n organizationId: String(currency.organizationId),\n tenantId: String(currency.tenantId),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listQuerySchema.safeParse({\n id: url.searchParams.get('id') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n sortField: url.searchParams.get('sortField') ?? undefined,\n sortDir: url.searchParams.get('sortDir') ?? undefined,\n isBase: url.searchParams.get('isBase') ?? undefined,\n isActive: url.searchParams.get('isActive') ?? undefined,\n code: url.searchParams.get('code') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, search, sortField, sortDir, isBase, isActive, code } = parsed.data\n const filter: FilterQuery<Currency> = {\n tenantId: auth.tenantId,\n deletedAt: null,\n }\n if (auth.orgId) {\n filter.organizationId = auth.orgId\n }\n \n if (id) filter.id = id\n if (code) filter.code = code\n if (search) {\n filter.$or = [\n { code: { $ilike: `%${search}%` } },\n { name: { $ilike: `%${search}%` } },\n { symbol: { $ilike: `%${search}%` } },\n ]\n }\n if (isBase === 'true') filter.isBase = true\n if (isBase === 'false') filter.isBase = false\n if (isActive === 'true') filter.isActive = true\n if (isActive === 'false') filter.isActive = false\n\n const fieldMap: Record<string, string> = {\n code: 'code',\n name: 'name',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n const orderBy: Record<string, 'ASC' | 'DESC'> = {}\n if (sortField) {\n const mapped = fieldMap[sortField] || 'code'\n orderBy[mapped] = sortDir === 'desc' ? 'DESC' : 'ASC'\n } else {\n orderBy.code = 'ASC'\n }\n\n const offset = (page - 1) * pageSize\n const [rows, total] = await em.findAndCount(Currency, filter, { orderBy, limit: pageSize, offset })\n const items = rows.map(toRow)\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n return NextResponse.json({ items, total, page, pageSize, totalPages })\n}\n\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst currencyListItemSchema = z.object({\n id: z.uuid(),\n code: z.string(),\n name: z.string(),\n symbol: z.string().nullable(),\n decimalPlaces: z.number(),\n thousandsSeparator: z.string().nullable(),\n decimalSeparator: z.string().nullable(),\n isBase: z.boolean(),\n isActive: z.boolean(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n organizationId: z.uuid(),\n tenantId: z.uuid(),\n})\n\nexport const openApi = createCurrenciesCrudOpenApi({\n resourceName: 'Currency',\n pluralName: 'Currencies',\n querySchema: listQuerySchema,\n listResponseSchema: createPagedListResponseSchema(currencyListItemSchema),\n create: {\n schema: currencyCreateSchema,\n description: 'Creates a new currency.',\n },\n update: {\n schema: currencyUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates an existing currency by id.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a currency by id.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -136,10 +136,9 @@ async function GET(req) {
|
|
|
136
136
|
} else {
|
|
137
137
|
orderBy.date = "DESC";
|
|
138
138
|
}
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const items = paged.map(toRow);
|
|
139
|
+
const offset = (page - 1) * pageSize;
|
|
140
|
+
const [rows, total] = await em.findAndCount(ExchangeRate, where, { orderBy, limit: pageSize, offset });
|
|
141
|
+
const items = rows.map(toRow);
|
|
143
142
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
144
143
|
return NextResponse.json({ items, total, page, pageSize, totalPages });
|
|
145
144
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/currencies/api/exchange-rates/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { ExchangeRate } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { exchangeRateCreateSchema, exchangeRateUpdateSchema } from '../../data/validators'\nimport {\n createCurrenciesCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['currencies.rates.view'] },\n POST: { requireAuth: true, requireFeatures: ['currencies.rates.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['currencies.rates.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['currencies.rates.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.looseObject({})\ntype CrudInput = Record<string, unknown>\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: ExchangeRate,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n events: {\n module: 'currencies',\n entity: 'exchange_rate',\n persistent: true,\n },\n actions: {\n create: {\n commandId: 'currencies.exchange_rates.create',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: ({ result }) => ({ id: String(result.exchangeRateId) }),\n status: 201,\n },\n update: {\n commandId: 'currencies.exchange_rates.update',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'currencies.exchange_rates.delete',\n schema: rawBodySchema,\n mapInput: ({ raw, ctx }) => ({\n id: ((raw as Record<string, unknown>).query as Record<string, unknown> | undefined)?.id as string | undefined,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? undefined,\n tenantId: ctx.auth?.tenantId ?? undefined,\n }),\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst listQuerySchema = z\n .object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n sortField: z.enum(['fromCurrencyCode', 'toCurrencyCode', 'date', 'createdAt', 'updatedAt']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n fromCurrencyCode: z.string().optional(),\n toCurrencyCode: z.string().optional(),\n isActive: z.enum(['true', 'false']).optional(),\n source: z.string().optional(),\n type: z.enum(['buy', 'sell']).optional(),\n })\n .loose()\n\ntype ExchangeRateRow = {\n id: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string\n type: string | null\n isActive: boolean\n createdAt: string | null\n updatedAt: string | null\n organizationId: string\n tenantId: string\n}\n\nconst toRow = (rate: ExchangeRate): ExchangeRateRow => ({\n id: String(rate.id),\n fromCurrencyCode: String(rate.fromCurrencyCode),\n toCurrencyCode: String(rate.toCurrencyCode),\n rate: String(rate.rate),\n date: rate.date.toISOString(),\n source: String(rate.source),\n type: rate.type ?? null,\n isActive: !!rate.isActive,\n createdAt: rate.createdAt ? rate.createdAt.toISOString() : null,\n updatedAt: rate.updatedAt ? rate.updatedAt.toISOString() : null,\n organizationId: String(rate.organizationId),\n tenantId: String(rate.tenantId),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listQuerySchema.safeParse({\n id: url.searchParams.get('id') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n sortField: url.searchParams.get('sortField') ?? undefined,\n sortDir: url.searchParams.get('sortDir') ?? undefined,\n fromCurrencyCode: url.searchParams.get('fromCurrencyCode') ?? undefined,\n toCurrencyCode: url.searchParams.get('toCurrencyCode') ?? undefined,\n isActive: url.searchParams.get('isActive') ?? undefined,\n source: url.searchParams.get('source') ?? undefined,\n type: url.searchParams.get('type') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, sortField, sortDir, fromCurrencyCode, toCurrencyCode, isActive, source, type } = parsed.data\n const where: FilterQuery<ExchangeRate> = {\n tenantId: auth.tenantId,\n deletedAt: null,\n }\n if (auth.orgId) {\n where.organizationId = auth.orgId\n }\n\n if (id) where.id = id\n if (fromCurrencyCode) where.fromCurrencyCode = fromCurrencyCode\n if (toCurrencyCode) where.toCurrencyCode = toCurrencyCode\n if (source) where.source = source\n if (type) where.type = type\n if (isActive === 'true') where.isActive = true\n if (isActive === 'false') where.isActive = false\n\n const fieldMap: Record<string, string> = {\n fromCurrencyCode: 'fromCurrencyCode',\n toCurrencyCode: 'toCurrencyCode',\n date: 'date',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n const orderBy: Record<string, 'ASC' | 'DESC'> = {}\n if (sortField) {\n const mapped = fieldMap[sortField] || 'date'\n orderBy[mapped] = sortDir === 'desc' ? 'DESC' : 'ASC'\n } else {\n orderBy.date = 'DESC'\n }\n\n const [
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { ExchangeRate } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { exchangeRateCreateSchema, exchangeRateUpdateSchema } from '../../data/validators'\nimport {\n createCurrenciesCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['currencies.rates.view'] },\n POST: { requireAuth: true, requireFeatures: ['currencies.rates.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['currencies.rates.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['currencies.rates.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.looseObject({})\ntype CrudInput = Record<string, unknown>\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: ExchangeRate,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n events: {\n module: 'currencies',\n entity: 'exchange_rate',\n persistent: true,\n },\n actions: {\n create: {\n commandId: 'currencies.exchange_rates.create',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: ({ result }) => ({ id: String(result.exchangeRateId) }),\n status: 201,\n },\n update: {\n commandId: 'currencies.exchange_rates.update',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'currencies.exchange_rates.delete',\n schema: rawBodySchema,\n mapInput: ({ raw, ctx }) => ({\n id: ((raw as Record<string, unknown>).query as Record<string, unknown> | undefined)?.id as string | undefined,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? undefined,\n tenantId: ctx.auth?.tenantId ?? undefined,\n }),\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst listQuerySchema = z\n .object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n sortField: z.enum(['fromCurrencyCode', 'toCurrencyCode', 'date', 'createdAt', 'updatedAt']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n fromCurrencyCode: z.string().optional(),\n toCurrencyCode: z.string().optional(),\n isActive: z.enum(['true', 'false']).optional(),\n source: z.string().optional(),\n type: z.enum(['buy', 'sell']).optional(),\n })\n .loose()\n\ntype ExchangeRateRow = {\n id: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string\n type: string | null\n isActive: boolean\n createdAt: string | null\n updatedAt: string | null\n organizationId: string\n tenantId: string\n}\n\nconst toRow = (rate: ExchangeRate): ExchangeRateRow => ({\n id: String(rate.id),\n fromCurrencyCode: String(rate.fromCurrencyCode),\n toCurrencyCode: String(rate.toCurrencyCode),\n rate: String(rate.rate),\n date: rate.date.toISOString(),\n source: String(rate.source),\n type: rate.type ?? null,\n isActive: !!rate.isActive,\n createdAt: rate.createdAt ? rate.createdAt.toISOString() : null,\n updatedAt: rate.updatedAt ? rate.updatedAt.toISOString() : null,\n organizationId: String(rate.organizationId),\n tenantId: String(rate.tenantId),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listQuerySchema.safeParse({\n id: url.searchParams.get('id') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n sortField: url.searchParams.get('sortField') ?? undefined,\n sortDir: url.searchParams.get('sortDir') ?? undefined,\n fromCurrencyCode: url.searchParams.get('fromCurrencyCode') ?? undefined,\n toCurrencyCode: url.searchParams.get('toCurrencyCode') ?? undefined,\n isActive: url.searchParams.get('isActive') ?? undefined,\n source: url.searchParams.get('source') ?? undefined,\n type: url.searchParams.get('type') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, sortField, sortDir, fromCurrencyCode, toCurrencyCode, isActive, source, type } = parsed.data\n const where: FilterQuery<ExchangeRate> = {\n tenantId: auth.tenantId,\n deletedAt: null,\n }\n if (auth.orgId) {\n where.organizationId = auth.orgId\n }\n\n if (id) where.id = id\n if (fromCurrencyCode) where.fromCurrencyCode = fromCurrencyCode\n if (toCurrencyCode) where.toCurrencyCode = toCurrencyCode\n if (source) where.source = source\n if (type) where.type = type\n if (isActive === 'true') where.isActive = true\n if (isActive === 'false') where.isActive = false\n\n const fieldMap: Record<string, string> = {\n fromCurrencyCode: 'fromCurrencyCode',\n toCurrencyCode: 'toCurrencyCode',\n date: 'date',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n const orderBy: Record<string, 'ASC' | 'DESC'> = {}\n if (sortField) {\n const mapped = fieldMap[sortField] || 'date'\n orderBy[mapped] = sortDir === 'desc' ? 'DESC' : 'ASC'\n } else {\n orderBy.date = 'DESC'\n }\n\n const offset = (page - 1) * pageSize\n const [rows, total] = await em.findAndCount(ExchangeRate, where, { orderBy, limit: pageSize, offset })\n const items = rows.map(toRow)\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n return NextResponse.json({ items, total, page, pageSize, totalPages })\n}\n\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst exchangeRateListItemSchema = z.object({\n id: z.string().uuid(),\n fromCurrencyCode: z.string(),\n toCurrencyCode: z.string(),\n rate: z.string(),\n date: z.string(),\n source: z.string(),\n type: z.string().nullable(),\n isActive: z.boolean(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n})\n\nexport const openApi = createCurrenciesCrudOpenApi({\n resourceName: 'ExchangeRate',\n pluralName: 'ExchangeRates',\n querySchema: listQuerySchema,\n listResponseSchema: createPagedListResponseSchema(exchangeRateListItemSchema),\n create: {\n schema: exchangeRateCreateSchema,\n description: 'Creates a new exchange rate.',\n },\n update: {\n schema: exchangeRateUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates an existing exchange rate by id.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes an exchange rate by id.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -399,35 +399,37 @@ const crud = makeCrudRoute({
|
|
|
399
399
|
tenantId: ctx.auth?.tenantId ?? null,
|
|
400
400
|
organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
|
|
401
401
|
};
|
|
402
|
-
const
|
|
403
|
-
em,
|
|
404
|
-
CustomerEntity,
|
|
405
|
-
{
|
|
406
|
-
id: { $in: ids },
|
|
407
|
-
deletedAt: null,
|
|
408
|
-
kind: "person"
|
|
409
|
-
},
|
|
410
|
-
void 0,
|
|
411
|
-
decryptionScope
|
|
412
|
-
);
|
|
413
|
-
const entitiesById = /* @__PURE__ */ new Map();
|
|
414
|
-
for (const entity of entities) {
|
|
415
|
-
entitiesById.set(entity.id, entity);
|
|
416
|
-
}
|
|
417
|
-
const where = {
|
|
402
|
+
const profileWhere = {
|
|
418
403
|
entity: { $in: ids },
|
|
419
404
|
tenantId: ctx.auth?.tenantId ?? null
|
|
420
405
|
};
|
|
421
406
|
if (ctx.selectedOrganizationId) {
|
|
422
|
-
|
|
407
|
+
profileWhere.organizationId = ctx.selectedOrganizationId;
|
|
408
|
+
}
|
|
409
|
+
const [entities, profiles] = await Promise.all([
|
|
410
|
+
findWithDecryption(
|
|
411
|
+
em,
|
|
412
|
+
CustomerEntity,
|
|
413
|
+
{
|
|
414
|
+
id: { $in: ids },
|
|
415
|
+
deletedAt: null,
|
|
416
|
+
kind: "person"
|
|
417
|
+
},
|
|
418
|
+
void 0,
|
|
419
|
+
decryptionScope
|
|
420
|
+
),
|
|
421
|
+
findWithDecryption(
|
|
422
|
+
em,
|
|
423
|
+
CustomerPersonProfile,
|
|
424
|
+
profileWhere,
|
|
425
|
+
{ populate: ["entity", "company"] },
|
|
426
|
+
decryptionScope
|
|
427
|
+
)
|
|
428
|
+
]);
|
|
429
|
+
const entitiesById = /* @__PURE__ */ new Map();
|
|
430
|
+
for (const entity of entities) {
|
|
431
|
+
entitiesById.set(entity.id, entity);
|
|
423
432
|
}
|
|
424
|
-
const profiles = await findWithDecryption(
|
|
425
|
-
em,
|
|
426
|
-
CustomerPersonProfile,
|
|
427
|
-
where,
|
|
428
|
-
{ populate: ["entity", "company"] },
|
|
429
|
-
decryptionScope
|
|
430
|
-
);
|
|
431
433
|
const profilesByEntityId = /* @__PURE__ */ new Map();
|
|
432
434
|
for (const profile of profiles) {
|
|
433
435
|
const profileEntity = profile.entity;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/people/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n CustomerDealPersonLink,\n CustomerEntity,\n CustomerPersonCompanyLink,\n CustomerPersonProfile,\n} from '../../data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { personCreateSchema, personUpdateSchema } from '../../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n applyEntityIdExclusion,\n applyEntityIdRestriction,\n findMatchingEntityIdsWithQueryEngine,\n findMatchingEntityIdsBySearchTokensAcrossSources,\n withScopedPayload,\n} from '../utils'\nimport { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries, splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport {\n filterActivePersonCompanyLinks,\n withActiveCustomerPersonCompanyLinkFilter,\n} from '../../lib/personCompanyLinkTable'\nimport { normalizeProfilePayload } from './payload'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n email: z.string().optional(),\n emailStartsWith: z.string().optional(),\n emailContains: z.string().optional(),\n status: z.string().optional(),\n lifecycleStage: z.string().optional(),\n source: z.string().optional(),\n hasEmail: z.string().optional(),\n hasPhone: z.string().optional(),\n hasNextInteraction: z.string().optional(),\n createdFrom: z.string().optional(),\n createdTo: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n id: z.string().uuid().optional(),\n tagIds: z.string().optional(),\n tagIdsEmpty: z.string().optional(),\n excludeIds: z.string().optional(),\n excludeLinkedCompanyId: z.string().uuid().optional(),\n excludeLinkedDealId: z.string().uuid().optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CustomerEntity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n enrichers: { entityId: 'customers.person' },\n indexer: { entityType: E.customers.customer_entity },\n list: {\n schema: listSchema,\n entityId: E.customers.customer_entity,\n fields: [\n 'id',\n 'display_name',\n 'description',\n 'owner_user_id',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_at',\n 'next_interaction_name',\n 'next_interaction_ref_id',\n 'next_interaction_icon',\n 'next_interaction_color',\n 'organization_id',\n 'tenant_id',\n 'kind',\n 'created_at',\n ],\n sortFieldMap: {\n name: 'display_name',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query, ctx) => {\n const advancedFilterTree = consumeAdvancedFilterState(query)\n const filters: Record<string, unknown> = { kind: { $eq: 'person' } }\n if (query.id) filters.id = { $eq: query.id }\n if (query.search) {\n const matchingIds = ctx\n ? await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_entity,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'description',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_name',\n ],\n },\n {\n entityType: E.customers.customer_person_profile,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'first_name',\n 'last_name',\n 'preferred_name',\n 'job_title',\n 'department',\n 'seniority',\n 'timezone',\n 'linked_in_url',\n 'twitter_url',\n ],\n mapToEntityIds: {\n table: 'customer_people',\n targetColumn: 'entity_id',\n },\n },\n ],\n })\n : null\n if (matchingIds !== null && matchingIds.length > 0) {\n applyEntityIdRestriction(filters, matchingIds)\n } else {\n const searchPattern = `%${escapeLikePattern(query.search)}%`\n filters.$or = [\n { display_name: { $ilike: searchPattern } },\n { primary_email: { $ilike: searchPattern } },\n { primary_phone: { $ilike: searchPattern } },\n { description: { $ilike: searchPattern } },\n { next_interaction_name: { $ilike: searchPattern } },\n ]\n }\n }\n const email = typeof query.email === 'string' ? query.email.trim().toLowerCase() : ''\n const emailStartsWith = typeof query.emailStartsWith === 'string' ? query.emailStartsWith.trim().toLowerCase() : ''\n const emailContains = typeof query.emailContains === 'string' ? query.emailContains.trim().toLowerCase() : ''\n if (email) {\n filters.primary_email = { $eq: email }\n } else if (emailStartsWith) {\n filters.primary_email = { $ilike: `${escapeLikePattern(emailStartsWith)}%` }\n } else if (emailContains) {\n filters.primary_email = { $ilike: `%${escapeLikePattern(emailContains)}%` }\n }\n if (query.status) {\n filters.status = { $eq: query.status }\n }\n if (query.lifecycleStage) {\n filters.lifecycle_stage = { $eq: query.lifecycleStage }\n }\n if (query.source) {\n filters.source = { $eq: query.source }\n }\n const tagIdsRaw = typeof query.tagIds === 'string' ? query.tagIds : ''\n const tagIds = tagIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n const tagIdsEmpty = parseBooleanToken(query.tagIdsEmpty) === true\n if (tagIdsEmpty) {\n filters.id = { $eq: '00000000-0000-0000-0000-000000000000' }\n } else if (tagIds.length > 0) {\n filters['tag_assignments.tag_id'] = { $in: tagIds }\n }\n const excludedIds = new Set<string>()\n const excludeIdsRaw = typeof query.excludeIds === 'string' ? query.excludeIds : ''\n excludeIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n .forEach((value: string) => excludedIds.add(value))\n if (ctx && query.excludeLinkedCompanyId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(\n em,\n { company: query.excludeLinkedCompanyId },\n 'customers.people.GET',\n )\n const links = filterActivePersonCompanyLinks(\n await findWithDecryption(\n em,\n CustomerPersonCompanyLink,\n linkWhere,\n { populate: ['person'] },\n decryptionScope,\n ),\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n if (ctx && query.excludeLinkedDealId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const links = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n {\n deal: query.excludeLinkedDealId,\n },\n { populate: ['person'] },\n decryptionScope,\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n applyEntityIdExclusion(filters, Array.from(excludedIds))\n const hasEmail = parseBooleanToken(query.hasEmail)\n if (!email && !emailStartsWith && !emailContains && hasEmail !== null) {\n filters.primary_email = { $exists: hasEmail }\n }\n const hasPhone = parseBooleanToken(query.hasPhone)\n if (hasPhone !== null) {\n filters.primary_phone = { $exists: hasPhone }\n }\n const hasNextInteraction = parseBooleanToken(query.hasNextInteraction)\n if (hasNextInteraction !== null) {\n filters.next_interaction_at = { $exists: hasNextInteraction }\n }\n const createdRange: Record<string, Date> = {}\n if (query.createdFrom) {\n const from = new Date(query.createdFrom)\n if (!Number.isNaN(from.getTime())) createdRange.$gte = from\n }\n if (query.createdTo) {\n const to = new Date(query.createdTo)\n if (!Number.isNaN(to.getTime())) createdRange.$lte = to\n }\n if (Object.keys(createdRange).length) {\n filters.created_at = createdRange\n }\n if (ctx) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch (err) {\n console.warn('[customers.people.list] custom field filter resolution failed; falling back to base filters', err)\n }\n }\n if (ctx && advancedFilterTree) {\n const advancedFilters = mergeAdvancedFilterTree({ ...filters }, advancedFilterTree)\n const matchedIds = await findMatchingEntityIdsWithQueryEngine({\n ctx,\n entityId: E.customers.customer_entity,\n filters: advancedFilters,\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n })\n applyEntityIdRestriction(filters, matchedIds)\n }\n return filters\n },\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n transformItem: (item) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const normalized: Record<string, unknown> = { ...record }\n delete normalized.kind\n const cfEntries = extractAllCustomFieldEntries(record)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) {\n delete normalized[key]\n }\n }\n return { ...normalized, ...cfEntries }\n },\n },\n actions: {\n create: {\n commandId: 'customers.people.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n const parsed = personCreateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n response: ({ result }) => ({\n id: result?.entityId ?? result?.id ?? null,\n personId: result?.personId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: 'customers.people.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const normalized = normalizeProfilePayload(scoped, translate)\n const { base, custom } = splitCustomFieldPayload(normalized)\n const parsed = personUpdateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'customers.people.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.person_required', 'Person id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload?.items) ? payload.items : []\n const ids = items\n .map((item: unknown) => (\n item && typeof item === 'object' && typeof (item as Record<string, unknown>).id === 'string'\n ? (item as Record<string, unknown>).id as string\n : null\n ))\n .filter((id: string | null): id is string => typeof id === 'string' && id.length > 0)\n if (!ids.length) return\n\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const entities = await findWithDecryption(\n em,\n CustomerEntity,\n {\n id: { $in: ids },\n deletedAt: null,\n kind: 'person',\n } as FilterQuery<CustomerEntity>,\n undefined,\n decryptionScope,\n )\n const entitiesById = new Map<string, CustomerEntity>()\n for (const entity of entities) {\n entitiesById.set(entity.id, entity)\n }\n\n const where: Record<string, unknown> = {\n entity: { $in: ids },\n tenantId: ctx.auth?.tenantId ?? null,\n }\n if (ctx.selectedOrganizationId) {\n where.organizationId = ctx.selectedOrganizationId\n }\n\n const profiles = await findWithDecryption(\n em,\n CustomerPersonProfile,\n where as FilterQuery<CustomerPersonProfile>,\n { populate: ['entity', 'company'] },\n decryptionScope,\n )\n\n const profilesByEntityId = new Map<string, CustomerPersonProfile>()\n for (const profile of profiles) {\n const profileEntity = (profile as { entity?: { id?: unknown } }).entity\n const entityId = typeof profileEntity?.id === 'string' ? profileEntity.id : null\n if (entityId) profilesByEntityId.set(entityId, profile)\n }\n\n payload.items = items.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const entity = typeof record.id === 'string' ? entitiesById.get(record.id) : undefined\n const profile = typeof record.id === 'string' ? profilesByEntityId.get(record.id) : undefined\n if (!entity && !profile) return item\n return {\n ...record,\n display_name: entity?.displayName ?? record.display_name ?? null,\n description: entity?.description ?? record.description ?? null,\n owner_user_id: entity?.ownerUserId ?? record.owner_user_id ?? null,\n primary_email: entity?.primaryEmail ?? record.primary_email ?? null,\n primary_phone: entity?.primaryPhone ?? record.primary_phone ?? null,\n status: entity?.status ?? record.status ?? null,\n lifecycle_stage: entity?.lifecycleStage ?? record.lifecycle_stage ?? null,\n source: entity?.source ?? record.source ?? null,\n next_interaction_at: entity?.nextInteractionAt ? entity.nextInteractionAt.toISOString() : record.next_interaction_at ?? null,\n next_interaction_name: entity?.nextInteractionName ?? record.next_interaction_name ?? null,\n next_interaction_ref_id: entity?.nextInteractionRefId ?? record.next_interaction_ref_id ?? null,\n next_interaction_icon: entity?.nextInteractionIcon ?? record.next_interaction_icon ?? null,\n next_interaction_color: entity?.nextInteractionColor ?? record.next_interaction_color ?? null,\n first_name: profile?.firstName ?? null,\n last_name: profile?.lastName ?? null,\n preferred_name: profile?.preferredName ?? null,\n job_title: profile?.jobTitle ?? null,\n department: profile?.department ?? null,\n seniority: profile?.seniority ?? null,\n timezone: profile?.timezone ?? null,\n linked_in_url: profile?.linkedInUrl ?? null,\n twitter_url: profile?.twitterUrl ?? null,\n company_entity_id:\n profile?.company && typeof profile.company === 'object'\n ? profile.company.id\n : profile?.company ?? null,\n }\n })\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst personListItemSchema = z.object({\n id: z.string().uuid(),\n display_name: z.string().optional(),\n description: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n primary_email: z.string().nullable().optional(),\n primary_phone: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n lifecycle_stage: z.string().nullable().optional(),\n source: z.string().nullable().optional(),\n next_interaction_at: z.string().nullable().optional(),\n next_interaction_name: z.string().nullable().optional(),\n next_interaction_ref_id: z.string().nullable().optional(),\n next_interaction_icon: z.string().nullable().optional(),\n next_interaction_color: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n})\n\nconst personCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n personId: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Person',\n pluralName: 'People',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(personListItemSchema),\n create: {\n schema: personCreateSchema,\n responseSchema: personCreateResponseSchema,\n description: 'Creates a person contact using scoped organization and tenant identifiers.',\n },\n update: {\n schema: personUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates contact details or custom fields for a person.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a person by id. Request body or query may provide the identifier.',\n errors: [\n {\n status: 422,\n description: 'Person has dependent records (e.g. linked deals); unlink or reassign before delete.',\n schema: z.object({\n error: z.string(),\n code: z.literal('PERSON_HAS_DEPENDENTS'),\n }),\n },\n ],\n },\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n CustomerDealPersonLink,\n CustomerEntity,\n CustomerPersonCompanyLink,\n CustomerPersonProfile,\n} from '../../data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { personCreateSchema, personUpdateSchema } from '../../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n applyEntityIdExclusion,\n applyEntityIdRestriction,\n findMatchingEntityIdsWithQueryEngine,\n findMatchingEntityIdsBySearchTokensAcrossSources,\n withScopedPayload,\n} from '../utils'\nimport { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries, splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport {\n filterActivePersonCompanyLinks,\n withActiveCustomerPersonCompanyLinkFilter,\n} from '../../lib/personCompanyLinkTable'\nimport { normalizeProfilePayload } from './payload'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n email: z.string().optional(),\n emailStartsWith: z.string().optional(),\n emailContains: z.string().optional(),\n status: z.string().optional(),\n lifecycleStage: z.string().optional(),\n source: z.string().optional(),\n hasEmail: z.string().optional(),\n hasPhone: z.string().optional(),\n hasNextInteraction: z.string().optional(),\n createdFrom: z.string().optional(),\n createdTo: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n id: z.string().uuid().optional(),\n tagIds: z.string().optional(),\n tagIdsEmpty: z.string().optional(),\n excludeIds: z.string().optional(),\n excludeLinkedCompanyId: z.string().uuid().optional(),\n excludeLinkedDealId: z.string().uuid().optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CustomerEntity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n enrichers: { entityId: 'customers.person' },\n indexer: { entityType: E.customers.customer_entity },\n list: {\n schema: listSchema,\n entityId: E.customers.customer_entity,\n fields: [\n 'id',\n 'display_name',\n 'description',\n 'owner_user_id',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_at',\n 'next_interaction_name',\n 'next_interaction_ref_id',\n 'next_interaction_icon',\n 'next_interaction_color',\n 'organization_id',\n 'tenant_id',\n 'kind',\n 'created_at',\n ],\n sortFieldMap: {\n name: 'display_name',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query, ctx) => {\n const advancedFilterTree = consumeAdvancedFilterState(query)\n const filters: Record<string, unknown> = { kind: { $eq: 'person' } }\n if (query.id) filters.id = { $eq: query.id }\n if (query.search) {\n const matchingIds = ctx\n ? await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_entity,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'description',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_name',\n ],\n },\n {\n entityType: E.customers.customer_person_profile,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'first_name',\n 'last_name',\n 'preferred_name',\n 'job_title',\n 'department',\n 'seniority',\n 'timezone',\n 'linked_in_url',\n 'twitter_url',\n ],\n mapToEntityIds: {\n table: 'customer_people',\n targetColumn: 'entity_id',\n },\n },\n ],\n })\n : null\n if (matchingIds !== null && matchingIds.length > 0) {\n applyEntityIdRestriction(filters, matchingIds)\n } else {\n const searchPattern = `%${escapeLikePattern(query.search)}%`\n filters.$or = [\n { display_name: { $ilike: searchPattern } },\n { primary_email: { $ilike: searchPattern } },\n { primary_phone: { $ilike: searchPattern } },\n { description: { $ilike: searchPattern } },\n { next_interaction_name: { $ilike: searchPattern } },\n ]\n }\n }\n const email = typeof query.email === 'string' ? query.email.trim().toLowerCase() : ''\n const emailStartsWith = typeof query.emailStartsWith === 'string' ? query.emailStartsWith.trim().toLowerCase() : ''\n const emailContains = typeof query.emailContains === 'string' ? query.emailContains.trim().toLowerCase() : ''\n if (email) {\n filters.primary_email = { $eq: email }\n } else if (emailStartsWith) {\n filters.primary_email = { $ilike: `${escapeLikePattern(emailStartsWith)}%` }\n } else if (emailContains) {\n filters.primary_email = { $ilike: `%${escapeLikePattern(emailContains)}%` }\n }\n if (query.status) {\n filters.status = { $eq: query.status }\n }\n if (query.lifecycleStage) {\n filters.lifecycle_stage = { $eq: query.lifecycleStage }\n }\n if (query.source) {\n filters.source = { $eq: query.source }\n }\n const tagIdsRaw = typeof query.tagIds === 'string' ? query.tagIds : ''\n const tagIds = tagIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n const tagIdsEmpty = parseBooleanToken(query.tagIdsEmpty) === true\n if (tagIdsEmpty) {\n filters.id = { $eq: '00000000-0000-0000-0000-000000000000' }\n } else if (tagIds.length > 0) {\n filters['tag_assignments.tag_id'] = { $in: tagIds }\n }\n const excludedIds = new Set<string>()\n const excludeIdsRaw = typeof query.excludeIds === 'string' ? query.excludeIds : ''\n excludeIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n .forEach((value: string) => excludedIds.add(value))\n if (ctx && query.excludeLinkedCompanyId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(\n em,\n { company: query.excludeLinkedCompanyId },\n 'customers.people.GET',\n )\n const links = filterActivePersonCompanyLinks(\n await findWithDecryption(\n em,\n CustomerPersonCompanyLink,\n linkWhere,\n { populate: ['person'] },\n decryptionScope,\n ),\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n if (ctx && query.excludeLinkedDealId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const links = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n {\n deal: query.excludeLinkedDealId,\n },\n { populate: ['person'] },\n decryptionScope,\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n applyEntityIdExclusion(filters, Array.from(excludedIds))\n const hasEmail = parseBooleanToken(query.hasEmail)\n if (!email && !emailStartsWith && !emailContains && hasEmail !== null) {\n filters.primary_email = { $exists: hasEmail }\n }\n const hasPhone = parseBooleanToken(query.hasPhone)\n if (hasPhone !== null) {\n filters.primary_phone = { $exists: hasPhone }\n }\n const hasNextInteraction = parseBooleanToken(query.hasNextInteraction)\n if (hasNextInteraction !== null) {\n filters.next_interaction_at = { $exists: hasNextInteraction }\n }\n const createdRange: Record<string, Date> = {}\n if (query.createdFrom) {\n const from = new Date(query.createdFrom)\n if (!Number.isNaN(from.getTime())) createdRange.$gte = from\n }\n if (query.createdTo) {\n const to = new Date(query.createdTo)\n if (!Number.isNaN(to.getTime())) createdRange.$lte = to\n }\n if (Object.keys(createdRange).length) {\n filters.created_at = createdRange\n }\n if (ctx) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch (err) {\n console.warn('[customers.people.list] custom field filter resolution failed; falling back to base filters', err)\n }\n }\n if (ctx && advancedFilterTree) {\n const advancedFilters = mergeAdvancedFilterTree({ ...filters }, advancedFilterTree)\n const matchedIds = await findMatchingEntityIdsWithQueryEngine({\n ctx,\n entityId: E.customers.customer_entity,\n filters: advancedFilters,\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n })\n applyEntityIdRestriction(filters, matchedIds)\n }\n return filters\n },\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n transformItem: (item) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const normalized: Record<string, unknown> = { ...record }\n delete normalized.kind\n const cfEntries = extractAllCustomFieldEntries(record)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) {\n delete normalized[key]\n }\n }\n return { ...normalized, ...cfEntries }\n },\n },\n actions: {\n create: {\n commandId: 'customers.people.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n const parsed = personCreateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n response: ({ result }) => ({\n id: result?.entityId ?? result?.id ?? null,\n personId: result?.personId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: 'customers.people.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const normalized = normalizeProfilePayload(scoped, translate)\n const { base, custom } = splitCustomFieldPayload(normalized)\n const parsed = personUpdateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'customers.people.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.person_required', 'Person id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload?.items) ? payload.items : []\n const ids = items\n .map((item: unknown) => (\n item && typeof item === 'object' && typeof (item as Record<string, unknown>).id === 'string'\n ? (item as Record<string, unknown>).id as string\n : null\n ))\n .filter((id: string | null): id is string => typeof id === 'string' && id.length > 0)\n if (!ids.length) return\n\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const profileWhere: Record<string, unknown> = {\n entity: { $in: ids },\n tenantId: ctx.auth?.tenantId ?? null,\n }\n if (ctx.selectedOrganizationId) {\n profileWhere.organizationId = ctx.selectedOrganizationId\n }\n\n const [entities, profiles] = await Promise.all([\n findWithDecryption(\n em,\n CustomerEntity,\n {\n id: { $in: ids },\n deletedAt: null,\n kind: 'person',\n } as FilterQuery<CustomerEntity>,\n undefined,\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerPersonProfile,\n profileWhere as FilterQuery<CustomerPersonProfile>,\n { populate: ['entity', 'company'] },\n decryptionScope,\n ),\n ])\n\n const entitiesById = new Map<string, CustomerEntity>()\n for (const entity of entities) {\n entitiesById.set(entity.id, entity)\n }\n\n const profilesByEntityId = new Map<string, CustomerPersonProfile>()\n for (const profile of profiles) {\n const profileEntity = (profile as { entity?: { id?: unknown } }).entity\n const entityId = typeof profileEntity?.id === 'string' ? profileEntity.id : null\n if (entityId) profilesByEntityId.set(entityId, profile)\n }\n\n payload.items = items.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const entity = typeof record.id === 'string' ? entitiesById.get(record.id) : undefined\n const profile = typeof record.id === 'string' ? profilesByEntityId.get(record.id) : undefined\n if (!entity && !profile) return item\n return {\n ...record,\n display_name: entity?.displayName ?? record.display_name ?? null,\n description: entity?.description ?? record.description ?? null,\n owner_user_id: entity?.ownerUserId ?? record.owner_user_id ?? null,\n primary_email: entity?.primaryEmail ?? record.primary_email ?? null,\n primary_phone: entity?.primaryPhone ?? record.primary_phone ?? null,\n status: entity?.status ?? record.status ?? null,\n lifecycle_stage: entity?.lifecycleStage ?? record.lifecycle_stage ?? null,\n source: entity?.source ?? record.source ?? null,\n next_interaction_at: entity?.nextInteractionAt ? entity.nextInteractionAt.toISOString() : record.next_interaction_at ?? null,\n next_interaction_name: entity?.nextInteractionName ?? record.next_interaction_name ?? null,\n next_interaction_ref_id: entity?.nextInteractionRefId ?? record.next_interaction_ref_id ?? null,\n next_interaction_icon: entity?.nextInteractionIcon ?? record.next_interaction_icon ?? null,\n next_interaction_color: entity?.nextInteractionColor ?? record.next_interaction_color ?? null,\n first_name: profile?.firstName ?? null,\n last_name: profile?.lastName ?? null,\n preferred_name: profile?.preferredName ?? null,\n job_title: profile?.jobTitle ?? null,\n department: profile?.department ?? null,\n seniority: profile?.seniority ?? null,\n timezone: profile?.timezone ?? null,\n linked_in_url: profile?.linkedInUrl ?? null,\n twitter_url: profile?.twitterUrl ?? null,\n company_entity_id:\n profile?.company && typeof profile.company === 'object'\n ? profile.company.id\n : profile?.company ?? null,\n }\n })\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst personListItemSchema = z.object({\n id: z.string().uuid(),\n display_name: z.string().optional(),\n description: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n primary_email: z.string().nullable().optional(),\n primary_phone: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n lifecycle_stage: z.string().nullable().optional(),\n source: z.string().nullable().optional(),\n next_interaction_at: z.string().nullable().optional(),\n next_interaction_name: z.string().nullable().optional(),\n next_interaction_ref_id: z.string().nullable().optional(),\n next_interaction_icon: z.string().nullable().optional(),\n next_interaction_color: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n})\n\nconst personCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n personId: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Person',\n pluralName: 'People',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(personListItemSchema),\n create: {\n schema: personCreateSchema,\n responseSchema: personCreateResponseSchema,\n description: 'Creates a person contact using scoped organization and tenant identifiers.',\n },\n update: {\n schema: personUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates contact details or custom fields for a person.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a person by id. Request body or query may provide the identifier.',\n errors: [\n {\n status: 422,\n description: 'Person has dependent records (e.g. linked deals); unlink or reassign before delete.',\n schema: z.object({\n error: z.string(),\n code: z.literal('PERSON_HAS_DEPENDENTS'),\n }),\n },\n ],\n },\n})\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS;AAClB,SAAS,oBAAoB,0BAA0B;AACvD,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kCAAkC,8BAA8B,+BAA+B;AACxG,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAClC,SAAS,0BAA0B;AACnC,SAAS,4BAA4B,+BAA+B;AACpE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,+BAA+B;AAExC,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,aAAa,EAChB,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,oBAAoB,EAAE,OAAO,EAAE,SAAS;AAAA,EACxC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA,EAC1C,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,wBAAwB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACnD,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAClD,CAAC,EACA,YAAY;AAEf,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AAAA,EACrE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAAA,EACxE,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAAA,EACvE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAC5E;AAEO,MAAM,WAAW;AAExB,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,WAAW,EAAE,UAAU,mBAAmB;AAAA,EAC1C,SAAS,EAAE,YAAY,EAAE,UAAU,gBAAgB;AAAA,EACnD,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,UAAU,EAAE,UAAU;AAAA,IACtB,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,IACA,cAAc,OAAO,OAAO,QAAQ;AAClC,YAAM,qBAAqB,2BAA2B,KAAK;AAC3D,YAAM,UAAmC,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE;AACnE,UAAI,MAAM,GAAI,SAAQ,KAAK,EAAE,KAAK,MAAM,GAAG;AAC3C,UAAI,MAAM,QAAQ;AAChB,cAAM,cAAc,MAChB,MAAM,iDAAiD;AAAA,UACrD;AAAA,UACA,OAAO,MAAM;AAAA,UACb,SAAS;AAAA,YACP;AAAA,cACE,YAAY,EAAE,UAAU;AAAA,cACxB,QAAQ;AAAA,gBACN;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,YACF;AAAA,YACA;AAAA,cACE,YAAY,EAAE,UAAU;AAAA,cACxB,QAAQ;AAAA,gBACN;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,cACA,gBAAgB;AAAA,gBACd,OAAO;AAAA,gBACP,cAAc;AAAA,cAChB;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC,IACD;AACJ,YAAI,gBAAgB,QAAQ,YAAY,SAAS,GAAG;AAClD,mCAAyB,SAAS,WAAW;AAAA,QAC/C,OAAO;AACL,gBAAM,gBAAgB,IAAI,kBAAkB,MAAM,MAAM,CAAC;AACzD,kBAAQ,MAAM;AAAA,YACZ,EAAE,cAAc,EAAE,QAAQ,cAAc,EAAE;AAAA,YAC1C,EAAE,eAAe,EAAE,QAAQ,cAAc,EAAE;AAAA,YAC3C,EAAE,eAAe,EAAE,QAAQ,cAAc,EAAE;AAAA,YAC3C,EAAE,aAAa,EAAE,QAAQ,cAAc,EAAE;AAAA,YACzC,EAAE,uBAAuB,EAAE,QAAQ,cAAc,EAAE;AAAA,UACrD;AAAA,QACF;AAAA,MACF;AACA,YAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,MAAM,MAAM,KAAK,EAAE,YAAY,IAAI;AACnF,YAAM,kBAAkB,OAAO,MAAM,oBAAoB,WAAW,MAAM,gBAAgB,KAAK,EAAE,YAAY,IAAI;AACjH,YAAM,gBAAgB,OAAO,MAAM,kBAAkB,WAAW,MAAM,cAAc,KAAK,EAAE,YAAY,IAAI;AAC3G,UAAI,OAAO;AACT,gBAAQ,gBAAgB,EAAE,KAAK,MAAM;AAAA,MACvC,WAAW,iBAAiB;AAC1B,gBAAQ,gBAAgB,EAAE,QAAQ,GAAG,kBAAkB,eAAe,CAAC,IAAI;AAAA,MAC7E,WAAW,eAAe;AACxB,gBAAQ,gBAAgB,EAAE,QAAQ,IAAI,kBAAkB,aAAa,CAAC,IAAI;AAAA,MAC5E;AACA,UAAI,MAAM,QAAQ;AAChB,gBAAQ,SAAS,EAAE,KAAK,MAAM,OAAO;AAAA,MACvC;AACA,UAAI,MAAM,gBAAgB;AACxB,gBAAQ,kBAAkB,EAAE,KAAK,MAAM,eAAe;AAAA,MACxD;AACA,UAAI,MAAM,QAAQ;AAChB,gBAAQ,SAAS,EAAE,KAAK,MAAM,OAAO;AAAA,MACvC;AACA,YAAM,YAAY,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACpE,YAAM,SAAS,UACZ,MAAM,GAAG,EACT,IAAI,CAAC,UAAkB,MAAM,KAAK,CAAC,EACnC,OAAO,CAAC,UAAkB,MAAM,SAAS,CAAC;AAC7C,YAAM,cAAc,kBAAkB,MAAM,WAAW,MAAM;AAC7D,UAAI,aAAa;AACf,gBAAQ,KAAK,EAAE,KAAK,uCAAuC;AAAA,MAC7D,WAAW,OAAO,SAAS,GAAG;AAC5B,gBAAQ,wBAAwB,IAAI,EAAE,KAAK,OAAO;AAAA,MACpD;AACA,YAAM,cAAc,oBAAI,IAAY;AACpC,YAAM,gBAAgB,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAChF,oBACG,MAAM,GAAG,EACT,IAAI,CAAC,UAAkB,MAAM,KAAK,CAAC,EACnC,OAAO,CAAC,UAAkB,MAAM,SAAS,CAAC,EAC1C,QAAQ,CAAC,UAAkB,YAAY,IAAI,KAAK,CAAC;AACpD,UAAI,OAAO,MAAM,wBAAwB;AACvC,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,gBAAM,kBAAkB;AAAA,YACtB,UAAU,IAAI,MAAM,YAAY;AAAA,YAChC,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,UACnE;AACA,gBAAM,YAAY,MAAM;AAAA,YACtB;AAAA,YACA,EAAE,SAAS,MAAM,uBAAuB;AAAA,YACxC;AAAA,UACF;AACA,gBAAM,QAAQ;AAAA,YACZ,MAAM;AAAA,cACJ;AAAA,cACA;AAAA,cACA;AAAA,cACA,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,cACvB;AAAA,YACF;AAAA,UACF;AACA,gBAAM,QAAQ,CAAC,SAAS;AACtB,kBAAM,WAAW,KAAK,QAAQ;AAC9B,gBAAI,OAAO,aAAa,YAAY,SAAS,SAAS,EAAG,aAAY,IAAI,QAAQ;AAAA,UACnF,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,kBAAQ,KAAK,oFAAoF,GAAG;AAAA,QACtG;AAAA,MACF;AACA,UAAI,OAAO,MAAM,qBAAqB;AACpC,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,gBAAM,kBAAkB;AAAA,YACtB,UAAU,IAAI,MAAM,YAAY;AAAA,YAChC,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,UACnE;AACA,gBAAM,QAAQ,MAAM;AAAA,YAClB;AAAA,YACA;AAAA,YACA;AAAA,cACE,MAAM,MAAM;AAAA,YACd;AAAA,YACA,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,YACvB;AAAA,UACF;AACA,gBAAM,QAAQ,CAAC,SAAS;AACtB,kBAAM,WAAW,KAAK,QAAQ;AAC9B,gBAAI,OAAO,aAAa,YAAY,SAAS,SAAS,EAAG,aAAY,IAAI,QAAQ;AAAA,UACnF,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,kBAAQ,KAAK,oFAAoF,GAAG;AAAA,QACtG;AAAA,MACF;AACA,6BAAuB,SAAS,MAAM,KAAK,WAAW,CAAC;AACvD,YAAM,WAAW,kBAAkB,MAAM,QAAQ;AACjD,UAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,iBAAiB,aAAa,MAAM;AACrE,gBAAQ,gBAAgB,EAAE,SAAS,SAAS;AAAA,MAC9C;AACA,YAAM,WAAW,kBAAkB,MAAM,QAAQ;AACjD,UAAI,aAAa,MAAM;AACrB,gBAAQ,gBAAgB,EAAE,SAAS,SAAS;AAAA,MAC9C;AACA,YAAM,qBAAqB,kBAAkB,MAAM,kBAAkB;AACrE,UAAI,uBAAuB,MAAM;AAC/B,gBAAQ,sBAAsB,EAAE,SAAS,mBAAmB;AAAA,MAC9D;AACA,YAAM,eAAqC,CAAC;AAC5C,UAAI,MAAM,aAAa;AACrB,cAAM,OAAO,IAAI,KAAK,MAAM,WAAW;AACvC,YAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,cAAa,OAAO;AAAA,MACzD;AACA,UAAI,MAAM,WAAW;AACnB,cAAM,KAAK,IAAI,KAAK,MAAM,SAAS;AACnC,YAAI,CAAC,OAAO,MAAM,GAAG,QAAQ,CAAC,EAAG,cAAa,OAAO;AAAA,MACvD;AACA,UAAI,OAAO,KAAK,YAAY,EAAE,QAAQ;AACpC,gBAAQ,aAAa;AAAA,MACvB;AACA,UAAI,KAAK;AACP,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,gBAAM,YAAY,MAAM,iCAAiC;AAAA,YACvD,WAAW,CAAC,EAAE,UAAU,iBAAiB,EAAE,UAAU,uBAAuB;AAAA,YAC5E;AAAA,YACA;AAAA,YACA,UAAU,IAAI,MAAM,YAAY;AAAA,UAClC,CAAC;AACD,iBAAO,OAAO,SAAS,SAAS;AAAA,QAClC,SAAS,KAAK;AACZ,kBAAQ,KAAK,+FAA+F,GAAG;AAAA,QACjH;AAAA,MACF;AACA,UAAI,OAAO,oBAAoB;AAC7B,cAAM,kBAAkB,wBAAwB,EAAE,GAAG,QAAQ,GAAG,kBAAkB;AAClF,cAAM,aAAa,MAAM,qCAAqC;AAAA,UAC5D;AAAA,UACA,UAAU,EAAE,UAAU;AAAA,UACtB,SAAS;AAAA,UACT,oBAAoB;AAAA,YAClB;AAAA,cACE,UAAU,EAAE,UAAU;AAAA,cACtB,OAAO;AAAA,cACP,OAAO;AAAA,cACP,gBAAgB;AAAA,cAChB,MAAM,EAAE,WAAW,MAAM,SAAS,YAAY;AAAA,YAChD;AAAA,UACF;AAAA,UACA,OAAO;AAAA,YACL;AAAA,cACE,OAAO;AAAA,cACP,OAAO;AAAA,cACP,MAAM,EAAE,OAAO,KAAK;AAAA,cACpB,IAAI,EAAE,OAAO,YAAY;AAAA,cACzB,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF,CAAC;AACD,iCAAyB,SAAS,UAAU;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAAA,IACA,oBAAoB;AAAA,MAClB;AAAA,QACE,UAAU,EAAE,UAAU;AAAA,QACtB,OAAO;AAAA,QACP,OAAO;AAAA,QACP,gBAAgB;AAAA,QAChB,MAAM,EAAE,WAAW,MAAM,SAAS,YAAY;AAAA,MAChD;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM,EAAE,OAAO,KAAK;AAAA,QACpB,IAAI,EAAE,OAAO,YAAY;AAAA,QACzB,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,eAAe,CAAC,SAAS;AACvB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,YAAM,SAAS;AACf,YAAM,aAAsC,EAAE,GAAG,OAAO;AACxD,aAAO,WAAW;AAClB,YAAM,YAAY,6BAA6B,MAAM;AACrD,iBAAW,OAAO,OAAO,KAAK,UAAU,GAAG;AACzC,YAAI,IAAI,WAAW,KAAK,GAAG;AACzB,iBAAO,WAAW,GAAG;AAAA,QACvB;AAAA,MACF;AACA,aAAO,EAAE,GAAG,YAAY,GAAG,UAAU;AAAA,IACvC;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,cAAM,EAAE,MAAM,OAAO,IAAI,wBAAwB,MAAM;AACvD,cAAM,SAAS,mBAAmB,MAAM,IAAI;AAC5C,eAAO,OAAO,KAAK,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,cAAc,OAAO,IAAI;AAAA,MAC5E;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,IAAI,QAAQ,YAAY,QAAQ,MAAM;AAAA,QACtC,UAAU,QAAQ,YAAY;AAAA,MAChC;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,cAAM,aAAa,wBAAwB,QAAQ,SAAS;AAC5D,cAAM,EAAE,MAAM,OAAO,IAAI,wBAAwB,UAAU;AAC3D,cAAM,SAAS,mBAAmB,MAAM,IAAI;AAC5C,eAAO,OAAO,KAAK,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,cAAc,OAAO,IAAI;AAAA,MAC5E;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,KACJ,QAAQ,MAAM,MACd,QAAQ,MACR,QAAQ,OAAO,OACd,IAAI,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,IAAI,IAAI;AACnE,YAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,oCAAoC,uBAAuB,EAAE,CAAC;AACvH,eAAO,EAAE,GAAG;AAAA,MACd;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,WAAW,OAAO,SAAS,QAAQ;AACjC,YAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC/D,YAAM,MAAM,MACT,IAAI,CAAC,SACJ,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAAiC,OAAO,WAC/E,KAAiC,KAClC,IACL,EACA,OAAO,CAAC,OAAoC,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACtF,UAAI,CAAC,IAAI,OAAQ;AAEjB,YAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,YAAM,kBAAkB;AAAA,QACtB,UAAU,IAAI,MAAM,YAAY;AAAA,QAChC,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,MACnE;AACA,YAAM,eAAwC;AAAA,QAC5C,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,UAAU,IAAI,MAAM,YAAY;AAAA,MAClC;AACA,UAAI,IAAI,wBAAwB;AAC9B,qBAAa,iBAAiB,IAAI;AAAA,MACpC;AAEA,YAAM,CAAC,UAAU,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC7C;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,YACE,IAAI,EAAE,KAAK,IAAI;AAAA,YACf,WAAW;AAAA,YACX,MAAM;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA,EAAE,UAAU,CAAC,UAAU,SAAS,EAAE;AAAA,UAClC;AAAA,QACF;AAAA,MACF,CAAC;AAED,YAAM,eAAe,oBAAI,IAA4B;AACrD,iBAAW,UAAU,UAAU;AAC7B,qBAAa,IAAI,OAAO,IAAI,MAAM;AAAA,MACpC;AAEA,YAAM,qBAAqB,oBAAI,IAAmC;AAClE,iBAAW,WAAW,UAAU;AAC9B,cAAM,gBAAiB,QAA0C;AACjE,cAAM,WAAW,OAAO,eAAe,OAAO,WAAW,cAAc,KAAK;AAC5E,YAAI,SAAU,oBAAmB,IAAI,UAAU,OAAO;AAAA,MACxD;AAEA,cAAQ,QAAQ,MAAM,IAAI,CAAC,SAAkB;AAC3C,YAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,cAAM,SAAS;AACf,cAAM,SAAS,OAAO,OAAO,OAAO,WAAW,aAAa,IAAI,OAAO,EAAE,IAAI;AAC7E,cAAM,UAAU,OAAO,OAAO,OAAO,WAAW,mBAAmB,IAAI,OAAO,EAAE,IAAI;AACpF,YAAI,CAAC,UAAU,CAAC,QAAS,QAAO;AAChC,eAAO;AAAA,UACL,GAAG;AAAA,UACH,cAAc,QAAQ,eAAe,OAAO,gBAAgB;AAAA,UAC5D,aAAa,QAAQ,eAAe,OAAO,eAAe;AAAA,UAC1D,eAAe,QAAQ,eAAe,OAAO,iBAAiB;AAAA,UAC9D,eAAe,QAAQ,gBAAgB,OAAO,iBAAiB;AAAA,UAC/D,eAAe,QAAQ,gBAAgB,OAAO,iBAAiB;AAAA,UAC/D,QAAQ,QAAQ,UAAU,OAAO,UAAU;AAAA,UAC3C,iBAAiB,QAAQ,kBAAkB,OAAO,mBAAmB;AAAA,UACrE,QAAQ,QAAQ,UAAU,OAAO,UAAU;AAAA,UAC3C,qBAAqB,QAAQ,oBAAoB,OAAO,kBAAkB,YAAY,IAAI,OAAO,uBAAuB;AAAA,UACxH,uBAAuB,QAAQ,uBAAuB,OAAO,yBAAyB;AAAA,UACtF,yBAAyB,QAAQ,wBAAwB,OAAO,2BAA2B;AAAA,UAC3F,uBAAuB,QAAQ,uBAAuB,OAAO,yBAAyB;AAAA,UACtF,wBAAwB,QAAQ,wBAAwB,OAAO,0BAA0B;AAAA,UACzF,YAAY,SAAS,aAAa;AAAA,UAClC,WAAW,SAAS,YAAY;AAAA,UAChC,gBAAgB,SAAS,iBAAiB;AAAA,UAC1C,WAAW,SAAS,YAAY;AAAA,UAChC,YAAY,SAAS,cAAc;AAAA,UACnC,WAAW,SAAS,aAAa;AAAA,UACjC,UAAU,SAAS,YAAY;AAAA,UAC/B,eAAe,SAAS,eAAe;AAAA,UACvC,aAAa,SAAS,cAAc;AAAA,UACpC,mBACE,SAAS,WAAW,OAAO,QAAQ,YAAY,WAC3C,QAAQ,QAAQ,KAChB,SAAS,WAAW;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAED,MAAM,EAAE,MAAM,KAAK,OAAO,IAAI;AAGvB,MAAM,MAAM,KAAK;AAExB,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACrD,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACpD,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,yBAAyB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,wBAAwB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC7C,CAAC;AAED,MAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,UAAU,2BAA2B;AAAA,EAChD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB,8BAA8B,oBAAoB;AAAA,EACtE,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,QAAQ;AAAA,MACN;AAAA,QACE,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,QAAQ,EAAE,OAAO;AAAA,UACf,OAAO,EAAE,OAAO;AAAA,UAChB,MAAM,EAAE,QAAQ,uBAAuB;AAAA,QACzC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
event: "directory.organization.*",
|
|
3
|
+
persistent: false,
|
|
4
|
+
id: "directory:invalidate-org-scope-cache"
|
|
5
|
+
};
|
|
6
|
+
async function handle(payload, ctx) {
|
|
7
|
+
const data = payload ?? {};
|
|
8
|
+
const tenantId = typeof data.tenantId === "string" ? data.tenantId : null;
|
|
9
|
+
if (!tenantId) return;
|
|
10
|
+
let cache = null;
|
|
11
|
+
try {
|
|
12
|
+
cache = ctx.resolve("cache");
|
|
13
|
+
} catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!cache) return;
|
|
17
|
+
try {
|
|
18
|
+
await cache.deleteByTags([`org-scope:tenant:${tenantId}`]);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
handle as default,
|
|
24
|
+
metadata
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=invalidateOrgScopeCache.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/directory/subscribers/invalidateOrgScopeCache.ts"],
|
|
4
|
+
"sourcesContent": ["// Invalidate the OrganizationScope cache when an organization mutates.\n//\n// resolveOrganizationScopeForRequest caches its result with a short TTL\n// (default 60s, OM_ORG_SCOPE_CACHE_TTL_MS). When an organization is\n// created/updated/deleted, the cached scope for users of the affected\n// tenant may be stale (visibility set or descendant tree changed). We\n// drop every cache entry tagged for that tenant; the TTL is the backstop\n// for races where the event fires after a request reads the cache.\n\ntype CacheService = {\n deleteByTags(tags: string[]): Promise<number>\n}\n\nexport const metadata = {\n event: 'directory.organization.*',\n persistent: false,\n id: 'directory:invalidate-org-scope-cache',\n}\n\nexport default async function handle(\n payload: unknown,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<void> {\n const data = (payload ?? {}) as Record<string, unknown>\n const tenantId = typeof data.tenantId === 'string' ? data.tenantId : null\n if (!tenantId) return\n let cache: CacheService | null = null\n try {\n cache = ctx.resolve<CacheService>('cache')\n } catch {\n return\n }\n if (!cache) return\n try {\n await cache.deleteByTags([`org-scope:tenant:${tenantId}`])\n } catch {\n // best-effort; TTL is the backstop.\n }\n}\n"],
|
|
5
|
+
"mappings": "AAaO,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,eAAO,OACL,SACA,KACe;AACf,QAAM,OAAQ,WAAW,CAAC;AAC1B,QAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AACrE,MAAI,CAAC,SAAU;AACf,MAAI,QAA6B;AACjC,MAAI;AACF,YAAQ,IAAI,QAAsB,OAAO;AAAA,EAC3C,QAAQ;AACN;AAAA,EACF;AACA,MAAI,CAAC,MAAO;AACZ,MAAI;AACF,UAAM,MAAM,aAAa,CAAC,oBAAoB,QAAQ,EAAE,CAAC;AAAA,EAC3D,QAAQ;AAAA,EAER;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|