@open-mercato/core 0.6.6-develop.6385.1.9a81faa5f0 → 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.
@@ -1,4 +1,4 @@
1
- [build:core] found 3437 entry points
1
+ [build:core] found 3438 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 186 entry points
4
4
  [build:core:generated] built successfully
@@ -0,0 +1,26 @@
1
+ function normalizeScopeValue(value) {
2
+ if (typeof value !== "string") return value ?? null;
3
+ const trimmed = value.trim();
4
+ return trimmed.length > 0 ? trimmed : null;
5
+ }
6
+ function isValidAttachmentScopePair(candidate) {
7
+ const organizationId = normalizeScopeValue(candidate.organizationId);
8
+ const tenantId = normalizeScopeValue(candidate.tenantId);
9
+ return organizationId === null === (tenantId === null);
10
+ }
11
+ function resolveAttachmentScopePair(...candidates) {
12
+ for (const candidate of candidates) {
13
+ if (!candidate) continue;
14
+ const organizationId = normalizeScopeValue(candidate.organizationId);
15
+ const tenantId = normalizeScopeValue(candidate.tenantId);
16
+ if (organizationId === null === (tenantId === null)) {
17
+ return { organizationId, tenantId };
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+ export {
23
+ isValidAttachmentScopePair,
24
+ resolveAttachmentScopePair
25
+ };
26
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/attachments/lib/scope.ts"],
4
+ "sourcesContent": ["export type AttachmentScopePair = {\n organizationId: string | null\n tenantId: string | null\n}\n\nexport type AttachmentScopeCandidate = {\n organizationId?: string | null\n tenantId?: string | null\n}\n\nfunction normalizeScopeValue(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return value ?? null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nexport function isValidAttachmentScopePair(candidate: AttachmentScopeCandidate): boolean {\n const organizationId = normalizeScopeValue(candidate.organizationId)\n const tenantId = normalizeScopeValue(candidate.tenantId)\n return (organizationId === null) === (tenantId === null)\n}\n\n/**\n * Returns the first candidate whose tenant/organization columns form a valid\n * \"both set or both null\" scope pair (blank/whitespace treated as null), or\n * `null` when no candidate satisfies the invariant. Copy/clone sites use this\n * to carry a scope pair across as a unit instead of coalescing the two columns\n * independently, which is what allows a partial-null row to be constructed.\n */\nexport function resolveAttachmentScopePair(\n ...candidates: Array<AttachmentScopeCandidate | null | undefined>\n): AttachmentScopePair | null {\n for (const candidate of candidates) {\n if (!candidate) continue\n const organizationId = normalizeScopeValue(candidate.organizationId)\n const tenantId = normalizeScopeValue(candidate.tenantId)\n if ((organizationId === null) === (tenantId === null)) {\n return { organizationId, tenantId }\n }\n }\n return null\n}\n"],
5
+ "mappings": "AAUA,SAAS,oBAAoB,OAAiD;AAC5E,MAAI,OAAO,UAAU,SAAU,QAAO,SAAS;AAC/C,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEO,SAAS,2BAA2B,WAA8C;AACvF,QAAM,iBAAiB,oBAAoB,UAAU,cAAc;AACnE,QAAM,WAAW,oBAAoB,UAAU,QAAQ;AACvD,SAAQ,mBAAmB,UAAW,aAAa;AACrD;AASO,SAAS,8BACX,YACyB;AAC5B,aAAW,aAAa,YAAY;AAClC,QAAI,CAAC,UAAW;AAChB,UAAM,iBAAiB,oBAAoB,UAAU,cAAc;AACnE,UAAM,WAAW,oBAAoB,UAAU,QAAQ;AACvD,QAAK,mBAAmB,UAAW,aAAa,OAAO;AACrD,aAAO,EAAE,gBAAgB,SAAS;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -16,6 +16,7 @@ import {
16
16
  CatalogPriceKind
17
17
  } from "../data/entities.js";
18
18
  import { Attachment } from "@open-mercato/core/modules/attachments/data/entities";
19
+ import { resolveAttachmentScopePair } from "@open-mercato/core/modules/attachments/lib/scope";
19
20
  import {
20
21
  variantCreateSchema,
21
22
  variantUpdateSchema
@@ -411,11 +412,12 @@ async function aggregateVariantMediaToProduct(em, variant) {
411
412
  for (const source of attachments) {
412
413
  const key = buildKey(source);
413
414
  if (existingKeys.has(key)) continue;
415
+ const scope = resolveAttachmentScopePair(source, variant) ?? { organizationId: null, tenantId: null };
414
416
  const clone = em.create(Attachment, {
415
417
  entityId: E.catalog.catalog_product,
416
418
  recordId: productId,
417
- organizationId: source.organizationId ?? variant.organizationId ?? null,
418
- tenantId: source.tenantId ?? variant.tenantId ?? null,
419
+ organizationId: scope.organizationId,
420
+ tenantId: scope.tenantId,
419
421
  partitionCode: source.partitionCode,
420
422
  fileName: source.fileName,
421
423
  mimeType: source.mimeType,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/catalog/commands/variants.ts"],
4
- "sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, parseWithCustomFields, setCustomFieldsIfAny, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { UniqueConstraintViolationException } from '@mikro-orm/core'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { loadCustomFieldSnapshot, buildCustomFieldResetMap } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveRedoSnapshot, restoreCreatedRow } from '@open-mercato/shared/lib/commands/redo'\nimport { E } from '#generated/entities.ids.generated'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n CatalogProductVariant,\n CatalogProductPrice,\n CatalogProduct,\n CatalogOffer,\n CatalogPriceKind,\n} from '../data/entities'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n variantCreateSchema,\n variantUpdateSchema,\n type VariantCreateInput,\n type VariantUpdateInput,\n} from '../data/validators'\nimport { isValidGtin, normalizeGtinValue } from '../lib/gtin'\nimport type { CatalogGtinType } from '../data/types'\nimport {\n cloneJson,\n commandActorScope,\n ensureOrganizationScope,\n ensureTenantScope,\n emitCatalogQueryIndexEvent,\n extractUndoPayload,\n requireProduct,\n toNumericString,\n getErrorConstraint,\n getErrorMessage,\n} from './shared'\nimport { SalesTaxRate } from '@open-mercato/core/modules/sales/data/entities'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\n\nconst variantCrudEvents: CrudEventsConfig = {\n module: 'catalog',\n entity: 'variant',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype VariantSnapshot = {\n id: string\n productId: string\n organizationId: string\n tenantId: string\n name: string | null\n sku: string | null\n barcode: string | null\n gtinType: CatalogGtinType | null\n hsCode: string | null\n statusEntryId: string | null\n isDefault: boolean\n isActive: boolean\n weightValue: string | null\n weightUnit: string | null\n taxRateId: string | null\n taxRate: string | null\n dimensions: Record<string, unknown> | null\n metadata: Record<string, unknown> | null\n optionValues: Record<string, string> | null\n customFieldsetCode: string | null\n createdAt: string\n updatedAt: string\n custom: Record<string, unknown> | null\n prices?: VariantPriceSnapshot[] | null\n}\n\ntype VariantUndoPayload = {\n before?: VariantSnapshot | null\n after?: VariantSnapshot | null\n previousDefaultVariantId?: string | null\n}\n\nconst VARIANT_CHANGE_KEYS = [\n 'name',\n 'sku',\n 'barcode',\n 'gtinType',\n 'hsCode',\n 'statusEntryId',\n 'isDefault',\n 'isActive',\n 'weightValue',\n 'weightUnit',\n 'taxRateId',\n 'taxRate',\n 'dimensions',\n 'optionValues',\n 'customFieldsetCode',\n 'metadata',\n] as const satisfies readonly string[]\n\nasync function loadVariantSnapshot(\n em: EntityManager,\n id: string,\n options: { includePrices?: boolean } = {}\n): Promise<VariantSnapshot | null> {\n const record = await em.findOne(CatalogProductVariant, { id, deletedAt: null })\n if (!record) return null\n const prices = options.includePrices\n ? await loadVariantPriceSnapshots(em, record.id, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n : null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n const productId = typeof record.product === 'string' ? record.product : record.product.id\n return {\n id: record.id,\n productId,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n name: record.name ?? null,\n sku: record.sku ?? null,\n barcode: record.barcode ?? null,\n gtinType: record.gtinType ?? null,\n hsCode: record.hsCode ?? null,\n statusEntryId: record.statusEntryId ?? null,\n isDefault: record.isDefault,\n isActive: record.isActive,\n weightValue: record.weightValue ?? null,\n weightUnit: record.weightUnit ?? null,\n taxRateId: record.taxRateId ?? null,\n taxRate: record.taxRate ?? null,\n dimensions: record.dimensions ? cloneJson(record.dimensions) : null,\n metadata: record.metadata ? cloneJson(record.metadata) : null,\n optionValues: record.optionValues ? cloneJson(record.optionValues) : null,\n customFieldsetCode: record.customFieldsetCode ?? null,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n custom: Object.keys(custom).length ? custom : null,\n prices: prices && prices.length ? prices : null,\n }\n}\n\nfunction variantSeedFromSnapshot(snapshot: VariantSnapshot): Record<string, unknown> {\n return {\n id: snapshot.id,\n product: snapshot.productId,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n name: snapshot.name ?? null,\n sku: snapshot.sku ?? null,\n barcode: snapshot.barcode ?? null,\n gtinType: snapshot.gtinType ?? null,\n hsCode: snapshot.hsCode ?? null,\n statusEntryId: snapshot.statusEntryId ?? null,\n isDefault: snapshot.isDefault,\n isActive: snapshot.isActive,\n weightValue: snapshot.weightValue ?? null,\n weightUnit: snapshot.weightUnit ?? null,\n taxRateId: snapshot.taxRateId ?? null,\n taxRate: snapshot.taxRate ?? null,\n dimensions: snapshot.dimensions ? cloneJson(snapshot.dimensions) : null,\n metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,\n optionValues: snapshot.optionValues ? cloneJson(snapshot.optionValues) : null,\n customFieldsetCode: snapshot.customFieldsetCode ?? null,\n createdAt: new Date(snapshot.createdAt),\n updatedAt: new Date(snapshot.updatedAt),\n }\n}\n\nfunction applyVariantSnapshot(record: CatalogProductVariant, snapshot: VariantSnapshot): void {\n record.organizationId = snapshot.organizationId\n record.tenantId = snapshot.tenantId\n record.name = snapshot.name ?? null\n record.sku = snapshot.sku ?? null\n record.barcode = snapshot.barcode ?? null\n record.gtinType = snapshot.gtinType ?? null\n record.hsCode = snapshot.hsCode ?? null\n record.statusEntryId = snapshot.statusEntryId ?? null\n record.isDefault = snapshot.isDefault\n record.isActive = snapshot.isActive\n record.weightValue = snapshot.weightValue ?? null\n record.weightUnit = snapshot.weightUnit ?? null\n record.taxRateId = snapshot.taxRateId ?? null\n record.taxRate = snapshot.taxRate ?? null\n record.dimensions = snapshot.dimensions ? cloneJson(snapshot.dimensions) : null\n record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null\n record.optionValues = snapshot.optionValues ? cloneJson(snapshot.optionValues) : null\n record.customFieldsetCode = snapshot.customFieldsetCode ?? null\n record.createdAt = new Date(snapshot.createdAt)\n record.updatedAt = new Date(snapshot.updatedAt)\n}\n\nasync function resolveVariantTaxRate(\n em: EntityManager,\n product: CatalogProduct,\n taxRateIdInput: string | null | undefined,\n taxRateInput: number | string | null | undefined\n): Promise<{ taxRateId: string | null; taxRate: string | null }> {\n const organizationId = product.organizationId\n const tenantId = product.tenantId\n const normalizedRate =\n taxRateInput === null || taxRateInput === undefined\n ? null\n : (() => {\n const numeric = typeof taxRateInput === 'string' ? Number(taxRateInput) : taxRateInput\n return Number.isFinite(numeric) ? toNumericString(numeric) : null\n })()\n if (taxRateIdInput === null) {\n return { taxRateId: product.taxRateId ?? null, taxRate: product.taxRate ?? null }\n }\n if (!taxRateIdInput) {\n return { taxRateId: product.taxRateId ?? null, taxRate: product.taxRate ?? normalizedRate }\n }\n const record = await em.findOne(SalesTaxRate, {\n id: taxRateIdInput,\n organizationId,\n tenantId,\n deletedAt: null,\n })\n if (!record) {\n throw new CrudHttpError(400, { error: 'Tax class not found' })\n }\n return { taxRateId: taxRateIdInput, taxRate: record.rate ?? normalizedRate }\n}\n\ntype VariantPriceSnapshot = {\n id: string\n variantId: string | null\n productId: string | null\n offerId: string | null\n organizationId: string\n tenantId: string\n priceKindId: string\n priceKindCode: string\n currencyCode: string\n kind: string\n minQuantity: number\n maxQuantity: number | null\n unitPriceNet: string | null\n unitPriceGross: string | null\n taxRate: string | null\n taxAmount: string | null\n channelId: string | null\n userId: string | null\n userGroupId: string | null\n customerId: string | null\n customerGroupId: string | null\n metadata: Record<string, unknown> | null\n startsAt: string | null\n endsAt: string | null\n createdAt: string\n updatedAt: string\n custom: Record<string, unknown> | null\n}\n\nasync function loadVariantPriceSnapshots(\n em: EntityManager,\n variantId: string,\n scope: { tenantId: string; organizationId: string }\n): Promise<VariantPriceSnapshot[]> {\n const prices = await findWithDecryption(\n em,\n CatalogProductPrice,\n { variant: variantId, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { populate: ['priceKind', 'product', 'offer'] },\n { tenantId: scope.tenantId, organizationId: scope.organizationId },\n )\n const snapshots: VariantPriceSnapshot[] = []\n for (const price of prices) {\n const variantRef = price.variant\n const variantIdValue =\n typeof variantRef === 'string'\n ? variantRef\n : variantRef\n ? variantRef.id\n : null\n const productRef = price.product\n ? price.product\n : typeof price.variant === 'object' && price.variant\n ? price.variant.product\n : null\n const productId =\n typeof productRef === 'string'\n ? productRef\n : productRef\n ? productRef.id\n : null\n const priceKindRef = price.priceKind\n const priceKindId =\n typeof priceKindRef === 'string'\n ? priceKindRef\n : priceKindRef\n ? priceKindRef.id\n : null\n if (!priceKindId) {\n throw new CrudHttpError(400, { error: 'Price is missing price kind metadata.' })\n }\n const priceKindCode =\n typeof priceKindRef === 'object' && priceKindRef ? priceKindRef.code : price.kind\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.catalog.catalog_product_price,\n recordId: price.id,\n tenantId: price.tenantId,\n organizationId: price.organizationId,\n })\n snapshots.push({\n id: price.id,\n variantId: variantIdValue,\n productId,\n offerId: typeof price.offer === 'string' ? price.offer : price.offer ? price.offer.id : null,\n organizationId: price.organizationId,\n tenantId: price.tenantId,\n priceKindId,\n priceKindCode,\n currencyCode: price.currencyCode,\n kind: price.kind,\n minQuantity: price.minQuantity,\n maxQuantity: price.maxQuantity ?? null,\n unitPriceNet: price.unitPriceNet ?? null,\n unitPriceGross: price.unitPriceGross ?? null,\n taxRate: price.taxRate ?? null,\n taxAmount: price.taxAmount ?? null,\n channelId: price.channelId ?? null,\n userId: price.userId ?? null,\n userGroupId: price.userGroupId ?? null,\n customerId: price.customerId ?? null,\n customerGroupId: price.customerGroupId ?? null,\n metadata: price.metadata ? cloneJson(price.metadata) : null,\n startsAt: price.startsAt ? price.startsAt.toISOString() : null,\n endsAt: price.endsAt ? price.endsAt.toISOString() : null,\n createdAt: price.createdAt.toISOString(),\n updatedAt: price.updatedAt.toISOString(),\n custom: Object.keys(custom).length ? custom : null,\n })\n }\n return snapshots\n}\n\nasync function restoreVariantPricesFromSnapshots(\n em: EntityManager,\n variant: CatalogProductVariant,\n snapshots: VariantPriceSnapshot[],\n dataEngine: DataEngine\n): Promise<void> {\n if (!snapshots.length) return\n const productRef =\n typeof variant.product === 'string'\n ? await requireProduct(em, variant.product, {\n tenantId: variant.tenantId,\n organizationId: variant.organizationId,\n })\n : variant.product\n for (const snapshot of snapshots) {\n const product =\n snapshot.productId && snapshot.productId !== productRef.id\n ? em.getReference(CatalogProduct, snapshot.productId)\n : productRef\n const offer = snapshot.offerId ? em.getReference(CatalogOffer, snapshot.offerId) : null\n const priceKind = em.getReference(CatalogPriceKind, snapshot.priceKindId)\n let record = await em.findOne(CatalogProductPrice, { id: snapshot.id })\n if (!record) {\n record = em.create(CatalogProductPrice, {\n id: snapshot.id,\n variant,\n product,\n offer,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n currencyCode: snapshot.currencyCode,\n priceKind,\n kind: snapshot.priceKindCode || snapshot.kind,\n minQuantity: snapshot.minQuantity,\n maxQuantity: snapshot.maxQuantity ?? null,\n unitPriceNet: snapshot.unitPriceNet ?? null,\n unitPriceGross: snapshot.unitPriceGross ?? null,\n taxRate: snapshot.taxRate ?? null,\n taxAmount: snapshot.taxAmount ?? null,\n channelId: snapshot.channelId ?? null,\n userId: snapshot.userId ?? null,\n userGroupId: snapshot.userGroupId ?? null,\n customerId: snapshot.customerId ?? null,\n customerGroupId: snapshot.customerGroupId ?? null,\n metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,\n startsAt: snapshot.startsAt ? new Date(snapshot.startsAt) : null,\n endsAt: snapshot.endsAt ? new Date(snapshot.endsAt) : null,\n createdAt: new Date(snapshot.createdAt),\n updatedAt: new Date(snapshot.updatedAt),\n })\n em.persist(record)\n } else {\n record.variant = variant\n record.product = product\n record.offer = offer\n record.priceKind = priceKind\n record.organizationId = snapshot.organizationId\n record.tenantId = snapshot.tenantId\n record.currencyCode = snapshot.currencyCode\n record.kind = snapshot.priceKindCode || snapshot.kind\n record.minQuantity = snapshot.minQuantity\n record.maxQuantity = snapshot.maxQuantity ?? null\n record.unitPriceNet = snapshot.unitPriceNet ?? null\n record.unitPriceGross = snapshot.unitPriceGross ?? null\n record.taxRate = snapshot.taxRate ?? null\n record.taxAmount = snapshot.taxAmount ?? null\n record.channelId = snapshot.channelId ?? null\n record.userId = snapshot.userId ?? null\n record.userGroupId = snapshot.userGroupId ?? null\n record.customerId = snapshot.customerId ?? null\n record.customerGroupId = snapshot.customerGroupId ?? null\n record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null\n record.startsAt = snapshot.startsAt ? new Date(snapshot.startsAt) : null\n record.endsAt = snapshot.endsAt ? new Date(snapshot.endsAt) : null\n record.createdAt = new Date(snapshot.createdAt)\n record.updatedAt = new Date(snapshot.updatedAt)\n }\n }\n await em.flush()\n for (const snapshot of snapshots) {\n if (!snapshot.custom || !Object.keys(snapshot.custom).length) continue\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.catalog.catalog_product_price,\n recordId: snapshot.id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n values: snapshot.custom,\n })\n }\n}\n\ntype MetadataSplitResult = {\n metadata: Record<string, unknown> | null\n optionValues: Record<string, string> | null\n hadOptionValues: boolean\n}\n\nfunction splitOptionValuesFromMetadata(\n metadata?: Record<string, unknown> | null\n): MetadataSplitResult {\n if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {\n return {\n metadata: metadata ? cloneJson(metadata) : null,\n optionValues: null,\n hadOptionValues: false,\n }\n }\n const { optionValues, ...rest } = metadata as Record<string, unknown> & {\n optionValues?: unknown\n }\n const normalizedMetadata = Object.keys(rest).length ? cloneJson(rest) : null\n return {\n metadata: normalizedMetadata,\n optionValues: normalizeOptionValues(optionValues),\n hadOptionValues: optionValues !== undefined,\n }\n}\n\nfunction normalizeOptionValues(input: unknown): Record<string, string> | null {\n if (!input || typeof input !== 'object' || Array.isArray(input)) return null\n const normalized: Record<string, string> = {}\n for (const [rawKey, rawValue] of Object.entries(input)) {\n if (typeof rawValue !== 'string') continue\n const key = rawKey.trim()\n const value = rawValue.trim()\n if (!key || !value) continue\n normalized[key] = value\n }\n return Object.keys(normalized).length ? normalized : null\n}\n\nfunction resolveProductId(record: CatalogProductVariant): string {\n return typeof record.product === 'string' ? record.product : record.product.id\n}\n\nasync function enforceSingleDefaultVariant(\n em: EntityManager,\n variant: CatalogProductVariant\n): Promise<string | null> {\n if (!variant.isDefault) return null\n const productId = resolveProductId(variant)\n const existingDefault = await em.findOne(\n CatalogProductVariant,\n { product: productId, isDefault: true, deletedAt: null, id: { $ne: variant.id } },\n { fields: ['id', 'isDefault'] }\n )\n if (existingDefault) {\n existingDefault.isDefault = false\n return existingDefault.id\n }\n return null\n}\n\nasync function aggregateVariantMediaToProduct(\n em: EntityManager,\n variant: CatalogProductVariant\n): Promise<void> {\n const productId = resolveProductId(variant)\n const buildKey = (\n attachment: Pick<Attachment, 'fileName' | 'fileSize' | 'storageDriver' | 'partitionCode' | 'storagePath'>\n ) =>\n [\n attachment.fileName?.trim() ?? '',\n attachment.fileSize ?? '',\n attachment.storageDriver ?? '',\n attachment.partitionCode ?? '',\n ].join('|')\n const attachments = await em.find(\n Attachment,\n {\n entityId: E.catalog.catalog_product_variant,\n recordId: variant.id,\n organizationId: variant.organizationId ?? undefined,\n tenantId: variant.tenantId ?? undefined,\n },\n {\n fields: [\n 'id',\n 'partitionCode',\n 'fileName',\n 'mimeType',\n 'fileSize',\n 'storageDriver',\n 'storagePath',\n 'storageMetadata',\n 'url',\n 'organizationId',\n 'tenantId',\n 'fileSize',\n 'storageDriver',\n 'partitionCode',\n ],\n }\n )\n if (!attachments.length) return\n const existing = await em.find(\n Attachment,\n {\n entityId: E.catalog.catalog_product,\n recordId: productId,\n organizationId: variant.organizationId ?? undefined,\n tenantId: variant.tenantId ?? undefined,\n },\n {\n fields: ['storagePath', 'fileName', 'fileSize', 'storageDriver', 'partitionCode'],\n }\n )\n const existingKeys = new Set(existing.map((item) => buildKey(item)))\n let created = 0\n for (const source of attachments) {\n const key = buildKey(source)\n if (existingKeys.has(key)) continue\n const clone = em.create(Attachment, {\n entityId: E.catalog.catalog_product,\n recordId: productId,\n organizationId: source.organizationId ?? variant.organizationId ?? null,\n tenantId: source.tenantId ?? variant.tenantId ?? null,\n partitionCode: source.partitionCode,\n fileName: source.fileName,\n mimeType: source.mimeType,\n fileSize: source.fileSize,\n storageDriver: source.storageDriver,\n storagePath: source.storagePath,\n storageMetadata: source.storageMetadata ? cloneJson(source.storageMetadata) : null,\n url: source.url,\n })\n em.persist(clone)\n existingKeys.add(key)\n created += 1\n }\n if (created > 0) {\n await em.flush()\n }\n}\n\nconst createVariantCommand: CommandHandler<VariantCreateInput, { variantId: string; previousDefaultVariantId?: string | null }> = {\n id: 'catalog.variants.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(variantCreateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const product = await requireProduct(em, parsed.productId, commandActorScope(ctx))\n ensureTenantScope(ctx, product.tenantId)\n ensureOrganizationScope(ctx, product.organizationId)\n const { taxRateId, taxRate } = await resolveVariantTaxRate(\n em,\n product,\n parsed.taxRateId ?? null,\n parsed.taxRate\n )\n\n const metadataSplit = splitOptionValuesFromMetadata(parsed.metadata)\n const resolvedOptionValues =\n parsed.optionValues ?? (metadataSplit.hadOptionValues ? metadataSplit.optionValues : null)\n\n const gtinType = parsed.gtinType ?? null\n const barcode =\n gtinType && parsed.barcode\n ? normalizeGtinValue(gtinType, parsed.barcode)\n : (parsed.barcode ?? null)\n\n const now = new Date()\n const record = em.create(CatalogProductVariant, {\n organizationId: product.organizationId,\n tenantId: product.tenantId,\n product,\n name: parsed.name ?? null,\n sku: parsed.sku ?? null,\n barcode,\n gtinType,\n hsCode: parsed.hsCode ?? null,\n statusEntryId: parsed.statusEntryId ?? null,\n isDefault: parsed.isDefault ?? false,\n isActive: parsed.isActive ?? true,\n weightValue: toNumericString(parsed.weightValue),\n weightUnit: parsed.weightUnit ?? null,\n taxRateId,\n taxRate,\n dimensions: parsed.dimensions ? cloneJson(parsed.dimensions) : null,\n metadata: metadataSplit.metadata,\n optionValues: resolvedOptionValues ? cloneJson(resolvedOptionValues) : null,\n customFieldsetCode: parsed.customFieldsetCode ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n let previousDefaultVariantId: string | null = null\n try {\n await withAtomicFlush(\n em,\n [\n () => em.flush(),\n async () => {\n if (record.isDefault) {\n previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)\n await em.flush()\n }\n },\n () => aggregateVariantMediaToProduct(em, record),\n ],\n { transaction: true }\n )\n } catch (error) {\n await rethrowVariantUniqueConstraint(error)\n }\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n action: 'created',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: record.id, previousDefaultVariantId }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadVariantSnapshot(em, result.variantId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as VariantSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.variants.create', 'Create product variant'),\n resourceKind: 'catalog.variant',\n resourceId: result.variantId,\n parentResourceKind: 'catalog.product',\n parentResourceId: after.productId ?? null,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: {\n after,\n previousDefaultVariantId: (result as { previousDefaultVariantId?: string | null })?.previousDefaultVariantId ?? null,\n } satisfies VariantUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<VariantUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductVariant, { id: after.id })\n if (!record) return\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n em.remove(record)\n await em.flush()\n if (payload?.previousDefaultVariantId) {\n const previousDefault = await em.findOne(CatalogProductVariant, { id: payload.previousDefaultVariantId })\n if (previousDefault) {\n ensureTenantScope(ctx, previousDefault.tenantId)\n ensureOrganizationScope(ctx, previousDefault.organizationId)\n previousDefault.isDefault = true\n await em.flush()\n }\n }\n const resetValues = buildCustomFieldResetMap(undefined, after.custom ?? undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: after.id,\n organizationId: after.organizationId,\n tenantId: after.tenantId,\n values: resetValues,\n })\n }\n },\n redo: async ({ ctx, logEntry }) => {\n const after = resolveRedoSnapshot<VariantSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for variant create' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await restoreCreatedRow(\n em,\n CatalogProductVariant,\n after.id,\n () => variantSeedFromSnapshot(after),\n )\n applyVariantSnapshot(record, after)\n let previousDefaultVariantId: string | null = null\n try {\n await withAtomicFlush(\n em,\n [\n () => em.flush(),\n async () => {\n if (record.isDefault) {\n previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)\n await em.flush()\n }\n },\n () => aggregateVariantMediaToProduct(em, record),\n ],\n { transaction: true }\n )\n } catch (error) {\n await rethrowVariantUniqueConstraint(error)\n }\n if (after.custom && Object.keys(after.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: after.custom,\n })\n }\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n action: 'created',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: record.id, previousDefaultVariantId }\n },\n}\n\nconst updateVariantCommand: CommandHandler<VariantUpdateInput, { variantId: string; previousDefaultVariantId?: string | null }> = {\n id: 'catalog.variants.update',\n async prepare(input, ctx) {\n const id = requireId(input, 'Variant id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadVariantSnapshot(em, id)\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(variantUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductVariant, { id: parsed.id, deletedAt: null })\n if (!record) throw new CrudHttpError(404, { error: 'Catalog variant not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n const product = await requireProduct(em, record.product.id, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n\n if (!product) throw new CrudHttpError(400, { error: 'Variant product missing' })\n\n const taxRateProvided = parsed.taxRateId !== undefined || parsed.taxRate !== undefined\n const resolvedTaxRate = taxRateProvided\n ? await resolveVariantTaxRate(em, product, parsed.taxRateId ?? null, parsed.taxRate)\n : null\n\n // Re-validate GTIN against the merged record state: a partial update may\n // carry only one of (gtinType, barcode) while the other half is stored.\n let normalizedGtinBarcode: string | null = null\n if (parsed.gtinType !== undefined || parsed.barcode !== undefined) {\n const effectiveGtinType =\n parsed.gtinType !== undefined ? (parsed.gtinType ?? null) : (record.gtinType ?? null)\n if (effectiveGtinType) {\n const effectiveBarcode =\n parsed.barcode !== undefined ? (parsed.barcode ?? null) : (record.barcode ?? null)\n if (!effectiveBarcode || !effectiveBarcode.trim().length) {\n await throwGtinBarcodeRequiredError()\n } else {\n const normalized = normalizeGtinValue(effectiveGtinType, effectiveBarcode)\n if (!isValidGtin(effectiveGtinType, normalized)) {\n await throwInvalidGtinError()\n }\n normalizedGtinBarcode = normalized\n }\n }\n }\n\n let previousDefaultVariantId: string | null = null\n try {\n await withAtomicFlush(\n em,\n [\n () => {\n if (parsed.name !== undefined) record.name = parsed.name ?? null\n if (parsed.sku !== undefined) record.sku = parsed.sku ?? null\n if (parsed.barcode !== undefined) record.barcode = parsed.barcode ?? null\n if (parsed.gtinType !== undefined) record.gtinType = parsed.gtinType ?? null\n if (parsed.hsCode !== undefined) record.hsCode = parsed.hsCode ?? null\n if (normalizedGtinBarcode !== null) record.barcode = normalizedGtinBarcode\n if (parsed.statusEntryId !== undefined) record.statusEntryId = parsed.statusEntryId ?? null\n if (parsed.isDefault !== undefined) record.isDefault = parsed.isDefault\n if (parsed.isActive !== undefined) record.isActive = parsed.isActive\n if (Object.prototype.hasOwnProperty.call(parsed, 'weightValue')) {\n record.weightValue = toNumericString(parsed.weightValue)\n }\n if (parsed.weightUnit !== undefined) record.weightUnit = parsed.weightUnit ?? null\n if (parsed.dimensions !== undefined) {\n record.dimensions = parsed.dimensions ? cloneJson(parsed.dimensions) : null\n }\n let metadataSplit: MetadataSplitResult | null = null\n if (parsed.metadata !== undefined) {\n metadataSplit = splitOptionValuesFromMetadata(parsed.metadata)\n record.metadata = metadataSplit.metadata\n }\n if (parsed.optionValues !== undefined) {\n record.optionValues = parsed.optionValues ? cloneJson(parsed.optionValues) : null\n } else if (metadataSplit?.hadOptionValues) {\n record.optionValues = metadataSplit.optionValues ? cloneJson(metadataSplit.optionValues) : null\n }\n if (taxRateProvided) {\n record.taxRateId = resolvedTaxRate?.taxRateId ?? null\n record.taxRate = resolvedTaxRate?.taxRate ?? null\n }\n if (parsed.customFieldsetCode !== undefined) {\n record.customFieldsetCode = parsed.customFieldsetCode ?? null\n }\n },\n async () => {\n if (parsed.isDefault === true) {\n previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)\n }\n },\n () => aggregateVariantMediaToProduct(em, record),\n ],\n { transaction: true }\n )\n } catch (error) {\n await rethrowVariantUniqueConstraint(error)\n }\n if (custom && Object.keys(custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n }\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n action: 'updated',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: record.id, previousDefaultVariantId }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadVariantSnapshot(em, result.variantId)\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as VariantSnapshot | undefined\n const after = snapshots.after as VariantSnapshot | undefined\n if (!before || !after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.variants.update', 'Update product variant'),\n resourceKind: 'catalog.variant',\n resourceId: before.id,\n parentResourceKind: 'catalog.product',\n parentResourceId: before.productId ?? null,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes: buildChanges(\n before as Record<string, unknown>,\n after as Record<string, unknown>,\n VARIANT_CHANGE_KEYS\n ),\n payload: {\n undo: {\n before,\n after,\n previousDefaultVariantId:\n (result as { previousDefaultVariantId?: string | null })?.previousDefaultVariantId ?? null,\n } satisfies VariantUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<VariantUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const after = payload?.after\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductVariant, { id: before.id })\n if (!record) {\n const product = await requireProduct(em, before.productId, {\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n record = em.create(CatalogProductVariant, {\n id: before.id,\n product,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n name: before.name ?? null,\n sku: before.sku ?? null,\n barcode: before.barcode ?? null,\n statusEntryId: before.statusEntryId ?? null,\n isDefault: before.isDefault,\n isActive: before.isActive,\n weightValue: before.weightValue ?? null,\n weightUnit: before.weightUnit ?? null,\n dimensions: before.dimensions ? cloneJson(before.dimensions) : null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n optionValues: before.optionValues ? cloneJson(before.optionValues) : null,\n customFieldsetCode: before.customFieldsetCode ?? null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyVariantSnapshot(record, before)\n await em.flush()\n const previousDefaultId = payload?.previousDefaultVariantId\n if (previousDefaultId) {\n const previousDefault = await em.findOne(CatalogProductVariant, { id: previousDefaultId })\n if (previousDefault) {\n ensureTenantScope(ctx, previousDefault.tenantId)\n ensureOrganizationScope(ctx, previousDefault.organizationId)\n previousDefault.isDefault = true\n await em.flush()\n }\n }\n const resetValues = buildCustomFieldResetMap(\n before.custom ?? undefined,\n after?.custom ?? undefined\n )\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: resetValues,\n })\n }\n },\n}\n\nconst deleteVariantCommand: CommandHandler<\n { body?: Record<string, unknown>; query?: Record<string, unknown> },\n { variantId: string }\n> = {\n id: 'catalog.variants.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Variant id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadVariantSnapshot(em, id, { includePrices: true })\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Variant id is required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductVariant, { id })\n if (!record) throw new CrudHttpError(404, { error: 'Catalog variant not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n const baseEm = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadVariantSnapshot(baseEm, id, { includePrices: true })\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const priceSnapshots =\n snapshot?.prices && snapshot.prices.length\n ? snapshot.prices\n : await loadVariantPriceSnapshots(baseEm, id, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n\n if (priceSnapshots.length) {\n await em.nativeDelete(CatalogProductPrice, { id: { $in: priceSnapshots.map((price) => price.id) } })\n } else {\n await em.nativeDelete(CatalogProductPrice, { variant: record })\n }\n em.remove(record)\n await em.flush()\n for (const priceSnapshot of priceSnapshots) {\n const resetValues = buildCustomFieldResetMap(priceSnapshot.custom ?? undefined, undefined)\n if (!Object.keys(resetValues).length) continue\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.catalog.catalog_product_price,\n recordId: priceSnapshot.id,\n organizationId: priceSnapshot.organizationId,\n tenantId: priceSnapshot.tenantId,\n values: resetValues,\n })\n }\n if (snapshot?.custom && Object.keys(snapshot.custom).length) {\n const resetValues = buildCustomFieldResetMap(snapshot.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.catalog.catalog_product_variant,\n recordId: id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n values: resetValues,\n })\n }\n }\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: id,\n organizationId: snapshot?.organizationId ?? record.organizationId,\n tenantId: snapshot?.tenantId ?? record.tenantId,\n action: 'deleted',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id,\n organizationId: snapshot?.organizationId ?? record.organizationId,\n tenantId: snapshot?.tenantId ?? record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as VariantSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.variants.delete', 'Delete product variant'),\n resourceKind: 'catalog.variant',\n resourceId: before.id,\n parentResourceKind: 'catalog.product',\n parentResourceId: before.productId ?? null,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies VariantUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<VariantUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductVariant, { id: before.id })\n if (!record) {\n const product = await requireProduct(em, before.productId, {\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n record = em.create(CatalogProductVariant, {\n id: before.id,\n product,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n name: before.name ?? null,\n sku: before.sku ?? null,\n barcode: before.barcode ?? null,\n statusEntryId: before.statusEntryId ?? null,\n isDefault: before.isDefault,\n isActive: before.isActive,\n weightValue: before.weightValue ?? null,\n weightUnit: before.weightUnit ?? null,\n dimensions: before.dimensions ? cloneJson(before.dimensions) : null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n customFieldsetCode: before.customFieldsetCode ?? null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyVariantSnapshot(record, before)\n if (before.prices?.length) {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await restoreVariantPricesFromSnapshots(em, record, before.prices, dataEngine)\n }\n await em.flush()\n if (before.custom && Object.keys(before.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: before.custom,\n })\n }\n },\n}\n\nasync function throwDuplicateVariantSkuError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate('catalog.variants.errors.skuExists', 'SKU already in use.')\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { sku: message },\n details: [{ path: ['sku'], message, code: 'duplicate', origin: 'validation' }],\n })\n}\n\nasync function throwDuplicateVariantGtinError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate(\n 'catalog.variants.errors.gtinExists',\n 'Another variant already uses this identifier.',\n )\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { barcode: message },\n details: [{ path: ['barcode'], message, code: 'duplicate', origin: 'validation' }],\n })\n}\n\nasync function throwGtinBarcodeRequiredError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate(\n 'catalog.variants.validation.gtinBarcodeRequired',\n 'A barcode value is required when an identifier type is set.',\n )\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { barcode: message },\n details: [{ path: ['barcode'], message, code: 'invalid', origin: 'validation' }],\n })\n}\n\nasync function throwInvalidGtinError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate(\n 'catalog.variants.validation.gtinChecksum',\n 'The barcode does not match the selected identifier type.',\n )\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { barcode: message },\n details: [{ path: ['barcode'], message, code: 'invalid', origin: 'validation' }],\n })\n}\n\nasync function rethrowVariantUniqueConstraint(error: unknown): Promise<never> {\n if (error instanceof UniqueConstraintViolationException) {\n const constraint = getErrorConstraint(error)\n const message = getErrorMessage(error).toLowerCase()\n if (\n constraint === 'catalog_product_variants_sku_unique' ||\n message.includes('catalog_product_variants_sku_unique')\n ) {\n await throwDuplicateVariantSkuError()\n }\n if (\n constraint === 'catalog_product_variants_gtin_scope_unique' ||\n message.includes('catalog_product_variants_gtin_scope_unique')\n ) {\n await throwDuplicateVariantGtinError()\n }\n }\n throw error\n}\n\nregisterCommand(createVariantCommand)\nregisterCommand(updateVariantCommand)\nregisterCommand(deleteVariantCommand)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, parseWithCustomFields, setCustomFieldsIfAny, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { UniqueConstraintViolationException } from '@mikro-orm/core'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { loadCustomFieldSnapshot, buildCustomFieldResetMap } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveRedoSnapshot, restoreCreatedRow } from '@open-mercato/shared/lib/commands/redo'\nimport { E } from '#generated/entities.ids.generated'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n CatalogProductVariant,\n CatalogProductPrice,\n CatalogProduct,\n CatalogOffer,\n CatalogPriceKind,\n} from '../data/entities'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport { resolveAttachmentScopePair } from '@open-mercato/core/modules/attachments/lib/scope'\nimport {\n variantCreateSchema,\n variantUpdateSchema,\n type VariantCreateInput,\n type VariantUpdateInput,\n} from '../data/validators'\nimport { isValidGtin, normalizeGtinValue } from '../lib/gtin'\nimport type { CatalogGtinType } from '../data/types'\nimport {\n cloneJson,\n commandActorScope,\n ensureOrganizationScope,\n ensureTenantScope,\n emitCatalogQueryIndexEvent,\n extractUndoPayload,\n requireProduct,\n toNumericString,\n getErrorConstraint,\n getErrorMessage,\n} from './shared'\nimport { SalesTaxRate } from '@open-mercato/core/modules/sales/data/entities'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\n\nconst variantCrudEvents: CrudEventsConfig = {\n module: 'catalog',\n entity: 'variant',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype VariantSnapshot = {\n id: string\n productId: string\n organizationId: string\n tenantId: string\n name: string | null\n sku: string | null\n barcode: string | null\n gtinType: CatalogGtinType | null\n hsCode: string | null\n statusEntryId: string | null\n isDefault: boolean\n isActive: boolean\n weightValue: string | null\n weightUnit: string | null\n taxRateId: string | null\n taxRate: string | null\n dimensions: Record<string, unknown> | null\n metadata: Record<string, unknown> | null\n optionValues: Record<string, string> | null\n customFieldsetCode: string | null\n createdAt: string\n updatedAt: string\n custom: Record<string, unknown> | null\n prices?: VariantPriceSnapshot[] | null\n}\n\ntype VariantUndoPayload = {\n before?: VariantSnapshot | null\n after?: VariantSnapshot | null\n previousDefaultVariantId?: string | null\n}\n\nconst VARIANT_CHANGE_KEYS = [\n 'name',\n 'sku',\n 'barcode',\n 'gtinType',\n 'hsCode',\n 'statusEntryId',\n 'isDefault',\n 'isActive',\n 'weightValue',\n 'weightUnit',\n 'taxRateId',\n 'taxRate',\n 'dimensions',\n 'optionValues',\n 'customFieldsetCode',\n 'metadata',\n] as const satisfies readonly string[]\n\nasync function loadVariantSnapshot(\n em: EntityManager,\n id: string,\n options: { includePrices?: boolean } = {}\n): Promise<VariantSnapshot | null> {\n const record = await em.findOne(CatalogProductVariant, { id, deletedAt: null })\n if (!record) return null\n const prices = options.includePrices\n ? await loadVariantPriceSnapshots(em, record.id, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n : null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n const productId = typeof record.product === 'string' ? record.product : record.product.id\n return {\n id: record.id,\n productId,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n name: record.name ?? null,\n sku: record.sku ?? null,\n barcode: record.barcode ?? null,\n gtinType: record.gtinType ?? null,\n hsCode: record.hsCode ?? null,\n statusEntryId: record.statusEntryId ?? null,\n isDefault: record.isDefault,\n isActive: record.isActive,\n weightValue: record.weightValue ?? null,\n weightUnit: record.weightUnit ?? null,\n taxRateId: record.taxRateId ?? null,\n taxRate: record.taxRate ?? null,\n dimensions: record.dimensions ? cloneJson(record.dimensions) : null,\n metadata: record.metadata ? cloneJson(record.metadata) : null,\n optionValues: record.optionValues ? cloneJson(record.optionValues) : null,\n customFieldsetCode: record.customFieldsetCode ?? null,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n custom: Object.keys(custom).length ? custom : null,\n prices: prices && prices.length ? prices : null,\n }\n}\n\nfunction variantSeedFromSnapshot(snapshot: VariantSnapshot): Record<string, unknown> {\n return {\n id: snapshot.id,\n product: snapshot.productId,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n name: snapshot.name ?? null,\n sku: snapshot.sku ?? null,\n barcode: snapshot.barcode ?? null,\n gtinType: snapshot.gtinType ?? null,\n hsCode: snapshot.hsCode ?? null,\n statusEntryId: snapshot.statusEntryId ?? null,\n isDefault: snapshot.isDefault,\n isActive: snapshot.isActive,\n weightValue: snapshot.weightValue ?? null,\n weightUnit: snapshot.weightUnit ?? null,\n taxRateId: snapshot.taxRateId ?? null,\n taxRate: snapshot.taxRate ?? null,\n dimensions: snapshot.dimensions ? cloneJson(snapshot.dimensions) : null,\n metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,\n optionValues: snapshot.optionValues ? cloneJson(snapshot.optionValues) : null,\n customFieldsetCode: snapshot.customFieldsetCode ?? null,\n createdAt: new Date(snapshot.createdAt),\n updatedAt: new Date(snapshot.updatedAt),\n }\n}\n\nfunction applyVariantSnapshot(record: CatalogProductVariant, snapshot: VariantSnapshot): void {\n record.organizationId = snapshot.organizationId\n record.tenantId = snapshot.tenantId\n record.name = snapshot.name ?? null\n record.sku = snapshot.sku ?? null\n record.barcode = snapshot.barcode ?? null\n record.gtinType = snapshot.gtinType ?? null\n record.hsCode = snapshot.hsCode ?? null\n record.statusEntryId = snapshot.statusEntryId ?? null\n record.isDefault = snapshot.isDefault\n record.isActive = snapshot.isActive\n record.weightValue = snapshot.weightValue ?? null\n record.weightUnit = snapshot.weightUnit ?? null\n record.taxRateId = snapshot.taxRateId ?? null\n record.taxRate = snapshot.taxRate ?? null\n record.dimensions = snapshot.dimensions ? cloneJson(snapshot.dimensions) : null\n record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null\n record.optionValues = snapshot.optionValues ? cloneJson(snapshot.optionValues) : null\n record.customFieldsetCode = snapshot.customFieldsetCode ?? null\n record.createdAt = new Date(snapshot.createdAt)\n record.updatedAt = new Date(snapshot.updatedAt)\n}\n\nasync function resolveVariantTaxRate(\n em: EntityManager,\n product: CatalogProduct,\n taxRateIdInput: string | null | undefined,\n taxRateInput: number | string | null | undefined\n): Promise<{ taxRateId: string | null; taxRate: string | null }> {\n const organizationId = product.organizationId\n const tenantId = product.tenantId\n const normalizedRate =\n taxRateInput === null || taxRateInput === undefined\n ? null\n : (() => {\n const numeric = typeof taxRateInput === 'string' ? Number(taxRateInput) : taxRateInput\n return Number.isFinite(numeric) ? toNumericString(numeric) : null\n })()\n if (taxRateIdInput === null) {\n return { taxRateId: product.taxRateId ?? null, taxRate: product.taxRate ?? null }\n }\n if (!taxRateIdInput) {\n return { taxRateId: product.taxRateId ?? null, taxRate: product.taxRate ?? normalizedRate }\n }\n const record = await em.findOne(SalesTaxRate, {\n id: taxRateIdInput,\n organizationId,\n tenantId,\n deletedAt: null,\n })\n if (!record) {\n throw new CrudHttpError(400, { error: 'Tax class not found' })\n }\n return { taxRateId: taxRateIdInput, taxRate: record.rate ?? normalizedRate }\n}\n\ntype VariantPriceSnapshot = {\n id: string\n variantId: string | null\n productId: string | null\n offerId: string | null\n organizationId: string\n tenantId: string\n priceKindId: string\n priceKindCode: string\n currencyCode: string\n kind: string\n minQuantity: number\n maxQuantity: number | null\n unitPriceNet: string | null\n unitPriceGross: string | null\n taxRate: string | null\n taxAmount: string | null\n channelId: string | null\n userId: string | null\n userGroupId: string | null\n customerId: string | null\n customerGroupId: string | null\n metadata: Record<string, unknown> | null\n startsAt: string | null\n endsAt: string | null\n createdAt: string\n updatedAt: string\n custom: Record<string, unknown> | null\n}\n\nasync function loadVariantPriceSnapshots(\n em: EntityManager,\n variantId: string,\n scope: { tenantId: string; organizationId: string }\n): Promise<VariantPriceSnapshot[]> {\n const prices = await findWithDecryption(\n em,\n CatalogProductPrice,\n { variant: variantId, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { populate: ['priceKind', 'product', 'offer'] },\n { tenantId: scope.tenantId, organizationId: scope.organizationId },\n )\n const snapshots: VariantPriceSnapshot[] = []\n for (const price of prices) {\n const variantRef = price.variant\n const variantIdValue =\n typeof variantRef === 'string'\n ? variantRef\n : variantRef\n ? variantRef.id\n : null\n const productRef = price.product\n ? price.product\n : typeof price.variant === 'object' && price.variant\n ? price.variant.product\n : null\n const productId =\n typeof productRef === 'string'\n ? productRef\n : productRef\n ? productRef.id\n : null\n const priceKindRef = price.priceKind\n const priceKindId =\n typeof priceKindRef === 'string'\n ? priceKindRef\n : priceKindRef\n ? priceKindRef.id\n : null\n if (!priceKindId) {\n throw new CrudHttpError(400, { error: 'Price is missing price kind metadata.' })\n }\n const priceKindCode =\n typeof priceKindRef === 'object' && priceKindRef ? priceKindRef.code : price.kind\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.catalog.catalog_product_price,\n recordId: price.id,\n tenantId: price.tenantId,\n organizationId: price.organizationId,\n })\n snapshots.push({\n id: price.id,\n variantId: variantIdValue,\n productId,\n offerId: typeof price.offer === 'string' ? price.offer : price.offer ? price.offer.id : null,\n organizationId: price.organizationId,\n tenantId: price.tenantId,\n priceKindId,\n priceKindCode,\n currencyCode: price.currencyCode,\n kind: price.kind,\n minQuantity: price.minQuantity,\n maxQuantity: price.maxQuantity ?? null,\n unitPriceNet: price.unitPriceNet ?? null,\n unitPriceGross: price.unitPriceGross ?? null,\n taxRate: price.taxRate ?? null,\n taxAmount: price.taxAmount ?? null,\n channelId: price.channelId ?? null,\n userId: price.userId ?? null,\n userGroupId: price.userGroupId ?? null,\n customerId: price.customerId ?? null,\n customerGroupId: price.customerGroupId ?? null,\n metadata: price.metadata ? cloneJson(price.metadata) : null,\n startsAt: price.startsAt ? price.startsAt.toISOString() : null,\n endsAt: price.endsAt ? price.endsAt.toISOString() : null,\n createdAt: price.createdAt.toISOString(),\n updatedAt: price.updatedAt.toISOString(),\n custom: Object.keys(custom).length ? custom : null,\n })\n }\n return snapshots\n}\n\nasync function restoreVariantPricesFromSnapshots(\n em: EntityManager,\n variant: CatalogProductVariant,\n snapshots: VariantPriceSnapshot[],\n dataEngine: DataEngine\n): Promise<void> {\n if (!snapshots.length) return\n const productRef =\n typeof variant.product === 'string'\n ? await requireProduct(em, variant.product, {\n tenantId: variant.tenantId,\n organizationId: variant.organizationId,\n })\n : variant.product\n for (const snapshot of snapshots) {\n const product =\n snapshot.productId && snapshot.productId !== productRef.id\n ? em.getReference(CatalogProduct, snapshot.productId)\n : productRef\n const offer = snapshot.offerId ? em.getReference(CatalogOffer, snapshot.offerId) : null\n const priceKind = em.getReference(CatalogPriceKind, snapshot.priceKindId)\n let record = await em.findOne(CatalogProductPrice, { id: snapshot.id })\n if (!record) {\n record = em.create(CatalogProductPrice, {\n id: snapshot.id,\n variant,\n product,\n offer,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n currencyCode: snapshot.currencyCode,\n priceKind,\n kind: snapshot.priceKindCode || snapshot.kind,\n minQuantity: snapshot.minQuantity,\n maxQuantity: snapshot.maxQuantity ?? null,\n unitPriceNet: snapshot.unitPriceNet ?? null,\n unitPriceGross: snapshot.unitPriceGross ?? null,\n taxRate: snapshot.taxRate ?? null,\n taxAmount: snapshot.taxAmount ?? null,\n channelId: snapshot.channelId ?? null,\n userId: snapshot.userId ?? null,\n userGroupId: snapshot.userGroupId ?? null,\n customerId: snapshot.customerId ?? null,\n customerGroupId: snapshot.customerGroupId ?? null,\n metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,\n startsAt: snapshot.startsAt ? new Date(snapshot.startsAt) : null,\n endsAt: snapshot.endsAt ? new Date(snapshot.endsAt) : null,\n createdAt: new Date(snapshot.createdAt),\n updatedAt: new Date(snapshot.updatedAt),\n })\n em.persist(record)\n } else {\n record.variant = variant\n record.product = product\n record.offer = offer\n record.priceKind = priceKind\n record.organizationId = snapshot.organizationId\n record.tenantId = snapshot.tenantId\n record.currencyCode = snapshot.currencyCode\n record.kind = snapshot.priceKindCode || snapshot.kind\n record.minQuantity = snapshot.minQuantity\n record.maxQuantity = snapshot.maxQuantity ?? null\n record.unitPriceNet = snapshot.unitPriceNet ?? null\n record.unitPriceGross = snapshot.unitPriceGross ?? null\n record.taxRate = snapshot.taxRate ?? null\n record.taxAmount = snapshot.taxAmount ?? null\n record.channelId = snapshot.channelId ?? null\n record.userId = snapshot.userId ?? null\n record.userGroupId = snapshot.userGroupId ?? null\n record.customerId = snapshot.customerId ?? null\n record.customerGroupId = snapshot.customerGroupId ?? null\n record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null\n record.startsAt = snapshot.startsAt ? new Date(snapshot.startsAt) : null\n record.endsAt = snapshot.endsAt ? new Date(snapshot.endsAt) : null\n record.createdAt = new Date(snapshot.createdAt)\n record.updatedAt = new Date(snapshot.updatedAt)\n }\n }\n await em.flush()\n for (const snapshot of snapshots) {\n if (!snapshot.custom || !Object.keys(snapshot.custom).length) continue\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.catalog.catalog_product_price,\n recordId: snapshot.id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n values: snapshot.custom,\n })\n }\n}\n\ntype MetadataSplitResult = {\n metadata: Record<string, unknown> | null\n optionValues: Record<string, string> | null\n hadOptionValues: boolean\n}\n\nfunction splitOptionValuesFromMetadata(\n metadata?: Record<string, unknown> | null\n): MetadataSplitResult {\n if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {\n return {\n metadata: metadata ? cloneJson(metadata) : null,\n optionValues: null,\n hadOptionValues: false,\n }\n }\n const { optionValues, ...rest } = metadata as Record<string, unknown> & {\n optionValues?: unknown\n }\n const normalizedMetadata = Object.keys(rest).length ? cloneJson(rest) : null\n return {\n metadata: normalizedMetadata,\n optionValues: normalizeOptionValues(optionValues),\n hadOptionValues: optionValues !== undefined,\n }\n}\n\nfunction normalizeOptionValues(input: unknown): Record<string, string> | null {\n if (!input || typeof input !== 'object' || Array.isArray(input)) return null\n const normalized: Record<string, string> = {}\n for (const [rawKey, rawValue] of Object.entries(input)) {\n if (typeof rawValue !== 'string') continue\n const key = rawKey.trim()\n const value = rawValue.trim()\n if (!key || !value) continue\n normalized[key] = value\n }\n return Object.keys(normalized).length ? normalized : null\n}\n\nfunction resolveProductId(record: CatalogProductVariant): string {\n return typeof record.product === 'string' ? record.product : record.product.id\n}\n\nasync function enforceSingleDefaultVariant(\n em: EntityManager,\n variant: CatalogProductVariant\n): Promise<string | null> {\n if (!variant.isDefault) return null\n const productId = resolveProductId(variant)\n const existingDefault = await em.findOne(\n CatalogProductVariant,\n { product: productId, isDefault: true, deletedAt: null, id: { $ne: variant.id } },\n { fields: ['id', 'isDefault'] }\n )\n if (existingDefault) {\n existingDefault.isDefault = false\n return existingDefault.id\n }\n return null\n}\n\nasync function aggregateVariantMediaToProduct(\n em: EntityManager,\n variant: CatalogProductVariant\n): Promise<void> {\n const productId = resolveProductId(variant)\n const buildKey = (\n attachment: Pick<Attachment, 'fileName' | 'fileSize' | 'storageDriver' | 'partitionCode' | 'storagePath'>\n ) =>\n [\n attachment.fileName?.trim() ?? '',\n attachment.fileSize ?? '',\n attachment.storageDriver ?? '',\n attachment.partitionCode ?? '',\n ].join('|')\n const attachments = await em.find(\n Attachment,\n {\n entityId: E.catalog.catalog_product_variant,\n recordId: variant.id,\n organizationId: variant.organizationId ?? undefined,\n tenantId: variant.tenantId ?? undefined,\n },\n {\n fields: [\n 'id',\n 'partitionCode',\n 'fileName',\n 'mimeType',\n 'fileSize',\n 'storageDriver',\n 'storagePath',\n 'storageMetadata',\n 'url',\n 'organizationId',\n 'tenantId',\n 'fileSize',\n 'storageDriver',\n 'partitionCode',\n ],\n }\n )\n if (!attachments.length) return\n const existing = await em.find(\n Attachment,\n {\n entityId: E.catalog.catalog_product,\n recordId: productId,\n organizationId: variant.organizationId ?? undefined,\n tenantId: variant.tenantId ?? undefined,\n },\n {\n fields: ['storagePath', 'fileName', 'fileSize', 'storageDriver', 'partitionCode'],\n }\n )\n const existingKeys = new Set(existing.map((item) => buildKey(item)))\n let created = 0\n for (const source of attachments) {\n const key = buildKey(source)\n if (existingKeys.has(key)) continue\n // Carry a scope pair across as a unit (source row first, else the variant\n // scope, else global) so independent `??` coalescing can't produce a\n // partial-null attachment row.\n const scope = resolveAttachmentScopePair(source, variant) ?? { organizationId: null, tenantId: null }\n const clone = em.create(Attachment, {\n entityId: E.catalog.catalog_product,\n recordId: productId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n partitionCode: source.partitionCode,\n fileName: source.fileName,\n mimeType: source.mimeType,\n fileSize: source.fileSize,\n storageDriver: source.storageDriver,\n storagePath: source.storagePath,\n storageMetadata: source.storageMetadata ? cloneJson(source.storageMetadata) : null,\n url: source.url,\n })\n em.persist(clone)\n existingKeys.add(key)\n created += 1\n }\n if (created > 0) {\n await em.flush()\n }\n}\n\nconst createVariantCommand: CommandHandler<VariantCreateInput, { variantId: string; previousDefaultVariantId?: string | null }> = {\n id: 'catalog.variants.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(variantCreateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const product = await requireProduct(em, parsed.productId, commandActorScope(ctx))\n ensureTenantScope(ctx, product.tenantId)\n ensureOrganizationScope(ctx, product.organizationId)\n const { taxRateId, taxRate } = await resolveVariantTaxRate(\n em,\n product,\n parsed.taxRateId ?? null,\n parsed.taxRate\n )\n\n const metadataSplit = splitOptionValuesFromMetadata(parsed.metadata)\n const resolvedOptionValues =\n parsed.optionValues ?? (metadataSplit.hadOptionValues ? metadataSplit.optionValues : null)\n\n const gtinType = parsed.gtinType ?? null\n const barcode =\n gtinType && parsed.barcode\n ? normalizeGtinValue(gtinType, parsed.barcode)\n : (parsed.barcode ?? null)\n\n const now = new Date()\n const record = em.create(CatalogProductVariant, {\n organizationId: product.organizationId,\n tenantId: product.tenantId,\n product,\n name: parsed.name ?? null,\n sku: parsed.sku ?? null,\n barcode,\n gtinType,\n hsCode: parsed.hsCode ?? null,\n statusEntryId: parsed.statusEntryId ?? null,\n isDefault: parsed.isDefault ?? false,\n isActive: parsed.isActive ?? true,\n weightValue: toNumericString(parsed.weightValue),\n weightUnit: parsed.weightUnit ?? null,\n taxRateId,\n taxRate,\n dimensions: parsed.dimensions ? cloneJson(parsed.dimensions) : null,\n metadata: metadataSplit.metadata,\n optionValues: resolvedOptionValues ? cloneJson(resolvedOptionValues) : null,\n customFieldsetCode: parsed.customFieldsetCode ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n let previousDefaultVariantId: string | null = null\n try {\n await withAtomicFlush(\n em,\n [\n () => em.flush(),\n async () => {\n if (record.isDefault) {\n previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)\n await em.flush()\n }\n },\n () => aggregateVariantMediaToProduct(em, record),\n ],\n { transaction: true }\n )\n } catch (error) {\n await rethrowVariantUniqueConstraint(error)\n }\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n action: 'created',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: record.id, previousDefaultVariantId }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadVariantSnapshot(em, result.variantId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as VariantSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.variants.create', 'Create product variant'),\n resourceKind: 'catalog.variant',\n resourceId: result.variantId,\n parentResourceKind: 'catalog.product',\n parentResourceId: after.productId ?? null,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: {\n after,\n previousDefaultVariantId: (result as { previousDefaultVariantId?: string | null })?.previousDefaultVariantId ?? null,\n } satisfies VariantUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<VariantUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductVariant, { id: after.id })\n if (!record) return\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n em.remove(record)\n await em.flush()\n if (payload?.previousDefaultVariantId) {\n const previousDefault = await em.findOne(CatalogProductVariant, { id: payload.previousDefaultVariantId })\n if (previousDefault) {\n ensureTenantScope(ctx, previousDefault.tenantId)\n ensureOrganizationScope(ctx, previousDefault.organizationId)\n previousDefault.isDefault = true\n await em.flush()\n }\n }\n const resetValues = buildCustomFieldResetMap(undefined, after.custom ?? undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: after.id,\n organizationId: after.organizationId,\n tenantId: after.tenantId,\n values: resetValues,\n })\n }\n },\n redo: async ({ ctx, logEntry }) => {\n const after = resolveRedoSnapshot<VariantSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for variant create' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await restoreCreatedRow(\n em,\n CatalogProductVariant,\n after.id,\n () => variantSeedFromSnapshot(after),\n )\n applyVariantSnapshot(record, after)\n let previousDefaultVariantId: string | null = null\n try {\n await withAtomicFlush(\n em,\n [\n () => em.flush(),\n async () => {\n if (record.isDefault) {\n previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)\n await em.flush()\n }\n },\n () => aggregateVariantMediaToProduct(em, record),\n ],\n { transaction: true }\n )\n } catch (error) {\n await rethrowVariantUniqueConstraint(error)\n }\n if (after.custom && Object.keys(after.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: after.custom,\n })\n }\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n action: 'created',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: record.id, previousDefaultVariantId }\n },\n}\n\nconst updateVariantCommand: CommandHandler<VariantUpdateInput, { variantId: string; previousDefaultVariantId?: string | null }> = {\n id: 'catalog.variants.update',\n async prepare(input, ctx) {\n const id = requireId(input, 'Variant id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadVariantSnapshot(em, id)\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(variantUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductVariant, { id: parsed.id, deletedAt: null })\n if (!record) throw new CrudHttpError(404, { error: 'Catalog variant not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n const product = await requireProduct(em, record.product.id, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n\n if (!product) throw new CrudHttpError(400, { error: 'Variant product missing' })\n\n const taxRateProvided = parsed.taxRateId !== undefined || parsed.taxRate !== undefined\n const resolvedTaxRate = taxRateProvided\n ? await resolveVariantTaxRate(em, product, parsed.taxRateId ?? null, parsed.taxRate)\n : null\n\n // Re-validate GTIN against the merged record state: a partial update may\n // carry only one of (gtinType, barcode) while the other half is stored.\n let normalizedGtinBarcode: string | null = null\n if (parsed.gtinType !== undefined || parsed.barcode !== undefined) {\n const effectiveGtinType =\n parsed.gtinType !== undefined ? (parsed.gtinType ?? null) : (record.gtinType ?? null)\n if (effectiveGtinType) {\n const effectiveBarcode =\n parsed.barcode !== undefined ? (parsed.barcode ?? null) : (record.barcode ?? null)\n if (!effectiveBarcode || !effectiveBarcode.trim().length) {\n await throwGtinBarcodeRequiredError()\n } else {\n const normalized = normalizeGtinValue(effectiveGtinType, effectiveBarcode)\n if (!isValidGtin(effectiveGtinType, normalized)) {\n await throwInvalidGtinError()\n }\n normalizedGtinBarcode = normalized\n }\n }\n }\n\n let previousDefaultVariantId: string | null = null\n try {\n await withAtomicFlush(\n em,\n [\n () => {\n if (parsed.name !== undefined) record.name = parsed.name ?? null\n if (parsed.sku !== undefined) record.sku = parsed.sku ?? null\n if (parsed.barcode !== undefined) record.barcode = parsed.barcode ?? null\n if (parsed.gtinType !== undefined) record.gtinType = parsed.gtinType ?? null\n if (parsed.hsCode !== undefined) record.hsCode = parsed.hsCode ?? null\n if (normalizedGtinBarcode !== null) record.barcode = normalizedGtinBarcode\n if (parsed.statusEntryId !== undefined) record.statusEntryId = parsed.statusEntryId ?? null\n if (parsed.isDefault !== undefined) record.isDefault = parsed.isDefault\n if (parsed.isActive !== undefined) record.isActive = parsed.isActive\n if (Object.prototype.hasOwnProperty.call(parsed, 'weightValue')) {\n record.weightValue = toNumericString(parsed.weightValue)\n }\n if (parsed.weightUnit !== undefined) record.weightUnit = parsed.weightUnit ?? null\n if (parsed.dimensions !== undefined) {\n record.dimensions = parsed.dimensions ? cloneJson(parsed.dimensions) : null\n }\n let metadataSplit: MetadataSplitResult | null = null\n if (parsed.metadata !== undefined) {\n metadataSplit = splitOptionValuesFromMetadata(parsed.metadata)\n record.metadata = metadataSplit.metadata\n }\n if (parsed.optionValues !== undefined) {\n record.optionValues = parsed.optionValues ? cloneJson(parsed.optionValues) : null\n } else if (metadataSplit?.hadOptionValues) {\n record.optionValues = metadataSplit.optionValues ? cloneJson(metadataSplit.optionValues) : null\n }\n if (taxRateProvided) {\n record.taxRateId = resolvedTaxRate?.taxRateId ?? null\n record.taxRate = resolvedTaxRate?.taxRate ?? null\n }\n if (parsed.customFieldsetCode !== undefined) {\n record.customFieldsetCode = parsed.customFieldsetCode ?? null\n }\n },\n async () => {\n if (parsed.isDefault === true) {\n previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)\n }\n },\n () => aggregateVariantMediaToProduct(em, record),\n ],\n { transaction: true }\n )\n } catch (error) {\n await rethrowVariantUniqueConstraint(error)\n }\n if (custom && Object.keys(custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n }\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n action: 'updated',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: record.id, previousDefaultVariantId }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadVariantSnapshot(em, result.variantId)\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as VariantSnapshot | undefined\n const after = snapshots.after as VariantSnapshot | undefined\n if (!before || !after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.variants.update', 'Update product variant'),\n resourceKind: 'catalog.variant',\n resourceId: before.id,\n parentResourceKind: 'catalog.product',\n parentResourceId: before.productId ?? null,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes: buildChanges(\n before as Record<string, unknown>,\n after as Record<string, unknown>,\n VARIANT_CHANGE_KEYS\n ),\n payload: {\n undo: {\n before,\n after,\n previousDefaultVariantId:\n (result as { previousDefaultVariantId?: string | null })?.previousDefaultVariantId ?? null,\n } satisfies VariantUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<VariantUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const after = payload?.after\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductVariant, { id: before.id })\n if (!record) {\n const product = await requireProduct(em, before.productId, {\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n record = em.create(CatalogProductVariant, {\n id: before.id,\n product,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n name: before.name ?? null,\n sku: before.sku ?? null,\n barcode: before.barcode ?? null,\n statusEntryId: before.statusEntryId ?? null,\n isDefault: before.isDefault,\n isActive: before.isActive,\n weightValue: before.weightValue ?? null,\n weightUnit: before.weightUnit ?? null,\n dimensions: before.dimensions ? cloneJson(before.dimensions) : null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n optionValues: before.optionValues ? cloneJson(before.optionValues) : null,\n customFieldsetCode: before.customFieldsetCode ?? null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyVariantSnapshot(record, before)\n await em.flush()\n const previousDefaultId = payload?.previousDefaultVariantId\n if (previousDefaultId) {\n const previousDefault = await em.findOne(CatalogProductVariant, { id: previousDefaultId })\n if (previousDefault) {\n ensureTenantScope(ctx, previousDefault.tenantId)\n ensureOrganizationScope(ctx, previousDefault.organizationId)\n previousDefault.isDefault = true\n await em.flush()\n }\n }\n const resetValues = buildCustomFieldResetMap(\n before.custom ?? undefined,\n after?.custom ?? undefined\n )\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: resetValues,\n })\n }\n },\n}\n\nconst deleteVariantCommand: CommandHandler<\n { body?: Record<string, unknown>; query?: Record<string, unknown> },\n { variantId: string }\n> = {\n id: 'catalog.variants.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Variant id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadVariantSnapshot(em, id, { includePrices: true })\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Variant id is required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductVariant, { id })\n if (!record) throw new CrudHttpError(404, { error: 'Catalog variant not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n const baseEm = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadVariantSnapshot(baseEm, id, { includePrices: true })\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const priceSnapshots =\n snapshot?.prices && snapshot.prices.length\n ? snapshot.prices\n : await loadVariantPriceSnapshots(baseEm, id, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n\n if (priceSnapshots.length) {\n await em.nativeDelete(CatalogProductPrice, { id: { $in: priceSnapshots.map((price) => price.id) } })\n } else {\n await em.nativeDelete(CatalogProductPrice, { variant: record })\n }\n em.remove(record)\n await em.flush()\n for (const priceSnapshot of priceSnapshots) {\n const resetValues = buildCustomFieldResetMap(priceSnapshot.custom ?? undefined, undefined)\n if (!Object.keys(resetValues).length) continue\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.catalog.catalog_product_price,\n recordId: priceSnapshot.id,\n organizationId: priceSnapshot.organizationId,\n tenantId: priceSnapshot.tenantId,\n values: resetValues,\n })\n }\n if (snapshot?.custom && Object.keys(snapshot.custom).length) {\n const resetValues = buildCustomFieldResetMap(snapshot.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.catalog.catalog_product_variant,\n recordId: id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n values: resetValues,\n })\n }\n }\n await emitCatalogQueryIndexEvent(ctx, {\n entityType: E.catalog.catalog_product_variant,\n recordId: id,\n organizationId: snapshot?.organizationId ?? record.organizationId,\n tenantId: snapshot?.tenantId ?? record.tenantId,\n action: 'deleted',\n })\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id,\n organizationId: snapshot?.organizationId ?? record.organizationId,\n tenantId: snapshot?.tenantId ?? record.tenantId,\n },\n events: variantCrudEvents,\n })\n return { variantId: id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as VariantSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.variants.delete', 'Delete product variant'),\n resourceKind: 'catalog.variant',\n resourceId: before.id,\n parentResourceKind: 'catalog.product',\n parentResourceId: before.productId ?? null,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies VariantUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<VariantUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductVariant, { id: before.id })\n if (!record) {\n const product = await requireProduct(em, before.productId, {\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n record = em.create(CatalogProductVariant, {\n id: before.id,\n product,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n name: before.name ?? null,\n sku: before.sku ?? null,\n barcode: before.barcode ?? null,\n statusEntryId: before.statusEntryId ?? null,\n isDefault: before.isDefault,\n isActive: before.isActive,\n weightValue: before.weightValue ?? null,\n weightUnit: before.weightUnit ?? null,\n dimensions: before.dimensions ? cloneJson(before.dimensions) : null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n customFieldsetCode: before.customFieldsetCode ?? null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyVariantSnapshot(record, before)\n if (before.prices?.length) {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await restoreVariantPricesFromSnapshots(em, record, before.prices, dataEngine)\n }\n await em.flush()\n if (before.custom && Object.keys(before.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_variant,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: before.custom,\n })\n }\n },\n}\n\nasync function throwDuplicateVariantSkuError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate('catalog.variants.errors.skuExists', 'SKU already in use.')\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { sku: message },\n details: [{ path: ['sku'], message, code: 'duplicate', origin: 'validation' }],\n })\n}\n\nasync function throwDuplicateVariantGtinError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate(\n 'catalog.variants.errors.gtinExists',\n 'Another variant already uses this identifier.',\n )\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { barcode: message },\n details: [{ path: ['barcode'], message, code: 'duplicate', origin: 'validation' }],\n })\n}\n\nasync function throwGtinBarcodeRequiredError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate(\n 'catalog.variants.validation.gtinBarcodeRequired',\n 'A barcode value is required when an identifier type is set.',\n )\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { barcode: message },\n details: [{ path: ['barcode'], message, code: 'invalid', origin: 'validation' }],\n })\n}\n\nasync function throwInvalidGtinError(): Promise<never> {\n const { translate } = await resolveTranslations()\n const message = translate(\n 'catalog.variants.validation.gtinChecksum',\n 'The barcode does not match the selected identifier type.',\n )\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { barcode: message },\n details: [{ path: ['barcode'], message, code: 'invalid', origin: 'validation' }],\n })\n}\n\nasync function rethrowVariantUniqueConstraint(error: unknown): Promise<never> {\n if (error instanceof UniqueConstraintViolationException) {\n const constraint = getErrorConstraint(error)\n const message = getErrorMessage(error).toLowerCase()\n if (\n constraint === 'catalog_product_variants_sku_unique' ||\n message.includes('catalog_product_variants_sku_unique')\n ) {\n await throwDuplicateVariantSkuError()\n }\n if (\n constraint === 'catalog_product_variants_gtin_scope_unique' ||\n message.includes('catalog_product_variants_gtin_scope_unique')\n ) {\n await throwDuplicateVariantGtinError()\n }\n }\n throw error\n}\n\nregisterCommand(createVariantCommand)\nregisterCommand(updateVariantCommand)\nregisterCommand(deleteVariantCommand)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -768,7 +768,6 @@ const forwardMessageCommand = {
768
768
  trx,
769
769
  forwardThreadSlice.map((item) => item.id),
770
770
  newMessage.id,
771
- input.organizationId,
772
771
  input.tenantId
773
772
  );
774
773
  }
@@ -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": "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",
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, organizationId, tenantId) {
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, targetOrganizationId, tenantId) {
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: targetOrganizationId,
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
- await em.flush();
121
- return dedupedById.size;
124
+ if (copied > 0) {
125
+ await em.flush();
126
+ }
127
+ return copied;
122
128
  }
123
129
  export {
124
130
  copyAttachmentsForForward,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/messages/lib/attachments.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport { MESSAGE_ATTACHMENT_ENTITY_ID } from './constants'\nconst LIBRARY_ATTACHMENT_ENTITY_ID = 'attachments:library'\n\nfunction buildOrganizationScopeFilter(organizationId: string | null) {\n if (!organizationId) {\n return { organizationId: null }\n }\n return {\n $or: [\n { organizationId },\n { organizationId: null },\n ],\n }\n}\n\nexport async function linkAttachmentsToMessage(\n em: EntityManager,\n messageId: string,\n attachmentIds: string[],\n organizationId: string | null,\n tenantId: string,\n): Promise<void> {\n if (attachmentIds.length === 0) return\n\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n id: { $in: attachmentIds },\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n for (const attachment of attachments) {\n attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID\n attachment.recordId = messageId\n }\n\n await em.flush()\n}\n\nexport async function getMessageAttachments(\n em: EntityManager,\n messageId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<Array<{\n id: string\n fileName: string\n fileSize: number\n mimeType: string\n url: string\n}>> {\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: messageId,\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n return attachments.map((attachment) => ({\n id: attachment.id,\n fileName: attachment.fileName,\n fileSize: attachment.fileSize,\n mimeType: attachment.mimeType,\n url: attachment.url,\n }))\n}\n\nexport type MessageEmailAttachment = {\n fileName: string\n fileSize: number\n mimeType: string\n partitionCode: string\n storagePath: string\n storageDriver: string\n}\n\nexport async function getMessageEmailAttachments(\n em: EntityManager,\n messageId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<MessageEmailAttachment[]> {\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: messageId,\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n return attachments.map((attachment) => ({\n fileName: attachment.fileName,\n fileSize: attachment.fileSize,\n mimeType: attachment.mimeType,\n partitionCode: attachment.partitionCode,\n storagePath: attachment.storagePath,\n storageDriver: attachment.storageDriver,\n }))\n}\n\nexport async function linkLibraryAttachmentsToMessage(\n em: EntityManager,\n messageId: string,\n sourceRecordId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<void> {\n if (!sourceRecordId.trim()) return\n\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n entityId: LIBRARY_ATTACHMENT_ENTITY_ID,\n recordId: sourceRecordId,\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n if (!attachments.length) return\n\n for (const attachment of attachments) {\n attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID\n attachment.recordId = messageId\n }\n\n await em.flush()\n}\n\nexport async function copyAttachmentsForForward(\n em: EntityManager,\n sourceMessageId: string,\n targetMessageId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<number> {\n return copyAttachmentsForForwardMessages(\n em,\n [sourceMessageId],\n targetMessageId,\n organizationId,\n tenantId,\n )\n}\n\nexport async function copyAttachmentsForForwardMessages(\n em: EntityManager,\n sourceMessageIds: string[],\n targetMessageId: string,\n targetOrganizationId: string | null,\n tenantId: string,\n): Promise<number> {\n if (sourceMessageIds.length === 0) return 0\n\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n const sourceAttachments = await em.find(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: { $in: sourceMessageIds },\n tenantId,\n })\n if (sourceAttachments.length === 0) {\n return 0\n }\n\n const dedupedById = new Map<string, typeof sourceAttachments[number]>()\n for (const sourceAttachment of sourceAttachments) {\n if (!dedupedById.has(sourceAttachment.id)) {\n dedupedById.set(sourceAttachment.id, sourceAttachment)\n }\n }\n\n for (const sourceAttachment of dedupedById.values()) {\n const copy = em.create(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: targetMessageId,\n organizationId: targetOrganizationId,\n tenantId,\n fileName: sourceAttachment.fileName,\n mimeType: sourceAttachment.mimeType,\n fileSize: sourceAttachment.fileSize,\n storageDriver: sourceAttachment.storageDriver,\n storagePath: sourceAttachment.storagePath,\n storageMetadata: sourceAttachment.storageMetadata,\n url: sourceAttachment.url,\n partitionCode: sourceAttachment.partitionCode,\n })\n\n em.persist(copy)\n }\n\n await em.flush()\n return dedupedById.size\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport { resolveAttachmentScopePair } from '@open-mercato/core/modules/attachments/lib/scope'\nimport { MESSAGE_ATTACHMENT_ENTITY_ID } from './constants'\nconst LIBRARY_ATTACHMENT_ENTITY_ID = 'attachments:library'\n\nfunction buildOrganizationScopeFilter(organizationId: string | null) {\n if (!organizationId) {\n return { organizationId: null }\n }\n return {\n $or: [\n { organizationId },\n { organizationId: null },\n ],\n }\n}\n\nexport async function linkAttachmentsToMessage(\n em: EntityManager,\n messageId: string,\n attachmentIds: string[],\n organizationId: string | null,\n tenantId: string,\n): Promise<void> {\n if (attachmentIds.length === 0) return\n\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n id: { $in: attachmentIds },\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n for (const attachment of attachments) {\n attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID\n attachment.recordId = messageId\n }\n\n await em.flush()\n}\n\nexport async function getMessageAttachments(\n em: EntityManager,\n messageId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<Array<{\n id: string\n fileName: string\n fileSize: number\n mimeType: string\n url: string\n}>> {\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: messageId,\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n return attachments.map((attachment) => ({\n id: attachment.id,\n fileName: attachment.fileName,\n fileSize: attachment.fileSize,\n mimeType: attachment.mimeType,\n url: attachment.url,\n }))\n}\n\nexport type MessageEmailAttachment = {\n fileName: string\n fileSize: number\n mimeType: string\n partitionCode: string\n storagePath: string\n storageDriver: string\n}\n\nexport async function getMessageEmailAttachments(\n em: EntityManager,\n messageId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<MessageEmailAttachment[]> {\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: messageId,\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n return attachments.map((attachment) => ({\n fileName: attachment.fileName,\n fileSize: attachment.fileSize,\n mimeType: attachment.mimeType,\n partitionCode: attachment.partitionCode,\n storagePath: attachment.storagePath,\n storageDriver: attachment.storageDriver,\n }))\n}\n\nexport async function linkLibraryAttachmentsToMessage(\n em: EntityManager,\n messageId: string,\n sourceRecordId: string,\n organizationId: string | null,\n tenantId: string,\n): Promise<void> {\n if (!sourceRecordId.trim()) return\n\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n\n const attachments = await em.find(Attachment, {\n entityId: LIBRARY_ATTACHMENT_ENTITY_ID,\n recordId: sourceRecordId,\n tenantId,\n ...buildOrganizationScopeFilter(organizationId),\n })\n\n if (!attachments.length) return\n\n for (const attachment of attachments) {\n attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID\n attachment.recordId = messageId\n }\n\n await em.flush()\n}\n\nexport async function copyAttachmentsForForward(\n em: EntityManager,\n sourceMessageId: string,\n targetMessageId: string,\n tenantId: string,\n): Promise<number> {\n return copyAttachmentsForForwardMessages(\n em,\n [sourceMessageId],\n targetMessageId,\n tenantId,\n )\n}\n\nexport async function copyAttachmentsForForwardMessages(\n em: EntityManager,\n sourceMessageIds: string[],\n targetMessageId: string,\n tenantId: string,\n): Promise<number> {\n if (sourceMessageIds.length === 0) return 0\n\n const { Attachment } = await import('@open-mercato/core/modules/attachments/data/entities')\n const sourceAttachments = await em.find(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: { $in: sourceMessageIds },\n tenantId,\n })\n if (sourceAttachments.length === 0) {\n return 0\n }\n\n const dedupedById = new Map<string, typeof sourceAttachments[number]>()\n for (const sourceAttachment of sourceAttachments) {\n if (!dedupedById.has(sourceAttachment.id)) {\n dedupedById.set(sourceAttachment.id, sourceAttachment)\n }\n }\n\n let copied = 0\n for (const sourceAttachment of dedupedById.values()) {\n // Carry the source attachment's scope across as a unit so a forwarded copy\n // can never become a partial-null row. Source rows that are themselves\n // partial-null are unreadable dead data; skip rather than propagate them.\n const scope = resolveAttachmentScopePair(sourceAttachment)\n if (!scope) continue\n\n const copy = em.create(Attachment, {\n entityId: MESSAGE_ATTACHMENT_ENTITY_ID,\n recordId: targetMessageId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n fileName: sourceAttachment.fileName,\n mimeType: sourceAttachment.mimeType,\n fileSize: sourceAttachment.fileSize,\n storageDriver: sourceAttachment.storageDriver,\n storagePath: sourceAttachment.storagePath,\n storageMetadata: sourceAttachment.storageMetadata,\n url: sourceAttachment.url,\n partitionCode: sourceAttachment.partitionCode,\n })\n\n em.persist(copy)\n copied += 1\n }\n\n if (copied > 0) {\n await em.flush()\n }\n return copied\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.6-develop.6385.1.9a81faa5f0",
3
+ "version": "0.6.6-develop.6386.1.391a1afb9e",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -246,16 +246,16 @@
246
246
  "zod": "^4.4.3"
247
247
  },
248
248
  "peerDependencies": {
249
- "@open-mercato/ai-assistant": "0.6.6-develop.6385.1.9a81faa5f0",
250
- "@open-mercato/shared": "0.6.6-develop.6385.1.9a81faa5f0",
251
- "@open-mercato/ui": "0.6.6-develop.6385.1.9a81faa5f0",
249
+ "@open-mercato/ai-assistant": "0.6.6-develop.6386.1.391a1afb9e",
250
+ "@open-mercato/shared": "0.6.6-develop.6386.1.391a1afb9e",
251
+ "@open-mercato/ui": "0.6.6-develop.6386.1.391a1afb9e",
252
252
  "react": "^19.0.0",
253
253
  "react-dom": "^19.0.0"
254
254
  },
255
255
  "devDependencies": {
256
- "@open-mercato/ai-assistant": "0.6.6-develop.6385.1.9a81faa5f0",
257
- "@open-mercato/shared": "0.6.6-develop.6385.1.9a81faa5f0",
258
- "@open-mercato/ui": "0.6.6-develop.6385.1.9a81faa5f0",
256
+ "@open-mercato/ai-assistant": "0.6.6-develop.6386.1.391a1afb9e",
257
+ "@open-mercato/shared": "0.6.6-develop.6386.1.391a1afb9e",
258
+ "@open-mercato/ui": "0.6.6-develop.6386.1.391a1afb9e",
259
259
  "@testing-library/dom": "^10.4.1",
260
260
  "@testing-library/jest-dom": "^6.9.1",
261
261
  "@testing-library/react": "^16.3.1",
@@ -0,0 +1,42 @@
1
+ export type AttachmentScopePair = {
2
+ organizationId: string | null
3
+ tenantId: string | null
4
+ }
5
+
6
+ export type AttachmentScopeCandidate = {
7
+ organizationId?: string | null
8
+ tenantId?: string | null
9
+ }
10
+
11
+ function normalizeScopeValue(value: string | null | undefined): string | null {
12
+ if (typeof value !== 'string') return value ?? null
13
+ const trimmed = value.trim()
14
+ return trimmed.length > 0 ? trimmed : null
15
+ }
16
+
17
+ export function isValidAttachmentScopePair(candidate: AttachmentScopeCandidate): boolean {
18
+ const organizationId = normalizeScopeValue(candidate.organizationId)
19
+ const tenantId = normalizeScopeValue(candidate.tenantId)
20
+ return (organizationId === null) === (tenantId === null)
21
+ }
22
+
23
+ /**
24
+ * Returns the first candidate whose tenant/organization columns form a valid
25
+ * "both set or both null" scope pair (blank/whitespace treated as null), or
26
+ * `null` when no candidate satisfies the invariant. Copy/clone sites use this
27
+ * to carry a scope pair across as a unit instead of coalescing the two columns
28
+ * independently, which is what allows a partial-null row to be constructed.
29
+ */
30
+ export function resolveAttachmentScopePair(
31
+ ...candidates: Array<AttachmentScopeCandidate | null | undefined>
32
+ ): AttachmentScopePair | null {
33
+ for (const candidate of candidates) {
34
+ if (!candidate) continue
35
+ const organizationId = normalizeScopeValue(candidate.organizationId)
36
+ const tenantId = normalizeScopeValue(candidate.tenantId)
37
+ if ((organizationId === null) === (tenantId === null)) {
38
+ return { organizationId, tenantId }
39
+ }
40
+ }
41
+ return null
42
+ }
@@ -19,6 +19,7 @@ import {
19
19
  } from '../data/entities'
20
20
  import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
21
21
  import { Attachment } from '@open-mercato/core/modules/attachments/data/entities'
22
+ import { resolveAttachmentScopePair } from '@open-mercato/core/modules/attachments/lib/scope'
22
23
  import {
23
24
  variantCreateSchema,
24
25
  variantUpdateSchema,
@@ -562,11 +563,15 @@ async function aggregateVariantMediaToProduct(
562
563
  for (const source of attachments) {
563
564
  const key = buildKey(source)
564
565
  if (existingKeys.has(key)) continue
566
+ // Carry a scope pair across as a unit (source row first, else the variant
567
+ // scope, else global) so independent `??` coalescing can't produce a
568
+ // partial-null attachment row.
569
+ const scope = resolveAttachmentScopePair(source, variant) ?? { organizationId: null, tenantId: null }
565
570
  const clone = em.create(Attachment, {
566
571
  entityId: E.catalog.catalog_product,
567
572
  recordId: productId,
568
- organizationId: source.organizationId ?? variant.organizationId ?? null,
569
- tenantId: source.tenantId ?? variant.tenantId ?? null,
573
+ organizationId: scope.organizationId,
574
+ tenantId: scope.tenantId,
570
575
  partitionCode: source.partitionCode,
571
576
  fileName: source.fileName,
572
577
  mimeType: source.mimeType,
@@ -933,7 +933,6 @@ const forwardMessageCommand: CommandHandler<unknown, { id: string; externalEmail
933
933
  trx,
934
934
  forwardThreadSlice.map((item) => item.id),
935
935
  newMessage.id,
936
- input.organizationId,
937
936
  input.tenantId,
938
937
  )
939
938
  }
@@ -1,4 +1,5 @@
1
1
  import type { EntityManager } from '@mikro-orm/core'
2
+ import { resolveAttachmentScopePair } from '@open-mercato/core/modules/attachments/lib/scope'
2
3
  import { MESSAGE_ATTACHMENT_ENTITY_ID } from './constants'
3
4
  const LIBRARY_ATTACHMENT_ENTITY_ID = 'attachments:library'
4
5
 
@@ -135,14 +136,12 @@ export async function copyAttachmentsForForward(
135
136
  em: EntityManager,
136
137
  sourceMessageId: string,
137
138
  targetMessageId: string,
138
- organizationId: string | null,
139
139
  tenantId: string,
140
140
  ): Promise<number> {
141
141
  return copyAttachmentsForForwardMessages(
142
142
  em,
143
143
  [sourceMessageId],
144
144
  targetMessageId,
145
- organizationId,
146
145
  tenantId,
147
146
  )
148
147
  }
@@ -151,7 +150,6 @@ export async function copyAttachmentsForForwardMessages(
151
150
  em: EntityManager,
152
151
  sourceMessageIds: string[],
153
152
  targetMessageId: string,
154
- targetOrganizationId: string | null,
155
153
  tenantId: string,
156
154
  ): Promise<number> {
157
155
  if (sourceMessageIds.length === 0) return 0
@@ -173,12 +171,19 @@ export async function copyAttachmentsForForwardMessages(
173
171
  }
174
172
  }
175
173
 
174
+ let copied = 0
176
175
  for (const sourceAttachment of dedupedById.values()) {
176
+ // Carry the source attachment's scope across as a unit so a forwarded copy
177
+ // can never become a partial-null row. Source rows that are themselves
178
+ // partial-null are unreadable dead data; skip rather than propagate them.
179
+ const scope = resolveAttachmentScopePair(sourceAttachment)
180
+ if (!scope) continue
181
+
177
182
  const copy = em.create(Attachment, {
178
183
  entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
179
184
  recordId: targetMessageId,
180
- organizationId: targetOrganizationId,
181
- tenantId,
185
+ organizationId: scope.organizationId,
186
+ tenantId: scope.tenantId,
182
187
  fileName: sourceAttachment.fileName,
183
188
  mimeType: sourceAttachment.mimeType,
184
189
  fileSize: sourceAttachment.fileSize,
@@ -190,8 +195,11 @@ export async function copyAttachmentsForForwardMessages(
190
195
  })
191
196
 
192
197
  em.persist(copy)
198
+ copied += 1
193
199
  }
194
200
 
195
- await em.flush()
196
- return dedupedById.size
201
+ if (copied > 0) {
202
+ await em.flush()
203
+ }
204
+ return copied
197
205
  }