@open-mercato/core 0.6.6-develop.6384.1.f06fc0b42c → 0.6.6-develop.6386.1.391a1afb9e
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/bootstrap.js +6 -1
- package/dist/bootstrap.js.map +2 -2
- package/dist/modules/attachments/lib/scope.js +26 -0
- package/dist/modules/attachments/lib/scope.js.map +7 -0
- package/dist/modules/catalog/commands/variants.js +4 -2
- package/dist/modules/catalog/commands/variants.js.map +2 -2
- package/dist/modules/configs/api/module-telemetry/route.js +75 -0
- package/dist/modules/configs/api/module-telemetry/route.js.map +7 -0
- package/dist/modules/configs/api/openapi.js +110 -0
- package/dist/modules/configs/api/openapi.js.map +2 -2
- package/dist/modules/configs/backend/config/cache/page.meta.js +1 -1
- package/dist/modules/configs/backend/config/cache/page.meta.js.map +1 -1
- package/dist/modules/configs/backend/config/module-telemetry/page.js +10 -0
- package/dist/modules/configs/backend/config/module-telemetry/page.js.map +7 -0
- package/dist/modules/configs/backend/config/module-telemetry/page.meta.js +36 -0
- package/dist/modules/configs/backend/config/module-telemetry/page.meta.js.map +7 -0
- package/dist/modules/configs/components/ModuleTelemetryPanel.js +801 -0
- package/dist/modules/configs/components/ModuleTelemetryPanel.js.map +7 -0
- package/dist/modules/messages/commands/messages.js +0 -1
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/lib/attachments.js +13 -7
- package/dist/modules/messages/lib/attachments.js.map +2 -2
- package/dist/modules/query_index/di.js +3 -3
- package/dist/modules/query_index/di.js.map +2 -2
- package/package.json +7 -7
- package/src/bootstrap.ts +7 -1
- package/src/modules/attachments/lib/scope.ts +42 -0
- package/src/modules/catalog/commands/variants.ts +7 -2
- package/src/modules/configs/api/module-telemetry/route.ts +77 -0
- package/src/modules/configs/api/openapi.ts +117 -0
- package/src/modules/configs/backend/config/cache/page.meta.ts +1 -1
- package/src/modules/configs/backend/config/module-telemetry/page.meta.ts +34 -0
- package/src/modules/configs/backend/config/module-telemetry/page.tsx +12 -0
- package/src/modules/configs/components/ModuleTelemetryPanel.tsx +1109 -0
- package/src/modules/configs/i18n/de.json +72 -0
- package/src/modules/configs/i18n/en.json +72 -0
- package/src/modules/configs/i18n/es.json +72 -0
- package/src/modules/configs/i18n/pl.json +72 -0
- package/src/modules/messages/commands/messages.ts +0 -1
- package/src/modules/messages/lib/attachments.ts +15 -7
- package/src/modules/query_index/di.ts +3 -3
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/configs/components/ModuleTelemetryPanel.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n SegmentedControl,\n SegmentedControlItem,\n} from '@open-mercato/ui/primitives/segmented-control'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { SimpleTooltip, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@open-mercato/ui/primitives/tooltip'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { formatAttachmentFileSize as formatBytes } from '@open-mercato/ui/backend/detail/AttachmentVisualPreview'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Copy, Info, RefreshCw, Trash2 } from 'lucide-react'\nimport type {\n ModuleResourceUsageEntry,\n ModuleResourceUsageReport,\n ModuleResourceUsageTimeBucket,\n ModuleResourceUsageTimeBucketModule,\n} from '@open-mercato/shared/lib/modules/resource-usage'\n\nconst API_PATH = '/api/configs/module-telemetry'\n\ntype FetchState = {\n loading: boolean\n error: string | null\n report: ModuleTelemetryApiResponse | null\n}\n\ntype ModuleTelemetryApiResponse = ModuleResourceUsageReport & {\n canClearTelemetry?: boolean\n}\n\ntype ModuleTelemetryClearResponse = {\n cleared: boolean\n}\n\nconst HOUR_MS = 60 * 60 * 1000\n\nconst REASON_LABEL_KEYS: Record<string, string> = {\n p95_duration: 'configs.moduleTelemetry.reason.p95Duration',\n cpu: 'configs.moduleTelemetry.reason.cpu',\n heap_allocations: 'configs.moduleTelemetry.reason.heapAllocations',\n rss_growth: 'configs.moduleTelemetry.reason.rssGrowth',\n errors: 'configs.moduleTelemetry.reason.errors',\n}\n\nconst REASON_FALLBACKS: Record<string, string> = {\n p95_duration: 'Slow p95',\n cpu: 'CPU',\n heap_allocations: 'Heap',\n rss_growth: 'RSS growth',\n errors: 'Errors',\n}\n\ntype UsageRangePreset = 'today' | 'last_1h' | 'last_6h' | 'last_24h' | 'all'\n\ntype UsageRange = {\n preset: UsageRangePreset\n startMs: number\n endMs: number\n}\n\ntype RangeModuleSummary = ModuleResourceUsageTimeBucketModule & {\n stage: ModuleResourceUsageTimeBucket['stage']\n}\n\ntype RangeModuleGroup = {\n moduleId: string\n stages: RangeModuleSummary[]\n candidateReasons: string[]\n calls: number\n totalCpuMs: number\n positiveRssDeltaBytes: number\n}\n\nconst USAGE_RANGE_PRESETS: UsageRangePreset[] = ['today', 'last_1h', 'last_6h', 'last_24h', 'all']\n\nfunction formatMs(ms: number): string {\n if (!Number.isFinite(ms) || ms <= 0) return '0 ms'\n if (ms >= 1000) return `${(ms / 1000).toFixed(ms >= 10_000 ? 1 : 2)} s`\n return `${Math.round(ms)} ms`\n}\n\nfunction formatCount(value: number): string {\n return new Intl.NumberFormat().format(value)\n}\n\nfunction formatBucketInterval(ms: number | undefined): string {\n if (!ms || !Number.isFinite(ms)) return 'interval'\n if (ms >= 60 * 60 * 1000) {\n const hours = Math.round(ms / (60 * 60 * 1000))\n return `${hours}h`\n }\n const minutes = Math.round(ms / (60 * 1000))\n return `${minutes}m`\n}\n\nfunction bucketIntervalLabel(\n buckets: ModuleResourceUsageTimeBucket[],\n fallbackMs: number | undefined,\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string,\n): string {\n const intervals = new Set(\n buckets\n .map((bucket) => bucket.bucketIntervalMs)\n .filter((value) => Number.isFinite(value) && value > 0),\n )\n if (intervals.size === 1) return formatBucketInterval(Array.from(intervals)[0])\n return formatBucketInterval(fallbackMs)\n}\n\nfunction bucketCountLabel(\n buckets: ModuleResourceUsageTimeBucket[],\n fallbackMs: number | undefined,\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string,\n): string {\n const intervals = new Set(\n buckets\n .map((bucket) => bucket.bucketIntervalMs)\n .filter((value) => Number.isFinite(value) && value > 0),\n )\n if (intervals.size > 1) {\n return translate(\n 'configs.moduleTelemetry.overview.mixedBucketCount',\n '{{count}} buckets \u00B7 mixed intervals',\n { count: buckets.length },\n )\n }\n return translate(\n 'configs.moduleTelemetry.overview.bucketCount',\n '{{count}} {{bucketWord}} ({{interval}} interval)',\n {\n count: buckets.length,\n bucketWord: buckets.length === 1\n ? translate('configs.moduleTelemetry.overview.bucketSingular', 'bucket')\n : translate('configs.moduleTelemetry.overview.bucketPlural', 'buckets'),\n interval: bucketIntervalLabel(buckets, fallbackMs, translate),\n },\n )\n}\n\nfunction formatRangeBoundary(ms: number): string {\n return new Date(ms).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })\n}\n\nfunction MetricTitle({\n label,\n tooltip,\n}: {\n label: string\n tooltip: string\n}) {\n return (\n <div className=\"inline-flex min-w-0 items-center gap-1.5 text-xs uppercase tracking-wide text-muted-foreground\">\n <span className=\"truncate\">{label}</span>\n <SimpleTooltip content={tooltip} side=\"top\" align=\"center\" variant=\"light\" size=\"lg\">\n <span\n className=\"inline-flex size-4 shrink-0 items-center justify-center rounded-full text-muted-foreground hover:text-foreground\"\n aria-label={tooltip}\n >\n <Info className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </span>\n </SimpleTooltip>\n </div>\n )\n}\n\nfunction MetricCard({\n label,\n tooltip,\n value,\n subValue,\n}: {\n label: string\n tooltip: string\n value: React.ReactNode\n subValue?: React.ReactNode\n}) {\n return (\n <div className=\"rounded-lg border bg-card p-4\">\n <MetricTitle label={label} tooltip={tooltip} />\n <div className=\"mt-2 text-2xl font-semibold\">{value}</div>\n {subValue ? <div className=\"mt-1 text-xs text-muted-foreground\">{subValue}</div> : null}\n </div>\n )\n}\n\nfunction firstBucketStartMs(buckets: ModuleResourceUsageTimeBucket[]): number | null {\n const starts = buckets\n .map((bucket) => Date.parse(bucket.bucketStart))\n .filter((value) => Number.isFinite(value))\n return starts.length ? Math.min(...starts) : null\n}\n\nfunction firstBucketEndMs(buckets: ModuleResourceUsageTimeBucket[]): number | null {\n const ends = buckets\n .map((bucket) => Date.parse(bucket.bucketEnd))\n .filter((value) => Number.isFinite(value))\n return ends.length ? Math.min(...ends) : null\n}\n\nfunction firstAvailableTelemetryMs(buckets: ModuleResourceUsageTimeBucket[], startedAtMs: number, nowMs: number): number {\n const firstBucketMs = firstBucketStartMs(buckets)\n if (firstBucketMs === null) return Number.isFinite(startedAtMs) ? startedAtMs : nowMs\n if (!Number.isFinite(startedAtMs)) return firstBucketMs\n\n const firstBucketEnd = firstBucketEndMs(buckets)\n if (firstBucketEnd !== null && firstBucketEnd > startedAtMs) return startedAtMs\n return firstBucketMs\n}\n\nfunction startOfTodayMs(nowMs: number): number {\n const date = new Date(nowMs)\n date.setHours(0, 0, 0, 0)\n return date.getTime()\n}\n\nfunction resolveUsageRange(\n preset: UsageRangePreset,\n buckets: ModuleResourceUsageTimeBucket[],\n startedAt: string,\n nowMs = Date.now(),\n): UsageRange {\n const startedAtMs = Date.parse(startedAt)\n const availableStartMs = firstAvailableTelemetryMs(buckets, startedAtMs, nowMs)\n const rawStartMs = preset === 'today'\n ? startOfTodayMs(nowMs)\n : preset === 'last_1h'\n ? nowMs - HOUR_MS\n : preset === 'last_6h'\n ? nowMs - 6 * HOUR_MS\n : preset === 'last_24h'\n ? nowMs - 24 * HOUR_MS\n : availableStartMs\n const boundedStartMs = preset === 'all' ? rawStartMs : Math.max(rawStartMs, availableStartMs)\n return {\n preset,\n startMs: Math.min(boundedStartMs, nowMs),\n endMs: nowMs,\n }\n}\n\nfunction bucketsInRange(buckets: ModuleResourceUsageTimeBucket[], range: UsageRange): ModuleResourceUsageTimeBucket[] {\n return buckets.filter((bucket) => {\n const bucketStartMs = Date.parse(bucket.bucketStart)\n const bucketEndMs = Date.parse(bucket.bucketEnd)\n if (!Number.isFinite(bucketStartMs) || !Number.isFinite(bucketEndMs)) return false\n return bucketEndMs > range.startMs && bucketStartMs <= range.endMs\n })\n}\n\n// Hours of telemetry actually observed in the range (sum of the returned buckets' own\n// intervals), not the wall-clock span of the range. Buckets are only created when an\n// operation runs, so idle stretches with no tracked calls would otherwise dilute the\n// wall-clock denominator and make \"per hour\" rates incomparable across range presets\n// (e.g. \"Today\" diluted by idle daytime vs. \"Last hour\" right after a burst).\nfunction activeRangeHours(buckets: ModuleResourceUsageTimeBucket[], bucketIntervalMs: number | undefined): number {\n const fallbackIntervalMs = bucketIntervalMs && Number.isFinite(bucketIntervalMs) && bucketIntervalMs > 0\n ? bucketIntervalMs\n : HOUR_MS\n if (!buckets.length) return fallbackIntervalMs / HOUR_MS\n const totalMs = buckets.reduce((sum, bucket) => {\n const interval = Number.isFinite(bucket.bucketIntervalMs) && bucket.bucketIntervalMs > 0\n ? bucket.bucketIntervalMs\n : fallbackIntervalMs\n return sum + interval\n }, 0)\n return Math.max(fallbackIntervalMs / HOUR_MS, totalMs / HOUR_MS)\n}\n\nfunction formatActiveDuration(hours: number): string {\n if (!Number.isFinite(hours) || hours <= 0) return '0m'\n if (hours < 1) return `${Math.max(Math.round(hours * 60), 1)}m`\n if (hours < 10) return `${hours.toFixed(1)}h`\n return `${Math.round(hours)}h`\n}\n\nfunction usageRangeLabel(preset: UsageRangePreset, translate: (key: string, fallback?: string) => string): string {\n if (preset === 'today') return translate('configs.moduleTelemetry.range.today', 'Today')\n if (preset === 'last_1h') return translate('configs.moduleTelemetry.range.last1h', 'Last hour')\n if (preset === 'last_6h') return translate('configs.moduleTelemetry.range.last6h', 'Last 6 hours')\n if (preset === 'last_24h') return translate('configs.moduleTelemetry.range.last24h', 'Last 24 hours')\n return translate('configs.moduleTelemetry.range.all', 'All available')\n}\n\nfunction isUsageRangePreset(value: string | null): value is UsageRangePreset {\n return !!value && USAGE_RANGE_PRESETS.includes(value as UsageRangePreset)\n}\n\nfunction readUsageRangePreset(searchParams: { get(name: string): string | null } | null): UsageRangePreset {\n const value = searchParams?.get('range') ?? null\n return isUsageRangePreset(value) ? value : 'today'\n}\n\nfunction usageStageLabel(stage: ModuleResourceUsageTimeBucket['stage'], translate: (key: string, fallback?: string) => string): string {\n if (stage === 'startup') return translate('configs.moduleTelemetry.stage.startup', 'Startup')\n return translate('configs.moduleTelemetry.stage.running', 'Running')\n}\n\nfunction sortStageRows(a: RangeModuleSummary, b: RangeModuleSummary): number {\n if (a.stage !== b.stage) return a.stage === 'startup' ? -1 : 1\n return b.totalCpuMs - a.totalCpuMs\n}\n\nfunction groupRangeModules(modules: RangeModuleSummary[]): RangeModuleGroup[] {\n const groups = new Map<string, RangeModuleSummary[]>()\n for (const module of modules) {\n const group = groups.get(module.moduleId) ?? []\n group.push(module)\n groups.set(module.moduleId, group)\n }\n\n return Array.from(groups.entries()).map(([moduleId, stages]) => {\n const candidateReasons = Array.from(new Set(stages.flatMap((stage) => stage.candidateReasons)))\n return {\n moduleId,\n stages: [...stages].sort(sortStageRows),\n candidateReasons,\n calls: stages.reduce((sum, stage) => sum + stage.calls, 0),\n totalCpuMs: stages.reduce((sum, stage) => sum + stage.totalCpuMs, 0),\n positiveRssDeltaBytes: stages.reduce((sum, stage) => sum + stage.positiveRssDeltaBytes, 0),\n }\n }).sort((a, b) =>\n b.candidateReasons.length - a.candidateReasons.length\n || b.totalCpuMs - a.totalCpuMs\n || b.positiveRssDeltaBytes - a.positiveRssDeltaBytes\n || b.calls - a.calls\n || a.moduleId.localeCompare(b.moduleId)\n )\n}\n\n// Thresholds are sized as \"per 5-minute bucket\" heavy-usage limits (see\n// getModuleResourceUsageThresholds()). A range can span anywhere from one bucket (\"Last hour\")\n// to hundreds (\"Today\"/\"All available\"), so comparing a raw range-wide sum against a per-bucket\n// threshold makes longer ranges strictly more likely to flag a signal regardless of actual\n// per-window intensity. Normalize CPU/heap/RSS/errors to \"average per bucket this module was\n// active in\" before comparing, so the same sustained workload reads the same way at any range.\n// p95DurationMs is already a max across buckets (not a sum), so it needs no normalization.\nfunction buildCandidateReasonsForModule(\n module: Pick<ModuleResourceUsageTimeBucketModule, 'p95DurationMs' | 'totalCpuMs' | 'positiveHeapDeltaBytes' | 'positiveRssDeltaBytes' | 'errors'>,\n thresholds: ModuleResourceUsageReport['thresholds'],\n bucketCount: number,\n): string[] {\n const divisor = Math.max(1, bucketCount)\n const reasons: string[] = []\n if (module.p95DurationMs >= thresholds.p95DurationMs) reasons.push('p95_duration')\n if (module.totalCpuMs / divisor >= thresholds.cpuMs) reasons.push('cpu')\n if (module.positiveHeapDeltaBytes / divisor >= thresholds.positiveHeapDeltaBytes) reasons.push('heap_allocations')\n if (module.positiveRssDeltaBytes / divisor >= thresholds.positiveRssDeltaBytes) reasons.push('rss_growth')\n if (module.errors / divisor >= thresholds.errors) reasons.push('errors')\n return reasons\n}\n\nfunction mergeRangeOperations(modules: ModuleResourceUsageTimeBucketModule[]) {\n const operations = new Map<string, ModuleResourceUsageTimeBucketModule['topOperations'][number]>()\n for (const module of modules) {\n for (const operation of module.topOperations) {\n const key = `${operation.surface}\\u0000${operation.operation}\\u0000${operation.resourceId ?? ''}`\n const existing = operations.get(key)\n if (!existing) {\n operations.set(key, { ...operation })\n continue\n }\n existing.calls += operation.calls\n existing.errors += operation.errors\n existing.totalDurationMs += operation.totalDurationMs\n existing.maxDurationMs = Math.max(existing.maxDurationMs, operation.maxDurationMs)\n existing.p95DurationMs = Math.max(existing.p95DurationMs, operation.p95DurationMs)\n existing.totalCpuUserMs += operation.totalCpuUserMs\n existing.totalCpuSystemMs += operation.totalCpuSystemMs\n existing.maxCpuMs = Math.max(existing.maxCpuMs, operation.maxCpuMs)\n existing.totalHeapDeltaBytes += operation.totalHeapDeltaBytes\n existing.positiveHeapDeltaBytes += operation.positiveHeapDeltaBytes\n existing.maxHeapDeltaBytes = Math.max(existing.maxHeapDeltaBytes, operation.maxHeapDeltaBytes)\n existing.totalRssDeltaBytes += operation.totalRssDeltaBytes\n existing.positiveRssDeltaBytes += operation.positiveRssDeltaBytes\n existing.maxRssDeltaBytes = Math.max(existing.maxRssDeltaBytes, operation.maxRssDeltaBytes)\n existing.firstSeenAt = existing.firstSeenAt < operation.firstSeenAt ? existing.firstSeenAt : operation.firstSeenAt\n existing.lastSeenAt = existing.lastSeenAt > operation.lastSeenAt ? existing.lastSeenAt : operation.lastSeenAt\n }\n }\n return Array.from(operations.values())\n .sort((a, b) =>\n (b.totalCpuUserMs + b.totalCpuSystemMs) - (a.totalCpuUserMs + a.totalCpuSystemMs)\n || b.totalDurationMs - a.totalDurationMs\n || b.calls - a.calls\n )\n .slice(0, 5)\n}\n\nfunction mergeRangeSurfaces(modules: ModuleResourceUsageTimeBucketModule[]): ModuleResourceUsageTimeBucketModule['surfaces'] {\n const surfaces = new Map<string, ModuleResourceUsageTimeBucketModule['surfaces'][number]>()\n for (const module of modules) {\n for (const surface of module.surfaces) {\n const existing = surfaces.get(surface.surface)\n if (!existing) {\n surfaces.set(surface.surface, { ...surface })\n continue\n }\n existing.calls += surface.calls\n existing.errors += surface.errors\n existing.totalDurationMs += surface.totalDurationMs\n existing.p95DurationMs = Math.max(existing.p95DurationMs, surface.p95DurationMs)\n existing.totalCpuMs += surface.totalCpuMs\n existing.positiveHeapDeltaBytes += surface.positiveHeapDeltaBytes\n existing.positiveRssDeltaBytes += surface.positiveRssDeltaBytes\n }\n }\n return Array.from(surfaces.values()).sort((a, b) => b.totalCpuMs - a.totalCpuMs || b.totalDurationMs - a.totalDurationMs)\n}\n\nfunction aggregateRangeModules(\n buckets: ModuleResourceUsageTimeBucket[],\n thresholds: ModuleResourceUsageReport['thresholds'],\n): RangeModuleSummary[] {\n const grouped = new Map<string, {\n moduleId: string\n stage: ModuleResourceUsageTimeBucket['stage']\n modules: ModuleResourceUsageTimeBucketModule[]\n // Distinct bucketStart values this module+stage appeared in. Tracked explicitly (rather than\n // relying on modules.length) so the \"per-bucket average\" candidate-signal divisor stays\n // correct even if a future change ever pushes more/fewer than one module entry per bucket.\n bucketKeys: Set<string>\n }>()\n for (const bucket of buckets) {\n for (const module of bucket.modules) {\n const key = `${module.moduleId}\\u0000${bucket.stage}`\n const group = grouped.get(key) ?? { moduleId: module.moduleId, stage: bucket.stage, modules: [], bucketKeys: new Set<string>() }\n group.modules.push(module)\n group.bucketKeys.add(bucket.bucketStart)\n grouped.set(key, group)\n }\n }\n\n return Array.from(grouped.values()).map(({ moduleId, stage, modules, bucketKeys }) => {\n const summaryBase = {\n moduleId,\n stage,\n calls: modules.reduce((sum, module) => sum + module.calls, 0),\n errors: modules.reduce((sum, module) => sum + module.errors, 0),\n totalDurationMs: modules.reduce((sum, module) => sum + module.totalDurationMs, 0),\n p95DurationMs: Math.max(...modules.map((module) => module.p95DurationMs), 0),\n totalCpuMs: modules.reduce((sum, module) => sum + module.totalCpuMs, 0),\n positiveHeapDeltaBytes: modules.reduce((sum, module) => sum + module.positiveHeapDeltaBytes, 0),\n positiveRssDeltaBytes: modules.reduce((sum, module) => sum + module.positiveRssDeltaBytes, 0),\n surfaces: mergeRangeSurfaces(modules),\n topOperations: mergeRangeOperations(modules),\n }\n return {\n ...summaryBase,\n candidateReasons: buildCandidateReasonsForModule(summaryBase, thresholds, bucketKeys.size),\n }\n }).sort((a, b) =>\n b.candidateReasons.length - a.candidateReasons.length\n || b.totalCpuMs - a.totalCpuMs\n || b.positiveRssDeltaBytes - a.positiveRssDeltaBytes\n || b.calls - a.calls\n )\n}\n\nfunction RangeOverview({\n buckets,\n modules,\n range,\n rangePreset,\n onRangePresetChange,\n bucketIntervalMs,\n translate,\n}: {\n buckets: ModuleResourceUsageTimeBucket[]\n modules: RangeModuleSummary[]\n range: UsageRange\n rangePreset: UsageRangePreset\n onRangePresetChange: (value: UsageRangePreset) => void\n bucketIntervalMs?: number\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string\n}) {\n const selectedHours = activeRangeHours(buckets, bucketIntervalMs)\n const activeDurationLabel = formatActiveDuration(selectedHours)\n const bucketsLabel = bucketCountLabel(buckets, bucketIntervalMs, translate)\n const totalHeapGrowthBytes = modules.reduce((sum, module) => sum + module.positiveHeapDeltaBytes, 0)\n const totalRssGrowthBytes = modules.reduce((sum, module) => sum + module.positiveRssDeltaBytes, 0)\n const totals = {\n modules: new Set(modules.map((module) => module.moduleId)).size,\n calls: modules.reduce((sum, module) => sum + module.calls, 0),\n totalCpuMs: modules.reduce((sum, module) => sum + module.totalCpuMs, 0),\n avgHeapGrowthPerHour: totalHeapGrowthBytes / selectedHours,\n avgRssGrowthPerHour: totalRssGrowthBytes / selectedHours,\n }\n const growthTooltip = translate(\n 'configs.moduleTelemetry.overview.growthTooltip',\n 'Average positive growth per hour of measured activity in the selected range: total growth divided by the hours actually covered by tracked buckets, not the full wall-clock span. Idle stretches with no tracked calls do not dilute this rate, so it stays comparable across range presets. This is allocation pressure over time, not the current live process memory.',\n )\n const heapSubValue = translate(\n 'configs.moduleTelemetry.overview.growthSubValue',\n '{{total}} total over {{duration}}',\n { total: formatBytes(totalHeapGrowthBytes), duration: activeDurationLabel },\n )\n const rssSubValue = translate(\n 'configs.moduleTelemetry.overview.growthSubValue',\n '{{total}} total over {{duration}}',\n { total: formatBytes(totalRssGrowthBytes), duration: activeDurationLabel },\n )\n\n return (\n <div className=\"space-y-4 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold\">{translate('configs.moduleTelemetry.overview.title', 'Usage overview')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {translate(\n 'configs.moduleTelemetry.overview.description',\n 'From {{from}} to now \u00B7 {{buckets}}',\n {\n from: formatRangeBoundary(range.startMs),\n buckets: bucketsLabel,\n },\n )}\n </p>\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <SegmentedControl\n value={rangePreset}\n onValueChange={(value) => onRangePresetChange(value as UsageRangePreset)}\n aria-label={translate('configs.moduleTelemetry.range.label', 'Time range')}\n size=\"sm\"\n className=\"max-w-full flex-wrap overflow-x-auto rounded-lg\"\n >\n {USAGE_RANGE_PRESETS.map((option) => (\n <SegmentedControlItem key={option} value={option}>\n {usageRangeLabel(option, translate)}\n </SegmentedControlItem>\n ))}\n </SegmentedControl>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-5\">\n <MetricCard\n label={translate('configs.moduleTelemetry.overview.modules', 'Modules')}\n tooltip={translate('configs.moduleTelemetry.overview.modulesTooltip', 'Unique modules with tracked activity in the selected range. Startup and running are shown as separate rows inside each module group.')}\n value={formatCount(totals.modules)}\n />\n <MetricCard\n label={translate('configs.moduleTelemetry.overview.calls', 'Calls')}\n tooltip={translate('configs.moduleTelemetry.overview.callsTooltip', 'Total tracked operation calls in the selected range.')}\n value={formatCount(totals.calls)}\n />\n <MetricCard\n label={translate('configs.moduleTelemetry.overview.cpu', 'CPU')}\n tooltip={translate('configs.moduleTelemetry.overview.cpuTooltip', 'Total CPU time attributed to tracked module operations in the selected range.')}\n value={formatMs(totals.totalCpuMs)}\n />\n <MetricCard\n label={translate('configs.moduleTelemetry.overview.heapPerHour', 'Heap / hour')}\n tooltip={growthTooltip}\n value={formatBytes(totals.avgHeapGrowthPerHour)}\n subValue={heapSubValue}\n />\n <MetricCard\n label={translate('configs.moduleTelemetry.overview.rssPerHour', 'RSS / hour')}\n tooltip={growthTooltip}\n value={formatBytes(totals.avgRssGrowthPerHour)}\n subValue={rssSubValue}\n />\n </div>\n </div>\n )\n}\n\nfunction CandidateBadges({\n module,\n translate,\n}: {\n module: ModuleResourceUsageTimeBucketModule\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string\n}) {\n if (!module.candidateReasons.length) {\n return (\n <StatusBadge variant=\"neutral\" dot>\n {translate('configs.moduleTelemetry.table.normal', 'Normal')}\n </StatusBadge>\n )\n }\n\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {module.candidateReasons.map((reason) => (\n <StatusBadge key={reason} variant=\"warning\" dot>\n {translate(REASON_LABEL_KEYS[reason] ?? `configs.moduleTelemetry.reason.${reason}`, REASON_FALLBACKS[reason] ?? reason)}\n </StatusBadge>\n ))}\n </div>\n )\n}\n\nfunction StageBadge({\n stage,\n translate,\n}: {\n stage: ModuleResourceUsageTimeBucket['stage']\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string\n}) {\n return (\n <StatusBadge variant={stage === 'startup' ? 'info' : 'neutral'} dot>\n {usageStageLabel(stage, translate)}\n </StatusBadge>\n )\n}\n\nfunction copyRowStats(module: RangeModuleSummary, translate: (key: string, fallback?: string) => string): void {\n const json = JSON.stringify(module, null, 2)\n navigator.clipboard\n .writeText(json)\n .then(() => {\n flash(translate('configs.moduleTelemetry.table.copySuccess', 'Row stats copied to clipboard.'), 'success')\n })\n .catch((err) => {\n console.warn('[ModuleTelemetryPanel] clipboard write failed', err)\n flash(translate('configs.moduleTelemetry.table.copyError', 'Failed to copy row stats.'), 'error')\n })\n}\n\nfunction TopOperationsBreakdown({\n operations,\n translate,\n}: {\n operations: ModuleResourceUsageEntry[]\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string\n}) {\n return (\n <div className=\"w-[26rem] max-w-[80vw] space-y-2\">\n <div className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {translate('configs.moduleTelemetry.table.topOperationsBreakdown', 'Top operations in this range')}\n </div>\n <table className=\"w-full text-xs\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"pb-1 pr-2 text-left font-medium\">{translate('configs.moduleTelemetry.table.topOperation', 'Top operation')}</th>\n <th className=\"pb-1 pr-2 text-right font-medium\">{translate('configs.moduleTelemetry.table.calls', 'Calls')}</th>\n <th className=\"pb-1 pr-2 text-right font-medium\">{translate('configs.moduleTelemetry.table.worstP95', 'Worst p95')}</th>\n <th className=\"pb-1 pr-2 text-right font-medium\">{translate('configs.moduleTelemetry.table.cpu', 'CPU')}</th>\n <th className=\"pb-1 pr-2 text-right font-medium\">{translate('configs.moduleTelemetry.table.heap', 'Heap growth')}</th>\n <th className=\"pb-1 text-right font-medium\">{translate('configs.moduleTelemetry.table.rss', 'RSS growth')}</th>\n </tr>\n </thead>\n <tbody>\n {operations.map((operation, index) => (\n <tr key={`${operation.surface}\u0000${operation.operation}\u0000${index}`} className=\"border-t border-border/50\">\n <td className=\"py-1 pr-2 align-top\">\n <div className=\"font-medium text-foreground\">{operation.operation}</div>\n <div className=\"text-muted-foreground\">\n {operation.surface}\n {operation.errors > 0\n ? ` \u00B7 ${translate('configs.moduleTelemetry.table.errorCount', '{{count}} errors', { count: operation.errors })}`\n : ''}\n </div>\n {operation.concurrentCalls > 0 ? (\n <div className=\"text-muted-foreground\">\n {translate(\n 'configs.moduleTelemetry.table.concurrentOverlap',\n '{{percent}}% overlapped other calls \u2014 CPU/heap/RSS less certain',\n { percent: Math.round((operation.concurrentCalls / Math.max(operation.calls, 1)) * 100) },\n )}\n </div>\n ) : null}\n </td>\n <td className=\"py-1 pr-2 text-right align-top\">{formatCount(operation.calls)}</td>\n <td className=\"py-1 pr-2 text-right align-top\">{formatMs(operation.p95DurationMs)}</td>\n <td className=\"py-1 pr-2 text-right align-top\">{formatMs(operation.totalCpuUserMs + operation.totalCpuSystemMs)}</td>\n <td className=\"py-1 pr-2 text-right align-top\">{formatBytes(operation.positiveHeapDeltaBytes)}</td>\n <td className=\"py-1 text-right align-top\">{formatBytes(operation.positiveRssDeltaBytes)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction RangeModuleTable({\n modules,\n translate,\n}: {\n modules: RangeModuleSummary[]\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string\n}) {\n if (!modules.length) {\n return (\n <p className=\"text-sm text-muted-foreground\">\n {translate('configs.moduleTelemetry.modules.empty', 'No module activity has been recorded for this range yet.')}\n </p>\n )\n }\n\n const groups = groupRangeModules(modules)\n\n return (\n <TooltipProvider delayDuration={200}>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[980px] text-sm\">\n <thead>\n <tr className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n <th className=\"px-3 py-2 text-left\">{translate('configs.moduleTelemetry.table.module', 'Module')}</th>\n <th className=\"px-3 py-2 text-left\">{translate('configs.moduleTelemetry.table.stage', 'Stage')}</th>\n <th className=\"px-3 py-2 text-left\">\n <MetricTitle\n label={translate('configs.moduleTelemetry.table.signals', 'Signals')}\n tooltip={translate(\n 'configs.moduleTelemetry.table.signalsTooltip',\n 'Flags a module whose CPU, heap growth, RSS growth, or errors average at or above a heavy-usage threshold per 5-minute bucket it was active in during this range. This is a per-bucket average, not a raw range-wide total, so the same sustained workload looks the same regardless of the selected time range.',\n )}\n />\n </th>\n <th className=\"px-3 py-2 text-right\">{translate('configs.moduleTelemetry.table.calls', 'Calls')}</th>\n <th className=\"px-3 py-2 text-right\">{translate('configs.moduleTelemetry.table.worstP95', 'Worst p95')}</th>\n <th className=\"px-3 py-2 text-right\">{translate('configs.moduleTelemetry.table.cpu', 'CPU')}</th>\n <th className=\"px-3 py-2 text-right\">{translate('configs.moduleTelemetry.table.heap', 'Heap growth')}</th>\n <th className=\"px-3 py-2 text-right\">{translate('configs.moduleTelemetry.table.rss', 'RSS growth')}</th>\n <th className=\"px-3 py-2 text-left\">{translate('configs.moduleTelemetry.table.topOperation', 'Top operation')}</th>\n <th className=\"px-3 py-2 text-right\">{translate('configs.moduleTelemetry.table.actions', 'Actions')}</th>\n </tr>\n </thead>\n <tbody>\n {groups.map((group) => (\n <React.Fragment key={group.moduleId}>\n {group.stages.map((module, stageIndex) => {\n const topOperation = module.topOperations[0]\n const topSurface = module.surfaces[0]\n return (\n <tr\n key={`${module.moduleId}:${module.stage}`}\n className={stageIndex === 0 ? 'border-t' : 'border-t border-border/50'}\n >\n {stageIndex === 0 ? (\n <td className=\"w-56 px-3 py-3 align-top\" rowSpan={group.stages.length}>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-medium\">{group.moduleId}</span>\n <span className=\"text-xs text-muted-foreground\">\n {translate(\n 'configs.moduleTelemetry.table.stageCount',\n '{{count}} stage rows',\n { count: group.stages.length },\n )}\n </span>\n </div>\n </td>\n ) : null}\n <td className=\"px-3 py-3 align-top\">\n <div className=\"flex flex-col gap-1\">\n <StageBadge stage={module.stage} translate={translate} />\n <span className=\"text-xs text-muted-foreground\">\n {topSurface\n ? translate(\n 'configs.moduleTelemetry.table.surfaceSummary',\n '{{surface}} \u00B7 {{calls}} calls',\n { surface: topSurface.surface, calls: topSurface.calls },\n )\n : translate('configs.moduleTelemetry.table.noSurface', 'No surface data')}\n </span>\n </div>\n </td>\n <td className=\"px-3 py-3 align-top\">\n <CandidateBadges module={module} translate={translate} />\n </td>\n <td className=\"px-3 py-3 align-top text-right\">\n <div className=\"flex flex-col gap-1\">\n <span>{formatCount(module.calls)}</span>\n {module.errors > 0 ? (\n <span className=\"text-xs text-destructive\">\n {translate('configs.moduleTelemetry.table.errorCount', '{{count}} errors', { count: module.errors })}\n </span>\n ) : null}\n </div>\n </td>\n <td className=\"px-3 py-3 align-top text-right\">{formatMs(module.p95DurationMs)}</td>\n <td className=\"px-3 py-3 align-top text-right\">{formatMs(module.totalCpuMs)}</td>\n <td className=\"px-3 py-3 align-top text-right\">{formatBytes(module.positiveHeapDeltaBytes)}</td>\n <td className=\"px-3 py-3 align-top text-right\">{formatBytes(module.positiveRssDeltaBytes)}</td>\n <td className=\"px-3 py-3 align-top\">\n {topOperation ? (\n <Tooltip delayDuration={200}>\n <TooltipTrigger asChild>\n <div className=\"flex cursor-default flex-col gap-1\">\n <span className=\"font-medium underline decoration-dotted decoration-muted-foreground underline-offset-2\">\n {topOperation.operation}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {translate(\n 'configs.moduleTelemetry.table.topOperationMeta',\n '{{surface}} \u00B7 {{cpu}} CPU \u00B7 {{calls}} calls',\n {\n surface: topOperation.surface,\n cpu: formatMs(topOperation.totalCpuUserMs + topOperation.totalCpuSystemMs),\n calls: topOperation.calls,\n },\n )}\n </span>\n </div>\n </TooltipTrigger>\n <TooltipContent side=\"left\" align=\"start\" variant=\"light\" className=\"max-w-none p-3\">\n <TopOperationsBreakdown operations={module.topOperations} translate={translate} />\n </TooltipContent>\n </Tooltip>\n ) : (\n <span className=\"text-muted-foreground\">{translate('configs.moduleTelemetry.table.none', 'None')}</span>\n )}\n </td>\n <td className=\"px-3 py-3 align-top text-right\">\n <SimpleTooltip\n content={translate('configs.moduleTelemetry.table.copyStats', 'Copy row stats for debugging')}\n side=\"top\"\n align=\"center\"\n variant=\"light\"\n >\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={translate('configs.moduleTelemetry.table.copyStats', 'Copy row stats for debugging')}\n onClick={() => copyRowStats(module, translate)}\n >\n <Copy className=\"h-4 w-4\" aria-hidden=\"true\" />\n </IconButton>\n </SimpleTooltip>\n </td>\n </tr>\n )\n })}\n </React.Fragment>\n ))}\n </tbody>\n </table>\n </div>\n </TooltipProvider>\n )\n}\n\nfunction RangeModuleSection({\n modules,\n range,\n bucketIntervalMs,\n bucketsLabel,\n translate,\n}: {\n modules: RangeModuleSummary[]\n range: UsageRange\n bucketIntervalMs?: number\n bucketsLabel: string\n translate: (key: string, fallback?: string, values?: Record<string, string | number>) => string\n}) {\n return (\n <div className=\"space-y-4 rounded-lg border bg-card p-4\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold\">{translate('configs.moduleTelemetry.modules.title', 'Modules')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {translate(\n 'configs.moduleTelemetry.modules.description',\n 'Aggregated module stats from {{from}} to now across {{buckets}}. Startup and running rows are separated.',\n {\n from: formatRangeBoundary(range.startMs),\n buckets: bucketsLabel,\n },\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {translate(\n 'configs.moduleTelemetry.modules.interval',\n 'Startup is the first {{interval}} bucket after telemetry starts. Later buckets are running.',\n { interval: formatBucketInterval(bucketIntervalMs) },\n )}\n </p>\n </div>\n <RangeModuleTable modules={modules} translate={translate} />\n </div>\n )\n}\n\nexport function ModuleTelemetryPanel() {\n const t = useT()\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const [state, setState] = React.useState<FetchState>({ loading: true, error: null, report: null })\n const [clearingTelemetry, setClearingTelemetry] = React.useState(false)\n const rangeParam = searchParams.get('range')\n const [usageRangePreset, setUsageRangePreset] = React.useState<UsageRangePreset>(() => readUsageRangePreset(searchParams))\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'configs-module-telemetry',\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const clearMutationContext = React.useMemo(\n () => ({\n formId: 'configs-module-telemetry',\n resourceKind: 'configs.moduleTelemetry',\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n const loadReport = React.useCallback(async () => {\n setState((current) => ({ ...current, loading: true, error: null }))\n try {\n const report = await readApiResultOrThrow<ModuleTelemetryApiResponse>(API_PATH, undefined, {\n errorMessage: t('configs.moduleTelemetry.loadError', 'Failed to load module telemetry.'),\n })\n setState({ loading: false, error: null, report })\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : t('configs.moduleTelemetry.loadError', 'Failed to load module telemetry.')\n setState({ loading: false, error: message, report: null })\n }\n }, [t])\n\n React.useEffect(() => {\n loadReport().catch(() => {})\n }, [loadReport])\n\n React.useEffect(() => {\n const nextRange = readUsageRangePreset(searchParams)\n setUsageRangePreset((current) => current === nextRange ? current : nextRange)\n }, [rangeParam, searchParams])\n\n const handleUsageRangeChange = React.useCallback((value: UsageRangePreset) => {\n setUsageRangePreset(value)\n const params = new URLSearchParams(searchParams.toString())\n params.set('range', value)\n const query = params.toString()\n router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false })\n }, [pathname, router, searchParams])\n\n const handleRefresh = React.useCallback(() => {\n loadReport().catch(() => {})\n }, [loadReport])\n\n const handleClearTelemetry = React.useCallback(async () => {\n if (clearingTelemetry) return\n const confirmed = await confirm({\n title: t('configs.moduleTelemetry.clear.confirmTitle', 'Clear all telemetry data?'),\n text: t(\n 'configs.moduleTelemetry.clear.confirmText',\n 'This removes current module telemetry and local process telemetry files for this development instance.',\n ),\n confirmText: t('configs.moduleTelemetry.clear.confirmButton', 'Clear telemetry'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n setClearingTelemetry(true)\n try {\n await runMutation({\n // optimistic-lock-exempt: clears in-process/dev-only telemetry buckets and\n // local process telemetry files \u2014 there is no versioned entity (no updated_at)\n // to lock against, so no expected-version header applies.\n operation: () =>\n readApiResultOrThrow<ModuleTelemetryClearResponse>(\n API_PATH,\n { method: 'DELETE' },\n {\n errorMessage: t('configs.moduleTelemetry.clear.error', 'Failed to clear module telemetry.'),\n allowNullResult: true,\n },\n ),\n context: clearMutationContext,\n mutationPayload: {},\n })\n await loadReport()\n flash(t('configs.moduleTelemetry.clear.success', 'Module telemetry data cleared.'), 'success')\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : t('configs.moduleTelemetry.clear.error', 'Failed to clear module telemetry.')\n flash(message, 'error')\n } finally {\n setClearingTelemetry(false)\n }\n }, [clearMutationContext, clearingTelemetry, confirm, loadReport, runMutation, t])\n\n if (state.loading) {\n return (\n <section className=\"space-y-3 rounded-lg border bg-background p-6\">\n <header className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('configs.moduleTelemetry.title', 'Module telemetry')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('configs.moduleTelemetry.description', 'Preview module-level resource attribution collected in this process.')}\n </p>\n </header>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('configs.moduleTelemetry.loading', 'Loading module telemetry\u2026')}\n </div>\n </section>\n )\n }\n\n if (state.error) {\n return (\n <section className=\"space-y-3 rounded-lg border bg-background p-6\">\n <header className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('configs.moduleTelemetry.title', 'Module telemetry')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('configs.moduleTelemetry.description', 'Preview module-level resource attribution collected in this process.')}\n </p>\n </header>\n <ErrorMessage label={state.error} />\n <Button type=\"button\" variant=\"outline\" onClick={() => loadReport().catch(() => {})}>\n {t('configs.moduleTelemetry.retry', 'Retry')}\n </Button>\n </section>\n )\n }\n\n const report = state.report\n if (!report) return null\n const usageRange = resolveUsageRange(usageRangePreset, report.buckets ?? [], report.startedAt)\n const rangeBuckets = bucketsInRange(report.buckets ?? [], usageRange)\n const rangeModules = aggregateRangeModules(rangeBuckets, report.thresholds)\n const bucketsLabel = bucketCountLabel(rangeBuckets, report.bucketIntervalMs, t)\n const telemetryStartedAtMs = firstAvailableTelemetryMs(report.buckets ?? [], Date.parse(report.startedAt), Date.now())\n\n return (\n <section className=\"space-y-6 rounded-lg border bg-background p-6\">\n <header className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('configs.moduleTelemetry.title', 'Module telemetry')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('configs.moduleTelemetry.description', 'Preview module-level resource attribution collected in this process.')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'configs.moduleTelemetry.generatedAt',\n 'Report generated {{timestamp}}',\n { timestamp: new Date(report.generatedAt).toLocaleString() },\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'configs.moduleTelemetry.startedAt',\n 'Collecting since {{timestamp}}',\n { timestamp: new Date(telemetryStartedAtMs).toLocaleString() },\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {report.canClearTelemetry ? (\n <Button\n type=\"button\"\n variant=\"destructive-outline\"\n onClick={() => { void handleClearTelemetry() }}\n disabled={clearingTelemetry}\n >\n <Trash2 className=\"h-4 w-4\" aria-hidden=\"true\" />\n {clearingTelemetry\n ? t('configs.moduleTelemetry.clear.clearing', 'Clearing...')\n : t('configs.moduleTelemetry.clear.button', 'Clear all telemetry data')}\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"outline\" onClick={handleRefresh}>\n <RefreshCw className=\"h-4 w-4\" aria-hidden=\"true\" />\n {t('configs.moduleTelemetry.refresh', 'Refresh')}\n </Button>\n </div>\n </header>\n\n <RangeOverview\n buckets={rangeBuckets}\n modules={rangeModules}\n range={usageRange}\n rangePreset={usageRangePreset}\n onRangePresetChange={handleUsageRangeChange}\n bucketIntervalMs={report.bucketIntervalMs}\n translate={t}\n />\n <RangeModuleSection\n modules={rangeModules}\n range={usageRange}\n bucketIntervalMs={report.bucketIntervalMs}\n bucketsLabel={bucketsLabel}\n translate={t}\n />\n\n {ConfirmDialogElement}\n </section>\n )\n}\n\nexport default ModuleTelemetryPanel\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["report"]
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/messages/commands/messages.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Message, MessageObject, MessageRecipient, type MessageActionData } from '../data/entities'\nimport { emitMessagesEvent } from '../events'\nimport {\n composeMessageSchema,\n forwardMessageSchema,\n replyMessageSchema,\n updateDraftSchema,\n} from '../data/validators'\nimport { linkAttachmentsToMessage, linkLibraryAttachmentsToMessage, copyAttachmentsForForwardMessages } from '../lib/attachments'\nimport { MESSAGE_ATTACHMENT_ENTITY_ID, MESSAGE_ENTITY_ID } from '../lib/constants'\nimport { getMessageTypeOrDefault } from '../lib/message-types-registry'\nimport { validateMessageObjectsForType } from '../lib/object-validation'\nimport { buildForwardBodyFromLegacyInput, buildForwardPreviewFromThreadSlice, buildForwardThreadSlice } from '../lib/forwarding'\nimport {\n assertOrganizationAccess,\n loadMessageAggregateSnapshot,\n restoreMessageAggregateSnapshot,\n type MessageAggregateSnapshot,\n type MessageScopeInput,\n} from './shared'\n\ntype MessageSentEventPayload = {\n messageId: string\n senderUserId: string\n recipientUserIds: string[]\n sendViaEmail: boolean\n externalEmail?: string | null\n forwardedFrom?: string\n replyTo?: string\n tenantId: string\n organizationId?: string | null\n}\n\ntype ContainerWithResolve = {\n resolve: (name: string) => unknown\n}\n\nasync function emitMessageSentEvent(_container: ContainerWithResolve, payload: MessageSentEventPayload) {\n await emitMessagesEvent('messages.message.sent', payload, { persistent: true })\n}\n\nasync function emitMessageDeletedEvent(_container: ContainerWithResolve, payload: {\n messageId: string\n actorUserId: string\n target: 'sender' | 'recipient'\n tenantId: string\n organizationId: string | null\n}) {\n await emitMessagesEvent(\n 'messages.message.deleted',\n { ...payload, recipientUserId: payload.actorUserId },\n { persistent: true },\n )\n}\n\nasync function emitMessageGloballyDeletedEvent(_container: ContainerWithResolve, payload: {\n messageId: string\n actorUserId: string\n recipientUserIds: string[]\n tenantId: string\n organizationId: string | null\n}) {\n const audience = Array.from(new Set([payload.actorUserId, ...payload.recipientUserIds]))\n await emitMessagesEvent(\n 'messages.message.deleted',\n {\n messageId: payload.messageId,\n actorUserId: payload.actorUserId,\n target: 'global',\n recipientUserIds: audience,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n },\n { persistent: true },\n )\n}\n\nasync function emitMessageIndexUpsert(\n container: ContainerWithResolve,\n payload: {\n messageId: string\n tenantId: string\n organizationId: string | null\n },\n) {\n let bus: { emitEvent: (name: string, body: unknown, options?: unknown) => Promise<void> } | null = null\n try {\n bus = container.resolve('eventBus') as { emitEvent: (name: string, body: unknown, options?: unknown) => Promise<void> }\n } catch {\n bus = null\n }\n\n if (!bus) return\n\n await bus.emitEvent(\n 'query_index.upsert_one',\n {\n entityType: MESSAGE_ENTITY_ID,\n recordId: payload.messageId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n crudAction: 'updated',\n coverageBaseDelta: 1,\n },\n {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n },\n ).catch(() => undefined)\n}\n\nconst scopeSchema = z.object({\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid().nullable(),\n userId: z.string().uuid(),\n})\n\nconst composeCommandSchema = composeMessageSchema.safeExtend({\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n // Optional dedup key (inbound email ingest sets it; other callers leave it\n // undefined). When set, a re-issued compose returns the first message instead\n // of creating a duplicate.\n idempotencyKey: z.string().min(1).max(255).optional(),\n})\n\nconst updateDraftCommandSchema = updateDraftSchema.safeExtend({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\nconst replyCommandSchema = replyMessageSchema.safeExtend({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\nconst forwardCommandSchema = forwardMessageSchema.safeExtend({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\nconst deleteForActorCommandSchema = z.object({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\ntype ComposeCommandInput = z.infer<typeof composeCommandSchema>\ntype UpdateDraftCommandInput = z.infer<typeof updateDraftCommandSchema>\ntype ReplyCommandInput = z.infer<typeof replyCommandSchema>\ntype ForwardCommandInput = z.infer<typeof forwardCommandSchema>\ntype DeleteForActorCommandInput = z.infer<typeof deleteForActorCommandSchema>\n\ntype MessageDeleteUndoState = {\n messageId: string\n messageDeletedAt: string | null\n recipientId: string | null\n recipientStatus: 'unread' | 'read' | 'archived' | 'deleted' | null\n recipientDeletedAt: string | null\n}\n\nfunction toIso(value: Date | null | undefined): string | null {\n return value ? value.toISOString() : null\n}\n\nfunction toDate(value: string | null | undefined): Date | null {\n if (!value) return null\n return new Date(value)\n}\n\nfunction buildReplySubject(subject: string): string {\n if (/^re:\\s*/i.test(subject)) return subject\n return `Re: ${subject}`\n}\n\nasync function requireMessageById(\n em: EntityManager,\n scope: MessageScopeInput,\n messageId: string,\n) {\n const message = await findOneWithDecryption(\n em,\n Message,\n {\n id: messageId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n },\n )\n if (!message) throw new Error('Message not found')\n assertOrganizationAccess(scope, message)\n return message\n}\n\nfunction isUniqueViolation(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const code = (err as { code?: string }).code\n if (code === '23505') return true\n const message = (err as { message?: string }).message\n return typeof message === 'string' && /duplicate key value|unique constraint/i.test(message)\n}\n\ntype ComposeMessageResult = {\n id: string\n threadId: string | null\n externalEmail: string | null\n isDraft: boolean\n recipientUserIds: string[]\n /**\n * True when this was an idempotent replay \u2014 an existing message was returned\n * and nothing was written. Signals `buildLog` to skip the audit/undo entry.\n */\n deduplicated?: boolean\n}\n\nasync function buildComposeResultFromExisting(\n em: EntityManager,\n message: Message,\n): Promise<ComposeMessageResult> {\n const recipients = await findWithDecryption(\n em,\n MessageRecipient,\n { messageId: message.id, deletedAt: null },\n undefined,\n { tenantId: message.tenantId, organizationId: message.organizationId ?? null },\n )\n return {\n id: message.id,\n threadId: message.threadId ?? null,\n externalEmail: message.externalEmail ?? null,\n isDraft: message.isDraft,\n recipientUserIds: recipients.map((recipient) => recipient.recipientUserId),\n deduplicated: true,\n }\n}\n\nconst composeMessageCommand: CommandHandler<unknown, { id: string; threadId: string | null; externalEmail: string | null; isDraft: boolean; recipientUserIds: string[]; deduplicated?: boolean }> = {\n id: 'messages.messages.compose',\n async execute(rawInput, ctx) {\n const input = composeCommandSchema.parse(rawInput)\n if (input.objects?.length) {\n const objectValidationError = validateMessageObjectsForType(input.type, input.objects)\n if (objectValidationError) throw new Error(objectValidationError)\n }\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = { tenantId: input.tenantId, organizationId: input.organizationId }\n\n // Idempotency fast-path: a retried inbound-email ingest re-issues compose\n // for the same source message (the first attempt committed the message but a\n // downstream transient failure rolled the ingest back). Return that message\n // so the retry cannot create a duplicate.\n if (input.idempotencyKey) {\n const existing = await findOneWithDecryption(\n em,\n Message,\n { tenantId: input.tenantId, idempotencyKey: input.idempotencyKey },\n undefined,\n scope,\n )\n if (existing) return buildComposeResultFromExisting(em, existing)\n }\n\n let messageId = ''\n let responseThreadId: string | null = null\n let responseExternalEmail: string | null = null\n\n const composeTx = em.transactional(async (trx) => {\n const threadId = input.parentMessageId\n ? (\n await findOneWithDecryption(\n trx,\n Message,\n {\n id: input.parentMessageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n )?.threadId ?? input.parentMessageId\n : undefined\n\n const isPublicVisibility = input.visibility === 'public'\n const sendViaEmail = isPublicVisibility ? true : input.sendViaEmail\n const message = trx.create(Message, {\n type: input.type,\n visibility: input.visibility ?? null,\n sourceEntityType: input.sourceEntityType,\n sourceEntityId: input.sourceEntityId,\n externalEmail: input.externalEmail,\n externalName: input.externalName,\n threadId: threadId ?? undefined,\n parentMessageId: input.parentMessageId,\n senderUserId: input.userId,\n subject: input.subject,\n body: input.body,\n bodyFormat: input.bodyFormat,\n priority: input.priority,\n status: input.isDraft ? 'draft' : 'sent',\n isDraft: input.isDraft ?? false,\n sentAt: input.isDraft ? null : new Date(),\n actionData: input.actionData as MessageActionData | undefined,\n sendViaEmail,\n idempotencyKey: input.idempotencyKey ?? null,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n await trx.persist(message).flush()\n if (!threadId && !input.isDraft && !message.threadId) {\n message.threadId = message.id\n await trx.flush()\n }\n\n for (const recipient of input.recipients) {\n trx.persist(trx.create(MessageRecipient, {\n messageId: message.id,\n recipientUserId: recipient.userId,\n recipientType: recipient.type,\n status: 'unread',\n }))\n }\n\n if (input.objects) {\n for (const obj of input.objects) {\n trx.persist(trx.create(MessageObject, {\n messageId: message.id,\n entityModule: obj.entityModule,\n entityType: obj.entityType,\n entityId: obj.entityId,\n actionRequired: obj.actionRequired,\n actionType: obj.actionType,\n actionLabel: obj.actionLabel,\n }))\n }\n }\n\n await trx.flush()\n\n if (input.attachmentIds?.length) {\n await linkAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentIds,\n input.organizationId,\n input.tenantId,\n )\n }\n\n if (input.attachmentRecordId) {\n await linkLibraryAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentRecordId,\n input.organizationId,\n input.tenantId,\n )\n }\n\n messageId = message.id\n responseThreadId = message.threadId ?? null\n responseExternalEmail = message.externalEmail ?? null\n })\n try {\n await composeTx\n } catch (err) {\n // Lost a concurrent race on the same idempotency key \u2014 return the message\n // the winning compose created. A propagated 23505 would otherwise be\n // classified permanent and dead-letter the inbound mail.\n if (input.idempotencyKey && isUniqueViolation(err)) {\n const existing = await findOneWithDecryption(\n em.fork(),\n Message,\n { tenantId: input.tenantId, idempotencyKey: input.idempotencyKey },\n undefined,\n scope,\n )\n if (existing) return buildComposeResultFromExisting(em.fork(), existing)\n }\n throw err\n }\n\n if (!input.isDraft) {\n await emitMessageSentEvent(ctx.container, {\n messageId,\n senderUserId: input.userId,\n recipientUserIds: input.recipients.map((recipient) => recipient.userId),\n sendViaEmail: input.visibility === 'public' ? true : input.sendViaEmail,\n externalEmail: responseExternalEmail,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n }\n\n await emitMessageIndexUpsert(ctx.container, {\n messageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n return {\n id: messageId,\n threadId: responseThreadId,\n externalEmail: responseExternalEmail,\n isDraft: input.isDraft,\n recipientUserIds: input.recipients.map((recipient) => recipient.userId),\n }\n },\n async captureAfter(_input, result, ctx) {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, result.id)\n },\n buildLog: async ({ input, result, snapshots }) => {\n // Idempotent replay: execute returned a pre-existing message without writing\n // anything. Skip the audit entry entirely \u2014 logging it as a fresh \"Compose\n // message\" would both misrepresent the dedup and expose an undo that\n // soft-deletes a legitimately-received inbound message.\n if (result.deduplicated) return { skipLog: true }\n const parsed = composeCommandSchema.parse(input)\n return {\n actionLabel: parsed.isDraft ? 'Create draft message' : 'Compose message',\n resourceKind: 'messages.message',\n resourceId: result.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const after = undo?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: after.message.id })\n if (!message) return\n message.deletedAt = new Date()\n await em.flush()\n if (!after.message.isDraft) {\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: after.message.id,\n actorUserId: after.message.senderUserId,\n recipientUserIds: after.recipients.map((recipient) => recipient.recipientUserId),\n tenantId: after.message.tenantId,\n organizationId: after.message.organizationId,\n })\n }\n },\n}\n\nconst updateDraftCommand: CommandHandler<unknown, { ok: true; id: string }> = {\n id: 'messages.messages.update_draft',\n async prepare(rawInput, ctx) {\n const input = updateDraftCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadMessageAggregateSnapshot(em, input.messageId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = updateDraftCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await requireMessageById(em, input, input.messageId)\n\n if (message.senderUserId !== input.userId) throw new Error('Access denied')\n if (!message.isDraft) throw new Error('Only draft messages can be edited')\n\n const isSending = input.isDraft === false\n const preloadedRecipients = isSending && !input.recipients\n ? await findWithDecryption(\n em,\n MessageRecipient,\n { messageId: message.id, deletedAt: null },\n undefined,\n {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n },\n )\n : null\n\n const nextMessageType = input.type ?? message.type\n if (input.objects) {\n const objectValidationError = validateMessageObjectsForType(nextMessageType, input.objects)\n if (objectValidationError) throw new Error(objectValidationError)\n } else if (input.type !== undefined) {\n const existingObjects = await em.find(MessageObject, { messageId: message.id })\n if (existingObjects.length > 0) {\n const objectValidationError = validateMessageObjectsForType(\n nextMessageType,\n existingObjects.map((item) => ({\n entityModule: item.entityModule,\n entityType: item.entityType,\n entityId: item.entityId,\n })),\n )\n if (objectValidationError) throw new Error(objectValidationError)\n }\n }\n\n // Validate the send transition BEFORE the atomic-flush block so a rejected\n // send never mutates or flushes anything (everything checked here is known\n // from the parsed input + the loaded message).\n if (isSending) {\n const finalVisibility = input.visibility ?? message.visibility\n const finalSubject = input.subject ?? message.subject\n const finalBody = input.body ?? message.body\n const finalRecipientCount = input.recipients\n ? input.recipients.length\n : (preloadedRecipients?.length ?? 0)\n if (finalVisibility !== 'public' && finalRecipientCount === 0) {\n throw new Error('at least one recipient is required')\n }\n if (!finalSubject?.trim()) throw new Error('subject is required')\n if (!finalBody?.trim()) throw new Error('body is required')\n }\n\n // Phase 1 mutates the managed `message` scalars; the per-phase flush issues\n // those changes before phase 2's interleaved reads run (e.g.\n // `linkAttachmentsToMessage` issues an `em.find`). Under MikroORM v7 a read\n // between a scalar mutation and the terminal flush on the same EntityManager\n // silently drops the pending changeset, so keeping the scalar edits in their\n // own phase guarantees subject/body/etc. are persisted (SPEC-018 Problem 1).\n await withAtomicFlush(em, [() => {\n if (input.type !== undefined) message.type = input.type\n if (input.visibility !== undefined) message.visibility = input.visibility\n if (input.sourceEntityType !== undefined) message.sourceEntityType = input.sourceEntityType\n if (input.sourceEntityId !== undefined) message.sourceEntityId = input.sourceEntityId\n if (input.externalEmail !== undefined) message.externalEmail = input.externalEmail\n if (input.externalName !== undefined) message.externalName = input.externalName\n if (input.subject !== undefined) message.subject = input.subject\n if (input.body !== undefined) message.body = input.body\n if (input.bodyFormat !== undefined) message.bodyFormat = input.bodyFormat\n if (input.priority !== undefined) message.priority = input.priority\n if (input.actionData !== undefined) message.actionData = input.actionData\n if (input.sendViaEmail !== undefined) message.sendViaEmail = input.sendViaEmail\n }, async () => {\n if (input.recipients) {\n await em.nativeDelete(MessageRecipient, { messageId: message.id })\n for (const recipient of input.recipients) {\n em.persist(em.create(MessageRecipient, {\n messageId: message.id,\n recipientUserId: recipient.userId,\n recipientType: recipient.type,\n status: 'unread',\n }))\n }\n }\n\n if (input.objects) {\n await em.nativeDelete(MessageObject, { messageId: message.id })\n for (const object of input.objects) {\n em.persist(em.create(MessageObject, {\n messageId: message.id,\n entityModule: object.entityModule,\n entityType: object.entityType,\n entityId: object.entityId,\n actionRequired: object.actionRequired,\n actionType: object.actionType,\n actionLabel: object.actionLabel,\n }))\n }\n }\n\n if (input.attachmentIds) {\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n if (input.attachmentIds.length === 0) {\n await em.nativeDelete(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: message.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n } else {\n await em.nativeDelete(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: message.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n id: { $nin: input.attachmentIds },\n })\n }\n await linkAttachmentsToMessage(\n em,\n message.id,\n input.attachmentIds,\n input.organizationId,\n input.tenantId,\n )\n }\n\n if (isSending) {\n // Send transition was validated before the block; just apply the\n // status mutations here (persisted by this phase's boundary flush).\n message.isDraft = false\n message.status = 'sent'\n message.sentAt = new Date()\n if (!message.threadId) message.threadId = message.id\n }\n }], { transaction: true })\n\n await emitMessageIndexUpsert(ctx.container, {\n messageId: message.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n if (isSending) {\n const recipientUserIds = input.recipients\n ? input.recipients.map((r) => r.userId)\n : (preloadedRecipients ?? []).map((r) => r.recipientUserId)\n const resolvedVisibility = input.visibility ?? message.visibility\n const resolvedSendViaEmail = input.sendViaEmail ?? message.sendViaEmail\n await emitMessageSentEvent(ctx.container, {\n messageId: message.id,\n senderUserId: input.userId,\n recipientUserIds,\n sendViaEmail: resolvedVisibility === 'public' ? true : resolvedSendViaEmail,\n externalEmail: message.externalEmail ?? null,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n }\n\n return { ok: true, id: message.id }\n },\n async captureAfter(rawInput, _result, ctx) {\n const input = updateDraftCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, input.messageId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n },\n buildLog: async ({ input, snapshots }) => {\n const parsed = updateDraftCommandSchema.parse(input)\n return {\n actionLabel: parsed.isDraft === false ? 'Send draft message' : 'Update draft message',\n resourceKind: 'messages.message',\n resourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n before: (snapshots.before as MessageAggregateSnapshot | undefined) ?? null,\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotBefore: snapshots.before ?? null,\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const before = undo?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreMessageAggregateSnapshot(em, before)\n },\n}\n\nconst replyMessageCommand: CommandHandler<unknown, { id: string; externalEmail: string | null; recipientUserIds: string[] }> = {\n id: 'messages.messages.reply',\n async execute(rawInput, ctx) {\n const input = replyCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const original = await requireMessageById(em, input, input.messageId)\n const ownRecipient = await em.findOne(MessageRecipient, {\n messageId: original.id,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n if (original.senderUserId !== input.userId && !ownRecipient) throw new Error('Access denied')\n\n const messageType = getMessageTypeOrDefault(original.type)\n if (messageType.allowReply === false) throw new Error('Reply is not allowed for this message type')\n\n const recipientIds = new Set(\n (input.recipients ?? [])\n .map((recipient) => recipient.userId)\n .filter((recipientUserId) => recipientUserId !== input.userId),\n )\n\n if (recipientIds.size === 0) {\n const originalRecipients = await em.find(MessageRecipient, { messageId: original.id, deletedAt: null })\n if (input.replyAll) {\n if (original.senderUserId !== input.userId) recipientIds.add(original.senderUserId)\n for (const recipient of originalRecipients) {\n if (recipient.recipientUserId !== input.userId) recipientIds.add(recipient.recipientUserId)\n }\n } else if (original.senderUserId !== input.userId) {\n recipientIds.add(original.senderUserId)\n } else {\n for (const recipient of originalRecipients) {\n if (recipient.recipientUserId !== input.userId) {\n recipientIds.add(recipient.recipientUserId)\n break\n }\n }\n }\n\n if (recipientIds.size === 0 && original.senderUserId === input.userId) {\n recipientIds.add(input.userId)\n }\n }\n if (recipientIds.size === 0) throw new Error('No recipients available for reply')\n\n let messageId = ''\n let responseExternalEmail: string | null = null\n await em.transactional(async (trx) => {\n const message = trx.create(Message, {\n type: original.type,\n visibility: original.visibility ?? null,\n sourceEntityType: original.sourceEntityType,\n sourceEntityId: original.sourceEntityId,\n externalEmail: original.externalEmail,\n externalName: original.externalName,\n threadId: original.threadId ?? original.id,\n parentMessageId: original.id,\n senderUserId: input.userId,\n subject: buildReplySubject(original.subject),\n body: input.body,\n bodyFormat: input.bodyFormat,\n priority: original.priority,\n status: 'sent',\n isDraft: false,\n sentAt: new Date(),\n sendViaEmail: input.sendViaEmail,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await trx.persist(message).flush()\n for (const recipientUserId of recipientIds) {\n trx.persist(trx.create(MessageRecipient, {\n messageId: message.id,\n recipientUserId,\n recipientType: 'to',\n status: 'unread',\n }))\n }\n await trx.flush()\n if (input.attachmentIds?.length) {\n await linkAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentIds,\n input.organizationId,\n input.tenantId,\n )\n }\n if (input.attachmentRecordId) {\n await linkLibraryAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentRecordId,\n input.organizationId,\n input.tenantId,\n )\n }\n messageId = message.id\n responseExternalEmail = message.externalEmail ?? null\n })\n\n await emitMessageSentEvent(ctx.container, {\n messageId,\n senderUserId: input.userId,\n recipientUserIds: Array.from(recipientIds),\n sendViaEmail: input.sendViaEmail,\n externalEmail: responseExternalEmail,\n replyTo: original.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await emitMessageIndexUpsert(ctx.container, {\n messageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n return {\n id: messageId,\n externalEmail: responseExternalEmail,\n recipientUserIds: Array.from(recipientIds),\n }\n },\n async captureAfter(_input, result, ctx) {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, result.id)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = replyCommandSchema.parse(input)\n return {\n actionLabel: 'Reply to message',\n resourceKind: 'messages.message',\n resourceId: result.id,\n parentResourceKind: 'messages.message',\n parentResourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const after = undo?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: after.message.id })\n if (!message) return\n message.deletedAt = new Date()\n await em.flush()\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: after.message.id,\n actorUserId: after.message.senderUserId,\n recipientUserIds: after.recipients.map((recipient) => recipient.recipientUserId),\n tenantId: after.message.tenantId,\n organizationId: after.message.organizationId,\n })\n },\n}\n\nconst forwardMessageCommand: CommandHandler<unknown, { id: string; externalEmail: string | null; recipientUserIds: string[] }> = {\n id: 'messages.messages.forward',\n async execute(rawInput, ctx) {\n const input = forwardCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const original = await requireMessageById(em, input, input.messageId)\n const isRecipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n if (original.senderUserId !== input.userId && !isRecipient) throw new Error('Access denied')\n\n const messageType = getMessageTypeOrDefault(original.type)\n if (messageType.allowForward === false) throw new Error('Forward is not allowed for this message type')\n\n const originalObjects = await em.find(MessageObject, { messageId: input.messageId })\n const forwardThreadSlice = await buildForwardThreadSlice(em, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n userId: input.userId,\n }, original)\n const generatedPreview = await buildForwardPreviewFromThreadSlice(em, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n userId: input.userId,\n }, original, forwardThreadSlice)\n const forwardedBody = typeof input.body === 'string'\n ? input.body\n : buildForwardBodyFromLegacyInput(generatedPreview.body, input.additionalBody)\n let newMessageId = ''\n let responseExternalEmail: string | null = null\n await em.transactional(async (trx) => {\n const newMessage = trx.create(Message, {\n type: original.type,\n visibility: original.visibility ?? null,\n sourceEntityType: original.sourceEntityType,\n sourceEntityId: original.sourceEntityId,\n externalEmail: original.externalEmail,\n externalName: original.externalName,\n threadId: original.threadId ?? original.id,\n parentMessageId: original.id,\n senderUserId: input.userId,\n subject: `Fwd: ${original.subject}`,\n body: forwardedBody,\n bodyFormat: original.bodyFormat,\n priority: original.priority,\n status: 'sent',\n isDraft: false,\n sentAt: new Date(),\n sendViaEmail: input.sendViaEmail,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await trx.persist(newMessage).flush()\n for (const recipient of input.recipients) {\n trx.persist(trx.create(MessageRecipient, {\n messageId: newMessage.id,\n recipientUserId: recipient.userId,\n recipientType: recipient.type,\n status: 'unread',\n }))\n }\n for (const obj of originalObjects) {\n trx.persist(trx.create(MessageObject, {\n messageId: newMessage.id,\n entityModule: obj.entityModule,\n entityType: obj.entityType,\n entityId: obj.entityId,\n actionRequired: obj.actionRequired,\n actionType: obj.actionType,\n actionLabel: obj.actionLabel,\n entitySnapshot: obj.entitySnapshot,\n }))\n }\n await trx.flush()\n if (input.includeAttachments !== false) {\n await copyAttachmentsForForwardMessages(\n trx,\n forwardThreadSlice.map((item) => item.id),\n newMessage.id,\n input.organizationId,\n input.tenantId,\n )\n }\n newMessageId = newMessage.id\n responseExternalEmail = newMessage.externalEmail ?? null\n })\n\n await emitMessageSentEvent(ctx.container, {\n messageId: newMessageId,\n senderUserId: input.userId,\n recipientUserIds: input.recipients.map((item) => item.userId),\n sendViaEmail: input.sendViaEmail,\n externalEmail: responseExternalEmail,\n forwardedFrom: original.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await emitMessageIndexUpsert(ctx.container, {\n messageId: newMessageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n return {\n id: newMessageId,\n externalEmail: responseExternalEmail,\n recipientUserIds: input.recipients.map((item) => item.userId),\n }\n },\n async captureAfter(_input, result, ctx) {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, result.id)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = forwardCommandSchema.parse(input)\n return {\n actionLabel: 'Forward message',\n resourceKind: 'messages.message',\n resourceId: result.id,\n parentResourceKind: 'messages.message',\n parentResourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const after = undo?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: after.message.id })\n if (!message) return\n message.deletedAt = new Date()\n await em.flush()\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: after.message.id,\n actorUserId: after.message.senderUserId,\n recipientUserIds: after.recipients.map((recipient) => recipient.recipientUserId),\n tenantId: after.message.tenantId,\n organizationId: after.message.organizationId,\n })\n },\n}\n\nconst deleteForActorCommand: CommandHandler<unknown, { ok: true }> = {\n id: 'messages.messages.delete_for_actor',\n async prepare(rawInput, ctx) {\n const input = deleteForActorCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await requireMessageById(em, input, input.messageId)\n const recipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n return {\n before: {\n messageId: message.id,\n messageDeletedAt: toIso(message.deletedAt),\n recipientId: recipient?.id ?? null,\n recipientStatus: recipient?.status ?? null,\n recipientDeletedAt: toIso(recipient?.deletedAt),\n } satisfies MessageDeleteUndoState,\n }\n },\n async execute(rawInput, ctx) {\n const input = deleteForActorCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await requireMessageById(em, input, input.messageId)\n const recipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n if (recipient) {\n recipient.status = 'deleted'\n recipient.deletedAt = new Date()\n await em.flush()\n await emitMessageDeletedEvent(ctx.container, {\n messageId: input.messageId,\n actorUserId: input.userId,\n target: 'recipient',\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n return { ok: true }\n }\n if (message.senderUserId === input.userId) {\n const recipientRows = await em.find(MessageRecipient, { messageId: input.messageId })\n message.deletedAt = new Date()\n await em.flush()\n const recipientUserIds = recipientRows\n .map((row) => row.recipientUserId)\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: input.messageId,\n actorUserId: input.userId,\n recipientUserIds,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n return { ok: true }\n }\n throw new Error('Access denied')\n },\n async captureAfter(rawInput, _result, ctx) {\n const input = deleteForActorCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: input.messageId, tenantId: input.tenantId })\n const recipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n })\n return {\n messageId: input.messageId,\n messageDeletedAt: toIso(message?.deletedAt),\n recipientId: recipient?.id ?? null,\n recipientStatus: recipient?.status ?? null,\n recipientDeletedAt: toIso(recipient?.deletedAt),\n } satisfies MessageDeleteUndoState\n },\n buildLog: async ({ input, snapshots }) => {\n const parsed = deleteForActorCommandSchema.parse(input)\n return {\n actionLabel: 'Delete message for actor',\n resourceKind: 'messages.message',\n resourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n before: (snapshots.before as MessageDeleteUndoState | undefined) ?? null,\n after: (snapshots.after as MessageDeleteUndoState | undefined) ?? null,\n } satisfies UndoPayload<MessageDeleteUndoState>,\n },\n snapshotBefore: snapshots.before ?? null,\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageDeleteUndoState>>(logEntry)\n const before = undo?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: before.messageId })\n if (message) {\n message.deletedAt = toDate(before.messageDeletedAt)\n }\n if (before.recipientId) {\n const recipient = await em.findOne(MessageRecipient, { id: before.recipientId })\n if (recipient) {\n recipient.status = (before.recipientStatus ?? 'unread') as MessageRecipient['status']\n recipient.deletedAt = toDate(before.recipientDeletedAt)\n }\n }\n await em.flush()\n },\n}\n\nregisterCommand(composeMessageCommand)\nregisterCommand(updateDraftCommand)\nregisterCommand(replyMessageCommand)\nregisterCommand(forwardMessageCommand)\nregisterCommand(deleteForActorCommand)\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Message, MessageObject, MessageRecipient, type MessageActionData } from '../data/entities'\nimport { emitMessagesEvent } from '../events'\nimport {\n composeMessageSchema,\n forwardMessageSchema,\n replyMessageSchema,\n updateDraftSchema,\n} from '../data/validators'\nimport { linkAttachmentsToMessage, linkLibraryAttachmentsToMessage, copyAttachmentsForForwardMessages } from '../lib/attachments'\nimport { MESSAGE_ATTACHMENT_ENTITY_ID, MESSAGE_ENTITY_ID } from '../lib/constants'\nimport { getMessageTypeOrDefault } from '../lib/message-types-registry'\nimport { validateMessageObjectsForType } from '../lib/object-validation'\nimport { buildForwardBodyFromLegacyInput, buildForwardPreviewFromThreadSlice, buildForwardThreadSlice } from '../lib/forwarding'\nimport {\n assertOrganizationAccess,\n loadMessageAggregateSnapshot,\n restoreMessageAggregateSnapshot,\n type MessageAggregateSnapshot,\n type MessageScopeInput,\n} from './shared'\n\ntype MessageSentEventPayload = {\n messageId: string\n senderUserId: string\n recipientUserIds: string[]\n sendViaEmail: boolean\n externalEmail?: string | null\n forwardedFrom?: string\n replyTo?: string\n tenantId: string\n organizationId?: string | null\n}\n\ntype ContainerWithResolve = {\n resolve: (name: string) => unknown\n}\n\nasync function emitMessageSentEvent(_container: ContainerWithResolve, payload: MessageSentEventPayload) {\n await emitMessagesEvent('messages.message.sent', payload, { persistent: true })\n}\n\nasync function emitMessageDeletedEvent(_container: ContainerWithResolve, payload: {\n messageId: string\n actorUserId: string\n target: 'sender' | 'recipient'\n tenantId: string\n organizationId: string | null\n}) {\n await emitMessagesEvent(\n 'messages.message.deleted',\n { ...payload, recipientUserId: payload.actorUserId },\n { persistent: true },\n )\n}\n\nasync function emitMessageGloballyDeletedEvent(_container: ContainerWithResolve, payload: {\n messageId: string\n actorUserId: string\n recipientUserIds: string[]\n tenantId: string\n organizationId: string | null\n}) {\n const audience = Array.from(new Set([payload.actorUserId, ...payload.recipientUserIds]))\n await emitMessagesEvent(\n 'messages.message.deleted',\n {\n messageId: payload.messageId,\n actorUserId: payload.actorUserId,\n target: 'global',\n recipientUserIds: audience,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n },\n { persistent: true },\n )\n}\n\nasync function emitMessageIndexUpsert(\n container: ContainerWithResolve,\n payload: {\n messageId: string\n tenantId: string\n organizationId: string | null\n },\n) {\n let bus: { emitEvent: (name: string, body: unknown, options?: unknown) => Promise<void> } | null = null\n try {\n bus = container.resolve('eventBus') as { emitEvent: (name: string, body: unknown, options?: unknown) => Promise<void> }\n } catch {\n bus = null\n }\n\n if (!bus) return\n\n await bus.emitEvent(\n 'query_index.upsert_one',\n {\n entityType: MESSAGE_ENTITY_ID,\n recordId: payload.messageId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n crudAction: 'updated',\n coverageBaseDelta: 1,\n },\n {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n },\n ).catch(() => undefined)\n}\n\nconst scopeSchema = z.object({\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid().nullable(),\n userId: z.string().uuid(),\n})\n\nconst composeCommandSchema = composeMessageSchema.safeExtend({\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n // Optional dedup key (inbound email ingest sets it; other callers leave it\n // undefined). When set, a re-issued compose returns the first message instead\n // of creating a duplicate.\n idempotencyKey: z.string().min(1).max(255).optional(),\n})\n\nconst updateDraftCommandSchema = updateDraftSchema.safeExtend({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\nconst replyCommandSchema = replyMessageSchema.safeExtend({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\nconst forwardCommandSchema = forwardMessageSchema.safeExtend({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\nconst deleteForActorCommandSchema = z.object({\n messageId: z.string().uuid(),\n tenantId: scopeSchema.shape.tenantId,\n organizationId: scopeSchema.shape.organizationId,\n userId: scopeSchema.shape.userId,\n})\n\ntype ComposeCommandInput = z.infer<typeof composeCommandSchema>\ntype UpdateDraftCommandInput = z.infer<typeof updateDraftCommandSchema>\ntype ReplyCommandInput = z.infer<typeof replyCommandSchema>\ntype ForwardCommandInput = z.infer<typeof forwardCommandSchema>\ntype DeleteForActorCommandInput = z.infer<typeof deleteForActorCommandSchema>\n\ntype MessageDeleteUndoState = {\n messageId: string\n messageDeletedAt: string | null\n recipientId: string | null\n recipientStatus: 'unread' | 'read' | 'archived' | 'deleted' | null\n recipientDeletedAt: string | null\n}\n\nfunction toIso(value: Date | null | undefined): string | null {\n return value ? value.toISOString() : null\n}\n\nfunction toDate(value: string | null | undefined): Date | null {\n if (!value) return null\n return new Date(value)\n}\n\nfunction buildReplySubject(subject: string): string {\n if (/^re:\\s*/i.test(subject)) return subject\n return `Re: ${subject}`\n}\n\nasync function requireMessageById(\n em: EntityManager,\n scope: MessageScopeInput,\n messageId: string,\n) {\n const message = await findOneWithDecryption(\n em,\n Message,\n {\n id: messageId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n },\n )\n if (!message) throw new Error('Message not found')\n assertOrganizationAccess(scope, message)\n return message\n}\n\nfunction isUniqueViolation(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const code = (err as { code?: string }).code\n if (code === '23505') return true\n const message = (err as { message?: string }).message\n return typeof message === 'string' && /duplicate key value|unique constraint/i.test(message)\n}\n\ntype ComposeMessageResult = {\n id: string\n threadId: string | null\n externalEmail: string | null\n isDraft: boolean\n recipientUserIds: string[]\n /**\n * True when this was an idempotent replay \u2014 an existing message was returned\n * and nothing was written. Signals `buildLog` to skip the audit/undo entry.\n */\n deduplicated?: boolean\n}\n\nasync function buildComposeResultFromExisting(\n em: EntityManager,\n message: Message,\n): Promise<ComposeMessageResult> {\n const recipients = await findWithDecryption(\n em,\n MessageRecipient,\n { messageId: message.id, deletedAt: null },\n undefined,\n { tenantId: message.tenantId, organizationId: message.organizationId ?? null },\n )\n return {\n id: message.id,\n threadId: message.threadId ?? null,\n externalEmail: message.externalEmail ?? null,\n isDraft: message.isDraft,\n recipientUserIds: recipients.map((recipient) => recipient.recipientUserId),\n deduplicated: true,\n }\n}\n\nconst composeMessageCommand: CommandHandler<unknown, { id: string; threadId: string | null; externalEmail: string | null; isDraft: boolean; recipientUserIds: string[]; deduplicated?: boolean }> = {\n id: 'messages.messages.compose',\n async execute(rawInput, ctx) {\n const input = composeCommandSchema.parse(rawInput)\n if (input.objects?.length) {\n const objectValidationError = validateMessageObjectsForType(input.type, input.objects)\n if (objectValidationError) throw new Error(objectValidationError)\n }\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = { tenantId: input.tenantId, organizationId: input.organizationId }\n\n // Idempotency fast-path: a retried inbound-email ingest re-issues compose\n // for the same source message (the first attempt committed the message but a\n // downstream transient failure rolled the ingest back). Return that message\n // so the retry cannot create a duplicate.\n if (input.idempotencyKey) {\n const existing = await findOneWithDecryption(\n em,\n Message,\n { tenantId: input.tenantId, idempotencyKey: input.idempotencyKey },\n undefined,\n scope,\n )\n if (existing) return buildComposeResultFromExisting(em, existing)\n }\n\n let messageId = ''\n let responseThreadId: string | null = null\n let responseExternalEmail: string | null = null\n\n const composeTx = em.transactional(async (trx) => {\n const threadId = input.parentMessageId\n ? (\n await findOneWithDecryption(\n trx,\n Message,\n {\n id: input.parentMessageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n )?.threadId ?? input.parentMessageId\n : undefined\n\n const isPublicVisibility = input.visibility === 'public'\n const sendViaEmail = isPublicVisibility ? true : input.sendViaEmail\n const message = trx.create(Message, {\n type: input.type,\n visibility: input.visibility ?? null,\n sourceEntityType: input.sourceEntityType,\n sourceEntityId: input.sourceEntityId,\n externalEmail: input.externalEmail,\n externalName: input.externalName,\n threadId: threadId ?? undefined,\n parentMessageId: input.parentMessageId,\n senderUserId: input.userId,\n subject: input.subject,\n body: input.body,\n bodyFormat: input.bodyFormat,\n priority: input.priority,\n status: input.isDraft ? 'draft' : 'sent',\n isDraft: input.isDraft ?? false,\n sentAt: input.isDraft ? null : new Date(),\n actionData: input.actionData as MessageActionData | undefined,\n sendViaEmail,\n idempotencyKey: input.idempotencyKey ?? null,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n await trx.persist(message).flush()\n if (!threadId && !input.isDraft && !message.threadId) {\n message.threadId = message.id\n await trx.flush()\n }\n\n for (const recipient of input.recipients) {\n trx.persist(trx.create(MessageRecipient, {\n messageId: message.id,\n recipientUserId: recipient.userId,\n recipientType: recipient.type,\n status: 'unread',\n }))\n }\n\n if (input.objects) {\n for (const obj of input.objects) {\n trx.persist(trx.create(MessageObject, {\n messageId: message.id,\n entityModule: obj.entityModule,\n entityType: obj.entityType,\n entityId: obj.entityId,\n actionRequired: obj.actionRequired,\n actionType: obj.actionType,\n actionLabel: obj.actionLabel,\n }))\n }\n }\n\n await trx.flush()\n\n if (input.attachmentIds?.length) {\n await linkAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentIds,\n input.organizationId,\n input.tenantId,\n )\n }\n\n if (input.attachmentRecordId) {\n await linkLibraryAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentRecordId,\n input.organizationId,\n input.tenantId,\n )\n }\n\n messageId = message.id\n responseThreadId = message.threadId ?? null\n responseExternalEmail = message.externalEmail ?? null\n })\n try {\n await composeTx\n } catch (err) {\n // Lost a concurrent race on the same idempotency key \u2014 return the message\n // the winning compose created. A propagated 23505 would otherwise be\n // classified permanent and dead-letter the inbound mail.\n if (input.idempotencyKey && isUniqueViolation(err)) {\n const existing = await findOneWithDecryption(\n em.fork(),\n Message,\n { tenantId: input.tenantId, idempotencyKey: input.idempotencyKey },\n undefined,\n scope,\n )\n if (existing) return buildComposeResultFromExisting(em.fork(), existing)\n }\n throw err\n }\n\n if (!input.isDraft) {\n await emitMessageSentEvent(ctx.container, {\n messageId,\n senderUserId: input.userId,\n recipientUserIds: input.recipients.map((recipient) => recipient.userId),\n sendViaEmail: input.visibility === 'public' ? true : input.sendViaEmail,\n externalEmail: responseExternalEmail,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n }\n\n await emitMessageIndexUpsert(ctx.container, {\n messageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n return {\n id: messageId,\n threadId: responseThreadId,\n externalEmail: responseExternalEmail,\n isDraft: input.isDraft,\n recipientUserIds: input.recipients.map((recipient) => recipient.userId),\n }\n },\n async captureAfter(_input, result, ctx) {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, result.id)\n },\n buildLog: async ({ input, result, snapshots }) => {\n // Idempotent replay: execute returned a pre-existing message without writing\n // anything. Skip the audit entry entirely \u2014 logging it as a fresh \"Compose\n // message\" would both misrepresent the dedup and expose an undo that\n // soft-deletes a legitimately-received inbound message.\n if (result.deduplicated) return { skipLog: true }\n const parsed = composeCommandSchema.parse(input)\n return {\n actionLabel: parsed.isDraft ? 'Create draft message' : 'Compose message',\n resourceKind: 'messages.message',\n resourceId: result.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const after = undo?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: after.message.id })\n if (!message) return\n message.deletedAt = new Date()\n await em.flush()\n if (!after.message.isDraft) {\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: after.message.id,\n actorUserId: after.message.senderUserId,\n recipientUserIds: after.recipients.map((recipient) => recipient.recipientUserId),\n tenantId: after.message.tenantId,\n organizationId: after.message.organizationId,\n })\n }\n },\n}\n\nconst updateDraftCommand: CommandHandler<unknown, { ok: true; id: string }> = {\n id: 'messages.messages.update_draft',\n async prepare(rawInput, ctx) {\n const input = updateDraftCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadMessageAggregateSnapshot(em, input.messageId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = updateDraftCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await requireMessageById(em, input, input.messageId)\n\n if (message.senderUserId !== input.userId) throw new Error('Access denied')\n if (!message.isDraft) throw new Error('Only draft messages can be edited')\n\n const isSending = input.isDraft === false\n const preloadedRecipients = isSending && !input.recipients\n ? await findWithDecryption(\n em,\n MessageRecipient,\n { messageId: message.id, deletedAt: null },\n undefined,\n {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n },\n )\n : null\n\n const nextMessageType = input.type ?? message.type\n if (input.objects) {\n const objectValidationError = validateMessageObjectsForType(nextMessageType, input.objects)\n if (objectValidationError) throw new Error(objectValidationError)\n } else if (input.type !== undefined) {\n const existingObjects = await em.find(MessageObject, { messageId: message.id })\n if (existingObjects.length > 0) {\n const objectValidationError = validateMessageObjectsForType(\n nextMessageType,\n existingObjects.map((item) => ({\n entityModule: item.entityModule,\n entityType: item.entityType,\n entityId: item.entityId,\n })),\n )\n if (objectValidationError) throw new Error(objectValidationError)\n }\n }\n\n // Validate the send transition BEFORE the atomic-flush block so a rejected\n // send never mutates or flushes anything (everything checked here is known\n // from the parsed input + the loaded message).\n if (isSending) {\n const finalVisibility = input.visibility ?? message.visibility\n const finalSubject = input.subject ?? message.subject\n const finalBody = input.body ?? message.body\n const finalRecipientCount = input.recipients\n ? input.recipients.length\n : (preloadedRecipients?.length ?? 0)\n if (finalVisibility !== 'public' && finalRecipientCount === 0) {\n throw new Error('at least one recipient is required')\n }\n if (!finalSubject?.trim()) throw new Error('subject is required')\n if (!finalBody?.trim()) throw new Error('body is required')\n }\n\n // Phase 1 mutates the managed `message` scalars; the per-phase flush issues\n // those changes before phase 2's interleaved reads run (e.g.\n // `linkAttachmentsToMessage` issues an `em.find`). Under MikroORM v7 a read\n // between a scalar mutation and the terminal flush on the same EntityManager\n // silently drops the pending changeset, so keeping the scalar edits in their\n // own phase guarantees subject/body/etc. are persisted (SPEC-018 Problem 1).\n await withAtomicFlush(em, [() => {\n if (input.type !== undefined) message.type = input.type\n if (input.visibility !== undefined) message.visibility = input.visibility\n if (input.sourceEntityType !== undefined) message.sourceEntityType = input.sourceEntityType\n if (input.sourceEntityId !== undefined) message.sourceEntityId = input.sourceEntityId\n if (input.externalEmail !== undefined) message.externalEmail = input.externalEmail\n if (input.externalName !== undefined) message.externalName = input.externalName\n if (input.subject !== undefined) message.subject = input.subject\n if (input.body !== undefined) message.body = input.body\n if (input.bodyFormat !== undefined) message.bodyFormat = input.bodyFormat\n if (input.priority !== undefined) message.priority = input.priority\n if (input.actionData !== undefined) message.actionData = input.actionData\n if (input.sendViaEmail !== undefined) message.sendViaEmail = input.sendViaEmail\n }, async () => {\n if (input.recipients) {\n await em.nativeDelete(MessageRecipient, { messageId: message.id })\n for (const recipient of input.recipients) {\n em.persist(em.create(MessageRecipient, {\n messageId: message.id,\n recipientUserId: recipient.userId,\n recipientType: recipient.type,\n status: 'unread',\n }))\n }\n }\n\n if (input.objects) {\n await em.nativeDelete(MessageObject, { messageId: message.id })\n for (const object of input.objects) {\n em.persist(em.create(MessageObject, {\n messageId: message.id,\n entityModule: object.entityModule,\n entityType: object.entityType,\n entityId: object.entityId,\n actionRequired: object.actionRequired,\n actionType: object.actionType,\n actionLabel: object.actionLabel,\n }))\n }\n }\n\n if (input.attachmentIds) {\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n if (input.attachmentIds.length === 0) {\n await em.nativeDelete(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: message.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n } else {\n await em.nativeDelete(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: message.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n id: { $nin: input.attachmentIds },\n })\n }\n await linkAttachmentsToMessage(\n em,\n message.id,\n input.attachmentIds,\n input.organizationId,\n input.tenantId,\n )\n }\n\n if (isSending) {\n // Send transition was validated before the block; just apply the\n // status mutations here (persisted by this phase's boundary flush).\n message.isDraft = false\n message.status = 'sent'\n message.sentAt = new Date()\n if (!message.threadId) message.threadId = message.id\n }\n }], { transaction: true })\n\n await emitMessageIndexUpsert(ctx.container, {\n messageId: message.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n if (isSending) {\n const recipientUserIds = input.recipients\n ? input.recipients.map((r) => r.userId)\n : (preloadedRecipients ?? []).map((r) => r.recipientUserId)\n const resolvedVisibility = input.visibility ?? message.visibility\n const resolvedSendViaEmail = input.sendViaEmail ?? message.sendViaEmail\n await emitMessageSentEvent(ctx.container, {\n messageId: message.id,\n senderUserId: input.userId,\n recipientUserIds,\n sendViaEmail: resolvedVisibility === 'public' ? true : resolvedSendViaEmail,\n externalEmail: message.externalEmail ?? null,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n }\n\n return { ok: true, id: message.id }\n },\n async captureAfter(rawInput, _result, ctx) {\n const input = updateDraftCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, input.messageId, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n },\n buildLog: async ({ input, snapshots }) => {\n const parsed = updateDraftCommandSchema.parse(input)\n return {\n actionLabel: parsed.isDraft === false ? 'Send draft message' : 'Update draft message',\n resourceKind: 'messages.message',\n resourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n before: (snapshots.before as MessageAggregateSnapshot | undefined) ?? null,\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotBefore: snapshots.before ?? null,\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const before = undo?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreMessageAggregateSnapshot(em, before)\n },\n}\n\nconst replyMessageCommand: CommandHandler<unknown, { id: string; externalEmail: string | null; recipientUserIds: string[] }> = {\n id: 'messages.messages.reply',\n async execute(rawInput, ctx) {\n const input = replyCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const original = await requireMessageById(em, input, input.messageId)\n const ownRecipient = await em.findOne(MessageRecipient, {\n messageId: original.id,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n if (original.senderUserId !== input.userId && !ownRecipient) throw new Error('Access denied')\n\n const messageType = getMessageTypeOrDefault(original.type)\n if (messageType.allowReply === false) throw new Error('Reply is not allowed for this message type')\n\n const recipientIds = new Set(\n (input.recipients ?? [])\n .map((recipient) => recipient.userId)\n .filter((recipientUserId) => recipientUserId !== input.userId),\n )\n\n if (recipientIds.size === 0) {\n const originalRecipients = await em.find(MessageRecipient, { messageId: original.id, deletedAt: null })\n if (input.replyAll) {\n if (original.senderUserId !== input.userId) recipientIds.add(original.senderUserId)\n for (const recipient of originalRecipients) {\n if (recipient.recipientUserId !== input.userId) recipientIds.add(recipient.recipientUserId)\n }\n } else if (original.senderUserId !== input.userId) {\n recipientIds.add(original.senderUserId)\n } else {\n for (const recipient of originalRecipients) {\n if (recipient.recipientUserId !== input.userId) {\n recipientIds.add(recipient.recipientUserId)\n break\n }\n }\n }\n\n if (recipientIds.size === 0 && original.senderUserId === input.userId) {\n recipientIds.add(input.userId)\n }\n }\n if (recipientIds.size === 0) throw new Error('No recipients available for reply')\n\n let messageId = ''\n let responseExternalEmail: string | null = null\n await em.transactional(async (trx) => {\n const message = trx.create(Message, {\n type: original.type,\n visibility: original.visibility ?? null,\n sourceEntityType: original.sourceEntityType,\n sourceEntityId: original.sourceEntityId,\n externalEmail: original.externalEmail,\n externalName: original.externalName,\n threadId: original.threadId ?? original.id,\n parentMessageId: original.id,\n senderUserId: input.userId,\n subject: buildReplySubject(original.subject),\n body: input.body,\n bodyFormat: input.bodyFormat,\n priority: original.priority,\n status: 'sent',\n isDraft: false,\n sentAt: new Date(),\n sendViaEmail: input.sendViaEmail,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await trx.persist(message).flush()\n for (const recipientUserId of recipientIds) {\n trx.persist(trx.create(MessageRecipient, {\n messageId: message.id,\n recipientUserId,\n recipientType: 'to',\n status: 'unread',\n }))\n }\n await trx.flush()\n if (input.attachmentIds?.length) {\n await linkAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentIds,\n input.organizationId,\n input.tenantId,\n )\n }\n if (input.attachmentRecordId) {\n await linkLibraryAttachmentsToMessage(\n trx,\n message.id,\n input.attachmentRecordId,\n input.organizationId,\n input.tenantId,\n )\n }\n messageId = message.id\n responseExternalEmail = message.externalEmail ?? null\n })\n\n await emitMessageSentEvent(ctx.container, {\n messageId,\n senderUserId: input.userId,\n recipientUserIds: Array.from(recipientIds),\n sendViaEmail: input.sendViaEmail,\n externalEmail: responseExternalEmail,\n replyTo: original.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await emitMessageIndexUpsert(ctx.container, {\n messageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n return {\n id: messageId,\n externalEmail: responseExternalEmail,\n recipientUserIds: Array.from(recipientIds),\n }\n },\n async captureAfter(_input, result, ctx) {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, result.id)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = replyCommandSchema.parse(input)\n return {\n actionLabel: 'Reply to message',\n resourceKind: 'messages.message',\n resourceId: result.id,\n parentResourceKind: 'messages.message',\n parentResourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const after = undo?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: after.message.id })\n if (!message) return\n message.deletedAt = new Date()\n await em.flush()\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: after.message.id,\n actorUserId: after.message.senderUserId,\n recipientUserIds: after.recipients.map((recipient) => recipient.recipientUserId),\n tenantId: after.message.tenantId,\n organizationId: after.message.organizationId,\n })\n },\n}\n\nconst forwardMessageCommand: CommandHandler<unknown, { id: string; externalEmail: string | null; recipientUserIds: string[] }> = {\n id: 'messages.messages.forward',\n async execute(rawInput, ctx) {\n const input = forwardCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const original = await requireMessageById(em, input, input.messageId)\n const isRecipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n if (original.senderUserId !== input.userId && !isRecipient) throw new Error('Access denied')\n\n const messageType = getMessageTypeOrDefault(original.type)\n if (messageType.allowForward === false) throw new Error('Forward is not allowed for this message type')\n\n const originalObjects = await em.find(MessageObject, { messageId: input.messageId })\n const forwardThreadSlice = await buildForwardThreadSlice(em, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n userId: input.userId,\n }, original)\n const generatedPreview = await buildForwardPreviewFromThreadSlice(em, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n userId: input.userId,\n }, original, forwardThreadSlice)\n const forwardedBody = typeof input.body === 'string'\n ? input.body\n : buildForwardBodyFromLegacyInput(generatedPreview.body, input.additionalBody)\n let newMessageId = ''\n let responseExternalEmail: string | null = null\n await em.transactional(async (trx) => {\n const newMessage = trx.create(Message, {\n type: original.type,\n visibility: original.visibility ?? null,\n sourceEntityType: original.sourceEntityType,\n sourceEntityId: original.sourceEntityId,\n externalEmail: original.externalEmail,\n externalName: original.externalName,\n threadId: original.threadId ?? original.id,\n parentMessageId: original.id,\n senderUserId: input.userId,\n subject: `Fwd: ${original.subject}`,\n body: forwardedBody,\n bodyFormat: original.bodyFormat,\n priority: original.priority,\n status: 'sent',\n isDraft: false,\n sentAt: new Date(),\n sendViaEmail: input.sendViaEmail,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await trx.persist(newMessage).flush()\n for (const recipient of input.recipients) {\n trx.persist(trx.create(MessageRecipient, {\n messageId: newMessage.id,\n recipientUserId: recipient.userId,\n recipientType: recipient.type,\n status: 'unread',\n }))\n }\n for (const obj of originalObjects) {\n trx.persist(trx.create(MessageObject, {\n messageId: newMessage.id,\n entityModule: obj.entityModule,\n entityType: obj.entityType,\n entityId: obj.entityId,\n actionRequired: obj.actionRequired,\n actionType: obj.actionType,\n actionLabel: obj.actionLabel,\n entitySnapshot: obj.entitySnapshot,\n }))\n }\n await trx.flush()\n if (input.includeAttachments !== false) {\n await copyAttachmentsForForwardMessages(\n trx,\n forwardThreadSlice.map((item) => item.id),\n newMessage.id,\n input.tenantId,\n )\n }\n newMessageId = newMessage.id\n responseExternalEmail = newMessage.externalEmail ?? null\n })\n\n await emitMessageSentEvent(ctx.container, {\n messageId: newMessageId,\n senderUserId: input.userId,\n recipientUserIds: input.recipients.map((item) => item.userId),\n sendViaEmail: input.sendViaEmail,\n externalEmail: responseExternalEmail,\n forwardedFrom: original.id,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n await emitMessageIndexUpsert(ctx.container, {\n messageId: newMessageId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n return {\n id: newMessageId,\n externalEmail: responseExternalEmail,\n recipientUserIds: input.recipients.map((item) => item.userId),\n }\n },\n async captureAfter(_input, result, ctx) {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadMessageAggregateSnapshot(em, result.id)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = forwardCommandSchema.parse(input)\n return {\n actionLabel: 'Forward message',\n resourceKind: 'messages.message',\n resourceId: result.id,\n parentResourceKind: 'messages.message',\n parentResourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n after: (snapshots.after as MessageAggregateSnapshot | undefined) ?? null,\n } satisfies UndoPayload<MessageAggregateSnapshot>,\n },\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageAggregateSnapshot>>(logEntry)\n const after = undo?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: after.message.id })\n if (!message) return\n message.deletedAt = new Date()\n await em.flush()\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: after.message.id,\n actorUserId: after.message.senderUserId,\n recipientUserIds: after.recipients.map((recipient) => recipient.recipientUserId),\n tenantId: after.message.tenantId,\n organizationId: after.message.organizationId,\n })\n },\n}\n\nconst deleteForActorCommand: CommandHandler<unknown, { ok: true }> = {\n id: 'messages.messages.delete_for_actor',\n async prepare(rawInput, ctx) {\n const input = deleteForActorCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await requireMessageById(em, input, input.messageId)\n const recipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n return {\n before: {\n messageId: message.id,\n messageDeletedAt: toIso(message.deletedAt),\n recipientId: recipient?.id ?? null,\n recipientStatus: recipient?.status ?? null,\n recipientDeletedAt: toIso(recipient?.deletedAt),\n } satisfies MessageDeleteUndoState,\n }\n },\n async execute(rawInput, ctx) {\n const input = deleteForActorCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await requireMessageById(em, input, input.messageId)\n const recipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n deletedAt: null,\n })\n if (recipient) {\n recipient.status = 'deleted'\n recipient.deletedAt = new Date()\n await em.flush()\n await emitMessageDeletedEvent(ctx.container, {\n messageId: input.messageId,\n actorUserId: input.userId,\n target: 'recipient',\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n return { ok: true }\n }\n if (message.senderUserId === input.userId) {\n const recipientRows = await em.find(MessageRecipient, { messageId: input.messageId })\n message.deletedAt = new Date()\n await em.flush()\n const recipientUserIds = recipientRows\n .map((row) => row.recipientUserId)\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n await emitMessageGloballyDeletedEvent(ctx.container, {\n messageId: input.messageId,\n actorUserId: input.userId,\n recipientUserIds,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n return { ok: true }\n }\n throw new Error('Access denied')\n },\n async captureAfter(rawInput, _result, ctx) {\n const input = deleteForActorCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: input.messageId, tenantId: input.tenantId })\n const recipient = await em.findOne(MessageRecipient, {\n messageId: input.messageId,\n recipientUserId: input.userId,\n })\n return {\n messageId: input.messageId,\n messageDeletedAt: toIso(message?.deletedAt),\n recipientId: recipient?.id ?? null,\n recipientStatus: recipient?.status ?? null,\n recipientDeletedAt: toIso(recipient?.deletedAt),\n } satisfies MessageDeleteUndoState\n },\n buildLog: async ({ input, snapshots }) => {\n const parsed = deleteForActorCommandSchema.parse(input)\n return {\n actionLabel: 'Delete message for actor',\n resourceKind: 'messages.message',\n resourceId: parsed.messageId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n before: (snapshots.before as MessageDeleteUndoState | undefined) ?? null,\n after: (snapshots.after as MessageDeleteUndoState | undefined) ?? null,\n } satisfies UndoPayload<MessageDeleteUndoState>,\n },\n snapshotBefore: snapshots.before ?? null,\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<MessageDeleteUndoState>>(logEntry)\n const before = undo?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const message = await em.findOne(Message, { id: before.messageId })\n if (message) {\n message.deletedAt = toDate(before.messageDeletedAt)\n }\n if (before.recipientId) {\n const recipient = await em.findOne(MessageRecipient, { id: before.recipientId })\n if (recipient) {\n recipient.status = (before.recipientStatus ?? 'unread') as MessageRecipient['status']\n recipient.deletedAt = toDate(before.recipientDeletedAt)\n }\n }\n await em.flush()\n },\n}\n\nregisterCommand(composeMessageCommand)\nregisterCommand(updateDraftCommand)\nregisterCommand(replyMessageCommand)\nregisterCommand(forwardMessageCommand)\nregisterCommand(deleteForActorCommand)\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,SAAS;AAClB,SAAS,uBAA4C;AACrD,SAAS,uBAAuB;AAChC,SAAS,0BAA4C;AACrD,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,SAAS,eAAe,wBAAgD;AACjF,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B,iCAAiC,yCAAyC;AAC7G,SAAS,8BAA8B,yBAAyB;AAChE,SAAS,+BAA+B;AACxC,SAAS,qCAAqC;AAC9C,SAAS,iCAAiC,oCAAoC,+BAA+B;AAC7G;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAkBP,eAAe,qBAAqB,YAAkC,SAAkC;AACtG,QAAM,kBAAkB,yBAAyB,SAAS,EAAE,YAAY,KAAK,CAAC;AAChF;AAEA,eAAe,wBAAwB,YAAkC,SAMtE;AACD,QAAM;AAAA,IACJ;AAAA,IACA,EAAE,GAAG,SAAS,iBAAiB,QAAQ,YAAY;AAAA,IACnD,EAAE,YAAY,KAAK;AAAA,EACrB;AACF;AAEA,eAAe,gCAAgC,YAAkC,SAM9E;AACD,QAAM,WAAW,MAAM,KAAK,oBAAI,IAAI,CAAC,QAAQ,aAAa,GAAG,QAAQ,gBAAgB,CAAC,CAAC;AACvF,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,MACE,WAAW,QAAQ;AAAA,MACnB,aAAa,QAAQ;AAAA,MACrB,QAAQ;AAAA,MACR,kBAAkB;AAAA,MAClB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,IACA,EAAE,YAAY,KAAK;AAAA,EACrB;AACF;AAEA,eAAe,uBACb,WACA,SAKA;AACA,MAAI,MAA+F;AACnG,MAAI;AACF,UAAM,UAAU,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,UAAM;AAAA,EACR;AAEA,MAAI,CAAC,IAAK;AAEV,QAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,MACE,YAAY;AAAA,MACZ,UAAU,QAAQ;AAAA,MAClB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,YAAY;AAAA,MACZ,mBAAmB;AAAA,IACrB;AAAA,IACA;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,EACF,EAAE,MAAM,MAAM,MAAS;AACzB;AAEA,MAAM,cAAc,EAAE,OAAO;AAAA,EAC3B,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,QAAQ,EAAE,OAAO,EAAE,KAAK;AAC1B,CAAC;AAED,MAAM,uBAAuB,qBAAqB,WAAW;AAAA,EAC3D,UAAU,YAAY,MAAM;AAAA,EAC5B,gBAAgB,YAAY,MAAM;AAAA,EAClC,QAAQ,YAAY,MAAM;AAAA;AAAA;AAAA;AAAA,EAI1B,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AACtD,CAAC;AAED,MAAM,2BAA2B,kBAAkB,WAAW;AAAA,EAC5D,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,UAAU,YAAY,MAAM;AAAA,EAC5B,gBAAgB,YAAY,MAAM;AAAA,EAClC,QAAQ,YAAY,MAAM;AAC5B,CAAC;AAED,MAAM,qBAAqB,mBAAmB,WAAW;AAAA,EACvD,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,UAAU,YAAY,MAAM;AAAA,EAC5B,gBAAgB,YAAY,MAAM;AAAA,EAClC,QAAQ,YAAY,MAAM;AAC5B,CAAC;AAED,MAAM,uBAAuB,qBAAqB,WAAW;AAAA,EAC3D,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,UAAU,YAAY,MAAM;AAAA,EAC5B,gBAAgB,YAAY,MAAM;AAAA,EAClC,QAAQ,YAAY,MAAM;AAC5B,CAAC;AAED,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAC3C,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,UAAU,YAAY,MAAM;AAAA,EAC5B,gBAAgB,YAAY,MAAM;AAAA,EAClC,QAAQ,YAAY,MAAM;AAC5B,CAAC;AAgBD,SAAS,MAAM,OAA+C;AAC5D,SAAO,QAAQ,MAAM,YAAY,IAAI;AACvC;AAEA,SAAS,OAAO,OAA+C;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,IAAI,KAAK,KAAK;AACvB;AAEA,SAAS,kBAAkB,SAAyB;AAClD,MAAI,WAAW,KAAK,OAAO,EAAG,QAAO;AACrC,SAAO,OAAO,OAAO;AACvB;AAEA,eAAe,mBACb,IACA,OACA,WACA;AACA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU,MAAM;AAAA,MAChB,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB;AAAA,EACF;AACA,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,mBAAmB;AACjD,2BAAyB,OAAO,OAAO;AACvC,SAAO;AACT;AAEA,SAAS,kBAAkB,KAAuB;AAChD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,OAAQ,IAA0B;AACxC,MAAI,SAAS,QAAS,QAAO;AAC7B,QAAM,UAAW,IAA6B;AAC9C,SAAO,OAAO,YAAY,YAAY,yCAAyC,KAAK,OAAO;AAC7F;AAeA,eAAe,+BACb,IACA,SAC+B;AAC/B,QAAM,aAAa,MAAM;AAAA,IACvB;AAAA,IACA;AAAA,IACA,EAAE,WAAW,QAAQ,IAAI,WAAW,KAAK;AAAA,IACzC;AAAA,IACA,EAAE,UAAU,QAAQ,UAAU,gBAAgB,QAAQ,kBAAkB,KAAK;AAAA,EAC/E;AACA,SAAO;AAAA,IACL,IAAI,QAAQ;AAAA,IACZ,UAAU,QAAQ,YAAY;AAAA,IAC9B,eAAe,QAAQ,iBAAiB;AAAA,IACxC,SAAS,QAAQ;AAAA,IACjB,kBAAkB,WAAW,IAAI,CAAC,cAAc,UAAU,eAAe;AAAA,IACzE,cAAc;AAAA,EAChB;AACF;AAEA,MAAM,wBAA8L;AAAA,EAClM,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,qBAAqB,MAAM,QAAQ;AACjD,QAAI,MAAM,SAAS,QAAQ;AACzB,YAAM,wBAAwB,8BAA8B,MAAM,MAAM,MAAM,OAAO;AACrF,UAAI,sBAAuB,OAAM,IAAI,MAAM,qBAAqB;AAAA,IAClE;AAEA,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAM/E,QAAI,MAAM,gBAAgB;AACxB,YAAM,WAAW,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,QACA,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,QACjE;AAAA,QACA;AAAA,MACF;AACA,UAAI,SAAU,QAAO,+BAA+B,IAAI,QAAQ;AAAA,IAClE;AAEA,QAAI,YAAY;AAChB,QAAI,mBAAkC;AACtC,QAAI,wBAAuC;AAE3C,UAAM,YAAY,GAAG,cAAc,OAAO,QAAQ;AAChD,YAAM,WAAW,MAAM,mBAEnB,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,UACE,IAAI,MAAM;AAAA,UACV,UAAU,MAAM;AAAA,UAChB,gBAAgB,MAAM;AAAA,UACtB,WAAW;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF,IACC,YAAY,MAAM,kBACnB;AAEJ,YAAM,qBAAqB,MAAM,eAAe;AAChD,YAAM,eAAe,qBAAqB,OAAO,MAAM;AACvD,YAAM,UAAU,IAAI,OAAO,SAAS;AAAA,QAClC,MAAM,MAAM;AAAA,QACZ,YAAY,MAAM,cAAc;AAAA,QAChC,kBAAkB,MAAM;AAAA,QACxB,gBAAgB,MAAM;AAAA,QACtB,eAAe,MAAM;AAAA,QACrB,cAAc,MAAM;AAAA,QACpB,UAAU,YAAY;AAAA,QACtB,iBAAiB,MAAM;AAAA,QACvB,cAAc,MAAM;AAAA,QACpB,SAAS,MAAM;AAAA,QACf,MAAM,MAAM;AAAA,QACZ,YAAY,MAAM;AAAA,QAClB,UAAU,MAAM;AAAA,QAChB,QAAQ,MAAM,UAAU,UAAU;AAAA,QAClC,SAAS,MAAM,WAAW;AAAA,QAC1B,QAAQ,MAAM,UAAU,OAAO,oBAAI,KAAK;AAAA,QACxC,YAAY,MAAM;AAAA,QAClB;AAAA,QACA,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AAED,YAAM,IAAI,QAAQ,OAAO,EAAE,MAAM;AACjC,UAAI,CAAC,YAAY,CAAC,MAAM,WAAW,CAAC,QAAQ,UAAU;AACpD,gBAAQ,WAAW,QAAQ;AAC3B,cAAM,IAAI,MAAM;AAAA,MAClB;AAEA,iBAAW,aAAa,MAAM,YAAY;AACxC,YAAI,QAAQ,IAAI,OAAO,kBAAkB;AAAA,UACvC,WAAW,QAAQ;AAAA,UACnB,iBAAiB,UAAU;AAAA,UAC3B,eAAe,UAAU;AAAA,UACzB,QAAQ;AAAA,QACV,CAAC,CAAC;AAAA,MACJ;AAEA,UAAI,MAAM,SAAS;AACjB,mBAAW,OAAO,MAAM,SAAS;AAC/B,cAAI,QAAQ,IAAI,OAAO,eAAe;AAAA,YACpC,WAAW,QAAQ;AAAA,YACnB,cAAc,IAAI;AAAA,YAClB,YAAY,IAAI;AAAA,YAChB,UAAU,IAAI;AAAA,YACd,gBAAgB,IAAI;AAAA,YACpB,YAAY,IAAI;AAAA,YAChB,aAAa,IAAI;AAAA,UACnB,CAAC,CAAC;AAAA,QACJ;AAAA,MACF;AAEA,YAAM,IAAI,MAAM;AAEhB,UAAI,MAAM,eAAe,QAAQ;AAC/B,cAAM;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAI,MAAM,oBAAoB;AAC5B,cAAM;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,QACR;AAAA,MACF;AAEA,kBAAY,QAAQ;AACpB,yBAAmB,QAAQ,YAAY;AACvC,8BAAwB,QAAQ,iBAAiB;AAAA,IACnD,CAAC;AACD,QAAI;AACF,YAAM;AAAA,IACR,SAAS,KAAK;AAIZ,UAAI,MAAM,kBAAkB,kBAAkB,GAAG,GAAG;AAClD,cAAM,WAAW,MAAM;AAAA,UACrB,GAAG,KAAK;AAAA,UACR;AAAA,UACA,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,UACjE;AAAA,UACA;AAAA,QACF;AACA,YAAI,SAAU,QAAO,+BAA+B,GAAG,KAAK,GAAG,QAAQ;AAAA,MACzE;AACA,YAAM;AAAA,IACR;AAEA,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,qBAAqB,IAAI,WAAW;AAAA,QACxC;AAAA,QACA,cAAc,MAAM;AAAA,QACpB,kBAAkB,MAAM,WAAW,IAAI,CAAC,cAAc,UAAU,MAAM;AAAA,QACtE,cAAc,MAAM,eAAe,WAAW,OAAO,MAAM;AAAA,QAC3D,eAAe;AAAA,QACf,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,UAAM,uBAAuB,IAAI,WAAW;AAAA,MAC1C;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AAED,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,eAAe;AAAA,MACf,SAAS,MAAM;AAAA,MACf,kBAAkB,MAAM,WAAW,IAAI,CAAC,cAAc,UAAU,MAAM;AAAA,IACxE;AAAA,EACF;AAAA,EACA,MAAM,aAAa,QAAQ,QAAQ,KAAK;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,6BAA6B,IAAI,OAAO,EAAE;AAAA,EACnD;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,QAAQ,UAAU,MAAM;AAKhD,QAAI,OAAO,aAAc,QAAO,EAAE,SAAS,KAAK;AAChD,UAAM,SAAS,qBAAqB,MAAM,KAAK;AAC/C,WAAO;AAAA,MACL,aAAa,OAAO,UAAU,yBAAyB;AAAA,MACvD,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAQ,UAAU,SAAkD;AAAA,QACtE;AAAA,MACF;AAAA,MACA,eAAe,UAAU,SAAS;AAAA,IACpC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAA0D,QAAQ;AAC/E,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,IAAI,MAAM,QAAQ,GAAG,CAAC;AAClE,QAAI,CAAC,QAAS;AACd,YAAQ,YAAY,oBAAI,KAAK;AAC7B,UAAM,GAAG,MAAM;AACf,QAAI,CAAC,MAAM,QAAQ,SAAS;AAC1B,YAAM,gCAAgC,IAAI,WAAW;AAAA,QACnD,WAAW,MAAM,QAAQ;AAAA,QACzB,aAAa,MAAM,QAAQ;AAAA,QAC3B,kBAAkB,MAAM,WAAW,IAAI,CAAC,cAAc,UAAU,eAAe;AAAA,QAC/E,UAAU,MAAM,QAAQ;AAAA,QACxB,gBAAgB,MAAM,QAAQ;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,MAAM,qBAAwE;AAAA,EAC5E,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,yBAAyB,MAAM,QAAQ;AACrD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,6BAA6B,IAAI,MAAM,WAAW;AAAA,MACvE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AACD,WAAO,WAAW,EAAE,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,yBAAyB,MAAM,QAAQ;AACrD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,mBAAmB,IAAI,OAAO,MAAM,SAAS;AAEnE,QAAI,QAAQ,iBAAiB,MAAM,OAAQ,OAAM,IAAI,MAAM,eAAe;AAC1E,QAAI,CAAC,QAAQ,QAAS,OAAM,IAAI,MAAM,mCAAmC;AAEzE,UAAM,YAAY,MAAM,YAAY;AACpC,UAAM,sBAAsB,aAAa,CAAC,MAAM,aAC5C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,EAAE,WAAW,QAAQ,IAAI,WAAW,KAAK;AAAA,MACzC;AAAA,MACA;AAAA,QACE,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB;AAAA,IACF,IACE;AAEJ,UAAM,kBAAkB,MAAM,QAAQ,QAAQ;AAC9C,QAAI,MAAM,SAAS;AACjB,YAAM,wBAAwB,8BAA8B,iBAAiB,MAAM,OAAO;AAC1F,UAAI,sBAAuB,OAAM,IAAI,MAAM,qBAAqB;AAAA,IAClE,WAAW,MAAM,SAAS,QAAW;AACnC,YAAM,kBAAkB,MAAM,GAAG,KAAK,eAAe,EAAE,WAAW,QAAQ,GAAG,CAAC;AAC9E,UAAI,gBAAgB,SAAS,GAAG;AAC9B,cAAM,wBAAwB;AAAA,UAC5B;AAAA,UACA,gBAAgB,IAAI,CAAC,UAAU;AAAA,YAC7B,cAAc,KAAK;AAAA,YACnB,YAAY,KAAK;AAAA,YACjB,UAAU,KAAK;AAAA,UACjB,EAAE;AAAA,QACJ;AACA,YAAI,sBAAuB,OAAM,IAAI,MAAM,qBAAqB;AAAA,MAClE;AAAA,IACF;AAKA,QAAI,WAAW;AACb,YAAM,kBAAkB,MAAM,cAAc,QAAQ;AACpD,YAAM,eAAe,MAAM,WAAW,QAAQ;AAC9C,YAAM,YAAY,MAAM,QAAQ,QAAQ;AACxC,YAAM,sBAAsB,MAAM,aAC9B,MAAM,WAAW,SAChB,qBAAqB,UAAU;AACpC,UAAI,oBAAoB,YAAY,wBAAwB,GAAG;AAC7D,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AACA,UAAI,CAAC,cAAc,KAAK,EAAG,OAAM,IAAI,MAAM,qBAAqB;AAChE,UAAI,CAAC,WAAW,KAAK,EAAG,OAAM,IAAI,MAAM,kBAAkB;AAAA,IAC5D;AAQA,UAAM,gBAAgB,IAAI,CAAC,MAAM;AAC/B,UAAI,MAAM,SAAS,OAAW,SAAQ,OAAO,MAAM;AACnD,UAAI,MAAM,eAAe,OAAW,SAAQ,aAAa,MAAM;AAC/D,UAAI,MAAM,qBAAqB,OAAW,SAAQ,mBAAmB,MAAM;AAC3E,UAAI,MAAM,mBAAmB,OAAW,SAAQ,iBAAiB,MAAM;AACvE,UAAI,MAAM,kBAAkB,OAAW,SAAQ,gBAAgB,MAAM;AACrE,UAAI,MAAM,iBAAiB,OAAW,SAAQ,eAAe,MAAM;AACnE,UAAI,MAAM,YAAY,OAAW,SAAQ,UAAU,MAAM;AACzD,UAAI,MAAM,SAAS,OAAW,SAAQ,OAAO,MAAM;AACnD,UAAI,MAAM,eAAe,OAAW,SAAQ,aAAa,MAAM;AAC/D,UAAI,MAAM,aAAa,OAAW,SAAQ,WAAW,MAAM;AAC3D,UAAI,MAAM,eAAe,OAAW,SAAQ,aAAa,MAAM;AAC/D,UAAI,MAAM,iBAAiB,OAAW,SAAQ,eAAe,MAAM;AAAA,IACrE,GAAG,YAAY;AACb,UAAI,MAAM,YAAY;AACpB,cAAM,GAAG,aAAa,kBAAkB,EAAE,WAAW,QAAQ,GAAG,CAAC;AACjE,mBAAW,aAAa,MAAM,YAAY;AACxC,aAAG,QAAQ,GAAG,OAAO,kBAAkB;AAAA,YACrC,WAAW,QAAQ;AAAA,YACnB,iBAAiB,UAAU;AAAA,YAC3B,eAAe,UAAU;AAAA,YACzB,QAAQ;AAAA,UACV,CAAC,CAAC;AAAA,QACJ;AAAA,MACF;AAEA,UAAI,MAAM,SAAS;AACjB,cAAM,GAAG,aAAa,eAAe,EAAE,WAAW,QAAQ,GAAG,CAAC;AAC9D,mBAAW,UAAU,MAAM,SAAS;AAClC,aAAG,QAAQ,GAAG,OAAO,eAAe;AAAA,YAClC,WAAW,QAAQ;AAAA,YACnB,cAAc,OAAO;AAAA,YACrB,YAAY,OAAO;AAAA,YACnB,UAAU,OAAO;AAAA,YACjB,gBAAgB,OAAO;AAAA,YACvB,YAAY,OAAO;AAAA,YACnB,aAAa,OAAO;AAAA,UACtB,CAAC,CAAC;AAAA,QACJ;AAAA,MACF;AAEA,UAAI,MAAM,eAAe;AACvB,cAAM,EAAE,WAAW,IAAI,MAAM,OAAO,sDAAsD;AAC1F,YAAI,MAAM,cAAc,WAAW,GAAG;AACpC,gBAAM,GAAG,aAAa,YAAY;AAAA,YAChC,UAAU;AAAA,YACV,UAAU,QAAQ;AAAA,YAClB,UAAU,MAAM;AAAA,YAChB,gBAAgB,MAAM;AAAA,UACxB,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,GAAG,aAAa,YAAY;AAAA,YAChC,UAAU;AAAA,YACV,UAAU,QAAQ;AAAA,YAClB,UAAU,MAAM;AAAA,YAChB,gBAAgB,MAAM;AAAA,YACtB,IAAI,EAAE,MAAM,MAAM,cAAc;AAAA,UAClC,CAAC;AAAA,QACH;AACA,cAAM;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAI,WAAW;AAGb,gBAAQ,UAAU;AAClB,gBAAQ,SAAS;AACjB,gBAAQ,SAAS,oBAAI,KAAK;AAC1B,YAAI,CAAC,QAAQ,SAAU,SAAQ,WAAW,QAAQ;AAAA,MACpD;AAAA,IACF,CAAC,GAAG,EAAE,aAAa,KAAK,CAAC;AAEzB,UAAM,uBAAuB,IAAI,WAAW;AAAA,MAC1C,WAAW,QAAQ;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AAED,QAAI,WAAW;AACb,YAAM,mBAAmB,MAAM,aAC3B,MAAM,WAAW,IAAI,CAAC,MAAM,EAAE,MAAM,KACnC,uBAAuB,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,eAAe;AAC5D,YAAM,qBAAqB,MAAM,cAAc,QAAQ;AACvD,YAAM,uBAAuB,MAAM,gBAAgB,QAAQ;AAC3D,YAAM,qBAAqB,IAAI,WAAW;AAAA,QACxC,WAAW,QAAQ;AAAA,QACnB,cAAc,MAAM;AAAA,QACpB;AAAA,QACA,cAAc,uBAAuB,WAAW,OAAO;AAAA,QACvD,eAAe,QAAQ,iBAAiB;AAAA,QACxC,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,WAAO,EAAE,IAAI,MAAM,IAAI,QAAQ,GAAG;AAAA,EACpC;AAAA,EACA,MAAM,aAAa,UAAU,SAAS,KAAK;AACzC,UAAM,QAAQ,yBAAyB,MAAM,QAAQ;AACrD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,6BAA6B,IAAI,MAAM,WAAW;AAAA,MACvD,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,UAAU,MAAM;AACxC,UAAM,SAAS,yBAAyB,MAAM,KAAK;AACnD,WAAO;AAAA,MACL,aAAa,OAAO,YAAY,QAAQ,uBAAuB;AAAA,MAC/D,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAS,UAAU,UAAmD;AAAA,UACtE,OAAQ,UAAU,SAAkD;AAAA,QACtE;AAAA,MACF;AAAA,MACA,gBAAgB,UAAU,UAAU;AAAA,MACpC,eAAe,UAAU,SAAS;AAAA,IACpC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAA0D,QAAQ;AAC/E,UAAM,SAAS,MAAM;AACrB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,gCAAgC,IAAI,MAAM;AAAA,EAClD;AACF;AAEA,MAAM,sBAAyH;AAAA,EAC7H,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,mBAAmB,MAAM,QAAQ;AAC/C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,mBAAmB,IAAI,OAAO,MAAM,SAAS;AACpE,UAAM,eAAe,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACtD,WAAW,SAAS;AAAA,MACpB,iBAAiB,MAAM;AAAA,MACvB,WAAW;AAAA,IACb,CAAC;AACD,QAAI,SAAS,iBAAiB,MAAM,UAAU,CAAC,aAAc,OAAM,IAAI,MAAM,eAAe;AAE5F,UAAM,cAAc,wBAAwB,SAAS,IAAI;AACzD,QAAI,YAAY,eAAe,MAAO,OAAM,IAAI,MAAM,4CAA4C;AAElG,UAAM,eAAe,IAAI;AAAA,OACtB,MAAM,cAAc,CAAC,GACnB,IAAI,CAAC,cAAc,UAAU,MAAM,EACnC,OAAO,CAAC,oBAAoB,oBAAoB,MAAM,MAAM;AAAA,IACjE;AAEA,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,qBAAqB,MAAM,GAAG,KAAK,kBAAkB,EAAE,WAAW,SAAS,IAAI,WAAW,KAAK,CAAC;AACtG,UAAI,MAAM,UAAU;AAClB,YAAI,SAAS,iBAAiB,MAAM,OAAQ,cAAa,IAAI,SAAS,YAAY;AAClF,mBAAW,aAAa,oBAAoB;AAC1C,cAAI,UAAU,oBAAoB,MAAM,OAAQ,cAAa,IAAI,UAAU,eAAe;AAAA,QAC5F;AAAA,MACF,WAAW,SAAS,iBAAiB,MAAM,QAAQ;AACjD,qBAAa,IAAI,SAAS,YAAY;AAAA,MACxC,OAAO;AACL,mBAAW,aAAa,oBAAoB;AAC1C,cAAI,UAAU,oBAAoB,MAAM,QAAQ;AAC9C,yBAAa,IAAI,UAAU,eAAe;AAC1C;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,aAAa,SAAS,KAAK,SAAS,iBAAiB,MAAM,QAAQ;AACrE,qBAAa,IAAI,MAAM,MAAM;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,aAAa,SAAS,EAAG,OAAM,IAAI,MAAM,mCAAmC;AAEhF,QAAI,YAAY;AAChB,QAAI,wBAAuC;AAC3C,UAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,YAAM,UAAU,IAAI,OAAO,SAAS;AAAA,QAClC,MAAM,SAAS;AAAA,QACf,YAAY,SAAS,cAAc;AAAA,QACnC,kBAAkB,SAAS;AAAA,QAC3B,gBAAgB,SAAS;AAAA,QACzB,eAAe,SAAS;AAAA,QACxB,cAAc,SAAS;AAAA,QACvB,UAAU,SAAS,YAAY,SAAS;AAAA,QACxC,iBAAiB,SAAS;AAAA,QAC1B,cAAc,MAAM;AAAA,QACpB,SAAS,kBAAkB,SAAS,OAAO;AAAA,QAC3C,MAAM,MAAM;AAAA,QACZ,YAAY,MAAM;AAAA,QAClB,UAAU,SAAS;AAAA,QACnB,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ,oBAAI,KAAK;AAAA,QACjB,cAAc,MAAM;AAAA,QACpB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AACD,YAAM,IAAI,QAAQ,OAAO,EAAE,MAAM;AACjC,iBAAW,mBAAmB,cAAc;AAC1C,YAAI,QAAQ,IAAI,OAAO,kBAAkB;AAAA,UACvC,WAAW,QAAQ;AAAA,UACnB;AAAA,UACA,eAAe;AAAA,UACf,QAAQ;AAAA,QACV,CAAC,CAAC;AAAA,MACJ;AACA,YAAM,IAAI,MAAM;AAChB,UAAI,MAAM,eAAe,QAAQ;AAC/B,cAAM;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,QACR;AAAA,MACF;AACA,UAAI,MAAM,oBAAoB;AAC5B,cAAM;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,QACR;AAAA,MACF;AACA,kBAAY,QAAQ;AACpB,8BAAwB,QAAQ,iBAAiB;AAAA,IACnD,CAAC;AAED,UAAM,qBAAqB,IAAI,WAAW;AAAA,MACxC;AAAA,MACA,cAAc,MAAM;AAAA,MACpB,kBAAkB,MAAM,KAAK,YAAY;AAAA,MACzC,cAAc,MAAM;AAAA,MACpB,eAAe;AAAA,MACf,SAAS,SAAS;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AACD,UAAM,uBAAuB,IAAI,WAAW;AAAA,MAC1C;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AAED,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,eAAe;AAAA,MACf,kBAAkB,MAAM,KAAK,YAAY;AAAA,IAC3C;AAAA,EACF;AAAA,EACA,MAAM,aAAa,QAAQ,QAAQ,KAAK;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,6BAA6B,IAAI,OAAO,EAAE;AAAA,EACnD;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,QAAQ,UAAU,MAAM;AAChD,UAAM,SAAS,mBAAmB,MAAM,KAAK;AAC7C,WAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,oBAAoB;AAAA,MACpB,kBAAkB,OAAO;AAAA,MACzB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAQ,UAAU,SAAkD;AAAA,QACtE;AAAA,MACF;AAAA,MACA,eAAe,UAAU,SAAS;AAAA,IACpC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAA0D,QAAQ;AAC/E,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,IAAI,MAAM,QAAQ,GAAG,CAAC;AAClE,QAAI,CAAC,QAAS;AACd,YAAQ,YAAY,oBAAI,KAAK;AAC7B,UAAM,GAAG,MAAM;AACf,UAAM,gCAAgC,IAAI,WAAW;AAAA,MACnD,WAAW,MAAM,QAAQ;AAAA,MACzB,aAAa,MAAM,QAAQ;AAAA,MAC3B,kBAAkB,MAAM,WAAW,IAAI,CAAC,cAAc,UAAU,eAAe;AAAA,MAC/E,UAAU,MAAM,QAAQ;AAAA,MACxB,gBAAgB,MAAM,QAAQ;AAAA,IAChC,CAAC;AAAA,EACH;AACF;AAEA,MAAM,wBAA2H;AAAA,EAC/H,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,qBAAqB,MAAM,QAAQ;AACjD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,mBAAmB,IAAI,OAAO,MAAM,SAAS;AACpE,UAAM,cAAc,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACrD,WAAW,MAAM;AAAA,MACjB,iBAAiB,MAAM;AAAA,MACvB,WAAW;AAAA,IACb,CAAC;AACD,QAAI,SAAS,iBAAiB,MAAM,UAAU,CAAC,YAAa,OAAM,IAAI,MAAM,eAAe;AAE3F,UAAM,cAAc,wBAAwB,SAAS,IAAI;AACzD,QAAI,YAAY,iBAAiB,MAAO,OAAM,IAAI,MAAM,8CAA8C;AAEtG,UAAM,kBAAkB,MAAM,GAAG,KAAK,eAAe,EAAE,WAAW,MAAM,UAAU,CAAC;AACnF,UAAM,qBAAqB,MAAM,wBAAwB,IAAI;AAAA,MAC3D,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,QAAQ,MAAM;AAAA,IAChB,GAAG,QAAQ;AACX,UAAM,mBAAmB,MAAM,mCAAmC,IAAI;AAAA,MACpE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,QAAQ,MAAM;AAAA,IAChB,GAAG,UAAU,kBAAkB;AAC/B,UAAM,gBAAgB,OAAO,MAAM,SAAS,WACxC,MAAM,OACN,gCAAgC,iBAAiB,MAAM,MAAM,cAAc;AAC/E,QAAI,eAAe;AACnB,QAAI,wBAAuC;AAC3C,UAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,YAAM,aAAa,IAAI,OAAO,SAAS;AAAA,QACrC,MAAM,SAAS;AAAA,QACf,YAAY,SAAS,cAAc;AAAA,QACnC,kBAAkB,SAAS;AAAA,QAC3B,gBAAgB,SAAS;AAAA,QACzB,eAAe,SAAS;AAAA,QACxB,cAAc,SAAS;AAAA,QACvB,UAAU,SAAS,YAAY,SAAS;AAAA,QACxC,iBAAiB,SAAS;AAAA,QAC1B,cAAc,MAAM;AAAA,QACpB,SAAS,QAAQ,SAAS,OAAO;AAAA,QACjC,MAAM;AAAA,QACN,YAAY,SAAS;AAAA,QACrB,UAAU,SAAS;AAAA,QACnB,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ,oBAAI,KAAK;AAAA,QACjB,cAAc,MAAM;AAAA,QACpB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AACD,YAAM,IAAI,QAAQ,UAAU,EAAE,MAAM;AACpC,iBAAW,aAAa,MAAM,YAAY;AACxC,YAAI,QAAQ,IAAI,OAAO,kBAAkB;AAAA,UACvC,WAAW,WAAW;AAAA,UACtB,iBAAiB,UAAU;AAAA,UAC3B,eAAe,UAAU;AAAA,UACzB,QAAQ;AAAA,QACV,CAAC,CAAC;AAAA,MACJ;AACA,iBAAW,OAAO,iBAAiB;AACjC,YAAI,QAAQ,IAAI,OAAO,eAAe;AAAA,UACpC,WAAW,WAAW;AAAA,UACtB,cAAc,IAAI;AAAA,UAClB,YAAY,IAAI;AAAA,UAChB,UAAU,IAAI;AAAA,UACd,gBAAgB,IAAI;AAAA,UACpB,YAAY,IAAI;AAAA,UAChB,aAAa,IAAI;AAAA,UACjB,gBAAgB,IAAI;AAAA,QACtB,CAAC,CAAC;AAAA,MACJ;AACA,YAAM,IAAI,MAAM;AAChB,UAAI,MAAM,uBAAuB,OAAO;AACtC,cAAM;AAAA,UACJ;AAAA,UACA,mBAAmB,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,UACxC,WAAW;AAAA,UACX,MAAM;AAAA,QACR;AAAA,MACF;AACA,qBAAe,WAAW;AAC1B,8BAAwB,WAAW,iBAAiB;AAAA,IACtD,CAAC;AAED,UAAM,qBAAqB,IAAI,WAAW;AAAA,MACxC,WAAW;AAAA,MACX,cAAc,MAAM;AAAA,MACpB,kBAAkB,MAAM,WAAW,IAAI,CAAC,SAAS,KAAK,MAAM;AAAA,MAC5D,cAAc,MAAM;AAAA,MACpB,eAAe;AAAA,MACf,eAAe,SAAS;AAAA,MACxB,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AACD,UAAM,uBAAuB,IAAI,WAAW;AAAA,MAC1C,WAAW;AAAA,MACX,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AAED,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,eAAe;AAAA,MACf,kBAAkB,MAAM,WAAW,IAAI,CAAC,SAAS,KAAK,MAAM;AAAA,IAC9D;AAAA,EACF;AAAA,EACA,MAAM,aAAa,QAAQ,QAAQ,KAAK;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,6BAA6B,IAAI,OAAO,EAAE;AAAA,EACnD;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,QAAQ,UAAU,MAAM;AAChD,UAAM,SAAS,qBAAqB,MAAM,KAAK;AAC/C,WAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,oBAAoB;AAAA,MACpB,kBAAkB,OAAO;AAAA,MACzB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAQ,UAAU,SAAkD;AAAA,QACtE;AAAA,MACF;AAAA,MACA,eAAe,UAAU,SAAS;AAAA,IACpC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAA0D,QAAQ;AAC/E,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,IAAI,MAAM,QAAQ,GAAG,CAAC;AAClE,QAAI,CAAC,QAAS;AACd,YAAQ,YAAY,oBAAI,KAAK;AAC7B,UAAM,GAAG,MAAM;AACf,UAAM,gCAAgC,IAAI,WAAW;AAAA,MACnD,WAAW,MAAM,QAAQ;AAAA,MACzB,aAAa,MAAM,QAAQ;AAAA,MAC3B,kBAAkB,MAAM,WAAW,IAAI,CAAC,cAAc,UAAU,eAAe;AAAA,MAC/E,UAAU,MAAM,QAAQ;AAAA,MACxB,gBAAgB,MAAM,QAAQ;AAAA,IAChC,CAAC;AAAA,EACH;AACF;AAEA,MAAM,wBAA+D;AAAA,EACnE,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,4BAA4B,MAAM,QAAQ;AACxD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,mBAAmB,IAAI,OAAO,MAAM,SAAS;AACnE,UAAM,YAAY,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACnD,WAAW,MAAM;AAAA,MACjB,iBAAiB,MAAM;AAAA,MACvB,WAAW;AAAA,IACb,CAAC;AACD,WAAO;AAAA,MACL,QAAQ;AAAA,QACN,WAAW,QAAQ;AAAA,QACnB,kBAAkB,MAAM,QAAQ,SAAS;AAAA,QACzC,aAAa,WAAW,MAAM;AAAA,QAC9B,iBAAiB,WAAW,UAAU;AAAA,QACtC,oBAAoB,MAAM,WAAW,SAAS;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,4BAA4B,MAAM,QAAQ;AACxD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,mBAAmB,IAAI,OAAO,MAAM,SAAS;AACnE,UAAM,YAAY,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACnD,WAAW,MAAM;AAAA,MACjB,iBAAiB,MAAM;AAAA,MACvB,WAAW;AAAA,IACb,CAAC;AACD,QAAI,WAAW;AACb,gBAAU,SAAS;AACnB,gBAAU,YAAY,oBAAI,KAAK;AAC/B,YAAM,GAAG,MAAM;AACf,YAAM,wBAAwB,IAAI,WAAW;AAAA,QAC3C,WAAW,MAAM;AAAA,QACjB,aAAa,MAAM;AAAA,QACnB,QAAQ;AAAA,QACR,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AACD,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AACA,QAAI,QAAQ,iBAAiB,MAAM,QAAQ;AACzC,YAAM,gBAAgB,MAAM,GAAG,KAAK,kBAAkB,EAAE,WAAW,MAAM,UAAU,CAAC;AACpF,cAAQ,YAAY,oBAAI,KAAK;AAC7B,YAAM,GAAG,MAAM;AACf,YAAM,mBAAmB,cACtB,IAAI,CAAC,QAAQ,IAAI,eAAe,EAChC,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AACnF,YAAM,gCAAgC,IAAI,WAAW;AAAA,QACnD,WAAW,MAAM;AAAA,QACjB,aAAa,MAAM;AAAA,QACnB;AAAA,QACA,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AACD,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AACA,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AAAA,EACA,MAAM,aAAa,UAAU,SAAS,KAAK;AACzC,UAAM,QAAQ,4BAA4B,MAAM,QAAQ;AACxD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,IAAI,MAAM,WAAW,UAAU,MAAM,SAAS,CAAC;AAC3F,UAAM,YAAY,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACnD,WAAW,MAAM;AAAA,MACjB,iBAAiB,MAAM;AAAA,IACzB,CAAC;AACD,WAAO;AAAA,MACL,WAAW,MAAM;AAAA,MACjB,kBAAkB,MAAM,SAAS,SAAS;AAAA,MAC1C,aAAa,WAAW,MAAM;AAAA,MAC9B,iBAAiB,WAAW,UAAU;AAAA,MACtC,oBAAoB,MAAM,WAAW,SAAS;AAAA,IAChD;AAAA,EACF;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,UAAU,MAAM;AACxC,UAAM,SAAS,4BAA4B,MAAM,KAAK;AACtD,WAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAS,UAAU,UAAiD;AAAA,UACpE,OAAQ,UAAU,SAAgD;AAAA,QACpE;AAAA,MACF;AAAA,MACA,gBAAgB,UAAU,UAAU;AAAA,MACpC,eAAe,UAAU,SAAS;AAAA,IACpC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAAwD,QAAQ;AAC7E,UAAM,SAAS,MAAM;AACrB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,IAAI,OAAO,UAAU,CAAC;AAClE,QAAI,SAAS;AACX,cAAQ,YAAY,OAAO,OAAO,gBAAgB;AAAA,IACpD;AACA,QAAI,OAAO,aAAa;AACtB,YAAM,YAAY,MAAM,GAAG,QAAQ,kBAAkB,EAAE,IAAI,OAAO,YAAY,CAAC;AAC/E,UAAI,WAAW;AACb,kBAAU,SAAU,OAAO,mBAAmB;AAC9C,kBAAU,YAAY,OAAO,OAAO,kBAAkB;AAAA,MACxD;AAAA,IACF;AACA,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,gBAAgB,qBAAqB;AACrC,gBAAgB,kBAAkB;AAClC,gBAAgB,mBAAmB;AACnC,gBAAgB,qBAAqB;AACrC,gBAAgB,qBAAqB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveAttachmentScopePair } from "@open-mercato/core/modules/attachments/lib/scope";
|
|
1
2
|
import { MESSAGE_ATTACHMENT_ENTITY_ID } from "./constants.js";
|
|
2
3
|
const LIBRARY_ATTACHMENT_ENTITY_ID = "attachments:library";
|
|
3
4
|
function buildOrganizationScopeFilter(organizationId) {
|
|
@@ -74,16 +75,15 @@ async function linkLibraryAttachmentsToMessage(em, messageId, sourceRecordId, or
|
|
|
74
75
|
}
|
|
75
76
|
await em.flush();
|
|
76
77
|
}
|
|
77
|
-
async function copyAttachmentsForForward(em, sourceMessageId, targetMessageId,
|
|
78
|
+
async function copyAttachmentsForForward(em, sourceMessageId, targetMessageId, tenantId) {
|
|
78
79
|
return copyAttachmentsForForwardMessages(
|
|
79
80
|
em,
|
|
80
81
|
[sourceMessageId],
|
|
81
82
|
targetMessageId,
|
|
82
|
-
organizationId,
|
|
83
83
|
tenantId
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
|
-
async function copyAttachmentsForForwardMessages(em, sourceMessageIds, targetMessageId,
|
|
86
|
+
async function copyAttachmentsForForwardMessages(em, sourceMessageIds, targetMessageId, tenantId) {
|
|
87
87
|
if (sourceMessageIds.length === 0) return 0;
|
|
88
88
|
const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
|
|
89
89
|
const sourceAttachments = await em.find(Attachment, {
|
|
@@ -100,12 +100,15 @@ async function copyAttachmentsForForwardMessages(em, sourceMessageIds, targetMes
|
|
|
100
100
|
dedupedById.set(sourceAttachment.id, sourceAttachment);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
let copied = 0;
|
|
103
104
|
for (const sourceAttachment of dedupedById.values()) {
|
|
105
|
+
const scope = resolveAttachmentScopePair(sourceAttachment);
|
|
106
|
+
if (!scope) continue;
|
|
104
107
|
const copy = em.create(Attachment, {
|
|
105
108
|
entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
|
|
106
109
|
recordId: targetMessageId,
|
|
107
|
-
organizationId:
|
|
108
|
-
tenantId,
|
|
110
|
+
organizationId: scope.organizationId,
|
|
111
|
+
tenantId: scope.tenantId,
|
|
109
112
|
fileName: sourceAttachment.fileName,
|
|
110
113
|
mimeType: sourceAttachment.mimeType,
|
|
111
114
|
fileSize: sourceAttachment.fileSize,
|
|
@@ -116,9 +119,12 @@ async function copyAttachmentsForForwardMessages(em, sourceMessageIds, targetMes
|
|
|
116
119
|
partitionCode: sourceAttachment.partitionCode
|
|
117
120
|
});
|
|
118
121
|
em.persist(copy);
|
|
122
|
+
copied += 1;
|
|
119
123
|
}
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
if (copied > 0) {
|
|
125
|
+
await em.flush();
|
|
126
|
+
}
|
|
127
|
+
return copied;
|
|
122
128
|
}
|
|
123
129
|
export {
|
|
124
130
|
copyAttachmentsForForward,
|