@modainteract/moda-interact-shared 2.4.3 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/chunk-G7FCWVBU.js +32 -0
- package/dist/chunk-G7FCWVBU.js.map +1 -0
- package/dist/{chunk-SU2DAOEC.js → chunk-NHIGHAEQ.js} +5 -1
- package/dist/{chunk-SU2DAOEC.js.map → chunk-NHIGHAEQ.js.map} +1 -1
- package/dist/chunk-QG53EVA6.js +56 -0
- package/dist/chunk-QG53EVA6.js.map +1 -0
- package/dist/identity/index.d.ts +52 -0
- package/dist/identity/index.js +61 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +14 -1
- package/dist/shopify/index.d.ts +5 -0
- package/dist/shopify/index.js +15 -2
- package/dist/shopify/node.d.ts +10 -1
- package/dist/shopify/node.js +12 -1
- package/dist/shopify/node.js.map +1 -1
- package/dist/whatsapp-consent-sync.schema-DEU84xTL.d.ts +27 -0
- package/dist/whatsapp-template-events.d.ts +29 -0
- package/dist/whatsapp-template-events.js +93 -0
- package/dist/whatsapp-template-events.js.map +1 -0
- package/fixtures/identity-v1.json +48 -0
- package/fixtures/meta-template-status-v2.json +91 -0
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@ necessarily a supported package subpath. Type-only exports require `import type`
|
|
|
31
31
|
| `/billing` | Billing plan/usage contracts, provider-status normalization, subscription-reconciliation jobs, purchased-credit counters and deterministic billing identifiers. |
|
|
32
32
|
| `/recovery-policy` | Recovery offer modes and validation of an already-resolved effective recovery policy. Does not retrieve or decide merchant entitlements. |
|
|
33
33
|
| `/whatsapp` | Normalized inbound WhatsApp content/message schemas and parsers. Provider delivery statuses are in `/billing`. |
|
|
34
|
+
| `/whatsapp-template-events` | ARCH-047 v2 Meta template-status observations: strict nine-field schema and pure occurrence UUID v4 event/job ID formatters. |
|
|
35
|
+
| `/whatsapp-template-events/fixtures` | Portable JSON golden vectors for v2 status observations and expected BullMQ job IDs. |
|
|
34
36
|
| `/merchant-communications` | Merchant communication and translation job contracts, constants and validation. |
|
|
35
37
|
| `/merchant-communications/node` | Node-only deterministic translation queue-job identifiers. |
|
|
36
38
|
| `/shopify` | Shopify commerce/recovery event schemas, parsers, event versions and shared queue contracts. |
|
|
@@ -1659,6 +1661,40 @@ Type-only exports:
|
|
|
1659
1661
|
|
|
1660
1662
|
</details>
|
|
1661
1663
|
|
|
1664
|
+
<details>
|
|
1665
|
+
<summary>/whatsapp-template-events — 6 runtime exports, 1 type</summary>
|
|
1666
|
+
|
|
1667
|
+
Import: `@modainteract/moda-interact-shared/whatsapp-template-events`.
|
|
1668
|
+
|
|
1669
|
+
From [src/whatsapp-template-events.ts](src/whatsapp-template-events.ts):
|
|
1670
|
+
|
|
1671
|
+
Runtime exports:
|
|
1672
|
+
|
|
1673
|
+
- `META_TEMPLATE_STATUS_QUEUE`
|
|
1674
|
+
- `META_TEMPLATE_STATUS_JOB`
|
|
1675
|
+
- `META_TEMPLATE_STATUS_SCHEMA_VERSION`
|
|
1676
|
+
- `MetaTemplateStatusObservationV2Schema`
|
|
1677
|
+
- `formatMetaTemplateStatusEventIdentity`
|
|
1678
|
+
- `formatMetaTemplateStatusJobId`
|
|
1679
|
+
|
|
1680
|
+
Type-only export:
|
|
1681
|
+
|
|
1682
|
+
- `MetaTemplateStatusObservationV2`
|
|
1683
|
+
|
|
1684
|
+
Messaging must verify the complete webhook batch **before** generating a fresh
|
|
1685
|
+
cryptographically random UUID v4 for every matching observation. Shared formats
|
|
1686
|
+
that UUID without generating randomness. Both ingress and Background must parse
|
|
1687
|
+
events with `MetaTemplateStatusObservationV2Schema`; it checks the exact nine own
|
|
1688
|
+
fields and the 4096-byte UTF-8 JSON limit. Neither event UUID nor `receivedAt`
|
|
1689
|
+
orders provider statuses, which remain Background-owned policy. Repeated Meta
|
|
1690
|
+
status values can correspond to separate event occurrences.
|
|
1691
|
+
|
|
1692
|
+
Compatibility vectors: [fixtures/meta-template-status-v2.json](fixtures/meta-template-status-v2.json),
|
|
1693
|
+
published at `/whatsapp-template-events/fixtures`. The fixture is test data,
|
|
1694
|
+
not a provider webhook payload.
|
|
1695
|
+
|
|
1696
|
+
</details>
|
|
1697
|
+
|
|
1662
1698
|
<details>
|
|
1663
1699
|
<summary>/merchant-communications — 40 exports</summary>
|
|
1664
1700
|
|
|
@@ -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-
|
|
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":";;;;;AAAO,IAAM,2CAA2C;AACjD,IAAM,2CAA2C;AACjD,IAAM,2CAA2C;AAGjD,IAAM,wCACX;AAEK,IAAM,+BAA+B;AAAA,EAC1C,mBAAmB;AAAA,EACnB,iBAAiB;AACnB;AAEO,IAAM,kCAAkC;AAAA,EAC7C,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAiB;AACnB;AAEO,IAAM,kCAAkC;AAAA,EAC7C,kBAAkB;AACpB;AAEO,IAAM,sCAAsC;AAAA,EACjD,iBAAiB;AAAA,EACjB,cAAc;AAChB;;;AC3BA,SAAS,SAAS;AAMlB,IAAM,gBAAgB;AACtB,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AACzB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAEvB,IAAM,uBAAuB;AAG7B,IAAM,sBAAsB,EACzB,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,MAAM,sBAAsB,wCAA0C;AAElE,IAAM,sBAAsB,EAChC,OAAO;AAAA,EACN,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa;AAAA,EAC3C,YAAY,EACT,OAAO,EACP,IAAI,CAAC,EACL,IAAI,qBAAqB,EACzB,YAAY,EACZ,KAAK;AACV,CAAC,EACA,OAAO;AAIH,IAAM,iCAAiC,EAC3C,OAAO;AAAA,EACN,mBAAmB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa,EAAE,SAAS;AAAA,EACjE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,qBAAqB,EAAE,SAAS;AAAA,EAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,EAC1C,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS;AAAA,EAC5D,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS;AAC7D,CAAC,EACA,OAAO;AAIH,IAAM,qBAAqB,EAC/B,OAAO;AAAA,EACN,QAAQ;AAAA,EACR,cAAc,EACX,OAAO,EACP,MAAM,cAAc,iDAAiD;AAC1E,CAAC,EACA,OAAO;AAQH,IAAM,gCAAgC,EAC1C,OAAO;AAAA,EACN,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa,EAAE,SAAS;AAAA,EAC1D,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa,EAAE,SAAS;AAAA,EACzD,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa,EAAE,SAAS;AAAA,EACzD,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB;AAAA,EAC7C,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS;AAAA,EAC/D,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACpC,WAAW,oBAAoB,SAAS;AAAA,EACxC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa,EAAE,SAAS;AACrD,CAAC,EACA,OAAO;AAIH,IAAM,iCAAiC,EAC3C,MAAM,6BAA6B,EACnC,IAAI,cAAc;;;AC/ErB,SAAS,KAAAA,UAAS;AAQX,IAAM,gCAAgCC,GAC1C,OAAO;AAAA,EACN,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACtC,aAAaA,GAAE,IAAI,EAAE,IAAI,cAAc,EAAE,SAAS;AAAA,EAClD,UAAU,+BAA+B,SAAS;AAAA,EAClD,OAAO,mBAAmB,SAAS;AAAA,EACnC,WAAW;AAAA,EACX,mBAAmBA,GAAE,IAAI,SAAS,EAAE,SAAS;AAAA,EAC7C,mBAAmBA,GAAE,IAAI,SAAS,EAAE,SAAS;AAAA,EAC7C,aAAaA,GAAE,IAAI,SAAS,EAAE,SAAS;AACzC,CAAC,EACA,OAAO;;;ACpBV,SAAS,KAAAC,UAAS;AAGX,IAAM,8BAA8BC,GACxC,OAAO;AAAA,EACN,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC1C,mBAAmBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC9C,OAAO,mBAAmB,SAAS;AAAA,EACnC,aAAaA,GAAE,IAAI,SAAS;AAC9B,CAAC,EACA,OAAO;;;ACXV,SAAS,KAAAC,UAAS;AASlB,IAAM,iCAAiCC,GAAE,OAAO;AAAA,EAC9C,eAAeA,GAAE,QAAQ,qCAAqC;AAAA,EAC9D,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,QAAQA,GAAE,QAAQ,SAAS;AAAA,EAC3B,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,QAAQ;AAAA,EACR,YAAYA,GAAE,IAAI,SAAS,EAAE,SAAS;AAAA,EACtC,YAAYA,GAAE,IAAI,SAAS;AAAA,EAC3B,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAC/B,CAAC;AAEM,IAAM,qCAAqC,+BAA+B,OAAO;AAAA,EACtF,WAAWA,GAAE,QAAQ,6BAA6B,iBAAiB;AAAA,EACnE,SAAS;AACX,CAAC,EAAE,OAAO;AAMH,IAAM,mCAAmC,+BAA+B,OAAO;AAAA,EACpF,WAAWA,GAAE,QAAQ,6BAA6B,eAAe;AAAA,EACjE,SAAS;AACX,CAAC,EAAE,OAAO;AAMH,IAAM,6BAA6BA,GAAE,mBAAmB,aAAa;AAAA,EAC1E;AAAA,EACA;AACF,CAAC;AAKM,SAAS,0BAA0B,OAAsC;AAC9E,SAAO,2BAA2B,MAAM,KAAK;AAC/C;AAEO,SAAS,8BAA8B,OAAgB;AAC5D,SAAO,2BAA2B,UAAU,KAAK;AACnD;AAEO,SAAS,wBACd,OACuC;AACvC,SAAO,MAAM,cAAc,6BAA6B;AAC1D;AAEO,SAAS,sBACd,OACqC;AACrC,SAAO,MAAM,cAAc,6BAA6B;AAC1D;AAOO,SAAS,iCACd,QACA,eACQ;AACR,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,kBAAkB,YAAY,cAAc,WAAW,GAAG;AACnE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,IAAI,aAAa;AACnC;AAEO,SAAS,8BACd,QACA,SACQ;AACR,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,IAAI,OAAO;AAC7B;;;AC9GA,SAAS,KAAAC,UAAS;AAGX,IAAM,iCAAiCC,GAC3C,OAAO;AAAA,EACN,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACtC,sBAAsBA,GAAE,IAAI,EAAE,IAAI,cAAc,EAAE,SAAS;AAAA,EAC3D,mBAAmBA,GAAE,IAAI,SAAS,EAAE,SAAS;AAC/C,CAAC,EACA,OAAO;;;ACVV,SAAS,KAAAC,UAAS;AAEX,IAAM,iCAAiCA,GAC3C,OAAO;AAAA,EACN,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AACjC,CAAC,EACA,OAAO;;;ACNV,SAAS,KAAAC,UAAS;AAEX,IAAM,qCAAqCA,GAC/C,OAAO;AAAA,EACN,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,SAASA,GAAE,QAAQ,EAAE,SAAS;AAChC,CAAC,EACA,OAAO;;;ACPV,SAAS,KAAAC,UAAS;AAEX,IAAM,gCAAgCA,GAC1C,OAAO;AAAA,EACN,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC1C,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACtC,aAAaA,GAAE,IAAI,SAAS;AAC9B,CAAC,EACA,OAAO;;;ACTV,SAAS,KAAAC,UAAS;AAYlB,IAAM,mCAAmCC,GACtC,OAAO;AAAA,EACN,eAAeA,GAAE,QAAQ,wCAAwC;AAAA,EACjE,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,QAAQA,GAAE,QAAQ,SAAS;AAAA,EAC3B,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,QAAQ;AAAA,EACR,YAAYA,GAAE,IAAI,SAAS,EAAE,SAAS;AAAA,EACtC,YAAYA,GAAE,IAAI,SAAS;AAAA,EAC3B,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,sBAAsB,2BAA2B,SAAS;AAC5D,CAAC,EACA,OAAO;AAEH,IAAM,sCACX,iCAAiC,OAAO;AAAA,EACtC,WAAWA,GAAE,QAAQ,gCAAgC,gBAAgB;AAAA,EACrE,SAAS;AACX,CAAC,EAAE,OAAO;AAML,IAAM,mCACX,iCAAiC,OAAO;AAAA,EACtC,WAAWA,GAAE,QAAQ,gCAAgC,aAAa;AAAA,EAClE,SAAS;AACX,CAAC,EAAE,OAAO;AAML,IAAM,sCACX,iCAAiC,OAAO;AAAA,EACtC,WAAWA,GAAE,QAAQ,gCAAgC,gBAAgB;AAAA,EACrE,SAAS;AACX,CAAC,EAAE,OAAO;AAML,IAAM,qCACX,iCAAiC,OAAO;AAAA,EACtC,WAAWA,GAAE,QAAQ,gCAAgC,eAAe;AAAA,EACpE,SAAS;AACX,CAAC,EAAE,OAAO;AAML,IAAM,+BAA+BA,GAAE,mBAAmB,aAAa;AAAA,EAC5E;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKM,SAAS,4BAA4B,OAAwC;AAClF,SAAO,6BAA6B,MAAM,KAAK;AACjD;AAEO,SAAS,gCAAgC,OAAgB;AAC9D,SAAO,6BAA6B,UAAU,KAAK;AACrD;AAEO,SAAS,yBACd,OACwC;AACxC,SAAO,MAAM,cAAc,gCAAgC;AAC7D;AAEO,SAAS,sBACd,OACqC;AACrC,SAAO,MAAM,cAAc,gCAAgC;AAC7D;AAEO,SAAS,yBACd,OACwC;AACxC,SAAO,MAAM,cAAc,gCAAgC;AAC7D;AAEO,SAAS,wBACd,OACuC;AACvC,SAAO,MAAM,cAAc,gCAAgC;AAC7D;AAEO,SAAS,wCACd,QACA,eACQ;AACR,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,kBAAkB,YAAY,cAAc,WAAW,GAAG;AACnE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,IAAI,aAAa;AACnC;AAEO,SAAS,qCACd,QACA,WACQ;AACR,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG;AAC3D,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,OAAO,MAAM,IAAI,MAAM,IAAI,UAAU,MAAM,IAAI,SAAS;AACzE;AAEO,SAAS,yCAAyC,OAI9C;AACT,MAAI,MAAM,eAAe;AACvB,WAAO,wCAAwC,MAAM,QAAQ,MAAM,aAAa;AAAA,EAClF;AAEA,MAAI,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,WAAW,GAAG;AACnE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,MAAM,WAAW,YAAY,MAAM,OAAO,WAAW,GAAG;AACjE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,MAAM,IAAI,MAAM,OAAO;AACzC;;;AC3KA,SAAS,KAAAC,WAAS;AAGlB,IAAM,wBAAwB;AAEvB,IAAM,iCAAiCC,IAC3C,OAAO;AAAA,EACN,eAAeA,IAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,WAAWA,IAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,qBAAqB,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5E,mBAAmBA,IAAE,IAAI,SAAS,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,sBAAsBA,IAAE,IAAI,EAAE,IAAI,cAAc,EAAE,SAAS,EAAE,SAAS;AACxE,CAAC,EACA,OAAO;;;ACZV,SAAS,KAAAC,WAAS;AASlB,IAAM,mCAAmCC,IACtC,OAAO;AAAA,EACN,eAAeA,IAAE,QAAQ,wCAAwC;AAAA,EACjE,WAAWA,IAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,YAAYA,IAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,SAASA,IAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,QAAQA,IAAE,QAAQ,SAAS;AAAA,EAC3B,eAAeA,IAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,QAAQ;AAAA,EACR,YAAYA,IAAE,IAAI,SAAS,EAAE,SAAS;AAAA,EACtC,YAAYA,IAAE,IAAI,SAAS;AAAA,EAC3B,SAASA,IAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,IAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,sBAAsB,2BAA2B,SAAS;AAC5D,CAAC,EACA,OAAO;AAEH,IAAM,sCACX,iCAAiC,OAAO;AAAA,EACtC,WAAWA,IAAE,QAAQ,gCAAgC,gBAAgB;AAAA,EACrE,SAAS;AACX,CAAC,EAAE,OAAO;;;AC9BZ,SAAS,KAAAC,WAAS;AAUX,IAAM,6BAA6BC,IAAE,MAAM;AAAA,EAChD;AAAA,EACA;AACF,CAAC;AAMM,SAAS,0BAA0B,OAAsC;AAC9E,SAAO,2BAA2B,MAAM,KAAK;AAC/C;AAEO,SAAS,8BAA8B,OAAgB;AAC5D,SAAO,2BAA2B,UAAU,KAAK;AACnD;;;ACzBA,SAAS,KAAAC,WAAS;AAEX,IAAM,kCAAkC;AAAA,EAC7C,iBAAiB;AAAA,IACf,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA,yBAAyB;AAAA,IACvB,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA,sBAAsB;AAAA,IACpB,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA,cAAc;AAAA,IACZ,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA,uBAAuB;AAAA,IACrB,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA,uBAAuB;AAAA,IACrB,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AACF;AAEO,IAAM,gCAAgC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uCAAuC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,kCAAkCA,IAAE,KAAK,6BAA6B;AAC5E,IAAM,wCAAwCA,IAAE,KAAK,oCAAoC;AAEzF,IAAM,+BAA+BA,IACzC,OAAO;AAAA,EACN,eAAeA,IAAE,QAAQ,CAAC;AAAA,EAC1B,QAAQA,IAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACxC,YAAYA,IAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC5C,QAAQ;AAAA,EACR,aAAaA,IAAE,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC;AAAA,EAC5C,YAAYA,IAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC9C,cAAc,sCAAsC,SAAS;AAC/D,CAAC,EACA,OAAO,EACP,YAAY,CAAC,OAAO,QAAQ;AAC3B,MAAI,MAAM,WAAW,oBAAoB;AACvC,QAAI,CAAC,MAAM,cAAc,MAAM,WAAW,KAAK,EAAE,WAAW,GAAG;AAC7D,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,YAAY;AAAA,QACnB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,QAAI,MAAM,iBAAiB,MAAM;AAC/B,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,cAAc;AAAA,QACrB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF,OAAO;AACL,QAAI,MAAM,iBAAiB,MAAM;AAC/B,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,cAAc;AAAA,QACrB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAII,SAAS,4BAA4B,OAAwC;AAClF,SAAO,6BAA6B,MAAM,KAAK;AACjD;AAEO,SAAS,gCAAgC,OAAgB;AAC9D,SAAO,6BAA6B,UAAU,KAAK;AACrD;","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-
|
|
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
|
package/dist/shopify/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/shopify/index.js
CHANGED
|
@@ -56,7 +56,15 @@ import {
|
|
|
56
56
|
safeParseShopifyDiscountSyncJob,
|
|
57
57
|
safeParseShopifyRecoveryEvent,
|
|
58
58
|
safeParseShopifyRecoveryEventV2
|
|
59
|
-
} from "../chunk-
|
|
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
|
package/dist/shopify/node.d.ts
CHANGED
|
@@ -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 };
|
package/dist/shopify/node.js
CHANGED
|
@@ -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
|
package/dist/shopify/node.js.map
CHANGED
|
@@ -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":"
|
|
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,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/** The existing WhatsApp event queue; this observation is a distinct job type. */
|
|
4
|
+
declare const META_TEMPLATE_STATUS_QUEUE: "whatsapp-events";
|
|
5
|
+
declare const META_TEMPLATE_STATUS_JOB: "meta-template-status-update";
|
|
6
|
+
declare const META_TEMPLATE_STATUS_SCHEMA_VERSION: 2;
|
|
7
|
+
/**
|
|
8
|
+
* Consumer and producer boundary schema. Preflight checks the *actual* UTF-8
|
|
9
|
+
* JSON encoding and exact own-key set before the typed, non-coercing Zod parse.
|
|
10
|
+
* It does not log rejected payloads or parse Meta business status semantics.
|
|
11
|
+
*/
|
|
12
|
+
declare const MetaTemplateStatusObservationV2Schema: z.ZodPipe<z.ZodUnknown, z.ZodObject<{
|
|
13
|
+
schemaVersion: z.ZodLiteral<2>;
|
|
14
|
+
eventIdentity: z.ZodString;
|
|
15
|
+
wabaId: z.ZodString;
|
|
16
|
+
providerTemplateId: z.ZodNullable<z.ZodString>;
|
|
17
|
+
providerTemplateName: z.ZodString;
|
|
18
|
+
providerLanguageCode: z.ZodString;
|
|
19
|
+
providerStatus: z.ZodString;
|
|
20
|
+
reasonCode: z.ZodNullable<z.ZodString>;
|
|
21
|
+
receivedAt: z.ZodString;
|
|
22
|
+
}, z.core.$strict>>;
|
|
23
|
+
type MetaTemplateStatusObservationV2 = z.infer<typeof MetaTemplateStatusObservationV2Schema>;
|
|
24
|
+
/** Identity of one authenticated observation; never a content fingerprint. */
|
|
25
|
+
declare function formatMetaTemplateStatusEventIdentity(observationId: string): string;
|
|
26
|
+
/** BullMQ-compatible job ID: the same UUID, without ':' separators. */
|
|
27
|
+
declare function formatMetaTemplateStatusJobId(observationId: string): string;
|
|
28
|
+
|
|
29
|
+
export { META_TEMPLATE_STATUS_JOB, META_TEMPLATE_STATUS_QUEUE, META_TEMPLATE_STATUS_SCHEMA_VERSION, type MetaTemplateStatusObservationV2, MetaTemplateStatusObservationV2Schema, formatMetaTemplateStatusEventIdentity, formatMetaTemplateStatusJobId };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/whatsapp-template-events.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var META_TEMPLATE_STATUS_QUEUE = "whatsapp-events";
|
|
4
|
+
var META_TEMPLATE_STATUS_JOB = "meta-template-status-update";
|
|
5
|
+
var META_TEMPLATE_STATUS_SCHEMA_VERSION = 2;
|
|
6
|
+
var MAX_JSON_BYTES = 4096;
|
|
7
|
+
var REQUIRED_JSON_KEYS = [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"eventIdentity",
|
|
10
|
+
"wabaId",
|
|
11
|
+
"providerTemplateId",
|
|
12
|
+
"providerTemplateName",
|
|
13
|
+
"providerLanguageCode",
|
|
14
|
+
"providerStatus",
|
|
15
|
+
"reasonCode",
|
|
16
|
+
"receivedAt"
|
|
17
|
+
];
|
|
18
|
+
var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
19
|
+
var EVENT_IDENTITY_PREFIX = "meta-template-status:v2:";
|
|
20
|
+
var JOB_ID_PREFIX = "meta-template-status-v2-";
|
|
21
|
+
var EVENT_IDENTITY = /^meta-template-status:v2:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
22
|
+
var DECIMAL_PROVIDER_ID = /^[0-9]{1,32}$/;
|
|
23
|
+
var CANONICAL_UTC_RECEIPT = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}Z$/;
|
|
24
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
|
|
25
|
+
function providerText(maxLength) {
|
|
26
|
+
return z.string().min(1).max(maxLength).refine(
|
|
27
|
+
(value) => value.trim().length > 0 && !CONTROL_CHARACTERS.test(value),
|
|
28
|
+
{ message: "Invalid Meta provider text" }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
var canonicalReceivedAt = z.string().regex(CANONICAL_UTC_RECEIPT).refine(
|
|
32
|
+
(value) => {
|
|
33
|
+
const date = new Date(value);
|
|
34
|
+
return !Number.isNaN(date.getTime()) && date.toISOString() === value;
|
|
35
|
+
},
|
|
36
|
+
{ message: "receivedAt must be a valid canonical UTC millisecond timestamp" }
|
|
37
|
+
);
|
|
38
|
+
var observationFields = z.object({
|
|
39
|
+
schemaVersion: z.literal(META_TEMPLATE_STATUS_SCHEMA_VERSION),
|
|
40
|
+
eventIdentity: z.string().regex(EVENT_IDENTITY),
|
|
41
|
+
wabaId: z.string().regex(DECIMAL_PROVIDER_ID),
|
|
42
|
+
providerTemplateId: z.string().regex(DECIMAL_PROVIDER_ID).nullable(),
|
|
43
|
+
providerTemplateName: providerText(255),
|
|
44
|
+
providerLanguageCode: providerText(16),
|
|
45
|
+
// Unknown but well-formed provider statuses remain text. The Background
|
|
46
|
+
// consumer, not this transport contract, interprets provider state.
|
|
47
|
+
providerStatus: providerText(32),
|
|
48
|
+
reasonCode: providerText(128).nullable(),
|
|
49
|
+
// Receipt time is diagnostic, never a provider-status ordering authority.
|
|
50
|
+
receivedAt: canonicalReceivedAt
|
|
51
|
+
}).strict();
|
|
52
|
+
var MetaTemplateStatusObservationV2Schema = z.unknown().superRefine(
|
|
53
|
+
(input, ctx) => {
|
|
54
|
+
let json;
|
|
55
|
+
try {
|
|
56
|
+
json = JSON.stringify(input);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
if (typeof json !== "string") {
|
|
60
|
+
ctx.addIssue({ code: "custom", message: "Meta template-status observation must be JSON-serializable" });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (new TextEncoder().encode(json).byteLength > MAX_JSON_BYTES) {
|
|
64
|
+
ctx.addIssue({ code: "custom", message: "Meta template-status observation exceeds 4096 UTF-8 bytes" });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (input === null || typeof input !== "object" || Array.isArray(input) || Object.keys(input).length !== REQUIRED_JSON_KEYS.length || !REQUIRED_JSON_KEYS.every((key) => Object.prototype.hasOwnProperty.call(input, key))) {
|
|
68
|
+
ctx.addIssue({ code: "custom", message: "Meta template-status observation requires exactly nine own JSON fields" });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
).pipe(observationFields);
|
|
72
|
+
function validateObservationId(observationId) {
|
|
73
|
+
if (typeof observationId !== "string" || !UUID_V4.test(observationId)) {
|
|
74
|
+
throw new TypeError("observationId must be a lowercase UUID v4");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function formatMetaTemplateStatusEventIdentity(observationId) {
|
|
78
|
+
validateObservationId(observationId);
|
|
79
|
+
return `${EVENT_IDENTITY_PREFIX}${observationId}`;
|
|
80
|
+
}
|
|
81
|
+
function formatMetaTemplateStatusJobId(observationId) {
|
|
82
|
+
validateObservationId(observationId);
|
|
83
|
+
return `${JOB_ID_PREFIX}${observationId}`;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
META_TEMPLATE_STATUS_JOB,
|
|
87
|
+
META_TEMPLATE_STATUS_QUEUE,
|
|
88
|
+
META_TEMPLATE_STATUS_SCHEMA_VERSION,
|
|
89
|
+
MetaTemplateStatusObservationV2Schema,
|
|
90
|
+
formatMetaTemplateStatusEventIdentity,
|
|
91
|
+
formatMetaTemplateStatusJobId
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=whatsapp-template-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/whatsapp-template-events.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/** The existing WhatsApp event queue; this observation is a distinct job type. */\nexport const META_TEMPLATE_STATUS_QUEUE = \"whatsapp-events\" as const;\nexport const META_TEMPLATE_STATUS_JOB = \"meta-template-status-update\" as const;\nexport const META_TEMPLATE_STATUS_SCHEMA_VERSION = 2 as const;\n\nconst MAX_JSON_BYTES = 4096;\nconst REQUIRED_JSON_KEYS = [\n \"schemaVersion\",\n \"eventIdentity\",\n \"wabaId\",\n \"providerTemplateId\",\n \"providerTemplateName\",\n \"providerLanguageCode\",\n \"providerStatus\",\n \"reasonCode\",\n \"receivedAt\",\n] as const;\n\n// Explicitly require a lowercase, RFC 4122 / RFC 9562 variant UUID *version 4*.\n// Meta does not supply an occurrence ID for this webhook: the authenticated\n// Messaging producer creates a fresh random UUID v4, not Shared.\nconst UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;\nconst EVENT_IDENTITY_PREFIX = \"meta-template-status:v2:\";\nconst JOB_ID_PREFIX = \"meta-template-status-v2-\";\nconst EVENT_IDENTITY = /^meta-template-status:v2:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;\nconst DECIMAL_PROVIDER_ID = /^[0-9]{1,32}$/;\nconst CANONICAL_UTC_RECEIPT = /^\\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01])T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d\\.\\d{3}Z$/;\nconst CONTROL_CHARACTERS = /[\\u0000-\\u001f\\u007f]/;\n\nfunction providerText(maxLength: number) {\n return z.string().min(1).max(maxLength).refine(\n (value) => value.trim().length > 0 && !CONTROL_CHARACTERS.test(value),\n { message: \"Invalid Meta provider text\" },\n );\n}\n\nconst canonicalReceivedAt = z.string().regex(CANONICAL_UTC_RECEIPT).refine(\n (value) => {\n const date = new Date(value);\n return !Number.isNaN(date.getTime()) && date.toISOString() === value;\n },\n { message: \"receivedAt must be a valid canonical UTC millisecond timestamp\" },\n);\n\nconst observationFields = z.object({\n schemaVersion: z.literal(META_TEMPLATE_STATUS_SCHEMA_VERSION),\n eventIdentity: z.string().regex(EVENT_IDENTITY),\n wabaId: z.string().regex(DECIMAL_PROVIDER_ID),\n providerTemplateId: z.string().regex(DECIMAL_PROVIDER_ID).nullable(),\n providerTemplateName: providerText(255),\n providerLanguageCode: providerText(16),\n // Unknown but well-formed provider statuses remain text. The Background\n // consumer, not this transport contract, interprets provider state.\n providerStatus: providerText(32),\n reasonCode: providerText(128).nullable(),\n // Receipt time is diagnostic, never a provider-status ordering authority.\n receivedAt: canonicalReceivedAt,\n}).strict();\n\n/**\n * Consumer and producer boundary schema. Preflight checks the *actual* UTF-8\n * JSON encoding and exact own-key set before the typed, non-coercing Zod parse.\n * It does not log rejected payloads or parse Meta business status semantics.\n */\nexport const MetaTemplateStatusObservationV2Schema = z.unknown().superRefine(\n (input, ctx) => {\n let json: string | undefined;\n try {\n json = JSON.stringify(input);\n } catch {\n // A circular / non-JSON payload is not a valid BullMQ wire observation.\n }\n if (typeof json !== \"string\") {\n ctx.addIssue({ code: \"custom\", message: \"Meta template-status observation must be JSON-serializable\" });\n return;\n }\n if (new TextEncoder().encode(json).byteLength > MAX_JSON_BYTES) {\n ctx.addIssue({ code: \"custom\", message: \"Meta template-status observation exceeds 4096 UTF-8 bytes\" });\n return;\n }\n if (input === null || typeof input !== \"object\" || Array.isArray(input)\n || Object.keys(input).length !== REQUIRED_JSON_KEYS.length\n || !REQUIRED_JSON_KEYS.every((key) => Object.prototype.hasOwnProperty.call(input, key))) {\n ctx.addIssue({ code: \"custom\", message: \"Meta template-status observation requires exactly nine own JSON fields\" });\n }\n },\n).pipe(observationFields);\n\nexport type MetaTemplateStatusObservationV2 = z.infer<typeof MetaTemplateStatusObservationV2Schema>;\n\nfunction validateObservationId(observationId: string): void {\n if (typeof observationId !== \"string\" || !UUID_V4.test(observationId)) {\n // Do not interpolate potentially untrusted identifiers into exceptions.\n throw new TypeError(\"observationId must be a lowercase UUID v4\");\n }\n}\n\n/** Identity of one authenticated observation; never a content fingerprint. */\nexport function formatMetaTemplateStatusEventIdentity(observationId: string): string {\n validateObservationId(observationId);\n return `${EVENT_IDENTITY_PREFIX}${observationId}`;\n}\n\n/** BullMQ-compatible job ID: the same UUID, without ':' separators. */\nexport function formatMetaTemplateStatusJobId(observationId: string): string {\n validateObservationId(observationId);\n return `${JOB_ID_PREFIX}${observationId}`;\n}\n"],"mappings":";AAAA,SAAS,SAAS;AAGX,IAAM,6BAA6B;AACnC,IAAM,2BAA2B;AACjC,IAAM,sCAAsC;AAEnD,IAAM,iBAAiB;AACvB,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,IAAM,UAAU;AAChB,IAAM,wBAAwB;AAC9B,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;AAC5B,IAAM,wBAAwB;AAC9B,IAAM,qBAAqB;AAE3B,SAAS,aAAa,WAAmB;AACvC,SAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,SAAS,EAAE;AAAA,IACtC,CAAC,UAAU,MAAM,KAAK,EAAE,SAAS,KAAK,CAAC,mBAAmB,KAAK,KAAK;AAAA,IACpE,EAAE,SAAS,6BAA6B;AAAA,EAC1C;AACF;AAEA,IAAM,sBAAsB,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE;AAAA,EAClE,CAAC,UAAU;AACT,UAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,WAAO,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,KAAK,KAAK,YAAY,MAAM;AAAA,EACjE;AAAA,EACA,EAAE,SAAS,iEAAiE;AAC9E;AAEA,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,eAAe,EAAE,QAAQ,mCAAmC;AAAA,EAC5D,eAAe,EAAE,OAAO,EAAE,MAAM,cAAc;AAAA,EAC9C,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB;AAAA,EAC5C,oBAAoB,EAAE,OAAO,EAAE,MAAM,mBAAmB,EAAE,SAAS;AAAA,EACnE,sBAAsB,aAAa,GAAG;AAAA,EACtC,sBAAsB,aAAa,EAAE;AAAA;AAAA;AAAA,EAGrC,gBAAgB,aAAa,EAAE;AAAA,EAC/B,YAAY,aAAa,GAAG,EAAE,SAAS;AAAA;AAAA,EAEvC,YAAY;AACd,CAAC,EAAE,OAAO;AAOH,IAAM,wCAAwC,EAAE,QAAQ,EAAE;AAAA,EAC/D,CAAC,OAAO,QAAQ;AACd,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,UAAU,KAAK;AAAA,IAC7B,QAAQ;AAAA,IAER;AACA,QAAI,OAAO,SAAS,UAAU;AAC5B,UAAI,SAAS,EAAE,MAAM,UAAU,SAAS,6DAA6D,CAAC;AACtG;AAAA,IACF;AACA,QAAI,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE,aAAa,gBAAgB;AAC9D,UAAI,SAAS,EAAE,MAAM,UAAU,SAAS,4DAA4D,CAAC;AACrG;AAAA,IACF;AACA,QAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,KACjE,OAAO,KAAK,KAAK,EAAE,WAAW,mBAAmB,UACjD,CAAC,mBAAmB,MAAM,CAAC,QAAQ,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG,CAAC,GAAG;AACzF,UAAI,SAAS,EAAE,MAAM,UAAU,SAAS,yEAAyE,CAAC;AAAA,IACpH;AAAA,EACF;AACF,EAAE,KAAK,iBAAiB;AAIxB,SAAS,sBAAsB,eAA6B;AAC1D,MAAI,OAAO,kBAAkB,YAAY,CAAC,QAAQ,KAAK,aAAa,GAAG;AAErE,UAAM,IAAI,UAAU,2CAA2C;AAAA,EACjE;AACF;AAGO,SAAS,sCAAsC,eAA+B;AACnF,wBAAsB,aAAa;AACnC,SAAO,GAAG,qBAAqB,GAAG,aAAa;AACjD;AAGO,SAAS,8BAA8B,eAA+B;AAC3E,wBAAsB,aAAa;AACnC,SAAO,GAAG,aAAa,GAAG,aAAa;AACzC;","names":[]}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"observation": {
|
|
3
|
+
"schemaVersion": 2,
|
|
4
|
+
"eventIdentity": "meta-template-status:v2:550e8400-e29b-41d4-a716-446655440000",
|
|
5
|
+
"wabaId": "123456789",
|
|
6
|
+
"providerTemplateId": "987654321",
|
|
7
|
+
"providerTemplateName": "checkout_recovery_initial_v2",
|
|
8
|
+
"providerLanguageCode": "en_GB",
|
|
9
|
+
"providerStatus": "APPROVED",
|
|
10
|
+
"reasonCode": "NONE",
|
|
11
|
+
"receivedAt": "2026-10-25T19:00:00.000Z"
|
|
12
|
+
},
|
|
13
|
+
"expectedJobId": "meta-template-status-v2-550e8400-e29b-41d4-a716-446655440000",
|
|
14
|
+
"additionalObservations": [
|
|
15
|
+
{
|
|
16
|
+
"case": "identical-content-other-occurrence",
|
|
17
|
+
"observation": {
|
|
18
|
+
"schemaVersion": 2,
|
|
19
|
+
"eventIdentity": "meta-template-status:v2:550e8400-e29b-41d4-a716-446655440001",
|
|
20
|
+
"wabaId": "123456789",
|
|
21
|
+
"providerTemplateId": "987654321",
|
|
22
|
+
"providerTemplateName": "checkout_recovery_initial_v2",
|
|
23
|
+
"providerLanguageCode": "en_GB",
|
|
24
|
+
"providerStatus": "APPROVED",
|
|
25
|
+
"reasonCode": "NONE",
|
|
26
|
+
"receivedAt": "2026-10-25T19:00:00.000Z"
|
|
27
|
+
},
|
|
28
|
+
"expectedJobId": "meta-template-status-v2-550e8400-e29b-41d4-a716-446655440001"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"case": "transition-paused",
|
|
32
|
+
"observation": {
|
|
33
|
+
"schemaVersion": 2,
|
|
34
|
+
"eventIdentity": "meta-template-status:v2:550e8400-e29b-41d4-a716-446655440002",
|
|
35
|
+
"wabaId": "123456789",
|
|
36
|
+
"providerTemplateId": "987654321",
|
|
37
|
+
"providerTemplateName": "checkout_recovery_initial_v2",
|
|
38
|
+
"providerLanguageCode": "en_GB",
|
|
39
|
+
"providerStatus": "PAUSED",
|
|
40
|
+
"reasonCode": "NONE",
|
|
41
|
+
"receivedAt": "2026-10-25T19:01:00.000Z"
|
|
42
|
+
},
|
|
43
|
+
"expectedJobId": "meta-template-status-v2-550e8400-e29b-41d4-a716-446655440002"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"case": "transition-approved-again",
|
|
47
|
+
"observation": {
|
|
48
|
+
"schemaVersion": 2,
|
|
49
|
+
"eventIdentity": "meta-template-status:v2:550e8400-e29b-41d4-a716-446655440003",
|
|
50
|
+
"wabaId": "123456789",
|
|
51
|
+
"providerTemplateId": "987654321",
|
|
52
|
+
"providerTemplateName": "checkout_recovery_initial_v2",
|
|
53
|
+
"providerLanguageCode": "en_GB",
|
|
54
|
+
"providerStatus": "APPROVED",
|
|
55
|
+
"reasonCode": "NONE",
|
|
56
|
+
"receivedAt": "2026-10-25T19:02:00.000Z"
|
|
57
|
+
},
|
|
58
|
+
"expectedJobId": "meta-template-status-v2-550e8400-e29b-41d4-a716-446655440003"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"case": "identical-delivery-first",
|
|
62
|
+
"observation": {
|
|
63
|
+
"schemaVersion": 2,
|
|
64
|
+
"eventIdentity": "meta-template-status:v2:550e8400-e29b-41d4-a716-446655440004",
|
|
65
|
+
"wabaId": "123456789",
|
|
66
|
+
"providerTemplateId": "987654321",
|
|
67
|
+
"providerTemplateName": "checkout_recovery_initial_v2",
|
|
68
|
+
"providerLanguageCode": "en_GB",
|
|
69
|
+
"providerStatus": "APPROVED",
|
|
70
|
+
"reasonCode": "NONE",
|
|
71
|
+
"receivedAt": "2026-10-25T19:00:00.000Z"
|
|
72
|
+
},
|
|
73
|
+
"expectedJobId": "meta-template-status-v2-550e8400-e29b-41d4-a716-446655440004"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"case": "identical-delivery-second",
|
|
77
|
+
"observation": {
|
|
78
|
+
"schemaVersion": 2,
|
|
79
|
+
"eventIdentity": "meta-template-status:v2:550e8400-e29b-41d4-a716-446655440005",
|
|
80
|
+
"wabaId": "123456789",
|
|
81
|
+
"providerTemplateId": "987654321",
|
|
82
|
+
"providerTemplateName": "checkout_recovery_initial_v2",
|
|
83
|
+
"providerLanguageCode": "en_GB",
|
|
84
|
+
"providerStatus": "APPROVED",
|
|
85
|
+
"reasonCode": "NONE",
|
|
86
|
+
"receivedAt": "2026-10-25T19:00:00.000Z"
|
|
87
|
+
},
|
|
88
|
+
"expectedJobId": "meta-template-status-v2-550e8400-e29b-41d4-a716-446655440005"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modainteract/moda-interact-shared",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
"types": "./dist/whatsapp.d.ts",
|
|
29
29
|
"import": "./dist/whatsapp.js"
|
|
30
30
|
},
|
|
31
|
+
"./whatsapp-template-events": {
|
|
32
|
+
"types": "./dist/whatsapp-template-events.d.ts",
|
|
33
|
+
"import": "./dist/whatsapp-template-events.js"
|
|
34
|
+
},
|
|
35
|
+
"./whatsapp-template-events/fixtures": "./fixtures/meta-template-status-v2.json",
|
|
31
36
|
"./merchant-communications": {
|
|
32
37
|
"types": "./dist/merchant-communications.d.ts",
|
|
33
38
|
"import": "./dist/merchant-communications.js"
|
|
@@ -40,6 +45,11 @@
|
|
|
40
45
|
"types": "./dist/testing/node.d.ts",
|
|
41
46
|
"import": "./dist/testing/node.js"
|
|
42
47
|
},
|
|
48
|
+
"./identity": {
|
|
49
|
+
"types": "./dist/identity/index.d.ts",
|
|
50
|
+
"import": "./dist/identity/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./identity/fixtures": "./fixtures/identity-v1.json",
|
|
43
53
|
"./shopify": {
|
|
44
54
|
"types": "./dist/shopify/index.d.ts",
|
|
45
55
|
"import": "./dist/shopify/index.js"
|
|
@@ -106,7 +116,9 @@
|
|
|
106
116
|
}
|
|
107
117
|
},
|
|
108
118
|
"files": [
|
|
109
|
-
"dist"
|
|
119
|
+
"dist",
|
|
120
|
+
"fixtures/identity-v1.json",
|
|
121
|
+
"fixtures/meta-template-status-v2.json"
|
|
110
122
|
],
|
|
111
123
|
"scripts": {
|
|
112
124
|
"build": "tsup",
|
|
@@ -123,7 +135,8 @@
|
|
|
123
135
|
"validate:commerce-entrypoints": "node scripts/validate-commerce-entrypoints.mjs",
|
|
124
136
|
"validate:arch023-shared-entrypoints": "npm run build && node scripts/validate-arch023-shared-entrypoints.mjs",
|
|
125
137
|
"validate:arch024-model-entrypoints": "npm run build && node scripts/validate-arch024-model-entrypoints.mjs",
|
|
126
|
-
"test:arch033-incidents:postgres": "node scripts/test-arch033-incidents-postgres.mjs"
|
|
138
|
+
"test:arch033-incidents:postgres": "node scripts/test-arch033-incidents-postgres.mjs",
|
|
139
|
+
"validate:arch047-whatsapp-template-events": "node scripts/validate-whatsapp-template-events-entrypoint.mjs"
|
|
127
140
|
},
|
|
128
141
|
"dependencies": {
|
|
129
142
|
"@opentelemetry/api": "^1.9.0",
|
|
@@ -142,6 +155,7 @@
|
|
|
142
155
|
"@prisma/instrumentation": "^6.19.3",
|
|
143
156
|
"bullmq-otel": "^2.0.1",
|
|
144
157
|
"intl-messageformat": "^10.7.18",
|
|
158
|
+
"libphonenumber-js": "1.12.30",
|
|
145
159
|
"semver": "7.8.5",
|
|
146
160
|
"winston": "3.19.0",
|
|
147
161
|
"winston-loki": "6.1.7",
|