@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,59 @@
1
+ import { IAMClient } from '@aws-sdk/client-iam';
2
+ import { ConnectCampaignsV2Client } from '@aws-sdk/client-connectcampaignsv2';
3
+ import { CustomerProfilesClient } from '@aws-sdk/client-customer-profiles';
4
+ /** Customer Profiles domain ARN for a domain name in the deploy account/region. */
5
+ export declare const domainArnOf: (account: string, region: string, domainName: string) => string;
6
+ /** connect-campaigns instance ARN used as the Customer Profiles integration Uri. */
7
+ export declare const campaignsInstanceArnOf: (account: string, region: string, connectInstanceId: string) => string;
8
+ /**
9
+ * Resolve the Outbound Campaigns service-linked role ARN
10
+ * (`AWSServiceRoleForConnectCampaigns_*`), which StartInstanceOnboardingJob
11
+ * auto-creates under `/aws-service-role/connect-campaigns.amazonaws.com/`. The
12
+ * newest role (last in the returned list) is used, matching the empirically
13
+ * verified recipe.
14
+ */
15
+ export declare const resolveCampaignsServiceLinkedRoleArn: (iam: IAMClient) => Promise<string>;
16
+ /**
17
+ * Associate the (created) Customer Profiles domain with the Connect instance's
18
+ * Outbound Campaigns v2 so Connect Journeys can target the domain's profiles:
19
+ * 1. connectcampaignsv2:PutConnectInstanceIntegration — binds the domain ARN.
20
+ * 2. customer-profiles:PutIntegration — the reciprocal binding, using the
21
+ * campaigns service-linked role.
22
+ * Succeeds on an EMPTY domain: `objectTypeNames` is a label/routing map to
23
+ * BUILT-IN templates, so no object types need pre-creating.
24
+ */
25
+ export declare const associateDomain: (clients: {
26
+ campaigns: ConnectCampaignsV2Client;
27
+ profiles: CustomerProfilesClient;
28
+ iam: IAMClient;
29
+ }, args: {
30
+ connectInstanceId: string;
31
+ domainName: string;
32
+ roleArn: string;
33
+ account: string;
34
+ region: string;
35
+ }) => Promise<void>;
36
+ /**
37
+ * Best-effort reverse of {@link associateDomain} on stack teardown. Each step is
38
+ * idempotent and swallows errors (logged) so a missing / already-removed
39
+ * integration — e.g. when CloudFormation deleted the parent instance / domain
40
+ * FIRST — NEVER fails teardown. Reverses everything onCreate did:
41
+ * 1. connectcampaignsv2:DeleteConnectInstanceIntegration (campaigns -> domain)
42
+ * 2. customer-profiles:DeleteIntegration (domain -> campaigns)
43
+ * 3. connectcampaignsv2:DeleteInstanceOnboardingJob (offboard from Campaigns v2)
44
+ *
45
+ * The `AWSServiceRoleForConnectCampaigns_*` service-linked role is deliberately
46
+ * NOT deleted: it is account-wide, shared across ALL Connect instances /
47
+ * campaigns in the account, free, and harmless — deleting it could break other
48
+ * instances still onboarded to Outbound Campaigns.
49
+ */
50
+ export declare const disassociateDomain: (clients: {
51
+ campaigns: ConnectCampaignsV2Client;
52
+ profiles: CustomerProfilesClient;
53
+ }, args: {
54
+ connectInstanceId: string;
55
+ domainName: string;
56
+ account: string;
57
+ region: string;
58
+ }) => Promise<void>;
59
+ //# sourceMappingURL=integration.d.ts.map
@@ -0,0 +1,108 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { ListRolesCommand } from '@aws-sdk/client-iam';
4
+ import { DeleteConnectInstanceIntegrationCommand, DeleteInstanceOnboardingJobCommand, PutConnectInstanceIntegrationCommand, } from '@aws-sdk/client-connectcampaignsv2';
5
+ import { DeleteIntegrationCommand, PutIntegrationCommand, } from '@aws-sdk/client-customer-profiles';
6
+ import { CAMPAIGN_OBJECT_TYPE_NAMES, CONNECT_CAMPAIGNS_SLR_PATH_PREFIX, } from '../../constants.js';
7
+ /** Object-type routing map supplied to both integration calls. */
8
+ const objectTypeNames = { ...CAMPAIGN_OBJECT_TYPE_NAMES };
9
+ /** Customer Profiles domain ARN for a domain name in the deploy account/region. */
10
+ export const domainArnOf = (account, region, domainName) => `arn:aws:profile:${region}:${account}:domains/${domainName}`;
11
+ /** connect-campaigns instance ARN used as the Customer Profiles integration Uri. */
12
+ export const campaignsInstanceArnOf = (account, region, connectInstanceId) => `arn:aws:connect-campaigns:${region}:${account}:instance/${connectInstanceId}`;
13
+ /**
14
+ * Resolve the Outbound Campaigns service-linked role ARN
15
+ * (`AWSServiceRoleForConnectCampaigns_*`), which StartInstanceOnboardingJob
16
+ * auto-creates under `/aws-service-role/connect-campaigns.amazonaws.com/`. The
17
+ * newest role (last in the returned list) is used, matching the empirically
18
+ * verified recipe.
19
+ */
20
+ export const resolveCampaignsServiceLinkedRoleArn = async (iam) => {
21
+ const { Roles: roles } = await iam.send(new ListRolesCommand({ PathPrefix: CONNECT_CAMPAIGNS_SLR_PATH_PREFIX }));
22
+ const arn = roles?.[roles.length - 1]?.Arn;
23
+ if (!arn) {
24
+ throw new Error('Outbound Campaigns service-linked role not found after onboarding');
25
+ }
26
+ return arn;
27
+ };
28
+ /**
29
+ * Associate the (created) Customer Profiles domain with the Connect instance's
30
+ * Outbound Campaigns v2 so Connect Journeys can target the domain's profiles:
31
+ * 1. connectcampaignsv2:PutConnectInstanceIntegration — binds the domain ARN.
32
+ * 2. customer-profiles:PutIntegration — the reciprocal binding, using the
33
+ * campaigns service-linked role.
34
+ * Succeeds on an EMPTY domain: `objectTypeNames` is a label/routing map to
35
+ * BUILT-IN templates, so no object types need pre-creating.
36
+ */
37
+ export const associateDomain = async (clients, args) => {
38
+ const { campaigns, profiles } = clients;
39
+ const { connectInstanceId, domainName, roleArn, account, region } = args;
40
+ const domainArn = domainArnOf(account, region, domainName);
41
+ await campaigns.send(new PutConnectInstanceIntegrationCommand({
42
+ connectInstanceId,
43
+ integrationConfig: {
44
+ customerProfiles: { domainArn, objectTypeNames },
45
+ },
46
+ }));
47
+ console.log('[campaign-assoc] connect instance integration created', {
48
+ connectInstanceId,
49
+ domainArn,
50
+ });
51
+ await profiles.send(new PutIntegrationCommand({
52
+ DomainName: domainName,
53
+ Uri: campaignsInstanceArnOf(account, region, connectInstanceId),
54
+ ObjectTypeNames: objectTypeNames,
55
+ RoleArn: roleArn,
56
+ }));
57
+ console.log('[campaign-assoc] customer profiles integration created', {
58
+ domainName,
59
+ connectInstanceId,
60
+ });
61
+ };
62
+ /**
63
+ * Best-effort reverse of {@link associateDomain} on stack teardown. Each step is
64
+ * idempotent and swallows errors (logged) so a missing / already-removed
65
+ * integration — e.g. when CloudFormation deleted the parent instance / domain
66
+ * FIRST — NEVER fails teardown. Reverses everything onCreate did:
67
+ * 1. connectcampaignsv2:DeleteConnectInstanceIntegration (campaigns -> domain)
68
+ * 2. customer-profiles:DeleteIntegration (domain -> campaigns)
69
+ * 3. connectcampaignsv2:DeleteInstanceOnboardingJob (offboard from Campaigns v2)
70
+ *
71
+ * The `AWSServiceRoleForConnectCampaigns_*` service-linked role is deliberately
72
+ * NOT deleted: it is account-wide, shared across ALL Connect instances /
73
+ * campaigns in the account, free, and harmless — deleting it could break other
74
+ * instances still onboarded to Outbound Campaigns.
75
+ */
76
+ export const disassociateDomain = async (clients, args) => {
77
+ const { campaigns, profiles } = clients;
78
+ const { connectInstanceId, domainName, account, region } = args;
79
+ await bestEffort('connect instance integration delete', () => campaigns.send(new DeleteConnectInstanceIntegrationCommand({
80
+ connectInstanceId,
81
+ integrationIdentifier: {
82
+ customerProfiles: {
83
+ domainArn: domainArnOf(account, region, domainName),
84
+ },
85
+ },
86
+ })));
87
+ await bestEffort('customer profiles integration delete', () => profiles.send(new DeleteIntegrationCommand({
88
+ DomainName: domainName,
89
+ Uri: campaignsInstanceArnOf(account, region, connectInstanceId),
90
+ })));
91
+ await bestEffort('instance offboarding', () => campaigns.send(new DeleteInstanceOnboardingJobCommand({ connectInstanceId })));
92
+ };
93
+ /**
94
+ * Run a teardown step, swallowing (and logging) any error so a partial /
95
+ * already-removed resource never fails CloudFormation teardown.
96
+ */
97
+ const bestEffort = async (label, fn) => {
98
+ try {
99
+ await fn();
100
+ console.log(`[campaign-assoc] ${label} ok`);
101
+ }
102
+ catch (err) {
103
+ console.warn(`[campaign-assoc] ${label} skipped`, {
104
+ error: err?.name ?? 'unknown',
105
+ });
106
+ }
107
+ };
108
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZWdyYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGFtYmRhL2NhbXBhaWduLWFzc29jaWF0aW9uL2ludGVncmF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHFFQUFxRTtBQUNyRSxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFhLGdCQUFnQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEUsT0FBTyxFQUVMLHVDQUF1QyxFQUN2QyxrQ0FBa0MsRUFDbEMsb0NBQW9DLEdBQ3JDLE1BQU0sb0NBQW9DLENBQUM7QUFDNUMsT0FBTyxFQUVMLHdCQUF3QixFQUN4QixxQkFBcUIsR0FDdEIsTUFBTSxtQ0FBbUMsQ0FBQztBQUUzQyxPQUFPLEVBQ0wsMEJBQTBCLEVBQzFCLGlDQUFpQyxHQUNsQyxNQUFNLG9CQUFvQixDQUFDO0FBRTVCLGtFQUFrRTtBQUNsRSxNQUFNLGVBQWUsR0FBRyxFQUFFLEdBQUcsMEJBQTBCLEVBQUUsQ0FBQztBQUUxRCxtRkFBbUY7QUFDbkYsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLENBQ3pCLE9BQWUsRUFDZixNQUFjLEVBQ2QsVUFBa0IsRUFDVixFQUFFLENBQUMsbUJBQW1CLE1BQU0sSUFBSSxPQUFPLFlBQVksVUFBVSxFQUFFLENBQUM7QUFFMUUsb0ZBQW9GO0FBQ3BGLE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLENBQ3BDLE9BQWUsRUFDZixNQUFjLEVBQ2QsaUJBQXlCLEVBQ2pCLEVBQUUsQ0FDViw2QkFBNkIsTUFBTSxJQUFJLE9BQU8sYUFBYSxpQkFBaUIsRUFBRSxDQUFDO0FBRWpGOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLG9DQUFvQyxHQUFHLEtBQUssRUFDdkQsR0FBYyxFQUNHLEVBQUU7SUFDbkIsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQ3JDLElBQUksZ0JBQWdCLENBQUMsRUFB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QztBQUVGOzs7R0FHRztBQUNILE1BQU0sVUFBVSxHQUFHLEtBQUssRUFDdEIsS0FBYSxFQUNiLEVBQTBCLEVBQ1gsRUFBRTtJQUNqQixJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsRUFBRSxDQUFDO1FBQ1gsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsS0FBSyxLQUFLLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE9BQU8sQ0FBQyxJQUFJLENBQUMsb0JBQW9CLEtBQUssVUFBVSxFQUFFO1lBQ2hELEtBQUssRUFBRyxHQUF5QixFQUFFLElBQUksSUFBSSxTQUFTO1NBQ3JELENBQUMsQ0FBQztJQUNMLENBQUM7QUFDSCxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IElBTUNsaWVudCwgTGlzdFJvbGVzQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1pYW0nO1xuaW1wb3J0IHtcbiAgQ29ubmVjdENhbXBhaWduc1YyQ2xpZW50LFxuICBEZWxldGVDb25uZWN0SW5zdGFuY2VJbnRlZ3JhdGlvbkNvbW1hbmQsXG4gIERlbGV0ZUluc3RhbmNlT25ib2FyZGluZ0pvYkNvbW1hbmQsXG4gIFB1dENvbm5lY3RJbnN0YW5jZUludGVncmF0aW9uQ29tbWFuZCxcbn0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNvbm5lY3RjYW1wYWlnbnN2Mic7XG5pbXBvcnQge1xuICBDdXN0b21lclByb2ZpbGVzQ2xpZW50LFxuICBEZWxldGVJbnRlZ3JhdGlvbkNvbW1hbmQsXG4gIFB1dEludGVncmF0aW9uQ29tbWFuZCxcbn0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWN1c3RvbWVyLXByb2ZpbGVzJztcblxuaW1wb3J0IHtcbiAgQ0FNUEFJR05fT0JKRUNUX1RZUEVfTkFNRVMsXG4gIENPTk5FQ1RfQ0FNUEFJR05TX1NMUl9QQVRIX1BSRUZJWCxcbn0gZnJvbSAnLi4vLi4vY29uc3RhbnRzLmpzJztcblxuLyoqIE9iamVjdC10eXBlIHJvdXRpbmcgbWFwIHN1cHBsaWVkIHRvIGJvdGggaW50ZWdyYXRpb24gY2FsbHMuICovXG5jb25zdCBvYmplY3RUeXBlTmFtZXMgPSB7IC4uLkNBTVBBSUdOX09CSkVDVF9UWVBFX05BTUVTIH07XG5cbi8qKiBDdXN0b21lciBQcm9maWxlcyBkb21haW4gQVJOIGZvciBhIGRvbWFpbiBuYW1lIGluIHRoZSBkZXBsb3kgYWNjb3VudC9yZWdpb24uICovXG5leHBvcnQgY29uc3QgZG9tYWluQXJuT2YgPSAoXG4gIGFjY291bnQ6IHN0cmluZyxcbiAgcmVnaW9uOiBzdHJpbmcsXG4gIGRvbWFpbk5hbWU6IHN0cmluZyxcbik6IHN0cmluZyA9PiBgYXJuOmF3czpwcm9maWxlOiR7cmVnaW9ufToke2FjY291bnR9OmRvbWFpbnMvJHtkb21haW5OYW1lfWA7XG5cbi8qKiBjb25uZWN0LWNhbXBhaWducyBpbnN0YW5jZSBBUk4gdXNlZCBhcyB0aGUgQ3VzdG9tZXIgUHJvZmlsZXMgaW50ZWdyYXRpb24gVXJpLiAqL1xuZXhwb3J0IGNvbnN0IGNhbXBhaWduc0luc3RhbmNlQXJuT2YgPSAoXG4gIGFjY291bnQ6IHN0cmluZyxcbiAgcmVnaW9uOiBzdHJpbmcsXG4gIGNvbm5lY3RJbnN0YW5jZUlkOiBzdHJpbmcsXG4pOiBzdHJpbmcgPT5cbiAgYGFybjphd3M6Y29ubmVjdC1jYW1wYWlnbnM6JHtyZWdpb259OiR7YWNjb3VudH06aW5zdGFuY2UvJHtjb25uZWN0SW5zdGFuY2VJZH1gO1xuXG4vKipcbiAqIFJlc29sdmUgdGhlIE91dGJvdW5kIENhbXBhaWducyBzZXJ2aWNlLWxpbmtlZCByb2xlIEFSTlxuICogKGBBV1NTZXJ2aWNlUm9sZUZvckNvbm5lY3RDYW1wYWlnbnNfKmApLCB3aGljaCBTdGFydEluc3RhbmNlT25ib2FyZGluZ0pvYlxuICogYXV0by1jcmVhdGVzIHVuZGVyIGAvYXdzLXNlcnZpY2Utcm9sZS9jb25uZWN0LWNhbXBhaWducy5hbWF6b25hd3MuY29tL2AuIFRoZVxuICogbmV3ZXN0IHJvbGUgKGxhc3QgaW4gdGhlIHJldHVybmVkIGxpc3QpIGlzIHVzZWQsIG1hdGNoaW5nIHRoZSBlbXBpcmljYWxseVxuICogdmVyaWZpZWQgcmVjaXBlLlxuICovXG5leHBvcnQgY29uc3QgcmVzb2x2ZUNhbXBhaWduc1NlcnZpY2VMaW5rZWRSb2xlQXJuID0gYXN5bmMgKFxuICBpYW06IElBTUNsaWVudCxcbik6IFByb21pc2U8c3RyaW5nPiA9PiB7XG4gIGNvbnN0IHsgUm9sZXM6IHJvbGVzIH0gPSBhd2FpdCBpYW0uc2VuZChcbiAgICBuZXcgTGlzdFJvbGVzQ29tbWFuZCh7IFBhdGhQcmVmaXg6IENPTk5FQ1RfQ0FNUEFJR05TX1NMUl9QQVRIX1BSRUZJWCB9KSxcbiAgKTtcbiAgY29uc3QgYXJuID0gcm9sZXM/Lltyb2xlcy5sZW5ndGggLSAxXT8uQXJuO1xuICBpZiAoIWFybikge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICdPdXRib3VuZCBDYW1wYWlnbnMgc2VydmljZS1saW5rZWQgcm9sZSBub3QgZm91bmQgYWZ0ZXIgb25ib2FyZGluZycsXG4gICAgKTtcbiAgfVxuICByZXR1cm4gYXJuO1xufTtcblxuLyoqXG4gKiBBc3NvY2lhdGUgdGhlIChjcmVhdGVkKSBDdXN0b21lciBQcm9maWxlcyBkb21haW4gd2l0aCB0aGUgQ29ubmVjdCBpbnN0YW5jZSdzXG4gKiBPdXRib3VuZCBDYW1wYWlnbnMgdjIgc28gQ29ubmVjdCBKb3VybmV5cyBjYW4gdGFyZ2V0IHRoZSBkb21haW4ncyBwcm9maWxlczpcbiAqICAgMS4gY29ubmVjdGNhbXBhaWduc3YyOlB1dENvbm5lY3RJbnN0YW5jZUludGVncmF0aW9uIOKAlCBiaW5kcyB0aGUgZG9tYWluIEFSTi5cbiAqICAgMi4gY3VzdG9tZXItcHJvZmlsZXM6UHV0SW50ZWdyYXRpb24g4oCUIHRoZSByZWNpcHJvY2FsIGJpbmRpbmcsIHVzaW5nIHRoZVxuICogICAgICBjYW1wYWlnbnMgc2VydmljZS1saW5rZWQgcm9sZS5cbiAqIFN1Y2NlZWRzIG9uIGFuIEVNUFRZIGRvbWFpbjogYG9iamVjdFR5cGVOYW1lc2AgaXMgYSBsYWJlbC9yb3V0aW5nIG1hcCB0b1xuICogQlVJTFQtSU4gdGVtcGxhdGVzLCBzbyBubyBvYmplY3QgdHlwZXMgbmVlZCBwcmUtY3JlYXRpbmcuXG4gKi9cbmV4cG9ydCBjb25zdCBhc3NvY2lhdGVEb21haW4gPSBhc3luYyAoXG4gIGNsaWVudHM6IHtcbiAgICBjYW1wYWlnbnM6IENvbm5lY3RDYW1wYWlnbnNWMkNsaWVudDtcbiAgICBwcm9maWxlczogQ3VzdG9tZXJQcm9maWxlc0NsaWVudDtcbiAgICBpYW06IElBTUNsaWVudDtcbiAgfSxcbiAgYXJnczoge1xuICAgIGNvbm5lY3RJbnN0YW5jZUlkOiBzdHJpbmc7XG4gICAgZG9tYWluTmFtZTogc3RyaW5nO1xuICAgIHJvbGVBcm46IHN0cmluZztcbiAgICBhY2NvdW50OiBzdHJpbmc7XG4gICAgcmVnaW9uOiBzdHJpbmc7XG4gIH0sXG4pOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgY29uc3QgeyBjYW1wYWlnbnMsIHByb2ZpbGVzIH0gPSBjbGllbnRzO1xuICBjb25zdCB7IGNvbm5lY3RJbnN0YW5jZUlkLCBkb21haW5OYW1lLCByb2xlQXJuLCBhY2NvdW50LCByZWdpb24gfSA9IGFyZ3M7XG4gIGNvbnN0IGRvbWFpbkFybiA9IGRvbWFpbkFybk9mKGFjY291bnQsIHJlZ2lvbiwgZG9tYWluTmFtZSk7XG5cbiAgYXdhaXQgY2FtcGFpZ25zLnNlbmQoXG4gICAgbmV3IFB1dENvbm5lY3RJbnN0YW5jZUludGVncmF0aW9uQ29tbWFuZCh7XG4gICAgICBjb25uZWN0SW5zdGFuY2VJZCxcbiAgICAgIGludGVncmF0aW9uQ29uZmlnOiB7XG4gICAgICAgIGN1c3RvbWVyUHJvZmlsZXM6IHsgZG9tYWluQXJuLCBvYmplY3RUeXBlTmFtZXMgfSxcbiAgICAgIH0sXG4gICAgfSksXG4gICk7XG4gIGNvbnNvbGUubG9nKCdbY2FtcGFpZ24tYXNzb2NdIGNvbm5lY3QgaW5zdGFuY2UgaW50ZWdyYXRpb24gY3JlYXRlZCcsIHtcbiAgICBjb25uZWN0SW5zdGFuY2VJZCxcbiAgICBkb21haW5Bcm4sXG4gIH0pO1xuXG4gIGF3YWl0IHByb2ZpbGVzLnNlbmQoXG4gICAgbmV3IFB1dEludGVncmF0aW9uQ29tbWFuZCh7XG4gICAgICBEb21haW5OYW1lOiBkb21haW5OYW1lLFxuICAgICAgVXJpOiBjYW1wYWlnbnNJbnN0YW5jZUFybk9mKGFjY291bnQsIHJlZ2lvbiwgY29ubmVjdEluc3RhbmNlSWQpLFxuICAgICAgT2JqZWN0VHlwZU5hbWVzOiBvYmplY3RUeXBlTmFtZXMsXG4gICAgICBSb2xlQXJuOiByb2xlQXJuLFxuICAgIH0pLFxuICApO1xuICBjb25zb2xlLmxvZygnW2NhbXBhaWduLWFzc29jXSBjdXN0b21lciBwcm9maWxlcyBpbnRlZ3JhdGlvbiBjcmVhdGVkJywge1xuICAgIGRvbWFpbk5hbWUsXG4gICAgY29ubmVjdEluc3RhbmNlSWQsXG4gIH0pO1xufTtcblxuLyoqXG4gKiBCZXN0LWVmZm9ydCByZXZlcnNlIG9mIHtAbGluayBhc3NvY2lhdGVEb21haW59IG9uIHN0YWNrIHRlYXJkb3duLiBFYWNoIHN0ZXAgaXNcbiAqIGlkZW1wb3RlbnQgYW5kIHN3YWxsb3dzIGVycm9ycyAobG9nZ2VkKSBzbyBhIG1pc3NpbmcgLyBhbHJlYWR5LXJlbW92ZWRcbiAqIGludGVncmF0aW9uIOKAlCBlLmcuIHdoZW4gQ2xvdWRGb3JtYXRpb24gZGVsZXRlZCB0aGUgcGFyZW50IGluc3RhbmNlIC8gZG9tYWluXG4gKiBGSVJTVCDigJQgTkVWRVIgZmFpbHMgdGVhcmRvd24uIFJldmVyc2VzIGV2ZXJ5dGhpbmcgb25DcmVhdGUgZGlkOlxuICogICAxLiBjb25uZWN0Y2FtcGFpZ25zdjI6RGVsZXRlQ29ubmVjdEluc3RhbmNlSW50ZWdyYXRpb24gKGNhbXBhaWducyAtPiBkb21haW4pXG4gKiAgIDIuIGN1c3RvbWVyLXByb2ZpbGVzOkRlbGV0ZUludGVncmF0aW9uIChkb21haW4gLT4gY2FtcGFpZ25zKVxuICogICAzLiBjb25uZWN0Y2FtcGFpZ25zdjI6RGVsZXRlSW5zdGFuY2VPbmJvYXJkaW5nSm9iIChvZmZib2FyZCBmcm9tIENhbXBhaWducyB2MilcbiAqXG4gKiBUaGUgYEFXU1NlcnZpY2VSb2xlRm9yQ29ubmVjdENhbXBhaWduc18qYCBzZXJ2aWNlLWxpbmtlZCByb2xlIGlzIGRlbGliZXJhdGVseVxuICogTk9UIGRlbGV0ZWQ6IGl0IGlzIGFjY291bnQtd2lkZSwgc2hhcmVkIGFjcm9zcyBBTEwgQ29ubmVjdCBpbnN0YW5jZXMgL1xuICogY2FtcGFpZ25zIGluIHRoZSBhY2NvdW50LCBmcmVlLCBhbmQgaGFybWxlc3Mg4oCUIGRlbGV0aW5nIGl0IGNvdWxkIGJyZWFrIG90aGVyXG4gKiBpbnN0YW5jZXMgc3RpbGwgb25ib2FyZGVkIHRvIE91dGJvdW5kIENhbXBhaWducy5cbiAqL1xuZXhwb3J0IGNvbnN0IGRpc2Fzc29jaWF0ZURvbWFpbiA9IGFzeW5jIChcbiAgY2xpZW50czoge1xuICAgIGNhbXBhaWduczogQ29ubmVjdENhbXBhaWduc1YyQ2xpZW50O1xuICAgIHByb2ZpbGVzOiBDdXN0b21lclByb2ZpbGVzQ2xpZW50O1xuICB9LFxuICBhcmdzOiB7XG4gICAgY29ubmVjdEluc3RhbmNlSWQ6IHN0cmluZztcbiAgICBkb21haW5OYW1lOiBzdHJpbmc7XG4gICAgYWNjb3VudDogc3RyaW5nO1xuICAgIHJlZ2lvbjogc3RyaW5nO1xuICB9LFxuKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIGNvbnN0IHsgY2FtcGFpZ25zLCBwcm9maWxlcyB9ID0gY2xpZW50cztcbiAgY29uc3QgeyBjb25uZWN0SW5zdGFuY2VJZCwgZG9tYWluTmFtZSwgYWNjb3VudCwgcmVnaW9uIH0gPSBhcmdzO1xuXG4gIGF3YWl0IGJlc3RFZmZvcnQoJ2Nvbm5lY3QgaW5zdGFuY2UgaW50ZWdyYXRpb24gZGVsZXRlJywgKCkgPT5cbiAgICBjYW1wYWlnbnMuc2VuZChcbiAgICAgIG5ldyBEZWxldGVDb25uZWN0SW5zdGFuY2VJbnRlZ3JhdGlvbkNvbW1hbmQoe1xuICAgICAgICBjb25uZWN0SW5zdGFuY2VJZCxcbiAgICAgICAgaW50ZWdyYXRpb25JZGVudGlmaWVyOiB7XG4gICAgICAgICAgY3VzdG9tZXJQcm9maWxlczoge1xuICAgICAgICAgICAgZG9tYWluQXJuOiBkb21haW5Bcm5PZihhY2NvdW50LCByZWdpb24sIGRvbWFpbk5hbWUpLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICApLFxuICApO1xuXG4gIGF3YWl0IGJlc3RFZmZvcnQoJ2N1c3RvbWVyIHByb2ZpbGVzIGludGVncmF0aW9uIGRlbGV0ZScsICgpID0+XG4gICAgcHJvZmlsZXMuc2VuZChcbiAgICAgIG5ldyBEZWxldGVJbnRlZ3JhdGlvbkNvbW1hbmQoe1xuICAgICAgICBEb21haW5OYW1lOiBkb21haW5OYW1lLFxuICAgICAgICBVcmk6IGNhbXBhaWduc0luc3RhbmNlQXJuT2YoYWNjb3VudCwgcmVnaW9uLCBjb25uZWN0SW5zdGFuY2VJZCksXG4gICAgICB9KSxcbiAgICApLFxuICApO1xuXG4gIGF3YWl0IGJlc3RFZmZvcnQoJ2luc3RhbmNlIG9mZmJvYXJkaW5nJywgKCkgPT5cbiAgICBjYW1wYWlnbnMuc2VuZChcbiAgICAgIG5ldyBEZWxldGVJbnN0YW5jZU9uYm9hcmRpbmdKb2JDb21tYW5kKHsgY29ubmVjdEluc3RhbmNlSWQgfSksXG4gICAgKSxcbiAgKTtcbn07XG5cbi8qKlxuICogUnVuIGEgdGVhcmRvd24gc3RlcCwgc3dhbGxvd2luZyAoYW5kIGxvZ2dpbmcpIGFueSBlcnJvciBzbyBhIHBhcnRpYWwgL1xuICogYWxyZWFkeS1yZW1vdmVkIHJlc291cmNlIG5ldmVyIGZhaWxzIENsb3VkRm9ybWF0aW9uIHRlYXJkb3duLlxuICovXG5jb25zdCBiZXN0RWZmb3J0ID0gYXN5bmMgKFxuICBsYWJlbDogc3RyaW5nLFxuICBmbjogKCkgPT4gUHJvbWlzZTx1bmtub3duPixcbik6IFByb21pc2U8dm9pZD4gPT4ge1xuICB0cnkge1xuICAgIGF3YWl0IGZuKCk7XG4gICAgY29uc29sZS5sb2coYFtjYW1wYWlnbi1hc3NvY10gJHtsYWJlbH0gb2tgKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgY29uc29sZS53YXJuKGBbY2FtcGFpZ24tYXNzb2NdICR7bGFiZWx9IHNraXBwZWRgLCB7XG4gICAgICBlcnJvcjogKGVyciBhcyB7IG5hbWU/OiBzdHJpbmcgfSk/Lm5hbWUgPz8gJ3Vua25vd24nLFxuICAgIH0pO1xuICB9XG59O1xuIl19
@@ -0,0 +1,25 @@
1
+ import { ConnectCampaignsV2Client } from '@aws-sdk/client-connectcampaignsv2';
2
+ /**
3
+ * Onboard an Amazon Connect instance to Outbound Campaigns v2 and wait until the
4
+ * onboarding job reaches SUCCEEDED.
5
+ *
6
+ * StartInstanceOnboardingJob (with encryption disabled) auto-creates the
7
+ * `AWSServiceRoleForConnectCampaigns_*` service-linked role, then the job runs
8
+ * asynchronously (~20s). We poll GetInstanceOnboardingJobStatus with bounded,
9
+ * exponentially-backed-off retries (capped ~2min) until it SUCCEEDS.
10
+ *
11
+ * Idempotent: if the instance is already onboarded (StartInstanceOnboardingJob
12
+ * conflicts or the status is already SUCCEEDED / absent), this resolves without
13
+ * error. Throws only on an explicit FAILED status or if the job never succeeds
14
+ * within the polling budget.
15
+ */
16
+ export declare const ensureInstanceOnboarded: (client: ConnectCampaignsV2Client, opts: {
17
+ connectInstanceId: string;
18
+ /** Injected in tests to avoid real delays. Defaults to real setTimeout. */
19
+ sleepFn?: (ms: number) => Promise<void>;
20
+ /** Max polling attempts. Default 24 (~2min with capped backoff). */
21
+ maxAttempts?: number;
22
+ /** Base backoff in ms. Default 2000. */
23
+ baseDelayMs?: number;
24
+ }) => Promise<void>;
25
+ //# sourceMappingURL=onboarding.d.ts.map
@@ -0,0 +1,74 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { GetInstanceOnboardingJobStatusCommand, StartInstanceOnboardingJobCommand, } from '@aws-sdk/client-connectcampaignsv2';
4
+ /** Terminal + in-flight onboarding job statuses reported by the service. */
5
+ const STATUS_SUCCEEDED = 'SUCCEEDED';
6
+ const STATUS_FAILED = 'FAILED';
7
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
+ /**
9
+ * Names that, when thrown by StartInstanceOnboardingJob, indicate the instance
10
+ * is ALREADY onboarded to Outbound Campaigns v2. Treated as success so the
11
+ * custom resource is idempotent across stack updates / retried deployments.
12
+ */
13
+ const isAlreadyOnboardedError = (err) => {
14
+ const name = err?.name ?? '';
15
+ return name === 'ConflictException' || name === 'ResourceConflictException';
16
+ };
17
+ /**
18
+ * Onboard an Amazon Connect instance to Outbound Campaigns v2 and wait until the
19
+ * onboarding job reaches SUCCEEDED.
20
+ *
21
+ * StartInstanceOnboardingJob (with encryption disabled) auto-creates the
22
+ * `AWSServiceRoleForConnectCampaigns_*` service-linked role, then the job runs
23
+ * asynchronously (~20s). We poll GetInstanceOnboardingJobStatus with bounded,
24
+ * exponentially-backed-off retries (capped ~2min) until it SUCCEEDS.
25
+ *
26
+ * Idempotent: if the instance is already onboarded (StartInstanceOnboardingJob
27
+ * conflicts or the status is already SUCCEEDED / absent), this resolves without
28
+ * error. Throws only on an explicit FAILED status or if the job never succeeds
29
+ * within the polling budget.
30
+ */
31
+ export const ensureInstanceOnboarded = async (client, opts) => {
32
+ const { connectInstanceId, sleepFn = sleep, maxAttempts = 24, baseDelayMs = 2000, } = opts;
33
+ try {
34
+ await client.send(new StartInstanceOnboardingJobCommand({
35
+ connectInstanceId,
36
+ encryptionConfig: { enabled: false },
37
+ }));
38
+ console.log('[campaign-assoc] onboarding job started', {
39
+ connectInstanceId,
40
+ });
41
+ }
42
+ catch (err) {
43
+ if (isAlreadyOnboardedError(err)) {
44
+ console.log('[campaign-assoc] instance already onboarded (start conflict)', {
45
+ connectInstanceId,
46
+ });
47
+ return;
48
+ }
49
+ throw err;
50
+ }
51
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
52
+ const { connectInstanceOnboardingJobStatus } = await client.send(new GetInstanceOnboardingJobStatusCommand({ connectInstanceId }));
53
+ const status = connectInstanceOnboardingJobStatus?.status;
54
+ const failureCode = connectInstanceOnboardingJobStatus?.failureCode;
55
+ console.log('[campaign-assoc] onboarding status', {
56
+ connectInstanceId,
57
+ status: status ?? 'NONE',
58
+ failureCode: failureCode ?? 'NONE',
59
+ attempt,
60
+ });
61
+ if (status === STATUS_SUCCEEDED || status === undefined) {
62
+ // SUCCEEDED, or no job present (already-onboarded steady state).
63
+ return;
64
+ }
65
+ if (status === STATUS_FAILED) {
66
+ throw new Error(`Instance onboarding to Outbound Campaigns FAILED for ${connectInstanceId}` +
67
+ (failureCode ? ` (failureCode: ${failureCode})` : ''));
68
+ }
69
+ const delay = Math.min(baseDelayMs * 2 ** attempt, 10000);
70
+ await sleepFn(delay);
71
+ }
72
+ throw new Error(`Instance onboarding did not reach SUCCEEDED within the polling budget for ${connectInstanceId}`);
73
+ };
74
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib25ib2FyZGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sYW1iZGEvY2FtcGFpZ24tYXNzb2NpYXRpb24vb25ib2FyZGluZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxxRUFBcUU7QUFDckUsc0NBQXNDO0FBRXRDLE9BQU8sRUFFTCxxQ0FBcUMsRUFDckMsaUNBQWlDLEdBQ2xDLE1BQU0sb0NBQW9DLENBQUM7QUFFNUMsNEVBQTRFO0FBQzVFLE1BQU0sZ0JBQWdCLEdBQUcsV0FBVyxDQUFDO0FBQ3JDLE1BQU0sYUFBYSxHQUFHLFFBQVEsQ0FBQztBQUUvQixNQUFNLEtBQUssR0FBRyxDQUFDLEVBQVUsRUFBaUIsRUFBRSxDQUMxQyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRXBEOzs7O0dBSUc7QUFDSCxNQUFNLHVCQUF1QixHQUFHLENBQUMsR0FBWSxFQUFXLEVBQUU7SUFDeEQsTUFBTSxJQUFJLEdBQUksR0FBeUIsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDO0lBQ3BELE9BQU8sSUFBSSxLQUFLLG1CQUFtQixJQUFJLElBQUksS0FBSywyQkFBMkIsQ0FBQztBQUM5RSxDQUFDLENBQUM7QUFFRjs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsS0FBSyxFQUMxQyxNQUFnQyxFQUNoQyxJQVFDLEVBQ2MsRUFBRTtJQUNqQixNQUFNLEVBQ0osaUJBQWlCLEVBQ2pCLE9BQU8sR0FBRyxLQUFLLEVBQ2YsV0FBVyxHQUFHLEVBQUUsRUFDaEIsV0FBVyxHQUFHLElBQUksR0FDbkIsR0FBRyxJQUFJLENBQUM7SUFFVCxJQUFJLENBQUM7UUFDSCxNQUFNLE1BQU0sQ0FBQyxJQUFJLENBQ2YsSUFBSSxpQ0FBaUMsQ0FBQztZQUNwQyxpQkFBaUI7WUFDakIsZ0JBQWdCLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1NBQ3JDLENBQUMsQ0FDSCxDQUFDO1FBQ0YsT0FBTyxDQUFDLEdBQUcsQ0FBQyx5Q0FBeUMsRUFBRTtZQUNyRCxpQkFBaUI7U0FDbEIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixJQUFJLHVCQUF1QixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDakMsT0FBTyxDQUFDLEdBQUcsQ0FDVCw4REFBOEQsRUFDOUQ7Z0JBQ0UsaUJBQWlCO2FBQ2xCLENBQ0YsQ0FBQztZQUNGLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxHQUFHLENBQUM7SUFDWixDQUFDO0lBRUQsS0FBSyxJQUFJLE9BQU8sR0FBRyxDQUFDLEVBQUUsT0FBTyxHQUFHLFdBQVcsRUFBRSxPQUFPLEVBQUUsRUFBRSxDQUFDO1FBQ3ZELE1BQU0sRUFBRSxrQ0FBa0MsRUFBRSxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUksQ0FDOUQsSUFBSSxxQ0FBcUMsQ0FBQyxFQUFFLGlCQUFpQixFQUFFLENBQUMsQ0FDakUsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLGtDQUFrQyxFQUFFLE1BQU0sQ0FBQztRQUMxRCxNQUFNLFdBQVcsR0FBRyxrQ0FBa0MsRUFBRSxXQUFXLENBQUM7UUFDcEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsRUFBRTtZQUNoRCxpQkFBaUI7WUFDakIsTUFBTSxFQUFFLE1BQU0sSUFBSSxNQUFNO1lBQ3hCLFdBQVcsRUFBRSxXQUFXLElBQUksTUFBTTtZQUNsQyxPQUFPO1NBQ1IsQ0FBQyxDQUFDO1FBRUgsSUFBSSxNQUFNLEtBQUssZ0JBQWdCLElBQUksTUFBTSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3hELGlFQUFpRTtZQUNqRSxPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksTUFBTSxLQUFLLGFBQWEsRUFBRSxDQUFDO1lBQzdCLE1BQU0sSUFBSSxLQUFLLENBQ2Isd0RBQXdELGlCQUFpQixFQUFFO2dCQUN6RSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsa0JBQWtCLFdBQVcsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FDeEQsQ0FBQztRQUNKLENBQUM7UUFFRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsR0FBRyxDQUFDLElBQUksT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzFELE1BQU0sT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUNiLDZFQUE2RSxpQkFBaUIsRUFBRSxDQUNqRyxDQUFDO0FBQ0osQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IEFtYXpvbi5jb20sIEluYy4gb3IgaXRzIGFmZmlsaWF0ZXMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQge1xuICBDb25uZWN0Q2FtcGFpZ25zVjJDbGllbnQsXG4gIEdldEluc3RhbmNlT25ib2FyZGluZ0pvYlN0YXR1c0NvbW1hbmQsXG4gIFN0YXJ0SW5zdGFuY2VPbmJvYXJkaW5nSm9iQ29tbWFuZCxcbn0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNvbm5lY3RjYW1wYWlnbnN2Mic7XG5cbi8qKiBUZXJtaW5hbCArIGluLWZsaWdodCBvbmJvYXJkaW5nIGpvYiBzdGF0dXNlcyByZXBvcnRlZCBieSB0aGUgc2VydmljZS4gKi9cbmNvbnN0IFNUQVRVU19TVUNDRUVERUQgPSAnU1VDQ0VFREVEJztcbmNvbnN0IFNUQVRVU19GQUlMRUQgPSAnRkFJTEVEJztcblxuY29uc3Qgc2xlZXAgPSAobXM6IG51bWJlcik6IFByb21pc2U8dm9pZD4gPT5cbiAgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcblxuLyoqXG4gKiBOYW1lcyB0aGF0LCB3aGVuIHRocm93biBieSBTdGFydEluc3RhbmNlT25ib2FyZGluZ0pvYiwgaW5kaWNhdGUgdGhlIGluc3RhbmNlXG4gKiBpcyBBTFJFQURZIG9uYm9hcmRlZCB0byBPdXRib3VuZCBDYW1wYWlnbnMgdjIuIFRyZWF0ZWQgYXMgc3VjY2VzcyBzbyB0aGVcbiAqIGN1c3RvbSByZXNvdXJjZSBpcyBpZGVtcG90ZW50IGFjcm9zcyBzdGFjayB1cGRhdGVzIC8gcmV0cmllZCBkZXBsb3ltZW50cy5cbiAqL1xuY29uc3QgaXNBbHJlYWR5T25ib2FyZGVkRXJyb3IgPSAoZXJyOiB1bmtub3duKTogYm9vbGVhbiA9PiB7XG4gIGNvbnN0IG5hbWUgPSAoZXJyIGFzIHsgbmFtZT86IHN0cmluZyB9KT8ubmFtZSA/PyAnJztcbiAgcmV0dXJuIG5hbWUgPT09ICdDb25mbGljdEV4Y2VwdGlvbicgfHwgbmFtZSA9PT0gJ1Jlc291cmNlQ29uZmxpY3RFeGNlcHRpb24nO1xufTtcblxuLyoqXG4gKiBPbmJvYXJkIGFuIEFtYXpvbiBDb25uZWN0IGluc3RhbmNlIHRvIE91dGJvdW5kIENhbXBhaWducyB2MiBhbmQgd2FpdCB1bnRpbCB0aGVcbiAqIG9uYm9hcmRpbmcgam9iIHJlYWNoZXMgU1VDQ0VFREVELlxuICpcbiAqIFN0YXJ0SW5zdGFuY2VPbmJvYXJkaW5nSm9iICh3aXRoIGVuY3J5cHRpb24gZGlzYWJsZWQpIGF1dG8tY3JlYXRlcyB0aGVcbiAqIGBBV1NTZXJ2aWNlUm9sZUZvckNvbm5lY3RDYW1wYWlnbnNfKmAgc2VydmljZS1saW5rZWQgcm9sZSwgdGhlbiB0aGUgam9iIHJ1bnNcbiAqIGFzeW5jaHJvbm91c2x5ICh+MjBzKS4gV2UgcG9sbCBHZXRJbnN0YW5jZU9uYm9hcmRpbmdKb2JTdGF0dXMgd2l0aCBib3VuZGVkLFxuICogZXhwb25lbnRpYWxseS1iYWNrZWQtb2ZmIHJldHJpZXMgKGNhcHBlZCB+Mm1pbikgdW50aWwgaXQgU1VDQ0VFRFMuXG4gKlxuICogSWRlbXBvdGVudDogaWYgdGhlIGluc3RhbmNlIGlzIGFscmVhZHkgb25ib2FyZGVkIChTdGFydEluc3RhbmNlT25ib2FyZGluZ0pvYlxuICogY29uZmxpY3RzIG9yIHRoZSBzdGF0dXMgaXMgYWxyZWFkeSBTVUNDRUVERUQgLyBhYnNlbnQpLCB0aGlzIHJlc29sdmVzIHdpdGhvdXRcbiAqIGVycm9yLiBUaHJvd3Mgb25seSBvbiBhbiBleHBsaWNpdCBGQUlMRUQgc3RhdHVzIG9yIGlmIHRoZSBqb2IgbmV2ZXIgc3VjY2VlZHNcbiAqIHdpdGhpbiB0aGUgcG9sbGluZyBidWRnZXQuXG4gKi9cbmV4cG9ydCBjb25zdCBlbnN1cmVJbnN0YW5jZU9uYm9hcmRlZCA9IGFzeW5jIChcbiAgY2xpZW50OiBDb25uZWN0Q2FtcGFpZ25zVjJDbGllbnQsXG4gIG9wdHM6IHtcbiAgICBjb25uZWN0SW5zdGFuY2VJZDogc3RyaW5nO1xuICAgIC8qKiBJbmplY3RlZCBpbiB0ZXN0cyB0byBhdm9pZCByZWFsIGRlbGF5cy4gRGVmYXVsdHMgdG8gcmVhbCBzZXRUaW1lb3V0LiAqL1xuICAgIHNsZWVwRm4/OiAobXM6IG51bWJlcikgPT4gUHJvbWlzZTx2b2lkPjtcbiAgICAvKiogTWF4IHBvbGxpbmcgYXR0ZW1wdHMuIERlZmF1bHQgMjQgKH4ybWluIHdpdGggY2FwcGVkIGJhY2tvZmYpLiAqL1xuICAgIG1heEF0dGVtcHRzPzogbnVtYmVyO1xuICAgIC8qKiBCYXNlIGJhY2tvZmYgaW4gbXMuIERlZmF1bHQgMjAwMC4gKi9cbiAgICBiYXNlRGVsYXlNcz86IG51bWJlcjtcbiAgfSxcbik6IFByb21pc2U8dm9pZD4gPT4ge1xuICBjb25zdCB7XG4gICAgY29ubmVjdEluc3RhbmNlSWQsXG4gICAgc2xlZXBGbiA9IHNsZWVwLFxuICAgIG1heEF0dGVtcHRzID0gMjQsXG4gICAgYmFzZURlbGF5TXMgPSAyMDAwLFxuICB9ID0gb3B0cztcblxuICB0cnkge1xuICAgIGF3YWl0IGNsaWVudC5zZW5kKFxuICAgICAgbmV3IFN0YXJ0SW5zdGFuY2VPbmJvYXJkaW5nSm9iQ29tbWFuZCh7XG4gICAgICAgIGNvbm5lY3RJbnN0YW5jZUlkLFxuICAgICAgICBlbmNyeXB0aW9uQ29uZmlnOiB7IGVuYWJsZWQ6IGZhbHNlIH0sXG4gICAgICB9KSxcbiAgICApO1xuICAgIGNvbnNvbGUubG9nKCdbY2FtcGFpZ24tYXNzb2NdIG9uYm9hcmRpbmcgam9iIHN0YXJ0ZWQnLCB7XG4gICAgICBjb25uZWN0SW5zdGFuY2VJZCxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKGlzQWxyZWFkeU9uYm9hcmRlZEVycm9yKGVycikpIHtcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAnW2NhbXBhaWduLWFzc29jXSBpbnN0YW5jZSBhbHJlYWR5IG9uYm9hcmRlZCAoc3RhcnQgY29uZmxpY3QpJyxcbiAgICAgICAge1xuICAgICAgICAgIGNvbm5lY3RJbnN0YW5jZUlkLFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhyb3cgZXJyO1xuICB9XG5cbiAgZm9yIChsZXQgYXR0ZW1wdCA9IDA7IGF0dGVtcHQgPCBtYXhBdHRlbXB0czsgYXR0ZW1wdCsrKSB7XG4gICAgY29uc3QgeyBjb25uZWN0SW5zdGFuY2VPbmJvYXJkaW5nSm9iU3RhdHVzIH0gPSBhd2FpdCBjbGllbnQuc2VuZChcbiAgICAgIG5ldyBHZXRJbnN0YW5jZU9uYm9hcmRpbmdKb2JTdGF0dXNDb21tYW5kKHsgY29ubmVjdEluc3RhbmNlSWQgfSksXG4gICAgKTtcbiAgICBjb25zdCBzdGF0dXMgPSBjb25uZWN0SW5zdGFuY2VPbmJvYXJkaW5nSm9iU3RhdHVzPy5zdGF0dXM7XG4gICAgY29uc3QgZmFpbHVyZUNvZGUgPSBjb25uZWN0SW5zdGFuY2VPbmJvYXJkaW5nSm9iU3RhdHVzPy5mYWlsdXJlQ29kZTtcbiAgICBjb25zb2xlLmxvZygnW2NhbXBhaWduLWFzc29jXSBvbmJvYXJkaW5nIHN0YXR1cycsIHtcbiAgICAgIGNvbm5lY3RJbnN0YW5jZUlkLFxuICAgICAgc3RhdHVzOiBzdGF0dXMgPz8gJ05PTkUnLFxuICAgICAgZmFpbHVyZUNvZGU6IGZhaWx1cmVDb2RlID8/ICdOT05FJyxcbiAgICAgIGF0dGVtcHQsXG4gICAgfSk7XG5cbiAgICBpZiAoc3RhdHVzID09PSBTVEFUVVNfU1VDQ0VFREVEIHx8IHN0YXR1cyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAvLyBTVUNDRUVERUQsIG9yIG5vIGpvYiBwcmVzZW50IChhbHJlYWR5LW9uYm9hcmRlZCBzdGVhZHkgc3RhdGUpLlxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoc3RhdHVzID09PSBTVEFUVVNfRkFJTEVEKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBJbnN0YW5jZSBvbmJvYXJkaW5nIHRvIE91dGJvdW5kIENhbXBhaWducyBGQUlMRUQgZm9yICR7Y29ubmVjdEluc3RhbmNlSWR9YCArXG4gICAgICAgICAgKGZhaWx1cmVDb2RlID8gYCAoZmFpbHVyZUNvZGU6ICR7ZmFpbHVyZUNvZGV9KWAgOiAnJyksXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IGRlbGF5ID0gTWF0aC5taW4oYmFzZURlbGF5TXMgKiAyICoqIGF0dGVtcHQsIDEwMDAwKTtcbiAgICBhd2FpdCBzbGVlcEZuKGRlbGF5KTtcbiAgfVxuXG4gIHRocm93IG5ldyBFcnJvcihcbiAgICBgSW5zdGFuY2Ugb25ib2FyZGluZyBkaWQgbm90IHJlYWNoIFNVQ0NFRURFRCB3aXRoaW4gdGhlIHBvbGxpbmcgYnVkZ2V0IGZvciAke2Nvbm5lY3RJbnN0YW5jZUlkfWAsXG4gICk7XG59O1xuIl19
@@ -0,0 +1,18 @@
1
+ import { CustomerProfilesClient } from '@aws-sdk/client-customer-profiles';
2
+ /**
3
+ * Read back the IMMUTABLE `createdAt` of the existing AmplifyDevice object for
4
+ * `deviceId` under the resolved profile, so a subsequent PutProfileObject (which
5
+ * REPLACES the object for that key) can preserve the original first-registration
6
+ * time while refreshing `updatedAt`.
7
+ *
8
+ * Pages through the profile's device objects and matches by `deviceId` (the
9
+ * UNIQUE object key). Returns the stored `createdAt` when a prior object exists,
10
+ * otherwise `undefined` so the caller sets a fresh `createdAt = now`.
11
+ *
12
+ * Best-effort and NEVER throws: any failure — a not-yet-propagated profile
13
+ * (SearchProfiles / ListProfileObjects are eventually consistent), a transient
14
+ * service error, or a permissions gap — simply yields `undefined` so the caller
15
+ * re-seeds `createdAt` and device registration stays resilient.
16
+ */
17
+ export declare const findExistingDeviceCreatedAt: (profiles: CustomerProfilesClient, domainName: string, profileId: string, deviceId: string) => Promise<string | undefined>;
18
+ //# sourceMappingURL=device_resolver.d.ts.map
@@ -0,0 +1,56 @@
1
+ import { ListProfileObjectsCommand, } from '@aws-sdk/client-customer-profiles';
2
+ import { OBJECT_TYPE_DEVICE } from '../../constants.js';
3
+ import { withTransientRetry } from '../shared/retry.js';
4
+ /**
5
+ * Read back the IMMUTABLE `createdAt` of the existing AmplifyDevice object for
6
+ * `deviceId` under the resolved profile, so a subsequent PutProfileObject (which
7
+ * REPLACES the object for that key) can preserve the original first-registration
8
+ * time while refreshing `updatedAt`.
9
+ *
10
+ * Pages through the profile's device objects and matches by `deviceId` (the
11
+ * UNIQUE object key). Returns the stored `createdAt` when a prior object exists,
12
+ * otherwise `undefined` so the caller sets a fresh `createdAt = now`.
13
+ *
14
+ * Best-effort and NEVER throws: any failure — a not-yet-propagated profile
15
+ * (SearchProfiles / ListProfileObjects are eventually consistent), a transient
16
+ * service error, or a permissions gap — simply yields `undefined` so the caller
17
+ * re-seeds `createdAt` and device registration stays resilient.
18
+ */
19
+ export const findExistingDeviceCreatedAt = async (profiles, domainName, profileId, deviceId) => {
20
+ try {
21
+ let nextToken;
22
+ do {
23
+ const res = await withTransientRetry(() => profiles.send(new ListProfileObjectsCommand({
24
+ DomainName: domainName,
25
+ ObjectTypeName: OBJECT_TYPE_DEVICE,
26
+ ProfileId: profileId,
27
+ NextToken: nextToken,
28
+ })));
29
+ for (const item of res.Items ?? []) {
30
+ if (!item.Object) {
31
+ continue;
32
+ }
33
+ try {
34
+ const parsed = JSON.parse(item.Object);
35
+ if (parsed.deviceId === deviceId &&
36
+ typeof parsed.createdAt === 'string' &&
37
+ parsed.createdAt.length > 0) {
38
+ return parsed.createdAt;
39
+ }
40
+ }
41
+ catch {
42
+ // Ignore a malformed stored object; treat as no prior createdAt.
43
+ continue;
44
+ }
45
+ }
46
+ nextToken = res.NextToken;
47
+ } while (nextToken);
48
+ }
49
+ catch {
50
+ // Best-effort: ListProfileObjects unavailable / errored; the caller
51
+ // re-seeds createdAt = now rather than failing device registration.
52
+ return undefined;
53
+ }
54
+ return undefined;
55
+ };
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV2aWNlX3Jlc29sdmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xhbWJkYS9pZGVudGlmeS9kZXZpY2VfcmVzb2x2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUVMLHlCQUF5QixHQUMxQixNQUFNLG1DQUFtQyxDQUFDO0FBRTNDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRXhEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMkJBQTJCLEdBQUcsS0FBSyxFQUM5QyxRQUFnQyxFQUNoQyxVQUFrQixFQUNsQixTQUFpQixFQUNqQixRQUFnQixFQUNhLEVBQUU7SUFDL0IsSUFBSSxDQUFDO1FBQ0gsSUFBSSxTQUE2QixDQUFDO1FBQ2xDLEdBQUcsQ0FBQztZQUNGLE1BQU0sR0FBRyxHQUFHLE1BQU0sa0JBQWtCLENBQUMsR0FBRyxFQUFFLENBQ3hDLFFBQVEsQ0FBQyxJQUFJLENBQ1gsSUFBSSx5QkFBeUIsQ0FBQztnQkFDNUIsVUFBVSxFQUFFLFVBQVU7Z0JBQ3RCLGNBQWMsRUFBRSxrQkFBa0I7Z0JBQ2xDLFNBQVMsRUFBRSxTQUFTO2dCQUNwQixTQUFTLEVBQUUsU0FBUzthQUNyQixDQUFDLENBQ0gsQ0FDRixDQUFDO1lBRUYsS0FBSyxNQUFNLElBQUksSUFBSSxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUUsRUFBRSxDQUFDO2dCQUNuQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO29CQUNqQixTQUFTO2dCQUNYLENBQUM7Z0JBQ0QsSUFBSSxDQUFDO29CQUNILE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBNEIsQ0FBQztvQkFDbEUsSUFDRSxNQUFNLENBQUMsUUFBUSxLQUFLLFFBQVE7d0JBQzVCLE9BQU8sTUFBTSxDQUFDLFNBQVMsS0FBSyxRQUFRO3dCQUNwQyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQzNCLENBQUM7d0JBQ0QsT0FBTyxNQUFNLENBQUMsU0FBUyxDQUFDO29CQUMxQixDQUFDO2dCQUNILENBQUM7Z0JBQUMsTUFBTSxDQUFDO29CQUNQLGlFQUFpRTtvQkFDakUsU0FBUztnQkFDWCxDQUFDO1lBQ0gsQ0FBQztZQUNELFNBQVMsR0FBRyxHQUFHLENBQUMsU0FBUyxDQUFDO1FBQzVCLENBQUMsUUFBUSxTQUFTLEVBQUU7SUFDdEIsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLG9FQUFvRTtRQUNwRSxvRUFBb0U7UUFDcEUsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUNELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEN1c3RvbWVyUHJvZmlsZXNDbGllbnQsXG4gIExpc3RQcm9maWxlT2JqZWN0c0NvbW1hbmQsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jdXN0b21lci1wcm9maWxlcyc7XG5cbmltcG9ydCB7IE9CSkVDVF9UWVBFX0RFVklDRSB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyB3aXRoVHJhbnNpZW50UmV0cnkgfSBmcm9tICcuLi9zaGFyZWQvcmV0cnkuanMnO1xuXG4vKipcbiAqIFJlYWQgYmFjayB0aGUgSU1NVVRBQkxFIGBjcmVhdGVkQXRgIG9mIHRoZSBleGlzdGluZyBBbXBsaWZ5RGV2aWNlIG9iamVjdCBmb3JcbiAqIGBkZXZpY2VJZGAgdW5kZXIgdGhlIHJlc29sdmVkIHByb2ZpbGUsIHNvIGEgc3Vic2VxdWVudCBQdXRQcm9maWxlT2JqZWN0ICh3aGljaFxuICogUkVQTEFDRVMgdGhlIG9iamVjdCBmb3IgdGhhdCBrZXkpIGNhbiBwcmVzZXJ2ZSB0aGUgb3JpZ2luYWwgZmlyc3QtcmVnaXN0cmF0aW9uXG4gKiB0aW1lIHdoaWxlIHJlZnJlc2hpbmcgYHVwZGF0ZWRBdGAuXG4gKlxuICogUGFnZXMgdGhyb3VnaCB0aGUgcHJvZmlsZSdzIGRldmljZSBvYmplY3RzIGFuZCBtYXRjaGVzIGJ5IGBkZXZpY2VJZGAgKHRoZVxuICogVU5JUVVFIG9iamVjdCBrZXkpLiBSZXR1cm5zIHRoZSBzdG9yZWQgYGNyZWF0ZWRBdGAgd2hlbiBhIHByaW9yIG9iamVjdCBleGlzdHMsXG4gKiBvdGhlcndpc2UgYHVuZGVmaW5lZGAgc28gdGhlIGNhbGxlciBzZXRzIGEgZnJlc2ggYGNyZWF0ZWRBdCA9IG5vd2AuXG4gKlxuICogQmVzdC1lZmZvcnQgYW5kIE5FVkVSIHRocm93czogYW55IGZhaWx1cmUg4oCUIGEgbm90LXlldC1wcm9wYWdhdGVkIHByb2ZpbGVcbiAqIChTZWFyY2hQcm9maWxlcyAvIExpc3RQcm9maWxlT2JqZWN0cyBhcmUgZXZlbnR1YWxseSBjb25zaXN0ZW50KSwgYSB0cmFuc2llbnRcbiAqIHNlcnZpY2UgZXJyb3IsIG9yIGEgcGVybWlzc2lvbnMgZ2FwIOKAlCBzaW1wbHkgeWllbGRzIGB1bmRlZmluZWRgIHNvIHRoZSBjYWxsZXJcbiAqIHJlLXNlZWRzIGBjcmVhdGVkQXRgIGFuZCBkZXZpY2UgcmVnaXN0cmF0aW9uIHN0YXlzIHJlc2lsaWVudC5cbiAqL1xuZXhwb3J0IGNvbnN0IGZpbmRFeGlzdGluZ0RldmljZUNyZWF0ZWRBdCA9IGFzeW5jIChcbiAgcHJvZmlsZXM6IEN1c3RvbWVyUHJvZmlsZXNDbGllbnQsXG4gIGRvbWFpbk5hbWU6IHN0cmluZyxcbiAgcHJvZmlsZUlkOiBzdHJpbmcsXG4gIGRldmljZUlkOiBzdHJpbmcsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4gPT4ge1xuICB0cnkge1xuICAgIGxldCBuZXh0VG9rZW46IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICBkbyB7XG4gICAgICBjb25zdCByZXMgPSBhd2FpdCB3aXRoVHJhbnNpZW50UmV0cnkoKCkgPT5cbiAgICAgICAgcHJvZmlsZXMuc2VuZChcbiAgICAgICAgICBuZXcgTGlzdFByb2ZpbGVPYmplY3RzQ29tbWFuZCh7XG4gICAgICAgICAgICBEb21haW5OYW1lOiBkb21haW5OYW1lLFxuICAgICAgICAgICAgT2JqZWN0VHlwZU5hbWU6IE9CSkVDVF9UWVBFX0RFVklDRSxcbiAgICAgICAgICAgIFByb2ZpbGVJZDogcHJvZmlsZUlkLFxuICAgICAgICAgICAgTmV4dFRva2VuOiBuZXh0VG9rZW4sXG4gICAgICAgICAgfSksXG4gICAgICAgICksXG4gICAgICApO1xuXG4gICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgcmVzLkl0ZW1zID8/IFtdKSB7XG4gICAgICAgIGlmICghaXRlbS5PYmplY3QpIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UoaXRlbS5PYmplY3QpIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIHBhcnNlZC5kZXZpY2VJZCA9PT0gZGV2aWNlSWQgJiZcbiAgICAgICAgICAgIHR5cGVvZiBwYXJzZWQuY3JlYXRlZEF0ID09PSAnc3RyaW5nJyAmJlxuICAgICAgICAgICAgcGFyc2VkLmNyZWF0ZWRBdC5sZW5ndGggPiAwXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VkLmNyZWF0ZWRBdDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIElnbm9yZSBhIG1hbGZvcm1lZCBzdG9yZWQgb2JqZWN0OyB0cmVhdCBhcyBubyBwcmlvciBjcmVhdGVkQXQuXG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIG5leHRUb2tlbiA9IHJlcy5OZXh0VG9rZW47XG4gICAgfSB3aGlsZSAobmV4dFRva2VuKTtcbiAgfSBjYXRjaCB7XG4gICAgLy8gQmVzdC1lZmZvcnQ6IExpc3RQcm9maWxlT2JqZWN0cyB1bmF2YWlsYWJsZSAvIGVycm9yZWQ7IHRoZSBjYWxsZXJcbiAgICAvLyByZS1zZWVkcyBjcmVhdGVkQXQgPSBub3cgcmF0aGVyIHRoYW4gZmFpbGluZyBkZXZpY2UgcmVnaXN0cmF0aW9uLlxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn07XG4iXX0=
@@ -0,0 +1,17 @@
1
+ import type { APIGatewayProxyStructuredResultV2 } from 'aws-lambda';
2
+ import { IdentifyEvent } from './principal.js';
3
+ /**
4
+ * HTTP API handler for the identify-user routes. Serves BOTH:
5
+ * - `POST /identify-user` — authed (Cognito user-pool JWT authorizer,
6
+ * payload format 2.0). Identity = verified `sub`.
7
+ * - `POST /identify-user-guest` — guest (IAM/SigV4 authorization, payload
8
+ * format 1.0). Identity = verified Cognito Identity Pool `cognitoIdentityId`
9
+ * of an UNAUTHENTICATED identity.
10
+ *
11
+ * The verified {@link Principal} abstracts the two modes; the profile
12
+ * find-or-create + device upsert + attribute write are identity-agnostic. On an
13
+ * authed call carrying `options.previousGuestIdentityId`, the prior guest
14
+ * profile (and its devices) is folded into the authed profile via MergeProfiles.
15
+ */
16
+ export declare const handler: (event: IdentifyEvent) => Promise<APIGatewayProxyStructuredResultV2>;
17
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1,124 @@
1
+ import { CustomerProfilesClient, PutProfileObjectCommand, UpdateProfileCommand, } from '@aws-sdk/client-customer-profiles';
2
+ import { validateBody } from './validation.js';
3
+ import { buildDeviceObject, buildProfileUpdate, hasDeviceData, } from './mapping.js';
4
+ import { resolveOrCreateProfile } from './profile_resolver.js';
5
+ import { findExistingDeviceCreatedAt } from './device_resolver.js';
6
+ import { mergeGuestIntoAuthed } from './merge_resolver.js';
7
+ import { resolvePrincipal } from './principal.js';
8
+ import { withTransientRetry } from '../shared/retry.js';
9
+ import { ENV_DOMAIN_NAME, OBJECT_TYPE_DEVICE } from '../../constants.js';
10
+ /**
11
+ * Module-level client so warm invocations reuse the connection pool. Region is
12
+ * resolved from the standard AWS_REGION Lambda environment variable.
13
+ */
14
+ const profiles = new CustomerProfilesClient({});
15
+ const response = (statusCode, body) => ({
16
+ statusCode,
17
+ headers: { 'content-type': 'application/json' },
18
+ body: JSON.stringify(body),
19
+ });
20
+ /**
21
+ * HTTP API handler for the identify-user routes. Serves BOTH:
22
+ * - `POST /identify-user` — authed (Cognito user-pool JWT authorizer,
23
+ * payload format 2.0). Identity = verified `sub`.
24
+ * - `POST /identify-user-guest` — guest (IAM/SigV4 authorization, payload
25
+ * format 1.0). Identity = verified Cognito Identity Pool `cognitoIdentityId`
26
+ * of an UNAUTHENTICATED identity.
27
+ *
28
+ * The verified {@link Principal} abstracts the two modes; the profile
29
+ * find-or-create + device upsert + attribute write are identity-agnostic. On an
30
+ * authed call carrying `options.previousGuestIdentityId`, the prior guest
31
+ * profile (and its devices) is folded into the authed profile via MergeProfiles.
32
+ */
33
+ export const handler = async (event) => {
34
+ const domainName = process.env[ENV_DOMAIN_NAME];
35
+ if (!domainName) {
36
+ console.error(`Missing required env var ${ENV_DOMAIN_NAME}`);
37
+ return response(500, { error: 'Server misconfiguration' });
38
+ }
39
+ const principal = resolvePrincipal(event);
40
+ if (!principal) {
41
+ return response(401, {
42
+ error: 'Unauthorized: missing or invalid verified caller identity',
43
+ });
44
+ }
45
+ let parsed;
46
+ try {
47
+ parsed = event.body ? JSON.parse(event.body) : undefined;
48
+ }
49
+ catch {
50
+ return response(400, { error: 'Invalid JSON request body' });
51
+ }
52
+ const validation = validateBody(parsed);
53
+ if (!validation.ok || !validation.value) {
54
+ return response(400, { error: validation.error ?? 'Invalid request' });
55
+ }
56
+ const request = validation.value;
57
+ try {
58
+ // 1) Resolve (or create + key) the profile bound to the verified caller.
59
+ // Isolated behind resolveOrCreateProfile so the identity-resolution
60
+ // strategy (authed sub vs guest identityId) is a single seam.
61
+ const { profileId } = await resolveOrCreateProfile(profiles, domainName, principal);
62
+ // 2) Register / update the device object (keyed by stable deviceId) only
63
+ // when device data is present. Token refreshes upsert in place: because
64
+ // PutProfileObject REPLACES the object for a deviceId, read back the
65
+ // prior object first to preserve its immutable `createdAt`.
66
+ if (hasDeviceData(request)) {
67
+ const deviceId = request.options.deviceId;
68
+ const existingCreatedAt = await findExistingDeviceCreatedAt(profiles, domainName, profileId, deviceId);
69
+ const deviceObject = buildDeviceObject(principal, request, existingCreatedAt);
70
+ await withTransientRetry(() => profiles.send(new PutProfileObjectCommand({
71
+ DomainName: domainName,
72
+ ObjectTypeName: OBJECT_TYPE_DEVICE,
73
+ Object: JSON.stringify(deviceObject),
74
+ })));
75
+ }
76
+ // 3) Set user-level / targeting attributes (incl. promoted hasGCM/hasAPNS)
77
+ // on the resolved profile.
78
+ const update = buildProfileUpdate(principal, request);
79
+ await withTransientRetry(() => profiles.send(new UpdateProfileCommand({
80
+ DomainName: domainName,
81
+ ProfileId: profileId,
82
+ EmailAddress: update.emailAddress,
83
+ FirstName: update.firstName,
84
+ LastName: update.lastName,
85
+ Address: update.address
86
+ ? {
87
+ City: update.address.city,
88
+ Country: update.address.country,
89
+ PostalCode: update.address.postalCode,
90
+ Province: update.address.province,
91
+ }
92
+ : undefined,
93
+ Attributes: update.attributes,
94
+ })));
95
+ // 4) Merge-on-sign-in: an AUTHENTICATED caller that carries its prior guest
96
+ // identityId folds the guest profile (+ its devices) into this authed
97
+ // profile via MergeProfiles. Guests never trigger a merge. Best-effort:
98
+ // a merge failure is logged but does not fail the identify result, which
99
+ // already succeeded above.
100
+ const guestIdentityId = request.options?.previousGuestIdentityId;
101
+ if (principal.kind === 'authed' && guestIdentityId) {
102
+ try {
103
+ const outcome = await mergeGuestIntoAuthed(profiles, domainName, guestIdentityId, profileId);
104
+ console.log('[identify] guestMerge', JSON.stringify({ merged: outcome.merged }));
105
+ }
106
+ catch (mergeErr) {
107
+ const name = mergeErr instanceof Error ? mergeErr.name : 'UnknownError';
108
+ console.error('[identify] guestMergeError', JSON.stringify({ name }));
109
+ }
110
+ }
111
+ return response(200, { status: 'ok' });
112
+ }
113
+ catch (err) {
114
+ const message = err instanceof Error ? err.message : 'unknown error';
115
+ // Log only the error name (and HTTP status when present) — never the full
116
+ // error object, whose stack / request input can carry customer content.
117
+ const name = err instanceof Error ? err.name : 'UnknownError';
118
+ const statusCode = err
119
+ ?.$metadata?.httpStatusCode;
120
+ console.error('[identify] customerProfilesError', JSON.stringify({ name, statusCode }));
121
+ return response(500, { error: `Customer Profiles error: ${message}` });
122
+ }
123
+ };
124
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sYW1iZGEvaWRlbnRpZnkvaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLHVCQUF1QixFQUN2QixvQkFBb0IsR0FDckIsTUFBTSxtQ0FBbUMsQ0FBQztBQUUzQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixrQkFBa0IsRUFDbEIsYUFBYSxHQUNkLE1BQU0sY0FBYyxDQUFDO0FBQ3RCLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9ELE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ25FLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzNELE9BQU8sRUFBaUIsZ0JBQWdCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNqRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsZUFBZSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFHekU7OztHQUdHO0FBQ0gsTUFBTSxRQUFRLEdBQUcsSUFBSSxzQkFBc0IsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUVoRCxNQUFNLFFBQVEsR0FBRyxDQUNmLFVBQWtCLEVBQ2xCLElBQXFDLEVBQ0YsRUFBRSxDQUFDLENBQUM7SUFDdkMsVUFBVTtJQUNWLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRTtJQUMvQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7Q0FDM0IsQ0FBQyxDQUFDO0FBRUg7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLEtBQUssRUFDMUIsS0FBb0Is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LEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDeEUsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLFFBQVEsQ0FBQyxHQUFHLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE1BQU0sT0FBTyxHQUFHLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQztRQUNyRSwwRUFBMEU7UUFDMUUsd0VBQXdFO1FBQ3hFLE1BQU0sSUFBSSxHQUFHLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQztRQUM5RCxNQUFNLFVBQVUsR0FBSSxHQUFtRDtZQUNyRSxFQUFFLFNBQVMsRUFBRSxjQUFjLENBQUM7UUFDOUIsT0FBTyxDQUFDLEtBQUssQ0FDWCxrQ0FBa0MsRUFDbEMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUNyQyxDQUFDO1FBQ0YsT0FBTyxRQUFRLENBQUMsR0FBRyxFQUFFLEVBQUUsS0FBSyxFQUFFLDRCQUE0QixPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDekUsQ0FBQztBQUNILENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgQVBJR2F0ZXdheVByb3h5U3RydWN0dXJlZFJlc3VsdFYyIH0gZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQge1xuICBDdXN0b21lclByb2ZpbGVzQ2xpZW50LFxuICBQdXRQcm9maWxlT2JqZWN0Q29tbWFuZCxcbiAgVXBkYXRlUHJvZmlsZUNvbW1hbmQsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jdXN0b21lci1wcm9maWxlcyc7XG5cbmltcG9ydCB7IHZhbGlkYXRlQm9keSB9IGZyb20gJy4vdmFsaWRhdGlvbi5qcyc7XG5pbXBvcnQge1xuICBidWlsZERldmljZU9iamVjdCxcbiAgYnVpbGRQcm9maWxlVXBkYXRlLFxuICBoYXNEZXZpY2VEYXRhLFxufSBmcm9tICcuL21hcHBpbmcuanMnO1xuaW1wb3J0IHsgcmVzb2x2ZU9yQ3JlYXRlUHJvZmlsZSB9IGZyb20gJy4vcHJvZmlsZV9yZXNvbHZlci5qcyc7XG5pbXBvcnQgeyBmaW5kRXhpc3RpbmdEZXZpY2VDcmVhdGVkQXQgfSBmcm9tICcuL2RldmljZV9yZXNvbHZlci5qcyc7XG5pbXBvcnQgeyBtZXJnZUd1ZXN0SW50b0F1dGhlZCB9IGZyb20gJy4vbWVyZ2VfcmVzb2x2ZXIuanMnO1xuaW1wb3J0IHsgSWRlbnRpZnlFdmVudCwgcmVzb2x2ZVByaW5jaXBhbCB9IGZyb20gJy4vcHJpbmNpcGFsLmpzJztcbmltcG9ydCB7IHdpdGhUcmFuc2llbnRSZXRyeSB9IGZyb20gJy4uL3NoYXJlZC9yZXRyeS5qcyc7XG5pbXBvcnQgeyBFTlZfRE9NQUlOX05BTUUsIE9CSkVDVF9UWVBFX0RFVklDRSB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBFcnJvclJlc3BvbnNlLCBTdWNjZXNzUmVzcG9uc2UgfSBmcm9tICcuL3R5cGVzLmpzJztcblxuLyoqXG4gKiBNb2R1bGUtbGV2ZWwgY2xpZW50IHNvIHdhcm0gaW52b2NhdGlvbnMgcmV1c2UgdGhlIGNvbm5lY3Rpb24gcG9vbC4gUmVnaW9uIGlzXG4gKiByZXNvbHZlZCBmcm9tIHRoZSBzdGFuZGFyZCBBV1NfUkVHSU9OIExhbWJkYSBlbnZpcm9ubWVudCB2YXJpYWJsZS5cbiAqL1xuY29uc3QgcHJvZmlsZXMgPSBuZXcgQ3VzdG9tZXJQcm9maWxlc0NsaWVudCh7fSk7XG5cbmNvbnN0IHJlc3BvbnNlID0gKFxuICBzdGF0dXNDb2RlOiBudW1iZXIsXG4gIGJvZHk6IFN1Y2Nlc3NSZXNwb25zZSB8IEVycm9yUmVzcG9uc2UsXG4pOiBBUElHYXRld2F5UHJveHlTdHJ1Y3R1cmVkUmVzdWx0VjIgPT4gKHtcbiAgc3RhdHVzQ29kZSxcbiAgaGVhZGVyczogeyAnY29udGVudC10eXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gIGJvZHk6IEpTT04uc3RyaW5naWZ5KGJvZHkpLFxufSk7XG5cbi8qKlxuICogSFRUUCBBUEkgaGFuZGxlciBmb3IgdGhlIGlkZW50aWZ5LXVzZXIgcm91dGVzLiBTZXJ2ZXMgQk9USDpcbiAqICAgLSBgUE9TVCAvaWRlbnRpZnktdXNlcmAgICAgICAgIOKAlCBhdXRoZWQgKENvZ25pdG8gdXNlci1wb29sIEpXVCBhdXRob3JpemVyLFxuICogICAgIHBheWxvYWQgZm9ybWF0IDIuMCkuIElkZW50aXR5ID0gdmVyaWZpZWQgYHN1YmAuXG4gKiAgIC0gYFBPU1QgL2lkZW50aWZ5LXVzZXItZ3Vlc3RgICDigJQgZ3Vlc3QgKElBTS9TaWdWNCBhdXRob3JpemF0aW9uLCBwYXlsb2FkXG4gKiAgICAgZm9ybWF0IDEuMCkuIElkZW50aXR5ID0gdmVyaWZpZWQgQ29nbml0byBJZGVudGl0eSBQb29sIGBjb2duaXRvSWRlbnRpdHlJZGBcbiAqICAgICBvZiBhbiBVTkFVVEhFTlRJQ0FURUQgaWRlbnRpdHkuXG4gKlxuICogVGhlIHZlcmlmaWVkIHtAbGluayBQcmluY2lwYWx9IGFic3RyYWN0cyB0aGUgdHdvIG1vZGVzOyB0aGUgcHJvZmlsZVxuICogZmluZC1vci1jcmVhdGUgKyBkZXZpY2UgdXBzZXJ0ICsgYXR0cmlidXRlIHdyaXRlIGFyZSBpZGVudGl0eS1hZ25vc3RpYy4gT24gYW5cbiAqIGF1dGhlZCBjYWxsIGNhcnJ5aW5nIGBvcHRpb25zLnByZXZpb3VzR3Vlc3RJZGVudGl0eUlkYCwgdGhlIHByaW9yIGd1ZXN0XG4gKiBwcm9maWxlIChhbmQgaXRzIGRldmljZXMpIGlzIGZvbGRlZCBpbnRvIHRoZSBhdXRoZWQgcHJvZmlsZSB2aWEgTWVyZ2VQcm9maWxlcy5cbiAqL1xuZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSBhc3luYyAoXG4gIGV2ZW50OiBJZGVudGlmeUV2ZW50LFxuKTogUHJvbWlzZTxBUElHYXRld2F5UHJveHlTdHJ1Y3R1cmVkUmVzdWx0VjI+ID0+IHtcbiAgY29uc3QgZG9tYWluTmFtZSA9IHByb2Nlc3MuZW52W0VOVl9ET01BSU5fTkFNRV07XG4gIGlmICghZG9tYWluTmFtZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoYE1pc3NpbmcgcmVxdWlyZWQgZW52IHZhciAke0VOVl9ET01BSU5fTkFNRX1gKTtcbiAgICByZXR1cm4gcmVzcG9uc2UoNTAwLCB7IGVycm9yOiAnU2VydmVyIG1pc2NvbmZpZ3VyYXRpb24nIH0pO1xuICB9XG5cbiAgY29uc3QgcHJpbmNpcGFsID0gcmVzb2x2ZVByaW5jaXBhbChldmVudCk7XG4gIGlmICghcHJpbmNpcGFsKSB7XG4gICAgcmV0dXJuIHJlc3BvbnNlKDQwMSwge1xuICAgICAgZXJyb3I6ICdVbmF1dGhvcml6ZWQ6IG1pc3Npbmcgb3IgaW52YWxpZCB2ZXJpZmllZCBjYWxsZXIgaWRlbnRpdHknLFxuICAgIH0pO1xuICB9XG5cbiAgbGV0IHBhcnNlZDogdW5rbm93bjtcbiAgdHJ5IHtcbiAgICBwYXJzZWQgPSBldmVudC5ib2R5ID8gSlNPTi5wYXJzZShldmVudC5ib2R5KSA6IHVuZGVmaW5lZDtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHJlc3BvbnNlKDQwMCwgeyBlcnJvcjogJ0ludmFsaWQgSlNPTiByZXF1ZXN0IGJvZHknIH0pO1xuICB9XG5cbiAgY29uc3QgdmFsaWRhdGlvbiA9IHZhbGlkYXRlQm9keShwYXJzZWQpO1xuICBpZiAoIXZhbGlkYXRpb24ub2sgfHwgIXZhbGlkYXRpb24udmFsdWUpIHtcbiAgICByZXR1cm4gcmVzcG9uc2UoNDAwLCB7IGVycm9yOiB2YWxpZGF0aW9uLmVycm9yID8/ICdJbnZhbGlkIHJlcXVlc3QnIH0pO1xuICB9XG4gIGNvbnN0IHJlcXVlc3QgPSB2YWxpZGF0aW9uLnZhbHVlO1xuXG4gIHRyeSB7XG4gICAgLy8gMSkgUmVzb2x2ZSAob3IgY3JlYXRlICsga2V5KSB0aGUgcHJvZmlsZSBib3VuZCB0byB0aGUgdmVyaWZpZWQgY2FsbGVyLlxuICAgIC8vICAgIElzb2xhdGVkIGJlaGluZCByZXNvbHZlT3JDcmVhdGVQcm9maWxlIHNvIHRoZSBpZGVudGl0eS1yZXNvbHV0aW9uXG4gICAgLy8gICAgc3RyYXRlZ3kgKGF1dGhlZCBzdWIgdnMgZ3Vlc3QgaWRlbnRpdHlJZCkgaXMgYSBzaW5nbGUgc2VhbS5cbiAgICBjb25zdCB7IHByb2ZpbGVJZCB9ID0gYXdhaXQgcmVzb2x2ZU9yQ3JlYXRlUHJvZmlsZShcbiAgICAgIHByb2ZpbGVzLFxuICAgICAgZG9tYWluTmFtZSxcbiAgICAgIHByaW5jaXBhbCxcbiAgICApO1xuXG4gICAgLy8gMikgUmVnaXN0ZXIgLyB1cGRhdGUgdGhlIGRldmljZSBvYmplY3QgKGtleWVkIGJ5IHN0YWJsZSBkZXZpY2VJZCkgb25seVxuICAgIC8vICAgIHdoZW4gZGV2aWNlIGRhdGEgaXMgcHJlc2VudC4gVG9rZW4gcmVmcmVzaGVzIHVwc2VydCBpbiBwbGFjZTogYmVjYXVzZVxuICAgIC8vICAgIFB1dFByb2ZpbGVPYmplY3QgUkVQTEFDRVMgdGhlIG9iamVjdCBmb3IgYSBkZXZpY2VJZCwgcmVhZCBiYWNrIHRoZVxuICAgIC8vICAgIHByaW9yIG9iamVjdCBmaXJzdCB0byBwcmVzZXJ2ZSBpdHMgaW1tdXRhYmxlIGBjcmVhdGVkQXRgLlxuICAgIGlmIChoYXNEZXZpY2VEYXRhKHJlcXVlc3QpKSB7XG4gICAgICBjb25zdCBkZXZpY2VJZCA9IHJlcXVlc3Qub3B0aW9ucyEuZGV2aWNlSWQhO1xuICAgICAgY29uc3QgZXhpc3RpbmdDcmVhdGVkQXQgPSBhd2FpdCBmaW5kRXhpc3RpbmdEZXZpY2VDcmVhdGVkQXQoXG4gICAgICAgIHByb2ZpbGVzLFxuICAgICAgICBkb21haW5OYW1lLFxuICAgICAgICBwcm9maWxlSWQsXG4gICAgICAgIGRldmljZUlkLFxuICAgICAgKTtcbiAgICAgIGNvbnN0IGRldmljZU9iamVjdCA9IGJ1aWxkRGV2aWNlT2JqZWN0KFxuICAgICAgICBwcmluY2lwYWwsXG4gICAgICAgIHJlcXVlc3QsXG4gICAgICAgIGV4aXN0aW5nQ3JlYXRlZEF0LFxuICAgICAgKTtcbiAgICAgIGF3YWl0IHdpdGhUcmFuc2llbnRSZXRyeSgoKSA9PlxuICAgICAgICBwcm9maWxlcy5zZW5kKFxuICAgICAgICAgIG5ldyBQdXRQcm9maWxlT2JqZWN0Q29tbWFuZCh7XG4gICAgICAgICAgICBEb21haW5OYW1lOiBkb21haW5OYW1lLFxuICAgICAgICAgICAgT2JqZWN0VHlwZU5hbWU6IE9CSkVDVF9UWVBFX0RFVklDRSxcbiAgICAgICAgICAgIE9iamVjdDogSlNPTi5zdHJpbmdpZnkoZGV2aWNlT2JqZWN0KSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgKSxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gMykgU2V0IHVzZXItbGV2ZWwgLyB0YXJnZXRpbmcgYXR0cmlidXRlcyAoaW5jbC4gcHJvbW90ZWQgaGFzR0NNL2hhc0FQTlMpXG4gICAgLy8gICAgb24gdGhlIHJlc29sdmVkIHByb2ZpbGUuXG4gICAgY29uc3QgdXBkYXRlID0gYnVpbGRQcm9maWxlVXBkYXRlKHByaW5jaXBhbCwgcmVxdWVzdCk7XG4gICAgYXdhaXQgd2l0aFRyYW5zaWVudFJldHJ5KCgpID0+XG4gICAgICBwcm9maWxlcy5zZW5kKFxuICAgICAgICBuZXcgVXBkYXRlUHJvZmlsZUNvbW1hbmQoe1xuICAgICAgICAgIERvbWFpbk5hbWU6IGRvbWFpbk5hbWUsXG4gICAgICAgICAgUHJvZmlsZUlkOiBwcm9maWxlSWQsXG4gICAgICAgICAgRW1haWxBZGRyZXNzOiB1cGRhdGUuZW1haWxBZGRyZXNzLFxuICAgICAgICAgIEZpcnN0TmFtZTogdXBkYXRlLmZpcnN0TmFtZSxcbiAgICAgICAgICBMYXN0TmFtZTogdXBkYXRlLmxhc3ROYW1lLFxuICAgICAgICAgIEFkZHJlc3M6IHVwZGF0ZS5hZGRyZXNzXG4gICAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgICBDaXR5OiB1cGRhdGUuYWRkcmVzcy5jaXR5LFxuICAgICAgICAgICAgICAgIENvdW50cnk6IHVwZGF0ZS5hZGRyZXNzLmNvdW50cnksXG4gICAgICAgICAgICAgICAgUG9zdGFsQ29kZTogdXBkYXRlLmFkZHJlc3MucG9zdGFsQ29kZSxcbiAgICAgICAgICAgICAgICBQcm92aW5jZTogdXBkYXRlLmFkZHJlc3MucHJvdmluY2UsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgICAgIEF0dHJpYnV0ZXM6IHVwZGF0ZS5hdHRyaWJ1dGVzLFxuICAgICAgICB9KSxcbiAgICAgICksXG4gICAgKTtcblxuICAgIC8vIDQpIE1lcmdlLW9uLXNpZ24taW46IGFuIEFVVEhFTlRJQ0FURUQgY2FsbGVyIHRoYXQgY2FycmllcyBpdHMgcHJpb3IgZ3Vlc3RcbiAgICAvLyAgICBpZGVudGl0eUlkIGZvbGRzIHRoZSBndWVzdCBwcm9maWxlICgrIGl0cyBkZXZpY2VzKSBpbnRvIHRoaXMgYXV0aGVkXG4gICAgLy8gICAgcHJvZmlsZSB2aWEgTWVyZ2VQcm9maWxlcy4gR3Vlc3RzIG5ldmVyIHRyaWdnZXIgYSBtZXJnZS4gQmVzdC1lZmZvcnQ6XG4gICAgLy8gICAgYSBtZXJnZSBmYWlsdXJlIGlzIGxvZ2dlZCBidXQgZG9lcyBub3QgZmFpbCB0aGUgaWRlbnRpZnkgcmVzdWx0LCB3aGljaFxuICAgIC8vICAgIGFscmVhZHkgc3VjY2VlZGVkIGFib3ZlLlxuICAgIGNvbnN0IGd1ZXN0SWRlbnRpdHlJZCA9IHJlcXVlc3Qub3B0aW9ucz8ucHJldmlvdXNHdWVzdElkZW50aXR5SWQ7XG4gICAgaWYgKHByaW5jaXBhbC5raW5kID09PSAnYXV0aGVkJyAmJiBndWVzdElkZW50aXR5SWQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IG91dGNvbWUgPSBhd2FpdCBtZXJnZUd1ZXN0SW50b0F1dGhlZChcbiAgICAgICAgICBwcm9maWxlcyxcbiAgICAgICAgICBkb21haW5OYW1lLFxuICAgICAgICAgIGd1ZXN0SWRlbnRpdHlJZCxcbiAgICAgICAgICBwcm9maWxlSWQsXG4gICAgICAgICk7XG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICdbaWRlbnRpZnldIGd1ZXN0TWVyZ2UnLFxuICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHsgbWVyZ2VkOiBvdXRjb21lLm1lcmdlZCB9KSxcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKG1lcmdlRXJyKSB7XG4gICAgICAgIGNvbnN0IG5hbWUgPSBtZXJnZUVyciBpbnN0YW5jZW9mIEVycm9yID8gbWVyZ2VFcnIubmFtZSA6ICdVbmtub3duRXJyb3InO1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbaWRlbnRpZnldIGd1ZXN0TWVyZ2VFcnJvcicsIEpTT04uc3RyaW5naWZ5KHsgbmFtZSB9KSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3BvbnNlKDIwMCwgeyBzdGF0dXM6ICdvaycgfSk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogJ3Vua25vd24gZXJyb3InO1xuICAgIC8vIExvZyBvbmx5IHRoZSBlcnJvciBuYW1lIChhbmQgSFRUUCBzdGF0dXMgd2hlbiBwcmVzZW50KSDigJQgbmV2ZXIgdGhlIGZ1bGxcbiAgICAvLyBlcnJvciBvYmplY3QsIHdob3NlIHN0YWNrIC8gcmVxdWVzdCBpbnB1dCBjYW4gY2FycnkgY3VzdG9tZXIgY29udGVudC5cbiAgICBjb25zdCBuYW1lID0gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubmFtZSA6ICdVbmtub3duRXJyb3InO1xuICAgIGNvbnN0IHN0YXR1c0NvZGUgPSAoZXJyIGFzIHsgJG1ldGFkYXRhPzogeyBodHRwU3RhdHVzQ29kZT86IG51bWJlciB9IH0pXG4gICAgICA/LiRtZXRhZGF0YT8uaHR0cFN0YXR1c0NvZGU7XG4gICAgY29uc29sZS5lcnJvcihcbiAgICAgICdbaWRlbnRpZnldIGN1c3RvbWVyUHJvZmlsZXNFcnJvcicsXG4gICAgICBKU09OLnN0cmluZ2lmeSh7IG5hbWUsIHN0YXR1c0NvZGUgfSksXG4gICAgKTtcbiAgICByZXR1cm4gcmVzcG9uc2UoNTAwLCB7IGVycm9yOiBgQ3VzdG9tZXIgUHJvZmlsZXMgZXJyb3I6ICR7bWVzc2FnZX1gIH0pO1xuICB9XG59O1xuIl19
@@ -0,0 +1,61 @@
1
+ import { ChannelType, IdentifyUserRequest } from './types.js';
2
+ import { Principal } from './principal.js';
3
+ /** A standard Customer Profiles address (subset used by this backend). */
4
+ export type ProfileAddress = {
5
+ city?: string;
6
+ country?: string;
7
+ postalCode?: string;
8
+ province?: string;
9
+ };
10
+ /** Person data mapped to UpdateProfile standard fields + Attributes map. */
11
+ export type ProfileUpdate = {
12
+ emailAddress?: string;
13
+ firstName?: string;
14
+ lastName?: string;
15
+ address?: ProfileAddress;
16
+ attributes: Record<string, string>;
17
+ };
18
+ /**
19
+ * Flatten a multi-value attribute into a single string that fits within the
20
+ * Customer Profiles attribute-value limit.
21
+ *
22
+ * - Zero values -> empty string.
23
+ * - Exactly one value -> that element verbatim.
24
+ * - More than one value -> JSON.stringify of the array (preserves all values).
25
+ * The result is truncated to {@link MAX_ATTRIBUTE_LENGTH} characters.
26
+ */
27
+ export declare const flatten: (values: string[]) => string;
28
+ /** Which promoted targeting attribute a channel type activates. */
29
+ export declare const targetingFlagsForChannel: (channelType: ChannelType | undefined) => {
30
+ hasGCM?: string;
31
+ hasAPNS?: string;
32
+ };
33
+ /** True when the request carries enough data to register/update a device. */
34
+ export declare const hasDeviceData: (req: IdentifyUserRequest) => boolean;
35
+ /**
36
+ * Build the UpdateProfile payload (standard fields + Attributes map) for the
37
+ * profile identified by the verified {@link Principal} (authed `sub` or guest
38
+ * `cognitoIdentityId`).
39
+ *
40
+ * SECURITY: `principal.value` comes from an authorizer-verified source (JWT
41
+ * claims or the IAM/Cognito identity). The client-supplied `userId` is stored
42
+ * only as the `appUserId` attribute — never the identity.
43
+ */
44
+ export declare const buildProfileUpdate: (principal: Principal, req: IdentifyUserRequest) => ProfileUpdate;
45
+ /**
46
+ * Build the `_source.*` payload for the AmplifyDevice object type.
47
+ *
48
+ * Keyed UNIQUELY by the stable `deviceId`; `deviceToken` (options.address) is a
49
+ * mutable field so token refreshes upsert the same object in place. Carries the
50
+ * verified identity field (`cognitoSub` for authed, `cognitoIdentityId` for
51
+ * guest) as the PROFILE-resolution key so the device merges into the profile
52
+ * bound to that {@link Principal}.
53
+ *
54
+ * `platform` / `appVersion` come from the options, falling back to the
55
+ * `userProfile.demographic` equivalents. `updatedAt` is server-set on every
56
+ * write; `createdAt` is the IMMUTABLE first-registration time — the caller
57
+ * passes the value read back from the existing device object (if any) so it is
58
+ * preserved across PutProfileObject replacements, otherwise it defaults to now.
59
+ */
60
+ export declare const buildDeviceObject: (principal: Principal, req: IdentifyUserRequest, existingCreatedAt?: string) => Record<string, string>;
61
+ //# sourceMappingURL=mapping.d.ts.map