@modainteract/moda-interact-shared 2.5.0 → 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 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,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,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.5.0",
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"
@@ -112,7 +117,8 @@
112
117
  },
113
118
  "files": [
114
119
  "dist",
115
- "fixtures/identity-v1.json"
120
+ "fixtures/identity-v1.json",
121
+ "fixtures/meta-template-status-v2.json"
116
122
  ],
117
123
  "scripts": {
118
124
  "build": "tsup",
@@ -129,7 +135,8 @@
129
135
  "validate:commerce-entrypoints": "node scripts/validate-commerce-entrypoints.mjs",
130
136
  "validate:arch023-shared-entrypoints": "npm run build && node scripts/validate-arch023-shared-entrypoints.mjs",
131
137
  "validate:arch024-model-entrypoints": "npm run build && node scripts/validate-arch024-model-entrypoints.mjs",
132
- "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"
133
140
  },
134
141
  "dependencies": {
135
142
  "@opentelemetry/api": "^1.9.0",