@nlabs/reaktor 1.19.0 → 1.20.0-reactorbox.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +290 -0
  2. package/docs/GENERATION.md +4 -0
  3. package/lib/actions/apps.js +17 -2
  4. package/lib/actions/contentGeneration.d.ts +19 -1
  5. package/lib/actions/contentGeneration.js +59 -24
  6. package/lib/actions/contentGenerationSources.d.ts +29 -0
  7. package/lib/actions/contentGenerationSources.js +103 -0
  8. package/lib/actions/crm.d.ts +6 -2
  9. package/lib/actions/crm.js +91 -12
  10. package/lib/actions/crmCanonicalOutcomes.d.ts +88 -0
  11. package/lib/actions/crmCanonicalOutcomes.js +398 -0
  12. package/lib/actions/crmCanonicalPolicy.d.ts +92 -0
  13. package/lib/actions/crmCanonicalPolicy.js +104 -0
  14. package/lib/actions/crmCheckoutRecovery.d.ts +72 -0
  15. package/lib/actions/crmCheckoutRecovery.js +413 -0
  16. package/lib/actions/crmCheckoutRecoveryPolicy.d.ts +38 -0
  17. package/lib/actions/crmCheckoutRecoveryPolicy.js +61 -0
  18. package/lib/actions/crmDeliveryEvents.d.ts +24 -0
  19. package/lib/actions/crmDeliveryEvents.js +207 -0
  20. package/lib/actions/crmDeliveryEvidence.d.ts +17 -0
  21. package/lib/actions/crmDeliveryEvidence.js +58 -0
  22. package/lib/actions/crmExecution.d.ts +125 -0
  23. package/lib/actions/crmExecution.js +830 -0
  24. package/lib/actions/crmFinancialEvidence.d.ts +12 -0
  25. package/lib/actions/crmFinancialEvidence.js +60 -0
  26. package/lib/actions/crmInsights.d.ts +60 -0
  27. package/lib/actions/crmInsights.js +225 -0
  28. package/lib/actions/crmLeadFollowups.d.ts +66 -0
  29. package/lib/actions/crmLeadFollowups.js +209 -0
  30. package/lib/actions/crmOutcomes.d.ts +106 -0
  31. package/lib/actions/crmOutcomes.js +289 -0
  32. package/lib/actions/crmRecoveryRetention.d.ts +13 -0
  33. package/lib/actions/crmRecoveryRetention.js +163 -0
  34. package/lib/actions/crmReminderDispatch.d.ts +10 -0
  35. package/lib/actions/crmReminderDispatch.js +65 -0
  36. package/lib/actions/crmSourceOwnership.d.ts +7 -0
  37. package/lib/actions/crmSourceOwnership.js +27 -0
  38. package/lib/actions/crmSupportInsights.d.ts +18 -0
  39. package/lib/actions/crmSupportInsights.js +47 -0
  40. package/lib/actions/crmWinback.d.ts +71 -0
  41. package/lib/actions/crmWinback.js +347 -0
  42. package/lib/actions/crmWinbackConsent.d.ts +29 -0
  43. package/lib/actions/crmWinbackConsent.js +158 -0
  44. package/lib/actions/crmWinbackPolicy.d.ts +33 -0
  45. package/lib/actions/crmWinbackPolicy.js +115 -0
  46. package/lib/actions/crmWinbackShopSource.d.ts +15 -0
  47. package/lib/actions/crmWinbackShopSource.js +63 -0
  48. package/lib/actions/email.d.ts +4 -1
  49. package/lib/actions/email.js +25 -3
  50. package/lib/actions/index.d.ts +56 -0
  51. package/lib/actions/index.js +42 -1
  52. package/lib/actions/merchantAccountExperiment.d.ts +50 -0
  53. package/lib/actions/merchantAccountExperiment.js +424 -0
  54. package/lib/actions/merchantAccountExperimentConfig.d.ts +7 -0
  55. package/lib/actions/merchantAccountExperimentConfig.js +32 -0
  56. package/lib/actions/merchantAccountExperimentHost.d.ts +69 -0
  57. package/lib/actions/merchantAccountExperimentHost.js +92 -0
  58. package/lib/actions/merchantAccountExperimentPolicy.d.ts +66 -0
  59. package/lib/actions/merchantAccountExperimentPolicy.js +121 -0
  60. package/lib/actions/merchantAccountExperimentRelay.d.ts +10 -0
  61. package/lib/actions/merchantAccountExperimentRelay.js +75 -0
  62. package/lib/actions/merchantCalendar.d.ts +34 -0
  63. package/lib/actions/merchantCalendar.js +119 -0
  64. package/lib/actions/merchantCalendarBookings.d.ts +17 -0
  65. package/lib/actions/merchantCalendarBookings.js +113 -0
  66. package/lib/actions/merchantCalendarDelegation.d.ts +80 -0
  67. package/lib/actions/merchantCalendarDelegation.js +353 -0
  68. package/lib/actions/merchantCalendarDiscovery.d.ts +38 -0
  69. package/lib/actions/merchantCalendarDiscovery.js +124 -0
  70. package/lib/actions/merchantCalendarEventHost.d.ts +105 -0
  71. package/lib/actions/merchantCalendarEventHost.js +16 -0
  72. package/lib/actions/merchantCalendarEventPrivacy.d.ts +7 -0
  73. package/lib/actions/merchantCalendarEventPrivacy.js +70 -0
  74. package/lib/actions/merchantCalendarEventRepository.d.ts +121 -0
  75. package/lib/actions/merchantCalendarEventRepository.js +295 -0
  76. package/lib/actions/merchantCalendarEventTransport.d.ts +65 -0
  77. package/lib/actions/merchantCalendarEventTransport.js +132 -0
  78. package/lib/actions/merchantCalendarEvents.d.ts +172 -0
  79. package/lib/actions/merchantCalendarEvents.js +365 -0
  80. package/lib/actions/merchantCalendarInvitationConsent.d.ts +28 -0
  81. package/lib/actions/merchantCalendarInvitationConsent.js +118 -0
  82. package/lib/actions/merchantCalendarManagedSource.d.ts +22 -0
  83. package/lib/actions/merchantCalendarManagedSource.js +24 -0
  84. package/lib/actions/merchantCalendarOAuth.d.ts +92 -0
  85. package/lib/actions/merchantCalendarOAuth.js +502 -0
  86. package/lib/actions/merchantCalendarOAuthConfig.d.ts +4 -0
  87. package/lib/actions/merchantCalendarOAuthConfig.js +30 -0
  88. package/lib/actions/merchantCalendarOAuthRepository.d.ts +57 -0
  89. package/lib/actions/merchantCalendarOAuthRepository.js +108 -0
  90. package/lib/actions/merchantCalendarOAuthTransport.d.ts +4 -0
  91. package/lib/actions/merchantCalendarOAuthTransport.js +25 -0
  92. package/lib/actions/merchantCalendarScopes.d.ts +4 -0
  93. package/lib/actions/merchantCalendarScopes.js +31 -0
  94. package/lib/actions/merchantFinance.d.ts +65 -0
  95. package/lib/actions/merchantFinance.js +244 -0
  96. package/lib/actions/merchantFinanceProvider.d.ts +57 -0
  97. package/lib/actions/merchantFinanceProvider.js +180 -0
  98. package/lib/actions/merchantOperations.d.ts +94 -0
  99. package/lib/actions/merchantOperations.js +489 -0
  100. package/lib/actions/providerCredentials.d.ts +35 -0
  101. package/lib/actions/providerCredentials.js +166 -0
  102. package/lib/actions/s3.d.ts +10 -7
  103. package/lib/actions/s3.js +59 -14
  104. package/lib/actions/supportChannelSources.d.ts +66 -0
  105. package/lib/actions/supportChannelSources.js +183 -0
  106. package/lib/actions/supportCommerce.d.ts +62 -0
  107. package/lib/actions/supportCommerce.js +252 -0
  108. package/lib/actions/supportConnect.d.ts +39 -0
  109. package/lib/actions/supportConnect.js +224 -0
  110. package/lib/actions/supportContext.d.ts +31 -0
  111. package/lib/actions/supportContext.js +88 -0
  112. package/lib/actions/supportConversation.d.ts +102 -0
  113. package/lib/actions/supportConversation.js +100 -0
  114. package/lib/actions/supportEmailEvents.d.ts +20 -0
  115. package/lib/actions/supportEmailEvents.js +130 -0
  116. package/lib/actions/supportEmailHandoff.d.ts +114 -0
  117. package/lib/actions/supportEmailHandoff.js +356 -0
  118. package/lib/actions/supportOperations.d.ts +32 -0
  119. package/lib/actions/supportOperations.js +74 -0
  120. package/lib/actions/supportPolicy.d.ts +9 -0
  121. package/lib/actions/supportPolicy.js +44 -0
  122. package/lib/actions/supportPurposeConsent.d.ts +32 -0
  123. package/lib/actions/supportPurposeConsent.js +196 -0
  124. package/lib/actions/supportPurposePrivacy.d.ts +15 -0
  125. package/lib/actions/supportPurposePrivacy.js +181 -0
  126. package/lib/actions/supportSemanticReview.d.ts +64 -0
  127. package/lib/actions/supportSemanticReview.js +239 -0
  128. package/lib/actions/users.js +22 -3
  129. package/lib/constants/collections.d.ts +2 -1
  130. package/lib/constants/collections.js +2 -1
  131. package/lib/mutations/crm/crm.js +395 -7
  132. package/lib/queries/crm/crm.js +251 -5
  133. package/lib/types/auth.types.d.ts +2 -0
  134. package/lib/types/crm.types.d.ts +6 -0
  135. package/lib/types/users.types.d.ts +2 -0
  136. package/lib/utils/authUtils.js +17 -1
  137. package/lib/utils/graphqlUtils.d.ts +2 -0
  138. package/lib/utils/stripeUtils.ts.bak +1 -1
  139. package/package.json +8 -6
@@ -0,0 +1,24 @@
1
+ import type { ApiContext } from '../types/auth.types.js';
2
+ export interface CrmDeliveryEventInput {
3
+ appId: string;
4
+ bounceType?: 'Permanent' | 'Transient' | 'Undetermined';
5
+ eventId: string;
6
+ kind: 'delivered' | 'bounce' | 'complaint';
7
+ occurredAt: number;
8
+ providerMessageId: string;
9
+ receiptId: string;
10
+ }
11
+ export interface CrmDeliveryEventReceipt {
12
+ bounceType?: CrmDeliveryEventInput['bounceType'];
13
+ id: string;
14
+ kind: CrmDeliveryEventInput['kind'];
15
+ occurredAt: number;
16
+ receiptId: string;
17
+ suppressed: boolean;
18
+ }
19
+ /** Trusted server relay scope authenticates a deployed relay; it does not verify unsigned SNS claims. */
20
+ export declare const ingestCrmDeliveryEvent: (context: ApiContext, input: CrmDeliveryEventInput, now?: () => number) => Promise<CrmDeliveryEventReceipt>;
21
+ /** Native administrator privacy action; persistent minimized tombstone prevents prior accepted-message reingestion. */
22
+ export declare const eraseCrmDeliveryEvidence: (context: ApiContext, appId: string, userId: string, now?: () => number) => Promise<{
23
+ erased: number;
24
+ }>;
@@ -0,0 +1,207 @@
1
+ /* eslint-disable no-await-in-loop, no-underscore-dangle -- Sequential collection setup and native graph metadata. */ import { aql } from 'arangojs';
2
+ import { createHash } from 'node:crypto';
3
+ import { scopeContextToApp } from '../utils/appContext.js';
4
+ import { useDb } from '../utils/arangodbUtils.js';
5
+ import { requireCrmAdministrator } from './crm.js';
6
+ import { crmDeliveryPrivacyKey } from './crmDeliveryEvidence.js';
7
+ import { authorizeCrmExecutionGrant, ensureOwnerExecutionCollection } from './crmExecution.js';
8
+ const digest = (value)=>createHash('sha256').update(JSON.stringify(value)).digest('hex');
9
+ const identifier = (value)=>typeof value === 'string' && /^[\w.-]{1,200}$/.test(value);
10
+ const valid = (input, at)=>Boolean(input && identifier(input.appId) && input.appId.length <= 100 && identifier(input.eventId) && /^[a-f0-9]{64}$/.test(input.receiptId) && identifier(input.providerMessageId) && [
11
+ 'delivered',
12
+ 'bounce',
13
+ 'complaint'
14
+ ].includes(input.kind) && Object.keys(input).every((key)=>[
15
+ 'appId',
16
+ 'bounceType',
17
+ 'eventId',
18
+ 'kind',
19
+ 'occurredAt',
20
+ 'providerMessageId',
21
+ 'receiptId'
22
+ ].includes(key)) && (input.kind === 'bounce' ? [
23
+ 'Permanent',
24
+ 'Transient',
25
+ 'Undetermined'
26
+ ].includes(input.bounceType || '') : input.bounceType === undefined) && Number.isSafeInteger(input.occurredAt) && input.occurredAt <= at + 60000 && input.occurredAt >= at - 30 * 86400000);
27
+ /** Trusted server relay scope authenticates a deployed relay; it does not verify unsigned SNS claims. */ export const ingestCrmDeliveryEvent = async (context, input, now = Date.now)=>{
28
+ if (!valid(input, now())) {
29
+ throw new Error('invalid_crm_delivery_event');
30
+ }
31
+ const grant = await authorizeCrmExecutionGrant(context, input.appId, 'crm:delivery_events', now());
32
+ const db = useDb(grant.databaseName);
33
+ const documents = [
34
+ 'crmDeliveryEvents',
35
+ 'crmExecutionReceipts',
36
+ 'crmDeliveryPrivacy',
37
+ 'users'
38
+ ];
39
+ const edges = [
40
+ 'hasCrmExecutionDelivery',
41
+ 'hasUserCrmDelivery'
42
+ ];
43
+ for (const name of [
44
+ ...documents,
45
+ ...edges
46
+ ]){
47
+ await ensureOwnerExecutionCollection(db, name, edges.includes(name));
48
+ }
49
+ const tx = await db.beginTransaction({
50
+ exclusive: [
51
+ ...documents,
52
+ ...edges
53
+ ]
54
+ }, {
55
+ lockTimeout: 10
56
+ });
57
+ const query = (value)=>tx.step(()=>db.query(value));
58
+ try {
59
+ const receipt = await query(aql`RETURN DOCUMENT('crmExecutionReceipts', ${input.receiptId})`).then((cursor)=>cursor.next());
60
+ if (!receipt || receipt.appId !== input.appId || receipt.id !== input.receiptId || receipt.state !== 'applied' || ![
61
+ 'crm_outreach',
62
+ 'scheduling_reminder'
63
+ ].includes(receipt.kind) || receipt.providerMessageId !== input.providerMessageId || !Number.isSafeInteger(receipt.providerAcceptedAt) || receipt.providerAcceptedAt < receipt.createdAt || receipt.providerAcceptedAt > now() || input.occurredAt < receipt.providerAcceptedAt - 60000 || input.occurredAt > receipt.providerAcceptedAt + 30 * 86400000) {
64
+ throw new Error('crm_delivery_accepted_message_required');
65
+ }
66
+ const user = await query(aql`RETURN DOCUMENT('users', ${receipt.recipient.id})`).then((cursor)=>cursor.next());
67
+ if (!user || user.deleted === true || user.email !== receipt.recipient.email || user._key !== receipt.recipient.id) {
68
+ throw new Error('crm_delivery_recipient_changed');
69
+ }
70
+ const erased = await query(aql`RETURN DOCUMENT('crmDeliveryPrivacy', ${crmDeliveryPrivacyKey(input.appId, user._key)})`).then((cursor)=>cursor.next());
71
+ if (erased && receipt.providerAcceptedAt <= erased.erasedAt) {
72
+ throw new Error('crm_delivery_privacy_erased');
73
+ }
74
+ const id = digest([
75
+ input.appId,
76
+ input.eventId
77
+ ]);
78
+ const fingerprint = digest([
79
+ input.appId,
80
+ input.receiptId,
81
+ input.providerMessageId,
82
+ input.kind,
83
+ input.bounceType,
84
+ input.occurredAt
85
+ ]);
86
+ const previous = await query(aql`RETURN DOCUMENT('crmDeliveryEvents', ${id})`).then((cursor)=>cursor.next());
87
+ if (previous && previous.fingerprint !== fingerprint) {
88
+ throw new Error('crm_delivery_event_conflict');
89
+ }
90
+ const current = await authorizeCrmExecutionGrant(context, input.appId, 'crm:delivery_events', now());
91
+ if (current.id !== grant.id || current.hash !== grant.hash || current.databaseName !== grant.databaseName) {
92
+ throw new Error('crm_delivery_relay_changed');
93
+ }
94
+ const suppressed = input.kind !== 'delivered' || Boolean(user.emailDeliveryStatus && user.emailDeliveryStatus !== 'valid');
95
+ if (previous) {
96
+ await tx.commit();
97
+ return {
98
+ ...previous.receipt,
99
+ suppressed
100
+ };
101
+ }
102
+ const timeline = await query(aql`FOR event IN crmDeliveryEvents
103
+ FILTER event.appId == ${input.appId} AND event.receipt.receiptId == ${input.receiptId}
104
+ LIMIT 32 RETURN event._key`).then((cursor)=>cursor.all());
105
+ if (timeline.length >= 32) {
106
+ throw new Error('crm_delivery_timeline_limit');
107
+ }
108
+ const result = {
109
+ bounceType: input.bounceType,
110
+ id,
111
+ kind: input.kind,
112
+ occurredAt: input.occurredAt,
113
+ receiptId: input.receiptId,
114
+ suppressed
115
+ };
116
+ await query(aql`INSERT ${{
117
+ _key: id,
118
+ appId: input.appId,
119
+ fingerprint,
120
+ receipt: result,
121
+ receivedAt: now(),
122
+ relayGrantId: grant.id
123
+ }} INTO crmDeliveryEvents`);
124
+ if (input.kind !== 'delivered') {
125
+ // Any bounce holds sends; classification is evidence, not a claim that a transient bounce is permanent.
126
+ const status = input.kind === 'complaint' || user.emailDeliveryStatus === 'complaint' ? 'complaint' : 'bounce_hold';
127
+ await query(aql`UPDATE ${user._key} WITH {emailDeliveryStatus: ${status}, modified: ${now()}} IN users`);
128
+ }
129
+ await query(aql`INSERT ${{
130
+ _from: `crmExecutionReceipts/${input.receiptId}`,
131
+ _key: id,
132
+ _to: `crmDeliveryEvents/${id}`,
133
+ appId: input.appId
134
+ }} INTO hasCrmExecutionDelivery`);
135
+ await query(aql`INSERT ${{
136
+ _from: `users/${user._key}`,
137
+ _key: id,
138
+ _to: `crmDeliveryEvents/${id}`,
139
+ appId: input.appId
140
+ }} INTO hasUserCrmDelivery`);
141
+ const closing = await authorizeCrmExecutionGrant(context, input.appId, 'crm:delivery_events', now());
142
+ if (closing.id !== grant.id || closing.hash !== grant.hash || closing.databaseName !== grant.databaseName) {
143
+ throw new Error('crm_delivery_relay_changed');
144
+ }
145
+ await tx.commit();
146
+ return result;
147
+ } catch (error) {
148
+ await tx.abort();
149
+ throw error;
150
+ }
151
+ };
152
+ /** Native administrator privacy action; persistent minimized tombstone prevents prior accepted-message reingestion. */ export const eraseCrmDeliveryEvidence = async (context, appId, userId, now = Date.now)=>{
153
+ requireCrmAdministrator(context);
154
+ if (!identifier(appId) || appId.length > 100 || !identifier(userId) || userId.length > 100) {
155
+ throw new Error('invalid_crm_delivery_erasure');
156
+ }
157
+ const scoped = await scopeContextToApp(context, appId);
158
+ const db = useDb(scoped.databaseName);
159
+ const documents = [
160
+ 'crmDeliveryEvents',
161
+ 'crmDeliveryPrivacy'
162
+ ];
163
+ const edges = [
164
+ 'hasCrmExecutionDelivery',
165
+ 'hasUserCrmDelivery'
166
+ ];
167
+ for (const name of [
168
+ ...documents,
169
+ ...edges
170
+ ]){
171
+ await ensureOwnerExecutionCollection(db, name, edges.includes(name));
172
+ }
173
+ const tx = await db.beginTransaction({
174
+ exclusive: [
175
+ ...documents,
176
+ ...edges
177
+ ]
178
+ }, {
179
+ lockTimeout: 10
180
+ });
181
+ const query = (value)=>tx.step(()=>db.query(value));
182
+ try {
183
+ const ids = await query(aql`FOR edge IN hasUserCrmDelivery
184
+ FILTER edge.appId == ${appId} AND edge._from == ${`users/${userId}`}
185
+ RETURN PARSE_IDENTIFIER(edge._to).key`).then((cursor)=>cursor.all());
186
+ const id = crmDeliveryPrivacyKey(appId, userId);
187
+ await query(aql`UPSERT {_key: ${id}} INSERT ${{
188
+ _key: id,
189
+ erasedAt: now()
190
+ }}
191
+ UPDATE {erasedAt: MAX([OLD.erasedAt, ${now()}])} IN crmDeliveryPrivacy`);
192
+ await query(aql`FOR event IN crmDeliveryEvents FILTER event._key IN ${ids} REMOVE event IN crmDeliveryEvents`);
193
+ await query(aql`FOR edge IN hasCrmExecutionDelivery FILTER edge.appId == ${appId}
194
+ AND PARSE_IDENTIFIER(edge._to).key IN ${ids} REMOVE edge IN hasCrmExecutionDelivery`);
195
+ await query(aql`FOR edge IN hasUserCrmDelivery FILTER edge.appId == ${appId}
196
+ AND edge._from == ${`users/${userId}`} REMOVE edge IN hasUserCrmDelivery`);
197
+ await tx.commit();
198
+ return {
199
+ erased: ids.length
200
+ };
201
+ } catch (error) {
202
+ await tx.abort();
203
+ throw error;
204
+ }
205
+ };
206
+
207
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvYWN0aW9ucy9jcm1EZWxpdmVyeUV2ZW50cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1hd2FpdC1pbi1sb29wLCBuby11bmRlcnNjb3JlLWRhbmdsZSAtLSBTZXF1ZW50aWFsIGNvbGxlY3Rpb24gc2V0dXAgYW5kIG5hdGl2ZSBncmFwaCBtZXRhZGF0YS4gKi9cbmltcG9ydCB7YXFsfSBmcm9tICdhcmFuZ29qcyc7XG5pbXBvcnQge2NyZWF0ZUhhc2h9IGZyb20gJ25vZGU6Y3J5cHRvJztcblxuaW1wb3J0IHtzY29wZUNvbnRleHRUb0FwcH0gZnJvbSAnLi4vdXRpbHMvYXBwQ29udGV4dC5qcyc7XG5pbXBvcnQge3VzZURifSBmcm9tICcuLi91dGlscy9hcmFuZ29kYlV0aWxzLmpzJztcbmltcG9ydCB7cmVxdWlyZUNybUFkbWluaXN0cmF0b3J9IGZyb20gJy4vY3JtLmpzJztcbmltcG9ydCB7Y3JtRGVsaXZlcnlQcml2YWN5S2V5fSBmcm9tICcuL2NybURlbGl2ZXJ5RXZpZGVuY2UuanMnO1xuaW1wb3J0IHthdXRob3JpemVDcm1FeGVjdXRpb25HcmFudCwgZW5zdXJlT3duZXJFeGVjdXRpb25Db2xsZWN0aW9ufSBmcm9tICcuL2NybUV4ZWN1dGlvbi5qcyc7XG5cbmltcG9ydCB0eXBlIHtBcGlDb250ZXh0fSBmcm9tICcuLi90eXBlcy9hdXRoLnR5cGVzLmpzJztcbmltcG9ydCB0eXBlIHtDcm1FeGVjdXRpb25SZWNlaXB0fSBmcm9tICcuL2NybUV4ZWN1dGlvbi5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ3JtRGVsaXZlcnlFdmVudElucHV0IHtcbiAgYXBwSWQ6IHN0cmluZztcbiAgYm91bmNlVHlwZT86ICdQZXJtYW5lbnQnIHwgJ1RyYW5zaWVudCcgfCAnVW5kZXRlcm1pbmVkJztcbiAgZXZlbnRJZDogc3RyaW5nO1xuICBraW5kOiAnZGVsaXZlcmVkJyB8ICdib3VuY2UnIHwgJ2NvbXBsYWludCc7XG4gIG9jY3VycmVkQXQ6IG51bWJlcjtcbiAgcHJvdmlkZXJNZXNzYWdlSWQ6IHN0cmluZztcbiAgcmVjZWlwdElkOiBzdHJpbmc7XG59XG5leHBvcnQgaW50ZXJmYWNlIENybURlbGl2ZXJ5RXZlbnRSZWNlaXB0IHtcbiAgYm91bmNlVHlwZT86IENybURlbGl2ZXJ5RXZlbnRJbnB1dFsnYm91bmNlVHlwZSddO1xuICBpZDogc3RyaW5nO1xuICBraW5kOiBDcm1EZWxpdmVyeUV2ZW50SW5wdXRbJ2tpbmQnXTtcbiAgb2NjdXJyZWRBdDogbnVtYmVyO1xuICByZWNlaXB0SWQ6IHN0cmluZztcbiAgc3VwcHJlc3NlZDogYm9vbGVhbjtcbn1cbmNvbnN0IGRpZ2VzdCA9ICh2YWx1ZTogdW5rbm93bik6IHN0cmluZyA9PiBjcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoSlNPTi5zdHJpbmdpZnkodmFsdWUpKS5kaWdlc3QoJ2hleCcpO1xuY29uc3QgaWRlbnRpZmllciA9ICh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIHN0cmluZyA9PiB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIC9eW1xcdy4tXXsxLDIwMH0kLy50ZXN0KHZhbHVlKTtcbmNvbnN0IHZhbGlkID0gKGlucHV0OiBDcm1EZWxpdmVyeUV2ZW50SW5wdXQsIGF0OiBudW1iZXIpOiBib29sZWFuID0+IEJvb2xlYW4oaW5wdXQgJiYgaWRlbnRpZmllcihpbnB1dC5hcHBJZClcbiAgJiYgaW5wdXQuYXBwSWQubGVuZ3RoIDw9IDEwMCAmJiBpZGVudGlmaWVyKGlucHV0LmV2ZW50SWQpICYmIC9eW2EtZjAtOV17NjR9JC8udGVzdChpbnB1dC5yZWNlaXB0SWQpXG4gICYmIGlkZW50aWZpZXIoaW5wdXQucHJvdmlkZXJNZXNzYWdlSWQpICYmIFsnZGVsaXZlcmVkJywgJ2JvdW5jZScsICdjb21wbGFpbnQnXS5pbmNsdWRlcyhpbnB1dC5raW5kKVxuICAmJiBPYmplY3Qua2V5cyhpbnB1dCkuZXZlcnkoKGtleSkgPT4gWydhcHBJZCcsICdib3VuY2VUeXBlJywgJ2V2ZW50SWQnLCAna2luZCcsICdvY2N1cnJlZEF0JywgJ3Byb3ZpZGVyTWVzc2FnZUlkJywgJ3JlY2VpcHRJZCddLmluY2x1ZGVzKGtleSkpXG4gICYmIChpbnB1dC5raW5kID09PSAnYm91bmNlJyA/IFsnUGVybWFuZW50JywgJ1RyYW5zaWVudCcsICdVbmRldGVybWluZWQnXS5pbmNsdWRlcyhpbnB1dC5ib3VuY2VUeXBlIHx8ICcnKSA6IGlucHV0LmJvdW5jZVR5cGUgPT09IHVuZGVmaW5lZClcbiAgJiYgTnVtYmVyLmlzU2FmZUludGVnZXIoaW5wdXQub2NjdXJyZWRBdCkgJiYgaW5wdXQub2NjdXJyZWRBdCA8PSBhdCArIDYwMDAwXG4gICYmIGlucHV0Lm9jY3VycmVkQXQgPj0gYXQgLSAoMzAgKiA4NjQwMDAwMCkpO1xuXG4vKiogVHJ1c3RlZCBzZXJ2ZXIgcmVsYXkgc2NvcGUgYXV0aGVudGljYXRlcyBhIGRlcGxveWVkIHJlbGF5OyBpdCBkb2VzIG5vdCB2ZXJpZnkgdW5zaWduZWQgU05TIGNsYWltcy4gKi9cbmV4cG9ydCBjb25zdCBpbmdlc3RDcm1EZWxpdmVyeUV2ZW50ID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LCBpbnB1dDogQ3JtRGVsaXZlcnlFdmVudElucHV0LCBub3cgPSBEYXRlLm5vd1xuKTogUHJvbWlzZTxDcm1EZWxpdmVyeUV2ZW50UmVjZWlwdD4gPT4ge1xuICBpZighdmFsaWQoaW5wdXQsIG5vdygpKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZF9jcm1fZGVsaXZlcnlfZXZlbnQnKTtcbiAgfVxuICBjb25zdCBncmFudCA9IGF3YWl0IGF1dGhvcml6ZUNybUV4ZWN1dGlvbkdyYW50KGNvbnRleHQsIGlucHV0LmFwcElkLCAnY3JtOmRlbGl2ZXJ5X2V2ZW50cycsIG5vdygpKTtcbiAgY29uc3QgZGIgPSB1c2VEYihncmFudC5kYXRhYmFzZU5hbWUpO1xuICBjb25zdCBkb2N1bWVudHMgPSBbJ2NybURlbGl2ZXJ5RXZlbnRzJywgJ2NybUV4ZWN1dGlvblJlY2VpcHRzJywgJ2NybURlbGl2ZXJ5UHJpdmFjeScsICd1c2VycyddO1xuICBjb25zdCBlZGdlcyA9IFsnaGFzQ3JtRXhlY3V0aW9uRGVsaXZlcnknLCAnaGFzVXNlckNybURlbGl2ZXJ5J107XG4gIGZvcihjb25zdCBuYW1lIG9mIFsuLi5kb2N1bWVudHMsIC4uLmVkZ2VzXSkge1xuICAgIGF3YWl0IGVuc3VyZU93bmVyRXhlY3V0aW9uQ29sbGVjdGlvbihkYiwgbmFtZSwgZWRnZXMuaW5jbHVkZXMobmFtZSkpO1xuICB9XG4gIGNvbnN0IHR4ID0gYXdhaXQgZGIuYmVnaW5UcmFuc2FjdGlvbih7ZXhjbHVzaXZlOiBbLi4uZG9jdW1lbnRzLCAuLi5lZGdlc119LCB7bG9ja1RpbWVvdXQ6IDEwfSk7XG4gIGNvbnN0IHF1ZXJ5ID0gKHZhbHVlOiBSZXR1cm5UeXBlPHR5cGVvZiBhcWw+KSA9PiB0eC5zdGVwKCgpID0+IGRiLnF1ZXJ5KHZhbHVlKSk7XG4gIHRyeSB7XG4gICAgY29uc3QgcmVjZWlwdCA9IGF3YWl0IHF1ZXJ5KGFxbGBSRVRVUk4gRE9DVU1FTlQoJ2NybUV4ZWN1dGlvblJlY2VpcHRzJywgJHtpbnB1dC5yZWNlaXB0SWR9KWApXG4gICAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSBhcyBDcm1FeGVjdXRpb25SZWNlaXB0O1xuICAgIGlmKCFyZWNlaXB0IHx8IHJlY2VpcHQuYXBwSWQgIT09IGlucHV0LmFwcElkIHx8IHJlY2VpcHQuaWQgIT09IGlucHV0LnJlY2VpcHRJZCB8fCByZWNlaXB0LnN0YXRlICE9PSAnYXBwbGllZCdcbiAgICAgIHx8ICFbJ2NybV9vdXRyZWFjaCcsICdzY2hlZHVsaW5nX3JlbWluZGVyJ10uaW5jbHVkZXMocmVjZWlwdC5raW5kKSB8fCByZWNlaXB0LnByb3ZpZGVyTWVzc2FnZUlkICE9PSBpbnB1dC5wcm92aWRlck1lc3NhZ2VJZFxuICAgICAgfHwgIU51bWJlci5pc1NhZmVJbnRlZ2VyKHJlY2VpcHQucHJvdmlkZXJBY2NlcHRlZEF0KSB8fCByZWNlaXB0LnByb3ZpZGVyQWNjZXB0ZWRBdCEgPCByZWNlaXB0LmNyZWF0ZWRBdFxuICAgICAgfHwgcmVjZWlwdC5wcm92aWRlckFjY2VwdGVkQXQhID4gbm93KCkgfHwgaW5wdXQub2NjdXJyZWRBdCA8IHJlY2VpcHQucHJvdmlkZXJBY2NlcHRlZEF0ISAtIDYwMDAwXG4gICAgICB8fCBpbnB1dC5vY2N1cnJlZEF0ID4gcmVjZWlwdC5wcm92aWRlckFjY2VwdGVkQXQhICsgKDMwICogODY0MDAwMDApKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NybV9kZWxpdmVyeV9hY2NlcHRlZF9tZXNzYWdlX3JlcXVpcmVkJyk7XG4gICAgfVxuICAgIGNvbnN0IHVzZXIgPSBhd2FpdCBxdWVyeShhcWxgUkVUVVJOIERPQ1VNRU5UKCd1c2VycycsICR7cmVjZWlwdC5yZWNpcGllbnQuaWR9KWApXG4gICAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSBhcyB7XG4gICAgICBfa2V5OiBzdHJpbmc7IGRlbGV0ZWQ/OiBib29sZWFuOyBlbWFpbDogc3RyaW5nOyBlbWFpbERlbGl2ZXJ5U3RhdHVzPzogc3RyaW5nO1xuICAgIH07XG4gICAgaWYoIXVzZXIgfHwgdXNlci5kZWxldGVkID09PSB0cnVlIHx8IHVzZXIuZW1haWwgIT09IHJlY2VpcHQucmVjaXBpZW50LmVtYWlsIHx8IHVzZXIuX2tleSAhPT0gcmVjZWlwdC5yZWNpcGllbnQuaWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY3JtX2RlbGl2ZXJ5X3JlY2lwaWVudF9jaGFuZ2VkJyk7XG4gICAgfVxuICAgIGNvbnN0IGVyYXNlZCA9IGF3YWl0IHF1ZXJ5KGFxbGBSRVRVUk4gRE9DVU1FTlQoJ2NybURlbGl2ZXJ5UHJpdmFjeScsICR7Y3JtRGVsaXZlcnlQcml2YWN5S2V5KGlucHV0LmFwcElkLCB1c2VyLl9rZXkpfSlgKVxuICAgICAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLm5leHQoKSkgYXMge2VyYXNlZEF0OiBudW1iZXJ9IHwgdW5kZWZpbmVkO1xuICAgIGlmKGVyYXNlZCAmJiByZWNlaXB0LnByb3ZpZGVyQWNjZXB0ZWRBdCEgPD0gZXJhc2VkLmVyYXNlZEF0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NybV9kZWxpdmVyeV9wcml2YWN5X2VyYXNlZCcpO1xuICAgIH1cbiAgICBjb25zdCBpZCA9IGRpZ2VzdChbaW5wdXQuYXBwSWQsIGlucHV0LmV2ZW50SWRdKTtcbiAgICBjb25zdCBmaW5nZXJwcmludCA9IGRpZ2VzdChbaW5wdXQuYXBwSWQsIGlucHV0LnJlY2VpcHRJZCwgaW5wdXQucHJvdmlkZXJNZXNzYWdlSWQsIGlucHV0LmtpbmQsXG4gICAgICBpbnB1dC5ib3VuY2VUeXBlLCBpbnB1dC5vY2N1cnJlZEF0XSk7XG4gICAgY29uc3QgcHJldmlvdXMgPSBhd2FpdCBxdWVyeShhcWxgUkVUVVJOIERPQ1VNRU5UKCdjcm1EZWxpdmVyeUV2ZW50cycsICR7aWR9KWApXG4gICAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSBhcyB7ZmluZ2VycHJpbnQ6IHN0cmluZzsgcmVjZWlwdDogQ3JtRGVsaXZlcnlFdmVudFJlY2VpcHR9IHwgdW5kZWZpbmVkO1xuICAgIGlmKHByZXZpb3VzICYmIHByZXZpb3VzLmZpbmdlcnByaW50ICE9PSBmaW5nZXJwcmludCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjcm1fZGVsaXZlcnlfZXZlbnRfY29uZmxpY3QnKTtcbiAgICB9XG4gICAgY29uc3QgY3VycmVudCA9IGF3YWl0IGF1dGhvcml6ZUNybUV4ZWN1dGlvbkdyYW50KGNvbnRleHQsIGlucHV0LmFwcElkLCAnY3JtOmRlbGl2ZXJ5X2V2ZW50cycsIG5vdygpKTtcbiAgICBpZihjdXJyZW50LmlkICE9PSBncmFudC5pZCB8fCBjdXJyZW50Lmhhc2ggIT09IGdyYW50Lmhhc2ggfHwgY3VycmVudC5kYXRhYmFzZU5hbWUgIT09IGdyYW50LmRhdGFiYXNlTmFtZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjcm1fZGVsaXZlcnlfcmVsYXlfY2hhbmdlZCcpO1xuICAgIH1cbiAgICBjb25zdCBzdXBwcmVzc2VkID0gaW5wdXQua2luZCAhPT0gJ2RlbGl2ZXJlZCcgfHwgQm9vbGVhbih1c2VyLmVtYWlsRGVsaXZlcnlTdGF0dXMgJiYgdXNlci5lbWFpbERlbGl2ZXJ5U3RhdHVzICE9PSAndmFsaWQnKTtcbiAgICBpZihwcmV2aW91cykge1xuICAgICAgYXdhaXQgdHguY29tbWl0KCk7XG4gICAgICByZXR1cm4gey4uLnByZXZpb3VzLnJlY2VpcHQsIHN1cHByZXNzZWR9O1xuICAgIH1cbiAgICBjb25zdCB0aW1lbGluZSA9IGF3YWl0IHF1ZXJ5KGFxbGBGT1IgZXZlbnQgSU4gY3JtRGVsaXZlcnlFdmVudHNcbiAgICAgIEZJTFRFUiBldmVudC5hcHBJZCA9PSAke2lucHV0LmFwcElkfSBBTkQgZXZlbnQucmVjZWlwdC5yZWNlaXB0SWQgPT0gJHtpbnB1dC5yZWNlaXB0SWR9XG4gICAgICBMSU1JVCAzMiBSRVRVUk4gZXZlbnQuX2tleWApLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLmFsbCgpKSBhcyBzdHJpbmdbXTtcbiAgICBpZih0aW1lbGluZS5sZW5ndGggPj0gMzIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY3JtX2RlbGl2ZXJ5X3RpbWVsaW5lX2xpbWl0Jyk7XG4gICAgfVxuICAgIGNvbnN0IHJlc3VsdDogQ3JtRGVsaXZlcnlFdmVudFJlY2VpcHQgPSB7Ym91bmNlVHlwZTogaW5wdXQuYm91bmNlVHlwZSwgaWQsIGtpbmQ6IGlucHV0LmtpbmQsXG4gICAgICBvY2N1cnJlZEF0OiBpbnB1dC5vY2N1cnJlZEF0LCByZWNlaXB0SWQ6IGlucHV0LnJlY2VpcHRJZCwgc3VwcHJlc3NlZH07XG4gICAgYXdhaXQgcXVlcnkoYXFsYElOU0VSVCAke3tfa2V5OiBpZCwgYXBwSWQ6IGlucHV0LmFwcElkLCBmaW5nZXJwcmludCwgcmVjZWlwdDogcmVzdWx0LCByZWNlaXZlZEF0OiBub3coKSxcbiAgICAgIHJlbGF5R3JhbnRJZDogZ3JhbnQuaWR9fSBJTlRPIGNybURlbGl2ZXJ5RXZlbnRzYCk7XG4gICAgaWYoaW5wdXQua2luZCAhPT0gJ2RlbGl2ZXJlZCcpIHtcbiAgICAgIC8vIEFueSBib3VuY2UgaG9sZHMgc2VuZHM7IGNsYXNzaWZpY2F0aW9uIGlzIGV2aWRlbmNlLCBub3QgYSBjbGFpbSB0aGF0IGEgdHJhbnNpZW50IGJvdW5jZSBpcyBwZXJtYW5lbnQuXG4gICAgICBjb25zdCBzdGF0dXMgPSBpbnB1dC5raW5kID09PSAnY29tcGxhaW50JyB8fCB1c2VyLmVtYWlsRGVsaXZlcnlTdGF0dXMgPT09ICdjb21wbGFpbnQnID8gJ2NvbXBsYWludCcgOiAnYm91bmNlX2hvbGQnO1xuICAgICAgYXdhaXQgcXVlcnkoYXFsYFVQREFURSAke3VzZXIuX2tleX0gV0lUSCB7ZW1haWxEZWxpdmVyeVN0YXR1czogJHtzdGF0dXN9LCBtb2RpZmllZDogJHtub3coKX19IElOIHVzZXJzYCk7XG4gICAgfVxuICAgIGF3YWl0IHF1ZXJ5KGFxbGBJTlNFUlQgJHt7X2Zyb206IGBjcm1FeGVjdXRpb25SZWNlaXB0cy8ke2lucHV0LnJlY2VpcHRJZH1gLCBfa2V5OiBpZCxcbiAgICAgIF90bzogYGNybURlbGl2ZXJ5RXZlbnRzLyR7aWR9YCwgYXBwSWQ6IGlucHV0LmFwcElkfX0gSU5UTyBoYXNDcm1FeGVjdXRpb25EZWxpdmVyeWApO1xuICAgIGF3YWl0IHF1ZXJ5KGFxbGBJTlNFUlQgJHt7X2Zyb206IGB1c2Vycy8ke3VzZXIuX2tleX1gLCBfa2V5OiBpZCxcbiAgICAgIF90bzogYGNybURlbGl2ZXJ5RXZlbnRzLyR7aWR9YCwgYXBwSWQ6IGlucHV0LmFwcElkfX0gSU5UTyBoYXNVc2VyQ3JtRGVsaXZlcnlgKTtcbiAgICBjb25zdCBjbG9zaW5nID0gYXdhaXQgYXV0aG9yaXplQ3JtRXhlY3V0aW9uR3JhbnQoY29udGV4dCwgaW5wdXQuYXBwSWQsICdjcm06ZGVsaXZlcnlfZXZlbnRzJywgbm93KCkpO1xuICAgIGlmKGNsb3NpbmcuaWQgIT09IGdyYW50LmlkIHx8IGNsb3NpbmcuaGFzaCAhPT0gZ3JhbnQuaGFzaCB8fCBjbG9zaW5nLmRhdGFiYXNlTmFtZSAhPT0gZ3JhbnQuZGF0YWJhc2VOYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NybV9kZWxpdmVyeV9yZWxheV9jaGFuZ2VkJyk7XG4gICAgfVxuICAgIGF3YWl0IHR4LmNvbW1pdCgpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBhd2FpdCB0eC5hYm9ydCgpO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59O1xuXG5cbi8qKiBOYXRpdmUgYWRtaW5pc3RyYXRvciBwcml2YWN5IGFjdGlvbjsgcGVyc2lzdGVudCBtaW5pbWl6ZWQgdG9tYnN0b25lIHByZXZlbnRzIHByaW9yIGFjY2VwdGVkLW1lc3NhZ2UgcmVpbmdlc3Rpb24uICovXG5leHBvcnQgY29uc3QgZXJhc2VDcm1EZWxpdmVyeUV2aWRlbmNlID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LCBhcHBJZDogc3RyaW5nLCB1c2VySWQ6IHN0cmluZywgbm93ID0gRGF0ZS5ub3dcbik6IFByb21pc2U8e2VyYXNlZDogbnVtYmVyfT4gPT4ge1xuICByZXF1aXJlQ3JtQWRtaW5pc3RyYXRvcihjb250ZXh0KTtcbiAgaWYoIWlkZW50aWZpZXIoYXBwSWQpIHx8IGFwcElkLmxlbmd0aCA+IDEwMCB8fCAhaWRlbnRpZmllcih1c2VySWQpIHx8IHVzZXJJZC5sZW5ndGggPiAxMDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWRfY3JtX2RlbGl2ZXJ5X2VyYXN1cmUnKTtcbiAgfVxuICBjb25zdCBzY29wZWQgPSBhd2FpdCBzY29wZUNvbnRleHRUb0FwcChjb250ZXh0LCBhcHBJZCk7XG4gIGNvbnN0IGRiID0gdXNlRGIoc2NvcGVkLmRhdGFiYXNlTmFtZSk7XG4gIGNvbnN0IGRvY3VtZW50cyA9IFsnY3JtRGVsaXZlcnlFdmVudHMnLCAnY3JtRGVsaXZlcnlQcml2YWN5J107XG4gIGNvbnN0IGVkZ2VzID0gWydoYXNDcm1FeGVjdXRpb25EZWxpdmVyeScsICdoYXNVc2VyQ3JtRGVsaXZlcnknXTtcbiAgZm9yKGNvbnN0IG5hbWUgb2YgWy4uLmRvY3VtZW50cywgLi4uZWRnZXNdKSB7XG4gICAgYXdhaXQgZW5zdXJlT3duZXJFeGVjdXRpb25Db2xsZWN0aW9uKGRiLCBuYW1lLCBlZGdlcy5pbmNsdWRlcyhuYW1lKSk7XG4gIH1cbiAgY29uc3QgdHggPSBhd2FpdCBkYi5iZWdpblRyYW5zYWN0aW9uKHtleGNsdXNpdmU6IFsuLi5kb2N1bWVudHMsIC4uLmVkZ2VzXX0sIHtsb2NrVGltZW91dDogMTB9KTtcbiAgY29uc3QgcXVlcnkgPSAodmFsdWU6IFJldHVyblR5cGU8dHlwZW9mIGFxbD4pID0+IHR4LnN0ZXAoKCkgPT4gZGIucXVlcnkodmFsdWUpKTtcbiAgdHJ5IHtcbiAgICBjb25zdCBpZHMgPSBhd2FpdCBxdWVyeShhcWxgRk9SIGVkZ2UgSU4gaGFzVXNlckNybURlbGl2ZXJ5XG4gICAgICBGSUxURVIgZWRnZS5hcHBJZCA9PSAke2FwcElkfSBBTkQgZWRnZS5fZnJvbSA9PSAke2B1c2Vycy8ke3VzZXJJZH1gfVxuICAgICAgUkVUVVJOIFBBUlNFX0lERU5USUZJRVIoZWRnZS5fdG8pLmtleWApLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLmFsbCgpKSBhcyBzdHJpbmdbXTtcbiAgICBjb25zdCBpZCA9IGNybURlbGl2ZXJ5UHJpdmFjeUtleShhcHBJZCwgdXNlcklkKTtcbiAgICBhd2FpdCBxdWVyeShhcWxgVVBTRVJUIHtfa2V5OiAke2lkfX0gSU5TRVJUICR7e19rZXk6IGlkLCBlcmFzZWRBdDogbm93KCl9fVxuICAgICAgVVBEQVRFIHtlcmFzZWRBdDogTUFYKFtPTEQuZXJhc2VkQXQsICR7bm93KCl9XSl9IElOIGNybURlbGl2ZXJ5UHJpdmFjeWApO1xuICAgIGF3YWl0IHF1ZXJ5KGFxbGBGT1IgZXZlbnQgSU4gY3JtRGVsaXZlcnlFdmVudHMgRklMVEVSIGV2ZW50Ll9rZXkgSU4gJHtpZHN9IFJFTU9WRSBldmVudCBJTiBjcm1EZWxpdmVyeUV2ZW50c2ApO1xuICAgIGF3YWl0IHF1ZXJ5KGFxbGBGT1IgZWRnZSBJTiBoYXNDcm1FeGVjdXRpb25EZWxpdmVyeSBGSUxURVIgZWRnZS5hcHBJZCA9PSAke2FwcElkfVxuICAgICAgQU5EIFBBUlNFX0lERU5USUZJRVIoZWRnZS5fdG8pLmtleSBJTiAke2lkc30gUkVNT1ZFIGVkZ2UgSU4gaGFzQ3JtRXhlY3V0aW9uRGVsaXZlcnlgKTtcbiAgICBhd2FpdCBxdWVyeShhcWxgRk9SIGVkZ2UgSU4gaGFzVXNlckNybURlbGl2ZXJ5IEZJTFRFUiBlZGdlLmFwcElkID09ICR7YXBwSWR9XG4gICAgICBBTkQgZWRnZS5fZnJvbSA9PSAke2B1c2Vycy8ke3VzZXJJZH1gfSBSRU1PVkUgZWRnZSBJTiBoYXNVc2VyQ3JtRGVsaXZlcnlgKTtcbiAgICBhd2FpdCB0eC5jb21taXQoKTtcbiAgICByZXR1cm4ge2VyYXNlZDogaWRzLmxlbmd0aH07XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBhd2FpdCB0eC5hYm9ydCgpO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59O1xuIl0sIm5hbWVzIjpbImFxbCIsImNyZWF0ZUhhc2giLCJzY29wZUNvbnRleHRUb0FwcCIsInVzZURiIiwicmVxdWlyZUNybUFkbWluaXN0cmF0b3IiLCJjcm1EZWxpdmVyeVByaXZhY3lLZXkiLCJhdXRob3JpemVDcm1FeGVjdXRpb25HcmFudCIsImVuc3VyZU93bmVyRXhlY3V0aW9uQ29sbGVjdGlvbiIsImRpZ2VzdCIsInZhbHVlIiwidXBkYXRlIiwiSlNPTiIsInN0cmluZ2lmeSIsImlkZW50aWZpZXIiLCJ0ZXN0IiwidmFsaWQiLCJpbnB1dCIsImF0IiwiQm9vbGVhbiIsImFwcElkIiwibGVuZ3RoIiwiZXZlbnRJZCIsInJlY2VpcHRJZCIsInByb3ZpZGVyTWVzc2FnZUlkIiwiaW5jbHVkZXMiLCJraW5kIiwiT2JqZWN0Iiwia2V5cyIsImV2ZXJ5Iiwia2V5IiwiYm91bmNlVHlwZSIsInVuZGVmaW5lZCIsIk51bWJlciIsImlzU2FmZUludGVnZXIiLCJvY2N1cnJlZEF0IiwiaW5nZXN0Q3JtRGVsaXZlcnlFdmVudCIsImNvbnRleHQiLCJub3ciLCJEYXRlIiwiRXJyb3IiLCJncmFudCIsImRiIiwiZGF0YWJhc2VOYW1lIiwiZG9jdW1lbnRzIiwiZWRnZXMiLCJuYW1lIiwidHgiLCJiZWdpblRyYW5zYWN0aW9uIiwiZXhjbHVzaXZlIiwibG9ja1RpbWVvdXQiLCJxdWVyeSIsInN0ZXAiLCJyZWNlaXB0IiwidGhlbiIsImN1cnNvciIsIm5leHQiLCJpZCIsInN0YXRlIiwicHJvdmlkZXJBY2NlcHRlZEF0IiwiY3JlYXRlZEF0IiwidXNlciIsInJlY2lwaWVudCIsImRlbGV0ZWQiLCJlbWFpbCIsIl9rZXkiLCJlcmFzZWQiLCJlcmFzZWRBdCIsImZpbmdlcnByaW50IiwicHJldmlvdXMiLCJjdXJyZW50IiwiaGFzaCIsInN1cHByZXNzZWQiLCJlbWFpbERlbGl2ZXJ5U3RhdHVzIiwiY29tbWl0IiwidGltZWxpbmUiLCJhbGwiLCJyZXN1bHQiLCJyZWNlaXZlZEF0IiwicmVsYXlHcmFudElkIiwic3RhdHVzIiwiX2Zyb20iLCJfdG8iLCJjbG9zaW5nIiwiZXJyb3IiLCJhYm9ydCIsImVyYXNlQ3JtRGVsaXZlcnlFdmlkZW5jZSIsInVzZXJJZCIsInNjb3BlZCIsImlkcyJdLCJtYXBwaW5ncyI6IkFBQUEsbUhBQW1ILEdBQ25ILFNBQVFBLEdBQUcsUUFBTyxXQUFXO0FBQzdCLFNBQVFDLFVBQVUsUUFBTyxjQUFjO0FBRXZDLFNBQVFDLGlCQUFpQixRQUFPLHlCQUF5QjtBQUN6RCxTQUFRQyxLQUFLLFFBQU8sNEJBQTRCO0FBQ2hELFNBQVFDLHVCQUF1QixRQUFPLFdBQVc7QUFDakQsU0FBUUMs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
@@ -0,0 +1,17 @@
1
+ import type { useDb } from '../utils/arangodbUtils.js';
2
+ import type { CrmExecutionReceipt } from './crmExecution.js';
3
+ export interface CrmDeliveryEvidence {
4
+ events: {
5
+ bounceType?: 'Permanent' | 'Transient' | 'Undetermined';
6
+ kind: 'delivered' | 'bounce' | 'complaint';
7
+ occurredAt: number;
8
+ }[];
9
+ generatedAt: number;
10
+ negativeHold: boolean | null;
11
+ schemaVersion: 'crm_delivery_evidence_v1';
12
+ status: 'available' | 'erased' | 'unavailable';
13
+ validUntil: number;
14
+ }
15
+ export declare const crmDeliveryPrivacyKey: (appId: string, userId: string) => string;
16
+ /** Internal projection composed only after the ordinary exact native receipt-read authority check. */
17
+ export declare const readCrmDeliveryEvidence: (db: ReturnType<typeof useDb>, receipt: CrmExecutionReceipt, now: number, expires: number) => Promise<CrmDeliveryEvidence>;
@@ -0,0 +1,58 @@
1
+ /* eslint-disable no-underscore-dangle -- Native minimized owner recipient and ledger metadata. */ import { aql } from 'arangojs';
2
+ import { createHash } from 'node:crypto';
3
+ export const crmDeliveryPrivacyKey = (appId, userId)=>createHash('sha256').update(JSON.stringify([
4
+ appId,
5
+ userId
6
+ ])).digest('hex');
7
+ /** Internal projection composed only after the ordinary exact native receipt-read authority check. */ export const readCrmDeliveryEvidence = async (db, receipt, now, expires)=>{
8
+ const base = {
9
+ events: [],
10
+ generatedAt: now,
11
+ negativeHold: null,
12
+ schemaVersion: 'crm_delivery_evidence_v1',
13
+ status: 'unavailable',
14
+ validUntil: Math.min(expires, now + 900000)
15
+ };
16
+ if (receipt.state !== 'applied' || !receipt.providerMessageId || !Number.isSafeInteger(receipt.providerAcceptedAt) || ![
17
+ 'crm_outreach',
18
+ 'scheduling_reminder'
19
+ ].includes(receipt.kind) || expires <= now || !await db.collection('crmDeliveryEvents').exists() || !await db.collection('crmDeliveryPrivacy').exists()) {
20
+ return base;
21
+ }
22
+ const row = await db.query(aql`LET user = DOCUMENT('users', ${receipt.recipient.id})
23
+ LET privacy = DOCUMENT('crmDeliveryPrivacy', ${crmDeliveryPrivacyKey(receipt.appId, receipt.recipient.id)})
24
+ LET events = (FOR event IN crmDeliveryEvents
25
+ FILTER event.appId == ${receipt.appId} AND event.receipt.receiptId == ${receipt.id}
26
+ SORT event.receipt.occurredAt ASC, event._key ASC LIMIT 33
27
+ RETURN KEEP(event.receipt, 'bounceType', 'kind', 'occurredAt'))
28
+ RETURN {events, privacy, user: user ? KEEP(user, '_key', 'deleted', 'email', 'emailDeliveryStatus') : null}`).then((cursor)=>cursor.next());
29
+ if (row?.privacy && receipt.providerAcceptedAt <= row.privacy.erasedAt) {
30
+ return {
31
+ ...base,
32
+ status: 'erased'
33
+ };
34
+ }
35
+ if (!row?.user || row.user.deleted === true || row.user._key !== receipt.recipient.id || row.user.email !== receipt.recipient.email || !Array.isArray(row.events) || row.events.length > 32 || row.events.some((event)=>![
36
+ 'delivered',
37
+ 'bounce',
38
+ 'complaint'
39
+ ].includes(event.kind) || !Number.isSafeInteger(event.occurredAt) || event.occurredAt > now + 60000 || event.occurredAt < receipt.providerAcceptedAt - 60000 || (event.kind === 'bounce' ? ![
40
+ 'Permanent',
41
+ 'Transient',
42
+ 'Undetermined'
43
+ ].includes(event.bounceType || '') : event.bounceType !== undefined))) {
44
+ return base;
45
+ }
46
+ return {
47
+ ...base,
48
+ events: row.events.map((event)=>({
49
+ bounceType: event.bounceType,
50
+ kind: event.kind,
51
+ occurredAt: event.occurredAt
52
+ })),
53
+ negativeHold: Boolean(row.user.emailDeliveryStatus && row.user.emailDeliveryStatus !== 'valid'),
54
+ status: 'available'
55
+ };
56
+ };
57
+
58
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvYWN0aW9ucy9jcm1EZWxpdmVyeUV2aWRlbmNlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIG5vLXVuZGVyc2NvcmUtZGFuZ2xlIC0tIE5hdGl2ZSBtaW5pbWl6ZWQgb3duZXIgcmVjaXBpZW50IGFuZCBsZWRnZXIgbWV0YWRhdGEuICovXG5pbXBvcnQge2FxbH0gZnJvbSAnYXJhbmdvanMnO1xuaW1wb3J0IHtjcmVhdGVIYXNofSBmcm9tICdub2RlOmNyeXB0byc7XG5cbmltcG9ydCB0eXBlIHt1c2VEYn0gZnJvbSAnLi4vdXRpbHMvYXJhbmdvZGJVdGlscy5qcyc7XG5pbXBvcnQgdHlwZSB7Q3JtRXhlY3V0aW9uUmVjZWlwdH0gZnJvbSAnLi9jcm1FeGVjdXRpb24uanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENybURlbGl2ZXJ5RXZpZGVuY2Uge1xuICBldmVudHM6IHtib3VuY2VUeXBlPzogJ1Blcm1hbmVudCcgfCAnVHJhbnNpZW50JyB8ICdVbmRldGVybWluZWQnOyBraW5kOiAnZGVsaXZlcmVkJyB8ICdib3VuY2UnIHwgJ2NvbXBsYWludCc7IG9jY3VycmVkQXQ6IG51bWJlcn1bXTtcbiAgZ2VuZXJhdGVkQXQ6IG51bWJlcjtcbiAgbmVnYXRpdmVIb2xkOiBib29sZWFuIHwgbnVsbDtcbiAgc2NoZW1hVmVyc2lvbjogJ2NybV9kZWxpdmVyeV9ldmlkZW5jZV92MSc7XG4gIHN0YXR1czogJ2F2YWlsYWJsZScgfCAnZXJhc2VkJyB8ICd1bmF2YWlsYWJsZSc7XG4gIHZhbGlkVW50aWw6IG51bWJlcjtcbn1cbmV4cG9ydCBjb25zdCBjcm1EZWxpdmVyeVByaXZhY3lLZXkgPSAoYXBwSWQ6IHN0cmluZywgdXNlcklkOiBzdHJpbmcpOiBzdHJpbmcgPT5cbiAgY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKEpTT04uc3RyaW5naWZ5KFthcHBJZCwgdXNlcklkXSkpLmRpZ2VzdCgnaGV4Jyk7XG5cbi8qKiBJbnRlcm5hbCBwcm9qZWN0aW9uIGNvbXBvc2VkIG9ubHkgYWZ0ZXIgdGhlIG9yZGluYXJ5IGV4YWN0IG5hdGl2ZSByZWNlaXB0LXJlYWQgYXV0aG9yaXR5IGNoZWNrLiAqL1xuZXhwb3J0IGNvbnN0IHJlYWRDcm1EZWxpdmVyeUV2aWRlbmNlID0gYXN5bmMgKFxuICBkYjogUmV0dXJuVHlwZTx0eXBlb2YgdXNlRGI+LCByZWNlaXB0OiBDcm1FeGVjdXRpb25SZWNlaXB0LCBub3c6IG51bWJlciwgZXhwaXJlczogbnVtYmVyXG4pOiBQcm9taXNlPENybURlbGl2ZXJ5RXZpZGVuY2U+ID0+IHtcbiAgY29uc3QgYmFzZTogQ3JtRGVsaXZlcnlFdmlkZW5jZSA9IHtldmVudHM6IFtdLCBnZW5lcmF0ZWRBdDogbm93LCBuZWdhdGl2ZUhvbGQ6IG51bGwsXG4gICAgc2NoZW1hVmVyc2lvbjogJ2NybV9kZWxpdmVyeV9ldmlkZW5jZV92MScsIHN0YXR1czogJ3VuYXZhaWxhYmxlJywgdmFsaWRVbnRpbDogTWF0aC5taW4oZXhwaXJlcywgbm93ICsgOTAwMDAwKX07XG4gIGlmKHJlY2VpcHQuc3RhdGUgIT09ICdhcHBsaWVkJyB8fCAhcmVjZWlwdC5wcm92aWRlck1lc3NhZ2VJZCB8fCAhTnVtYmVyLmlzU2FmZUludGVnZXIocmVjZWlwdC5wcm92aWRlckFjY2VwdGVkQXQpXG4gICAgfHwgIVsnY3JtX291dHJlYWNoJywgJ3NjaGVkdWxpbmdfcmVtaW5kZXInXS5pbmNsdWRlcyhyZWNlaXB0LmtpbmQpIHx8IGV4cGlyZXMgPD0gbm93XG4gICAgfHwgIWF3YWl0IGRiLmNvbGxlY3Rpb24oJ2NybURlbGl2ZXJ5RXZlbnRzJykuZXhpc3RzKCkgfHwgIWF3YWl0IGRiLmNvbGxlY3Rpb24oJ2NybURlbGl2ZXJ5UHJpdmFjeScpLmV4aXN0cygpKSB7XG4gICAgcmV0dXJuIGJhc2U7XG4gIH1cbiAgY29uc3Qgcm93ID0gYXdhaXQgZGIucXVlcnkoYXFsYExFVCB1c2VyID0gRE9DVU1FTlQoJ3VzZXJzJywgJHtyZWNlaXB0LnJlY2lwaWVudC5pZH0pXG4gICAgTEVUIHByaXZhY3kgPSBET0NVTUVOVCgnY3JtRGVsaXZlcnlQcml2YWN5JywgJHtjcm1EZWxpdmVyeVByaXZhY3lLZXkocmVjZWlwdC5hcHBJZCwgcmVjZWlwdC5yZWNpcGllbnQuaWQpfSlcbiAgICBMRVQgZXZlbnRzID0gKEZPUiBldmVudCBJTiBjcm1EZWxpdmVyeUV2ZW50c1xuICAgICAgRklMVEVSIGV2ZW50LmFwcElkID09ICR7cmVjZWlwdC5hcHBJZH0gQU5EIGV2ZW50LnJlY2VpcHQucmVjZWlwdElkID09ICR7cmVjZWlwdC5pZH1cbiAgICAgIFNPUlQgZXZlbnQucmVjZWlwdC5vY2N1cnJlZEF0IEFTQywgZXZlbnQuX2tleSBBU0MgTElNSVQgMzNcbiAgICAgIFJFVFVSTiBLRUVQKGV2ZW50LnJlY2VpcHQsICdib3VuY2VUeXBlJywgJ2tpbmQnLCAnb2NjdXJyZWRBdCcpKVxuICAgIFJFVFVSTiB7ZXZlbnRzLCBwcml2YWN5LCB1c2VyOiB1c2VyID8gS0VFUCh1c2VyLCAnX2tleScsICdkZWxldGVkJywgJ2VtYWlsJywgJ2VtYWlsRGVsaXZlcnlTdGF0dXMnKSA6IG51bGx9YClcbiAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSBhcyB7ZXZlbnRzOiBDcm1EZWxpdmVyeUV2aWRlbmNlWydldmVudHMnXTsgcHJpdmFjeT86IHtlcmFzZWRBdDogbnVtYmVyfTtcbiAgICAgIHVzZXI/OiB7X2tleTogc3RyaW5nOyBkZWxldGVkPzogYm9vbGVhbjsgZW1haWw6IHN0cmluZzsgZW1haWxEZWxpdmVyeVN0YXR1cz86IHN0cmluZ307fSB8IHVuZGVmaW5lZDtcbiAgaWYocm93Py5wcml2YWN5ICYmIHJlY2VpcHQucHJvdmlkZXJBY2NlcHRlZEF0ISA8PSByb3cucHJpdmFjeS5lcmFzZWRBdCkge1xuICAgIHJldHVybiB7Li4uYmFzZSwgc3RhdHVzOiAnZXJhc2VkJ307XG4gIH1cbiAgaWYoIXJvdz8udXNlciB8fCByb3cudXNlci5kZWxldGVkID09PSB0cnVlIHx8IHJvdy51c2VyLl9rZXkgIT09IHJlY2VpcHQucmVjaXBpZW50LmlkXG4gICAgfHwgcm93LnVzZXIuZW1haWwgIT09IHJlY2VpcHQucmVjaXBpZW50LmVtYWlsIHx8ICFBcnJheS5pc0FycmF5KHJvdy5ldmVudHMpIHx8IHJvdy5ldmVudHMubGVuZ3RoID4gMzJcbiAgICB8fCByb3cuZXZlbnRzLnNvbWUoKGV2ZW50KSA9PiAhWydkZWxpdmVyZWQnLCAnYm91bmNlJywgJ2NvbXBsYWludCddLmluY2x1ZGVzKGV2ZW50LmtpbmQpXG4gICAgICB8fCAhTnVtYmVyLmlzU2FmZUludGVnZXIoZXZlbnQub2NjdXJyZWRBdCkgfHwgZXZlbnQub2NjdXJyZWRBdCA+IG5vdyArIDYwMDAwXG4gICAgICB8fCBldmVudC5vY2N1cnJlZEF0IDwgcmVjZWlwdC5wcm92aWRlckFjY2VwdGVkQXQhIC0gNjAwMDBcbiAgICAgIHx8IChldmVudC5raW5kID09PSAnYm91bmNlJyA/ICFbJ1Blcm1hbmVudCcsICdUcmFuc2llbnQnLCAnVW5kZXRlcm1pbmVkJ10uaW5jbHVkZXMoZXZlbnQuYm91bmNlVHlwZSB8fCAnJylcbiAgICAgICAgOiBldmVudC5ib3VuY2VUeXBlICE9PSB1bmRlZmluZWQpKSkge1xuICAgIHJldHVybiBiYXNlO1xuICB9XG4gIHJldHVybiB7Li4uYmFzZSwgZXZlbnRzOiByb3cuZXZlbnRzLm1hcCgoZXZlbnQpID0+ICh7Ym91bmNlVHlwZTogZXZlbnQuYm91bmNlVHlwZSxcbiAgICBraW5kOiBldmVudC5raW5kLCBvY2N1cnJlZEF0OiBldmVudC5vY2N1cnJlZEF0fSkpLFxuICBuZWdhdGl2ZUhvbGQ6IEJvb2xlYW4ocm93LnVzZXIuZW1haWxEZWxpdmVyeVN0YXR1cyAmJiByb3cudXNlci5lbWFpbERlbGl2ZXJ5U3RhdHVzICE9PSAndmFsaWQnKSwgc3RhdHVzOiAnYXZhaWxhYmxlJ307XG59O1xuIl0sIm5hbWVzIjpbImFxbCIsImNyZWF0ZUhhc2giLCJjcm1EZWxpdmVyeVByaXZhY3lLZXkiLCJhcHBJZCIsInVzZXJJZCIsInVwZGF0ZSIsIkpTT04iLCJzdHJpbmdpZnkiLCJkaWdlc3QiLCJyZWFkQ3JtRGVsaXZlcnlFdmlkZW5jZSIsImRiIiwicmVjZWlwdCIsIm5vdyIsImV4cGlyZXMiLCJiYXNlIiwiZXZlbnRzIiwiZ2VuZXJhdGVkQXQiLCJuZWdhdGl2ZUhvbGQiLCJzY2hlbWFWZXJzaW9uIiwic3RhdHVzIiwidmFsaWRVbnRpbCIsIk1hdGgiLCJtaW4iLCJzdGF0ZSIsInByb3ZpZGVyTWVzc2FnZUlkIiwiTnVtYmVyIiwiaXNTYWZlSW50ZWdlciIsInByb3ZpZGVyQWNjZXB0ZWRBdCIsImluY2x1ZGVzIiwia2luZCIsImNvbGxlY3Rpb24iLCJleGlzdHMiLCJyb3ciLCJxdWVyeSIsInJlY2lwaWVudCIsImlkIiwidGhlbiIsImN1cnNvciIsIm5leHQiLCJwcml2YWN5IiwiZXJhc2VkQXQiLCJ1c2VyIiwiZGVsZXRlZCIsIl9rZXkiLCJlbWFpbCIsIkFycmF5IiwiaXNBcnJheSIsImxlbmd0aCIsInNvbWUiLCJldmVudCIsIm9jY3VycmVkQXQiLCJib3VuY2VUeXBlIiwidW5kZWZpbmVkIiwibWFwIiwiQm9vbGVhbiIsImVtYWlsRGVsaXZlcnlTdGF0dXMiXSwibWFwcGluZ3MiOiJBQUFBLGdHQUFnRyxHQUNoRyxTQUFRQSxHQUFHLFFBQU8sV0FBVztBQUM3QixTQUFRQyxVQUFVLFFBQU8sY0FBYztBQWF2QyxPQUFPLE1BQU1DLHdCQUF3QixDQUFDQyxPQUFlQyxTQUNuREgsV0FBVyxVQUFVSSxNQUFNLENBQUNDLEtBQUtDLFNBQVMsQ0FBQztRQUFDSjtRQUFPQztLQUFPLEdBQUdJLE1BQU0sQ0FBQyxPQUFPO0FBRTdFLG9HQUFvRyxHQUNwRyxPQUFPLE1BQU1DLDBCQUEwQixPQUNyQ0MsSUFBOEJDLFNBQThCQyxLQUFhQztJQUV6RSxNQUFNQyxPQUE0QjtRQUFDQyxRQUFRLEVBQUU7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
@@ -0,0 +1,125 @@
1
+ import { useDb } from '../utils/arangodbUtils.js';
2
+ import { sesSend } from './email.js';
3
+ import type { ApiContext } from '../types/auth.types.js';
4
+ import type { CrmCheckoutRecoveryBinding, CrmCheckoutRecoveryBounds } from './crmCheckoutRecoveryPolicy.js';
5
+ import type { CrmDeliveryEvidence } from './crmDeliveryEvidence.js';
6
+ import type { CrmLeadFollowupBinding, CrmLeadFollowupBounds } from './crmLeadFollowups.js';
7
+ import type { CrmWinbackBinding } from './crmWinback.js';
8
+ import type { CrmWinbackBounds } from './crmWinbackPolicy.js';
9
+ import type { SupportManagement } from './supportPolicy.js';
10
+ export type CrmExecutionScope = 'crm:checkout_recovery' | 'crm:delivery_events' | 'crm:lead_followup' | 'crm:outreach' | 'crm:winback' | 'support:commerce' | 'support:conversation' | 'support:summary' | 'support:email_reply' | 'support:delivery_events' | 'support:channel_ingest' | 'support:context' | 'support:read' | 'support:triage' | 'support:reply' | 'support:manage' | 'scheduling:calendar_sync' | 'scheduling:calendar_invite' | 'scheduling:read' | 'scheduling:book' | 'scheduling:remind' | 'finance:provider_read' | 'finance:read' | 'finance:invoice';
11
+ export interface CrmExecutionGrantInput {
12
+ appId: string;
13
+ expiresAt: string;
14
+ followupBounds?: CrmLeadFollowupBounds;
15
+ recoveryBounds?: CrmCheckoutRecoveryBounds;
16
+ winbackBounds?: CrmWinbackBounds;
17
+ scopes: CrmExecutionScope[];
18
+ senderEmail: string;
19
+ unsubscribeUrl?: string;
20
+ }
21
+ export interface CrmExecutionInput {
22
+ appId: string;
23
+ body: string;
24
+ expectedSourceRevision: string;
25
+ includeVerifiedRecoveryLink?: boolean;
26
+ followup?: CrmLeadFollowupBinding;
27
+ recovery?: CrmCheckoutRecoveryBinding;
28
+ winback?: CrmWinbackBinding;
29
+ idempotencyKey: string;
30
+ kind: 'crm_outreach' | 'support_triage' | 'support_reply' | 'support_manage' | 'scheduling_reminder';
31
+ management?: SupportManagement;
32
+ status?: 'in_progress' | 'waiting_on_customer' | 'waiting_on_internal';
33
+ subject: string;
34
+ targetId: string;
35
+ }
36
+ export interface CrmExecutionReceipt {
37
+ appId: string;
38
+ body?: string;
39
+ contentStatus?: 'current' | 'redacted';
40
+ createdAt: number;
41
+ deliveryEvidence?: CrmDeliveryEvidence;
42
+ expiresAt: number;
43
+ followup?: CrmLeadFollowupBinding;
44
+ recovery?: CrmCheckoutRecoveryBinding;
45
+ winback?: CrmWinbackBinding;
46
+ grantId: string;
47
+ id: string;
48
+ kind: CrmExecutionInput['kind'];
49
+ management?: SupportManagement;
50
+ maxActions: 1;
51
+ notBefore?: number;
52
+ providerAcceptedAt?: number;
53
+ providerMessageId?: string;
54
+ recipient: {
55
+ email?: string;
56
+ id: string;
57
+ };
58
+ recoveryLinkHash?: string;
59
+ revision: string;
60
+ sourceRevision: string;
61
+ scheduled?: boolean;
62
+ scheduleStatus?: 'scheduled' | 'cancelled' | 'held' | 'completed';
63
+ scheduleUpdatedAt?: number;
64
+ state: 'pending' | 'approved' | 'rejected' | 'submitting' | 'unknown' | 'applied';
65
+ status?: CrmExecutionInput['status'];
66
+ subject?: string;
67
+ targetId: string;
68
+ }
69
+ interface Grant extends CrmExecutionGrantInput {
70
+ databaseName: string;
71
+ expires: number;
72
+ hash: string;
73
+ id: string;
74
+ ownerId: string;
75
+ revokedAt?: number;
76
+ }
77
+ interface Source {
78
+ email: string;
79
+ id: string;
80
+ revision: string;
81
+ notBefore?: number;
82
+ reminderConsentId?: string;
83
+ subject?: string;
84
+ ticketStatus?: string;
85
+ }
86
+ /** Native hosts can expire capability content in bounded maintenance without altering send claims. */
87
+ export declare const retainCrmCheckoutRecoveryContent: (context: ApiContext, appId: string, now?: () => number) => Promise<number>;
88
+ export declare const ensureOwnerExecutionCollection: (db: ReturnType<typeof useDb>, name: string, edge?: boolean) => Promise<void>;
89
+ /** Grant writes are administrator-only; the secret is returned once and never stored. */
90
+ export declare const createCrmExecutionKey: (context: ApiContext, input: CrmExecutionGrantInput, now?: () => number) => Promise<{
91
+ appId: string;
92
+ executionKey: string;
93
+ expiresAt: string;
94
+ followupBounds: CrmLeadFollowupBounds;
95
+ id: `${string}-${string}-${string}-${string}-${string}`;
96
+ recoveryBounds: CrmCheckoutRecoveryBounds;
97
+ scopes: CrmExecutionScope[];
98
+ winbackBounds: CrmWinbackBounds;
99
+ }>;
100
+ export declare const revokeCrmExecutionKey: (context: ApiContext, appId: string, id: string, now?: () => number) => Promise<boolean>;
101
+ export declare const authorizeCrmExecutionGrant: (context: ApiContext, appId: string, scope: CrmExecutionScope, now: number) => Promise<Grant>;
102
+ declare const source: (db: ReturnType<typeof useDb>, kind: CrmExecutionInput['kind'], targetId: string, query?: any, at?: number, appId?: string) => Promise<Source>;
103
+ /** Source policy shared by already-authorized owner workflows; never exposed as an HTTP resolver. */
104
+ export declare const readCrmExecutionTargetSource: typeof source;
105
+ /** Minimized authorized target read; no ticket descriptions, history or unrelated customer fields. */
106
+ export declare const readCrmExecutionTarget: (context: ApiContext, appId: string, kind: CrmExecutionInput['kind'], targetId: string, now?: () => number) => Promise<{
107
+ email: string;
108
+ id: string;
109
+ revision: string;
110
+ notBefore?: number;
111
+ reminderConsentId?: string;
112
+ subject?: string;
113
+ ticketStatus?: string;
114
+ appId: string;
115
+ targetId: string;
116
+ }>;
117
+ export declare const getCrmExecutionReceipt: (context: ApiContext, appId: string, id: string, now?: () => number) => Promise<CrmExecutionReceipt>;
118
+ /** Freeze exactly one recipient and plain-text action. Approval is a separate revision-bound operation. */
119
+ export declare const previewCrmExecution: (context: ApiContext, input: CrmExecutionInput, now?: () => number) => Promise<CrmExecutionReceipt>;
120
+ export declare const decideCrmExecution: (context: ApiContext, appId: string, id: string, revision: string, decision: 'approved' | 'rejected', now?: () => number) => Promise<CrmExecutionReceipt>;
121
+ /** Customer-facing Support reply/triage commits atomically. SES submissions are never blindly retried. */
122
+ export declare const executeCrmExecution: (context: ApiContext, appId: string, id: string, revision: string, now?: () => number, send?: (params: Parameters<typeof sesSend>[0]) => Promise<import("@aws-sdk/client-ses").SendEmailCommandOutput>, scheduled?: boolean) => Promise<CrmExecutionReceipt>;
123
+ /** Public opaque unsubscribe capability; only updates the exact consent-bound recipient. */
124
+ export declare const unsubscribeCrmExecution: (context: ApiContext, appId: string, token: string, now?: () => number) => Promise<boolean>;
125
+ export {};