@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,100 @@
1
+ /* eslint-disable no-underscore-dangle -- Native document identity and revision checks. */ import { aql } from 'arangojs';
2
+ import { scopeContextToApp } from '../utils/appContext.js';
3
+ import { useDb } from '../utils/arangodbUtils.js';
4
+ import { crmCanonicalDigest } from './crmCanonicalPolicy.js';
5
+ import { authorizeCrmExecutionGrant } from './crmExecution.js';
6
+ export const supportConversationKey = (value)=>typeof value === 'string' && /^[\w.-]{1,160}$/.test(value);
7
+ export const validSupportConversationInput = (input)=>Boolean(input && Object.keys(input).sort().join(',') === 'appId,ticketId,ticketRevision' && [
8
+ input.appId,
9
+ input.ticketId,
10
+ input.ticketRevision
11
+ ].every(supportConversationKey));
12
+ export const readSupportConversationRow = async (db, input, purpose, query = db.query.bind(db))=>query(aql`
13
+ LET ticket = DOCUMENT('supportTickets', ${input.ticketId})
14
+ LET user = ticket ? DOCUMENT('users', ticket.userId) : null
15
+ LET matchingConsent = (FOR candidate IN supportPurposeConsents FILTER candidate.appId == ${input.appId}
16
+ AND candidate.userId == user._key AND candidate.purpose == ${purpose} LIMIT 2 RETURN candidate)
17
+ LET messages = (FOR message IN supportTicketMessages FILTER message.supportTicketId == ${input.ticketId}
18
+ AND (message.appId == null OR message.appId == ${input.appId}) AND message.visibility == 'customer' AND
19
+ message.deleted != true
20
+ SORT message.modified ASC, message._key ASC LIMIT 51
21
+ LET bindings = (FOR edge IN hasSupportTicketMessage FILTER edge._from == ${`supportTickets/${input.ticketId}`}
22
+ AND edge._to == CONCAT('supportTicketMessages/', message._key) AND edge.visibility == 'customer' LIMIT 2
23
+ RETURN edge._key)
24
+ RETURN {bindingCount: LENGTH(bindings), _key: message._key, _rev: message._rev, appId: message.appId, body:
25
+ message.body,
26
+ deleted: message.deleted, modified: message.modified, supportTicketId: message.supportTicketId,
27
+ visibility: message.visibility})
28
+ RETURN {consent: LENGTH(matchingConsent) == 1 ? matchingConsent[0] : null, messages,
29
+ ticket: ticket ? {_key: ticket._key, _rev: ticket._rev, appId: ticket.appId, deleted: ticket.deleted,
30
+ status: ticket.status, userId: ticket.userId} : null,
31
+ user: user ? {_key: user._key, _rev: user._rev, active: user.active, appId: user.appId, deleted: user.deleted,
32
+ email: user.email, verifiedEmail: user.verifiedEmail, emailDeliveryStatus: user.emailDeliveryStatus, supportEmailSuppressed:
33
+ user.supportEmailSuppressed} : null}`).then((cursor)=>cursor.next());
34
+ export const validateSupportConversationRow = (row, input, purpose, at)=>{
35
+ const { consent, ticket, user } = row || {};
36
+ if (!ticket || ticket._key !== input.ticketId || ticket._rev !== input.ticketRevision || ticket.appId !== input.appId || ticket.deleted === true || ![
37
+ 'submitted',
38
+ 'open',
39
+ 'in_progress',
40
+ 'waiting_on_customer',
41
+ 'waiting_on_internal'
42
+ ].includes(ticket.status) || !user || user._key !== ticket.userId || !supportConversationKey(user._rev) || user.appId !== input.appId || user.deleted === true || user.active === false || !consent || consent.appId !== input.appId || consent.userId !== user._key || !supportConversationKey(consent._rev) || consent.purpose !== purpose || consent.source !== 'authenticated_customer' || consent.status !== 'active' || !Number.isSafeInteger(consent.expiresAt) || consent.expiresAt <= at || !Array.isArray(row.messages) || row.messages.some((message)=>!supportConversationKey(message._key) || !supportConversationKey(message._rev) || message.appId !== undefined && message.appId !== input.appId || message.bindingCount !== 1 || message.deleted === true || message.supportTicketId !== input.ticketId || message.visibility !== 'customer' || typeof message.body !== 'string' || message.body.length > 4000 || !Number.isSafeInteger(message.modified) || message.modified < 0 || message.modified > at)) {
43
+ throw new Error('support_conversation_source_hold');
44
+ }
45
+ return crmCanonicalDigest([
46
+ ticket._rev,
47
+ user._rev,
48
+ consent._rev,
49
+ row.messages.map((message)=>[
50
+ message._key,
51
+ message._rev
52
+ ])
53
+ ]);
54
+ };
55
+ export const supportPurposeScope = {
56
+ support_conversation: 'support:conversation',
57
+ support_email_reply: 'support:email_reply',
58
+ support_semantic_summary: 'support:summary'
59
+ };
60
+ /** The bounded native customer-visible feed is an owned source, never an external transcript assertion. */ export const readSupportPurposeConversation = async (context, input, purpose, now = Date.now)=>{
61
+ if (!validSupportConversationInput(input)) {
62
+ throw new Error('invalid_support_conversation');
63
+ }
64
+ // These new scopes are added to the public grant contract during coordinated package wiring.
65
+ const grant = await authorizeCrmExecutionGrant(context, input.appId, supportPurposeScope[purpose], now());
66
+ await authorizeCrmExecutionGrant(context, input.appId, 'support:read', now());
67
+ const scoped = await scopeContextToApp(context, input.appId);
68
+ if (scoped.databaseName !== grant.databaseName) {
69
+ throw new Error('support_conversation_app_changed');
70
+ }
71
+ const db = useDb(grant.databaseName);
72
+ const row = await readSupportConversationRow(db, input, purpose);
73
+ const revision = validateSupportConversationRow(row, input, purpose, now());
74
+ const latest = await authorizeCrmExecutionGrant(context, input.appId, supportPurposeScope[purpose], now());
75
+ const scope = await scopeContextToApp(context, input.appId);
76
+ if (grant.id !== latest.id || grant.hash !== latest.hash || grant.databaseName !== latest.databaseName || scope.databaseName !== grant.databaseName || validateSupportConversationRow(await readSupportConversationRow(db, input, purpose), input, purpose, now()) !== revision) {
77
+ throw new Error('support_conversation_changed');
78
+ }
79
+ return {
80
+ appId: input.appId,
81
+ generatedAt: now(),
82
+ messages: row.messages.slice(0, 50).map((message)=>({
83
+ body: message.body,
84
+ id: message._key,
85
+ modifiedAt: message.modified,
86
+ revision: message._rev
87
+ })),
88
+ provenance: 'native_customer_visible_feed',
89
+ revision,
90
+ schemaVersion: 'support_conversation_v1',
91
+ ticketId: input.ticketId,
92
+ ticketRevision: input.ticketRevision,
93
+ truncated: row.messages.length > 50,
94
+ validUntil: Math.min(now() + 300000, latest.expires, row.consent.expiresAt)
95
+ };
96
+ };
97
+ export const readSupportConversation = async (context, input, now = Date.now)=>readSupportPurposeConversation(context, input, 'support_conversation', now);
98
+ export const readSupportSemanticSource = async (context, input, now = Date.now)=>readSupportPurposeConversation(context, input, 'support_semantic_summary', now);
99
+
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi90bXAvcmVhY3RvcmJveC1zdXBwb3J0LW93bmVyLWZyZWV6ZS0yMDI2MTAwOC9pbnB1dC9hY3Rpb25zL3N1cHBvcnRDb252ZXJzYXRpb24udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZXJzY29yZS1kYW5nbGUgLS0gTmF0aXZlIGRvY3VtZW50IGlkZW50aXR5IGFuZCByZXZpc2lvbiBjaGVja3MuICovXG5pbXBvcnQge2FxbH0gZnJvbSAnYXJhbmdvanMnO1xuXG5pbXBvcnQge3Njb3BlQ29udGV4dFRvQXBwfSBmcm9tICcuLi91dGlscy9hcHBDb250ZXh0LmpzJztcbmltcG9ydCB7dXNlRGJ9IGZyb20gJy4uL3V0aWxzL2FyYW5nb2RiVXRpbHMuanMnO1xuaW1wb3J0IHtjcm1DYW5vbmljYWxEaWdlc3R9IGZyb20gJy4vY3JtQ2Fub25pY2FsUG9saWN5LmpzJztcbmltcG9ydCB7YXV0aG9yaXplQ3JtRXhlY3V0aW9uR3JhbnR9IGZyb20gJy4vY3JtRXhlY3V0aW9uLmpzJztcblxuaW1wb3J0IHR5cGUge0FwaUNvbnRleHR9IGZyb20gJy4uL3R5cGVzL2F1dGgudHlwZXMuanMnO1xuaW1wb3J0IHR5cGUge1N1cHBvcnRDb25zZW50UHVycG9zZSwgU3VwcG9ydFB1cnBvc2VDb25zZW50UmVjb3JkfSBmcm9tICcuL3N1cHBvcnRQdXJwb3NlQ29uc2VudC5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3VwcG9ydENvbnZlcnNhdGlvbklucHV0IHtcbiAgYXBwSWQ6IHN0cmluZztcbiAgdGlja2V0SWQ6IHN0cmluZztcbiAgdGlja2V0UmV2aXNpb246IHN0cmluZztcbn1cbmV4cG9ydCBjb25zdCBzdXBwb3J0Q29udmVyc2F0aW9uS2V5ID0gKHZhbHVlOiB1bmtub3duKTogdmFsdWUgaXMgc3RyaW5nID0+IHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcbiAgL15bXFx3Li1dezEsMTYwfSQvLnRlc3QodmFsdWUpO1xuZXhwb3J0IGNvbnN0IHZhbGlkU3VwcG9ydENvbnZlcnNhdGlvbklucHV0ID0gKGlucHV0OiBTdXBwb3J0Q29udmVyc2F0aW9uSW5wdXQpID0+IEJvb2xlYW4oaW5wdXRcbiAgICAmJiBPYmplY3Qua2V5cyhpbnB1dCkuc29ydCgpLmpvaW4oJywnKSA9PT0gJ2FwcElkLHRpY2tldElkLHRpY2tldFJldmlzaW9uJ1xuICAgICYmIFtpbnB1dC5hcHBJZCwgaW5wdXQudGlja2V0SWQsIGlucHV0LnRpY2tldFJldmlzaW9uXS5ldmVyeShzdXBwb3J0Q29udmVyc2F0aW9uS2V5KSk7XG5leHBvcnQgaW50ZXJmYWNlIFN1cHBvcnRDb252ZXJzYXRpb25Sb3cge1xuICBjb25zZW50PzogU3VwcG9ydFB1cnBvc2VDb25zZW50UmVjb3JkO1xuICBtZXNzYWdlczoge1xuICAgIF9rZXk6IHN0cmluZztcbiAgICBfcmV2OiBzdHJpbmc7XG4gICAgYXBwSWQ/OiBzdHJpbmc7XG4gICAgYmluZGluZ0NvdW50OiBudW1iZXI7XG4gICAgYm9keTogc3RyaW5nO1xuICAgIGRlbGV0ZWQ/OiBib29sZWFuO1xuICAgIG1vZGlmaWVkOiBudW1iZXI7XG4gICAgc3VwcG9ydFRpY2tldElkOiBzdHJpbmc7XG4gICAgdmlzaWJpbGl0eTogc3RyaW5nO1xuICB9W107XG4gIHRpY2tldD86IHtcbiAgICBfa2V5OiBzdHJpbmc7XG4gICAgX3Jldjogc3RyaW5nO1xuICAgIGFwcElkOiBzdHJpbmc7XG4gICAgZGVsZXRlZD86IGJvb2xlYW47XG4gICAgc3RhdHVzOiBzdHJpbmc7XG4gICAgdXNlcklkOiBzdHJpbmc7XG4gIH07XG4gIHVzZXI/OiB7XG4gICAgX2tleTogc3RyaW5nO1xuICAgIF9yZXY6IHN0cmluZztcbiAgICBhY3RpdmU/OiBib29sZWFuO1xuICAgIGFwcElkOiBzdHJpbmc7XG4gICAgZGVsZXRlZD86IGJvb2xlYW47XG4gICAgZW1haWw/OiBzdHJpbmc7XG4gICAgdmVyaWZpZWRFbWFpbD86IGJvb2xlYW47IGVtYWlsRGVsaXZlcnlTdGF0dXM/OiBzdHJpbmc7XG4gICAgc3VwcG9ydEVtYWlsU3VwcHJlc3NlZD86IGJvb2xlYW47XG4gIH07XG59XG5leHBvcnQgY29uc3QgcmVhZFN1cHBvcnRDb252ZXJzYXRpb25Sb3cgPSBhc3luYyAoZGI6IFJldHVyblR5cGU8dHlwZW9mIHVzZURiPiwgaW5wdXQ6IFN1cHBvcnRDb252ZXJzYXRpb25JbnB1dCxcbiAgcHVycG9zZTogU3VwcG9ydENvbnNlbnRQdXJwb3NlLCBxdWVyeSA9IGRiLnF1ZXJ5LmJpbmQoZGIpKTogUHJvbWlzZTxTdXBwb3J0Q29udmVyc2F0aW9uUm93PiA9PiBxdWVyeShhcWwgYFxuICBMRVQgdGlja2V0ID0gRE9DVU1FTlQoJ3N1cHBvcnRUaWNrZXRzJywgJHtpbnB1dC50aWNrZXRJZH0pXG4gIExFVCB1c2VyID0gdGlja2V0ID8gRE9DVU1FTlQoJ3VzZXJzJywgdGlja2V0LnVzZXJJZCkgOiBudWxsXG4gIExFVCBtYXRjaGluZ0NvbnNlbnQgPSAoRk9SIGNhbmRpZGF0ZSBJTiBzdXBwb3J0UHVycG9zZUNvbnNlbnRzIEZJTFRFUiBjYW5kaWRhdGUuYXBwSWQgPT0gJHtpbnB1dC5hcHBJZH1cbiAgICBBTkQgY2FuZGlkYXRlLnVzZXJJZCA9PSB1c2VyLl9rZXkgQU5EIGNhbmRpZGF0ZS5wdXJwb3NlID09ICR7cHVycG9zZX0gTElNSVQgMiBSRVRVUk4gY2FuZGlkYXRlKVxuICBMRVQgbWVzc2FnZXMgPSAoRk9SIG1lc3NhZ2UgSU4gc3VwcG9ydFRpY2tldE1lc3NhZ2VzIEZJTFRFUiBtZXNzYWdlLnN1cHBvcnRUaWNrZXRJZCA9PSAke2lucHV0LnRpY2tldElkfVxuICAgIEFORCAobWVzc2FnZS5hcHBJZCA9PSBudWxsIE9SIG1lc3NhZ2UuYXBwSWQgPT0gJHtpbnB1dC5hcHBJZH0pIEFORCBtZXNzYWdlLnZpc2liaWxpdHkgPT0gJ2N1c3RvbWVyJyBBTkRcbiAgICAgIG1lc3NhZ2UuZGVsZXRlZCAhPSB0cnVlXG4gICAgU09SVCBtZXNzYWdlLm1vZGlmaWVkIEFTQywgbWVzc2FnZS5fa2V5IEFTQyBMSU1JVCA1MVxuICAgIExFVCBiaW5kaW5ncyA9IChGT1IgZWRnZSBJTiBoYXNTdXBwb3J0VGlja2V0TWVzc2FnZSBGSUxURVIgZWRnZS5fZnJvbSA9PSAke2BzdXBwb3J0VGlja2V0cy8ke2lucHV0LnRpY2tldElkfWB9XG4gICAgICBBTkQgZWRnZS5fdG8gPT0gQ09OQ0FUKCdzdXBwb3J0VGlja2V0TWVzc2FnZXMvJywgbWVzc2FnZS5fa2V5KSBBTkQgZWRnZS52aXNpYmlsaXR5ID09ICdjdXN0b21lcicgTElNSVQgMlxuICAgICAgICBSRVRVUk4gZWRnZS5fa2V5KVxuICAgIFJFVFVSTiB7YmluZGluZ0NvdW50OiBMRU5HVEgoYmluZGluZ3MpLCBfa2V5OiBtZXNzYWdlLl9rZXksIF9yZXY6IG1lc3NhZ2UuX3JldiwgYXBwSWQ6IG1lc3NhZ2UuYXBwSWQsIGJvZHk6XG4gICAgICBtZXNzYWdlLmJvZHksXG4gICAgICBkZWxldGVkOiBtZXNzYWdlLmRlbGV0ZWQsIG1vZGlmaWVkOiBtZXNzYWdlLm1vZGlmaWVkLCBzdXBwb3J0VGlja2V0SWQ6IG1lc3NhZ2Uuc3VwcG9ydFRpY2tldElkLFxuICAgICAgdmlzaWJpbGl0eTogbWVzc2FnZS52aXNpYmlsaXR5fSlcbiAgUkVUVVJOIHtjb25zZW50OiBMRU5HVEgobWF0Y2hpbmdDb25zZW50KSA9PSAxID8gbWF0Y2hpbmdDb25zZW50WzBdIDogbnVsbCwgbWVzc2FnZXMsXG4gICAgdGlja2V0OiB0aWNrZXQgPyB7X2tleTogdGlja2V0Ll9rZXksIF9yZXY6IHRpY2tldC5fcmV2LCBhcHBJZDogdGlja2V0LmFwcElkLCBkZWxldGVkOiB0aWNrZXQuZGVsZXRlZCxcbiAgICAgIHN0YXR1czogdGlja2V0LnN0YXR1cywgdXNlcklkOiB0aWNrZXQudXNlcklkfSA6IG51bGwsXG4gICAgdXNlcjogdXNlciA/IHtfa2V5OiB1c2VyLl9rZXksIF9yZXY6IHVzZXIuX3JldiwgYWN0aXZlOiB1c2VyLmFjdGl2ZSwgYXBwSWQ6IHVzZXIuYXBwSWQsIGRlbGV0ZWQ6IHVzZXIuZGVsZXRlZCxcbiAgICAgIGVtYWlsOiB1c2VyLmVtYWlsLCB2ZXJpZmllZEVtYWlsOiB1c2VyLnZlcmlmaWVkRW1haWwsIGVtYWlsRGVsaXZlcnlTdGF0dXM6IHVzZXIuZW1haWxEZWxpdmVyeVN0YXR1cywgc3VwcG9ydEVtYWlsU3VwcHJlc3NlZDpcbiAgICAgICAgdXNlci5zdXBwb3J0RW1haWxTdXBwcmVzc2VkfSA6IG51bGx9YClcbiAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLm5leHQoKSk7XG5leHBvcnQgY29uc3QgdmFsaWRhdGVTdXBwb3J0Q29udmVyc2F0aW9uUm93ID0gKHJvdzogU3VwcG9ydENvbnZlcnNhdGlvblJvdyB8IHVuZGVmaW5lZCwgaW5wdXQ6XG5TdXBwb3J0Q29udmVyc2F0aW9uSW5wdXQsIHB1cnBvc2U6IFN1cHBvcnRDb25zZW50UHVycG9zZSwgYXQ6IG51bWJlcikgPT4ge1xuICBjb25zdCB7Y29uc2VudCwgdGlja2V0LCB1c2VyfSA9IHJvdyB8fCB7fTtcbiAgaWYoIXRpY2tldCB8fCB0aWNrZXQuX2tleSAhPT0gaW5wdXQudGlja2V0SWQgfHwgdGlja2V0Ll9yZXYgIT09IGlucHV0LnRpY2tldFJldmlzaW9uXG4gICAgICAgIHx8IHRpY2tldC5hcHBJZCAhPT0gaW5wdXQuYXBwSWQgfHwgdGlja2V0LmRlbGV0ZWQgPT09IHRydWVcbiAgICAgICAgfHwgIVsnc3VibWl0dGVkJywgJ29wZW4nLCAnaW5fcHJvZ3Jlc3MnLCAnd2FpdGluZ19vbl9jdXN0b21lcicsICd3YWl0aW5nX29uX2ludGVybmFsJ10uaW5jbHVkZXModGlja2V0LnN0YXR1cylcbiAgICAgICAgfHwgIXVzZXIgfHwgdXNlci5fa2V5ICE9PSB0aWNrZXQudXNlcklkIHx8ICFzdXBwb3J0Q29udmVyc2F0aW9uS2V5KHVzZXIuX3JldilcbiAgICAgICAgfHwgdXNlci5hcHBJZCAhPT0gaW5wdXQuYXBwSWQgfHwgdXNlci5kZWxldGVkID09PSB0cnVlIHx8IHVzZXIuYWN0aXZlID09PSBmYWxzZVxuICAgICAgICB8fCAhY29uc2VudCB8fCBjb25zZW50LmFwcElkICE9PSBpbnB1dC5hcHBJZCB8fCBjb25zZW50LnVzZXJJZCAhPT0gdXNlci5fa2V5XG4gICAgICAgIHx8ICFzdXBwb3J0Q29udmVyc2F0aW9uS2V5KGNvbnNlbnQuX3JldikgfHwgY29uc2VudC5wdXJwb3NlICE9PSBwdXJwb3NlXG4gICAgICAgIHx8IGNvbnNlbnQuc291cmNlICE9PSAnYXV0aGVudGljYXRlZF9jdXN0b21lcicgfHwgY29uc2VudC5zdGF0dXMgIT09ICdhY3RpdmUnXG4gICAgICAgIHx8ICFOdW1iZXIuaXNTYWZlSW50ZWdlcihjb25zZW50LmV4cGlyZXNBdCkgfHwgY29uc2VudC5leHBpcmVzQXQgPD0gYXRcbiAgICAgICAgfHwgIUFycmF5LmlzQXJyYXkocm93Lm1lc3NhZ2VzKSB8fCByb3cubWVzc2FnZXMuc29tZSgobWVzc2FnZSkgPT4gIXN1cHBvcnRDb252ZXJzYXRpb25LZXkobWVzc2FnZS5fa2V5KVxuICAgICAgICB8fCAhc3VwcG9ydENvbnZlcnNhdGlvbktleShtZXNzYWdlLl9yZXYpIHx8IChtZXNzYWdlLmFwcElkICE9PSB1bmRlZmluZWQgJiYgbWVzc2FnZS5hcHBJZCAhPT0gaW5wdXQuYXBwSWQpXG4gICAgICAgICAgfHwgbWVzc2FnZS5iaW5kaW5nQ291bnQgIT09IDEgfHwgbWVzc2FnZS5kZWxldGVkID09PSB0cnVlXG4gICAgICAgIHx8IG1lc3NhZ2Uuc3VwcG9ydFRpY2tldElkICE9PSBpbnB1dC50aWNrZXRJZCB8fCBtZXNzYWdlLnZpc2liaWxpdHkgIT09ICdjdXN0b21lcidcbiAgICAgICAgfHwgdHlwZW9mIG1lc3NhZ2UuYm9keSAhPT0gJ3N0cmluZycgfHwgbWVzc2FnZS5ib2R5Lmxlbmd0aCA+IDQwMDBcbiAgICAgICAgfHwgIU51bWJlci5pc1NhZmVJbnRlZ2VyKG1lc3NhZ2UubW9kaWZpZWQpIHx8IG1lc3NhZ2UubW9kaWZpZWQgPCAwIHx8IG1lc3NhZ2UubW9kaWZpZWQgPiBhdCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3N1cHBvcnRfY29udmVyc2F0aW9uX3NvdXJjZV9ob2xkJyk7XG4gIH1cbiAgcmV0dXJuIGNybUNhbm9uaWNhbERpZ2VzdChbdGlja2V0Ll9yZXYsIHVzZXIuX3JldiwgY29uc2VudC5fcmV2LFxuICAgIHJvdy5tZXNzYWdlcy5tYXAoKG1lc3NhZ2UpID0+IFttZXNzYWdlLl9rZXksIG1lc3NhZ2UuX3Jldl0pXSk7XG59O1xuZXhwb3J0IGNvbnN0IHN1cHBvcnRQdXJwb3NlU2NvcGUgPSB7XG4gIHN1cHBvcnRfY29udmVyc2F0aW9uOiAnc3VwcG9ydDpjb252ZXJzYXRpb24nLFxuICBzdXBwb3J0X2VtYWlsX3JlcGx5OiAnc3VwcG9ydDplbWFpbF9yZXBseScsIHN1cHBvcnRfc2VtYW50aWNfc3VtbWFyeTogJ3N1cHBvcnQ6c3VtbWFyeSdcbn0gYXMgY29uc3Q7XG4vKiogVGhlIGJvdW5kZWQgbmF0aXZlIGN1c3RvbWVyLXZpc2libGUgZmVlZCBpcyBhbiBvd25lZCBzb3VyY2UsIG5ldmVyIGFuIGV4dGVybmFsIHRyYW5zY3JpcHQgYXNzZXJ0aW9uLiAqL1xuZXhwb3J0IGNvbnN0IHJlYWRTdXBwb3J0UHVycG9zZUNvbnZlcnNhdGlvbiA9IGFzeW5jIChjb250ZXh0OiBBcGlDb250ZXh0LCBpbnB1dDogU3VwcG9ydENvbnZlcnNhdGlvbklucHV0LFxuICBwdXJwb3NlOiBTdXBwb3J0Q29uc2VudFB1cnBvc2UsIG5vdyA9IERhdGUubm93KSA9PiB7XG4gIGlmKCF2YWxpZFN1cHBvcnRDb252ZXJzYXRpb25JbnB1dChpbnB1dCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWRfc3VwcG9ydF9jb252ZXJzYXRpb24nKTtcbiAgfVxuICAvLyBUaGVzZSBuZXcgc2NvcGVzIGFyZSBhZGRlZCB0byB0aGUgcHVibGljIGdyYW50IGNvbnRyYWN0IGR1cmluZyBjb29yZGluYXRlZCBwYWNrYWdlIHdpcmluZy5cbiAgY29uc3QgZ3JhbnQgPSBhd2FpdCBhdXRob3JpemVDcm1FeGVjdXRpb25HcmFudChjb250ZXh0LCBpbnB1dC5hcHBJZCwgc3VwcG9ydFB1cnBvc2VTY29wZVtwdXJwb3NlXSwgbm93KCkpO1xuICBhd2FpdCBhdXRob3JpemVDcm1FeGVjdXRpb25HcmFudChjb250ZXh0LCBpbnB1dC5hcHBJZCwgJ3N1cHBvcnQ6cmVhZCcsIG5vdygpKTtcbiAgY29uc3Qgc2NvcGVkID0gYXdhaXQgc2NvcGVDb250ZXh0VG9BcHAoY29udGV4dCwgaW5wdXQuYXBwSWQpO1xuICBpZihzY29wZWQuZGF0YWJhc2VOYW1lICE9PSBncmFudC5kYXRhYmFzZU5hbWUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3N1cHBvcnRfY29udmVyc2F0aW9uX2FwcF9jaGFuZ2VkJyk7XG4gIH1cbiAgY29uc3QgZGIgPSB1c2VEYihncmFudC5kYXRhYmFzZU5hbWUpO1xuICBjb25zdCByb3cgPSBhd2FpdCByZWFkU3VwcG9ydENvbnZlcnNhdGlvblJvdyhkYiwgaW5wdXQsIHB1cnBvc2UpO1xuICBjb25zdCByZXZpc2lvbiA9IHZhbGlkYXRlU3VwcG9ydENvbnZlcnNhdGlvblJvdyhyb3csIGlucHV0LCBwdXJwb3NlLCBub3coKSk7XG4gIGNvbnN0IGxhdGVzdCA9IGF3YWl0IGF1dGhvcml6ZUNybUV4ZWN1dGlvbkdyYW50KGNvbnRleHQsIGlucHV0LmFwcElkLCBzdXBwb3J0UHVycG9zZVNjb3BlW3B1cnBvc2VdLCBub3coKSk7XG4gIGNvbnN0IHNjb3BlID0gYXdhaXQgc2NvcGVDb250ZXh0VG9BcHAoY29udGV4dCwgaW5wdXQuYXBwSWQpO1xuICBpZihncmFudC5pZCAhPT0gbGF0ZXN0LmlkIHx8IGdyYW50Lmhhc2ggIT09IGxhdGVzdC5oYXNoIHx8IGdyYW50LmRhdGFiYXNlTmFtZSAhPT0gbGF0ZXN0LmRhdGFiYXNlTmFtZVxuICAgICAgICB8fCBzY29wZS5kYXRhYmFzZU5hbWUgIT09IGdyYW50LmRhdGFiYXNlTmFtZVxuICAgICAgICB8fCB2YWxpZGF0ZVN1cHBvcnRDb252ZXJzYXRpb25Sb3coYXdhaXQgcmVhZFN1cHBvcnRDb252ZXJzYXRpb25Sb3coZGIsIGlucHV0LCBwdXJwb3NlKSwgaW5wdXQsIHB1cnBvc2UsXG4gICAgICAgICAgbm93KCkpICE9PSByZXZpc2lvbikge1xuICAgIHRocm93IG5ldyBFcnJvcignc3VwcG9ydF9jb252ZXJzYXRpb25fY2hhbmdlZCcpO1xuICB9XG4gIHJldHVybiB7XG4gICAgYXBwSWQ6IGlucHV0LmFwcElkLCBnZW5lcmF0ZWRBdDogbm93KCksIG1lc3NhZ2VzOiByb3cubWVzc2FnZXMuc2xpY2UoMCwgNTApLm1hcCgobWVzc2FnZSkgPT4gKHtcbiAgICAgIGJvZHk6IG1lc3NhZ2UuYm9keSwgaWQ6IG1lc3NhZ2UuX2tleSwgbW9kaWZpZWRBdDogbWVzc2FnZS5tb2RpZmllZCwgcmV2aXNpb246IG1lc3NhZ2UuX3JldlxuICAgIH0pKSxcbiAgICBwcm92ZW5hbmNlOiAnbmF0aXZlX2N1c3RvbWVyX3Zpc2libGVfZmVlZCcsIHJldmlzaW9uLCBzY2hlbWFWZXJzaW9uOiAnc3VwcG9ydF9jb252ZXJzYXRpb25fdjEnLFxuICAgIHRpY2tldElkOiBpbnB1dC50aWNrZXRJZCwgdGlja2V0UmV2aXNpb246IGlucHV0LnRpY2tldFJldmlzaW9uLCB0cnVuY2F0ZWQ6IHJvdy5tZXNzYWdlcy5sZW5ndGggPiA1MCxcbiAgICB2YWxpZFVudGlsOiBNYXRoLm1pbihub3coKSArIDMwMDAwMCwgbGF0ZXN0LmV4cGlyZXMsIHJvdy5jb25zZW50LmV4cGlyZXNBdClcbiAgfTtcbn07XG5leHBvcnQgY29uc3QgcmVhZFN1cHBvcnRDb252ZXJzYXRpb24gPSBhc3luYyAoY29udGV4dDogQXBpQ29udGV4dCwgaW5wdXQ6IFN1cHBvcnRDb252ZXJzYXRpb25JbnB1dCwgbm93ID1cbiAgRGF0ZS5ub3cpID0+IHJlYWRTdXBwb3J0UHVycG9zZUNvbnZlcnNhdGlvbihjb250ZXh0LCBpbnB1dCwgJ3N1cHBvcnRfY29udmVyc2F0aW9uJywgbm93KTtcbmV4cG9ydCBjb25zdCByZWFkU3VwcG9ydFNlbWFudGljU291cmNlID0gYXN5bmMgKGNvbnRleHQ6IEFwaUNvbnRleHQsIGlucHV0OiBTdXBwb3J0Q29udmVyc2F0aW9uSW5wdXQsIG5vdyA9XG4gIERhdGUubm93KSA9PiByZWFkU3VwcG9ydFB1cnBvc2VDb252ZXJzYXRpb24oY29udGV4dCwgaW5wdXQsICdzdXBwb3J0X3NlbWFudGljX3N1bW1hcnknLCBub3cpO1xuIl0sIm5hbWVzIjpbImFxbCIsInNjb3BlQ29udGV4dFRvQXBwIiwidXNlRGIiLCJjcm1DYW5vbmljYWxEaWdlc3QiLCJhdXRob3JpemVDcm1FeGVjdXRpb25HcmFudCIsInN1cHBvcnRDb252ZXJzYXRpb25LZXkiLCJ2YWx1ZSIsInRlc3QiLCJ2YWxpZFN1cHBvcnRDb252ZXJzYXRpb25JbnB1dCIsImlucHV0IiwiQm9vbGVhbiIsIk9iamVjdCIsImtleXMiLCJzb3J0Iiwiam9pbiIsImFwcElkIiwidGlja2V0SWQiLCJ0aWNrZXRSZXZpc2lvbiIsImV2ZXJ5IiwicmVhZFN1cHBvcnRDb252ZXJzYXRpb25Sb3ciLCJkYiIsInB1cnBvc2UiLCJxdWVyeSIsImJpbmQiLCJ0aGVuIiwiY3Vyc29yIiwibmV4dCIsInZhbGlkYXRlU3VwcG9ydENvbnZlcnNhdGlvblJvdyIsInJvdyIsImF0IiwiY29uc2VudCIsInRpY2tldCIsInVzZXIiLCJfa2V5IiwiX3JldiIsImRlbGV0ZWQiLCJpbmNsdWRlcyIsInN0YXR1cyIsInVzZXJJZCIsImFjdGl2ZSIsInNvdXJjZSIsIk51bWJlciIsImlzU2FmZUludGVnZXIiLCJleHBpcmVzQXQiLCJBcnJheSIsImlzQXJyYXkiLCJtZXNzYWdlcyIsInNvbWUiLCJtZXNzYWdlIiwidW5kZWZpbmVkIiwiYmluZGluZ0NvdW50Iiwic3VwcG9ydFRpY2tldElkIiwidmlzaWJpbGl0eSIsImJvZHkiLCJsZW5ndGgiLCJtb2RpZmllZCIsIkVycm9yIiwibWFwIiwic3VwcG9ydFB1cnBvc2VTY29wZSIsInN1cHBvcnRfY29udmVyc2F0aW9uIiwic3VwcG9ydF9lbWFpbF9yZXBseSIsInN1cHBvcnRfc2VtYW50aWNfc3VtbWFyeSIsInJlYWRTdXBwb3J0UHVycG9zZUNvbnZlcnNhdGlvbiIsImNvbnRleHQiLCJub3ciLCJEYXRlIiwiZ3JhbnQiLCJzY29wZWQiLCJkYXRhYmFzZU5hbWUiLCJyZXZpc2lvbiIsImxhdGVzdCIsInNjb3BlIiwiaWQiLCJoYXNoIiwiZ2VuZXJhdGVkQXQiLCJzbGljZSIsIm1vZGlmaWVkQXQiLCJwcm92ZW5hbmNlIiwic2NoZW1hVmVyc2lvbiIsInRydW5jYXRlZCIsInZhbGlkVW50aWwiLCJNYXRoIiwibWluIiwiZXhwaXJlcyIsInJlYWRTdXBwb3J0Q29udmVyc2F0aW9uIiwicmVhZFN1cHBvcnRTZW1hbnRpY1NvdXJjZSJdLCJtYXBwaW5ncyI6IkFBQUEsd0ZBQXdGLEdBQ3hGLFNBQVFBLEdBQUcsUUFBTyxXQUFXO0FBRTdCLFNBQVFDLGlCQUFpQixRQUFPLHlCQUF5QjtBQUN6RCxTQUFRQyxLQUFLLFFBQU8sNEJBQTRCO0FBQ2hELFNBQVFDLGtCQUFrQixRQUFPLDBCQUEwQjtBQUMzRCxTQUFRQywwQkFBMEIsUUFBTyxvQkFBb0I7QUFVN0QsT0FBTyxNQUFNQyx5QkFBeUIsQ0FBQ0MsUUFBb0MsT0FBT0EsVUFBVSxZQUMxRixrQkFBa0JDLElBQUksQ0FBQ0QsT0FBTztBQUNoQyxPQUFPLE1BQU1FLGdDQUFnQyxDQUFDQyxRQUFvQ0MsUUFBUUQsU0FDbkZFLE9BQU9DLElBQUksQ0FBQ0gsT0FBT0ksSUFBSSxHQUFHQyxJQUFJLENBQUMsU0FBUyxtQ0FDeEM7UUFBQ0wsTUFBTU0sS0FBSztRQUFFTixNQUFNTyxRQUFRO1FBQUVQLE1BQU1RLGNBQWM7S0FBQyxDQUFDQyxLQUFLLENBQUNiLHlCQUF5QjtBQWlDMUYsT0FBTyxNQUFNYyw2QkFBNkIsT0FBT0MsSUFBOEJYLE9BQzdFWSxTQUFnQ0MsUUFBUUYsR0FBR0UsS0FBSyxDQUFDQyxJQUFJ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
@@ -0,0 +1,20 @@
1
+ import type { ApiContext } from '../types/auth.types.js';
2
+ export interface SupportEmailEventInput {
3
+ appId: string;
4
+ eventId: string;
5
+ kind: 'accepted' | 'delivered' | 'bounce' | 'complaint';
6
+ occurredAt: number;
7
+ providerMessageId: string;
8
+ receiptId: string;
9
+ recipient: string;
10
+ }
11
+ /** Trusted provider bridge only. It must verify SES/SNS authenticity before calling this scoped owner boundary.
12
+ * support_receipt SES tag + exact recipient bind an unknown acknowledgement to its actual submission.
13
+ * Ordinary Ads/customer keys never receive this scope; browser event assertions are not provider evidence.
14
+ */
15
+ export declare const ingestSupportEmailEvent: (context: ApiContext, input: SupportEmailEventInput, now?: () => number) => Promise<{
16
+ id: string;
17
+ kind: string;
18
+ occurredAt: number;
19
+ receiptId: string;
20
+ }>;
@@ -0,0 +1,130 @@
1
+ import { aql } from 'arangojs';
2
+ import { useDb } from '../utils/arangodbUtils.js';
3
+ import { crmCanonicalDigest } from './crmCanonicalPolicy.js';
4
+ import { authorizeCrmExecutionGrant, ensureOwnerExecutionCollection } from './crmExecution.js';
5
+ import { supportConversationKey } from './supportConversation.js';
6
+ /** Trusted provider bridge only. It must verify SES/SNS authenticity before calling this scoped owner boundary.
7
+ * support_receipt SES tag + exact recipient bind an unknown acknowledgement to its actual submission.
8
+ * Ordinary Ads/customer keys never receive this scope; browser event assertions are not provider evidence.
9
+ */ export const ingestSupportEmailEvent = async (context, input, now = Date.now)=>{
10
+ if (!input || Object.keys(input).sort().join(',') !== 'appId,eventId,kind,occurredAt,providerMessageId,receiptId,recipient' || ![
11
+ input.appId,
12
+ input.eventId,
13
+ input.providerMessageId
14
+ ].every(supportConversationKey) || !/^[a-f0-9]{64}$/.test(input.receiptId) || typeof input.recipient !== 'string' || input.recipient.length > 254 || ![
15
+ 'accepted',
16
+ 'delivered',
17
+ 'bounce',
18
+ 'complaint'
19
+ ].includes(input.kind) || !Number.isSafeInteger(input.occurredAt) || input.occurredAt > now() + 60000 || input.occurredAt < now() - 30 * 86400000) {
20
+ throw new Error('invalid_support_email_event');
21
+ }
22
+ const grant = await authorizeCrmExecutionGrant(context, input.appId, 'support:delivery_events', now());
23
+ const db = useDb(grant.databaseName);
24
+ await Promise.all([
25
+ ensureOwnerExecutionCollection(db, 'supportEmailEvents'),
26
+ ensureOwnerExecutionCollection(db, 'hasSupportEmailEvent', true)
27
+ ]);
28
+ await db.collection('supportEmailEvents').ensureIndex({
29
+ expireAfter: 0,
30
+ fields: [
31
+ 'retireAt'
32
+ ],
33
+ type: 'ttl'
34
+ });
35
+ await db.collection('hasSupportEmailEvent').ensureIndex({
36
+ expireAfter: 0,
37
+ fields: [
38
+ 'retireAt'
39
+ ],
40
+ type: 'ttl'
41
+ });
42
+ const tx = await db.beginTransaction({
43
+ exclusive: [
44
+ 'supportEmailEvents',
45
+ 'hasSupportEmailEvent',
46
+ 'supportEmailReceipts',
47
+ 'users'
48
+ ],
49
+ read: [
50
+ 'supportTickets'
51
+ ]
52
+ }, {
53
+ lockTimeout: 10
54
+ });
55
+ const query = (value)=>tx.step(()=>db.query(value));
56
+ try {
57
+ const receipt = await query(aql`RETURN DOCUMENT('supportEmailReceipts', ${input.receiptId})`).then((cursor)=>cursor.next());
58
+ if (!receipt || receipt.appId !== input.appId || receipt.id !== input.receiptId || ![
59
+ 'submitting',
60
+ 'unknown',
61
+ 'accepted'
62
+ ].includes(receipt.state) || receipt.recipientHash !== crmCanonicalDigest(input.recipient) || receipt.providerMessageId && receipt.providerMessageId !== input.providerMessageId || input.occurredAt < receipt.createdAt - 60000 || input.occurredAt > receipt.createdAt + 30 * 86400000) {
63
+ throw new Error('support_email_event_submission_hold');
64
+ }
65
+ const id = crmCanonicalDigest([
66
+ input.appId,
67
+ input.eventId
68
+ ]);
69
+ const fingerprint = crmCanonicalDigest(input);
70
+ const prior = await query(aql`RETURN DOCUMENT('supportEmailEvents', ${id})`).then((cursor)=>cursor.next());
71
+ if (prior && prior.fingerprint !== fingerprint) {
72
+ throw new Error('support_email_event_conflict');
73
+ }
74
+ const latest = await authorizeCrmExecutionGrant(context, input.appId, 'support:delivery_events', now());
75
+ if (latest.hash !== grant.hash || latest.id !== grant.id || latest.databaseName !== grant.databaseName) {
76
+ throw new Error('support_email_relay_changed');
77
+ }
78
+ if (prior) {
79
+ await tx.commit();
80
+ return prior.receipt;
81
+ }
82
+ const events = await query(aql`FOR event IN supportEmailEvents FILTER event.appId == ${input.appId}
83
+ AND event.receipt.receiptId == ${input.receiptId} LIMIT 32 RETURN event._key`).then((cursor)=>cursor.all());
84
+ if (events.length >= 32) {
85
+ throw new Error('support_email_event_limit');
86
+ }
87
+ // Negative delivery evidence suppresses even when conversation consent/revision has since changed.
88
+ if ([
89
+ 'bounce',
90
+ 'complaint'
91
+ ].includes(input.kind)) {
92
+ await query(aql`LET ticket = DOCUMENT('supportTickets', ${receipt.ticketId})
93
+ FOR user IN users FILTER user._key == ticket.userId AND user.appId == ${input.appId}
94
+ AND user.email == ${input.recipient} AND user.deleted != true
95
+ UPDATE user WITH {emailDeliveryStatus: user.emailDeliveryStatus == 'complaint' OR ${input.kind} == 'complaint'
96
+ ? 'complaint' : 'bounce_hold', supportEmailSuppressed: true} IN users`);
97
+ }
98
+ const result = {
99
+ id,
100
+ kind: input.kind,
101
+ occurredAt: input.occurredAt,
102
+ receiptId: input.receiptId
103
+ };
104
+ const retireAt = Math.floor((now() + 30 * 86400000) / 1000);
105
+ await query(aql`INSERT ${{
106
+ _key: id,
107
+ appId: input.appId,
108
+ fingerprint,
109
+ receipt: result,
110
+ retireAt
111
+ }} IN supportEmailEvents`);
112
+ await query(aql`INSERT ${{
113
+ _from: `supportEmailReceipts/${input.receiptId}`,
114
+ _key: id,
115
+ _to: `supportEmailEvents/${id}`,
116
+ appId: input.appId,
117
+ retireAt
118
+ }} IN hasSupportEmailEvent`);
119
+ await query(aql`UPDATE ${input.receiptId} WITH {state: 'accepted', providerAcceptedAt:
120
+ ${receipt.providerAcceptedAt || input.occurredAt}, providerMessageId: ${input.providerMessageId}} IN
121
+ supportEmailReceipts`);
122
+ await tx.commit();
123
+ return result;
124
+ } catch (error) {
125
+ await tx.abort();
126
+ throw error;
127
+ }
128
+ };
129
+
130
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi90bXAvcmVhY3RvcmJveC1zdXBwb3J0LW93bmVyLWZyZWV6ZS0yMDI2MTAwOC9pbnB1dC9hY3Rpb25zL3N1cHBvcnRFbWFpbEV2ZW50cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge2FxbH0gZnJvbSAnYXJhbmdvanMnO1xuXG5pbXBvcnQge3VzZURifSBmcm9tICcuLi91dGlscy9hcmFuZ29kYlV0aWxzLmpzJztcbmltcG9ydCB7Y3JtQ2Fub25pY2FsRGlnZXN0fSBmcm9tICcuL2NybUNhbm9uaWNhbFBvbGljeS5qcyc7XG5pbXBvcnQge2F1dGhvcml6ZUNybUV4ZWN1dGlvbkdyYW50LCBlbnN1cmVPd25lckV4ZWN1dGlvbkNvbGxlY3Rpb259IGZyb20gJy4vY3JtRXhlY3V0aW9uLmpzJztcbmltcG9ydCB7c3VwcG9ydENvbnZlcnNhdGlvbktleX0gZnJvbSAnLi9zdXBwb3J0Q29udmVyc2F0aW9uLmpzJztcblxuaW1wb3J0IHR5cGUge0FwaUNvbnRleHR9IGZyb20gJy4uL3R5cGVzL2F1dGgudHlwZXMuanMnO1xuaW1wb3J0IHR5cGUge1N1cHBvcnRFbWFpbFJlY2VpcHR9IGZyb20gJy4vc3VwcG9ydEVtYWlsSGFuZG9mZi5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3VwcG9ydEVtYWlsRXZlbnRJbnB1dCB7XG4gIGFwcElkOiBzdHJpbmc7XG4gIGV2ZW50SWQ6IHN0cmluZztcbiAga2luZDogJ2FjY2VwdGVkJyB8ICdkZWxpdmVyZWQnIHwgJ2JvdW5jZScgfCAnY29tcGxhaW50JztcbiAgb2NjdXJyZWRBdDogbnVtYmVyO1xuICBwcm92aWRlck1lc3NhZ2VJZDogc3RyaW5nO1xuICByZWNlaXB0SWQ6IHN0cmluZztcbiAgcmVjaXBpZW50OiBzdHJpbmc7XG59XG4vKiogVHJ1c3RlZCBwcm92aWRlciBicmlkZ2Ugb25seS4gSXQgbXVzdCB2ZXJpZnkgU0VTL1NOUyBhdXRoZW50aWNpdHkgYmVmb3JlIGNhbGxpbmcgdGhpcyBzY29wZWQgb3duZXIgYm91bmRhcnkuXG4gKiBzdXBwb3J0X3JlY2VpcHQgU0VTIHRhZyArIGV4YWN0IHJlY2lwaWVudCBiaW5kIGFuIHVua25vd24gYWNrbm93bGVkZ2VtZW50IHRvIGl0cyBhY3R1YWwgc3VibWlzc2lvbi5cbiAqIE9yZGluYXJ5IEFkcy9jdXN0b21lciBrZXlzIG5ldmVyIHJlY2VpdmUgdGhpcyBzY29wZTsgYnJvd3NlciBldmVudCBhc3NlcnRpb25zIGFyZSBub3QgcHJvdmlkZXIgZXZpZGVuY2UuXG4gKi9cbmV4cG9ydCBjb25zdCBpbmdlc3RTdXBwb3J0RW1haWxFdmVudCA9IGFzeW5jIChjb250ZXh0OiBBcGlDb250ZXh0LCBpbnB1dDogU3VwcG9ydEVtYWlsRXZlbnRJbnB1dCwgbm93ID0gRGF0ZS5ub3cpID0+IHtcbiAgaWYoIWlucHV0IHx8IE9iamVjdC5rZXlzKGlucHV0KS5zb3J0KCkuam9pbignLCcpICE9PVxuICAgICdhcHBJZCxldmVudElkLGtpbmQsb2NjdXJyZWRBdCxwcm92aWRlck1lc3NhZ2VJZCxyZWNlaXB0SWQscmVjaXBpZW50J1xuICAgICAgICB8fCAhW2lucHV0LmFwcElkLCBpbnB1dC5ldmVudElkLCBpbnB1dC5wcm92aWRlck1lc3NhZ2VJZF0uZXZlcnkoc3VwcG9ydENvbnZlcnNhdGlvbktleSlcbiAgICAgICAgfHwgIS9eW2EtZjAtOV17NjR9JC8udGVzdChpbnB1dC5yZWNlaXB0SWQpIHx8IHR5cGVvZiBpbnB1dC5yZWNpcGllbnQgIT09ICdzdHJpbmcnIHx8XG4gICAgICAgICAgaW5wdXQucmVjaXBpZW50Lmxlbmd0aCA+IDI1NFxuICAgICAgICB8fCAhWydhY2NlcHRlZCcsICdkZWxpdmVyZWQnLCAnYm91bmNlJywgJ2NvbXBsYWludCddLmluY2x1ZGVzKGlucHV0LmtpbmQpIHx8XG4gICAgICAgICAgIU51bWJlci5pc1NhZmVJbnRlZ2VyKGlucHV0Lm9jY3VycmVkQXQpXG4gICAgICAgIHx8IGlucHV0Lm9jY3VycmVkQXQgPiBub3coKSArIDYwMDAwIHx8IGlucHV0Lm9jY3VycmVkQXQgPCBub3coKSAtICgzMCAqIDg2NDAwMDAwKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZF9zdXBwb3J0X2VtYWlsX2V2ZW50Jyk7XG4gIH1cbiAgY29uc3QgZ3JhbnQgPSBhd2FpdCBhdXRob3JpemVDcm1FeGVjdXRpb25HcmFudChjb250ZXh0LCBpbnB1dC5hcHBJZCwgJ3N1cHBvcnQ6ZGVsaXZlcnlfZXZlbnRzJywgbm93KCkpO1xuICBjb25zdCBkYiA9IHVzZURiKGdyYW50LmRhdGFiYXNlTmFtZSk7XG4gIGF3YWl0IFByb21pc2UuYWxsKFtlbnN1cmVPd25lckV4ZWN1dGlvbkNvbGxlY3Rpb24oZGIsICdzdXBwb3J0RW1haWxFdmVudHMnKSxcbiAgICBlbnN1cmVPd25lckV4ZWN1dGlvbkNvbGxlY3Rpb24oZGIsICdoYXNTdXBwb3J0RW1haWxFdmVudCcsIHRydWUpXSk7XG4gIGF3YWl0IGRiLmNvbGxlY3Rpb24oJ3N1cHBvcnRFbWFpbEV2ZW50cycpLmVuc3VyZUluZGV4KHtcbiAgICBleHBpcmVBZnRlcjogMCwgZmllbGRzOiBbJ3JldGlyZUF0J10sIHR5cGU6ICd0dGwnXG4gIH0pO1xuICBhd2FpdCBkYi5jb2xsZWN0aW9uKCdoYXNTdXBwb3J0RW1haWxFdmVudCcpLmVuc3VyZUluZGV4KHtcbiAgICBleHBpcmVBZnRlcjogMCwgZmllbGRzOiBbJ3JldGlyZUF0J10sIHR5cGU6ICd0dGwnXG4gIH0pO1xuICBjb25zdCB0eCA9IGF3YWl0IGRiLmJlZ2luVHJhbnNhY3Rpb24oe1xuICAgIGV4Y2x1c2l2ZTogWydzdXBwb3J0RW1haWxFdmVudHMnLCAnaGFzU3VwcG9ydEVtYWlsRXZlbnQnLCAnc3VwcG9ydEVtYWlsUmVjZWlwdHMnLCAndXNlcnMnXSxcbiAgICByZWFkOiBbJ3N1cHBvcnRUaWNrZXRzJ11cbiAgfSwge1xuICAgIGxvY2tUaW1lb3V0OiAxMFxuICB9KTtcbiAgY29uc3QgcXVlcnkgPSAodmFsdWU6IFJldHVyblR5cGU8dHlwZW9mIGFxbD4pID0+IHR4LnN0ZXAoKCkgPT4gZGIucXVlcnkodmFsdWUpKTtcbiAgdHJ5IHtcbiAgICBjb25zdCByZWNlaXB0ID0gYXdhaXQgcXVlcnkoYXFsYFJFVFVSTiBET0NVTUVOVCgnc3VwcG9ydEVtYWlsUmVjZWlwdHMnLCAke2lucHV0LnJlY2VpcHRJZH0pYCkudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSBhcyAoU3VwcG9ydEVtYWlsUmVjZWlwdCAmIHtcbiAgICAgIHJlY2lwaWVudEhhc2g6IHN0cmluZztcbiAgICB9KSB8IHVuZGVmaW5lZDtcbiAgICBpZighcmVjZWlwdCB8fCByZWNlaXB0LmFwcElkICE9PSBpbnB1dC5hcHBJZCB8fCByZWNlaXB0LmlkICE9PSBpbnB1dC5yZWNlaXB0SWRcbiAgICAgICAgICAgIHx8ICFbJ3N1Ym1pdHRpbmcnLCAndW5rbm93bicsICdhY2NlcHRlZCddLmluY2x1ZGVzKHJlY2VpcHQuc3RhdGUpXG4gICAgICAgICAgICB8fCByZWNlaXB0LnJlY2lwaWVudEhhc2ggIT09IGNybUNhbm9uaWNhbERpZ2VzdChpbnB1dC5yZWNpcGllbnQpXG4gICAgICAgICAgICB8fCAocmVjZWlwdC5wcm92aWRlck1lc3NhZ2VJZCAmJiByZWNlaXB0LnByb3ZpZGVyTWVzc2FnZUlkICE9PSBpbnB1dC5wcm92aWRlck1lc3NhZ2VJZClcbiAgICAgICAgICAgIHx8IGlucHV0Lm9jY3VycmVkQXQgPCByZWNlaXB0LmNyZWF0ZWRBdCAtIDYwMDAwIHx8IGlucHV0Lm9jY3VycmVkQXQgPiByZWNlaXB0LmNyZWF0ZWRBdCArICgzMCAqIDg2NDAwMDAwKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzdXBwb3J0X2VtYWlsX2V2ZW50X3N1Ym1pc3Npb25faG9sZCcpO1xuICAgIH1cbiAgICBjb25zdCBpZCA9IGNybUNhbm9uaWNhbERpZ2VzdChbaW5wdXQuYXBwSWQsIGlucHV0LmV2ZW50SWRdKTtcbiAgICBjb25zdCBmaW5nZXJwcmludCA9IGNybUNhbm9uaWNhbERpZ2VzdChpbnB1dCk7XG4gICAgY29uc3QgcHJpb3IgPSBhd2FpdCBxdWVyeShhcWwgYFJFVFVSTiBET0NVTUVOVCgnc3VwcG9ydEVtYWlsRXZlbnRzJywgJHtpZH0pYCkudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSBhcyB7XG4gICAgICBmaW5nZXJwcmludDogc3RyaW5nO1xuICAgICAgcmVjZWlwdDoge1xuICAgICAgICBpZDogc3RyaW5nO1xuICAgICAgICBraW5kOiBzdHJpbmc7XG4gICAgICAgIG9jY3VycmVkQXQ6IG51bWJlcjtcbiAgICAgICAgcmVjZWlwdElkOiBzdHJpbmc7XG4gICAgICB9O1xuICAgIH0gfCB1bmRlZmluZWQ7XG4gICAgaWYocHJpb3IgJiYgcHJpb3IuZmluZ2VycHJpbnQgIT09IGZpbmdlcnByaW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3N1cHBvcnRfZW1haWxfZXZlbnRfY29uZmxpY3QnKTtcbiAgICB9XG4gICAgY29uc3QgbGF0ZXN0ID0gYXdhaXQgYXV0aG9yaXplQ3JtRXhlY3V0aW9uR3JhbnQoY29udGV4dCwgaW5wdXQuYXBwSWQsICdzdXBwb3J0OmRlbGl2ZXJ5X2V2ZW50cycsIG5vdygpKTtcbiAgICBpZihsYXRlc3QuaGFzaCAhPT0gZ3JhbnQuaGFzaCB8fCBsYXRlc3QuaWQgIT09IGdyYW50LmlkIHx8IGxhdGVzdC5kYXRhYmFzZU5hbWUgIT09IGdyYW50LmRhdGFiYXNlTmFtZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzdXBwb3J0X2VtYWlsX3JlbGF5X2NoYW5nZWQnKTtcbiAgICB9XG4gICAgaWYocHJpb3IpIHtcbiAgICAgIGF3YWl0IHR4LmNvbW1pdCgpO1xuICAgICAgcmV0dXJuIHByaW9yLnJlY2VpcHQ7XG4gICAgfVxuICAgIGNvbnN0IGV2ZW50cyA9IGF3YWl0IHF1ZXJ5KGFxbCBgRk9SIGV2ZW50IElOIHN1cHBvcnRFbWFpbEV2ZW50cyBGSUxURVIgZXZlbnQuYXBwSWQgPT0gJHtpbnB1dC5hcHBJZH1cbiAgICAgIEFORCBldmVudC5yZWNlaXB0LnJlY2VpcHRJZCA9PSAke2lucHV0LnJlY2VpcHRJZH0gTElNSVQgMzIgUkVUVVJOIGV2ZW50Ll9rZXlgKS50aGVuKChjdXJzb3IpID0+IGN1cnNvci5hbGwoKSk7XG4gICAgaWYoZXZlbnRzLmxlbmd0aCA+PSAzMikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzdXBwb3J0X2VtYWlsX2V2ZW50X2xpbWl0Jyk7XG4gICAgfVxuICAgIC8vIE5lZ2F0aXZlIGRlbGl2ZXJ5IGV2aWRlbmNlIHN1cHByZXNzZXMgZXZlbiB3aGVuIGNvbnZlcnNhdGlvbiBjb25zZW50L3JldmlzaW9uIGhhcyBzaW5jZSBjaGFuZ2VkLlxuICAgIGlmKFsnYm91bmNlJywgJ2NvbXBsYWludCddLmluY2x1ZGVzKGlucHV0LmtpbmQpKSB7XG4gICAgICBhd2FpdCBxdWVyeShhcWwgYExFVCB0aWNrZXQgPSBET0NVTUVOVCgnc3VwcG9ydFRpY2tldHMnLCAke3JlY2VpcHQudGlja2V0SWR9KVxuICAgICAgICBGT1IgdXNlciBJTiB1c2VycyBGSUxURVIgdXNlci5fa2V5ID09IHRpY2tldC51c2VySWQgQU5EIHVzZXIuYXBwSWQgPT0gJHtpbnB1dC5hcHBJZH1cbiAgICAgICAgQU5EIHVzZXIuZW1haWwgPT0gJHtpbnB1dC5yZWNpcGllbnR9IEFORCB1c2VyLmRlbGV0ZWQgIT0gdHJ1ZVxuICAgICAgICBVUERBVEUgdXNlciBXSVRIIHtlbWFpbERlbGl2ZXJ5U3RhdHVzOiB1c2VyLmVtYWlsRGVsaXZlcnlTdGF0dXMgPT0gJ2NvbXBsYWludCcgT1IgJHtpbnB1dC5raW5kfSA9PSAnY29tcGxhaW50J1xuICAgICAgICAgID8gJ2NvbXBsYWludCcgOiAnYm91bmNlX2hvbGQnLCBzdXBwb3J0RW1haWxTdXBwcmVzc2VkOiB0cnVlfSBJTiB1c2Vyc2ApO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHQgPSB7XG4gICAgICBpZCwga2luZDogaW5wdXQua2luZCwgb2NjdXJyZWRBdDogaW5wdXQub2NjdXJyZWRBdCwgcmVjZWlwdElkOiBpbnB1dC5yZWNlaXB0SWRcbiAgICB9O1xuICAgIGNvbnN0IHJldGlyZUF0ID0gTWF0aC5mbG9vcigobm93KCkgKyAoMzAgKiA4NjQwMDAwMCkpIC8gMTAwMCk7XG4gICAgYXdhaXQgcXVlcnkoYXFsIGBJTlNFUlQgJHt7XG4gICAgICBfa2V5OiBpZCwgYXBwSWQ6IGlucHV0LmFwcElkLCBmaW5nZXJwcmludCwgcmVjZWlwdDogcmVzdWx0LCByZXRpcmVBdFxuICAgIH19IElOIHN1cHBvcnRFbWFpbEV2ZW50c2ApO1xuICAgIGF3YWl0IHF1ZXJ5KGFxbCBgSU5TRVJUICR7e1xuICAgICAgX2Zyb206IGBzdXBwb3J0RW1haWxSZWNlaXB0cy8ke2lucHV0LnJlY2VpcHRJZH1gLCBfa2V5OiBpZCxcbiAgICAgIF90bzogYHN1cHBvcnRFbWFpbEV2ZW50cy8ke2lkfWAsIGFwcElkOiBpbnB1dC5hcHBJZCwgcmV0aXJlQXRcbiAgICB9fSBJTiBoYXNTdXBwb3J0RW1haWxFdmVudGApO1xuICAgIGF3YWl0IHF1ZXJ5KGFxbCBgVVBEQVRFICR7aW5wdXQucmVjZWlwdElkfSBXSVRIIHtzdGF0ZTogJ2FjY2VwdGVkJywgcHJvdmlkZXJBY2NlcHRlZEF0OlxuICAgICAgJHtyZWNlaXB0LnByb3ZpZGVyQWNjZXB0ZWRBdCB8fCBpbnB1dC5vY2N1cnJlZEF0fSwgcHJvdmlkZXJNZXNzYWdlSWQ6ICR7aW5wdXQucHJvdmlkZXJNZXNzYWdlSWR9fSBJTlxuICAgICAgICBzdXBwb3J0RW1haWxSZWNlaXB0c2ApO1xuICAgIGF3YWl0IHR4LmNvbW1pdCgpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBhd2FpdCB0eC5hYm9ydCgpO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59O1xuIl0sIm5hbWVzIjpbImFxbCIsInVzZURiIiwiY3JtQ2Fub25pY2FsRGlnZXN0IiwiYXV0aG9yaXplQ3JtRXhlY3V0aW9uR3JhbnQiLCJlbnN1cmVPd25lckV4ZWN1dGlvbkNvbGxlY3Rpb24iLCJzdXBwb3J0Q29udmVyc2F0aW9uS2V5IiwiaW5nZXN0U3VwcG9ydEVtYWlsRXZlbnQiLCJjb250ZXh0IiwiaW5wdXQiLCJub3ciLCJEYXRlIiwiT2JqZWN0Iiwia2V5cyIsInNvcnQiLCJqb2luIiwiYXBwSWQiLCJldmVudElkIiwicHJvdmlkZXJNZXNzYWdlSWQiLCJldmVyeSIsInRlc3QiLCJyZWNlaXB0SWQiLCJyZWNpcGllbnQiLCJsZW5ndGgiLCJpbmNsdWRlcyIsImtpbmQiLCJOdW1iZXIiLCJpc1NhZmVJbnRlZ2VyIiwib2NjdXJyZWRBdCIsIkVycm9yIiwiZ3JhbnQiLCJkYiIsImRhdGFiYXNlTmFtZSIsIlByb21pc2UiLCJhbGwiLCJjb2xsZWN0aW9uIiwiZW5zdXJlSW5kZXgiLCJleHBpcmVBZnRlciIsImZpZWxkcyIsInR5cGUiLCJ0eCIsImJlZ2luVHJhbnNhY3Rpb24iLCJleGNsdXNpdmUiLCJyZWFkIiwibG9ja1RpbWVvdXQiLCJxdWVyeSIsInZhbHVlIiwic3RlcCIsInJlY2VpcHQiLCJ0aGVuIiwiY3Vyc29yIiwibmV4dCIsImlkIiwic3RhdGUiLCJyZWNpcGllbnRIYXNoIiwiY3JlYXRlZEF0IiwiZmluZ2VycHJpbnQiLCJwcmlvciIsImxhdGVzdCIsImhhc2giLCJjb21taXQiLCJldmVudHMiLCJ0aWNrZXRJZCIsInJlc3VsdCIsInJldGlyZUF0IiwiTWF0aCIsImZsb29yIiwiX2tleSIsIl9mcm9tIiwiX3RvIiwicHJvdmlkZXJBY2NlcHRlZEF0IiwiZXJyb3IiLCJhYm9ydCJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBUUEsR0FBRyxRQUFPLFdBQVc7QUFFN0IsU0FBUUMsS0FBSyxRQUFPLDRCQUE0QjtBQUNoRCxTQUFRQyxrQkFBa0IsUUFBTywwQkFBMEI7QUFDM0QsU0FBUUMsMEJBQTBCLEVBQUVDLDhCQUE4QixRQUFPLG9CQUFvQjtBQUM3RixTQUFRQyxzQkFBc0IsUUFBTywyQkFBMkI7QUFjaEU7OztDQUdDLEdBQ0QsT0FBTyxNQUFNQywwQkFBMEIsT0FBT0MsU0FBcUJDLE9BQStCQyxNQUFNQyxLQUFLRCxHQUFHO0lBQzlHLElBQUcsQ0FBQ0QsU0FBU0csT0FBT0MsSUFBSSxDQUFDSixPQUFPSyxJQUFJLEdBQUdDLElBQUksQ0FBQyxTQUMxQyx5RUFDTyxDQUFDO1FBQUNOLE1BQU1PLEtBQUs7UUFBRVAsTUFBTVEsT0FBTztRQUFFUixNQUFNUyxpQkFBaUI7S0FBQyxDQUFDQyxLQUFLLENBQUNiLDJCQUM3RCxDQUFDLGlCQUFpQmMsSUFBSSxDQUFDWCxNQUFNWSxTQUFTLEtBQUssT0FBT1osTUFBTWEsU0FBUyxLQUFLLFlBQ3ZFYixNQUFNYSxTQUFTLENBQUNDLE1BQU0sR0FBRyxPQUN4QixDQUFDO1FBQUM7UUFBWTtRQUFhO1FBQVU7S0FBWSxDQUFDQyxRQUFRLENBQUNmLE1BQU1nQixJQUFJLEtBQ3RFLENBQUNDLE9BQU9DLGFBQWEsQ0FBQ2xCLE1BQU1tQixVQUFVLEtBQ3JDbkIsTUFBTW1CLFVBQVUsR0FBR2xCLFFBQVEsU0FBU0QsTUFBTW1CLFVBQVUsR0FBR2xCLFFBQVMsS0FBSyxVQUFXO1FBQ3ZGLE1BQU0sSUFBSW1CLE1BQU07SUFDbEI7SUFDQSxNQUFNQyxRQUFRLE1BQU0xQiwyQkFBMkJJLFNBQVNDLE1BQU1PLEtBQUssRUFBRSwyQkFBMkJOO0lBQ2hHLE1BQU1xQixLQUFLN0IsTUFBTTRCLE1BQU1FLFlBQVk7SUFDbkMsTUFBTUMsUUFBUUMsR0FBRyxDQUFDO1FBQUM3QiwrQkFBK0IwQixJQUFJO1FBQ3BEMUIsK0JBQStCMEIsSUFBSSx3QkFBd0I7S0FBTTtJQUNuRSxNQUFNQSxHQUFHSSxVQUFVLENBQUMsc0JBQXNCQyxXQUFXLENBQUM7UUFDcERDLGFBQWE7UUFBR0MsUUFBUTtZQUFDO1NBQVc7UUFBRUMsTUFBTTtJQUM5QztJQUNBLE1BQU1SLEdBQUdJLFVBQVUsQ0FBQyx3QkFBd0JDLFdBQVcsQ0FBQztRQUN0REMsYUFBYTtRQUFHQyxRQUFRO1lBQUM7U0FBVztRQUFFQyxNQUFNO0lBQzlDO0lBQ0EsTUFBTUMsS0FBSyxNQUFNVCxHQUFHVSxnQkFBZ0IsQ0FBQztRQUNuQ0MsV0FBVztZQUFDO1lBQXNCO1lBQXdCO1lBQXdCO1NBQVE7UUFDMUZDLE1BQU07WUFBQztTQUFpQjtJQUMxQixHQUFHO1FBQ0RDLGFBQWE7SUFDZjtJQUNBLE1BQU1DLFFBQVEsQ0FBQ0MsUUFBa0NOLEdBQUdPLElBQUksQ0FBQyxJQUFNaEIsR0FBR2MsS0FBSyxDQUFDQztJQUN4RSxJQUFJO1FBQ0YsTUFBTUUsVUFBVSxNQUFNSCxNQUFNNUMsR0FBRyxDQUFDLHdDQUF3QyxFQUFFUSxNQUFNWSxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUU0QixJQUFJLENBQUMsQ0FBQ0MsU0FBV0EsT0FBT0MsSUFBSTtRQUcxSCxJQUFHLENBQUNILFdBQVdBLFFBQVFoQyxLQUFLLEtBQUtQLE1BQU1PLEtBQUssSUFBSWdDLFFBQVFJLEVBQUUsS0FBSzNDLE1BQU1ZLFNBQVMsSUFDbkUsQ0FBQztZQUFDO1lBQWM7WUFBVztTQUFXLENBQUNHLFFBQVEsQ0FBQ3dCLFFBQVFLLEtBQUssS0FDN0RMLFFBQVFNLGFBQWEsS0FBS25ELG1CQUFtQk0sTUFBTWEsU0FBUyxLQUMzRDBCLFFBQVE5QixpQkFBaUIsSUFBSThCLFFBQVE5QixpQkFBaUIsS0FBS1QsTUFBTVMsaUJBQWlCLElBQ25GVCxNQUFNbUIsVUFBVSxHQUFHb0IsUUFBUU8sU0FBUyxHQUFHLFNBQVM5QyxNQUFNbUIsVUFBVSxHQUFHb0IsUUFBUU8sU0FBUyxHQUFJLEtBQUssVUFBVztZQUNqSCxNQUFNLElBQUkxQixNQUFNO1FBQ2xCO1FBQ0EsTUFBTXVCLEtBQUtqRCxtQkFBbUI7WUFBQ00sTUFBTU8sS0FBSztZQUFFUCxNQUFNUSxPQUFPO1NBQUM7UUFDMUQsTUFBTXVDLGNBQWNyRCxtQkFBbUJNO1FBQ3ZDLE1BQU1nRCxRQUFRLE1BQU1aLE1BQU01QyxHQUFJLENBQUMsc0NBQXNDLEVBQUVtRCxHQUFHLENBQUMsQ0FBQyxFQUFFSCxJQUFJLENBQUMsQ0FBQ0MsU0FBV0EsT0FBT0MsSUFBSTtRQVMxRyxJQUFHTSxTQUFTQSxNQUFNRCxXQUFXLEtBQUtBLGFBQWE7WUFDN0MsTUFBTSxJQUFJM0IsTUFBTTtRQUNsQjtRQUNBLE1BQU02QixTQUFTLE1BQU10RCwyQkFBMkJJLFNBQVNDLE1BQU1PLEtBQUssRUFBRSwyQkFBMkJOO1FBQ2pHLElBQUdnRCxPQUFPQyxJQUFJLEtBQUs3QixNQUFNNkIsSUFBSSxJQUFJRCxPQUFPTixFQUFFLEtBQUt0QixNQUFNc0IsRUFBRSxJQUFJTSxPQUFPMUIsWUFBWSxLQUFLRixNQUFNRSxZQUFZLEVBQUU7WUFDckcsTUFBTSxJQUFJSCxNQUFNO1FBQ2xCO1FBQ0EsSUFBRzRCLE9BQU87WUFDUixNQUFNakIsR0FBR29CLE1BQU07WUFDZixPQUFPSCxNQUFNVCxPQUFPO1FBQ3RCO1FBQ0EsTUFBTWEsU0FBUyxNQUFNaEIsTUFBTTVDLEdBQUksQ0FBQyxzREFBc0QsRUFBRVEsTUFBTU8sS0FBSyxDQUFDO3FDQUNuRSxFQUFFUCxNQUFNWSxTQUFTLENBQUMsMkJBQTJCLENBQUMsRUFBRTRCLElBQUksQ0FBQyxDQUFDQyxTQUFXQSxPQUFPaEIsR0FBRztRQUM1RyxJQUFHMkIsT0FBT3RDLE1BQU0sSUFBSSxJQUFJO1lBQ3RCLE1BQU0sSUFBSU0sTUFBTTtRQUNsQjtRQUNBLG1HQUFtRztRQUNuRyxJQUFHO1lBQUM7WUFBVTtTQUFZLENBQUNMLFFBQVEsQ0FBQ2YsTUFBTWdCLElBQUksR0FBRztZQUMvQyxNQUFNb0IsTUFBTTVDLEdBQUksQ0FBQyx3Q0FBd0MsRUFBRStDLFFBQVFjLFFBQVEsQ0FBQzs4RUFDSixFQUFFckQsTUFBTU8sS0FBSyxDQUFDOzBCQUNsRSxFQUFFUCxNQUFNYSxTQUFTLENBQUM7MEZBQzhDLEVBQUViLE1BQU1nQixJQUFJLENBQUM7K0VBQ3hCLENBQUM7UUFDNUU7UUFDQSxNQUFNc0MsU0FBUztZQUNiWDtZQUFJM0IsTUFBTWhCLE1BQU1nQixJQUFJO1lBQUVHLFlBQVluQixNQUFNbUIsVUFBVTtZQUFFUCxXQUFXWixNQUFNWSxTQUFTO1FBQ2hGO1FBQ0EsTUFBTTJDLFdBQVdDLEtBQUtDLEtBQUssQ0FBQyxBQUFDeEQsQ0FBQUEsUUFBUyxLQUFLLFFBQVEsSUFBSztRQUN4RCxNQUFNbUMsTUFBTTVDLEdBQUksQ0FBQyxPQUFPLEVBQUU7WUFDeEJrRSxNQUFNZjtZQUFJcEMsT0FBT1AsTUFBTU8sS0FBSztZQUFFd0M7WUFBYVIsU0FBU2U7WUFBUUM7UUFDOUQsRUFBRSxzQkFBc0IsQ0FBQztRQUN6QixNQUFNbkIsTUFBTTVDLEdBQUksQ0FBQyxPQUFPLEVBQUU7WUFDeEJtRSxPQUFPLENBQUMscUJBQXFCLEVBQUUzRCxNQUFNWSxTQUFTLEVBQUU7WUFBRThDLE1BQU1mO1lBQ3hEaUIsS0FBSyxDQUFDLG1CQUFtQixFQUFFakIsSUFBSTtZQUFFcEMsT0FBT1AsTUFBTU8sS0FBSztZQUFFZ0Q7UUFDdkQsRUFBRSx3QkFBd0IsQ0FBQztRQUMzQixNQUFNbkIsTUFBTTVDLEdBQUksQ0FBQyxPQUFPLEVBQUVRLE1BQU1ZLFNBQVMsQ0FBQztNQUN4QyxFQUFFMkIsUUFBUXNCLGtCQUFrQixJQUFJN0QsTUFBTW1CLFVBQVUsQ0FBQyxxQkFBcUIsRUFBRW5CLE1BQU1TLGlCQUFpQixDQUFDOzRCQUMxRSxDQUFDO1FBQ3pCLE1BQU1zQixHQUFHb0IsTUFBTTtRQUNmLE9BQU9HO0lBQ1QsRUFBRSxPQUFNUSxPQUFPO1FBQ2IsTUFBTS9CLEdBQUdnQyxLQUFLO1FBQ2QsTUFBTUQ7SUFDUjtBQUNGLEVBQUUifQ==
@@ -0,0 +1,114 @@
1
+ import { sesSend } from './email.js';
2
+ import type { ApiContext } from '../types/auth.types.js';
3
+ import type { SupportConversationInput } from './supportConversation.js';
4
+ export interface SupportEmailPreviewInput extends SupportConversationInput {
5
+ body: string;
6
+ expectedConversationRevision: string;
7
+ idempotencyKey: string;
8
+ subject: string;
9
+ }
10
+ export interface SupportEmailReceipt {
11
+ appId: string;
12
+ contentStatus: 'current' | 'redacted';
13
+ conversationRevision: string;
14
+ createdAt: number;
15
+ expiresAt: number;
16
+ id: string;
17
+ maxActions: 1;
18
+ providerAcceptedAt?: number;
19
+ providerMessageId?: string;
20
+ revision: string;
21
+ state: 'pending' | 'approved' | 'rejected' | 'submitting' | 'unknown' | 'accepted';
22
+ ticketId: string;
23
+ ticketRevision: string;
24
+ }
25
+ /** Current exact email target review; no inferred channel binding or inbound provider provenance. */
26
+ export declare const readSupportEmailHandoffTarget: (context: ApiContext, input: SupportConversationInput, now?: () => number) => Promise<{
27
+ appId: string;
28
+ conversationRevision: string;
29
+ recipient: string;
30
+ sender: string;
31
+ ticketId: string;
32
+ ticketRevision: string;
33
+ validUntil: number;
34
+ }>;
35
+ /** Own bounded conversation revision plus a separately consented exact email recipient/body. */
36
+ export declare const previewSupportEmailHandoff: (context: ApiContext, input: SupportEmailPreviewInput, now?: () => number) => Promise<{
37
+ appId: string;
38
+ contentStatus: 'current' | 'redacted';
39
+ conversationRevision: string;
40
+ createdAt: number;
41
+ expiresAt: number;
42
+ id: string;
43
+ maxActions: 1;
44
+ providerAcceptedAt?: number;
45
+ providerMessageId?: string;
46
+ revision: string;
47
+ state: 'pending' | 'approved' | 'rejected' | 'submitting' | 'unknown' | 'accepted';
48
+ ticketId: string;
49
+ ticketRevision: string;
50
+ body?: string;
51
+ recipient?: string;
52
+ sender?: string;
53
+ subject?: string;
54
+ }>;
55
+ /** Inspection redacts private content immediately when any current source/authority fails. */
56
+ export declare const getSupportEmailHandoff: (context: ApiContext, appId: string, id: string, now?: () => number) => Promise<{
57
+ appId: string;
58
+ contentStatus: 'current' | 'redacted';
59
+ conversationRevision: string;
60
+ createdAt: number;
61
+ expiresAt: number;
62
+ id: string;
63
+ maxActions: 1;
64
+ providerAcceptedAt?: number;
65
+ providerMessageId?: string;
66
+ revision: string;
67
+ state: 'pending' | 'approved' | 'rejected' | 'submitting' | 'unknown' | 'accepted';
68
+ ticketId: string;
69
+ ticketRevision: string;
70
+ body?: string;
71
+ recipient?: string;
72
+ sender?: string;
73
+ subject?: string;
74
+ }>;
75
+ /** Separate explicit revision-bound approval/rejection; never granted by an Ads plan. */
76
+ export declare const decideSupportEmailHandoff: (context: ApiContext, appId: string, id: string, revision: string, decision: 'approved' | 'rejected', now?: () => number) => Promise<{
77
+ appId: string;
78
+ contentStatus: 'current' | 'redacted';
79
+ conversationRevision: string;
80
+ createdAt: number;
81
+ expiresAt: number;
82
+ id: string;
83
+ maxActions: 1;
84
+ providerAcceptedAt?: number;
85
+ providerMessageId?: string;
86
+ revision: string;
87
+ state: 'pending' | 'approved' | 'rejected' | 'submitting' | 'unknown' | 'accepted';
88
+ ticketId: string;
89
+ ticketRevision: string;
90
+ body?: string;
91
+ recipient?: string;
92
+ sender?: string;
93
+ subject?: string;
94
+ }>;
95
+ /** Durable claim precedes exactly one SES attempt. Missing/ambiguous acknowledgement is a permanent hold. */
96
+ export declare const executeSupportEmailHandoff: (context: ApiContext, appId: string, id: string, revision: string, now?: () => number, send?: typeof sesSend) => Promise<{
97
+ appId: string;
98
+ contentStatus: 'current' | 'redacted';
99
+ conversationRevision: string;
100
+ createdAt: number;
101
+ expiresAt: number;
102
+ id: string;
103
+ maxActions: 1;
104
+ providerAcceptedAt?: number;
105
+ providerMessageId?: string;
106
+ revision: string;
107
+ state: 'pending' | 'approved' | 'rejected' | 'submitting' | 'unknown' | 'accepted';
108
+ ticketId: string;
109
+ ticketRevision: string;
110
+ body?: string;
111
+ recipient?: string;
112
+ sender?: string;
113
+ subject?: string;
114
+ }>;