@modainteract/moda-interact-shared 2.4.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ import {
2
+ isShopifyCustomerNumericId
3
+ } from "./chunk-QG53EVA6.js";
4
+
5
+ // src/shopify/whatsapp-consent-sync.schema.ts
6
+ import { z } from "zod";
7
+ var SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION = 1;
8
+ var SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC = "customers_whats_app_marketing_consent/update";
9
+ var ShopifyWhatsAppConsentSyncJobV1Schema = z.object({
10
+ schemaVersion: z.literal(SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION),
11
+ shopId: z.string().min(1).max(128).regex(/^\S+$/),
12
+ shopifyCustomerId: z.string().refine(isShopifyCustomerNumericId, {
13
+ message: "Must be a canonical Shopify numeric Customer ID"
14
+ }),
15
+ deliveryId: z.string().min(1).max(256).regex(/^\S+$/),
16
+ triggeredAt: z.iso.datetime({ offset: true })
17
+ }).strict();
18
+ function parseShopifyWhatsAppConsentSyncJobV1(input) {
19
+ return ShopifyWhatsAppConsentSyncJobV1Schema.parse(input);
20
+ }
21
+ function safeParseShopifyWhatsAppConsentSyncJobV1(input) {
22
+ return ShopifyWhatsAppConsentSyncJobV1Schema.safeParse(input);
23
+ }
24
+
25
+ export {
26
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION,
27
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC,
28
+ ShopifyWhatsAppConsentSyncJobV1Schema,
29
+ parseShopifyWhatsAppConsentSyncJobV1,
30
+ safeParseShopifyWhatsAppConsentSyncJobV1
31
+ };
32
+ //# sourceMappingURL=chunk-G7FCWVBU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shopify/whatsapp-consent-sync.schema.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { isShopifyCustomerNumericId } from \"../identity/shopify-customer.js\";\n\nexport const SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION = 1 as const;\nexport const SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC =\n \"customers_whats_app_marketing_consent/update\" as const;\n\n/**\n * A webhook notification to re-read CURRENT Shopify consent in Background.\n * These fields contain no consent decision, phone, checkout or recovery data;\n * parsing this job never authorises a recovery or an outbound message.\n */\nexport const ShopifyWhatsAppConsentSyncJobV1Schema = z\n .object({\n schemaVersion: z.literal(SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION),\n shopId: z.string().min(1).max(128).regex(/^\\S+$/),\n shopifyCustomerId: z.string().refine(isShopifyCustomerNumericId, {\n message: \"Must be a canonical Shopify numeric Customer ID\",\n }),\n deliveryId: z.string().min(1).max(256).regex(/^\\S+$/),\n triggeredAt: z.iso.datetime({ offset: true }),\n })\n .strict();\n\nexport type ShopifyWhatsAppConsentSyncJobV1 =\n z.infer<typeof ShopifyWhatsAppConsentSyncJobV1Schema>;\n\nexport function parseShopifyWhatsAppConsentSyncJobV1(\n input: unknown,\n): ShopifyWhatsAppConsentSyncJobV1 {\n return ShopifyWhatsAppConsentSyncJobV1Schema.parse(input);\n}\n\nexport function safeParseShopifyWhatsAppConsentSyncJobV1(input: unknown) {\n return ShopifyWhatsAppConsentSyncJobV1Schema.safeParse(input);\n}\n"],"mappings":";;;;;AAAA,SAAS,SAAS;AAGX,IAAM,+CAA+C;AACrD,IAAM,sCACX;AAOK,IAAM,wCAAwC,EAClD,OAAO;AAAA,EACN,eAAe,EAAE,QAAQ,4CAA4C;AAAA,EACrE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,MAAM,OAAO;AAAA,EAChD,mBAAmB,EAAE,OAAO,EAAE,OAAO,4BAA4B;AAAA,IAC/D,SAAS;AAAA,EACX,CAAC;AAAA,EACD,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,MAAM,OAAO;AAAA,EACpD,aAAa,EAAE,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC;AAC9C,CAAC,EACA,OAAO;AAKH,SAAS,qCACd,OACiC;AACjC,SAAO,sCAAsC,MAAM,KAAK;AAC1D;AAEO,SAAS,yCAAyC,OAAgB;AACvE,SAAO,sCAAsC,UAAU,KAAK;AAC9D;","names":[]}
@@ -344,6 +344,10 @@ var SHOPIFY_WEBHOOK_QUEUE_CONTRACTS = {
344
344
  queueName: "order-events",
345
345
  jobName: "order-completed"
346
346
  },
347
+ WHATSAPP_CONSENT_SYNC: {
348
+ queueName: "checkout-events",
349
+ jobName: "shopify-whatsapp-consent-sync"
350
+ },
347
351
  SHOPIFY_DISCOUNT_SYNC: {
348
352
  queueName: "shopify-discount-sync",
349
353
  jobName: "reconcile-shopify-discounts"
@@ -465,4 +469,4 @@ export {
465
469
  parseShopifyDiscountSyncJob,
466
470
  safeParseShopifyDiscountSyncJob
467
471
  };
468
- //# sourceMappingURL=chunk-SU2DAOEC.js.map
472
+ //# sourceMappingURL=chunk-NHIGHAEQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shopify/constants.ts","../src/shopify/common.schema.ts","../src/shopify/v1/checkout-observed.schema.ts","../src/shopify/v1/order-completed.schema.ts","../src/shopify/v1/commerce-event.schema.ts","../src/shopify/v2/checkout-created.schema.ts","../src/shopify/v2/checkout-updated.schema.ts","../src/shopify/v2/cart-activity.schema.ts","../src/shopify/v2/order-completed.schema.ts","../src/shopify/v2/recovery-event.schema.ts","../src/shopify/v3/checkout-updated.schema.ts","../src/shopify/v3/recovery-event.schema.ts","../src/shopify/recovery-event.schema.ts","../src/shopify/queue-contracts.ts"],"sourcesContent":["export const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V1 = 1 as const;\nexport const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2 = 2 as const;\nexport const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3 = 3 as const;\n\n// Backward-compatible alias for existing v1 callers.\nexport const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION =\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V1;\n\nexport const SHOPIFY_COMMERCE_EVENT_TYPES = {\n CHECKOUT_OBSERVED: \"checkout.observed\",\n ORDER_COMPLETED: \"order.completed\",\n} as const;\n\nexport const SHOPIFY_RECOVERY_EVENT_TYPES_V2 = {\n CART_ACTIVITY: \"cart.activity\",\n CHECKOUT_CREATED: \"checkout.created\",\n CHECKOUT_UPDATED: \"checkout.updated\",\n ORDER_COMPLETED: \"order.completed\",\n} as const;\n\nexport const SHOPIFY_RECOVERY_EVENT_TYPES_V3 = {\n CHECKOUT_UPDATED: \"checkout.updated\",\n} as const;\n\nexport const SHOPIFY_WEBHOOK_OUTBOX_DESTINATIONS = {\n CHECKOUT_EVENTS: \"CHECKOUT_EVENTS\",\n ORDER_EVENTS: \"ORDER_EVENTS\",\n} as const;\n","import { z } from \"zod\";\n\n/**\n * Reasonable upper bounds for externally controlled (Shopify-originated) strings.\n * These exist to keep queue messages small and predictable, not to validate business rules.\n */\nconst MAX_ID_LENGTH = 100;\nconst MAX_SHORT_TEXT_LENGTH = 320;\nconst MAX_TITLE_LENGTH = 500;\nconst MAX_URL_LENGTH = 2048;\nconst MAX_LINE_ITEMS = 250;\n\nconst decimalStringPattern = /^-?\\d+(\\.\\d+)?$/;\n\n/** Decimal amount encoded as a string; never a JS number to avoid float precision loss. */\nconst DecimalStringSchema = z\n .string()\n .min(1)\n .max(32)\n .regex(decimalStringPattern, \"must be a decimal string, e.g. \\\"12.34\\\"\");\n\nexport const ShopifyTenantSchema = z\n .object({\n shopId: z.string().min(1).max(MAX_ID_LENGTH),\n shopDomain: z\n .string()\n .min(1)\n .max(MAX_SHORT_TEXT_LENGTH)\n .toLowerCase()\n .trim(),\n })\n .strict();\n\nexport type ShopifyTenant = z.infer<typeof ShopifyTenantSchema>;\n\nexport const ShopifyCustomerReferenceSchema = z\n .object({\n shopifyCustomerId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n email: z.string().email().max(MAX_SHORT_TEXT_LENGTH).nullable(),\n phone: z.string().min(1).max(64).nullable(),\n firstName: z.string().min(1).max(MAX_TITLE_LENGTH).nullable(),\n lastName: z.string().min(1).max(MAX_TITLE_LENGTH).nullable(),\n })\n .strict();\n\nexport type ShopifyCustomerReference = z.infer<typeof ShopifyCustomerReferenceSchema>;\n\nexport const ShopifyMoneySchema = z\n .object({\n amount: DecimalStringSchema,\n currencyCode: z\n .string()\n .regex(/^[A-Z]{3}$/, \"must be an uppercase three-letter currency code\"),\n })\n .strict();\n\nexport type ShopifyMoney = z.infer<typeof ShopifyMoneySchema>;\n\n/**\n * Fields limited to what checkout recovery and customer-facing basket context require.\n * Line-item arrays are bounded (see MAX_LINE_ITEMS) to prevent unexpectedly large queue messages.\n */\nexport const ShopifyCheckoutLineItemSchema = z\n .object({\n lineItemId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n variantId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n productId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n title: z.string().min(1).max(MAX_TITLE_LENGTH),\n variantTitle: z.string().min(1).max(MAX_TITLE_LENGTH).nullable(),\n quantity: z.number().int().positive(),\n unitPrice: DecimalStringSchema.nullable(),\n sku: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n })\n .strict();\n\nexport type ShopifyCheckoutLineItem = z.infer<typeof ShopifyCheckoutLineItemSchema>;\n\nexport const ShopifyCheckoutLineItemsSchema = z\n .array(ShopifyCheckoutLineItemSchema)\n .max(MAX_LINE_ITEMS);\n\nexport { MAX_URL_LENGTH, MAX_LINE_ITEMS };\n","import { z } from \"zod\";\nimport {\n MAX_URL_LENGTH,\n ShopifyCheckoutLineItemsSchema,\n ShopifyCustomerReferenceSchema,\n ShopifyMoneySchema,\n} from \"../common.schema.js\";\n\nexport const CheckoutObservedPayloadSchema = z\n .object({\n checkoutToken: z.string().min(1),\n cartToken: z.string().min(1).nullable(),\n checkoutUrl: z.url().max(MAX_URL_LENGTH).nullable(),\n customer: ShopifyCustomerReferenceSchema.nullable(),\n total: ShopifyMoneySchema.nullable(),\n lineItems: ShopifyCheckoutLineItemsSchema,\n checkoutCreatedAt: z.iso.datetime().nullable(),\n checkoutUpdatedAt: z.iso.datetime().nullable(),\n completedAt: z.iso.datetime().nullable(),\n })\n .strict();\n\nexport type CheckoutObservedPayload = z.infer<typeof CheckoutObservedPayloadSchema>;\n","import { z } from \"zod\";\nimport { ShopifyMoneySchema } from \"../common.schema.js\";\n\nexport const OrderCompletedPayloadSchema = z\n .object({\n orderId: z.string().min(1),\n checkoutToken: z.string().min(1).nullable(),\n shopifyCustomerId: z.string().min(1).nullable(),\n total: ShopifyMoneySchema.nullable(),\n completedAt: z.iso.datetime(),\n })\n .strict();\n\nexport type OrderCompletedPayload = z.infer<typeof OrderCompletedPayloadSchema>;\n","import { z } from \"zod\";\nimport {\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION,\n SHOPIFY_COMMERCE_EVENT_TYPES,\n} from \"../constants.js\";\nimport { ShopifyTenantSchema } from \"../common.schema.js\";\nimport { CheckoutObservedPayloadSchema } from \"./checkout-observed.schema.js\";\nimport { OrderCompletedPayloadSchema } from \"./order-completed.schema.js\";\n\nconst ShopifyCommerceEventBaseSchema = z.object({\n schemaVersion: z.literal(SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION),\n receiptId: z.string().min(1),\n deliveryId: z.string().min(1),\n eventId: z.string().min(1).nullable(),\n source: z.literal(\"shopify\"),\n providerTopic: z.string().min(1),\n tenant: ShopifyTenantSchema,\n occurredAt: z.iso.datetime().nullable(),\n receivedAt: z.iso.datetime(),\n traceId: z.string().min(1),\n orderingKey: z.string().min(1),\n});\n\nexport const ShopifyCheckoutObservedEventSchema = ShopifyCommerceEventBaseSchema.extend({\n eventType: z.literal(SHOPIFY_COMMERCE_EVENT_TYPES.CHECKOUT_OBSERVED),\n payload: CheckoutObservedPayloadSchema,\n}).strict();\n\nexport type ShopifyCheckoutObservedEvent = z.infer<\n typeof ShopifyCheckoutObservedEventSchema\n>;\n\nexport const ShopifyOrderCompletedEventSchema = ShopifyCommerceEventBaseSchema.extend({\n eventType: z.literal(SHOPIFY_COMMERCE_EVENT_TYPES.ORDER_COMPLETED),\n payload: OrderCompletedPayloadSchema,\n}).strict();\n\nexport type ShopifyOrderCompletedEvent = z.infer<\n typeof ShopifyOrderCompletedEventSchema\n>;\n\nexport const ShopifyCommerceEventSchema = z.discriminatedUnion(\"eventType\", [\n ShopifyCheckoutObservedEventSchema,\n ShopifyOrderCompletedEventSchema,\n]);\n\nexport type ShopifyCommerceEvent = z.infer<typeof ShopifyCommerceEventSchema>;\nexport type ShopifyCommerceEventType = ShopifyCommerceEvent[\"eventType\"];\n\nexport function parseShopifyCommerceEvent(input: unknown): ShopifyCommerceEvent {\n return ShopifyCommerceEventSchema.parse(input);\n}\n\nexport function safeParseShopifyCommerceEvent(input: unknown) {\n return ShopifyCommerceEventSchema.safeParse(input);\n}\n\nexport function isCheckoutObservedEvent(\n event: ShopifyCommerceEvent,\n): event is ShopifyCheckoutObservedEvent {\n return event.eventType === SHOPIFY_COMMERCE_EVENT_TYPES.CHECKOUT_OBSERVED;\n}\n\nexport function isOrderCompletedEvent(\n event: ShopifyCommerceEvent,\n): event is ShopifyOrderCompletedEvent {\n return event.eventType === SHOPIFY_COMMERCE_EVENT_TYPES.ORDER_COMPLETED;\n}\n\n/**\n * Stable ordering key for a shop/checkout pair.\n * Checkout and order events for the same checkout must share this key, so\n * deliveryId is intentionally excluded.\n */\nexport function createShopifyCommerceOrderingKey(\n shopId: string,\n checkoutToken: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyCommerceOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof checkoutToken !== \"string\" || checkoutToken.length === 0) {\n throw new Error(\n \"createShopifyCommerceOrderingKey: checkoutToken must be a non-empty string\",\n );\n }\n\n return `${shopId}:${checkoutToken}`;\n}\n\nexport function createShopifyOrderOrderingKey(\n shopId: string,\n orderId: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyOrderOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof orderId !== \"string\" || orderId.length === 0) {\n throw new Error(\n \"createShopifyOrderOrderingKey: orderId must be a non-empty string\",\n );\n }\n\n return `${shopId}:${orderId}`;\n}","import { z } from \"zod\";\nimport { MAX_URL_LENGTH } from \"../common.schema.js\";\n\nexport const CheckoutCreatedPayloadV2Schema = z\n .object({\n checkoutToken: z.string().min(1),\n cartToken: z.string().min(1).nullable(),\n abandonedCheckoutUrl: z.url().max(MAX_URL_LENGTH).nullable(),\n checkoutCreatedAt: z.iso.datetime().nullable(),\n })\n .strict();\n\nexport type CheckoutCreatedPayloadV2 = z.infer<\n typeof CheckoutCreatedPayloadV2Schema\n>;\n","import { z } from \"zod\";\n\nexport const CheckoutUpdatedPayloadV2Schema = z\n .object({\n checkoutToken: z.string().min(1),\n })\n .strict();\n\nexport type CheckoutUpdatedPayloadV2 = z.infer<\n typeof CheckoutUpdatedPayloadV2Schema\n>;\n","import { z } from \"zod\";\n\nexport const ShopifyCartActivityPayloadV2Schema = z\n .object({\n cartToken: z.string().min(1),\n isEmpty: z.boolean().nullable(),\n })\n .strict();\n\nexport type ShopifyCartActivityPayloadV2 = z.infer<\n typeof ShopifyCartActivityPayloadV2Schema\n>;","import { z } from \"zod\";\n\nexport const OrderCompletedPayloadV2Schema = z\n .object({\n orderId: z.string().min(1),\n checkoutToken: z.string().min(1).nullable(),\n cartToken: z.string().min(1).nullable(),\n completedAt: z.iso.datetime(),\n })\n .strict();\n\nexport type OrderCompletedPayloadV2 = z.infer<typeof OrderCompletedPayloadV2Schema>;\n","import { z } from \"zod\";\nimport {\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2,\n SHOPIFY_RECOVERY_EVENT_TYPES_V2,\n} from \"../constants.js\";\nimport { ShopifyTenantSchema } from \"../common.schema.js\";\nimport { InternationalContextSchema } from \"../../internationalization.js\";\nimport { CheckoutCreatedPayloadV2Schema } from \"./checkout-created.schema.js\";\nimport { CheckoutUpdatedPayloadV2Schema } from \"./checkout-updated.schema.js\";\nimport { OrderCompletedPayloadV2Schema } from \"./order-completed.schema.js\";\nimport { ShopifyCartActivityPayloadV2Schema } from \"./cart-activity.schema.js\";\n\nconst ShopifyRecoveryEventBaseV2Schema = z\n .object({\n schemaVersion: z.literal(SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2),\n receiptId: z.string().min(1),\n deliveryId: z.string().min(1),\n eventId: z.string().min(1).nullable(),\n source: z.literal(\"shopify\"),\n providerTopic: z.string().min(1),\n tenant: ShopifyTenantSchema,\n occurredAt: z.iso.datetime().nullable(),\n receivedAt: z.iso.datetime(),\n traceId: z.string().min(1),\n orderingKey: z.string().min(1),\n internationalContext: InternationalContextSchema.optional(),\n })\n .strict();\n\nexport const ShopifyCheckoutCreatedEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_CREATED),\n payload: CheckoutCreatedPayloadV2Schema,\n }).strict();\n\nexport type ShopifyCheckoutCreatedEventV2 = z.infer<\n typeof ShopifyCheckoutCreatedEventV2Schema\n>;\n\nexport const ShopifyCartActivityEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.CART_ACTIVITY),\n payload: ShopifyCartActivityPayloadV2Schema,\n }).strict();\n\nexport type ShopifyCartActivityEventV2 = z.infer<\n typeof ShopifyCartActivityEventV2Schema\n>;\n\nexport const ShopifyCheckoutUpdatedEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_UPDATED),\n payload: CheckoutUpdatedPayloadV2Schema,\n }).strict();\n\nexport type ShopifyCheckoutUpdatedEventV2 = z.infer<\n typeof ShopifyCheckoutUpdatedEventV2Schema\n>;\n\nexport const ShopifyOrderCompletedEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.ORDER_COMPLETED),\n payload: OrderCompletedPayloadV2Schema,\n }).strict();\n\nexport type ShopifyOrderCompletedEventV2 = z.infer<\n typeof ShopifyOrderCompletedEventV2Schema\n>;\n\nexport const ShopifyRecoveryEventV2Schema = z.discriminatedUnion(\"eventType\", [\n ShopifyCartActivityEventV2Schema,\n ShopifyCheckoutCreatedEventV2Schema,\n ShopifyCheckoutUpdatedEventV2Schema,\n ShopifyOrderCompletedEventV2Schema,\n]);\n\nexport type ShopifyRecoveryEventV2 = z.infer<typeof ShopifyRecoveryEventV2Schema>;\nexport type ShopifyRecoveryEventTypeV2 = ShopifyRecoveryEventV2[\"eventType\"];\n\nexport function parseShopifyRecoveryEventV2(input: unknown): ShopifyRecoveryEventV2 {\n return ShopifyRecoveryEventV2Schema.parse(input);\n}\n\nexport function safeParseShopifyRecoveryEventV2(input: unknown) {\n return ShopifyRecoveryEventV2Schema.safeParse(input);\n}\n\nexport function isCheckoutCreatedEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyCheckoutCreatedEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_CREATED;\n}\n\nexport function isCartActivityEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyCartActivityEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.CART_ACTIVITY;\n}\n\nexport function isCheckoutUpdatedEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyCheckoutUpdatedEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_UPDATED;\n}\n\nexport function isOrderCompletedEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyOrderCompletedEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.ORDER_COMPLETED;\n}\n\nexport function createShopifyPendingRecoveryOrderingKey(\n shopId: string,\n checkoutToken: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyPendingRecoveryOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof checkoutToken !== \"string\" || checkoutToken.length === 0) {\n throw new Error(\n \"createShopifyPendingRecoveryOrderingKey: checkoutToken must be a non-empty string\",\n );\n }\n\n return `${shopId}:${checkoutToken}`;\n}\n\nexport function createShopifyCartActivityOrderingKey(\n shopId: string,\n cartToken: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyCartActivityOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof cartToken !== \"string\" || cartToken.length === 0) {\n throw new Error(\n \"createShopifyCartActivityOrderingKey: cartToken must be a non-empty string\",\n );\n }\n\n return `cart:${shopId.length}:${shopId}:${cartToken.length}:${cartToken}`;\n}\n\nexport function createShopifyOrderCorrelationOrderingKey(input: {\n shopId: string;\n orderId: string;\n checkoutToken: string | null;\n}): string {\n if (input.checkoutToken) {\n return createShopifyPendingRecoveryOrderingKey(input.shopId, input.checkoutToken);\n }\n\n if (typeof input.orderId !== \"string\" || input.orderId.length === 0) {\n throw new Error(\n \"createShopifyOrderCorrelationOrderingKey: orderId must be a non-empty string\",\n );\n }\n\n if (typeof input.shopId !== \"string\" || input.shopId.length === 0) {\n throw new Error(\n \"createShopifyOrderCorrelationOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n return `${input.shopId}:${input.orderId}`;\n}\n","import { z } from \"zod\";\nimport { MAX_URL_LENGTH } from \"../common.schema.js\";\n\nconst MAX_CART_TOKEN_LENGTH = 100;\n\nexport const CheckoutUpdatedPayloadV3Schema = z\n .object({\n checkoutToken: z.string().min(1),\n cartToken: z.string().min(1).max(MAX_CART_TOKEN_LENGTH).nullable().optional(),\n checkoutCreatedAt: z.iso.datetime().nullable().optional(),\n abandonedCheckoutUrl: z.url().max(MAX_URL_LENGTH).nullable().optional(),\n })\n .strict();\n\nexport type CheckoutUpdatedPayloadV3 = z.infer<\n typeof CheckoutUpdatedPayloadV3Schema\n>;","import { z } from \"zod\";\nimport {\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3,\n SHOPIFY_RECOVERY_EVENT_TYPES_V3,\n} from \"../constants.js\";\nimport { ShopifyTenantSchema } from \"../common.schema.js\";\nimport { InternationalContextSchema } from \"../../internationalization.js\";\nimport { CheckoutUpdatedPayloadV3Schema } from \"./checkout-updated.schema.js\";\n\nconst ShopifyRecoveryEventBaseV3Schema = z\n .object({\n schemaVersion: z.literal(SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3),\n receiptId: z.string().min(1),\n deliveryId: z.string().min(1),\n eventId: z.string().min(1).nullable(),\n source: z.literal(\"shopify\"),\n providerTopic: z.string().min(1),\n tenant: ShopifyTenantSchema,\n occurredAt: z.iso.datetime().nullable(),\n receivedAt: z.iso.datetime(),\n traceId: z.string().min(1),\n orderingKey: z.string().min(1),\n internationalContext: InternationalContextSchema.optional(),\n })\n .strict();\n\nexport const ShopifyCheckoutUpdatedEventV3Schema =\n ShopifyRecoveryEventBaseV3Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V3.CHECKOUT_UPDATED),\n payload: CheckoutUpdatedPayloadV3Schema,\n }).strict();\n\nexport type ShopifyCheckoutUpdatedEventV3 = z.infer<\n typeof ShopifyCheckoutUpdatedEventV3Schema\n>;\n","import { z } from \"zod\";\nimport {\n ShopifyRecoveryEventV2Schema,\n type ShopifyRecoveryEventV2,\n} from \"./v2/recovery-event.schema.js\";\nimport {\n ShopifyCheckoutUpdatedEventV3Schema,\n type ShopifyCheckoutUpdatedEventV3,\n} from \"./v3/recovery-event.schema.js\";\n\nexport const ShopifyRecoveryEventSchema = z.union([\n ShopifyRecoveryEventV2Schema,\n ShopifyCheckoutUpdatedEventV3Schema,\n]);\n\nexport type ShopifyRecoveryEvent =\n | ShopifyRecoveryEventV2\n | ShopifyCheckoutUpdatedEventV3;\n\nexport function parseShopifyRecoveryEvent(input: unknown): ShopifyRecoveryEvent {\n return ShopifyRecoveryEventSchema.parse(input);\n}\n\nexport function safeParseShopifyRecoveryEvent(input: unknown) {\n return ShopifyRecoveryEventSchema.safeParse(input);\n}","import { z } from \"zod\";\n\nexport const SHOPIFY_WEBHOOK_QUEUE_CONTRACTS = {\n CHECKOUT_EVENTS: {\n queueName: \"checkout-events\",\n jobName: \"checkout-created\",\n },\n CHECKOUT_UPDATED_EVENTS: {\n queueName: \"checkout-events\",\n jobName: \"checkout-updated\",\n },\n CART_ACTIVITY_EVENTS: {\n queueName: \"checkout-events\",\n jobName: \"cart-activity\",\n },\n ORDER_EVENTS: {\n queueName: \"order-events\",\n jobName: \"order-completed\",\n },\n SHOPIFY_DISCOUNT_SYNC: {\n queueName: \"shopify-discount-sync\",\n jobName: \"reconcile-shopify-discounts\",\n },\n} as const;\n\nexport const SHOPIFY_DISCOUNT_SYNC_REASONS = [\n \"SUBSCRIPTION_ACTIVATED\",\n \"REINSTALL_RECONCILED\",\n \"SCOPES_UPDATED\",\n \"ADMIN_REQUESTED\",\n \"DISCOUNT_WEBHOOK\",\n] as const;\n\nexport const SHOPIFY_DISCOUNT_SYNC_WEBHOOK_TOPICS = [\n \"discounts/create\",\n \"discounts/update\",\n \"discounts/delete\",\n \"discounts/redeemcode_added\",\n \"discounts/redeemcode_removed\",\n] as const;\n\nexport const ShopifyDiscountSyncReasonSchema = z.enum(SHOPIFY_DISCOUNT_SYNC_REASONS);\nexport const ShopifyDiscountSyncWebhookTopicSchema = z.enum(SHOPIFY_DISCOUNT_SYNC_WEBHOOK_TOPICS);\n\nexport const ShopifyDiscountSyncJobSchema = z\n .object({\n schemaVersion: z.literal(1),\n shopId: z.string().trim().min(1).max(128),\n shopDomain: z.string().trim().min(1).max(512),\n reason: ShopifyDiscountSyncReasonSchema,\n requestedAt: z.iso.datetime({ offset: true }),\n deliveryId: z.string().trim().min(1).nullable(),\n webhookTopic: ShopifyDiscountSyncWebhookTopicSchema.nullable(),\n })\n .strict()\n .superRefine((value, ctx) => {\n if (value.reason === \"DISCOUNT_WEBHOOK\") {\n if (!value.deliveryId || value.deliveryId.trim().length === 0) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"deliveryId\"],\n message: \"deliveryId is required when reason is DISCOUNT_WEBHOOK\",\n });\n }\n\n if (value.webhookTopic === null) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"webhookTopic\"],\n message: \"webhookTopic is required when reason is DISCOUNT_WEBHOOK\",\n });\n }\n } else {\n if (value.webhookTopic !== null) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"webhookTopic\"],\n message: \"webhookTopic must be null when reason is not DISCOUNT_WEBHOOK\",\n });\n }\n }\n });\n\nexport type ShopifyDiscountSyncJob = z.infer<typeof ShopifyDiscountSyncJobSchema>;\n\nexport function parseShopifyDiscountSyncJob(input: unknown): ShopifyDiscountSyncJob {\n return ShopifyDiscountSyncJobSchema.parse(input);\n}\n\nexport function safeParseShopifyDiscountSyncJob(input: unknown) {\n return ShopifyDiscountSyncJobSchema.safeParse(input);\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z"]}
1
+ {"version":3,"sources":["../src/shopify/constants.ts","../src/shopify/common.schema.ts","../src/shopify/v1/checkout-observed.schema.ts","../src/shopify/v1/order-completed.schema.ts","../src/shopify/v1/commerce-event.schema.ts","../src/shopify/v2/checkout-created.schema.ts","../src/shopify/v2/checkout-updated.schema.ts","../src/shopify/v2/cart-activity.schema.ts","../src/shopify/v2/order-completed.schema.ts","../src/shopify/v2/recovery-event.schema.ts","../src/shopify/v3/checkout-updated.schema.ts","../src/shopify/v3/recovery-event.schema.ts","../src/shopify/recovery-event.schema.ts","../src/shopify/queue-contracts.ts"],"sourcesContent":["export const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V1 = 1 as const;\nexport const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2 = 2 as const;\nexport const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3 = 3 as const;\n\n// Backward-compatible alias for existing v1 callers.\nexport const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION =\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V1;\n\nexport const SHOPIFY_COMMERCE_EVENT_TYPES = {\n CHECKOUT_OBSERVED: \"checkout.observed\",\n ORDER_COMPLETED: \"order.completed\",\n} as const;\n\nexport const SHOPIFY_RECOVERY_EVENT_TYPES_V2 = {\n CART_ACTIVITY: \"cart.activity\",\n CHECKOUT_CREATED: \"checkout.created\",\n CHECKOUT_UPDATED: \"checkout.updated\",\n ORDER_COMPLETED: \"order.completed\",\n} as const;\n\nexport const SHOPIFY_RECOVERY_EVENT_TYPES_V3 = {\n CHECKOUT_UPDATED: \"checkout.updated\",\n} as const;\n\nexport const SHOPIFY_WEBHOOK_OUTBOX_DESTINATIONS = {\n CHECKOUT_EVENTS: \"CHECKOUT_EVENTS\",\n ORDER_EVENTS: \"ORDER_EVENTS\",\n} as const;\n","import { z } from \"zod\";\n\n/**\n * Reasonable upper bounds for externally controlled (Shopify-originated) strings.\n * These exist to keep queue messages small and predictable, not to validate business rules.\n */\nconst MAX_ID_LENGTH = 100;\nconst MAX_SHORT_TEXT_LENGTH = 320;\nconst MAX_TITLE_LENGTH = 500;\nconst MAX_URL_LENGTH = 2048;\nconst MAX_LINE_ITEMS = 250;\n\nconst decimalStringPattern = /^-?\\d+(\\.\\d+)?$/;\n\n/** Decimal amount encoded as a string; never a JS number to avoid float precision loss. */\nconst DecimalStringSchema = z\n .string()\n .min(1)\n .max(32)\n .regex(decimalStringPattern, \"must be a decimal string, e.g. \\\"12.34\\\"\");\n\nexport const ShopifyTenantSchema = z\n .object({\n shopId: z.string().min(1).max(MAX_ID_LENGTH),\n shopDomain: z\n .string()\n .min(1)\n .max(MAX_SHORT_TEXT_LENGTH)\n .toLowerCase()\n .trim(),\n })\n .strict();\n\nexport type ShopifyTenant = z.infer<typeof ShopifyTenantSchema>;\n\nexport const ShopifyCustomerReferenceSchema = z\n .object({\n shopifyCustomerId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n email: z.string().email().max(MAX_SHORT_TEXT_LENGTH).nullable(),\n phone: z.string().min(1).max(64).nullable(),\n firstName: z.string().min(1).max(MAX_TITLE_LENGTH).nullable(),\n lastName: z.string().min(1).max(MAX_TITLE_LENGTH).nullable(),\n })\n .strict();\n\nexport type ShopifyCustomerReference = z.infer<typeof ShopifyCustomerReferenceSchema>;\n\nexport const ShopifyMoneySchema = z\n .object({\n amount: DecimalStringSchema,\n currencyCode: z\n .string()\n .regex(/^[A-Z]{3}$/, \"must be an uppercase three-letter currency code\"),\n })\n .strict();\n\nexport type ShopifyMoney = z.infer<typeof ShopifyMoneySchema>;\n\n/**\n * Fields limited to what checkout recovery and customer-facing basket context require.\n * Line-item arrays are bounded (see MAX_LINE_ITEMS) to prevent unexpectedly large queue messages.\n */\nexport const ShopifyCheckoutLineItemSchema = z\n .object({\n lineItemId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n variantId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n productId: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n title: z.string().min(1).max(MAX_TITLE_LENGTH),\n variantTitle: z.string().min(1).max(MAX_TITLE_LENGTH).nullable(),\n quantity: z.number().int().positive(),\n unitPrice: DecimalStringSchema.nullable(),\n sku: z.string().min(1).max(MAX_ID_LENGTH).nullable(),\n })\n .strict();\n\nexport type ShopifyCheckoutLineItem = z.infer<typeof ShopifyCheckoutLineItemSchema>;\n\nexport const ShopifyCheckoutLineItemsSchema = z\n .array(ShopifyCheckoutLineItemSchema)\n .max(MAX_LINE_ITEMS);\n\nexport { MAX_URL_LENGTH, MAX_LINE_ITEMS };\n","import { z } from \"zod\";\nimport {\n MAX_URL_LENGTH,\n ShopifyCheckoutLineItemsSchema,\n ShopifyCustomerReferenceSchema,\n ShopifyMoneySchema,\n} from \"../common.schema.js\";\n\nexport const CheckoutObservedPayloadSchema = z\n .object({\n checkoutToken: z.string().min(1),\n cartToken: z.string().min(1).nullable(),\n checkoutUrl: z.url().max(MAX_URL_LENGTH).nullable(),\n customer: ShopifyCustomerReferenceSchema.nullable(),\n total: ShopifyMoneySchema.nullable(),\n lineItems: ShopifyCheckoutLineItemsSchema,\n checkoutCreatedAt: z.iso.datetime().nullable(),\n checkoutUpdatedAt: z.iso.datetime().nullable(),\n completedAt: z.iso.datetime().nullable(),\n })\n .strict();\n\nexport type CheckoutObservedPayload = z.infer<typeof CheckoutObservedPayloadSchema>;\n","import { z } from \"zod\";\nimport { ShopifyMoneySchema } from \"../common.schema.js\";\n\nexport const OrderCompletedPayloadSchema = z\n .object({\n orderId: z.string().min(1),\n checkoutToken: z.string().min(1).nullable(),\n shopifyCustomerId: z.string().min(1).nullable(),\n total: ShopifyMoneySchema.nullable(),\n completedAt: z.iso.datetime(),\n })\n .strict();\n\nexport type OrderCompletedPayload = z.infer<typeof OrderCompletedPayloadSchema>;\n","import { z } from \"zod\";\nimport {\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION,\n SHOPIFY_COMMERCE_EVENT_TYPES,\n} from \"../constants.js\";\nimport { ShopifyTenantSchema } from \"../common.schema.js\";\nimport { CheckoutObservedPayloadSchema } from \"./checkout-observed.schema.js\";\nimport { OrderCompletedPayloadSchema } from \"./order-completed.schema.js\";\n\nconst ShopifyCommerceEventBaseSchema = z.object({\n schemaVersion: z.literal(SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION),\n receiptId: z.string().min(1),\n deliveryId: z.string().min(1),\n eventId: z.string().min(1).nullable(),\n source: z.literal(\"shopify\"),\n providerTopic: z.string().min(1),\n tenant: ShopifyTenantSchema,\n occurredAt: z.iso.datetime().nullable(),\n receivedAt: z.iso.datetime(),\n traceId: z.string().min(1),\n orderingKey: z.string().min(1),\n});\n\nexport const ShopifyCheckoutObservedEventSchema = ShopifyCommerceEventBaseSchema.extend({\n eventType: z.literal(SHOPIFY_COMMERCE_EVENT_TYPES.CHECKOUT_OBSERVED),\n payload: CheckoutObservedPayloadSchema,\n}).strict();\n\nexport type ShopifyCheckoutObservedEvent = z.infer<\n typeof ShopifyCheckoutObservedEventSchema\n>;\n\nexport const ShopifyOrderCompletedEventSchema = ShopifyCommerceEventBaseSchema.extend({\n eventType: z.literal(SHOPIFY_COMMERCE_EVENT_TYPES.ORDER_COMPLETED),\n payload: OrderCompletedPayloadSchema,\n}).strict();\n\nexport type ShopifyOrderCompletedEvent = z.infer<\n typeof ShopifyOrderCompletedEventSchema\n>;\n\nexport const ShopifyCommerceEventSchema = z.discriminatedUnion(\"eventType\", [\n ShopifyCheckoutObservedEventSchema,\n ShopifyOrderCompletedEventSchema,\n]);\n\nexport type ShopifyCommerceEvent = z.infer<typeof ShopifyCommerceEventSchema>;\nexport type ShopifyCommerceEventType = ShopifyCommerceEvent[\"eventType\"];\n\nexport function parseShopifyCommerceEvent(input: unknown): ShopifyCommerceEvent {\n return ShopifyCommerceEventSchema.parse(input);\n}\n\nexport function safeParseShopifyCommerceEvent(input: unknown) {\n return ShopifyCommerceEventSchema.safeParse(input);\n}\n\nexport function isCheckoutObservedEvent(\n event: ShopifyCommerceEvent,\n): event is ShopifyCheckoutObservedEvent {\n return event.eventType === SHOPIFY_COMMERCE_EVENT_TYPES.CHECKOUT_OBSERVED;\n}\n\nexport function isOrderCompletedEvent(\n event: ShopifyCommerceEvent,\n): event is ShopifyOrderCompletedEvent {\n return event.eventType === SHOPIFY_COMMERCE_EVENT_TYPES.ORDER_COMPLETED;\n}\n\n/**\n * Stable ordering key for a shop/checkout pair.\n * Checkout and order events for the same checkout must share this key, so\n * deliveryId is intentionally excluded.\n */\nexport function createShopifyCommerceOrderingKey(\n shopId: string,\n checkoutToken: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyCommerceOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof checkoutToken !== \"string\" || checkoutToken.length === 0) {\n throw new Error(\n \"createShopifyCommerceOrderingKey: checkoutToken must be a non-empty string\",\n );\n }\n\n return `${shopId}:${checkoutToken}`;\n}\n\nexport function createShopifyOrderOrderingKey(\n shopId: string,\n orderId: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyOrderOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof orderId !== \"string\" || orderId.length === 0) {\n throw new Error(\n \"createShopifyOrderOrderingKey: orderId must be a non-empty string\",\n );\n }\n\n return `${shopId}:${orderId}`;\n}","import { z } from \"zod\";\nimport { MAX_URL_LENGTH } from \"../common.schema.js\";\n\nexport const CheckoutCreatedPayloadV2Schema = z\n .object({\n checkoutToken: z.string().min(1),\n cartToken: z.string().min(1).nullable(),\n abandonedCheckoutUrl: z.url().max(MAX_URL_LENGTH).nullable(),\n checkoutCreatedAt: z.iso.datetime().nullable(),\n })\n .strict();\n\nexport type CheckoutCreatedPayloadV2 = z.infer<\n typeof CheckoutCreatedPayloadV2Schema\n>;\n","import { z } from \"zod\";\n\nexport const CheckoutUpdatedPayloadV2Schema = z\n .object({\n checkoutToken: z.string().min(1),\n })\n .strict();\n\nexport type CheckoutUpdatedPayloadV2 = z.infer<\n typeof CheckoutUpdatedPayloadV2Schema\n>;\n","import { z } from \"zod\";\n\nexport const ShopifyCartActivityPayloadV2Schema = z\n .object({\n cartToken: z.string().min(1),\n isEmpty: z.boolean().nullable(),\n })\n .strict();\n\nexport type ShopifyCartActivityPayloadV2 = z.infer<\n typeof ShopifyCartActivityPayloadV2Schema\n>;","import { z } from \"zod\";\n\nexport const OrderCompletedPayloadV2Schema = z\n .object({\n orderId: z.string().min(1),\n checkoutToken: z.string().min(1).nullable(),\n cartToken: z.string().min(1).nullable(),\n completedAt: z.iso.datetime(),\n })\n .strict();\n\nexport type OrderCompletedPayloadV2 = z.infer<typeof OrderCompletedPayloadV2Schema>;\n","import { z } from \"zod\";\nimport {\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2,\n SHOPIFY_RECOVERY_EVENT_TYPES_V2,\n} from \"../constants.js\";\nimport { ShopifyTenantSchema } from \"../common.schema.js\";\nimport { InternationalContextSchema } from \"../../internationalization.js\";\nimport { CheckoutCreatedPayloadV2Schema } from \"./checkout-created.schema.js\";\nimport { CheckoutUpdatedPayloadV2Schema } from \"./checkout-updated.schema.js\";\nimport { OrderCompletedPayloadV2Schema } from \"./order-completed.schema.js\";\nimport { ShopifyCartActivityPayloadV2Schema } from \"./cart-activity.schema.js\";\n\nconst ShopifyRecoveryEventBaseV2Schema = z\n .object({\n schemaVersion: z.literal(SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2),\n receiptId: z.string().min(1),\n deliveryId: z.string().min(1),\n eventId: z.string().min(1).nullable(),\n source: z.literal(\"shopify\"),\n providerTopic: z.string().min(1),\n tenant: ShopifyTenantSchema,\n occurredAt: z.iso.datetime().nullable(),\n receivedAt: z.iso.datetime(),\n traceId: z.string().min(1),\n orderingKey: z.string().min(1),\n internationalContext: InternationalContextSchema.optional(),\n })\n .strict();\n\nexport const ShopifyCheckoutCreatedEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_CREATED),\n payload: CheckoutCreatedPayloadV2Schema,\n }).strict();\n\nexport type ShopifyCheckoutCreatedEventV2 = z.infer<\n typeof ShopifyCheckoutCreatedEventV2Schema\n>;\n\nexport const ShopifyCartActivityEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.CART_ACTIVITY),\n payload: ShopifyCartActivityPayloadV2Schema,\n }).strict();\n\nexport type ShopifyCartActivityEventV2 = z.infer<\n typeof ShopifyCartActivityEventV2Schema\n>;\n\nexport const ShopifyCheckoutUpdatedEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_UPDATED),\n payload: CheckoutUpdatedPayloadV2Schema,\n }).strict();\n\nexport type ShopifyCheckoutUpdatedEventV2 = z.infer<\n typeof ShopifyCheckoutUpdatedEventV2Schema\n>;\n\nexport const ShopifyOrderCompletedEventV2Schema =\n ShopifyRecoveryEventBaseV2Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V2.ORDER_COMPLETED),\n payload: OrderCompletedPayloadV2Schema,\n }).strict();\n\nexport type ShopifyOrderCompletedEventV2 = z.infer<\n typeof ShopifyOrderCompletedEventV2Schema\n>;\n\nexport const ShopifyRecoveryEventV2Schema = z.discriminatedUnion(\"eventType\", [\n ShopifyCartActivityEventV2Schema,\n ShopifyCheckoutCreatedEventV2Schema,\n ShopifyCheckoutUpdatedEventV2Schema,\n ShopifyOrderCompletedEventV2Schema,\n]);\n\nexport type ShopifyRecoveryEventV2 = z.infer<typeof ShopifyRecoveryEventV2Schema>;\nexport type ShopifyRecoveryEventTypeV2 = ShopifyRecoveryEventV2[\"eventType\"];\n\nexport function parseShopifyRecoveryEventV2(input: unknown): ShopifyRecoveryEventV2 {\n return ShopifyRecoveryEventV2Schema.parse(input);\n}\n\nexport function safeParseShopifyRecoveryEventV2(input: unknown) {\n return ShopifyRecoveryEventV2Schema.safeParse(input);\n}\n\nexport function isCheckoutCreatedEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyCheckoutCreatedEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_CREATED;\n}\n\nexport function isCartActivityEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyCartActivityEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.CART_ACTIVITY;\n}\n\nexport function isCheckoutUpdatedEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyCheckoutUpdatedEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.CHECKOUT_UPDATED;\n}\n\nexport function isOrderCompletedEventV2(\n event: ShopifyRecoveryEventV2,\n): event is ShopifyOrderCompletedEventV2 {\n return event.eventType === SHOPIFY_RECOVERY_EVENT_TYPES_V2.ORDER_COMPLETED;\n}\n\nexport function createShopifyPendingRecoveryOrderingKey(\n shopId: string,\n checkoutToken: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyPendingRecoveryOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof checkoutToken !== \"string\" || checkoutToken.length === 0) {\n throw new Error(\n \"createShopifyPendingRecoveryOrderingKey: checkoutToken must be a non-empty string\",\n );\n }\n\n return `${shopId}:${checkoutToken}`;\n}\n\nexport function createShopifyCartActivityOrderingKey(\n shopId: string,\n cartToken: string,\n): string {\n if (typeof shopId !== \"string\" || shopId.length === 0) {\n throw new Error(\n \"createShopifyCartActivityOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n if (typeof cartToken !== \"string\" || cartToken.length === 0) {\n throw new Error(\n \"createShopifyCartActivityOrderingKey: cartToken must be a non-empty string\",\n );\n }\n\n return `cart:${shopId.length}:${shopId}:${cartToken.length}:${cartToken}`;\n}\n\nexport function createShopifyOrderCorrelationOrderingKey(input: {\n shopId: string;\n orderId: string;\n checkoutToken: string | null;\n}): string {\n if (input.checkoutToken) {\n return createShopifyPendingRecoveryOrderingKey(input.shopId, input.checkoutToken);\n }\n\n if (typeof input.orderId !== \"string\" || input.orderId.length === 0) {\n throw new Error(\n \"createShopifyOrderCorrelationOrderingKey: orderId must be a non-empty string\",\n );\n }\n\n if (typeof input.shopId !== \"string\" || input.shopId.length === 0) {\n throw new Error(\n \"createShopifyOrderCorrelationOrderingKey: shopId must be a non-empty string\",\n );\n }\n\n return `${input.shopId}:${input.orderId}`;\n}\n","import { z } from \"zod\";\nimport { MAX_URL_LENGTH } from \"../common.schema.js\";\n\nconst MAX_CART_TOKEN_LENGTH = 100;\n\nexport const CheckoutUpdatedPayloadV3Schema = z\n .object({\n checkoutToken: z.string().min(1),\n cartToken: z.string().min(1).max(MAX_CART_TOKEN_LENGTH).nullable().optional(),\n checkoutCreatedAt: z.iso.datetime().nullable().optional(),\n abandonedCheckoutUrl: z.url().max(MAX_URL_LENGTH).nullable().optional(),\n })\n .strict();\n\nexport type CheckoutUpdatedPayloadV3 = z.infer<\n typeof CheckoutUpdatedPayloadV3Schema\n>;","import { z } from \"zod\";\nimport {\n SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3,\n SHOPIFY_RECOVERY_EVENT_TYPES_V3,\n} from \"../constants.js\";\nimport { ShopifyTenantSchema } from \"../common.schema.js\";\nimport { InternationalContextSchema } from \"../../internationalization.js\";\nimport { CheckoutUpdatedPayloadV3Schema } from \"./checkout-updated.schema.js\";\n\nconst ShopifyRecoveryEventBaseV3Schema = z\n .object({\n schemaVersion: z.literal(SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3),\n receiptId: z.string().min(1),\n deliveryId: z.string().min(1),\n eventId: z.string().min(1).nullable(),\n source: z.literal(\"shopify\"),\n providerTopic: z.string().min(1),\n tenant: ShopifyTenantSchema,\n occurredAt: z.iso.datetime().nullable(),\n receivedAt: z.iso.datetime(),\n traceId: z.string().min(1),\n orderingKey: z.string().min(1),\n internationalContext: InternationalContextSchema.optional(),\n })\n .strict();\n\nexport const ShopifyCheckoutUpdatedEventV3Schema =\n ShopifyRecoveryEventBaseV3Schema.extend({\n eventType: z.literal(SHOPIFY_RECOVERY_EVENT_TYPES_V3.CHECKOUT_UPDATED),\n payload: CheckoutUpdatedPayloadV3Schema,\n }).strict();\n\nexport type ShopifyCheckoutUpdatedEventV3 = z.infer<\n typeof ShopifyCheckoutUpdatedEventV3Schema\n>;\n","import { z } from \"zod\";\nimport {\n ShopifyRecoveryEventV2Schema,\n type ShopifyRecoveryEventV2,\n} from \"./v2/recovery-event.schema.js\";\nimport {\n ShopifyCheckoutUpdatedEventV3Schema,\n type ShopifyCheckoutUpdatedEventV3,\n} from \"./v3/recovery-event.schema.js\";\n\nexport const ShopifyRecoveryEventSchema = z.union([\n ShopifyRecoveryEventV2Schema,\n ShopifyCheckoutUpdatedEventV3Schema,\n]);\n\nexport type ShopifyRecoveryEvent =\n | ShopifyRecoveryEventV2\n | ShopifyCheckoutUpdatedEventV3;\n\nexport function parseShopifyRecoveryEvent(input: unknown): ShopifyRecoveryEvent {\n return ShopifyRecoveryEventSchema.parse(input);\n}\n\nexport function safeParseShopifyRecoveryEvent(input: unknown) {\n return ShopifyRecoveryEventSchema.safeParse(input);\n}","import { z } from \"zod\";\n\nexport const SHOPIFY_WEBHOOK_QUEUE_CONTRACTS = {\n CHECKOUT_EVENTS: {\n queueName: \"checkout-events\",\n jobName: \"checkout-created\",\n },\n CHECKOUT_UPDATED_EVENTS: {\n queueName: \"checkout-events\",\n jobName: \"checkout-updated\",\n },\n CART_ACTIVITY_EVENTS: {\n queueName: \"checkout-events\",\n jobName: \"cart-activity\",\n },\n ORDER_EVENTS: {\n queueName: \"order-events\",\n jobName: \"order-completed\",\n },\n WHATSAPP_CONSENT_SYNC: {\n queueName: \"checkout-events\",\n jobName: \"shopify-whatsapp-consent-sync\",\n },\n SHOPIFY_DISCOUNT_SYNC: {\n queueName: \"shopify-discount-sync\",\n jobName: \"reconcile-shopify-discounts\",\n },\n} as const;\n\nexport const SHOPIFY_DISCOUNT_SYNC_REASONS = [\n \"SUBSCRIPTION_ACTIVATED\",\n \"REINSTALL_RECONCILED\",\n \"SCOPES_UPDATED\",\n \"ADMIN_REQUESTED\",\n \"DISCOUNT_WEBHOOK\",\n] as const;\n\nexport const SHOPIFY_DISCOUNT_SYNC_WEBHOOK_TOPICS = [\n \"discounts/create\",\n \"discounts/update\",\n \"discounts/delete\",\n \"discounts/redeemcode_added\",\n \"discounts/redeemcode_removed\",\n] as const;\n\nexport const ShopifyDiscountSyncReasonSchema = z.enum(SHOPIFY_DISCOUNT_SYNC_REASONS);\nexport const ShopifyDiscountSyncWebhookTopicSchema = z.enum(SHOPIFY_DISCOUNT_SYNC_WEBHOOK_TOPICS);\n\nexport const ShopifyDiscountSyncJobSchema = z\n .object({\n schemaVersion: z.literal(1),\n shopId: z.string().trim().min(1).max(128),\n shopDomain: z.string().trim().min(1).max(512),\n reason: ShopifyDiscountSyncReasonSchema,\n requestedAt: z.iso.datetime({ offset: true }),\n deliveryId: z.string().trim().min(1).nullable(),\n webhookTopic: ShopifyDiscountSyncWebhookTopicSchema.nullable(),\n })\n .strict()\n .superRefine((value, ctx) => {\n if (value.reason === \"DISCOUNT_WEBHOOK\") {\n if (!value.deliveryId || value.deliveryId.trim().length === 0) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"deliveryId\"],\n message: \"deliveryId is required when reason is DISCOUNT_WEBHOOK\",\n });\n }\n\n if (value.webhookTopic === null) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"webhookTopic\"],\n message: \"webhookTopic is required when reason is DISCOUNT_WEBHOOK\",\n });\n }\n } else {\n if (value.webhookTopic !== null) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"webhookTopic\"],\n message: \"webhookTopic must be null when reason is not DISCOUNT_WEBHOOK\",\n });\n }\n }\n });\n\nexport type ShopifyDiscountSyncJob = z.infer<typeof ShopifyDiscountSyncJobSchema>;\n\nexport function parseShopifyDiscountSyncJob(input: unknown): ShopifyDiscountSyncJob {\n return ShopifyDiscountSyncJobSchema.parse(input);\n}\n\nexport function safeParseShopifyDiscountSyncJob(input: unknown) {\n return ShopifyDiscountSyncJobSchema.safeParse(input);\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z"]}
@@ -0,0 +1,56 @@
1
+ // src/identity/shopify-customer.ts
2
+ var SHOPIFY_CUSTOMER_ID_MAX = 9223372036854775807n;
3
+ var CUSTOMER_GID_PREFIX = "gid://shopify/Customer/";
4
+ var CANONICAL_DECIMAL_ID = /^[1-9][0-9]*$/;
5
+ var ShopifyCustomerIdentityError = class extends Error {
6
+ constructor(code) {
7
+ super(code);
8
+ this.code = code;
9
+ this.name = "ShopifyCustomerIdentityError";
10
+ }
11
+ code;
12
+ };
13
+ function parseShopifyCustomerIdentity(input) {
14
+ const identifier = typeof input === "number" ? Number.isSafeInteger(input) && input > 0 ? String(input) : "" : typeof input === "string" ? input.startsWith(CUSTOMER_GID_PREFIX) ? input.slice(CUSTOMER_GID_PREFIX.length) : input : "";
15
+ if (!CANONICAL_DECIMAL_ID.test(identifier)) {
16
+ throw new ShopifyCustomerIdentityError("INVALID_CUSTOMER_ID");
17
+ }
18
+ if (identifier.length > 19 || BigInt(identifier) > SHOPIFY_CUSTOMER_ID_MAX) {
19
+ throw new ShopifyCustomerIdentityError("CUSTOMER_ID_OUT_OF_RANGE");
20
+ }
21
+ return Object.freeze({ numericId: identifier, gid: `${CUSTOMER_GID_PREFIX}${identifier}` });
22
+ }
23
+ function toShopifyCustomerGid(input) {
24
+ return parseShopifyCustomerIdentity(input).gid;
25
+ }
26
+ function toShopifyCustomerNumericId(input) {
27
+ return parseShopifyCustomerIdentity(input).numericId;
28
+ }
29
+ function isShopifyCustomerNumericId(input) {
30
+ if (typeof input !== "string") return false;
31
+ try {
32
+ return parseShopifyCustomerIdentity(input).numericId === input;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+ function areSameShopifyCustomerWithinShop(first, second) {
38
+ for (const shopId of [first.shopId, second.shopId]) {
39
+ if (typeof shopId !== "string" || shopId.length === 0 || shopId.length > 128 || shopId.trim() !== shopId || /\s/.test(shopId)) {
40
+ throw new ShopifyCustomerIdentityError("INVALID_SHOP_ID");
41
+ }
42
+ }
43
+ const firstId = toShopifyCustomerNumericId(first.customerId);
44
+ const secondId = toShopifyCustomerNumericId(second.customerId);
45
+ return first.shopId === second.shopId && firstId === secondId;
46
+ }
47
+
48
+ export {
49
+ ShopifyCustomerIdentityError,
50
+ parseShopifyCustomerIdentity,
51
+ toShopifyCustomerGid,
52
+ toShopifyCustomerNumericId,
53
+ isShopifyCustomerNumericId,
54
+ areSameShopifyCustomerWithinShop
55
+ };
56
+ //# sourceMappingURL=chunk-QG53EVA6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/identity/shopify-customer.ts"],"sourcesContent":["/** Shopify's numeric customer IDs are decimal strings, not JavaScript numbers.\n * The signed 64-bit bound avoids accidentally accepting out-of-range IDs in\n * consumers that persist or compare them with PostgreSQL BIGINT values.\n */\nconst SHOPIFY_CUSTOMER_ID_MAX = 9_223_372_036_854_775_807n;\nconst CUSTOMER_GID_PREFIX = \"gid://shopify/Customer/\";\nconst CANONICAL_DECIMAL_ID = /^[1-9][0-9]*$/;\n\nexport type ShopifyCustomerIdentityErrorCode =\n | \"INVALID_CUSTOMER_ID\"\n | \"CUSTOMER_ID_OUT_OF_RANGE\"\n | \"INVALID_SHOP_ID\";\n\n/** Never echo an untrusted customer identifier in an error message. */\nexport class ShopifyCustomerIdentityError extends Error {\n constructor(readonly code: ShopifyCustomerIdentityErrorCode) {\n super(code);\n this.name = \"ShopifyCustomerIdentityError\";\n }\n}\n\nexport type ShopifyCustomerIdentity = Readonly<{\n numericId: string;\n gid: string;\n}>;\n\n/**\n * Accept exact numeric webhook IDs or exact GraphQL Customer GIDs. A JSON\n * number is accepted only if JS can represent it without precision loss.\n */\nexport function parseShopifyCustomerIdentity(input: unknown): ShopifyCustomerIdentity {\n const identifier = typeof input === \"number\"\n ? Number.isSafeInteger(input) && input > 0\n ? String(input)\n : \"\"\n : typeof input === \"string\"\n ? input.startsWith(CUSTOMER_GID_PREFIX)\n ? input.slice(CUSTOMER_GID_PREFIX.length)\n : input\n : \"\";\n\n if (!CANONICAL_DECIMAL_ID.test(identifier)) {\n throw new ShopifyCustomerIdentityError(\"INVALID_CUSTOMER_ID\");\n }\n\n // Guard length before BigInt parsing of any externally controlled string.\n if (identifier.length > 19 || BigInt(identifier) > SHOPIFY_CUSTOMER_ID_MAX) {\n throw new ShopifyCustomerIdentityError(\"CUSTOMER_ID_OUT_OF_RANGE\");\n }\n\n return Object.freeze({ numericId: identifier, gid: `${CUSTOMER_GID_PREFIX}${identifier}` });\n}\n\nexport function toShopifyCustomerGid(input: unknown): string {\n return parseShopifyCustomerIdentity(input).gid;\n}\n\nexport function toShopifyCustomerNumericId(input: unknown): string {\n return parseShopifyCustomerIdentity(input).numericId;\n}\n\n/** Used by event schemas, which require the canonical decimal string form. */\nexport function isShopifyCustomerNumericId(input: unknown): input is string {\n if (typeof input !== \"string\") return false;\n try {\n return parseShopifyCustomerIdentity(input).numericId === input;\n } catch {\n return false;\n }\n}\n\n/**\n * Identity comparison is always scoped to a shop. It does NOT authenticate\n * either shop or establish that the caller may access either customer.\n */\nexport function areSameShopifyCustomerWithinShop(\n first: { shopId: string; customerId: unknown },\n second: { shopId: string; customerId: unknown },\n): boolean {\n for (const shopId of [first.shopId, second.shopId]) {\n if (typeof shopId !== \"string\" || shopId.length === 0 || shopId.length > 128 ||\n shopId.trim() !== shopId || /\\s/.test(shopId)) {\n throw new ShopifyCustomerIdentityError(\"INVALID_SHOP_ID\");\n }\n }\n\n const firstId = toShopifyCustomerNumericId(first.customerId);\n const secondId = toShopifyCustomerNumericId(second.customerId);\n return first.shopId === second.shopId && firstId === secondId;\n}\n"],"mappings":";AAIA,IAAM,0BAA0B;AAChC,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAQtB,IAAM,+BAAN,cAA2C,MAAM;AAAA,EACtD,YAAqB,MAAwC;AAC3D,UAAM,IAAI;AADS;AAEnB,SAAK,OAAO;AAAA,EACd;AAAA,EAHqB;AAIvB;AAWO,SAAS,6BAA6B,OAAyC;AACpF,QAAM,aAAa,OAAO,UAAU,WAChC,OAAO,cAAc,KAAK,KAAK,QAAQ,IACrC,OAAO,KAAK,IACZ,KACF,OAAO,UAAU,WACf,MAAM,WAAW,mBAAmB,IAClC,MAAM,MAAM,oBAAoB,MAAM,IACtC,QACF;AAEN,MAAI,CAAC,qBAAqB,KAAK,UAAU,GAAG;AAC1C,UAAM,IAAI,6BAA6B,qBAAqB;AAAA,EAC9D;AAGA,MAAI,WAAW,SAAS,MAAM,OAAO,UAAU,IAAI,yBAAyB;AAC1E,UAAM,IAAI,6BAA6B,0BAA0B;AAAA,EACnE;AAEA,SAAO,OAAO,OAAO,EAAE,WAAW,YAAY,KAAK,GAAG,mBAAmB,GAAG,UAAU,GAAG,CAAC;AAC5F;AAEO,SAAS,qBAAqB,OAAwB;AAC3D,SAAO,6BAA6B,KAAK,EAAE;AAC7C;AAEO,SAAS,2BAA2B,OAAwB;AACjE,SAAO,6BAA6B,KAAK,EAAE;AAC7C;AAGO,SAAS,2BAA2B,OAAiC;AAC1E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,6BAA6B,KAAK,EAAE,cAAc;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,iCACd,OACA,QACS;AACT,aAAW,UAAU,CAAC,MAAM,QAAQ,OAAO,MAAM,GAAG;AAClD,QAAI,OAAO,WAAW,YAAY,OAAO,WAAW,KAAK,OAAO,SAAS,OACrE,OAAO,KAAK,MAAM,UAAU,KAAK,KAAK,MAAM,GAAG;AACjD,YAAM,IAAI,6BAA6B,iBAAiB;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,UAAU,2BAA2B,MAAM,UAAU;AAC3D,QAAM,WAAW,2BAA2B,OAAO,UAAU;AAC7D,SAAO,MAAM,WAAW,OAAO,UAAU,YAAY;AACvD;","names":[]}
@@ -0,0 +1,52 @@
1
+ type RecipientPhoneRejection = "INVALID_FORMAT" | "COUNTRY_REQUIRED" | "UNSUPPORTED_COUNTRY" | "COUNTRY_MISMATCH" | "EXTENSION_UNSUPPORTED" | "INVALID_NUMBER";
2
+ type RecipientPhoneResult = Readonly<{
3
+ ok: true;
4
+ e164: string;
5
+ }> | Readonly<{
6
+ ok: false;
7
+ reason: RecipientPhoneRejection;
8
+ }>;
9
+ /**
10
+ * Canonical *number* identity only: it does not validate ownership,
11
+ * WhatsApp reachability, local STOP, or any marketing consent.
12
+ *
13
+ * `verifiedCountryCode` must come from a trustworthy country source, not from
14
+ * language/locale guesses. A national number without one always fails closed.
15
+ * Provider wa_id/digits-only conversion belongs in service-specific adapters.
16
+ */
17
+ declare function normalizeRecipientPhone(input: unknown, context?: {
18
+ verifiedCountryCode?: string;
19
+ }): RecipientPhoneResult;
20
+
21
+ type ShopifyCustomerIdentityErrorCode = "INVALID_CUSTOMER_ID" | "CUSTOMER_ID_OUT_OF_RANGE" | "INVALID_SHOP_ID";
22
+ /** Never echo an untrusted customer identifier in an error message. */
23
+ declare class ShopifyCustomerIdentityError extends Error {
24
+ readonly code: ShopifyCustomerIdentityErrorCode;
25
+ constructor(code: ShopifyCustomerIdentityErrorCode);
26
+ }
27
+ type ShopifyCustomerIdentity = Readonly<{
28
+ numericId: string;
29
+ gid: string;
30
+ }>;
31
+ /**
32
+ * Accept exact numeric webhook IDs or exact GraphQL Customer GIDs. A JSON
33
+ * number is accepted only if JS can represent it without precision loss.
34
+ */
35
+ declare function parseShopifyCustomerIdentity(input: unknown): ShopifyCustomerIdentity;
36
+ declare function toShopifyCustomerGid(input: unknown): string;
37
+ declare function toShopifyCustomerNumericId(input: unknown): string;
38
+ /** Used by event schemas, which require the canonical decimal string form. */
39
+ declare function isShopifyCustomerNumericId(input: unknown): input is string;
40
+ /**
41
+ * Identity comparison is always scoped to a shop. It does NOT authenticate
42
+ * either shop or establish that the caller may access either customer.
43
+ */
44
+ declare function areSameShopifyCustomerWithinShop(first: {
45
+ shopId: string;
46
+ customerId: unknown;
47
+ }, second: {
48
+ shopId: string;
49
+ customerId: unknown;
50
+ }): boolean;
51
+
52
+ export { type RecipientPhoneRejection, type RecipientPhoneResult, type ShopifyCustomerIdentity, ShopifyCustomerIdentityError, type ShopifyCustomerIdentityErrorCode, areSameShopifyCustomerWithinShop, isShopifyCustomerNumericId, normalizeRecipientPhone, parseShopifyCustomerIdentity, toShopifyCustomerGid, toShopifyCustomerNumericId };
@@ -0,0 +1,61 @@
1
+ import {
2
+ ShopifyCustomerIdentityError,
3
+ areSameShopifyCustomerWithinShop,
4
+ isShopifyCustomerNumericId,
5
+ parseShopifyCustomerIdentity,
6
+ toShopifyCustomerGid,
7
+ toShopifyCustomerNumericId
8
+ } from "../chunk-QG53EVA6.js";
9
+
10
+ // src/identity/recipient-phone.ts
11
+ import {
12
+ isSupportedCountry,
13
+ parsePhoneNumberFromString
14
+ } from "libphonenumber-js/max";
15
+ function normalizeRecipientPhone(input, context = {}) {
16
+ if (typeof input !== "string" || input.length > 64) {
17
+ return { ok: false, reason: "INVALID_FORMAT" };
18
+ }
19
+ const raw = input.trim();
20
+ if (raw.length === 0) return { ok: false, reason: "INVALID_FORMAT" };
21
+ if (/\b(?:ext\.?|extension|x)\s*\d/i.test(raw) || /[#;]/.test(raw)) {
22
+ return { ok: false, reason: "EXTENSION_UNSUPPORTED" };
23
+ }
24
+ if (!/^\+?[0-9() .-]+$/.test(raw)) {
25
+ return { ok: false, reason: "INVALID_FORMAT" };
26
+ }
27
+ const isInternational = raw.startsWith("+");
28
+ const suppliedCountry = context.verifiedCountryCode;
29
+ if (!isInternational && suppliedCountry === void 0) {
30
+ return { ok: false, reason: "COUNTRY_REQUIRED" };
31
+ }
32
+ if (suppliedCountry !== void 0 && (!/^[A-Z]{2}$/.test(suppliedCountry) || !isSupportedCountry(suppliedCountry))) {
33
+ return { ok: false, reason: "UNSUPPORTED_COUNTRY" };
34
+ }
35
+ try {
36
+ const phone = parsePhoneNumberFromString(raw, {
37
+ ...suppliedCountry ? { defaultCountry: suppliedCountry } : {},
38
+ extract: false
39
+ });
40
+ if (!phone || !phone.isValid() || !/^\+[1-9][0-9]{1,14}$/.test(phone.number)) {
41
+ return { ok: false, reason: "INVALID_NUMBER" };
42
+ }
43
+ if (phone.ext) return { ok: false, reason: "EXTENSION_UNSUPPORTED" };
44
+ if (suppliedCountry && phone.country !== suppliedCountry) {
45
+ return { ok: false, reason: "COUNTRY_MISMATCH" };
46
+ }
47
+ return { ok: true, e164: phone.number };
48
+ } catch {
49
+ return { ok: false, reason: "INVALID_NUMBER" };
50
+ }
51
+ }
52
+ export {
53
+ ShopifyCustomerIdentityError,
54
+ areSameShopifyCustomerWithinShop,
55
+ isShopifyCustomerNumericId,
56
+ normalizeRecipientPhone,
57
+ parseShopifyCustomerIdentity,
58
+ toShopifyCustomerGid,
59
+ toShopifyCustomerNumericId
60
+ };
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/identity/recipient-phone.ts"],"sourcesContent":["import {\n isSupportedCountry,\n parsePhoneNumberFromString,\n type CountryCode,\n} from \"libphonenumber-js/max\";\n\nexport type RecipientPhoneRejection =\n | \"INVALID_FORMAT\"\n | \"COUNTRY_REQUIRED\"\n | \"UNSUPPORTED_COUNTRY\"\n | \"COUNTRY_MISMATCH\"\n | \"EXTENSION_UNSUPPORTED\"\n | \"INVALID_NUMBER\";\n\nexport type RecipientPhoneResult =\n | Readonly<{ ok: true; e164: string }>\n | Readonly<{ ok: false; reason: RecipientPhoneRejection }>;\n\n/**\n * Canonical *number* identity only: it does not validate ownership,\n * WhatsApp reachability, local STOP, or any marketing consent.\n *\n * `verifiedCountryCode` must come from a trustworthy country source, not from\n * language/locale guesses. A national number without one always fails closed.\n * Provider wa_id/digits-only conversion belongs in service-specific adapters.\n */\nexport function normalizeRecipientPhone(\n input: unknown,\n context: { verifiedCountryCode?: string } = {},\n): RecipientPhoneResult {\n if (typeof input !== \"string\" || input.length > 64) {\n return { ok: false, reason: \"INVALID_FORMAT\" };\n }\n\n const raw = input.trim();\n if (raw.length === 0) return { ok: false, reason: \"INVALID_FORMAT\" };\n // Reject letters, dial strings, extensions and characters other than normal\n // phone presentation punctuation. Do not silently remove an extension.\n if (/\\b(?:ext\\.?|extension|x)\\s*\\d/i.test(raw) || /[#;]/.test(raw)) {\n return { ok: false, reason: \"EXTENSION_UNSUPPORTED\" };\n }\n if (!/^\\+?[0-9() .-]+$/.test(raw)) {\n return { ok: false, reason: \"INVALID_FORMAT\" };\n }\n\n const isInternational = raw.startsWith(\"+\");\n const suppliedCountry = context.verifiedCountryCode;\n if (!isInternational && suppliedCountry === undefined) {\n return { ok: false, reason: \"COUNTRY_REQUIRED\" };\n }\n if (suppliedCountry !== undefined &&\n (!/^[A-Z]{2}$/.test(suppliedCountry) ||\n !isSupportedCountry(suppliedCountry as CountryCode))) {\n return { ok: false, reason: \"UNSUPPORTED_COUNTRY\" };\n }\n\n try {\n const phone = parsePhoneNumberFromString(raw, {\n ...(suppliedCountry ? { defaultCountry: suppliedCountry as CountryCode } : {}),\n extract: false,\n });\n if (!phone || !phone.isValid() || !/^\\+[1-9][0-9]{1,14}$/.test(phone.number)) {\n return { ok: false, reason: \"INVALID_NUMBER\" };\n }\n if (phone.ext) return { ok: false, reason: \"EXTENSION_UNSUPPORTED\" };\n // With a verified context, an unrelated international number must not be\n // silently attributed to that country. The caller can omit the context\n // entirely when normalising an explicitly international number.\n if (suppliedCountry && phone.country !== suppliedCountry) {\n return { ok: false, reason: \"COUNTRY_MISMATCH\" };\n }\n return { ok: true, e164: phone.number };\n } catch {\n return { ok: false, reason: \"INVALID_NUMBER\" };\n }\n}\n"],"mappings":";;;;;;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAsBA,SAAS,wBACd,OACA,UAA4C,CAAC,GACvB;AACtB,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI;AAClD,WAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAAA,EAC/C;AAEA,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,IAAI,WAAW,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAGnE,MAAI,iCAAiC,KAAK,GAAG,KAAK,OAAO,KAAK,GAAG,GAAG;AAClE,WAAO,EAAE,IAAI,OAAO,QAAQ,wBAAwB;AAAA,EACtD;AACA,MAAI,CAAC,mBAAmB,KAAK,GAAG,GAAG;AACjC,WAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAAA,EAC/C;AAEA,QAAM,kBAAkB,IAAI,WAAW,GAAG;AAC1C,QAAM,kBAAkB,QAAQ;AAChC,MAAI,CAAC,mBAAmB,oBAAoB,QAAW;AACrD,WAAO,EAAE,IAAI,OAAO,QAAQ,mBAAmB;AAAA,EACjD;AACA,MAAI,oBAAoB,WACnB,CAAC,aAAa,KAAK,eAAe,KAClC,CAAC,mBAAmB,eAA8B,IAAI;AACzD,WAAO,EAAE,IAAI,OAAO,QAAQ,sBAAsB;AAAA,EACpD;AAEA,MAAI;AACF,UAAM,QAAQ,2BAA2B,KAAK;AAAA,MAC5C,GAAI,kBAAkB,EAAE,gBAAgB,gBAA+B,IAAI,CAAC;AAAA,MAC5E,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,uBAAuB,KAAK,MAAM,MAAM,GAAG;AAC5E,aAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAAA,IAC/C;AACA,QAAI,MAAM,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,wBAAwB;AAInE,QAAI,mBAAmB,MAAM,YAAY,iBAAiB;AACxD,aAAO,EAAE,IAAI,OAAO,QAAQ,mBAAmB;AAAA,IACjD;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,MAAM,OAAO;AAAA,EACxC,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAAA,EAC/C;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -3,5 +3,6 @@ export { APP_PRICING_BILLING_PERIOD_DRAIN_WINDOW_MS, ARCH007_BILLING_CONTRACT_SC
3
3
  export { AuthoredSupportBodySchema, MERCHANT_COMMUNICATIONS_JOB_NAMES, MERCHANT_COMMUNICATIONS_QUEUE_NAME, MERCHANT_COMMUNICATIONS_SCHEMA_VERSION, MerchantMessageTranslationContract, MerchantMessageTranslationContractSchema, MerchantMessageTranslationStatus, MerchantMessageTranslationStatusSchema, MerchantSupportMessageContract, MerchantSupportMessageContractSchema, MerchantSupportMessageKind, MerchantSupportMessageKindSchema, MerchantSupportMessageState, MerchantSupportMessageStateSchema, MerchantTranslationBatchContract, MerchantTranslationBatchContractSchema, MerchantTranslationBatchStatus, MerchantTranslationBatchStatusSchema, MerchantTranslationDirection, MerchantTranslationDirectionSchema, MerchantTranslationReconciliationRequestContract, MerchantTranslationReconciliationRequestContractSchema, MerchantTranslationReconciliationScope, MerchantTranslationReconciliationScopeSchema, MerchantTranslationReconciliationStatus, MerchantTranslationReconciliationStatusSchema, PLATFORM_SUPPORT_LANGUAGE_TAG, TranslatedSupportBodySchema, TranslationBatchPollJob, TranslationBatchPollJobSchema, TranslationBatchResultsJob, TranslationBatchResultsJobSchema, TranslationBatchSubmitJob, TranslationBatchSubmitJobSchema, TranslationDispatchJob, TranslationDispatchJobSchema, TranslationReconcileJob, TranslationReconcileJobSchema, countUnicodeGraphemes, requiresMerchantTranslation } from './merchant-communications.js';
4
4
  export { EffectiveRecoveryPolicy, EffectiveRecoveryPolicySchema, RECOVERY_OFFER_MODES, RecoveryOfferMode, RecoveryOfferModeSchema, parseEffectiveRecoveryPolicy, safeParseEffectiveRecoveryPolicy } from './recovery-policy.js';
5
5
  export { CheckoutCreatedPayloadV2, CheckoutCreatedPayloadV2Schema, CheckoutObservedPayload, CheckoutObservedPayloadSchema, CheckoutUpdatedPayloadV2, CheckoutUpdatedPayloadV2Schema, CheckoutUpdatedPayloadV3, CheckoutUpdatedPayloadV3Schema, MAX_LINE_ITEMS, MAX_URL_LENGTH, OrderCompletedPayload, OrderCompletedPayloadSchema, OrderCompletedPayloadV2, OrderCompletedPayloadV2Schema, SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION, SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V1, SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2, SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V3, SHOPIFY_COMMERCE_EVENT_TYPES, SHOPIFY_DISCOUNT_SYNC_REASONS, SHOPIFY_DISCOUNT_SYNC_WEBHOOK_TOPICS, SHOPIFY_RECOVERY_EVENT_TYPES_V2, SHOPIFY_RECOVERY_EVENT_TYPES_V3, SHOPIFY_WEBHOOK_OUTBOX_DESTINATIONS, SHOPIFY_WEBHOOK_QUEUE_CONTRACTS, ShopifyCartActivityEventV2, ShopifyCartActivityEventV2Schema, ShopifyCartActivityPayloadV2, ShopifyCartActivityPayloadV2Schema, ShopifyCheckoutCreatedEventV2, ShopifyCheckoutCreatedEventV2Schema, ShopifyCheckoutLineItem, ShopifyCheckoutLineItemSchema, ShopifyCheckoutLineItemsSchema, ShopifyCheckoutObservedEvent, ShopifyCheckoutObservedEventSchema, ShopifyCheckoutUpdatedEventV2, ShopifyCheckoutUpdatedEventV2Schema, ShopifyCheckoutUpdatedEventV3, ShopifyCheckoutUpdatedEventV3Schema, ShopifyCommerceEvent, ShopifyCommerceEventSchema, ShopifyCommerceEventType, ShopifyCustomerReference, ShopifyCustomerReferenceSchema, ShopifyDiscountSyncJob, ShopifyDiscountSyncJobSchema, ShopifyDiscountSyncReasonSchema, ShopifyDiscountSyncWebhookTopicSchema, ShopifyMoney, ShopifyMoneySchema, ShopifyOrderCompletedEvent, ShopifyOrderCompletedEventSchema, ShopifyOrderCompletedEventV2, ShopifyOrderCompletedEventV2Schema, ShopifyRecoveryEvent, ShopifyRecoveryEventSchema, ShopifyRecoveryEventTypeV2, ShopifyRecoveryEventV2, ShopifyRecoveryEventV2Schema, ShopifyTenant, ShopifyTenantSchema, createShopifyCartActivityOrderingKey, createShopifyCommerceOrderingKey, createShopifyOrderCorrelationOrderingKey, createShopifyOrderOrderingKey, createShopifyPendingRecoveryOrderingKey, isCartActivityEventV2, isCheckoutCreatedEventV2, isCheckoutObservedEvent, isCheckoutUpdatedEventV2, isOrderCompletedEvent, isOrderCompletedEventV2, parseShopifyCommerceEvent, parseShopifyDiscountSyncJob, parseShopifyRecoveryEvent, parseShopifyRecoveryEventV2, safeParseShopifyCommerceEvent, safeParseShopifyDiscountSyncJob, safeParseShopifyRecoveryEvent, safeParseShopifyRecoveryEventV2 } from './shopify/index.js';
6
+ export { S as SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION, a as SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC, b as ShopifyWhatsAppConsentSyncJobV1, c as ShopifyWhatsAppConsentSyncJobV1Schema, p as parseShopifyWhatsAppConsentSyncJobV1, s as safeParseShopifyWhatsAppConsentSyncJobV1 } from './whatsapp-consent-sync.schema-DEU84xTL.js';
6
7
  import 'zod';
7
8
  import 'intl-messageformat';
package/dist/index.js CHANGED
@@ -56,7 +56,15 @@ import {
56
56
  safeParseShopifyDiscountSyncJob,
57
57
  safeParseShopifyRecoveryEvent,
58
58
  safeParseShopifyRecoveryEventV2
59
- } from "./chunk-SU2DAOEC.js";
59
+ } from "./chunk-NHIGHAEQ.js";
60
+ import {
61
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION,
62
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC,
63
+ ShopifyWhatsAppConsentSyncJobV1Schema,
64
+ parseShopifyWhatsAppConsentSyncJobV1,
65
+ safeParseShopifyWhatsAppConsentSyncJobV1
66
+ } from "./chunk-G7FCWVBU.js";
67
+ import "./chunk-QG53EVA6.js";
60
68
  import {
61
69
  APP_PRICING_BILLING_PERIOD_DRAIN_WINDOW_MS,
62
70
  ARCH007_BILLING_CONTRACT_SCHEMA_VERSION,
@@ -204,6 +212,8 @@ export {
204
212
  SHOPIFY_RECOVERY_EVENT_TYPES_V3,
205
213
  SHOPIFY_WEBHOOK_OUTBOX_DESTINATIONS,
206
214
  SHOPIFY_WEBHOOK_QUEUE_CONTRACTS,
215
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION,
216
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC,
207
217
  ShopifyCartActivityEventV2Schema,
208
218
  ShopifyCartActivityPayloadV2Schema,
209
219
  ShopifyCheckoutCreatedEventV2Schema,
@@ -223,6 +233,7 @@ export {
223
233
  ShopifyRecoveryEventSchema,
224
234
  ShopifyRecoveryEventV2Schema,
225
235
  ShopifyTenantSchema,
236
+ ShopifyWhatsAppConsentSyncJobV1Schema,
226
237
  TimeZoneIdSchema,
227
238
  TranslatedSupportBodySchema,
228
239
  TranslationBatchPollJobSchema,
@@ -268,6 +279,7 @@ export {
268
279
  parseShopifyDiscountSyncJob,
269
280
  parseShopifyRecoveryEvent,
270
281
  parseShopifyRecoveryEventV2,
282
+ parseShopifyWhatsAppConsentSyncJobV1,
271
283
  requiresMerchantTranslation,
272
284
  resolveLocaleDirection,
273
285
  resolveModaConfigurationLocale,
@@ -278,6 +290,7 @@ export {
278
290
  safeParseShopifyDiscountSyncJob,
279
291
  safeParseShopifyRecoveryEvent,
280
292
  safeParseShopifyRecoveryEventV2,
293
+ safeParseShopifyWhatsAppConsentSyncJobV1,
281
294
  validateIcuCatalogue
282
295
  };
283
296
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ export { S as SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION, a as SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC, b as ShopifyWhatsAppConsentSyncJobV1, c as ShopifyWhatsAppConsentSyncJobV1Schema, p as parseShopifyWhatsAppConsentSyncJobV1, s as safeParseShopifyWhatsAppConsentSyncJobV1 } from '../whatsapp-consent-sync.schema-DEU84xTL.js';
2
3
 
3
4
  declare const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V1: 1;
4
5
  declare const SHOPIFY_COMMERCE_EVENT_SCHEMA_VERSION_V2: 2;
@@ -1154,6 +1155,10 @@ declare const SHOPIFY_WEBHOOK_QUEUE_CONTRACTS: {
1154
1155
  readonly queueName: "order-events";
1155
1156
  readonly jobName: "order-completed";
1156
1157
  };
1158
+ readonly WHATSAPP_CONSENT_SYNC: {
1159
+ readonly queueName: "checkout-events";
1160
+ readonly jobName: "shopify-whatsapp-consent-sync";
1161
+ };
1157
1162
  readonly SHOPIFY_DISCOUNT_SYNC: {
1158
1163
  readonly queueName: "shopify-discount-sync";
1159
1164
  readonly jobName: "reconcile-shopify-discounts";
@@ -56,7 +56,15 @@ import {
56
56
  safeParseShopifyDiscountSyncJob,
57
57
  safeParseShopifyRecoveryEvent,
58
58
  safeParseShopifyRecoveryEventV2
59
- } from "../chunk-SU2DAOEC.js";
59
+ } from "../chunk-NHIGHAEQ.js";
60
+ import {
61
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION,
62
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC,
63
+ ShopifyWhatsAppConsentSyncJobV1Schema,
64
+ parseShopifyWhatsAppConsentSyncJobV1,
65
+ safeParseShopifyWhatsAppConsentSyncJobV1
66
+ } from "../chunk-G7FCWVBU.js";
67
+ import "../chunk-QG53EVA6.js";
60
68
  import "../chunk-4KSODB2R.js";
61
69
  export {
62
70
  CheckoutCreatedPayloadV2Schema,
@@ -78,6 +86,8 @@ export {
78
86
  SHOPIFY_RECOVERY_EVENT_TYPES_V3,
79
87
  SHOPIFY_WEBHOOK_OUTBOX_DESTINATIONS,
80
88
  SHOPIFY_WEBHOOK_QUEUE_CONTRACTS,
89
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION,
90
+ SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC,
81
91
  ShopifyCartActivityEventV2Schema,
82
92
  ShopifyCartActivityPayloadV2Schema,
83
93
  ShopifyCheckoutCreatedEventV2Schema,
@@ -97,6 +107,7 @@ export {
97
107
  ShopifyRecoveryEventSchema,
98
108
  ShopifyRecoveryEventV2Schema,
99
109
  ShopifyTenantSchema,
110
+ ShopifyWhatsAppConsentSyncJobV1Schema,
100
111
  createShopifyCartActivityOrderingKey,
101
112
  createShopifyCommerceOrderingKey,
102
113
  createShopifyOrderCorrelationOrderingKey,
@@ -112,9 +123,11 @@ export {
112
123
  parseShopifyDiscountSyncJob,
113
124
  parseShopifyRecoveryEvent,
114
125
  parseShopifyRecoveryEventV2,
126
+ parseShopifyWhatsAppConsentSyncJobV1,
115
127
  safeParseShopifyCommerceEvent,
116
128
  safeParseShopifyDiscountSyncJob,
117
129
  safeParseShopifyRecoveryEvent,
118
- safeParseShopifyRecoveryEventV2
130
+ safeParseShopifyRecoveryEventV2,
131
+ safeParseShopifyWhatsAppConsentSyncJobV1
119
132
  };
120
133
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,6 @@
1
+ import { b as ShopifyWhatsAppConsentSyncJobV1 } from '../whatsapp-consent-sync.schema-DEU84xTL.js';
2
+ import 'zod';
3
+
1
4
  /**
2
5
  * Legacy webhook delivery job id used by earlier receipt/outbox flows.
3
6
  * Prefer the resource-scoped helpers below for current queue publication.
@@ -12,5 +15,11 @@ declare function createShopifyDiscountSyncJobId(event: {
12
15
  requestedAt: string;
13
16
  deliveryId?: string | null;
14
17
  }): string;
18
+ /**
19
+ * One deduplication identity per authenticated shop + webhook delivery.
20
+ * Length-safe JSON tuple serialization avoids `a:b` vs `a`/`b:c` collisions;
21
+ * the job ID never reveals the customer ID, delivery ID or phone.
22
+ */
23
+ declare function createShopifyWhatsAppConsentSyncJobId(input: ShopifyWhatsAppConsentSyncJobV1): string;
15
24
 
16
- export { createPendingRecoveryCandidateJobId, createShopifyCheckoutJobId, createShopifyDiscountSyncJobId, createShopifyOrderJobId, createShopifyWebhookJobId };
25
+ export { createPendingRecoveryCandidateJobId, createShopifyCheckoutJobId, createShopifyDiscountSyncJobId, createShopifyOrderJobId, createShopifyWebhookJobId, createShopifyWhatsAppConsentSyncJobId };
@@ -1,3 +1,8 @@
1
+ import {
2
+ ShopifyWhatsAppConsentSyncJobV1Schema
3
+ } from "../chunk-G7FCWVBU.js";
4
+ import "../chunk-QG53EVA6.js";
5
+
1
6
  // src/shopify/node.ts
2
7
  import { createHash } from "crypto";
3
8
  function createShopifyWebhookJobDigest(prefix, first, second) {
@@ -37,11 +42,17 @@ function createShopifyDiscountSyncJobId(event) {
37
42
  }
38
43
  return createShopifyWebhookJobDigest("discount-sync", `${event.shopId}:${event.reason}`, event.requestedAt);
39
44
  }
45
+ function createShopifyWhatsAppConsentSyncJobId(input) {
46
+ const job = ShopifyWhatsAppConsentSyncJobV1Schema.parse(input);
47
+ const digest = createHash("sha256").update(JSON.stringify([job.shopId, job.deliveryId])).digest("hex");
48
+ return `shopify-whatsapp-consent-sync-${digest}`;
49
+ }
40
50
  export {
41
51
  createPendingRecoveryCandidateJobId,
42
52
  createShopifyCheckoutJobId,
43
53
  createShopifyDiscountSyncJobId,
44
54
  createShopifyOrderJobId,
45
- createShopifyWebhookJobId
55
+ createShopifyWebhookJobId,
56
+ createShopifyWhatsAppConsentSyncJobId
46
57
  };
47
58
  //# sourceMappingURL=node.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shopify/node.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nfunction createShopifyWebhookJobDigest(prefix: string, first: string, second: string): string {\n if (typeof first !== \"string\" || first.length === 0) {\n throw new Error(`${prefix}: first input must be a non-empty string`);\n }\n\n if (typeof second !== \"string\" || second.length === 0) {\n throw new Error(`${prefix}: second input must be a non-empty string`);\n }\n\n const digest = createHash(\"sha256\").update(`${first}:${second}`).digest(\"hex\");\n return `${prefix}-${digest}`;\n}\n\n/**\n * Legacy webhook delivery job id used by earlier receipt/outbox flows.\n * Prefer the resource-scoped helpers below for current queue publication.\n */\nexport function createShopifyWebhookJobId(appKey: string, deliveryId: string): string {\n return createShopifyWebhookJobDigest(\"shopify\", appKey, deliveryId);\n}\n\nexport function createShopifyCheckoutJobId(shopId: string, checkoutToken: string): string {\n return createShopifyWebhookJobDigest(\"checkout\", shopId, checkoutToken);\n}\n\nexport function createShopifyOrderJobId(shopId: string, orderGid: string): string {\n return createShopifyWebhookJobDigest(\"order-created\", shopId, orderGid);\n}\n\nexport function createPendingRecoveryCandidateJobId(\n shopId: string,\n checkoutToken: string,\n): string {\n return createShopifyWebhookJobDigest(\"pending-recovery\", shopId, checkoutToken);\n}\n\nexport function createShopifyDiscountSyncJobId(event: {\n shopId: string;\n reason: \"SUBSCRIPTION_ACTIVATED\" | \"REINSTALL_RECONCILED\" | \"SCOPES_UPDATED\" | \"ADMIN_REQUESTED\" | \"DISCOUNT_WEBHOOK\";\n requestedAt: string;\n deliveryId?: string | null;\n}): string {\n if (typeof event.shopId !== \"string\" || event.shopId.trim().length === 0) {\n throw new Error(\"createShopifyDiscountSyncJobId: shopId must be a non-empty string\");\n }\n\n if (event.reason === \"DISCOUNT_WEBHOOK\") {\n if (typeof event.deliveryId !== \"string\" || event.deliveryId.trim().length === 0) {\n throw new Error(\"createShopifyDiscountSyncJobId: deliveryId is required for DISCOUNT_WEBHOOK\");\n }\n return createShopifyWebhookJobDigest(\"discount-sync\", event.shopId, event.deliveryId);\n }\n\n if (typeof event.requestedAt !== \"string\" || event.requestedAt.trim().length === 0) {\n throw new Error(\"createShopifyDiscountSyncJobId: requestedAt must be a non-empty ISO datetime string\");\n }\n\n return createShopifyWebhookJobDigest(\"discount-sync\", `${event.shopId}:${event.reason}`, event.requestedAt);\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAE3B,SAAS,8BAA8B,QAAgB,OAAe,QAAwB;AAC5F,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,UAAM,IAAI,MAAM,GAAG,MAAM,0CAA0C;AAAA,EACrE;AAEA,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI,MAAM,GAAG,MAAM,2CAA2C;AAAA,EACtE;AAEA,QAAM,SAAS,WAAW,QAAQ,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,EAAE,EAAE,OAAO,KAAK;AAC7E,SAAO,GAAG,MAAM,IAAI,MAAM;AAC5B;AAMO,SAAS,0BAA0B,QAAgB,YAA4B;AACpF,SAAO,8BAA8B,WAAW,QAAQ,UAAU;AACpE;AAEO,SAAS,2BAA2B,QAAgB,eAA+B;AACxF,SAAO,8BAA8B,YAAY,QAAQ,aAAa;AACxE;AAEO,SAAS,wBAAwB,QAAgB,UAA0B;AAChF,SAAO,8BAA8B,iBAAiB,QAAQ,QAAQ;AACxE;AAEO,SAAS,oCACd,QACA,eACQ;AACR,SAAO,8BAA8B,oBAAoB,QAAQ,aAAa;AAChF;AAEO,SAAS,+BAA+B,OAKpC;AACT,MAAI,OAAO,MAAM,WAAW,YAAY,MAAM,OAAO,KAAK,EAAE,WAAW,GAAG;AACxE,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AAEA,MAAI,MAAM,WAAW,oBAAoB;AACvC,QAAI,OAAO,MAAM,eAAe,YAAY,MAAM,WAAW,KAAK,EAAE,WAAW,GAAG;AAChF,YAAM,IAAI,MAAM,6EAA6E;AAAA,IAC/F;AACA,WAAO,8BAA8B,iBAAiB,MAAM,QAAQ,MAAM,UAAU;AAAA,EACtF;AAEA,MAAI,OAAO,MAAM,gBAAgB,YAAY,MAAM,YAAY,KAAK,EAAE,WAAW,GAAG;AAClF,UAAM,IAAI,MAAM,qFAAqF;AAAA,EACvG;AAEA,SAAO,8BAA8B,iBAAiB,GAAG,MAAM,MAAM,IAAI,MAAM,MAAM,IAAI,MAAM,WAAW;AAC5G;","names":[]}
1
+ {"version":3,"sources":["../../src/shopify/node.ts"],"sourcesContent":["import { ShopifyWhatsAppConsentSyncJobV1Schema, type ShopifyWhatsAppConsentSyncJobV1 } from \"./whatsapp-consent-sync.schema.js\";\nimport { createHash } from \"node:crypto\";\n\nfunction createShopifyWebhookJobDigest(prefix: string, first: string, second: string): string {\n if (typeof first !== \"string\" || first.length === 0) {\n throw new Error(`${prefix}: first input must be a non-empty string`);\n }\n\n if (typeof second !== \"string\" || second.length === 0) {\n throw new Error(`${prefix}: second input must be a non-empty string`);\n }\n\n const digest = createHash(\"sha256\").update(`${first}:${second}`).digest(\"hex\");\n return `${prefix}-${digest}`;\n}\n\n/**\n * Legacy webhook delivery job id used by earlier receipt/outbox flows.\n * Prefer the resource-scoped helpers below for current queue publication.\n */\nexport function createShopifyWebhookJobId(appKey: string, deliveryId: string): string {\n return createShopifyWebhookJobDigest(\"shopify\", appKey, deliveryId);\n}\n\nexport function createShopifyCheckoutJobId(shopId: string, checkoutToken: string): string {\n return createShopifyWebhookJobDigest(\"checkout\", shopId, checkoutToken);\n}\n\nexport function createShopifyOrderJobId(shopId: string, orderGid: string): string {\n return createShopifyWebhookJobDigest(\"order-created\", shopId, orderGid);\n}\n\nexport function createPendingRecoveryCandidateJobId(\n shopId: string,\n checkoutToken: string,\n): string {\n return createShopifyWebhookJobDigest(\"pending-recovery\", shopId, checkoutToken);\n}\n\nexport function createShopifyDiscountSyncJobId(event: {\n shopId: string;\n reason: \"SUBSCRIPTION_ACTIVATED\" | \"REINSTALL_RECONCILED\" | \"SCOPES_UPDATED\" | \"ADMIN_REQUESTED\" | \"DISCOUNT_WEBHOOK\";\n requestedAt: string;\n deliveryId?: string | null;\n}): string {\n if (typeof event.shopId !== \"string\" || event.shopId.trim().length === 0) {\n throw new Error(\"createShopifyDiscountSyncJobId: shopId must be a non-empty string\");\n }\n\n if (event.reason === \"DISCOUNT_WEBHOOK\") {\n if (typeof event.deliveryId !== \"string\" || event.deliveryId.trim().length === 0) {\n throw new Error(\"createShopifyDiscountSyncJobId: deliveryId is required for DISCOUNT_WEBHOOK\");\n }\n return createShopifyWebhookJobDigest(\"discount-sync\", event.shopId, event.deliveryId);\n }\n\n if (typeof event.requestedAt !== \"string\" || event.requestedAt.trim().length === 0) {\n throw new Error(\"createShopifyDiscountSyncJobId: requestedAt must be a non-empty ISO datetime string\");\n }\n\n return createShopifyWebhookJobDigest(\"discount-sync\", `${event.shopId}:${event.reason}`, event.requestedAt);\n}\n\n/**\n * One deduplication identity per authenticated shop + webhook delivery.\n * Length-safe JSON tuple serialization avoids `a:b` vs `a`/`b:c` collisions;\n * the job ID never reveals the customer ID, delivery ID or phone.\n */\nexport function createShopifyWhatsAppConsentSyncJobId(\n input: ShopifyWhatsAppConsentSyncJobV1,\n): string {\n const job = ShopifyWhatsAppConsentSyncJobV1Schema.parse(input);\n const digest = createHash(\"sha256\")\n .update(JSON.stringify([job.shopId, job.deliveryId]))\n .digest(\"hex\");\n return `shopify-whatsapp-consent-sync-${digest}`;\n}\n"],"mappings":";;;;;;AACA,SAAS,kBAAkB;AAE3B,SAAS,8BAA8B,QAAgB,OAAe,QAAwB;AAC5F,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,UAAM,IAAI,MAAM,GAAG,MAAM,0CAA0C;AAAA,EACrE;AAEA,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI,MAAM,GAAG,MAAM,2CAA2C;AAAA,EACtE;AAEA,QAAM,SAAS,WAAW,QAAQ,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,EAAE,EAAE,OAAO,KAAK;AAC7E,SAAO,GAAG,MAAM,IAAI,MAAM;AAC5B;AAMO,SAAS,0BAA0B,QAAgB,YAA4B;AACpF,SAAO,8BAA8B,WAAW,QAAQ,UAAU;AACpE;AAEO,SAAS,2BAA2B,QAAgB,eAA+B;AACxF,SAAO,8BAA8B,YAAY,QAAQ,aAAa;AACxE;AAEO,SAAS,wBAAwB,QAAgB,UAA0B;AAChF,SAAO,8BAA8B,iBAAiB,QAAQ,QAAQ;AACxE;AAEO,SAAS,oCACd,QACA,eACQ;AACR,SAAO,8BAA8B,oBAAoB,QAAQ,aAAa;AAChF;AAEO,SAAS,+BAA+B,OAKpC;AACT,MAAI,OAAO,MAAM,WAAW,YAAY,MAAM,OAAO,KAAK,EAAE,WAAW,GAAG;AACxE,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AAEA,MAAI,MAAM,WAAW,oBAAoB;AACvC,QAAI,OAAO,MAAM,eAAe,YAAY,MAAM,WAAW,KAAK,EAAE,WAAW,GAAG;AAChF,YAAM,IAAI,MAAM,6EAA6E;AAAA,IAC/F;AACA,WAAO,8BAA8B,iBAAiB,MAAM,QAAQ,MAAM,UAAU;AAAA,EACtF;AAEA,MAAI,OAAO,MAAM,gBAAgB,YAAY,MAAM,YAAY,KAAK,EAAE,WAAW,GAAG;AAClF,UAAM,IAAI,MAAM,qFAAqF;AAAA,EACvG;AAEA,SAAO,8BAA8B,iBAAiB,GAAG,MAAM,MAAM,IAAI,MAAM,MAAM,IAAI,MAAM,WAAW;AAC5G;AAOO,SAAS,sCACd,OACQ;AACR,QAAM,MAAM,sCAAsC,MAAM,KAAK;AAC7D,QAAM,SAAS,WAAW,QAAQ,EAC/B,OAAO,KAAK,UAAU,CAAC,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC,EACnD,OAAO,KAAK;AACf,SAAO,iCAAiC,MAAM;AAChD;","names":[]}
@@ -0,0 +1,27 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION: 1;
4
+ declare const SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC: "customers_whats_app_marketing_consent/update";
5
+ /**
6
+ * A webhook notification to re-read CURRENT Shopify consent in Background.
7
+ * These fields contain no consent decision, phone, checkout or recovery data;
8
+ * parsing this job never authorises a recovery or an outbound message.
9
+ */
10
+ declare const ShopifyWhatsAppConsentSyncJobV1Schema: z.ZodObject<{
11
+ schemaVersion: z.ZodLiteral<1>;
12
+ shopId: z.ZodString;
13
+ shopifyCustomerId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
14
+ deliveryId: z.ZodString;
15
+ triggeredAt: z.ZodISODateTime;
16
+ }, z.core.$strict>;
17
+ type ShopifyWhatsAppConsentSyncJobV1 = z.infer<typeof ShopifyWhatsAppConsentSyncJobV1Schema>;
18
+ declare function parseShopifyWhatsAppConsentSyncJobV1(input: unknown): ShopifyWhatsAppConsentSyncJobV1;
19
+ declare function safeParseShopifyWhatsAppConsentSyncJobV1(input: unknown): z.ZodSafeParseResult<{
20
+ schemaVersion: 1;
21
+ shopId: string;
22
+ shopifyCustomerId: string;
23
+ deliveryId: string;
24
+ triggeredAt: string;
25
+ }>;
26
+
27
+ export { SHOPIFY_WHATSAPP_CONSENT_SYNC_SCHEMA_VERSION as S, SHOPIFY_WHATSAPP_CONSENT_SYNC_TOPIC as a, type ShopifyWhatsAppConsentSyncJobV1 as b, ShopifyWhatsAppConsentSyncJobV1Schema as c, parseShopifyWhatsAppConsentSyncJobV1 as p, safeParseShopifyWhatsAppConsentSyncJobV1 as s };
@@ -0,0 +1,48 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "description": "Portable exact-recipient and Shopify Customer ID golden vectors. Illustrative values for pure validation tests only; never send or call them. Equivalence does not imply consent, reachability or ownership.",
4
+ "phoneCases": [
5
+ { "id": "gb-national", "input": "07400 123456", "verifiedCountryCode": "GB", "e164": "+447400123456" },
6
+ { "id": "gb-international", "input": "+44 7400 123456", "verifiedCountryCode": null, "e164": "+447400123456" },
7
+ { "id": "gb-compact-international", "input": "+447400123456", "verifiedCountryCode": "GB", "e164": "+447400123456" },
8
+ { "id": "gb-reserved-fictional", "input": "+44 7700 900123", "verifiedCountryCode": null, "reject": "INVALID_NUMBER" },
9
+ { "id": "us-national", "input": "(213) 373-4253", "verifiedCountryCode": "US", "e164": "+12133734253" },
10
+ { "id": "us-international", "input": "+1 213 373 4253", "verifiedCountryCode": null, "e164": "+12133734253" },
11
+ { "id": "fr-national", "input": "06 12 34 56 78", "verifiedCountryCode": "FR", "e164": "+33612345678" },
12
+ { "id": "fr-international", "input": "+33 6 12 34 56 78", "verifiedCountryCode": null, "e164": "+33612345678" },
13
+ { "id": "national-country-unknown", "input": "07400 123456", "verifiedCountryCode": null, "reject": "COUNTRY_REQUIRED" },
14
+ { "id": "unverified-country-code", "input": "07400 123456", "verifiedCountryCode": "ZZ", "reject": "UNSUPPORTED_COUNTRY" },
15
+ { "id": "foreign-context", "input": "+44 7400 123456", "verifiedCountryCode": "US", "reject": "COUNTRY_MISMATCH" },
16
+ { "id": "extension", "input": "07400 123456 ext. 42", "verifiedCountryCode": "GB", "reject": "EXTENSION_UNSUPPORTED" },
17
+ { "id": "extension-hash", "input": "+447400123456#42", "verifiedCountryCode": null, "reject": "EXTENSION_UNSUPPORTED" },
18
+ { "id": "ambiguous-short-number", "input": "900123", "verifiedCountryCode": "GB", "reject": "INVALID_NUMBER" },
19
+ { "id": "invalid-international-country", "input": "+999123456789", "verifiedCountryCode": null, "reject": "INVALID_NUMBER" },
20
+ { "id": "embedded-free-text", "input": "Call +447400123456", "verifiedCountryCode": null, "reject": "INVALID_FORMAT" }
21
+ ],
22
+ "shopifyCustomerCases": [
23
+ { "id": "numeric-webhook-number", "input": 123456789, "numericId": "123456789", "gid": "gid://shopify/Customer/123456789" },
24
+ { "id": "numeric-webhook-string", "input": "123456789", "numericId": "123456789", "gid": "gid://shopify/Customer/123456789" },
25
+ { "id": "graphql-customer-gid", "input": "gid://shopify/Customer/123456789", "numericId": "123456789", "gid": "gid://shopify/Customer/123456789" },
26
+ { "id": "64bit-boundary-string", "input": "9223372036854775807", "numericId": "9223372036854775807", "gid": "gid://shopify/Customer/9223372036854775807" }
27
+ ],
28
+ "rejectedShopifyCustomerCases": [
29
+ { "id": "wrong-resource", "input": "gid://shopify/Order/123", "code": "INVALID_CUSTOMER_ID" },
30
+ { "id": "missing-resource-id", "input": "gid://shopify/Customer/", "code": "INVALID_CUSTOMER_ID" },
31
+ { "id": "zero", "input": "0", "code": "INVALID_CUSTOMER_ID" },
32
+ { "id": "leading-zeroes", "input": "000123", "code": "INVALID_CUSTOMER_ID" },
33
+ { "id": "query-suffix", "input": "gid://shopify/Customer/123?shop=other", "code": "INVALID_CUSTOMER_ID" },
34
+ { "id": "principal-string", "input": "shop_user_123", "code": "INVALID_CUSTOMER_ID" },
35
+ { "id": "negative", "input": -1, "code": "INVALID_CUSTOMER_ID" },
36
+ { "id": "unsafe-js-number", "input": 9007199254740992, "code": "INVALID_CUSTOMER_ID" },
37
+ { "id": "overflow", "input": "9223372036854775808", "code": "CUSTOMER_ID_OUT_OF_RANGE" }
38
+ ],
39
+ "shopifyScopeCases": [
40
+ { "id": "numeric-gid-same-shop", "shopA": "shop-a", "shopB": "shop-a", "customerA": "123456789", "customerB": "gid://shopify/Customer/123456789", "equal": true },
41
+ { "id": "same-id-different-shop", "shopA": "shop-a", "shopB": "shop-b", "customerA": "123456789", "customerB": "gid://shopify/Customer/123456789", "equal": false },
42
+ { "id": "different-customer", "shopA": "shop-a", "shopB": "shop-a", "customerA": "123456789", "customerB": "123456790", "equal": false }
43
+ ],
44
+ "recipientLifecycleCases": [
45
+ { "id": "number-rotated", "before": "+447400123456", "after": "+447400123457", "sameNumber": false, "grantCarriesAcrossPrincipal": false },
46
+ { "id": "reassigned-to-other-person", "before": "+447400123456", "after": "+447400123456", "sameNumber": true, "grantCarriesAcrossPrincipal": false }
47
+ ]
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modainteract/moda-interact-shared",
3
- "version": "2.4.3",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -40,6 +40,11 @@
40
40
  "types": "./dist/testing/node.d.ts",
41
41
  "import": "./dist/testing/node.js"
42
42
  },
43
+ "./identity": {
44
+ "types": "./dist/identity/index.d.ts",
45
+ "import": "./dist/identity/index.js"
46
+ },
47
+ "./identity/fixtures": "./fixtures/identity-v1.json",
43
48
  "./shopify": {
44
49
  "types": "./dist/shopify/index.d.ts",
45
50
  "import": "./dist/shopify/index.js"
@@ -106,7 +111,8 @@
106
111
  }
107
112
  },
108
113
  "files": [
109
- "dist"
114
+ "dist",
115
+ "fixtures/identity-v1.json"
110
116
  ],
111
117
  "scripts": {
112
118
  "build": "tsup",
@@ -142,6 +148,7 @@
142
148
  "@prisma/instrumentation": "^6.19.3",
143
149
  "bullmq-otel": "^2.0.1",
144
150
  "intl-messageformat": "^10.7.18",
151
+ "libphonenumber-js": "1.12.30",
145
152
  "semver": "7.8.5",
146
153
  "winston": "3.19.0",
147
154
  "winston-loki": "6.1.7",