@open-mercato/core 0.9.1-develop.7513.1.ae2715553f → 0.9.1-develop.7516.1.176184247d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/communication_channels/lib/send-as-user.js.map +2 -2
  3. package/dist/modules/customers/api/people/[id]/emails/route.js +44 -1
  4. package/dist/modules/customers/api/people/[id]/emails/route.js.map +2 -2
  5. package/dist/modules/customers/lib/personEmailThreads.js +36 -1
  6. package/dist/modules/customers/lib/personEmailThreads.js.map +2 -2
  7. package/dist/modules/messages/api/[id]/route.js +3 -9
  8. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  9. package/dist/modules/messages/api/route.js +33 -16
  10. package/dist/modules/messages/api/route.js.map +2 -2
  11. package/dist/modules/messages/commands/messages.js.map +2 -2
  12. package/dist/modules/messages/lib/routeHelpers.js +40 -1
  13. package/dist/modules/messages/lib/routeHelpers.js.map +2 -2
  14. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +4 -7
  15. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
  16. package/dist/modules/shipping_carriers/lib/shipping-service.js +29 -11
  17. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
  18. package/dist/modules/shipping_carriers/lib/status-sync.js +2 -2
  19. package/dist/modules/shipping_carriers/lib/status-sync.js.map +2 -2
  20. package/dist/modules/shipping_carriers/workers/webhook-processor.js +60 -33
  21. package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +3 -3
  22. package/dist/modules/warranty_claims/commands/claim-lines.js +89 -68
  23. package/dist/modules/warranty_claims/commands/claim-lines.js.map +2 -2
  24. package/dist/modules/warranty_claims/commands/claims.js +14 -13
  25. package/dist/modules/warranty_claims/commands/claims.js.map +2 -2
  26. package/dist/modules/warranty_claims/commands/shared.js +8 -2
  27. package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
  28. package/package.json +7 -7
  29. package/src/modules/communication_channels/lib/send-as-user.ts +4 -0
  30. package/src/modules/customers/api/people/[id]/emails/route.ts +67 -1
  31. package/src/modules/customers/i18n/de.json +1 -0
  32. package/src/modules/customers/i18n/en.json +1 -0
  33. package/src/modules/customers/i18n/es.json +1 -0
  34. package/src/modules/customers/i18n/ko.json +1 -0
  35. package/src/modules/customers/i18n/pl.json +1 -0
  36. package/src/modules/customers/lib/personEmailThreads.ts +61 -0
  37. package/src/modules/messages/api/[id]/route.ts +2 -9
  38. package/src/modules/messages/api/route.ts +42 -20
  39. package/src/modules/messages/commands/messages.ts +7 -0
  40. package/src/modules/messages/i18n/de.json +1 -0
  41. package/src/modules/messages/i18n/en.json +1 -0
  42. package/src/modules/messages/i18n/es.json +1 -0
  43. package/src/modules/messages/i18n/ko.json +1 -0
  44. package/src/modules/messages/i18n/pl.json +1 -0
  45. package/src/modules/messages/lib/routeHelpers.ts +84 -1
  46. package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +4 -7
  47. package/src/modules/shipping_carriers/lib/shipping-service.ts +29 -11
  48. package/src/modules/shipping_carriers/lib/status-sync.ts +2 -2
  49. package/src/modules/shipping_carriers/workers/webhook-processor.ts +71 -39
  50. package/src/modules/warranty_claims/commands/claim-lines.ts +107 -66
  51. package/src/modules/warranty_claims/commands/claims.ts +14 -13
  52. package/src/modules/warranty_claims/commands/shared.ts +7 -1
@@ -1,3 +1,4 @@
1
+ import { LockMode } from "@mikro-orm/core";
1
2
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
3
  import { CarrierShipment } from "../data/entities.js";
3
4
  import { emitShippingEvent } from "../events.js";
@@ -152,7 +153,7 @@ function createShippingCarrierService(deps) {
152
153
  trackingNumber: created.trackingNumber,
153
154
  organizationId: input.organizationId,
154
155
  tenantId: input.tenantId
155
- });
156
+ }, { tenantId: input.tenantId, organizationId: input.organizationId });
156
157
  return shipment;
157
158
  } catch (error) {
158
159
  if (claim && !carrierShipmentCreated && input.idempotencyKey && !isShipmentIdempotencyConflictError(error)) {
@@ -193,24 +194,41 @@ function createShippingCarrierService(deps) {
193
194
  trackingNumber: shipment.trackingNumber,
194
195
  credentials
195
196
  });
196
- const previousStatus = shipment.unifiedStatus;
197
- shipment.trackingEvents = tracking.events;
198
- shipment.lastPolledAt = /* @__PURE__ */ new Date();
199
- const transitionApplied = syncShipmentStatus(shipment, tracking.status);
200
- await em.flush();
201
- if (transitionApplied) {
197
+ const outcome = await em.transactional(async (tx) => {
198
+ const locked = await findOneWithDecryption(
199
+ tx,
200
+ CarrierShipment,
201
+ {
202
+ id: shipment.id,
203
+ organizationId: scope.organizationId,
204
+ tenantId: scope.tenantId,
205
+ deletedAt: null
206
+ },
207
+ { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true },
208
+ scope
209
+ );
210
+ if (!locked) return null;
211
+ const previousStatus = locked.unifiedStatus;
212
+ locked.trackingEvents = tracking.events;
213
+ locked.lastPolledAt = /* @__PURE__ */ new Date();
214
+ const applied = syncShipmentStatus(locked, tracking.status);
215
+ await tx.flush();
216
+ return { applied, previousStatus };
217
+ }, { clear: true });
218
+ if (outcome?.applied) {
202
219
  const eventPayload = {
203
220
  shipmentId: shipment.id,
204
221
  providerKey: input.providerKey,
205
- previousStatus,
222
+ previousStatus: outcome.previousStatus,
206
223
  newStatus: tracking.status,
207
224
  organizationId: input.organizationId,
208
225
  tenantId: input.tenantId
209
226
  };
210
- await emitShippingEvent("shipping_carriers.shipment.status_changed", eventPayload);
227
+ const eventScope = { tenantId: input.tenantId, organizationId: input.organizationId };
228
+ await emitShippingEvent("shipping_carriers.shipment.status_changed", eventPayload, eventScope);
211
229
  if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {
212
230
  const terminalEvent = getTerminalShippingEvent(tracking.status);
213
- if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload);
231
+ if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload, eventScope);
214
232
  }
215
233
  }
216
234
  return tracking;
@@ -237,7 +255,7 @@ function createShippingCarrierService(deps) {
237
255
  providerKey: input.providerKey,
238
256
  organizationId: input.organizationId,
239
257
  tenantId: input.tenantId
240
- });
258
+ }, { tenantId: input.tenantId, organizationId: input.organizationId });
241
259
  return result;
242
260
  },
243
261
  async findShipmentByCarrierId(providerKey, carrierShipmentId, scope) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/lib/shipping-service.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment, type CarrierShipmentIdempotencyKey } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport {\n claimShipmentIdempotency,\n computeShipmentRequestHash,\n findShipmentIdempotencyClaim,\n isShipmentIdempotencyConflictError,\n releaseShipmentIdempotency,\n resolveShipmentIdempotency,\n ShipmentIdempotencyConflictError,\n} from './shipment-idempotency'\nimport {\n getTerminalShippingEvent,\n isValidShippingTransition,\n ShipmentCancelNotAllowedError,\n syncShipmentStatus,\n TERMINAL_SHIPPING_STATUSES,\n} from './status-sync'\n\nexport class CarrierShipmentNotFoundError extends Error {\n constructor() {\n super('Shipment not found')\n this.name = 'CarrierShipmentNotFoundError'\n }\n}\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new CarrierShipmentNotFoundError()\n }\n return shipment\n }\n\n async function findShipmentByTrackingNumberOrThrow(\n providerKey: string,\n trackingNumber: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n trackingNumber,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new CarrierShipmentNotFoundError()\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n idempotencyKey?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n let claim: CarrierShipmentIdempotencyKey | null = null\n if (input.idempotencyKey) {\n const { idempotencyKey: _idempotencyKey, ...fingerprint } = input\n const requestHash = computeShipmentRequestHash(fingerprint)\n const existing = await findShipmentIdempotencyClaim(em, input.idempotencyKey, input.providerKey, scope)\n if (existing) {\n if (existing.requestHash !== requestHash) {\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n if (existing.shipmentId) {\n return findShipmentOrThrow(existing.shipmentId, scope)\n }\n // Claim exists but the original request has not resolved yet (concurrent in-flight).\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n claim = await claimShipmentIdempotency(em, input.idempotencyKey, input.providerKey, requestHash, scope)\n if (!claim) {\n // Lost the claim race with a concurrent request creating the same shipment.\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n }\n let carrierShipmentCreated = false\n try {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n carrierShipmentCreated = true\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(shipment).flush()\n if (claim) {\n await resolveShipmentIdempotency(em, claim, shipment.id)\n }\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n } catch (error: unknown) {\n // Release the claim only when the carrier was NOT successfully called, so a retry can\n // proceed. If the carrier already created the shipment, keep the claim so a retry does\n // not produce a duplicate upstream shipment.\n if (claim && !carrierShipmentCreated && input.idempotencyKey && !isShipmentIdempotencyConflictError(error)) {\n await releaseShipmentIdempotency(em, input.idempotencyKey, input.providerKey, scope).catch(() => {})\n }\n throw error\n }\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n let shipment: CarrierShipment\n if (input.shipmentId) {\n shipment = await findShipmentOrThrow(input.shipmentId, scope)\n } else {\n const trackingNumber = input.trackingNumber\n if (!trackingNumber) {\n throw new CarrierShipmentNotFoundError()\n }\n shipment = await findShipmentByTrackingNumberOrThrow(input.providerKey, trackingNumber, scope)\n }\n return adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n },\n\n async refreshTracking(input: {\n providerKey: string\n shipmentId: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const tracking = await adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n const previousStatus = shipment.unifiedStatus\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n const transitionApplied = syncShipmentStatus(shipment, tracking.status)\n await em.flush()\n if (transitionApplied) {\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n previousStatus,\n newStatus: tracking.status,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload)\n if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {\n const terminalEvent = getTerminalShippingEvent(tracking.status)\n if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload)\n }\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment, type CarrierShipmentIdempotencyKey } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport {\n claimShipmentIdempotency,\n computeShipmentRequestHash,\n findShipmentIdempotencyClaim,\n isShipmentIdempotencyConflictError,\n releaseShipmentIdempotency,\n resolveShipmentIdempotency,\n ShipmentIdempotencyConflictError,\n} from './shipment-idempotency'\nimport {\n getTerminalShippingEvent,\n isValidShippingTransition,\n ShipmentCancelNotAllowedError,\n syncShipmentStatus,\n TERMINAL_SHIPPING_STATUSES,\n} from './status-sync'\n\nexport class CarrierShipmentNotFoundError extends Error {\n constructor() {\n super('Shipment not found')\n this.name = 'CarrierShipmentNotFoundError'\n }\n}\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new CarrierShipmentNotFoundError()\n }\n return shipment\n }\n\n async function findShipmentByTrackingNumberOrThrow(\n providerKey: string,\n trackingNumber: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n trackingNumber,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new CarrierShipmentNotFoundError()\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n idempotencyKey?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n let claim: CarrierShipmentIdempotencyKey | null = null\n if (input.idempotencyKey) {\n const { idempotencyKey: _idempotencyKey, ...fingerprint } = input\n const requestHash = computeShipmentRequestHash(fingerprint)\n const existing = await findShipmentIdempotencyClaim(em, input.idempotencyKey, input.providerKey, scope)\n if (existing) {\n if (existing.requestHash !== requestHash) {\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n if (existing.shipmentId) {\n return findShipmentOrThrow(existing.shipmentId, scope)\n }\n // Claim exists but the original request has not resolved yet (concurrent in-flight).\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n claim = await claimShipmentIdempotency(em, input.idempotencyKey, input.providerKey, requestHash, scope)\n if (!claim) {\n // Lost the claim race with a concurrent request creating the same shipment.\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n }\n let carrierShipmentCreated = false\n try {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n carrierShipmentCreated = true\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(shipment).flush()\n if (claim) {\n await resolveShipmentIdempotency(em, claim, shipment.id)\n }\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }, { tenantId: input.tenantId, organizationId: input.organizationId })\n return shipment\n } catch (error: unknown) {\n // Release the claim only when the carrier was NOT successfully called, so a retry can\n // proceed. If the carrier already created the shipment, keep the claim so a retry does\n // not produce a duplicate upstream shipment.\n if (claim && !carrierShipmentCreated && input.idempotencyKey && !isShipmentIdempotencyConflictError(error)) {\n await releaseShipmentIdempotency(em, input.idempotencyKey, input.providerKey, scope).catch(() => {})\n }\n throw error\n }\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n let shipment: CarrierShipment\n if (input.shipmentId) {\n shipment = await findShipmentOrThrow(input.shipmentId, scope)\n } else {\n const trackingNumber = input.trackingNumber\n if (!trackingNumber) {\n throw new CarrierShipmentNotFoundError()\n }\n shipment = await findShipmentByTrackingNumberOrThrow(input.providerKey, trackingNumber, scope)\n }\n return adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n },\n\n async refreshTracking(input: {\n providerKey: string\n shipmentId: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const tracking = await adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n const outcome = await em.transactional(async (tx) => {\n const locked = await findOneWithDecryption(\n tx,\n CarrierShipment,\n {\n id: shipment.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true },\n scope,\n )\n if (!locked) return null\n const previousStatus = locked.unifiedStatus\n locked.trackingEvents = tracking.events\n locked.lastPolledAt = new Date()\n const applied = syncShipmentStatus(locked, tracking.status)\n await tx.flush()\n return { applied, previousStatus }\n }, { clear: true })\n if (outcome?.applied) {\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n previousStatus: outcome.previousStatus,\n newStatus: tracking.status,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n const eventScope = { tenantId: input.tenantId, organizationId: input.organizationId }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload, eventScope)\n if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {\n const terminalEvent = getTerminalShippingEvent(tracking.status)\n if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload, eventScope)\n }\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }, { tenantId: input.tenantId, organizationId: input.organizationId })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -11,8 +11,8 @@ function isShipmentCancelNotAllowedError(error) {
11
11
  return !!error && typeof error === "object" && error[SHIPMENT_CANCEL_NOT_ALLOWED_MARKER] === true;
12
12
  }
13
13
  const VALID_SHIPPING_TRANSITIONS = {
14
- label_created: ["picked_up", "in_transit", "cancelled"],
15
- picked_up: ["in_transit", "cancelled"],
14
+ label_created: ["picked_up", "in_transit", "out_for_delivery", "delivered", "failed_delivery", "returned", "cancelled"],
15
+ picked_up: ["in_transit", "out_for_delivery", "delivered", "failed_delivery", "returned", "cancelled"],
16
16
  in_transit: ["out_for_delivery", "delivered", "returned", "failed_delivery"],
17
17
  out_for_delivery: ["delivered", "returned", "failed_delivery"],
18
18
  failed_delivery: ["in_transit", "out_for_delivery", "delivered", "returned", "cancelled"]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/lib/status-sync.ts"],
4
- "sourcesContent": ["import type { UnifiedShipmentStatus } from './adapter'\nimport type { CarrierShipment } from '../data/entities'\nimport type { ShippingEventId } from '../events'\n\n// Use Symbol.for so the marker survives module duplication across bundle\n// boundaries \u2014 production builds can split this class into separate chunks,\n// which breaks `instanceof` (see isCrudHttpError in\n// @open-mercato/shared/lib/crud/errors for the same pattern).\nconst SHIPMENT_CANCEL_NOT_ALLOWED_MARKER = Symbol.for('@open-mercato/shipping_carriers/ShipmentCancelNotAllowedError')\n\nexport class ShipmentCancelNotAllowedError extends Error {\n readonly [SHIPMENT_CANCEL_NOT_ALLOWED_MARKER] = true\n\n constructor(status: string) {\n super(`Shipment cannot be cancelled in its current status: ${status}`)\n this.name = 'ShipmentCancelNotAllowedError'\n }\n}\n\n/**\n * Type-safe check that works across module/bundle boundaries. Prefer this over\n * `instanceof ShipmentCancelNotAllowedError` in route handlers, where the thrown\n * error may originate from a different bundle than the one performing the check.\n */\nexport function isShipmentCancelNotAllowedError(error: unknown): error is ShipmentCancelNotAllowedError {\n return !!error\n && typeof error === 'object'\n && (error as Record<symbol, unknown>)[SHIPMENT_CANCEL_NOT_ALLOWED_MARKER] === true\n}\n\nconst VALID_SHIPPING_TRANSITIONS: Record<string, UnifiedShipmentStatus[]> = {\n label_created: ['picked_up', 'in_transit', 'cancelled'],\n picked_up: ['in_transit', 'cancelled'],\n in_transit: ['out_for_delivery', 'delivered', 'returned', 'failed_delivery'],\n out_for_delivery: ['delivered', 'returned', 'failed_delivery'],\n failed_delivery: ['in_transit', 'out_for_delivery', 'delivered', 'returned', 'cancelled'],\n}\n\nexport const TERMINAL_SHIPPING_STATUSES: Set<UnifiedShipmentStatus> = new Set([\n 'delivered',\n 'returned',\n 'cancelled',\n])\n\nexport function isValidShippingTransition(from: UnifiedShipmentStatus, to: UnifiedShipmentStatus): boolean {\n if (from === to) return false\n const allowed = VALID_SHIPPING_TRANSITIONS[from]\n if (!allowed) return false\n return allowed.includes(to)\n}\n\nexport function syncShipmentStatus(shipment: CarrierShipment, newStatus: UnifiedShipmentStatus): boolean {\n const currentStatus = shipment.unifiedStatus as UnifiedShipmentStatus\n if (!isValidShippingTransition(currentStatus, newStatus)) return false\n shipment.unifiedStatus = newStatus\n return true\n}\n\nexport function getTerminalShippingEvent(status: UnifiedShipmentStatus): ShippingEventId | null {\n if (status === 'delivered') return 'shipping_carriers.shipment.delivered'\n if (status === 'returned') return 'shipping_carriers.shipment.returned'\n if (status === 'cancelled') return 'shipping_carriers.shipment.cancelled'\n return null\n}\n"],
5
- "mappings": "AAAA;AAQA,MAAM,qCAAqC,uBAAO,IAAI,+DAA+D;AAE9G,MAAM,uCAAsC,YACvC,yCADuC,IAAM;AAAA,EAGvD,YAAY,QAAgB;AAC1B,UAAM,uDAAuD,MAAM,EAAE;AAHvE,SAAU,MAAsC;AAI9C,SAAK,OAAO;AAAA,EACd;AACF;AAOO,SAAS,gCAAgC,OAAwD;AACtG,SAAO,CAAC,CAAC,SACJ,OAAO,UAAU,YAChB,MAAkC,kCAAkC,MAAM;AAClF;AAEA,MAAM,6BAAsE;AAAA,EAC1E,eAAe,CAAC,aAAa,cAAc,WAAW;AAAA,EACtD,WAAW,CAAC,cAAc,WAAW;AAAA,EACrC,YAAY,CAAC,oBAAoB,aAAa,YAAY,iBAAiB;AAAA,EAC3E,kBAAkB,CAAC,aAAa,YAAY,iBAAiB;AAAA,EAC7D,iBAAiB,CAAC,cAAc,oBAAoB,aAAa,YAAY,WAAW;AAC1F;AAEO,MAAM,6BAAyD,oBAAI,IAAI;AAAA,EAC5E;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,0BAA0B,MAA6B,IAAoC;AACzG,MAAI,SAAS,GAAI,QAAO;AACxB,QAAM,UAAU,2BAA2B,IAAI;AAC/C,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,SAAS,EAAE;AAC5B;AAEO,SAAS,mBAAmB,UAA2B,WAA2C;AACvG,QAAM,gBAAgB,SAAS;AAC/B,MAAI,CAAC,0BAA0B,eAAe,SAAS,EAAG,QAAO;AACjE,WAAS,gBAAgB;AACzB,SAAO;AACT;AAEO,SAAS,yBAAyB,QAAuD;AAC9F,MAAI,WAAW,YAAa,QAAO;AACnC,MAAI,WAAW,WAAY,QAAO;AAClC,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;",
4
+ "sourcesContent": ["import type { UnifiedShipmentStatus } from './adapter'\nimport type { CarrierShipment } from '../data/entities'\nimport type { ShippingEventId } from '../events'\n\n// Use Symbol.for so the marker survives module duplication across bundle\n// boundaries \u2014 production builds can split this class into separate chunks,\n// which breaks `instanceof` (see isCrudHttpError in\n// @open-mercato/shared/lib/crud/errors for the same pattern).\nconst SHIPMENT_CANCEL_NOT_ALLOWED_MARKER = Symbol.for('@open-mercato/shipping_carriers/ShipmentCancelNotAllowedError')\n\nexport class ShipmentCancelNotAllowedError extends Error {\n readonly [SHIPMENT_CANCEL_NOT_ALLOWED_MARKER] = true\n\n constructor(status: string) {\n super(`Shipment cannot be cancelled in its current status: ${status}`)\n this.name = 'ShipmentCancelNotAllowedError'\n }\n}\n\n/**\n * Type-safe check that works across module/bundle boundaries. Prefer this over\n * `instanceof ShipmentCancelNotAllowedError` in route handlers, where the thrown\n * error may originate from a different bundle than the one performing the check.\n */\nexport function isShipmentCancelNotAllowedError(error: unknown): error is ShipmentCancelNotAllowedError {\n return !!error\n && typeof error === 'object'\n && (error as Record<symbol, unknown>)[SHIPMENT_CANCEL_NOT_ALLOWED_MARKER] === true\n}\n\nconst VALID_SHIPPING_TRANSITIONS: Record<string, UnifiedShipmentStatus[]> = {\n label_created: ['picked_up', 'in_transit', 'out_for_delivery', 'delivered', 'failed_delivery', 'returned', 'cancelled'],\n picked_up: ['in_transit', 'out_for_delivery', 'delivered', 'failed_delivery', 'returned', 'cancelled'],\n in_transit: ['out_for_delivery', 'delivered', 'returned', 'failed_delivery'],\n out_for_delivery: ['delivered', 'returned', 'failed_delivery'],\n failed_delivery: ['in_transit', 'out_for_delivery', 'delivered', 'returned', 'cancelled'],\n}\n\nexport const TERMINAL_SHIPPING_STATUSES: Set<UnifiedShipmentStatus> = new Set([\n 'delivered',\n 'returned',\n 'cancelled',\n])\n\nexport function isValidShippingTransition(from: UnifiedShipmentStatus, to: UnifiedShipmentStatus): boolean {\n if (from === to) return false\n const allowed = VALID_SHIPPING_TRANSITIONS[from]\n if (!allowed) return false\n return allowed.includes(to)\n}\n\nexport function syncShipmentStatus(shipment: CarrierShipment, newStatus: UnifiedShipmentStatus): boolean {\n const currentStatus = shipment.unifiedStatus as UnifiedShipmentStatus\n if (!isValidShippingTransition(currentStatus, newStatus)) return false\n shipment.unifiedStatus = newStatus\n return true\n}\n\nexport function getTerminalShippingEvent(status: UnifiedShipmentStatus): ShippingEventId | null {\n if (status === 'delivered') return 'shipping_carriers.shipment.delivered'\n if (status === 'returned') return 'shipping_carriers.shipment.returned'\n if (status === 'cancelled') return 'shipping_carriers.shipment.cancelled'\n return null\n}\n"],
5
+ "mappings": "AAAA;AAQA,MAAM,qCAAqC,uBAAO,IAAI,+DAA+D;AAE9G,MAAM,uCAAsC,YACvC,yCADuC,IAAM;AAAA,EAGvD,YAAY,QAAgB;AAC1B,UAAM,uDAAuD,MAAM,EAAE;AAHvE,SAAU,MAAsC;AAI9C,SAAK,OAAO;AAAA,EACd;AACF;AAOO,SAAS,gCAAgC,OAAwD;AACtG,SAAO,CAAC,CAAC,SACJ,OAAO,UAAU,YAChB,MAAkC,kCAAkC,MAAM;AAClF;AAEA,MAAM,6BAAsE;AAAA,EAC1E,eAAe,CAAC,aAAa,cAAc,oBAAoB,aAAa,mBAAmB,YAAY,WAAW;AAAA,EACtH,WAAW,CAAC,cAAc,oBAAoB,aAAa,mBAAmB,YAAY,WAAW;AAAA,EACrG,YAAY,CAAC,oBAAoB,aAAa,YAAY,iBAAiB;AAAA,EAC3E,kBAAkB,CAAC,aAAa,YAAY,iBAAiB;AAAA,EAC7D,iBAAiB,CAAC,cAAc,oBAAoB,aAAa,YAAY,WAAW;AAC1F;AAEO,MAAM,6BAAyD,oBAAI,IAAI;AAAA,EAC5E;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,0BAA0B,MAA6B,IAAoC;AACzG,MAAI,SAAS,GAAI,QAAO;AACxB,QAAM,UAAU,2BAA2B,IAAI;AAC/C,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,SAAS,EAAE;AAC5B;AAEO,SAAS,mBAAmB,UAA2B,WAA2C;AACvG,QAAM,gBAAgB,SAAS;AAC/B,MAAI,CAAC,0BAA0B,eAAe,SAAS,EAAG,QAAO;AACjE,WAAS,gBAAgB;AACzB,SAAO;AACT;AAEO,SAAS,yBAAyB,QAAuD;AAC9F,MAAI,WAAW,YAAa,QAAO;AACnC,MAAI,WAAW,WAAY,QAAO;AAClC,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { LockMode } from "@mikro-orm/core";
1
2
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
3
  import { CarrierShipment } from "../data/entities.js";
3
4
  import { emitShippingEvent } from "../events.js";
@@ -6,73 +7,99 @@ import { getTerminalShippingEvent, syncShipmentStatus, TERMINAL_SHIPPING_STATUSE
6
7
  import { claimWebhookProcessing, releaseWebhookClaim } from "../lib/webhook-utils.js";
7
8
  import { createLogger } from "@open-mercato/shared/lib/logger";
8
9
  const logger = createLogger("shipping_carriers").child({ component: "webhook-processor" });
10
+ function readWebhookJobPayload(data) {
11
+ if ("providerKey" in data) return data;
12
+ return data.payload;
13
+ }
9
14
  const metadata = {
10
15
  queue: "shipping-carriers-webhook",
11
16
  id: "shipping-carriers:webhook-processor",
12
17
  concurrency: 5
13
18
  };
14
19
  async function handle(job, ctx) {
20
+ const payload = readWebhookJobPayload(job.payload);
15
21
  try {
16
22
  const em = ctx.resolve("em");
17
- const adapter = getShippingAdapter(job.payload.providerKey);
23
+ const adapter = getShippingAdapter(payload.providerKey);
18
24
  if (!adapter) return;
19
- const shipment = job.payload.shipmentId && job.payload.scope ? await findOneWithDecryption(
25
+ const shipment = payload.shipmentId && payload.scope ? await findOneWithDecryption(
20
26
  em,
21
27
  CarrierShipment,
22
28
  {
23
- id: job.payload.shipmentId,
24
- organizationId: job.payload.scope.organizationId,
25
- tenantId: job.payload.scope.tenantId,
29
+ id: payload.shipmentId,
30
+ organizationId: payload.scope.organizationId,
31
+ tenantId: payload.scope.tenantId,
26
32
  deletedAt: null
27
33
  },
28
34
  void 0,
29
- job.payload.scope
35
+ payload.scope
30
36
  ) : null;
31
37
  if (!shipment) return;
32
38
  const scope = { organizationId: shipment.organizationId, tenantId: shipment.tenantId };
33
39
  const claimed = await claimWebhookProcessing(
34
40
  em,
35
- job.payload.event.idempotencyKey,
36
- job.payload.providerKey,
41
+ payload.event.idempotencyKey,
42
+ payload.providerKey,
37
43
  scope,
38
- job.payload.event.eventType
44
+ payload.event.eventType
39
45
  );
40
46
  if (!claimed) {
41
47
  return;
42
48
  }
49
+ const carrierStatus = typeof payload.event.data.status === "string" ? payload.event.data.status : payload.event.eventType;
50
+ let transition = null;
43
51
  try {
44
- const carrierStatus = typeof job.payload.event.data.status === "string" ? job.payload.event.data.status : job.payload.event.eventType;
45
- const unifiedStatus = adapter.mapStatus(carrierStatus);
46
- shipment.carrierStatus = carrierStatus;
47
- shipment.lastWebhookAt = /* @__PURE__ */ new Date();
48
- const transitionApplied = syncShipmentStatus(shipment, unifiedStatus);
49
- if (!transitionApplied) return;
50
- await em.flush();
51
- const eventPayload = {
52
- shipmentId: shipment.id,
53
- providerKey: job.payload.providerKey,
54
- previousStatus: carrierStatus,
55
- newStatus: unifiedStatus,
56
- organizationId: shipment.organizationId,
57
- tenantId: shipment.tenantId
58
- };
59
- await emitShippingEvent("shipping_carriers.shipment.status_changed", eventPayload);
60
- if (TERMINAL_SHIPPING_STATUSES.has(unifiedStatus)) {
61
- const terminalEvent = getTerminalShippingEvent(unifiedStatus);
62
- if (!terminalEvent) return;
63
- await emitShippingEvent(terminalEvent, eventPayload);
64
- }
52
+ const unifiedStatus2 = adapter.mapStatus(carrierStatus);
53
+ transition = await em.transactional(async (tx) => {
54
+ const locked = await findOneWithDecryption(
55
+ tx,
56
+ CarrierShipment,
57
+ {
58
+ id: shipment.id,
59
+ organizationId: scope.organizationId,
60
+ tenantId: scope.tenantId,
61
+ deletedAt: null
62
+ },
63
+ { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true },
64
+ scope
65
+ );
66
+ if (!locked) return null;
67
+ const previousStatus = locked.unifiedStatus;
68
+ if (!syncShipmentStatus(locked, unifiedStatus2)) return null;
69
+ locked.carrierStatus = carrierStatus;
70
+ locked.lastWebhookAt = /* @__PURE__ */ new Date();
71
+ await tx.flush();
72
+ return { previousStatus, newStatus: unifiedStatus2 };
73
+ }, { clear: true });
65
74
  } catch (error) {
66
75
  await releaseWebhookClaim(
67
76
  em,
68
- job.payload.event.idempotencyKey,
69
- job.payload.providerKey,
77
+ payload.event.idempotencyKey,
78
+ payload.providerKey,
70
79
  scope
71
80
  );
72
81
  throw error;
73
82
  }
83
+ if (!transition) return;
84
+ const unifiedStatus = transition.newStatus;
85
+ const eventPayload = {
86
+ shipmentId: shipment.id,
87
+ providerKey: payload.providerKey,
88
+ previousStatus: transition.previousStatus,
89
+ newStatus: unifiedStatus,
90
+ carrierStatus,
91
+ organizationId: shipment.organizationId,
92
+ tenantId: shipment.tenantId
93
+ };
94
+ const eventScope = { tenantId: shipment.tenantId, organizationId: shipment.organizationId };
95
+ await emitShippingEvent("shipping_carriers.shipment.status_changed", eventPayload, eventScope);
96
+ if (TERMINAL_SHIPPING_STATUSES.has(unifiedStatus)) {
97
+ const terminalEvent = getTerminalShippingEvent(unifiedStatus);
98
+ if (!terminalEvent) return;
99
+ await emitShippingEvent(terminalEvent, eventPayload, eventScope);
100
+ }
74
101
  } catch (error) {
75
- logger.error("Job processing failed", { providerKey: job.payload.providerKey, shipmentId: job.payload.shipmentId, err: error });
102
+ logger.error("Job processing failed", { providerKey: payload.providerKey, shipmentId: payload.shipmentId, err: error });
76
103
  throw error;
77
104
  }
78
105
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/workers/webhook-processor.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport type { ShippingWebhookEvent } from '../lib/adapter'\nimport { getShippingAdapter } from '../lib/adapter-registry'\nimport { getTerminalShippingEvent, syncShipmentStatus, TERMINAL_SHIPPING_STATUSES } from '../lib/status-sync'\nimport { claimWebhookProcessing, releaseWebhookClaim } from '../lib/webhook-utils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('shipping_carriers').child({ component: 'webhook-processor' })\n\ntype WebhookJobPayload = {\n providerKey: string\n event: ShippingWebhookEvent\n shipmentId?: string | null\n scope?: {\n organizationId: string\n tenantId: string\n } | null\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'shipping-carriers-webhook',\n id: 'shipping-carriers:webhook-processor',\n concurrency: 5,\n}\n\nexport default async function handle(job: QueuedJob<WebhookJobPayload>, ctx: HandlerContext): Promise<void> {\n try {\n const em = ctx.resolve<EntityManager>('em')\n const adapter = getShippingAdapter(job.payload.providerKey)\n if (!adapter) return\n\n const shipment = job.payload.shipmentId && job.payload.scope\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: job.payload.shipmentId,\n organizationId: job.payload.scope.organizationId,\n tenantId: job.payload.scope.tenantId,\n deletedAt: null,\n },\n undefined,\n job.payload.scope,\n )\n : null\n if (!shipment) return\n\n const scope = { organizationId: shipment.organizationId, tenantId: shipment.tenantId }\n const claimed = await claimWebhookProcessing(\n em,\n job.payload.event.idempotencyKey,\n job.payload.providerKey,\n scope,\n job.payload.event.eventType,\n )\n if (!claimed) {\n return\n }\n\n try {\n const carrierStatus = typeof job.payload.event.data.status === 'string'\n ? job.payload.event.data.status\n : job.payload.event.eventType\n const unifiedStatus = adapter.mapStatus(carrierStatus)\n shipment.carrierStatus = carrierStatus\n shipment.lastWebhookAt = new Date()\n\n const transitionApplied = syncShipmentStatus(shipment, unifiedStatus)\n if (!transitionApplied) return\n\n await em.flush()\n\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: job.payload.providerKey,\n previousStatus: carrierStatus,\n newStatus: unifiedStatus,\n organizationId: shipment.organizationId,\n tenantId: shipment.tenantId,\n }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload)\n if (TERMINAL_SHIPPING_STATUSES.has(unifiedStatus)) {\n const terminalEvent = getTerminalShippingEvent(unifiedStatus)\n if (!terminalEvent) return\n await emitShippingEvent(terminalEvent, eventPayload)\n }\n } catch (error) {\n await releaseWebhookClaim(\n em,\n job.payload.event.idempotencyKey,\n job.payload.providerKey,\n scope,\n )\n throw error\n }\n } catch (error) {\n logger.error('Job processing failed', { providerKey: job.payload.providerKey, shipmentId: job.payload.shipmentId, err: error })\n throw error\n }\n}\n"],
5
- "mappings": "AAEA,SAAS,6BAA6B;AACtC,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAElC,SAAS,0BAA0B;AACnC,SAAS,0BAA0B,oBAAoB,kCAAkC;AACzF,SAAS,wBAAwB,2BAA2B;AAC5D,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,mBAAmB,EAAE,MAAM,EAAE,WAAW,oBAAoB,CAAC;AAgBlF,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAEA,eAAO,OAA8B,KAAmC,KAAoC;AAC1G,MAAI;AACF,UAAM,KAAK,IAAI,QAAuB,IAAI;AAC1C,UAAM,UAAU,mBAAmB,IAAI,QAAQ,WAAW;AAC1D,QAAI,CAAC,QAAS;AAEd,UAAM,WAAW,IAAI,QAAQ,cAAc,IAAI,QAAQ,QACnD,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,QACE,IAAI,IAAI,QAAQ;AAAA,QAChB,gBAAgB,IAAI,QAAQ,MAAM;AAAA,QAClC,UAAU,IAAI,QAAQ,MAAM;AAAA,QAC5B,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA,IAAI,QAAQ;AAAA,IACd,IACE;AACJ,QAAI,CAAC,SAAU;AAEf,UAAM,QAAQ,EAAE,gBAAgB,SAAS,gBAAgB,UAAU,SAAS,SAAS;AACrF,UAAM,UAAU,MAAM;AAAA,MACpB;AAAA,MACA,IAAI,QAAQ,MAAM;AAAA,MAClB,IAAI,QAAQ;AAAA,MACZ;AAAA,MACA,IAAI,QAAQ,MAAM;AAAA,IACpB;AACA,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,QAAI;AACF,YAAM,gBAAgB,OAAO,IAAI,QAAQ,MAAM,KAAK,WAAW,WAC3D,IAAI,QAAQ,MAAM,KAAK,SACvB,IAAI,QAAQ,MAAM;AACtB,YAAM,gBAAgB,QAAQ,UAAU,aAAa;AACrD,eAAS,gBAAgB;AACzB,eAAS,gBAAgB,oBAAI,KAAK;AAElC,YAAM,oBAAoB,mBAAmB,UAAU,aAAa;AACpE,UAAI,CAAC,kBAAmB;AAExB,YAAM,GAAG,MAAM;AAEf,YAAM,eAAe;AAAA,QACnB,YAAY,SAAS;AAAA,QACrB,aAAa,IAAI,QAAQ;AAAA,QACzB,gBAAgB;AAAA,QAChB,WAAW;AAAA,QACX,gBAAgB,SAAS;AAAA,QACzB,UAAU,SAAS;AAAA,MACrB;AACA,YAAM,kBAAkB,6CAA6C,YAAY;AACjF,UAAI,2BAA2B,IAAI,aAAa,GAAG;AACjD,cAAM,gBAAgB,yBAAyB,aAAa;AAC5D,YAAI,CAAC,cAAe;AACpB,cAAM,kBAAkB,eAAe,YAAY;AAAA,MACrD;AAAA,IACF,SAAS,OAAO;AACd,YAAM;AAAA,QACJ;AAAA,QACA,IAAI,QAAQ,MAAM;AAAA,QAClB,IAAI,QAAQ;AAAA,QACZ;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF,SAAS,OAAO;AACd,WAAO,MAAM,yBAAyB,EAAE,aAAa,IAAI,QAAQ,aAAa,YAAY,IAAI,QAAQ,YAAY,KAAK,MAAM,CAAC;AAC9H,UAAM;AAAA,EACR;AACF;",
6
- "names": []
4
+ "sourcesContent": ["import { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport type { ShippingWebhookEvent, UnifiedShipmentStatus } from '../lib/adapter'\nimport { getShippingAdapter } from '../lib/adapter-registry'\nimport { getTerminalShippingEvent, syncShipmentStatus, TERMINAL_SHIPPING_STATUSES } from '../lib/status-sync'\nimport { claimWebhookProcessing, releaseWebhookClaim } from '../lib/webhook-utils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('shipping_carriers').child({ component: 'webhook-processor' })\n\ntype WebhookJobPayload = {\n providerKey: string\n event: ShippingWebhookEvent\n shipmentId?: string | null\n scope?: {\n organizationId: string\n tenantId: string\n } | null\n}\n\ntype LegacyWebhookJobEnvelope = {\n name?: string\n payload: WebhookJobPayload\n}\n\nfunction readWebhookJobPayload(data: WebhookJobPayload | LegacyWebhookJobEnvelope): WebhookJobPayload {\n if ('providerKey' in data) return data\n return data.payload\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'shipping-carriers-webhook',\n id: 'shipping-carriers:webhook-processor',\n concurrency: 5,\n}\n\nexport default async function handle(job: QueuedJob<WebhookJobPayload | LegacyWebhookJobEnvelope>, ctx: HandlerContext): Promise<void> {\n const payload = readWebhookJobPayload(job.payload)\n try {\n const em = ctx.resolve<EntityManager>('em')\n const adapter = getShippingAdapter(payload.providerKey)\n if (!adapter) return\n\n const shipment = payload.shipmentId && payload.scope\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: payload.shipmentId,\n organizationId: payload.scope.organizationId,\n tenantId: payload.scope.tenantId,\n deletedAt: null,\n },\n undefined,\n payload.scope,\n )\n : null\n if (!shipment) return\n\n const scope = { organizationId: shipment.organizationId, tenantId: shipment.tenantId }\n const claimed = await claimWebhookProcessing(\n em,\n payload.event.idempotencyKey,\n payload.providerKey,\n scope,\n payload.event.eventType,\n )\n if (!claimed) {\n return\n }\n\n const carrierStatus = typeof payload.event.data.status === 'string'\n ? payload.event.data.status\n : payload.event.eventType\n\n let transition: { previousStatus: string; newStatus: UnifiedShipmentStatus } | null = null\n try {\n const unifiedStatus = adapter.mapStatus(carrierStatus)\n transition = await em.transactional(async (tx) => {\n const locked = await findOneWithDecryption(\n tx,\n CarrierShipment,\n {\n id: shipment.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true },\n scope,\n )\n if (!locked) return null\n const previousStatus = locked.unifiedStatus\n if (!syncShipmentStatus(locked, unifiedStatus)) return null\n locked.carrierStatus = carrierStatus\n locked.lastWebhookAt = new Date()\n await tx.flush()\n return { previousStatus, newStatus: unifiedStatus }\n }, { clear: true })\n } catch (error) {\n await releaseWebhookClaim(\n em,\n payload.event.idempotencyKey,\n payload.providerKey,\n scope,\n )\n throw error\n }\n if (!transition) return\n\n const unifiedStatus = transition.newStatus\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: payload.providerKey,\n previousStatus: transition.previousStatus,\n newStatus: unifiedStatus,\n carrierStatus,\n organizationId: shipment.organizationId,\n tenantId: shipment.tenantId,\n }\n const eventScope = { tenantId: shipment.tenantId, organizationId: shipment.organizationId }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload, eventScope)\n if (TERMINAL_SHIPPING_STATUSES.has(unifiedStatus)) {\n const terminalEvent = getTerminalShippingEvent(unifiedStatus)\n if (!terminalEvent) return\n await emitShippingEvent(terminalEvent, eventPayload, eventScope)\n }\n } catch (error) {\n logger.error('Job processing failed', { providerKey: payload.providerKey, shipmentId: payload.shipmentId, err: error })\n throw error\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["unifiedStatus"]
7
7
  }