@aws-amplify/backend-notifications 0.0.0-test-20260708085627

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +235 -0
  2. package/lib/campaign-association-asset/index.js +48413 -0
  3. package/lib/constants.d.ts +122 -0
  4. package/lib/constants.js +125 -0
  5. package/lib/construct.d.ts +283 -0
  6. package/lib/construct.js +824 -0
  7. package/lib/factory.d.ts +53 -0
  8. package/lib/factory.js +158 -0
  9. package/lib/handler-asset/index.js +530 -0
  10. package/lib/index.d.ts +7 -0
  11. package/lib/index.js +5 -0
  12. package/lib/lambda/campaign-association/handler.d.ts +29 -0
  13. package/lib/lambda/campaign-association/handler.js +80 -0
  14. package/lib/lambda/campaign-association/integration.d.ts +59 -0
  15. package/lib/lambda/campaign-association/integration.js +108 -0
  16. package/lib/lambda/campaign-association/onboarding.d.ts +25 -0
  17. package/lib/lambda/campaign-association/onboarding.js +74 -0
  18. package/lib/lambda/identify/device_resolver.d.ts +18 -0
  19. package/lib/lambda/identify/device_resolver.js +56 -0
  20. package/lib/lambda/identify/handler.d.ts +17 -0
  21. package/lib/lambda/identify/handler.js +124 -0
  22. package/lib/lambda/identify/mapping.d.ts +61 -0
  23. package/lib/lambda/identify/mapping.js +171 -0
  24. package/lib/lambda/identify/merge_resolver.d.ts +26 -0
  25. package/lib/lambda/identify/merge_resolver.js +36 -0
  26. package/lib/lambda/identify/principal.d.ts +68 -0
  27. package/lib/lambda/identify/principal.js +48 -0
  28. package/lib/lambda/identify/profile_resolver.d.ts +35 -0
  29. package/lib/lambda/identify/profile_resolver.js +65 -0
  30. package/lib/lambda/identify/types.d.ts +90 -0
  31. package/lib/lambda/identify/types.js +10 -0
  32. package/lib/lambda/identify/validation.d.ts +15 -0
  33. package/lib/lambda/identify/validation.js +99 -0
  34. package/lib/lambda/push/delivery.d.ts +49 -0
  35. package/lib/lambda/push/delivery.js +120 -0
  36. package/lib/lambda/push/device_lookup.d.ts +35 -0
  37. package/lib/lambda/push/device_lookup.js +88 -0
  38. package/lib/lambda/push/eum_client.d.ts +31 -0
  39. package/lib/lambda/push/eum_client.js +140 -0
  40. package/lib/lambda/push/event.d.ts +32 -0
  41. package/lib/lambda/push/event.js +122 -0
  42. package/lib/lambda/push/fixtures/real_journey_event.d.ts +19 -0
  43. package/lib/lambda/push/fixtures/real_journey_event.js +22 -0
  44. package/lib/lambda/push/handler.d.ts +15 -0
  45. package/lib/lambda/push/handler.js +86 -0
  46. package/lib/lambda/push/message_template.d.ts +72 -0
  47. package/lib/lambda/push/message_template.js +270 -0
  48. package/lib/lambda/push/payload.d.ts +25 -0
  49. package/lib/lambda/push/payload.js +53 -0
  50. package/lib/lambda/push/types.d.ts +153 -0
  51. package/lib/lambda/push/types.js +4 -0
  52. package/lib/lambda/shared/retry.d.ts +18 -0
  53. package/lib/lambda/shared/retry.js +45 -0
  54. package/lib/object_types.d.ts +63 -0
  55. package/lib/object_types.js +131 -0
  56. package/lib/push-handler-asset/index.js +75517 -0
  57. package/lib/types.d.ts +125 -0
  58. package/lib/types.js +2 -0
  59. package/package.json +49 -0
@@ -0,0 +1,140 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { SendMessagesCommand } from '@aws-sdk/client-pinpoint';
4
+ import { buildMessageConfiguration } from './payload.js';
5
+ /**
6
+ * Case-insensitive substrings of a Pinpoint per-address `StatusMessage` that
7
+ * indicate the DEVICE TOKEN itself is permanently invalid — the ONLY condition
8
+ * that justifies deleting the backing AmplifyDevice object (stale-token
9
+ * cleanup).
10
+ *
11
+ * - APNs: `BadDeviceToken`, `Unregistered`, `DeviceTokenNotForTopic`
12
+ * - FCM / GCM legacy HTTP API: `NotRegistered`, `InvalidRegistration`,
13
+ * `MismatchSenderId`
14
+ * - FCM HTTP v1 (current API): `UNREGISTERED` (matched by the APNs
15
+ * `Unregistered` entry), `SENDER_ID_MISMATCH`, and a malformed token's
16
+ * `INVALID_ARGUMENT` whose per-field message is "...not a valid FCM
17
+ * registration token"
18
+ *
19
+ * Everything else that yields a non-delivery — a channel that is not enabled on
20
+ * the EUM/Pinpoint application, missing channel credentials, `OPT_OUT`,
21
+ * `THROTTLED`, `TEMPORARY_FAILURE`, or a `PERMANENT_FAILURE` whose message does
22
+ * not match a token-invalidity signal — is treated as a transient / config
23
+ * problem and the token is KEPT. Deleting on those would wrongly wipe valid
24
+ * device registrations (e.g. when the app simply has no APNS/GCM channel yet).
25
+ */
26
+ const INVALID_TOKEN_INDICATORS = [
27
+ // APNs
28
+ 'BadDeviceToken',
29
+ 'Unregistered',
30
+ 'DeviceTokenNotForTopic',
31
+ // FCM / GCM legacy HTTP API
32
+ 'NotRegistered',
33
+ 'InvalidRegistration',
34
+ 'MismatchSenderId',
35
+ // FCM HTTP v1 (current API). v1 reports token invalidity with different
36
+ // strings than the legacy API: `UNREGISTERED` (already matched
37
+ // case-insensitively by the APNs `Unregistered` entry), `SENDER_ID_MISMATCH`,
38
+ // and a malformed / non-FCM token as `INVALID_ARGUMENT` carrying the specific
39
+ // per-field message matched below. Match that exact token phrase rather than
40
+ // bare `INVALID_ARGUMENT` — the latter also covers non-token payload errors,
41
+ // so matching it broadly could wrongly delete a valid device on a
42
+ // malformed-message bug.
43
+ 'SENDER_ID_MISMATCH',
44
+ 'not a valid FCM registration token',
45
+ ];
46
+ /**
47
+ * Decide whether a per-address delivery failure represents a permanently
48
+ * invalid TOKEN (safe to delete) versus a channel / app misconfiguration or
49
+ * transient error (keep the token).
50
+ *
51
+ * Conservative by design: returns `true` ONLY for a `PERMANENT_FAILURE` whose
52
+ * `StatusMessage` matches a known invalid-token indicator. The default for any
53
+ * ambiguous or unknown failure is `false` (keep the device).
54
+ */
55
+ export const isInvalidTokenFailure = (status, statusMessage) => {
56
+ if (status !== 'PERMANENT_FAILURE') {
57
+ return false;
58
+ }
59
+ const haystack = (statusMessage ?? '').toLowerCase();
60
+ return INVALID_TOKEN_INDICATORS.some((indicator) => haystack.includes(indicator.toLowerCase()));
61
+ };
62
+ /**
63
+ * Deliver a single push to one device via AWS End User Messaging (Pinpoint
64
+ * `SendMessages`), addressing the message to the device token on its channel.
65
+ *
66
+ * Returns a structured per-device result classifying the Pinpoint
67
+ * `DeliveryStatus` into delivered / failed and flagging ONLY a genuinely
68
+ * invalid token as `stale` (via {@link isInvalidTokenFailure}) so the caller
69
+ * can delete its device object. A thrown call (e.g. the channel is not
70
+ * configured on the EUM app) is caught and reported as `status: 'ERROR'`
71
+ * (failed, NOT stale — the token itself may be fine).
72
+ *
73
+ * Emits greppable `[push] send.response` / `[push] classify` log lines carrying
74
+ * the request channel and the per-address response (DeliveryStatus /
75
+ * StatusCode / StatusMessage) plus the keep-vs-delete decision, so a failed
76
+ * send can be diagnosed as channel-not-enabled vs invalid-token from the logs.
77
+ * These carry NO device token, profile id, or message copy.
78
+ */
79
+ export const deliverToDevice = async (pinpoint, applicationId, deviceToken, channelType, message) => {
80
+ try {
81
+ const messageConfiguration = buildMessageConfiguration(channelType, message);
82
+ const res = await pinpoint.send(new SendMessagesCommand({
83
+ ApplicationId: applicationId,
84
+ MessageRequest: {
85
+ Addresses: {
86
+ [deviceToken]: { ChannelType: channelType },
87
+ },
88
+ MessageConfiguration: messageConfiguration,
89
+ },
90
+ }));
91
+ const result = res.MessageResponse?.Result?.[deviceToken];
92
+ const status = result?.DeliveryStatus ?? 'UNKNOWN_FAILURE';
93
+ const statusCode = result?.StatusCode;
94
+ const statusMessage = result?.StatusMessage;
95
+ const stale = isInvalidTokenFailure(status, statusMessage);
96
+ console.log('[push] send.response', JSON.stringify({
97
+ channelType,
98
+ deliveryStatus: status,
99
+ statusCode,
100
+ statusMessage,
101
+ }));
102
+ console.log('[push] classify', JSON.stringify({
103
+ channelType,
104
+ deliveryStatus: status,
105
+ decision: stale ? 'STALE_DELETE' : 'KEEP',
106
+ reason: stale
107
+ ? 'StatusMessage matched an invalid-token indicator'
108
+ : `not a token-invalidity signal (status=${status})`,
109
+ statusMessage,
110
+ }));
111
+ return {
112
+ deviceToken,
113
+ channelType,
114
+ status,
115
+ delivered: status === 'SUCCESSFUL',
116
+ stale,
117
+ statusCode,
118
+ statusMessage,
119
+ };
120
+ }
121
+ catch (err) {
122
+ const statusMessage = err instanceof Error ? err.message : 'unknown error';
123
+ console.log('[push] send.error', JSON.stringify({
124
+ channelType,
125
+ deliveryStatus: 'ERROR',
126
+ decision: 'KEEP',
127
+ reason: 'SendMessages threw (e.g. channel not configured) — token kept',
128
+ statusMessage,
129
+ }));
130
+ return {
131
+ deviceToken,
132
+ channelType,
133
+ status: 'ERROR',
134
+ delivered: false,
135
+ stale: false,
136
+ statusMessage,
137
+ };
138
+ }
139
+ };
140
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXVtX2NsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sYW1iZGEvcHVzaC9ldW1fY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHFFQUFxRTtBQUNyRSxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFrQixtQkFBbUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQy9FLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUd6RDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFDSCxNQUFNLHdCQUF3QixHQUFHO0lBQy9CLE9BQU87SUFDUCxnQkFBZ0I7SUFDaEIsY0FBYztJQUNkLHdCQUF3QjtJQUN4Qiw0QkFBNEI7SUFDNUIsZUFBZTtJQUNmLHFCQUFxQjtJQUNyQixrQkFBa0I7SUFDbEIsd0VBQXdFO0lBQ3hFLCtEQUErRDtJQUMvRCw4RUFBOEU7SUFDOUUsOEVBQThFO0lBQzlFLDZFQUE2RTtJQUM3RSw2RUFBNkU7SUFDN0Usa0VBQWtFO0lBQ2xFLHlCQUF5QjtJQUN6QixvQkFBb0I7SUFDcEIsb0NBQW9DO0NBQ3JDLENBQUM7QUFFRjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLENBQ25DLE1BQWMsRUFDZCxhQUFpQyxFQUN4QixFQUFFO0lBQ1gsSUFBSSxNQUFNLEtBQUssbUJBQW1CLEVBQUUsQ0FBQztRQUNuQyxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFDRCxNQUFNLFFBQVEsR0FBRyxDQUFDLGFBQWEsSUFBSSxFQUFFLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNyRCxPQUFPLHdCQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQ2pELFFBQVEsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQzNDLENBQUM7QUFDSixDQUFDLENBQUM7QUFFRjs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxLQUFLLEVBQ2xDLFFBQXdCLEVBQ3hCLGFBQXFCLEVBQ3JCLFdBQW1CLEVBQ25CLFdBQTRCLEVBQzVCLE9BQW9CLEVBQ1csRUFBRTtJQUNqQyxJQUFJLENBQUM7UUFDSCxNQUFNLG9CQUFvQixHQUFHLHlCQUF5QixDQUNwRCxXQUFXLEVBQ1gsT0FBTyxDQUNSLENBQUM7UUFFRixNQUFNLEdBQUcsR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLENBQzdCLElBQUksbUJBQW1CLENBQUM7WUFDdEIsYUFBYSxFQUFFLGFBQWE7WUFDNUIsY0FBYyxFQUFFO2dCQUNkLFNBQVMsRUFBRTtvQkFDVCxDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRTtpQkFDNUM7Z0JBQ0Qsb0JBQW9CLEVBQUUsb0JBQW9CO2FBQzNDO1NBQ0YsQ0FBQyxDQUNILENBQUM7UUFFRixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsZUFBZSxFQUFFLE1BQU0sRUFBRSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzFELE1BQU0sTUFBTSxHQUFHLE1BQU0sRUFBRSxjQUFjLElBQUksaUJBQWlCLENBQUM7UUFDM0QsTUFBTSxVQUFVLEdBQUcsTUFBTSxFQUFFLFVBQVUsQ0FBQztRQUN0QyxNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUUsYUFBYSxDQUFDO1FBQzVDLE1BQU0sS0FBSyxHQUFHLHFCQUFxQixDQUFDLE1BQU0sRUFBRSxhQUFhLENBQUMsQ0FBQztRQUUzRCxPQUFPLENBQUMsR0FBRyxDQUNULHNCQUFzQixFQUN0QixJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ2IsV0FBVztZQUNYLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFVBQVU7WUFDVixhQUFhO1NBQ2QsQ0FBQyxDQUNILENBQUM7UUFDRixPQUFPLENBQUMsR0FBRyxDQUNULGlCQUFpQixFQUNqQixJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ2IsV0FBVztZQUNYLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFFBQVEsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsTUFBTTtZQUN6QyxNQUFNLEVBQUUsS0FBSztnQkFDWCxDQUFDLENBQUMsa0RBQWtEO2dCQUNwRCxDQUFDLENBQUMseUNBQXlDLE1BQU0sR0FBRztZQUN0RCxhQUFhO1NBQ2QsQ0FBQyxDQUNILENBQUM7UUFFRixPQUFPO1lBQ0wsV0FBVztZQUNYLFdBQVc7WUFDWCxNQUFNO1lBQ04sU0FBUyxFQUFFLE1BQU0sS0FBSyxZQUFZO1lBQ2xDLEtBQUs7WUFDTCxVQUFVO1lBQ1YsYUFBYTtTQUNkLENBQUM7SUFDSixDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE1BQU0sYUFBYSxHQUFHLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQztRQUMzRSxPQUFPLENBQUMsR0FBRyxDQUNULG1CQUFtQixFQUNuQixJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ2IsV0FBVztZQUNYLGNBQWMsRUFBRSxPQUFPO1lBQ3ZCLFFBQVEsRUFBRSxNQUFNO1lBQ2hCLE1BQU0sRUFBRSwrREFBK0Q7WUFDdkUsYUFBYTtTQUNkLENBQUMsQ0FDSCxDQUFDO1FBQ0YsT0FBTztZQUNMLFdBQVc7WUFDWCxXQUFXO1lBQ1gsTUFBTSxFQUFFLE9BQU87WUFDZixTQUFTLEVBQUUsS0FBSztZQUNoQixLQUFLLEVBQUUsS0FBSztZQUNaLGFBQWE7U0FDZCxDQUFDO0lBQ0osQ0FBQztBQUNILENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgUGlucG9pbnRDbGllbnQsIFNlbmRNZXNzYWdlc0NvbW1hbmQgfSBmcm9tICdAYXdzLXNkay9jbGllbnQtcGlucG9pbnQnO1xuaW1wb3J0IHsgYnVpbGRNZXNzYWdlQ29uZmlndXJhdGlvbiB9IGZyb20gJy4vcGF5bG9hZC5qcyc7XG5pbXBvcnQgeyBEZXZpY2VEZWxpdmVyeVJlc3VsdCwgUHVzaENoYW5uZWxUeXBlLCBQdXNoTWVzc2FnZSB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG4vKipcbiAqIENhc2UtaW5zZW5zaXRpdmUgc3Vic3RyaW5ncyBvZiBhIFBpbnBvaW50IHBlci1hZGRyZXNzIGBTdGF0dXNNZXNzYWdlYCB0aGF0XG4gKiBpbmRpY2F0ZSB0aGUgREVWSUNFIFRPS0VOIGl0c2VsZiBpcyBwZXJtYW5lbnRseSBpbnZhbGlkIOKAlCB0aGUgT05MWSBjb25kaXRpb25cbiAqIHRoYXQganVzdGlmaWVzIGRlbGV0aW5nIHRoZSBiYWNraW5nIEFtcGxpZnlEZXZpY2Ugb2JqZWN0IChzdGFsZS10b2tlblxuICogY2xlYW51cCkuXG4gKlxuICogLSBBUE5zOiBgQmFkRGV2aWNlVG9rZW5gLCBgVW5yZWdpc3RlcmVkYCwgYERldmljZVRva2VuTm90Rm9yVG9waWNgXG4gKiAtIEZDTSAvIEdDTSBsZWdhY3kgSFRUUCBBUEk6IGBOb3RSZWdpc3RlcmVkYCwgYEludmFsaWRSZWdpc3RyYXRpb25gLFxuICogICBgTWlzbWF0Y2hTZW5kZXJJZGBcbiAqIC0gRkNNIEhUVFAgdjEgKGN1cnJlbnQgQVBJKTogYFVOUkVHSVNURVJFRGAgKG1hdGNoZWQgYnkgdGhlIEFQTnNcbiAqICAgYFVucmVnaXN0ZXJlZGAgZW50cnkpLCBgU0VOREVSX0lEX01JU01BVENIYCwgYW5kIGEgbWFsZm9ybWVkIHRva2VuJ3NcbiAqICAgYElOVkFMSURfQVJHVU1FTlRgIHdob3NlIHBlci1maWVsZCBtZXNzYWdlIGlzIFwiLi4ubm90IGEgdmFsaWQgRkNNXG4gKiAgIHJlZ2lzdHJhdGlvbiB0b2tlblwiXG4gKlxuICogRXZlcnl0aGluZyBlbHNlIHRoYXQgeWllbGRzIGEgbm9uLWRlbGl2ZXJ5IOKAlCBhIGNoYW5uZWwgdGhhdCBpcyBub3QgZW5hYmxlZCBvblxuICogdGhlIEVVTS9QaW5wb2ludCBhcHBsaWNhdGlvbiwgbWlzc2luZyBjaGFubmVsIGNyZWRlbnRpYWxzLCBgT1BUX09VVGAsXG4gKiBgVEhST1RUTEVEYCwgYFRFTVBPUkFSWV9GQUlMVVJFYCwgb3IgYSBgUEVSTUFORU5UX0ZBSUxVUkVgIHdob3NlIG1lc3NhZ2UgZG9lc1xuICogbm90IG1hdGNoIGEgdG9rZW4taW52YWxpZGl0eSBzaWduYWwg4oCUIGlzIHRyZWF0ZWQgYXMgYSB0cmFuc2llbnQgLyBjb25maWdcbiAqIHByb2JsZW0gYW5kIHRoZSB0b2tlbiBpcyBLRVBULiBEZWxldGluZyBvbiB0aG9zZSB3b3VsZCB3cm9uZ2x5IHdpcGUgdmFsaWRcbiAqIGRldmljZSByZWdpc3RyYXRpb25zIChlLmcuIHdoZW4gdGhlIGFwcCBzaW1wbHkgaGFzIG5vIEFQTlMvR0NNIGNoYW5uZWwgeWV0KS5cbiAqL1xuY29uc3QgSU5WQUxJRF9UT0tFTl9JTkRJQ0FUT1JTID0gW1xuICAvLyBBUE5zXG4gICdCYWREZXZpY2VUb2tlbicsXG4gICdVbnJlZ2lzdGVyZWQnLFxuICAnRGV2aWNlVG9rZW5Ob3RGb3JUb3BpYycsXG4gIC8vIEZDTSAvIEdDTSBsZWdhY3kgSFRUUCBBUElcbiAgJ05vdFJlZ2lzdGVyZWQnLFxuICAnSW52YWxpZFJlZ2lzdHJhdGlvbicsXG4gICdNaXNtYXRjaFNlbmRlcklkJyxcbiAgLy8gRkNNIEhUVFAgdjEgKGN1cnJlbnQgQVBJKS4gdjEgcmVwb3J0cyB0b2tlbiBpbnZhbGlkaXR5IHdpdGggZGlmZmVyZW50XG4gIC8vIHN0cmluZ3MgdGhhbiB0aGUgbGVnYWN5IEFQSTogYFVOUkVHSVNURVJFRGAgKGFscmVhZHkgbWF0Y2hlZFxuICAvLyBjYXNlLWluc2Vuc2l0aXZlbHkgYnkgdGhlIEFQTnMgYFVucmVnaXN0ZXJlZGAgZW50cnkpLCBgU0VOREVSX0lEX01JU01BVENIYCxcbiAgLy8gYW5kIGEgbWFsZm9ybWVkIC8gbm9uLUZDTSB0b2tlbiBhcyBgSU5WQUxJRF9BUkdVTUVOVGAgY2FycnlpbmcgdGhlIHNwZWNpZmljXG4gIC8vIHBlci1maWVsZCBtZXNzYWdlIG1hdGNoZWQgYmVsb3cuIE1hdGNoIHRoYXQgZXhhY3QgdG9rZW4gcGhyYXNlIHJhdGhlciB0aGFuXG4gIC8vIGJhcmUgYElOVkFMSURfQVJHVU1FTlRgIOKAlCB0aGUgbGF0dGVyIGFsc28gY292ZXJzIG5vbi10b2tlbiBwYXlsb2FkIGVycm9ycyxcbiAgLy8gc28gbWF0Y2hpbmcgaXQgYnJvYWRseSBjb3VsZCB3cm9uZ2x5IGRlbGV0ZSBhIHZhbGlkIGRldmljZSBvbiBhXG4gIC8vIG1hbGZvcm1lZC1tZXNzYWdlIGJ1Zy5cbiAgJ1NFTkRFUl9JRF9NSVNNQVRDSCcsXG4gICdub3QgYSB2YWxpZCBGQ00gcmVnaXN0cmF0aW9uIHRva2VuJyxcbl07XG5cbi8qKlxuICogRGVjaWRlIHdoZXRoZXIgYSBwZXItYWRkcmVzcyBkZWxpdmVyeSBmYWlsdXJlIHJlcHJlc2VudHMgYSBwZXJtYW5lbnRseVxuICogaW52YWxpZCBUT0tFTiAoc2FmZSB0byBkZWxldGUpIHZlcnN1cyBhIGNoYW5uZWwgLyBhcHAgbWlzY29uZmlndXJhdGlvbiBvclxuICogdHJhbnNpZW50IGVycm9yIChrZWVwIHRoZSB0b2tlbikuXG4gKlxuICogQ29uc2VydmF0aXZlIGJ5IGRlc2lnbjogcmV0dXJucyBgdHJ1ZWAgT05MWSBmb3IgYSBgUEVSTUFORU5UX0ZBSUxVUkVgIHdob3NlXG4gKiBgU3RhdHVzTWVzc2FnZWAgbWF0Y2hlcyBhIGtub3duIGludmFsaWQtdG9rZW4gaW5kaWNhdG9yLiBUaGUgZGVmYXVsdCBmb3IgYW55XG4gKiBhbWJpZ3VvdXMgb3IgdW5rbm93biBmYWlsdXJlIGlzIGBmYWxzZWAgKGtlZXAgdGhlIGRldmljZSkuXG4gKi9cbmV4cG9ydCBjb25zdCBpc0ludmFsaWRUb2tlbkZhaWx1cmUgPSAoXG4gIHN0YXR1czogc3RyaW5nLFxuICBzdGF0dXNNZXNzYWdlOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4pOiBib29sZWFuID0+IHtcbiAgaWYgKHN0YXR1cyAhPT0gJ1BFUk1BTkVOVF9GQUlMVVJFJykge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBjb25zdCBoYXlzdGFjayA9IChzdGF0dXNNZXNzYWdlID8/ICcnKS50b0xvd2VyQ2FzZSgpO1xuICByZXR1cm4gSU5WQUxJRF9UT0tFTl9JTkRJQ0FUT1JTLnNvbWUoKGluZGljYXRvcikgPT5cbiAgICBoYXlzdGFjay5pbmNsdWRlcyhpbmRpY2F0b3IudG9Mb3dlckNhc2UoKSksXG4gICk7XG59O1xuXG4vKipcbiAqIERlbGl2ZXIgYSBzaW5nbGUgcHVzaCB0byBvbmUgZGV2aWNlIHZpYSBBV1MgRW5kIFVzZXIgTWVzc2FnaW5nIChQaW5wb2ludFxuICogYFNlbmRNZXNzYWdlc2ApLCBhZGRyZXNzaW5nIHRoZSBtZXNzYWdlIHRvIHRoZSBkZXZpY2UgdG9rZW4gb24gaXRzIGNoYW5uZWwuXG4gKlxuICogUmV0dXJucyBhIHN0cnVjdHVyZWQgcGVyLWRldmljZSByZXN1bHQgY2xhc3NpZnlpbmcgdGhlIFBpbnBvaW50XG4gKiBgRGVsaXZlcnlTdGF0dXNgIGludG8gZGVsaXZlcmVkIC8gZmFpbGVkIGFuZCBmbGFnZ2luZyBPTkxZIGEgZ2VudWluZWx5XG4gKiBpbnZhbGlkIHRva2VuIGFzIGBzdGFsZWAgKHZpYSB7QGxpbmsgaXNJbnZhbGlkVG9rZW5GYWlsdXJlfSkgc28gdGhlIGNhbGxlclxuICogY2FuIGRlbGV0ZSBpdHMgZGV2aWNlIG9iamVjdC4gQSB0aHJvd24gY2FsbCAoZS5nLiB0aGUgY2hhbm5lbCBpcyBub3RcbiAqIGNvbmZpZ3VyZWQgb24gdGhlIEVVTSBhcHApIGlzIGNhdWdodCBhbmQgcmVwb3J0ZWQgYXMgYHN0YXR1czogJ0VSUk9SJ2BcbiAqIChmYWlsZWQsIE5PVCBzdGFsZSDigJQgdGhlIHRva2VuIGl0c2VsZiBtYXkgYmUgZmluZSkuXG4gKlxuICogRW1pdHMgZ3JlcHBhYmxlIGBbcHVzaF0gc2VuZC5yZXNwb25zZWAgLyBgW3B1c2hdIGNsYXNzaWZ5YCBsb2cgbGluZXMgY2FycnlpbmdcbiAqIHRoZSByZXF1ZXN0IGNoYW5uZWwgYW5kIHRoZSBwZXItYWRkcmVzcyByZXNwb25zZSAoRGVsaXZlcnlTdGF0dXMgL1xuICogU3RhdHVzQ29kZSAvIFN0YXR1c01lc3NhZ2UpIHBsdXMgdGhlIGtlZXAtdnMtZGVsZXRlIGRlY2lzaW9uLCBzbyBhIGZhaWxlZFxuICogc2VuZCBjYW4gYmUgZGlhZ25vc2VkIGFzIGNoYW5uZWwtbm90LWVuYWJsZWQgdnMgaW52YWxpZC10b2tlbiBmcm9tIHRoZSBsb2dzLlxuICogVGhlc2UgY2FycnkgTk8gZGV2aWNlIHRva2VuLCBwcm9maWxlIGlkLCBvciBtZXNzYWdlIGNvcHkuXG4gKi9cbmV4cG9ydCBjb25zdCBkZWxpdmVyVG9EZXZpY2UgPSBhc3luYyAoXG4gIHBpbnBvaW50OiBQaW5wb2ludENsaWVudCxcbiAgYXBwbGljYXRpb25JZDogc3RyaW5nLFxuICBkZXZpY2VUb2tlbjogc3RyaW5nLFxuICBjaGFubmVsVHlwZTogUHVzaENoYW5uZWxUeXBlLFxuICBtZXNzYWdlOiBQdXNoTWVzc2FnZSxcbik6IFByb21pc2U8RGV2aWNlRGVsaXZlcnlSZXN1bHQ+ID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBtZXNzYWdlQ29uZmlndXJhdGlvbiA9IGJ1aWxkTWVzc2FnZUNvbmZpZ3VyYXRpb24oXG4gICAgICBjaGFubmVsVHlwZSxcbiAgICAgIG1lc3NhZ2UsXG4gICAgKTtcblxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IHBpbnBvaW50LnNlbmQoXG4gICAgICBuZXcgU2VuZE1lc3NhZ2VzQ29tbWFuZCh7XG4gICAgICAgIEFwcGxpY2F0aW9uSWQ6IGFwcGxpY2F0aW9uSWQsXG4gICAgICAgIE1lc3NhZ2VSZXF1ZXN0OiB7XG4gICAgICAgICAgQWRkcmVzc2VzOiB7XG4gICAgICAgICAgICBbZGV2aWNlVG9rZW5dOiB7IENoYW5uZWxUeXBlOiBjaGFubmVsVHlwZSB9LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgTWVzc2FnZUNvbmZpZ3VyYXRpb246IG1lc3NhZ2VDb25maWd1cmF0aW9uLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgKTtcblxuICAgIGNvbnN0IHJlc3VsdCA9IHJlcy5NZXNzYWdlUmVzcG9uc2U/LlJlc3VsdD8uW2RldmljZVRva2VuXTtcbiAgICBjb25zdCBzdGF0dXMgPSByZXN1bHQ/LkRlbGl2ZXJ5U3RhdHVzID8/ICdVTktOT1dOX0ZBSUxVUkUnO1xuICAgIGNvbnN0IHN0YXR1c0NvZGUgPSByZXN1bHQ/LlN0YXR1c0NvZGU7XG4gICAgY29uc3Qgc3RhdHVzTWVzc2FnZSA9IHJlc3VsdD8uU3RhdHVzTWVzc2FnZTtcbiAgICBjb25zdCBzdGFsZSA9IGlzSW52YWxpZFRva2VuRmFpbHVyZShzdGF0dXMsIHN0YXR1c01lc3NhZ2UpO1xuXG4gICAgY29uc29sZS5sb2coXG4gICAgICAnW3B1c2hdIHNlbmQucmVzcG9uc2UnLFxuICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICBjaGFubmVsVHlwZSxcbiAgICAgICAgZGVsaXZlcnlTdGF0dXM6IHN0YXR1cyxcbiAgICAgICAgc3RhdHVzQ29kZSxcbiAgICAgICAgc3RhdHVzTWVzc2FnZSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgY29uc29sZS5sb2coXG4gICAgICAnW3B1c2hdIGNsYXNzaWZ5JyxcbiAgICAgIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgY2hhbm5lbFR5cGUsXG4gICAgICAgIGRlbGl2ZXJ5U3RhdHVzOiBzdGF0dXMsXG4gICAgICAgIGRlY2lzaW9uOiBzdGFsZSA/ICdTVEFMRV9ERUxFVEUnIDogJ0tFRVAnLFxuICAgICAgICByZWFzb246IHN0YWxlXG4gICAgICAgICAgPyAnU3RhdHVzTWVzc2FnZSBtYXRjaGVkIGFuIGludmFsaWQtdG9rZW4gaW5kaWNhdG9yJ1xuICAgICAgICAgIDogYG5vdCBhIHRva2VuLWludmFsaWRpdHkgc2lnbmFsIChzdGF0dXM9JHtzdGF0dXN9KWAsXG4gICAgICAgIHN0YXR1c01lc3NhZ2UsXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGRldmljZVRva2VuLFxuICAgICAgY2hhbm5lbFR5cGUsXG4gICAgICBzdGF0dXMsXG4gICAgICBkZWxpdmVyZWQ6IHN0YXR1cyA9PT0gJ1NVQ0NFU1NGVUwnLFxuICAgICAgc3RhbGUsXG4gICAgICBzdGF0dXNDb2RlLFxuICAgICAgc3RhdHVzTWVzc2FnZSxcbiAgICB9O1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBjb25zdCBzdGF0dXNNZXNzYWdlID0gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6ICd1bmtub3duIGVycm9yJztcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgICdbcHVzaF0gc2VuZC5lcnJvcicsXG4gICAgICBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgIGNoYW5uZWxUeXBlLFxuICAgICAgICBkZWxpdmVyeVN0YXR1czogJ0VSUk9SJyxcbiAgICAgICAgZGVjaXNpb246ICdLRUVQJyxcbiAgICAgICAgcmVhc29uOiAnU2VuZE1lc3NhZ2VzIHRocmV3IChlLmcuIGNoYW5uZWwgbm90IGNvbmZpZ3VyZWQpIOKAlCB0b2tlbiBrZXB0JyxcbiAgICAgICAgc3RhdHVzTWVzc2FnZSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGRldmljZVRva2VuLFxuICAgICAgY2hhbm5lbFR5cGUsXG4gICAgICBzdGF0dXM6ICdFUlJPUicsXG4gICAgICBkZWxpdmVyZWQ6IGZhbHNlLFxuICAgICAgc3RhbGU6IGZhbHNlLFxuICAgICAgc3RhdHVzTWVzc2FnZSxcbiAgICB9O1xuICB9XG59O1xuIl19
@@ -0,0 +1,32 @@
1
+ import { ParsedPushEvent } from './types.js';
2
+ /**
3
+ * Parse a raw Connect Journey Custom-action event into a flat list of profile
4
+ * targets plus the message to deliver.
5
+ *
6
+ * CANONICAL shape — the Amazon Connect Outbound Campaigns v2 journey
7
+ * invocation, confirmed verbatim from a live journey run (see
8
+ * `fixtures/real_journey_event.ts` for the source-log reference). This is the
9
+ * ONLY shape Connect sends, and the single primary parse path:
10
+ *
11
+ * {
12
+ * InvocationMetadata: { CampaignContext: { CampaignId, RunId, ActionId, CampaignName } },
13
+ * Items: { CustomerProfiles: [ { ProfileId, CustomerData, ... }, ... ] }
14
+ * }
15
+ *
16
+ * Shape specifics (all PascalCase on the envelope):
17
+ * - `Items` is an OBJECT `{ CustomerProfiles: [...] }`;
18
+ * - each entry carries a top-level `ProfileId` (used for device lookup);
19
+ * - each entry's `CustomerData` is a SERIALIZED JSON STRING with camelCase
20
+ * keys (`profileId`, `firstName`, `messageTitle`, `messageBody`,
21
+ * `attributes.*`) that is `JSON.parse`d.
22
+ *
23
+ * Minimal, documented defensive handling only: a `CustomerData` already given as
24
+ * a parsed object is tolerated (see {@link coerceCustomerData}); missing /
25
+ * malformed fields are skipped rather than aborting the batch. The real journey
26
+ * carries NO message copy, so {@link ParsedPushEvent.message} is always the safe
27
+ * {@link DEFAULT_PUSH_TITLE} / {@link DEFAULT_PUSH_BODY}; personalized copy comes
28
+ * from the Q Connect PUSH template rendered per profile downstream, and this
29
+ * default is used only as the per-channel fallback.
30
+ */
31
+ export declare const parsePushEvent: (event: unknown) => ParsedPushEvent;
32
+ //# sourceMappingURL=event.d.ts.map
@@ -0,0 +1,122 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { DEFAULT_PUSH_BODY, DEFAULT_PUSH_TITLE } from '../../constants.js';
4
+ /**
5
+ * Parse a raw Connect Journey Custom-action event into a flat list of profile
6
+ * targets plus the message to deliver.
7
+ *
8
+ * CANONICAL shape — the Amazon Connect Outbound Campaigns v2 journey
9
+ * invocation, confirmed verbatim from a live journey run (see
10
+ * `fixtures/real_journey_event.ts` for the source-log reference). This is the
11
+ * ONLY shape Connect sends, and the single primary parse path:
12
+ *
13
+ * {
14
+ * InvocationMetadata: { CampaignContext: { CampaignId, RunId, ActionId, CampaignName } },
15
+ * Items: { CustomerProfiles: [ { ProfileId, CustomerData, ... }, ... ] }
16
+ * }
17
+ *
18
+ * Shape specifics (all PascalCase on the envelope):
19
+ * - `Items` is an OBJECT `{ CustomerProfiles: [...] }`;
20
+ * - each entry carries a top-level `ProfileId` (used for device lookup);
21
+ * - each entry's `CustomerData` is a SERIALIZED JSON STRING with camelCase
22
+ * keys (`profileId`, `firstName`, `messageTitle`, `messageBody`,
23
+ * `attributes.*`) that is `JSON.parse`d.
24
+ *
25
+ * Minimal, documented defensive handling only: a `CustomerData` already given as
26
+ * a parsed object is tolerated (see {@link coerceCustomerData}); missing /
27
+ * malformed fields are skipped rather than aborting the batch. The real journey
28
+ * carries NO message copy, so {@link ParsedPushEvent.message} is always the safe
29
+ * {@link DEFAULT_PUSH_TITLE} / {@link DEFAULT_PUSH_BODY}; personalized copy comes
30
+ * from the Q Connect PUSH template rendered per profile downstream, and this
31
+ * default is used only as the per-channel fallback.
32
+ */
33
+ export const parsePushEvent = (event) => {
34
+ const root = isRecord(event) ? event : {};
35
+ const { targets, parsePath } = extractTargets(root);
36
+ const campaign = extractCampaign(root);
37
+ return {
38
+ targets,
39
+ message: { title: DEFAULT_PUSH_TITLE, body: DEFAULT_PUSH_BODY },
40
+ parsePath,
41
+ ...(campaign ? { campaign } : {}),
42
+ };
43
+ };
44
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
45
+ const asString = (v) => typeof v === 'string' && v.length > 0 ? v : undefined;
46
+ /**
47
+ * Extract profile targets from the canonical `Items.CustomerProfiles[]` array.
48
+ * Each entry contributes its `ProfileId` + parsed `CustomerData`; entries
49
+ * without a usable `ProfileId` are skipped.
50
+ */
51
+ const extractTargets = (root) => {
52
+ const targets = [];
53
+ const items = root.Items;
54
+ const customerProfiles = isRecord(items) ? items.CustomerProfiles : undefined;
55
+ if (Array.isArray(customerProfiles)) {
56
+ for (const entry of customerProfiles) {
57
+ if (!isRecord(entry)) {
58
+ continue;
59
+ }
60
+ const profileId = asString(entry.ProfileId);
61
+ if (!profileId) {
62
+ continue;
63
+ }
64
+ targets.push({
65
+ profileId,
66
+ customerData: coerceCustomerData(entry.CustomerData),
67
+ });
68
+ }
69
+ }
70
+ return { targets, parsePath: targets.length > 0 ? 'canonical' : 'none' };
71
+ };
72
+ /**
73
+ * Coerce a raw `CustomerData` value into a property bag.
74
+ *
75
+ * The real Connect journey delivers `CustomerData` as a SERIALIZED JSON STRING
76
+ * (camelCase keys), so a string is `JSON.parse`d. Minimal defensive tolerance:
77
+ * a value already given as a plain object is used as-is. Anything else — or a
78
+ * string that isn't valid JSON, or JSON that isn't an object — yields
79
+ * `undefined` so a malformed entry never aborts the batch.
80
+ */
81
+ const coerceCustomerData = (raw) => {
82
+ if (typeof raw === 'string') {
83
+ const trimmed = raw.trim();
84
+ if (!trimmed) {
85
+ return undefined;
86
+ }
87
+ try {
88
+ const parsed = JSON.parse(trimmed);
89
+ return isRecord(parsed) ? parsed : undefined;
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
95
+ return isRecord(raw) ? raw : undefined;
96
+ };
97
+ /**
98
+ * Extract the Outbound Campaigns v2 journey context from
99
+ * `InvocationMetadata.CampaignContext`, when present. Returns `undefined` when
100
+ * the envelope carries no campaign metadata (e.g. direct-invoke test payloads)
101
+ * or when none of the expected fields resolve.
102
+ */
103
+ const extractCampaign = (root) => {
104
+ const meta = root.InvocationMetadata;
105
+ if (!isRecord(meta)) {
106
+ return undefined;
107
+ }
108
+ const ctx = meta.CampaignContext;
109
+ if (!isRecord(ctx)) {
110
+ return undefined;
111
+ }
112
+ const campaign = {
113
+ campaignId: asString(ctx.CampaignId),
114
+ campaignName: asString(ctx.CampaignName),
115
+ actionId: asString(ctx.ActionId),
116
+ runId: asString(ctx.RunId),
117
+ };
118
+ return Object.values(campaign).some((v) => v !== undefined)
119
+ ? campaign
120
+ : undefined;
121
+ };
122
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGFtYmRhL3B1c2gvZXZlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEscUVBQXFFO0FBQ3JFLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQVEzRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUNILE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWMsRUFBbUIsRUFBRTtJQUNoRSxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQzFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BELE1BQU0sUUFBUSxHQUFHLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN2QyxPQUFPO1FBQ0wsT0FBTztRQUNQLE9BQU8sRUFBRSxFQUFFLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxJQUFJLEVBQUUsaUJBQWlCLEVBQUU7UUFDL0QsU0FBUztRQUNULEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUNsQyxDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBRUYsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFVLEVBQWdDLEVBQUUsQ0FDNUQsT0FBTyxDQUFDLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRTNELE1BQU0sUUFBUSxHQUFHLENBQUMsQ0FBVSxFQUFzQixFQUFFLENBQ2xELE9BQU8sQ0FBQyxLQUFLLFFBQVEsSUFBSSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFFeEQ7Ozs7R0FJRztBQUNILE1BQU0sY0FBYyxHQUFHLENBQ3JCLElBQTZCLEVBQ2dDLEVBQUU7SUFDL0QsTUFBTSxPQUFPLEdBQW9CLEVBQUUsQ0FBQztJQUVwQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBQ3pCLE1BQU0sZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUM5RSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDO1FBQ3BDLEtBQUssTUFBTSxLQUFLLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztZQUNyQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3JCLFNBQVM7WUFDWCxDQUFDO1lBQ0QsTUFBTSxTQUFTLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM1QyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ2YsU0FBUztZQUNYLENBQUM7WUFDRCxPQUFPLENBQUMsSUFBSSxDQUFDO2dCQUNYLFNBQVM7Z0JBQ1QsWUFBWSxFQUFFLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUM7YUFDckQsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztBQUMzRSxDQUFDLENBQUM7QUFFRjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sa0JBQWtCLEdBQUcsQ0FDekIsR0FBWSxFQUN5QixFQUFFO0lBQ3ZDLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDNUIsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7UUFDRCxJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBWSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzVDLE9BQU8sUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUMvQyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDekMsQ0FBQyxDQUFDO0FBRUY7Ozs7O0dBS0c7QUFDSCxNQUFNLGVBQWUsR0FBRyxDQUN0QixJQUE2QixFQUNBLEVBQUU7SUFDL0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLGtCQUFrQixDQUFDO0lBQ3JDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNwQixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUNqQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbkIsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUNELE1BQU0sUUFBUSxHQUFvQjtRQUNoQyxVQUFVLEVBQUUsUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUM7UUFDcEMsWUFBWSxFQUFFLFFBQVEsQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDO1FBQ3hDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztRQUNoQyxLQUFLLEVBQUUsUUFBUSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUM7S0FDM0IsQ0FBQztJQUNGLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxTQUFTLENBQUM7UUFDekQsQ0FBQyxDQUFDLFFBQVE7UUFDVixDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ2hCLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgREVGQVVMVF9QVVNIX0JPRFksIERFRkFVTFRfUFVTSF9USVRMRSB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQge1xuICBDYW1wYWlnbkNvbnRleHQsXG4gIFBhcnNlZFB1c2hFdmVudCxcbiAgUHJvZmlsZVRhcmdldCxcbiAgUHVzaEV2ZW50UGFyc2VQYXRoLFxufSBmcm9tICcuL3R5cGVzLmpzJztcblxuLyoqXG4gKiBQYXJzZSBhIHJhdyBDb25uZWN0IEpvdXJuZXkgQ3VzdG9tLWFjdGlvbiBldmVudCBpbnRvIGEgZmxhdCBsaXN0IG9mIHByb2ZpbGVcbiAqIHRhcmdldHMgcGx1cyB0aGUgbWVzc2FnZSB0byBkZWxpdmVyLlxuICpcbiAqIENBTk9OSUNBTCBzaGFwZSDigJQgdGhlIEFtYXpvbiBDb25uZWN0IE91dGJvdW5kIENhbXBhaWducyB2MiBqb3VybmV5XG4gKiBpbnZvY2F0aW9uLCBjb25maXJtZWQgdmVyYmF0aW0gZnJvbSBhIGxpdmUgam91cm5leSBydW4gKHNlZVxuICogYGZpeHR1cmVzL3JlYWxfam91cm5leV9ldmVudC50c2AgZm9yIHRoZSBzb3VyY2UtbG9nIHJlZmVyZW5jZSkuIFRoaXMgaXMgdGhlXG4gKiBPTkxZIHNoYXBlIENvbm5lY3Qgc2VuZHMsIGFuZCB0aGUgc2luZ2xlIHByaW1hcnkgcGFyc2UgcGF0aDpcbiAqXG4gKiAgIHtcbiAqICAgICBJbnZvY2F0aW9uTWV0YWRhdGE6IHsgQ2FtcGFpZ25Db250ZXh0OiB7IENhbXBhaWduSWQsIFJ1bklkLCBBY3Rpb25JZCwgQ2FtcGFpZ25OYW1lIH0gfSxcbiAqICAgICBJdGVtczogeyBDdXN0b21lclByb2ZpbGVzOiBbIHsgUHJvZmlsZUlkLCBDdXN0b21lckRhdGEsIC4uLiB9LCAuLi4gXSB9XG4gKiAgIH1cbiAqXG4gKiBTaGFwZSBzcGVjaWZpY3MgKGFsbCBQYXNjYWxDYXNlIG9uIHRoZSBlbnZlbG9wZSk6XG4gKiAgIC0gYEl0ZW1zYCBpcyBhbiBPQkpFQ1QgYHsgQ3VzdG9tZXJQcm9maWxlczogWy4uLl0gfWA7XG4gKiAgIC0gZWFjaCBlbnRyeSBjYXJyaWVzIGEgdG9wLWxldmVsIGBQcm9maWxlSWRgICh1c2VkIGZvciBkZXZpY2UgbG9va3VwKTtcbiAqICAgLSBlYWNoIGVudHJ5J3MgYEN1c3RvbWVyRGF0YWAgaXMgYSBTRVJJQUxJWkVEIEpTT04gU1RSSU5HIHdpdGggY2FtZWxDYXNlXG4gKiAgICAga2V5cyAoYHByb2ZpbGVJZGAsIGBmaXJzdE5hbWVgLCBgbWVzc2FnZVRpdGxlYCwgYG1lc3NhZ2VCb2R5YCxcbiAqICAgICBgYXR0cmlidXRlcy4qYCkgdGhhdCBpcyBgSlNPTi5wYXJzZWBkLlxuICpcbiAqIE1pbmltYWwsIGRvY3VtZW50ZWQgZGVmZW5zaXZlIGhhbmRsaW5nIG9ubHk6IGEgYEN1c3RvbWVyRGF0YWAgYWxyZWFkeSBnaXZlbiBhc1xuICogYSBwYXJzZWQgb2JqZWN0IGlzIHRvbGVyYXRlZCAoc2VlIHtAbGluayBjb2VyY2VDdXN0b21lckRhdGF9KTsgbWlzc2luZyAvXG4gKiBtYWxmb3JtZWQgZmllbGRzIGFyZSBza2lwcGVkIHJhdGhlciB0aGFuIGFib3J0aW5nIHRoZSBiYXRjaC4gVGhlIHJlYWwgam91cm5leVxuICogY2FycmllcyBOTyBtZXNzYWdlIGNvcHksIHNvIHtAbGluayBQYXJzZWRQdXNoRXZlbnQubWVzc2FnZX0gaXMgYWx3YXlzIHRoZSBzYWZlXG4gKiB7QGxpbmsgREVGQVVMVF9QVVNIX1RJVExFfSAvIHtAbGluayBERUZBVUxUX1BVU0hfQk9EWX07IHBlcnNvbmFsaXplZCBjb3B5IGNvbWVzXG4gKiBmcm9tIHRoZSBRIENvbm5lY3QgUFVTSCB0ZW1wbGF0ZSByZW5kZXJlZCBwZXIgcHJvZmlsZSBkb3duc3RyZWFtLCBhbmQgdGhpc1xuICogZGVmYXVsdCBpcyB1c2VkIG9ubHkgYXMgdGhlIHBlci1jaGFubmVsIGZhbGxiYWNrLlxuICovXG5leHBvcnQgY29uc3QgcGFyc2VQdXNoRXZlbnQgPSAoZXZlbnQ6IHVua25vd24pOiBQYXJzZWRQdXNoRXZlbnQgPT4ge1xuICBjb25zdCByb290ID0gaXNSZWNvcmQoZXZlbnQpID8gZXZlbnQgOiB7fTtcbiAgY29uc3QgeyB0YXJnZXRzLCBwYXJzZVBhdGggfSA9IGV4dHJhY3RUYXJnZXRzKHJvb3QpO1xuICBjb25zdCBjYW1wYWlnbiA9IGV4dHJhY3RDYW1wYWlnbihyb290KTtcbiAgcmV0dXJuIHtcbiAgICB0YXJnZXRzLFxuICAgIG1lc3NhZ2U6IHsgdGl0bGU6IERFRkFVTFRfUFVTSF9USVRMRSwgYm9keTogREVGQVVMVF9QVVNIX0JPRFkgfSxcbiAgICBwYXJzZVBhdGgsXG4gICAgLi4uKGNhbXBhaWduID8geyBjYW1wYWlnbiB9IDoge30pLFxuICB9O1xufTtcblxuY29uc3QgaXNSZWNvcmQgPSAodjogdW5rbm93bik6IHYgaXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPT5cbiAgdHlwZW9mIHYgPT09ICdvYmplY3QnICYmIHYgIT09IG51bGwgJiYgIUFycmF5LmlzQXJyYXkodik7XG5cbmNvbnN0IGFzU3RyaW5nID0gKHY6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQgPT5cbiAgdHlwZW9mIHYgPT09ICdzdHJpbmcnICYmIHYubGVuZ3RoID4gMCA/IHYgOiB1bmRlZmluZWQ7XG5cbi8qKlxuICogRXh0cmFjdCBwcm9maWxlIHRhcmdldHMgZnJvbSB0aGUgY2Fub25pY2FsIGBJdGVtcy5DdXN0b21lclByb2ZpbGVzW11gIGFycmF5LlxuICogRWFjaCBlbnRyeSBjb250cmlidXRlcyBpdHMgYFByb2ZpbGVJZGAgKyBwYXJzZWQgYEN1c3RvbWVyRGF0YWA7IGVudHJpZXNcbiAqIHdpdGhvdXQgYSB1c2FibGUgYFByb2ZpbGVJZGAgYXJlIHNraXBwZWQuXG4gKi9cbmNvbnN0IGV4dHJhY3RUYXJnZXRzID0gKFxuICByb290OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbik6IHsgdGFyZ2V0czogUHJvZmlsZVRhcmdldFtdOyBwYXJzZVBhdGg6IFB1c2hFdmVudFBhcnNlUGF0aCB9ID0+IHtcbiAgY29uc3QgdGFyZ2V0czogUHJvZmlsZVRhcmdldFtdID0gW107XG5cbiAgY29uc3QgaXRlbXMgPSByb290Lkl0ZW1zO1xuICBjb25zdCBjdXN0b21lclByb2ZpbGVzID0gaXNSZWNvcmQoaXRlbXMpID8gaXRlbXMuQ3VzdG9tZXJQcm9maWxlcyA6IHVuZGVmaW5lZDtcbiAgaWYgKEFycmF5LmlzQXJyYXkoY3VzdG9tZXJQcm9maWxlcykpIHtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGN1c3RvbWVyUHJvZmlsZXMpIHtcbiAgICAgIGlmICghaXNSZWNvcmQoZW50cnkpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgY29uc3QgcHJvZmlsZUlkID0gYXNTdHJpbmcoZW50cnkuUHJvZmlsZUlkKTtcbiAgICAgIGlmICghcHJvZmlsZUlkKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgdGFyZ2V0cy5wdXNoKHtcbiAgICAgICAgcHJvZmlsZUlkLFxuICAgICAgICBjdXN0b21lckRhdGE6IGNvZXJjZUN1c3RvbWVyRGF0YShlbnRyeS5DdXN0b21lckRhdGEpLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHsgdGFyZ2V0cywgcGFyc2VQYXRoOiB0YXJnZXRzLmxlbmd0aCA+IDAgPyAnY2Fub25pY2FsJyA6ICdub25lJyB9O1xufTtcblxuLyoqXG4gKiBDb2VyY2UgYSByYXcgYEN1c3RvbWVyRGF0YWAgdmFsdWUgaW50byBhIHByb3BlcnR5IGJhZy5cbiAqXG4gKiBUaGUgcmVhbCBDb25uZWN0IGpvdXJuZXkgZGVsaXZlcnMgYEN1c3RvbWVyRGF0YWAgYXMgYSBTRVJJQUxJWkVEIEpTT04gU1RSSU5HXG4gKiAoY2FtZWxDYXNlIGtleXMpLCBzbyBhIHN0cmluZyBpcyBgSlNPTi5wYXJzZWBkLiBNaW5pbWFsIGRlZmVuc2l2ZSB0b2xlcmFuY2U6XG4gKiBhIHZhbHVlIGFscmVhZHkgZ2l2ZW4gYXMgYSBwbGFpbiBvYmplY3QgaXMgdXNlZCBhcy1pcy4gQW55dGhpbmcgZWxzZSDigJQgb3IgYVxuICogc3RyaW5nIHRoYXQgaXNuJ3QgdmFsaWQgSlNPTiwgb3IgSlNPTiB0aGF0IGlzbid0IGFuIG9iamVjdCDigJQgeWllbGRzXG4gKiBgdW5kZWZpbmVkYCBzbyBhIG1hbGZvcm1lZCBlbnRyeSBuZXZlciBhYm9ydHMgdGhlIGJhdGNoLlxuICovXG5jb25zdCBjb2VyY2VDdXN0b21lckRhdGEgPSAoXG4gIHJhdzogdW5rbm93bixcbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkID0+IHtcbiAgaWYgKHR5cGVvZiByYXcgPT09ICdzdHJpbmcnKSB7XG4gICAgY29uc3QgdHJpbW1lZCA9IHJhdy50cmltKCk7XG4gICAgaWYgKCF0cmltbWVkKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgcGFyc2VkOiB1bmtub3duID0gSlNPTi5wYXJzZSh0cmltbWVkKTtcbiAgICAgIHJldHVybiBpc1JlY29yZChwYXJzZWQpID8gcGFyc2VkIDogdW5kZWZpbmVkO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGlzUmVjb3JkKHJhdykgPyByYXcgOiB1bmRlZmluZWQ7XG59O1xuXG4vKipcbiAqIEV4dHJhY3QgdGhlIE91dGJvdW5kIENhbXBhaWducyB2MiBqb3VybmV5IGNvbnRleHQgZnJvbVxuICogYEludm9jYXRpb25NZXRhZGF0YS5DYW1wYWlnbkNvbnRleHRgLCB3aGVuIHByZXNlbnQuIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlblxuICogdGhlIGVudmVsb3BlIGNhcnJpZXMgbm8gY2FtcGFpZ24gbWV0YWRhdGEgKGUuZy4gZGlyZWN0LWludm9rZSB0ZXN0IHBheWxvYWRzKVxuICogb3Igd2hlbiBub25lIG9mIHRoZSBleHBlY3RlZCBmaWVsZHMgcmVzb2x2ZS5cbiAqL1xuY29uc3QgZXh0cmFjdENhbXBhaWduID0gKFxuICByb290OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbik6IENhbXBhaWduQ29udGV4dCB8IHVuZGVmaW5lZCA9PiB7XG4gIGNvbnN0IG1ldGEgPSByb290Lkludm9jYXRpb25NZXRhZGF0YTtcbiAgaWYgKCFpc1JlY29yZChtZXRhKSkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3QgY3R4ID0gbWV0YS5DYW1wYWlnbkNvbnRleHQ7XG4gIGlmICghaXNSZWNvcmQoY3R4KSkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3QgY2FtcGFpZ246IENhbXBhaWduQ29udGV4dCA9IHtcbiAgICBjYW1wYWlnbklkOiBhc1N0cmluZyhjdHguQ2FtcGFpZ25JZCksXG4gICAgY2FtcGFpZ25OYW1lOiBhc1N0cmluZyhjdHguQ2FtcGFpZ25OYW1lKSxcbiAgICBhY3Rpb25JZDogYXNTdHJpbmcoY3R4LkFjdGlvbklkKSxcbiAgICBydW5JZDogYXNTdHJpbmcoY3R4LlJ1bklkKSxcbiAgfTtcbiAgcmV0dXJuIE9iamVjdC52YWx1ZXMoY2FtcGFpZ24pLnNvbWUoKHYpID0+IHYgIT09IHVuZGVmaW5lZClcbiAgICA/IGNhbXBhaWduXG4gICAgOiB1bmRlZmluZWQ7XG59O1xuIl19
@@ -0,0 +1,19 @@
1
+ /**
2
+ * VERBATIM real Amazon Connect Outbound Campaigns v2 journey invocation event.
3
+ *
4
+ * Captured byte-for-byte from the `[push] rawEvent` CloudWatch log line of a
5
+ * live journey run:
6
+ * - log group: /aws/lambda/amplify-connectidentifyus-notificationsPushHandler-N0RSPJyrq5gp
7
+ * - RequestId: 78da5698-8f31-4662-bf7b-d69502347fde
8
+ * - CampaignName: journey-2 (CampaignId a87c62f3-e312-4b6d-9a0a-5b0cc8ef852b)
9
+ *
10
+ * This is the AUTHORITATIVE fixture for the real journey envelope: `Items` is an
11
+ * OBJECT { CustomerProfiles: [...] }, each entry's `CustomerData` is a
12
+ * SERIALIZED JSON STRING with camelCase keys, `ProfileId` is top-level
13
+ * PascalCase, and there is an `IdempotencyToken`. Do not hand-edit — re-capture
14
+ * from CloudWatch if the real shape changes.
15
+ */
16
+ export declare const REAL_JOURNEY_EVENT_RAW = "{\"InvocationMetadata\":{\"CampaignContext\":{\"CampaignId\":\"a87c62f3-e312-4b6d-9a0a-5b0cc8ef852b\",\"RunId\":\"a87c62f3-e312-4b6d-9a0a-5b0cc8ef852b#2026-07-03T10-04-00Z\",\"ActionId\":\"Push Notification\",\"CampaignName\":\"journey-2\"}},\"Items\":{\"CustomerProfiles\":[{\"ProfileId\":\"b1a19259aff1472fa4e4332b4f2ba441\",\"CustomerData\":\"{\\\"profileId\\\":\\\"b1a19259aff1472fa4e4332b4f2ba441\\\",\\\"attributes\\\":{\\\"cognitoSub\\\":\\\"push-e2e-sub-1783067414142\\\",\\\"deviceId\\\":\\\"dev-apns-1783067414142\\\"}}\",\"IdempotencyToken\":\"3709179dfad1fa981d2388a8c0fef99df26dd45b3c1725d097564239337c45e4\"},{\"ProfileId\":\"eb155c66aae14a10b775437c40a4e44d\",\"CustomerData\":\"{\\\"profileId\\\":\\\"eb155c66aae14a10b775437c40a4e44d\\\",\\\"firstName\\\":\\\"Manual\\\",\\\"lastName\\\":\\\"Tester\\\",\\\"emailAddress\\\":\\\"manual-test@example.com\\\",\\\"attributes\\\":{\\\"appUserId\\\":\\\"manual-test-user\\\",\\\"appVersion\\\":\\\"1.0.0\\\",\\\"cognitoSub\\\":\\\"74c824b8-9031-70bc-9e6c-e64660a34d32\\\",\\\"cognitoUserKey\\\":\\\"74c824b8-9031-70bc-9e6c-e64660a34d32\\\",\\\"deviceId\\\":\\\"manual-test-gcm-device\\\",\\\"hasAPNS\\\":\\\"true\\\",\\\"hasGCM\\\":\\\"true\\\",\\\"plan\\\":\\\"premium\\\",\\\"platform\\\":\\\"android\\\"}}\",\"IdempotencyToken\":\"2ec09dbd1477f39e1feeed32c9f6dc882a152d98d0096d2e36b5a9c900f05f00\"},{\"ProfileId\":\"980662c93bdd4527aeecbacc1aae296a\",\"CustomerData\":\"{\\\"profileId\\\":\\\"980662c93bdd4527aeecbacc1aae296a\\\",\\\"firstName\\\":\\\"Ada\\\",\\\"lastName\\\":\\\"Lovelace\\\",\\\"emailAddress\\\":\\\"ada@example.com\\\",\\\"address\\\":{\\\"city\\\":\\\"Seattle\\\",\\\"province\\\":\\\"WA\\\",\\\"country\\\":\\\"US\\\",\\\"postalCode\\\":\\\"98101\\\"},\\\"attributes\\\":{\\\"appUserId\\\":\\\"app-user-1\\\",\\\"appVersion\\\":\\\"1.2.3\\\",\\\"cognitoSub\\\":\\\"5498b4a8-30e1-7034-f334-15520531a361\\\",\\\"cognitoUserKey\\\":\\\"5498b4a8-30e1-7034-f334-15520531a361\\\",\\\"deviceId\\\":\\\"verify-device-0001\\\",\\\"hasAPNS\\\":\\\"true\\\",\\\"locale\\\":\\\"en_US\\\",\\\"plan\\\":\\\"premium\\\",\\\"platform\\\":\\\"ios\\\"}}\",\"IdempotencyToken\":\"64396f1aca4c1db9d2fb729102140a2e4250e4503659f1d047b9fca9987a7f38\"},{\"ProfileId\":\"594a41c0a6d84f46a56df716a3f62e7d\",\"CustomerData\":\"{\\\"profileId\\\":\\\"594a41c0a6d84f46a56df716a3f62e7d\\\",\\\"firstName\\\":\\\"Grace\\\",\\\"lastName\\\":\\\"Hopper\\\",\\\"emailAddress\\\":\\\"grace@example.com\\\",\\\"attributes\\\":{\\\"appUserId\\\":\\\"app-user-2\\\",\\\"cognitoSub\\\":\\\"b4780458-0061-70e3-a5a4-3b76098dc0bb\\\",\\\"cognitoUserKey\\\":\\\"b4780458-0061-70e3-a5a4-3b76098dc0bb\\\",\\\"deviceId\\\":\\\"verify-device-u2\\\",\\\"hasGCM\\\":\\\"true\\\",\\\"plan\\\":\\\"basic\\\"}}\",\"IdempotencyToken\":\"b7c8133e2ce51ed8aebb74ec65237f39090864df601fbac864fc8211e0564829\"}]}}";
17
+ /** The verbatim real journey event, parsed into a JS object. */
18
+ export declare const REAL_JOURNEY_EVENT: unknown;
19
+ //# sourceMappingURL=real_journey_event.d.ts.map
@@ -0,0 +1,22 @@
1
+ /* eslint-disable spellcheck/spell-checker -- verbatim captured Connect journey event (UUIDs, hashes, PII-shaped test data) */
2
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ /**
5
+ * VERBATIM real Amazon Connect Outbound Campaigns v2 journey invocation event.
6
+ *
7
+ * Captured byte-for-byte from the `[push] rawEvent` CloudWatch log line of a
8
+ * live journey run:
9
+ * - log group: /aws/lambda/amplify-connectidentifyus-notificationsPushHandler-N0RSPJyrq5gp
10
+ * - RequestId: 78da5698-8f31-4662-bf7b-d69502347fde
11
+ * - CampaignName: journey-2 (CampaignId a87c62f3-e312-4b6d-9a0a-5b0cc8ef852b)
12
+ *
13
+ * This is the AUTHORITATIVE fixture for the real journey envelope: `Items` is an
14
+ * OBJECT { CustomerProfiles: [...] }, each entry's `CustomerData` is a
15
+ * SERIALIZED JSON STRING with camelCase keys, `ProfileId` is top-level
16
+ * PascalCase, and there is an `IdempotencyToken`. Do not hand-edit — re-capture
17
+ * from CloudWatch if the real shape changes.
18
+ */
19
+ export const REAL_JOURNEY_EVENT_RAW = '{"InvocationMetadata":{"CampaignContext":{"CampaignId":"a87c62f3-e312-4b6d-9a0a-5b0cc8ef852b","RunId":"a87c62f3-e312-4b6d-9a0a-5b0cc8ef852b#2026-07-03T10-04-00Z","ActionId":"Push Notification","CampaignName":"journey-2"}},"Items":{"CustomerProfiles":[{"ProfileId":"b1a19259aff1472fa4e4332b4f2ba441","CustomerData":"{\\"profileId\\":\\"b1a19259aff1472fa4e4332b4f2ba441\\",\\"attributes\\":{\\"cognitoSub\\":\\"push-e2e-sub-1783067414142\\",\\"deviceId\\":\\"dev-apns-1783067414142\\"}}","IdempotencyToken":"3709179dfad1fa981d2388a8c0fef99df26dd45b3c1725d097564239337c45e4"},{"ProfileId":"eb155c66aae14a10b775437c40a4e44d","CustomerData":"{\\"profileId\\":\\"eb155c66aae14a10b775437c40a4e44d\\",\\"firstName\\":\\"Manual\\",\\"lastName\\":\\"Tester\\",\\"emailAddress\\":\\"manual-test@example.com\\",\\"attributes\\":{\\"appUserId\\":\\"manual-test-user\\",\\"appVersion\\":\\"1.0.0\\",\\"cognitoSub\\":\\"74c824b8-9031-70bc-9e6c-e64660a34d32\\",\\"cognitoUserKey\\":\\"74c824b8-9031-70bc-9e6c-e64660a34d32\\",\\"deviceId\\":\\"manual-test-gcm-device\\",\\"hasAPNS\\":\\"true\\",\\"hasGCM\\":\\"true\\",\\"plan\\":\\"premium\\",\\"platform\\":\\"android\\"}}","IdempotencyToken":"2ec09dbd1477f39e1feeed32c9f6dc882a152d98d0096d2e36b5a9c900f05f00"},{"ProfileId":"980662c93bdd4527aeecbacc1aae296a","CustomerData":"{\\"profileId\\":\\"980662c93bdd4527aeecbacc1aae296a\\",\\"firstName\\":\\"Ada\\",\\"lastName\\":\\"Lovelace\\",\\"emailAddress\\":\\"ada@example.com\\",\\"address\\":{\\"city\\":\\"Seattle\\",\\"province\\":\\"WA\\",\\"country\\":\\"US\\",\\"postalCode\\":\\"98101\\"},\\"attributes\\":{\\"appUserId\\":\\"app-user-1\\",\\"appVersion\\":\\"1.2.3\\",\\"cognitoSub\\":\\"5498b4a8-30e1-7034-f334-15520531a361\\",\\"cognitoUserKey\\":\\"5498b4a8-30e1-7034-f334-15520531a361\\",\\"deviceId\\":\\"verify-device-0001\\",\\"hasAPNS\\":\\"true\\",\\"locale\\":\\"en_US\\",\\"plan\\":\\"premium\\",\\"platform\\":\\"ios\\"}}","IdempotencyToken":"64396f1aca4c1db9d2fb729102140a2e4250e4503659f1d047b9fca9987a7f38"},{"ProfileId":"594a41c0a6d84f46a56df716a3f62e7d","CustomerData":"{\\"profileId\\":\\"594a41c0a6d84f46a56df716a3f62e7d\\",\\"firstName\\":\\"Grace\\",\\"lastName\\":\\"Hopper\\",\\"emailAddress\\":\\"grace@example.com\\",\\"attributes\\":{\\"appUserId\\":\\"app-user-2\\",\\"cognitoSub\\":\\"b4780458-0061-70e3-a5a4-3b76098dc0bb\\",\\"cognitoUserKey\\":\\"b4780458-0061-70e3-a5a4-3b76098dc0bb\\",\\"deviceId\\":\\"verify-device-u2\\",\\"hasGCM\\":\\"true\\",\\"plan\\":\\"basic\\"}}","IdempotencyToken":"b7c8133e2ce51ed8aebb74ec65237f39090864df601fbac864fc8211e0564829"}]}}';
20
+ /** The verbatim real journey event, parsed into a JS object. */
21
+ export const REAL_JOURNEY_EVENT = JSON.parse(REAL_JOURNEY_EVENT_RAW);
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhbF9qb3VybmV5X2V2ZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xhbWJkYS9wdXNoL2ZpeHR1cmVzL3JlYWxfam91cm5leV9ldmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSw4SEFBOEg7QUFDOUgscUVBQXFFO0FBQ3JFLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUNqQywyaEZBQTJoRixDQUFDO0FBRTloRixnRUFBZ0U7QUFDaEUsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQVksSUFBSSxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgc3BlbGxjaGVjay9zcGVsbC1jaGVja2VyIC0tIHZlcmJhdGltIGNhcHR1cmVkIENvbm5lY3Qgam91cm5leSBldmVudCAoVVVJRHMsIGhhc2hlcywgUElJLXNoYXBlZCB0ZXN0IGRhdGEpICovXG4vLyBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogVkVSQkFUSU0gcmVhbCBBbWF6b24gQ29ubmVjdCBPdXRib3VuZCBDYW1wYWlnbnMgdjIgam91cm5leSBpbnZvY2F0aW9uIGV2ZW50LlxuICpcbiAqIENhcHR1cmVkIGJ5dGUtZm9yLWJ5dGUgZnJvbSB0aGUgYFtwdXNoXSByYXdFdmVudGAgQ2xvdWRXYXRjaCBsb2cgbGluZSBvZiBhXG4gKiBsaXZlIGpvdXJuZXkgcnVuOlxuICogICAtIGxvZyBncm91cDogL2F3cy9sYW1iZGEvYW1wbGlmeS1jb25uZWN0aWRlbnRpZnl1cy1ub3RpZmljYXRpb25zUHVzaEhhbmRsZXItTjBSU1BKeXJxNWdwXG4gKiAgIC0gUmVxdWVzdElkOiAgNzhkYTU2OTgtOGYzMS00NjYyLWJmN2ItZDY5NTAyMzQ3ZmRlXG4gKiAgIC0gQ2FtcGFpZ25OYW1lOiBqb3VybmV5LTIgIChDYW1wYWlnbklkIGE4N2M2MmYzLWUzMTItNGI2ZC05YTBhLTViMGNjOGVmODUyYilcbiAqXG4gKiBUaGlzIGlzIHRoZSBBVVRIT1JJVEFUSVZFIGZpeHR1cmUgZm9yIHRoZSByZWFsIGpvdXJuZXkgZW52ZWxvcGU6IGBJdGVtc2AgaXMgYW5cbiAqIE9CSkVDVCB7IEN1c3RvbWVyUHJvZmlsZXM6IFsuLi5dIH0sIGVhY2ggZW50cnkncyBgQ3VzdG9tZXJEYXRhYCBpcyBhXG4gKiBTRVJJQUxJWkVEIEpTT04gU1RSSU5HIHdpdGggY2FtZWxDYXNlIGtleXMsIGBQcm9maWxlSWRgIGlzIHRvcC1sZXZlbFxuICogUGFzY2FsQ2FzZSwgYW5kIHRoZXJlIGlzIGFuIGBJZGVtcG90ZW5jeVRva2VuYC4gRG8gbm90IGhhbmQtZWRpdCDigJQgcmUtY2FwdHVyZVxuICogZnJvbSBDbG91ZFdhdGNoIGlmIHRoZSByZWFsIHNoYXBlIGNoYW5nZXMuXG4gKi9cbmV4cG9ydCBjb25zdCBSRUFMX0pPVVJORVlfRVZFTlRfUkFXID1cbiAgJ3tcIkludm9jYXRpb25NZXRhZGF0YVwiOntcIkNhbXBhaWduQ29udGV4dFwiOntcIkNhbXBhaWduSWRcIjpcImE4N2M2MmYzLWUzMTItNGI2ZC05YTBhLTViMGNjOGVmODUyYlwiLFwiUnVuSWRcIjpcImE4N2M2MmYzLWUzMTItNGI2ZC05YTBhLTViMGNjOGVmODUyYiMyMDI2LTA3LTAzVDEwLTA0LTAwWlwiLFwiQWN0aW9uSWRcIjpcIlB1c2ggTm90aWZpY2F0aW9uXCIsXCJDYW1wYWlnbk5hbWVcIjpcImpvdXJuZXktMlwifX0sXCJJdGVtc1wiOntcIkN1c3RvbWVyUHJvZmlsZXNcIjpbe1wiUHJvZmlsZUlkXCI6XCJiMWExOTI1OWFmZjE0NzJmYTRlNDMzMmI0ZjJiYTQ0MVwiLFwiQ3VzdG9tZXJEYXRhXCI6XCJ7XFxcXFwicHJvZmlsZUlkXFxcXFwiOlxcXFxcImIxYTE5MjU5YWZmMTQ3MmZhNGU0MzMyYjRmMmJhNDQxXFxcXFwiLFxcXFxcImF0dHJpYnV0ZXNcXFxcXCI6e1xcXFxcImNvZ25pdG9TdWJcXFxcXCI6XFxcXFwicHVzaC1lMmUtc3ViLTE3ODMwNjc0MTQxNDJcXFxcXCIsXFxcXFwiZGV2aWNlSWRcXFxcXCI6XFxcXFwiZGV2LWFwbnMtMTc4MzA2NzQxNDE0MlxcXFxcIn19XCIsXCJJZGVtcG90ZW5jeVRva2VuXCI6XCIzNzA5MTc5ZGZhZDFmYTk4MWQyMzg4YThjMGZlZjk5ZGYyNmRkNDViM2MxNzI1ZDA5NzU2NDIzOTMzN2M0NWU0XCJ9LHtcIlByb2ZpbGVJZFwiOlwiZWIxNTVjNjZhYWUxNGExMGI3NzU0MzdjNDBhNGU0NGRcIixcIkN1c3RvbWVyRGF0YVwiOlwie1xcXFxcInByb2ZpbGVJZFxcXFxcIjpcXFxcXCJlYjE1NWM2NmFhZTE0YTEwYjc3NTQzN2M0MGE0ZTQ0ZFxcXFxcIixcXFxcXCJmaXJzdE5hbWVcXFxcXCI6XFxcXFwiTWFudWFsXFxcXFwiLFxcXFxcImxhc3ROYW1lXFxcXFwiOlxcXFxcIlRlc3RlclxcXFxcIixcXFxcXCJlbWFpbEFkZHJlc3NcXFxcXCI6XFxcXFwibWFudWFsLXRlc3RAZXhhbXBsZS5jb21cXFxcXCIsXFxcXFwiYXR0cmlidXRlc1xcXFxcIjp7XFxcXFwiYXBwVXNlcklkXFxcXFwiOlxcXFxcIm1hbnVhbC10ZXN0LXVzZXJcXFxcXCIsXFxcXFwiYXBwVmVyc2lvblxcXFxcIjpcXFxcXCIxLjAuMFxcXFxcIixcXFxcXCJjb2duaXRvU3ViXFxcXFwiOlxcXFxcIjc0YzgyNGI4LTkwMzEtNzBiYy05ZTZjLWU2NDY2MGEzNGQzMlxcXFxcIixcXFxcXCJjb2duaXRvVXNlcktleVxcXFxcIjpcXFxcXCI3NGM4MjRiOC05MDMxLTcwYmMtOWU2Yy1lNjQ2NjBhMzRkMzJcXFxcXCIsXFxcXFwiZGV2aWNlSWRcXFxcXCI6XFxcXFwibWFudWFsLXRlc3QtZ2NtLWRldmljZVxcXFxcIixcXFxcXCJoYXNBUE5TXFxcXFwiOlxcXFxcInRydWVcXFxcXCIsXFxcXFwiaGFzR0NNXFxcXFwiOlxcXFxcInRydWVcXFxcXCIsXFxcXFwicGxhblxcXFxcIjpcXFxcXCJwcmVtaXVtXFxcXFwiLFxcXFxcInBsYXRmb3JtXFxcXFwiOlxcXFxcImFuZHJvaWRcXFxcXCJ9fVwiLFwiSWRlbXBvdGVuY3lUb2tlblwiOlwiMmVjMDlkYmQxNDc3ZjM5ZTFmZWVlZDMyYzlmNmRjODgyYTE1MmQ5OGQwMDk2ZDJlMzZiNWE5YzkwMGYwNWYwMFwifSx7XCJQcm9maWxlSWRcIjpcIjk4MDY2MmM5M2JkZDQ1MjdhZWVjYmFjYzFhYWUyOTZhXCIsXCJDdXN0b21lckRhdGFcIjpcIntcXFxcXCJwcm9maWxlSWRcXFxcXCI6XFxcXFwiOTgwNjYyYzkzYmRkNDUyN2FlZWNiYWNjMWFhZTI5NmFcXFxcXCIsXFxcXFwiZmlyc3ROYW1lXFxcXFwiOlxcXFxcIkFkYVxcXFxcIixcXFxcXCJsYXN0TmFtZVxcXFxcIjpcXFxcXCJMb3ZlbGFjZVxcXFxcIixcXFxcXCJlbWFpbEFkZHJlc3NcXFxcXCI6XFxcXFwiYWRhQGV4YW1wbGUuY29tXFxcXFwiLFxcXFxcImFkZHJlc3NcXFxcXCI6e1xcXFxcImNpdHlcXFxcXCI6XFxcXFwiU2VhdHRsZVxcXFxcIixcXFxcXCJwcm92aW5jZVxcXFxcIjpcXFxcXCJXQVxcXFxcIixcXFxcXCJjb3VudHJ5XFxcXFwiOlxcXFxcIlVTXFxcXFwiLFxcXFxcInBvc3RhbENvZGVcXFxcXCI6XFxcXFwiOTgxMDFcXFxcXCJ9LFxcXFxcImF0dHJpYnV0ZXNcXFxcXCI6e1xcXFxcImFwcFVzZXJJZFxcXFxcIjpcXFxcXCJhcHAtdXNlci0xXFxcXFwiLFxcXFxcImFwcFZlcnNpb25cXFxcXCI6XFxcXFwiMS4yLjNcXFxcXCIsXFxcXFwiY29nbml0b1N1YlxcXFxcIjpcXFxcXCI1NDk4YjRhOC0zMGUxLTcwMzQtZjMzNC0xNTUyMDUzMWEzNjFcXFxcXCIsXFxcXFwiY29nbml0b1VzZXJLZXlcXFxcXCI6XFxcXFwiNTQ5OGI0YTgtMzBlMS03MDM0LWYzMzQtMTU1MjA1MzFhMzYxXFxcXFwiLFxcXFxcImRldmljZUlkXFxcXFwiOlxcXFxcInZlcmlmeS1kZXZpY2UtMDAwMVxcXFxcIixcXFxcXCJoYXNBUE5TXFxcXFwiOlxcXFxcInRydWVcXFxcXCIsXFxcXFwibG9jYWxlXFxcXFwiOlxcXFxcImVuX1VTXFxcXFwiLFxcXFxcInBsYW5cXFxcXCI6XFxcXFwicHJlbWl1bVxcXFxcIixcXFxcXCJwbGF0Zm9ybVxcXFxcIjpcXFxcXCJpb3NcXFxcXCJ9fVwiLFwiSWRlbXBvdGVuY3lUb2tlblwiOlwiNjQzOTZmMWFjYTRjMWRiOWQyZmI3MjkxMDIxNDBhMmU0MjUwZTQ1MDM2NTlmMWQwNDdiOWZjYTk5ODdhN2YzOFwifSx7XCJQcm9maWxlSWRcIjpcIjU5NGE0MWMwYTZkODRmNDZhNTZkZjcxNmEzZjYyZTdkXCIsXCJDdXN0b21lckRhdGFcIjpcIntcXFxcXCJwcm9maWxlSWRcXFxcXCI6XFxcXFwiNTk0YTQxYzBhNmQ4NGY0NmE1NmRmNzE2YTNmNjJlN2RcXFxcXCIsXFxcXFwiZmlyc3ROYW1lXFxcXFwiOlxcXFxcIkdyYWNlXFxcXFwiLFxcXFxcImxhc3ROYW1lXFxcXFwiOlxcXFxcIkhvcHBlclxcXFxcIixcXFxcXCJlbWFpbEFkZHJlc3NcXFxcXCI6XFxcXFwiZ3JhY2VAZXhhbXBsZS5jb21cXFxcXCIsXFxcXFwiYXR0cmlidXRlc1xcXFxcIjp7XFxcXFwiYXBwVXNlcklkXFxcXFwiOlxcXFxcImFwcC11c2VyLTJcXFxcXCIsXFxcXFwiY29nbml0b1N1YlxcXFxcIjpcXFxcXCJiNDc4MDQ1OC0wMDYxLTcwZTMtYTVhNC0zYjc2MDk4ZGMwYmJcXFxcXCIsXFxcXFwiY29nbml0b1VzZXJLZXlcXFxcXCI6XFxcXFwiYjQ3ODA0NTgtMDA2MS03MGUzLWE1YTQtM2I3NjA5OGRjMGJiXFxcXFwiLFxcXFxcImRldmljZUlkXFxcXFwiOlxcXFxcInZlcmlmeS1kZXZpY2UtdTJcXFxcXCIsXFxcXFwiaGFzR0NNXFxcXFwiOlxcXFxcInRydWVcXFxcXCIsXFxcXFwicGxhblxcXFxcIjpcXFxcXCJiYXNpY1xcXFxcIn19XCIsXCJJZGVtcG90ZW5jeVRva2VuXCI6XCJiN2M4MTMzZTJjZTUxZWQ4YWViYjc0ZWM2NTIzN2YzOTA5MDg2NGRmNjAxZmJhYzg2NGZjODIxMWUwNTY0ODI5XCJ9XX19JztcblxuLyoqIFRoZSB2ZXJiYXRpbSByZWFsIGpvdXJuZXkgZXZlbnQsIHBhcnNlZCBpbnRvIGEgSlMgb2JqZWN0LiAqL1xuZXhwb3J0IGNvbnN0IFJFQUxfSk9VUk5FWV9FVkVOVDogdW5rbm93biA9IEpTT04ucGFyc2UoUkVBTF9KT1VSTkVZX0VWRU5UX1JBVyk7XG4iXX0=
@@ -0,0 +1,15 @@
1
+ import { PushDeliveryResponse } from './types.js';
2
+ /**
3
+ * Push-delivery Lambda invoked by an Amazon Connect Journey Custom-action
4
+ * (Invoke Lambda) block. Amazon Connect has no native mobile-push channel, so
5
+ * this Lambda bridges the gap: it resolves each targeted profile's registered
6
+ * devices from Customer Profiles (ListProfileObjects on the AmplifyDevice
7
+ * object type) and delivers the message to each device via AWS End User
8
+ * Messaging (Pinpoint `SendMessages`), mapping the device's channel to a
9
+ * GCM (Android/FCM) or APNS (iOS) payload. Tokens permanently rejected by the
10
+ * push provider are cleaned up by deleting their AmplifyDevice object.
11
+ *
12
+ * Returns a per-profile delivered / failed / cleaned summary.
13
+ */
14
+ export declare const handler: (event: unknown) => Promise<PushDeliveryResponse>;
15
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1,86 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { CustomerProfilesClient } from '@aws-sdk/client-customer-profiles';
4
+ import { PinpointClient } from '@aws-sdk/client-pinpoint';
5
+ import { ConnectCampaignsV2Client } from '@aws-sdk/client-connectcampaignsv2';
6
+ import { ConnectClient } from '@aws-sdk/client-connect';
7
+ import { QConnectClient } from '@aws-sdk/client-qconnect';
8
+ import { ENV_DOMAIN_NAME, ENV_EUM_APPLICATION_ID } from '../../constants.js';
9
+ import { parsePushEvent } from './event.js';
10
+ import { deliverToTargets } from './delivery.js';
11
+ import { resolvePushTemplateContext, } from './message_template.js';
12
+ /**
13
+ * Module-level clients so warm invocations reuse the connection pool. Region is
14
+ * resolved from the standard AWS_REGION Lambda environment variable.
15
+ *
16
+ * `campaigns` / `connect` / `qconnect` back runtime message-template resolution:
17
+ * discovering the Q in Connect knowledge base from the journey's campaign and
18
+ * rendering the PUSH template whose name matches the Custom-action ActionId.
19
+ */
20
+ const profiles = new CustomerProfilesClient({});
21
+ const pinpoint = new PinpointClient({});
22
+ const campaigns = new ConnectCampaignsV2Client({});
23
+ const connect = new ConnectClient({});
24
+ const qconnect = new QConnectClient({});
25
+ /**
26
+ * Push-delivery Lambda invoked by an Amazon Connect Journey Custom-action
27
+ * (Invoke Lambda) block. Amazon Connect has no native mobile-push channel, so
28
+ * this Lambda bridges the gap: it resolves each targeted profile's registered
29
+ * devices from Customer Profiles (ListProfileObjects on the AmplifyDevice
30
+ * object type) and delivers the message to each device via AWS End User
31
+ * Messaging (Pinpoint `SendMessages`), mapping the device's channel to a
32
+ * GCM (Android/FCM) or APNS (iOS) payload. Tokens permanently rejected by the
33
+ * push provider are cleaned up by deleting their AmplifyDevice object.
34
+ *
35
+ * Returns a per-profile delivered / failed / cleaned summary.
36
+ */
37
+ export const handler = async (event) => {
38
+ const domainName = process.env[ENV_DOMAIN_NAME];
39
+ const applicationId = process.env[ENV_EUM_APPLICATION_ID];
40
+ if (!domainName || !applicationId) {
41
+ throw new Error(`Missing required env var(s): ${[
42
+ !domainName ? ENV_DOMAIN_NAME : undefined,
43
+ !applicationId ? ENV_EUM_APPLICATION_ID : undefined,
44
+ ]
45
+ .filter(Boolean)
46
+ .join(', ')}`);
47
+ }
48
+ const parsed = parsePushEvent(event);
49
+ // Operational signals only: how the event was parsed, how many profiles it
50
+ // targeted, and the (non-personal) campaign / action identifiers. Profile ids
51
+ // and message copy are deliberately NOT logged.
52
+ console.log('[push] parsed', JSON.stringify({
53
+ parsePath: parsed.parsePath,
54
+ profileCount: parsed.targets.length,
55
+ campaign: parsed.campaign ?? null,
56
+ }));
57
+ if (parsed.targets.length === 0) {
58
+ console.warn('[push] no resolvable profile targets in event');
59
+ return {
60
+ profilesProcessed: 0,
61
+ totalDelivered: 0,
62
+ totalFailed: 0,
63
+ totalCleaned: 0,
64
+ results: [],
65
+ };
66
+ }
67
+ // Resolve the Q in Connect PUSH message template ONCE per invocation (KB
68
+ // discovery + template lookup are per-journey, not per-profile). When the
69
+ // event carries campaign metadata, the template whose name == the
70
+ // Custom-action ActionId is used to render personalized per-platform copy;
71
+ // otherwise (or on any miss/failure) delivery falls back to the safe DEFAULT
72
+ // copy.
73
+ let templateContext;
74
+ if (parsed.campaign) {
75
+ templateContext = await resolvePushTemplateContext({ campaigns, connect, qconnect }, parsed.campaign);
76
+ }
77
+ const summary = await deliverToTargets({ profiles, pinpoint, domainName, applicationId, templateContext }, parsed);
78
+ console.log('[push] summary', JSON.stringify({
79
+ profilesProcessed: summary.profilesProcessed,
80
+ totalDelivered: summary.totalDelivered,
81
+ totalFailed: summary.totalFailed,
82
+ totalCleaned: summary.totalCleaned,
83
+ }));
84
+ return summary;
85
+ };
86
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sYW1iZGEvcHVzaC9oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHFFQUFxRTtBQUNyRSxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDM0UsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzFELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFMUQsT0FBTyxFQUFFLGVBQWUsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDNUMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pELE9BQU8sRUFFTCwwQkFBMEIsR0FDM0IsTUFBTSx1QkFBdUIsQ0FBQztBQUcvQjs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxRQUFRLEdBQUcsSUFBSSxzQkFBc0IsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUNoRCxNQUFNLFFBQVEsR0FBRyxJQUFJLGNBQWMsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUN4QyxNQUFNLFNBQVMsR0FBRyxJQUFJLHdCQUF3QixDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ25ELE1BQU0sT0FBTyxHQUFHLElBQUksYUFBYSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLElBQUksY0FBYyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBRXhDOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLEtBQUssRUFDMUIsS0FBYyxFQUNpQixFQUFFO0lBQ2pDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDaEQsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNsQyxNQUFNLElBQUksS0FBSyxDQUNiLGdDQUFnQztZQUM5QixDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxTQUFTO1lBQ3pDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLENBQUMsU0FBUztTQUNwRDthQUNFLE1BQU0sQ0FBQyxPQUFPLENBQUM7YUFDZixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDaEIsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckMsMkVBQTJFO0lBQzNFLDhFQUE4RTtJQUM5RSxnREFBZ0Q7SUFDaEQsT0FBTyxDQUFDLEdBQUcsQ0FDVCxlQUFlLEVBQ2YsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUNiLFNBQVMsRUFBRSxNQUFNLENBQUMsU0FBUztRQUMzQixZQUFZLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNO1FBQ25DLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxJQUFJLElBQUk7S0FDbEMsQ0FBQyxDQUNILENBQUM7SUFFRixJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sQ0FBQyxJQUFJLENBQUMsK0NBQStDLENBQUMsQ0FBQztRQUM5RCxPQUFPO1lBQ0wsaUJBQWlCLEVBQUUsQ0FBQztZQUNwQixjQUFjLEVBQUUsQ0FBQztZQUNqQixXQUFXLEVBQUUsQ0FBQztZQUNkLFlBQVksRUFBRSxDQUFDO1lBQ2YsT0FBTyxFQUFFLEVBQUU7U0FDWixDQUFDO0lBQ0osQ0FBQztJQUVELHlFQUF5RTtJQUN6RSwwRUFBMEU7SUFDMUUsa0VBQWtFO0lBQ2xFLDJFQUEyRTtJQUMzRSw2RUFBNkU7SUFDN0UsUUFBUTtJQUNSLElBQUksZUFBZ0QsQ0FBQztJQUNyRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNwQixlQUFlLEdBQUcsTUFBTSwwQkFBMEIsQ0FDaEQsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxFQUNoQyxNQUFNLENBQUMsUUFBUSxDQUNoQixDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0sT0FBTyxHQUFHLE1BQU0sZ0JBQWdCLENBQ3BDLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLGVBQWUsRUFBRSxFQUNsRSxNQUFNLENBQ1AsQ0FBQztJQUVGLE9BQU8sQ0FBQyxHQUFHLENBQ1QsZ0JBQWdCLEVBQ2hCLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDYixpQkFBaUIsRUFBRSxPQUFPLENBQUMsaUJBQWlCO1FBQzVDLGNBQWMsRUFBRSxPQUFPLENBQUMsY0FBYztRQUN0QyxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVc7UUFDaEMsWUFBWSxFQUFFLE9BQU8sQ0FBQyxZQUFZO0tBQ25DLENBQUMsQ0FDSCxDQUFDO0lBRUYsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IEFtYXpvbi5jb20sIEluYy4gb3IgaXRzIGFmZmlsaWF0ZXMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBDdXN0b21lclByb2ZpbGVzQ2xpZW50IH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWN1c3RvbWVyLXByb2ZpbGVzJztcbmltcG9ydCB7IFBpbnBvaW50Q2xpZW50IH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXBpbnBvaW50JztcbmltcG9ydCB7IENvbm5lY3RDYW1wYWlnbnNWMkNsaWVudCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jb25uZWN0Y2FtcGFpZ25zdjInO1xuaW1wb3J0IHsgQ29ubmVjdENsaWVudCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jb25uZWN0JztcbmltcG9ydCB7IFFDb25uZWN0Q2xpZW50IH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXFjb25uZWN0JztcblxuaW1wb3J0IHsgRU5WX0RPTUFJTl9OQU1FLCBFTlZfRVVNX0FQUExJQ0FUSU9OX0lEIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IHBhcnNlUHVzaEV2ZW50IH0gZnJvbSAnLi9ldmVudC5qcyc7XG5pbXBvcnQgeyBkZWxpdmVyVG9UYXJnZXRzIH0gZnJvbSAnLi9kZWxpdmVyeS5qcyc7XG5pbXBvcnQge1xuICBQdXNoVGVtcGxhdGVDb250ZXh0LFxuICByZXNvbHZlUHVzaFRlbXBsYXRlQ29udGV4dCxcbn0gZnJvbSAnLi9tZXNzYWdlX3RlbXBsYXRlLmpzJztcbmltcG9ydCB7IFB1c2hEZWxpdmVyeVJlc3BvbnNlIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbi8qKlxuICogTW9kdWxlLWxldmVsIGNsaWVudHMgc28gd2FybSBpbnZvY2F0aW9ucyByZXVzZSB0aGUgY29ubmVjdGlvbiBwb29sLiBSZWdpb24gaXNcbiAqIHJlc29sdmVkIGZyb20gdGhlIHN0YW5kYXJkIEFXU19SRUdJT04gTGFtYmRhIGVudmlyb25tZW50IHZhcmlhYmxlLlxuICpcbiAqIGBjYW1wYWlnbnNgIC8gYGNvbm5lY3RgIC8gYHFjb25uZWN0YCBiYWNrIHJ1bnRpbWUgbWVzc2FnZS10ZW1wbGF0ZSByZXNvbHV0aW9uOlxuICogZGlzY292ZXJpbmcgdGhlIFEgaW4gQ29ubmVjdCBrbm93bGVkZ2UgYmFzZSBmcm9tIHRoZSBqb3VybmV5J3MgY2FtcGFpZ24gYW5kXG4gKiByZW5kZXJpbmcgdGhlIFBVU0ggdGVtcGxhdGUgd2hvc2UgbmFtZSBtYXRjaGVzIHRoZSBDdXN0b20tYWN0aW9uIEFjdGlvbklkLlxuICovXG5jb25zdCBwcm9maWxlcyA9IG5ldyBDdXN0b21lclByb2ZpbGVzQ2xpZW50KHt9KTtcbmNvbnN0IHBpbnBvaW50ID0gbmV3IFBpbnBvaW50Q2xpZW50KHt9KTtcbmNvbnN0IGNhbXBhaWducyA9IG5ldyBDb25uZWN0Q2FtcGFpZ25zVjJDbGllbnQoe30pO1xuY29uc3QgY29ubmVjdCA9IG5ldyBDb25uZWN0Q2xpZW50KHt9KTtcbmNvbnN0IHFjb25uZWN0ID0gbmV3IFFDb25uZWN0Q2xpZW50KHt9KTtcblxuLyoqXG4gKiBQdXNoLWRlbGl2ZXJ5IExhbWJkYSBpbnZva2VkIGJ5IGFuIEFtYXpvbiBDb25uZWN0IEpvdXJuZXkgQ3VzdG9tLWFjdGlvblxuICogKEludm9rZSBMYW1iZGEpIGJsb2NrLiBBbWF6b24gQ29ubmVjdCBoYXMgbm8gbmF0aXZlIG1vYmlsZS1wdXNoIGNoYW5uZWwsIHNvXG4gKiB0aGlzIExhbWJkYSBicmlkZ2VzIHRoZSBnYXA6IGl0IHJlc29sdmVzIGVhY2ggdGFyZ2V0ZWQgcHJvZmlsZSdzIHJlZ2lzdGVyZWRcbiAqIGRldmljZXMgZnJvbSBDdXN0b21lciBQcm9maWxlcyAoTGlzdFByb2ZpbGVPYmplY3RzIG9uIHRoZSBBbXBsaWZ5RGV2aWNlXG4gKiBvYmplY3QgdHlwZSkgYW5kIGRlbGl2ZXJzIHRoZSBtZXNzYWdlIHRvIGVhY2ggZGV2aWNlIHZpYSBBV1MgRW5kIFVzZXJcbiAqIE1lc3NhZ2luZyAoUGlucG9pbnQgYFNlbmRNZXNzYWdlc2ApLCBtYXBwaW5nIHRoZSBkZXZpY2UncyBjaGFubmVsIHRvIGFcbiAqIEdDTSAoQW5kcm9pZC9GQ00pIG9yIEFQTlMgKGlPUykgcGF5bG9hZC4gVG9rZW5zIHBlcm1hbmVudGx5IHJlamVjdGVkIGJ5IHRoZVxuICogcHVzaCBwcm92aWRlciBhcmUgY2xlYW5lZCB1cCBieSBkZWxldGluZyB0aGVpciBBbXBsaWZ5RGV2aWNlIG9iamVjdC5cbiAqXG4gKiBSZXR1cm5zIGEgcGVyLXByb2ZpbGUgZGVsaXZlcmVkIC8gZmFpbGVkIC8gY2xlYW5lZCBzdW1tYXJ5LlxuICovXG5leHBvcnQgY29uc3QgaGFuZGxlciA9IGFzeW5jIChcbiAgZXZlbnQ6IHVua25vd24sXG4pOiBQcm9taXNlPFB1c2hEZWxpdmVyeVJlc3BvbnNlPiA9PiB7XG4gIGNvbnN0IGRvbWFpbk5hbWUgPSBwcm9jZXNzLmVudltFTlZfRE9NQUlOX05BTUVdO1xuICBjb25zdCBhcHBsaWNhdGlvbklkID0gcHJvY2Vzcy5lbnZbRU5WX0VVTV9BUFBMSUNBVElPTl9JRF07XG4gIGlmICghZG9tYWluTmFtZSB8fCAhYXBwbGljYXRpb25JZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBNaXNzaW5nIHJlcXVpcmVkIGVudiB2YXIocyk6ICR7W1xuICAgICAgICAhZG9tYWluTmFtZSA/IEVOVl9ET01BSU5fTkFNRSA6IHVuZGVmaW5lZCxcbiAgICAgICAgIWFwcGxpY2F0aW9uSWQgPyBFTlZfRVVNX0FQUExJQ0FUSU9OX0lEIDogdW5kZWZpbmVkLFxuICAgICAgXVxuICAgICAgICAuZmlsdGVyKEJvb2xlYW4pXG4gICAgICAgIC5qb2luKCcsICcpfWAsXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlUHVzaEV2ZW50KGV2ZW50KTtcbiAgLy8gT3BlcmF0aW9uYWwgc2lnbmFscyBvbmx5OiBob3cgdGhlIGV2ZW50IHdhcyBwYXJzZWQsIGhvdyBtYW55IHByb2ZpbGVzIGl0XG4gIC8vIHRhcmdldGVkLCBhbmQgdGhlIChub24tcGVyc29uYWwpIGNhbXBhaWduIC8gYWN0aW9uIGlkZW50aWZpZXJzLiBQcm9maWxlIGlkc1xuICAvLyBhbmQgbWVzc2FnZSBjb3B5IGFyZSBkZWxpYmVyYXRlbHkgTk9UIGxvZ2dlZC5cbiAgY29uc29sZS5sb2coXG4gICAgJ1twdXNoXSBwYXJzZWQnLFxuICAgIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIHBhcnNlUGF0aDogcGFyc2VkLnBhcnNlUGF0aCxcbiAgICAgIHByb2ZpbGVDb3VudDogcGFyc2VkLnRhcmdldHMubGVuZ3RoLFxuICAgICAgY2FtcGFpZ246IHBhcnNlZC5jYW1wYWlnbiA/PyBudWxsLFxuICAgIH0pLFxuICApO1xuXG4gIGlmIChwYXJzZWQudGFyZ2V0cy5sZW5ndGggPT09IDApIHtcbiAgICBjb25zb2xlLndhcm4oJ1twdXNoXSBubyByZXNvbHZhYmxlIHByb2ZpbGUgdGFyZ2V0cyBpbiBldmVudCcpO1xuICAgIHJldHVybiB7XG4gICAgICBwcm9maWxlc1Byb2Nlc3NlZDogMCxcbiAgICAgIHRvdGFsRGVsaXZlcmVkOiAwLFxuICAgICAgdG90YWxGYWlsZWQ6IDAsXG4gICAgICB0b3RhbENsZWFuZWQ6IDAsXG4gICAgICByZXN1bHRzOiBbXSxcbiAgICB9O1xuICB9XG5cbiAgLy8gUmVzb2x2ZSB0aGUgUSBpbiBDb25uZWN0IFBVU0ggbWVzc2FnZSB0ZW1wbGF0ZSBPTkNFIHBlciBpbnZvY2F0aW9uIChLQlxuICAvLyBkaXNjb3ZlcnkgKyB0ZW1wbGF0ZSBsb29rdXAgYXJlIHBlci1qb3VybmV5LCBub3QgcGVyLXByb2ZpbGUpLiBXaGVuIHRoZVxuICAvLyBldmVudCBjYXJyaWVzIGNhbXBhaWduIG1ldGFkYXRhLCB0aGUgdGVtcGxhdGUgd2hvc2UgbmFtZSA9PSB0aGVcbiAgLy8gQ3VzdG9tLWFjdGlvbiBBY3Rpb25JZCBpcyB1c2VkIHRvIHJlbmRlciBwZXJzb25hbGl6ZWQgcGVyLXBsYXRmb3JtIGNvcHk7XG4gIC8vIG90aGVyd2lzZSAob3Igb24gYW55IG1pc3MvZmFpbHVyZSkgZGVsaXZlcnkgZmFsbHMgYmFjayB0byB0aGUgc2FmZSBERUZBVUxUXG4gIC8vIGNvcHkuXG4gIGxldCB0ZW1wbGF0ZUNvbnRleHQ6IFB1c2hUZW1wbGF0ZUNvbnRleHQgfCB1bmRlZmluZWQ7XG4gIGlmIChwYXJzZWQuY2FtcGFpZ24pIHtcbiAgICB0ZW1wbGF0ZUNvbnRleHQgPSBhd2FpdCByZXNvbHZlUHVzaFRlbXBsYXRlQ29udGV4dChcbiAgICAgIHsgY2FtcGFpZ25zLCBjb25uZWN0LCBxY29ubmVjdCB9LFxuICAgICAgcGFyc2VkLmNhbXBhaWduLFxuICAgICk7XG4gIH1cblxuICBjb25zdCBzdW1tYXJ5ID0gYXdhaXQgZGVsaXZlclRvVGFyZ2V0cyhcbiAgICB7IHByb2ZpbGVzLCBwaW5wb2ludCwgZG9tYWluTmFtZSwgYXBwbGljYXRpb25JZCwgdGVtcGxhdGVDb250ZXh0IH0sXG4gICAgcGFyc2VkLFxuICApO1xuXG4gIGNvbnNvbGUubG9nKFxuICAgICdbcHVzaF0gc3VtbWFyeScsXG4gICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgcHJvZmlsZXNQcm9jZXNzZWQ6IHN1bW1hcnkucHJvZmlsZXNQcm9jZXNzZWQsXG4gICAgICB0b3RhbERlbGl2ZXJlZDogc3VtbWFyeS50b3RhbERlbGl2ZXJlZCxcbiAgICAgIHRvdGFsRmFpbGVkOiBzdW1tYXJ5LnRvdGFsRmFpbGVkLFxuICAgICAgdG90YWxDbGVhbmVkOiBzdW1tYXJ5LnRvdGFsQ2xlYW5lZCxcbiAgICB9KSxcbiAgKTtcblxuICByZXR1cm4gc3VtbWFyeTtcbn07XG4iXX0=
@@ -0,0 +1,72 @@
1
+ import { ConnectCampaignsV2Client } from '@aws-sdk/client-connectcampaignsv2';
2
+ import { ConnectClient } from '@aws-sdk/client-connect';
3
+ import { QConnectClient } from '@aws-sdk/client-qconnect';
4
+ import { CampaignContext, ProfileTarget, PushChannelType, PushMessage } from './types.js';
5
+ /** The AWS clients the template resolver needs. */
6
+ export type TemplateClients = {
7
+ campaigns: ConnectCampaignsV2Client;
8
+ connect: ConnectClient;
9
+ qconnect: QConnectClient;
10
+ };
11
+ /**
12
+ * A resolved Q in Connect PUSH message template for one journey run: the
13
+ * knowledge base + the specific template id to render per profile. Produced by
14
+ * {@link resolvePushTemplateContext} and carried on the delivery deps so
15
+ * {@link renderProfileChannelMessages} can render personalized copy per profile.
16
+ */
17
+ export type PushTemplateContext = {
18
+ qconnect: QConnectClient;
19
+ /** The discovered Q in Connect knowledge base id. */
20
+ knowledgeBaseId: string;
21
+ /** The PUSH message template id whose name matched the journey ActionId. */
22
+ messageTemplateId: string;
23
+ /** The template name (== the Custom-action block name / ActionId). */
24
+ templateName: string;
25
+ };
26
+ /**
27
+ * Discover the Q in Connect knowledge base backing a campaign's Connect
28
+ * instance, caching per container:
29
+ *
30
+ * CampaignId -> connectcampaignsv2 DescribeCampaign -> campaign.connectInstanceId
31
+ * -> connect ListIntegrationAssociations(Q_MESSAGE_TEMPLATES)
32
+ * -> IntegrationArn (arn:aws:wisdom:...:knowledge-base/<kbId>) -> kbId
33
+ *
34
+ * Returns `undefined` (never throws) when the instance id or the KB integration
35
+ * cannot be resolved — the caller then falls back to non-templated copy. If
36
+ * DescribeCampaign does not carry `connectInstanceId`, that is logged so the
37
+ * real shape can be inspected.
38
+ */
39
+ export declare const discoverKnowledgeBaseId: (clients: Pick<TemplateClients, "campaigns" | "connect">, campaignId: string) => Promise<string | undefined>;
40
+ /**
41
+ * Resolve the PUSH message template for a journey run: discover the knowledge
42
+ * base from the campaign, find the PUSH template whose name == the ActionId,
43
+ * and confirm it has an ACTIVE version. Returns `undefined` (never throws) when
44
+ * anything is missing — no campaign metadata, no KB integration, no matching
45
+ * template — so the caller falls back to the non-templated per-profile copy.
46
+ */
47
+ export declare const resolvePushTemplateContext: (clients: TemplateClients, campaign: CampaignContext) => Promise<PushTemplateContext | undefined>;
48
+ /**
49
+ * Flatten a profile's `CustomerData` into a flat `customAttributes` map for
50
+ * RenderMessageTemplate. Empirically only `{{Attributes.<flatKey>}}` variables
51
+ * interpolate (from `customAttributes`); the `{{Customer.*}}` namespace does not
52
+ * resolve here — so the profile's `firstName` and every `attributes.*` entry are
53
+ * passed as flat custom attributes (templates reference e.g.
54
+ * `{{Attributes.firstName}}`). Nested objects (`address`, `attributes`) are not
55
+ * added directly; `attributes` is merged one level deep. Non-string / empty
56
+ * values are dropped and values are capped at the render attribute-length limit.
57
+ */
58
+ export declare const buildCustomAttributes: (customerData?: Record<string, unknown>) => Record<string, string>;
59
+ /**
60
+ * Render the resolved PUSH template for ONE profile and map the per-platform
61
+ * copy to push channels:
62
+ * - `push.apns` -> `APNS` (+ `APNS_SANDBOX`)
63
+ * - `push.fcm` -> `GCM`
64
+ *
65
+ * The profile's `CustomerData` is passed as flat `customAttributes` so
66
+ * `{{Attributes.<key>}}` variables personalize. Returns a partial per-channel
67
+ * map (only channels the template defines both a title and body for), or
68
+ * `undefined` (never throws) when there is no push content or the render fails —
69
+ * the caller then falls back to the non-templated copy for that profile.
70
+ */
71
+ export declare const renderProfileChannelMessages: (ctx: PushTemplateContext, target: ProfileTarget) => Promise<Partial<Record<PushChannelType, PushMessage>> | undefined>;
72
+ //# sourceMappingURL=message_template.d.ts.map