@openlup/platform 0.27.0 → 0.29.0

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 (232) hide show
  1. package/CHANGELOG.md +386 -127
  2. package/README.md +251 -9
  3. package/dist/account-support/accountReadFacade.d.ts +8 -0
  4. package/dist/account-support/accountReadFacade.js +10 -0
  5. package/dist/account-support/accountReadModels.d.ts +69 -0
  6. package/dist/account-support/accountReadModels.js +272 -0
  7. package/dist/account-support/bindings.d.ts +23 -0
  8. package/dist/account-support/bindings.js +16 -0
  9. package/dist/account-support/contracts/account.d.ts +281 -0
  10. package/dist/account-support/contracts/account.js +1 -0
  11. package/dist/account-support/contracts/capabilities.d.ts +44 -0
  12. package/dist/account-support/contracts/capabilities.js +1 -0
  13. package/dist/account-support/contracts/commands.d.ts +307 -0
  14. package/dist/account-support/contracts/commands.js +1 -0
  15. package/dist/account-support/contracts/customer360.d.ts +154 -0
  16. package/dist/account-support/contracts/customer360.js +1 -0
  17. package/dist/account-support/contracts/index.d.ts +10 -0
  18. package/dist/account-support/contracts/index.js +1 -0
  19. package/dist/account-support/contracts/invoice.d.ts +79 -0
  20. package/dist/account-support/contracts/invoice.js +1 -0
  21. package/dist/account-support/contracts/operator.d.ts +289 -0
  22. package/dist/account-support/contracts/operator.js +1 -0
  23. package/dist/account-support/contribution.d.ts +24 -0
  24. package/dist/account-support/contribution.js +22 -0
  25. package/dist/account-support/customer360.d.ts +106 -0
  26. package/dist/account-support/customer360.js +163 -0
  27. package/dist/account-support/customerAccountReadDiagnostics.d.ts +11 -0
  28. package/dist/account-support/customerAccountReadDiagnostics.js +71 -0
  29. package/dist/account-support/index.d.ts +26 -0
  30. package/dist/account-support/index.js +15 -0
  31. package/dist/account-support/invoiceAccess.d.ts +57 -0
  32. package/dist/account-support/invoiceAccess.js +40 -0
  33. package/dist/account-support/migrations.d.ts +14 -0
  34. package/dist/account-support/migrations.js +7 -0
  35. package/dist/account-support/operatorAuthority.d.ts +67 -0
  36. package/dist/account-support/operatorAuthority.js +379 -0
  37. package/dist/account-support/operatorRecovery.d.ts +66 -0
  38. package/dist/account-support/operatorRecovery.js +129 -0
  39. package/dist/account-support/orderHistory.d.ts +50 -0
  40. package/dist/account-support/orderHistory.js +188 -0
  41. package/dist/account-support/paymentMethodComposition.d.ts +5 -0
  42. package/dist/account-support/paymentMethodComposition.js +4 -0
  43. package/dist/account-support/postgresOperatorAuthority.d.ts +16 -0
  44. package/dist/account-support/postgresOperatorAuthority.js +93 -0
  45. package/dist/account-support/selfService.d.ts +6 -0
  46. package/dist/account-support/selfService.js +81 -0
  47. package/dist/account-support/subscriptionCommandCodec.d.ts +10 -0
  48. package/dist/account-support/subscriptionCommandCodec.js +38 -0
  49. package/dist/catalog/draftSchemas.d.ts +1 -1
  50. package/dist/catalog/foundationSchemas.d.ts +3 -3
  51. package/dist/catalog/resolvedSkuProjection.d.ts +2 -2
  52. package/dist/catalog/sellableSchemas.d.ts +2 -2
  53. package/dist/commerce/checkoutCommandSchemas.d.ts +2 -2
  54. package/dist/commerce/checkoutResumeSchemas.d.ts +6 -6
  55. package/dist/commerce/checkoutSchemas.d.ts +12 -12
  56. package/dist/commerce/commerceReturnsPort.d.ts +2 -2
  57. package/dist/commerce/configuratorIntentSchemas.d.ts +2 -2
  58. package/dist/commerce/contractPrimitives.d.ts +8 -8
  59. package/dist/commerce/customerDefaultsSnapshotSchemas.d.ts +4 -4
  60. package/dist/commerce/model/omsReadModelHelpers.d.ts +4 -4
  61. package/dist/commerce/offerAvailabilitySchemas.d.ts +1 -1
  62. package/dist/commerce/oms/omsContractBase.d.ts +15 -15
  63. package/dist/commerce/oms/omsContractReadAuxSchemas.d.ts +11 -11
  64. package/dist/commerce/oms/omsContractReadSchemas.d.ts +121 -121
  65. package/dist/commerce/oms/omsContractRequests.d.ts +13 -13
  66. package/dist/commerce/oms/omsControlPlane.d.ts +22 -22
  67. package/dist/commerce/oms/omsFulfillmentContractSchemas.d.ts +12 -12
  68. package/dist/commerce/oms/omsFulfillmentDebugSchemas.d.ts +12 -12
  69. package/dist/commerce/orderReviewContracts.d.ts +33 -33
  70. package/dist/commerce/outboxEventSchemas.d.ts +4 -4
  71. package/dist/commerce/schemas.d.ts +21 -21
  72. package/dist/commerce/server/orderReviewHandlers.d.ts +10 -10
  73. package/dist/communications/consentPurposeRegistry.d.ts +1 -1
  74. package/dist/communications/newsletterIntegrationContracts.d.ts +8 -8
  75. package/dist/composition/accountSupportContribution.d.ts +18 -0
  76. package/dist/composition/accountSupportContribution.js +21 -0
  77. package/dist/composition/index.d.ts +2 -0
  78. package/dist/composition/index.js +2 -0
  79. package/dist/composition/marketingContribution.d.ts +21 -0
  80. package/dist/composition/marketingContribution.js +29 -0
  81. package/dist/fulfillment/handlerSchemas.d.ts +40 -40
  82. package/dist/identity/customerContracts.d.ts +2 -2
  83. package/dist/invoicing/snapshot.d.ts +1 -1
  84. package/dist/marketing/acquisitionCaseSchemas.d.ts +103 -0
  85. package/dist/marketing/acquisitionCaseSchemas.js +53 -0
  86. package/dist/marketing/acquisitionEvidenceSchemas.d.ts +105 -0
  87. package/dist/marketing/acquisitionEvidenceSchemas.js +55 -0
  88. package/dist/marketing/analytics/dataLayer.d.ts +18 -0
  89. package/dist/marketing/analytics/dataLayer.js +111 -0
  90. package/dist/marketing/analytics/noop.d.ts +9 -0
  91. package/dist/marketing/analytics/noop.js +8 -0
  92. package/dist/marketing/analytics/purchaseFacts.d.ts +4 -0
  93. package/dist/marketing/analytics/purchaseFacts.js +69 -0
  94. package/dist/marketing/analytics/routePolicy.d.ts +10 -0
  95. package/dist/marketing/analytics/routePolicy.js +59 -0
  96. package/dist/marketing/consentEvidence.d.ts +8 -0
  97. package/dist/marketing/consentEvidence.js +32 -0
  98. package/dist/marketing/contactLegal.d.ts +14 -0
  99. package/dist/marketing/contactLegal.js +19 -0
  100. package/dist/marketing/contracts/acquisition.d.ts +88 -0
  101. package/dist/marketing/contracts/acquisition.js +1 -0
  102. package/dist/marketing/contracts/acquisitionPorts.d.ts +49 -0
  103. package/dist/marketing/contracts/acquisitionPorts.js +1 -0
  104. package/dist/marketing/contracts/analytics.d.ts +144 -0
  105. package/dist/marketing/contracts/analytics.js +1 -0
  106. package/dist/marketing/contracts/contactLegal.d.ts +53 -0
  107. package/dist/marketing/contracts/contactLegal.js +1 -0
  108. package/dist/marketing/contracts/index.d.ts +6 -0
  109. package/dist/marketing/contracts/index.js +1 -0
  110. package/dist/marketing/contracts/prelaunch.d.ts +39 -0
  111. package/dist/marketing/contracts/prelaunch.js +1 -0
  112. package/dist/marketing/index.d.ts +16 -0
  113. package/dist/marketing/index.js +14 -0
  114. package/dist/marketing/prelaunchAcquisitionSchemas.d.ts +116 -0
  115. package/dist/marketing/prelaunchAcquisitionSchemas.js +51 -0
  116. package/dist/marketing/seo/jsonLd.d.ts +157 -0
  117. package/dist/marketing/seo/jsonLd.js +127 -0
  118. package/dist/marketing/seo/metadata.d.ts +41 -0
  119. package/dist/marketing/seo/metadata.js +36 -0
  120. package/dist/marketing/seo/serializeJsonLd.d.ts +1 -0
  121. package/dist/marketing/seo/serializeJsonLd.js +6 -0
  122. package/dist/marketing/seo/urls.d.ts +14 -0
  123. package/dist/marketing/seo/urls.js +23 -0
  124. package/dist/marketing/server/acquisitionCaseDirect.d.ts +4 -0
  125. package/dist/marketing/server/acquisitionCaseDirect.js +88 -0
  126. package/dist/marketing/server/acquisitionCaseHandlers.d.ts +28 -0
  127. package/dist/marketing/server/acquisitionCaseHandlers.js +40 -0
  128. package/dist/marketing/server/acquisitionCasePorts.d.ts +42 -0
  129. package/dist/marketing/server/acquisitionCasePorts.js +1 -0
  130. package/dist/marketing/server/acquisitionEvidenceDirect.d.ts +5 -0
  131. package/dist/marketing/server/acquisitionEvidenceDirect.js +100 -0
  132. package/dist/marketing/server/analyticsContext.d.ts +3 -0
  133. package/dist/marketing/server/analyticsContext.js +65 -0
  134. package/dist/marketing/server/analyticsContextPorts.d.ts +31 -0
  135. package/dist/marketing/server/analyticsContextPorts.js +1 -0
  136. package/dist/marketing/server/bindings.d.ts +14 -0
  137. package/dist/marketing/server/bindings.js +18 -0
  138. package/dist/marketing/server/directPorts.d.ts +26 -0
  139. package/dist/marketing/server/directPorts.js +1 -0
  140. package/dist/marketing/server/index.d.ts +14 -0
  141. package/dist/marketing/server/index.js +11 -0
  142. package/dist/marketing/server/postgres/acquisitionCaseRoutines.d.ts +10 -0
  143. package/dist/marketing/server/postgres/acquisitionCaseRoutines.js +204 -0
  144. package/dist/marketing/server/postgres/acquisitionEvidenceRoutines.d.ts +10 -0
  145. package/dist/marketing/server/postgres/acquisitionEvidenceRoutines.js +107 -0
  146. package/dist/marketing/server/postgres/index.d.ts +3 -0
  147. package/dist/marketing/server/postgres/index.js +3 -0
  148. package/dist/marketing/server/prelaunchAcquisitionDirect.d.ts +4 -0
  149. package/dist/marketing/server/prelaunchAcquisitionDirect.js +122 -0
  150. package/dist/marketing/server/privacyContributor.d.ts +66 -0
  151. package/dist/marketing/server/privacyContributor.js +87 -0
  152. package/dist/marketing/server/research/adminSurveyResponsesHandler.d.ts +9 -0
  153. package/dist/marketing/server/research/adminSurveyResponsesHandler.js +53 -0
  154. package/dist/marketing/server/research/managedSurveyResponseSubmit.d.ts +39 -0
  155. package/dist/marketing/server/research/managedSurveyResponseSubmit.js +102 -0
  156. package/dist/marketing/server/research/surveyResponseSubmitHandler.d.ts +8 -0
  157. package/dist/marketing/server/research/surveyResponseSubmitHandler.js +41 -0
  158. package/dist/marketing/validation.d.ts +2 -0
  159. package/dist/marketing/validation.js +3 -0
  160. package/dist/pricing/acquisitionQuoteBinding.d.ts +1 -1
  161. package/dist/pricing/catalogFactsSchemas.d.ts +1 -1
  162. package/dist/pricing/quotePromotionEvidence.d.ts +1 -1
  163. package/dist/pricing/quoteSchemas.d.ts +56 -56
  164. package/dist/promotions/adminPromotionCodesContracts.d.ts +30 -30
  165. package/dist/promotions/adminPromotionsContracts.d.ts +14 -14
  166. package/package.json +32 -2
  167. package/release-gates.json +99 -4
  168. package/sql/20261026120000_account_support_operator_authority.sql +693 -0
  169. package/src/account-support/accountReadFacade.ts +15 -0
  170. package/src/account-support/accountReadModels.ts +371 -0
  171. package/src/account-support/bindings.ts +28 -0
  172. package/src/account-support/contracts/account.ts +306 -0
  173. package/src/account-support/contracts/capabilities.ts +30 -0
  174. package/src/account-support/contracts/commands.ts +243 -0
  175. package/src/account-support/contracts/customer360.ts +150 -0
  176. package/src/account-support/contracts/index.ts +141 -0
  177. package/src/account-support/contracts/invoice.ts +44 -0
  178. package/src/account-support/contracts/operator.ts +123 -0
  179. package/src/account-support/contribution.ts +35 -0
  180. package/src/account-support/customer360.ts +268 -0
  181. package/src/account-support/customerAccountReadDiagnostics.ts +83 -0
  182. package/src/account-support/index.ts +72 -0
  183. package/src/account-support/invoiceAccess.ts +63 -0
  184. package/src/account-support/migrations.ts +7 -0
  185. package/src/account-support/operatorAuthority.ts +393 -0
  186. package/src/account-support/operatorRecovery.ts +136 -0
  187. package/src/account-support/orderHistory.ts +309 -0
  188. package/src/account-support/paymentMethodComposition.ts +8 -0
  189. package/src/account-support/postgresOperatorAuthority.ts +92 -0
  190. package/src/account-support/selfService.ts +83 -0
  191. package/src/account-support/subscriptionCommandCodec.ts +48 -0
  192. package/src/composition/accountSupportContribution.ts +28 -0
  193. package/src/composition/index.ts +2 -0
  194. package/src/composition/marketingContribution.ts +37 -0
  195. package/src/marketing/acquisitionCaseSchemas.ts +75 -0
  196. package/src/marketing/acquisitionEvidenceSchemas.ts +72 -0
  197. package/src/marketing/analytics/dataLayer.ts +100 -0
  198. package/src/marketing/analytics/noop.ts +14 -0
  199. package/src/marketing/analytics/purchaseFacts.ts +63 -0
  200. package/src/marketing/analytics/routePolicy.ts +55 -0
  201. package/src/marketing/consentEvidence.ts +36 -0
  202. package/src/marketing/contactLegal.ts +22 -0
  203. package/src/marketing/contracts/acquisition.ts +50 -0
  204. package/src/marketing/contracts/acquisitionPorts.ts +48 -0
  205. package/src/marketing/contracts/analytics.ts +88 -0
  206. package/src/marketing/contracts/contactLegal.ts +41 -0
  207. package/src/marketing/contracts/index.ts +17 -0
  208. package/src/marketing/contracts/prelaunch.ts +15 -0
  209. package/src/marketing/index.ts +16 -0
  210. package/src/marketing/prelaunchAcquisitionSchemas.ts +64 -0
  211. package/src/marketing/seo/jsonLd.ts +153 -0
  212. package/src/marketing/seo/metadata.ts +62 -0
  213. package/src/marketing/seo/serializeJsonLd.ts +6 -0
  214. package/src/marketing/seo/urls.ts +34 -0
  215. package/src/marketing/server/acquisitionCaseDirect.ts +67 -0
  216. package/src/marketing/server/acquisitionCaseHandlers.ts +57 -0
  217. package/src/marketing/server/acquisitionCasePorts.ts +42 -0
  218. package/src/marketing/server/acquisitionEvidenceDirect.ts +76 -0
  219. package/src/marketing/server/analyticsContext.ts +55 -0
  220. package/src/marketing/server/analyticsContextPorts.ts +28 -0
  221. package/src/marketing/server/bindings.ts +27 -0
  222. package/src/marketing/server/directPorts.ts +27 -0
  223. package/src/marketing/server/index.ts +14 -0
  224. package/src/marketing/server/postgres/acquisitionCaseRoutines.ts +218 -0
  225. package/src/marketing/server/postgres/acquisitionEvidenceRoutines.ts +103 -0
  226. package/src/marketing/server/postgres/index.ts +3 -0
  227. package/src/marketing/server/prelaunchAcquisitionDirect.ts +121 -0
  228. package/src/marketing/server/privacyContributor.ts +123 -0
  229. package/src/marketing/server/research/adminSurveyResponsesHandler.ts +78 -0
  230. package/src/marketing/server/research/managedSurveyResponseSubmit.ts +158 -0
  231. package/src/marketing/server/research/surveyResponseSubmitHandler.ts +57 -0
  232. package/src/marketing/validation.ts +3 -0
@@ -0,0 +1,289 @@
1
+ import type { OperatorContext } from "./customer360.js";
2
+ export type OperatorSubscriptionAction = "pause" | "resume" | "slide_next_cycle" | "change_shipping_address";
3
+ export interface OperatorNewShippingAddress {
4
+ recipientName: string;
5
+ line1: string;
6
+ line2?: string;
7
+ postalCode: string;
8
+ city: string;
9
+ country: string;
10
+ contactPhone?: string;
11
+ }
12
+ export type OperatorShippingAddressPayload = {
13
+ shippingAddressId: string;
14
+ reason?: string;
15
+ } | {
16
+ newAddress: OperatorNewShippingAddress;
17
+ reason?: string;
18
+ };
19
+ export type OperatorSubscriptionPayload = {
20
+ pausePreset?: "2_weeks" | "1_month" | "indefinite";
21
+ reason?: string;
22
+ newNextCycleAt?: string;
23
+ slideMinDays?: number;
24
+ confirmedChargeTiming?: boolean;
25
+ } | OperatorShippingAddressPayload;
26
+ export type OperatorSubscriptionRefusal = "subscription_not_found" | "subject_not_found" | "subject_account_unlinked" | "version_conflict" | "slide_target_invalid" | "slide_not_available" | "slide_out_of_window" | "payment_blocked" | "invalid_transition" | "charge_timing_not_confirmed" | "payment_method_not_chargeable" | "invalid_address";
27
+ export type RecoveryRefusal = "subject_not_found" | "case_not_found" | "lifecycle_not_eligible" | "dunning_authority_unavailable" | "case_not_open" | "recoverable_order_unavailable";
28
+ export type EmailCorrectionRefusal = "subject_not_found" | "subject_account_linked" | "email_expectation_conflict" | "email_already_in_use";
29
+ export type PhoneCorrectionRefusal = "subject_not_found" | "phone_expectation_conflict";
30
+ export type LeadRefusal = "lead_not_found" | "customer_not_found" | "lead_email_expectation_conflict" | "lead_has_identity" | "lead_has_commercial_footprint" | "unclassified_referencing_table";
31
+ export type OperatorContactCommand = {
32
+ action: "correct_email";
33
+ subjectId: string;
34
+ expectedEmail: string;
35
+ newEmail: string;
36
+ idempotencyKey: string;
37
+ } | {
38
+ action: "correct_phone";
39
+ subjectId: string;
40
+ expectedPhone: string;
41
+ newPhone: string;
42
+ idempotencyKey: string;
43
+ };
44
+ export interface OperatorLeadCommand {
45
+ action: "absorb_lead";
46
+ subjectId: string;
47
+ leadId: string;
48
+ expectedLeadEmail: string;
49
+ idempotencyKey: string;
50
+ }
51
+ export type OperatorRecoveryCommand = {
52
+ action: "issue_recovery";
53
+ subjectId: string;
54
+ caseId: string;
55
+ idempotencyKey: string;
56
+ } | {
57
+ action: "apply_subscription_action";
58
+ subscriptionId: string;
59
+ subscriptionAction: OperatorSubscriptionAction;
60
+ expectedVersion: number;
61
+ idempotencyKey: string;
62
+ payload?: OperatorSubscriptionPayload;
63
+ } | OperatorContactCommand | OperatorLeadCommand;
64
+ /** Carries trusted context and the original command; performs no authorization or writes.
65
+ * The host preserves the command key, optional reason and expected version.
66
+ */
67
+ export interface OperatorRecoveryCommandEnvelope<Command = OperatorRecoveryCommand> {
68
+ actor: OperatorContext;
69
+ subject: {
70
+ subjectId: string;
71
+ subscriptionId?: string;
72
+ orderId?: string;
73
+ caseId?: string;
74
+ };
75
+ command: Command;
76
+ reason: string | null;
77
+ idempotencyKey: string;
78
+ expectedVersion: number | null;
79
+ evidence: readonly {
80
+ owner: string;
81
+ resourceId: string;
82
+ revision: string | null;
83
+ }[];
84
+ }
85
+ export type OperatorSubscriptionResult = {
86
+ outcome: "applied" | "noop" | "replayed";
87
+ subscriptionStatus: string;
88
+ nextCycleAt: string | null;
89
+ templateVersion: number;
90
+ eventId: string | null;
91
+ appliedBy: "operator_reschedule_band" | "customer_self_service_delegate" | null;
92
+ } | {
93
+ outcome: "refused" | "conflict";
94
+ refusalCode: OperatorSubscriptionRefusal;
95
+ };
96
+ /** Actual M10 receipt evidence; support never manufactures an audit identifier. */
97
+ export interface OperatorOwnerReceipt {
98
+ id: string;
99
+ actorId: string;
100
+ idempotencyKey: string;
101
+ commandFingerprint: string;
102
+ result: Record<string, unknown>;
103
+ }
104
+ /** Additive native superset. The managed transcription above remains unchanged. */
105
+ export type NativeOperatorSubscriptionResult = (OperatorSubscriptionResult & {
106
+ ownerReceipt?: OperatorOwnerReceipt;
107
+ }) | {
108
+ outcome: "refused";
109
+ refusalCode: "operator_subscription_authority_unavailable";
110
+ ownerReceipt?: OperatorOwnerReceipt;
111
+ };
112
+ export type OperatorAuthorityUnavailable = {
113
+ outcome: "refused";
114
+ refusalCode: "operator_subscription_authority_unavailable";
115
+ };
116
+ export type OperatorEmailCorrectionResult = {
117
+ outcome: "applied" | "noop" | "replayed";
118
+ authUserLinked: boolean;
119
+ } | {
120
+ outcome: "refused" | "conflict";
121
+ refusalCode: EmailCorrectionRefusal;
122
+ };
123
+ export type OperatorPhoneCorrectionResult = {
124
+ outcome: "applied" | "noop" | "replayed";
125
+ } | {
126
+ outcome: "refused" | "conflict";
127
+ refusalCode: PhoneCorrectionRefusal;
128
+ };
129
+ export type CustomerRecoveryAuthorityResult = {
130
+ outcome: "issued";
131
+ deliveryStatus: string;
132
+ auditEventId: string;
133
+ } | {
134
+ outcome: "replayed";
135
+ deliveryStatus: string;
136
+ auditEventId: string;
137
+ } | {
138
+ outcome: "refused";
139
+ refusalCode: RecoveryRefusal;
140
+ } | {
141
+ outcome: "conflict";
142
+ conflictCode: "idempotency_conflict";
143
+ };
144
+ export interface LeadAbsorptionCarried {
145
+ personalization: number;
146
+ consents: number;
147
+ sourceLinks: number;
148
+ deliveries: number;
149
+ }
150
+ export type OperatorLeadAbsorptionResult = {
151
+ outcome: "applied" | "noop" | "replayed";
152
+ leadId: string;
153
+ customerId: string;
154
+ carried: LeadAbsorptionCarried;
155
+ } | {
156
+ outcome: "refused" | "conflict";
157
+ leadId: string;
158
+ refusalCode: LeadRefusal;
159
+ blockingTables: string[];
160
+ };
161
+ export interface OperatorEmailCorrectionClaim {
162
+ idempotencyKey: string;
163
+ claimToken: string;
164
+ sequence: string;
165
+ }
166
+ export interface OperatorIdentityMovePort {
167
+ findClientHoldingEmail(email: string): Promise<{
168
+ clientId: string;
169
+ } | null>;
170
+ readIdentityEmail(authUserId: string): Promise<string>;
171
+ moveIdentityEmail(authUserId: string, email: string, claim: OperatorEmailCorrectionClaim): Promise<void>;
172
+ }
173
+ export type OperatorEmailCorrectionAdmission = OperatorEmailCorrectionResult | {
174
+ outcome: "admitted";
175
+ authUserId: string | null;
176
+ originalEmail: string;
177
+ newEmail: string;
178
+ };
179
+ export type OperatorEmailCorrectionPhase = "complete" | "release" | "fail";
180
+ export type OperatorEmailCorrectionSettlement = OperatorEmailCorrectionResult | {
181
+ outcome: "pending";
182
+ } | {
183
+ outcome: "failed";
184
+ };
185
+ export type OperatorEmailCorrectionExecution = OperatorEmailCorrectionSettlement | (OperatorEmailCorrectionClaim & {
186
+ outcome: "claimed";
187
+ authUserId: string | null;
188
+ originalEmail: string;
189
+ newEmail: string;
190
+ });
191
+ export type OperatorAccountLinkRefusal = "subject_not_found" | "subject_not_customer" | "subject_email_missing" | "email_expectation_conflict" | "subject_account_linked" | "identity_not_linkable" | "identity_linked_elsewhere";
192
+ export type OperatorAccountLinkResult = {
193
+ outcome: "applied" | "noop" | "replayed";
194
+ } | {
195
+ outcome: "refused" | "conflict";
196
+ refusalCode: OperatorAccountLinkRefusal;
197
+ };
198
+ export interface OperatorAccountLinkRequest {
199
+ action: "link_account";
200
+ subjectId: string;
201
+ expectedEmail: string;
202
+ idempotencyKey: string;
203
+ }
204
+ export interface OperatorAccountLinkIdentityPort {
205
+ findSubject(clientId: string): Promise<{
206
+ identityKind: string | null;
207
+ email: string | null;
208
+ authUserId: string | null;
209
+ } | null>;
210
+ resolveIdentityForEmail(email: string): Promise<string>;
211
+ }
212
+ export interface OperatorSubscriptionInput {
213
+ subscriptionId: string;
214
+ subscriptionAction: OperatorSubscriptionAction;
215
+ expectedVersion: number;
216
+ payload: OperatorSubscriptionPayload;
217
+ idempotencyKey: string;
218
+ operatorId: string;
219
+ }
220
+ export interface OperatorEmailInput {
221
+ subjectId: string;
222
+ expectedEmail: string;
223
+ newEmail: string;
224
+ idempotencyKey: string;
225
+ operatorId: string;
226
+ }
227
+ export interface OperatorPhoneInput {
228
+ subjectId: string;
229
+ expectedPhone: string;
230
+ newPhone: string;
231
+ idempotencyKey: string;
232
+ operatorId: string;
233
+ }
234
+ export interface OperatorLinkInput {
235
+ subjectId: string;
236
+ expectedEmail: string;
237
+ authUserId: string | null;
238
+ idempotencyKey: string;
239
+ operatorId: string;
240
+ }
241
+ export interface OperatorRecoveryInput {
242
+ subjectId: string;
243
+ caseId: string;
244
+ idempotencyKey: string;
245
+ operatorId: string;
246
+ }
247
+ export interface OperatorAbsorptionInput {
248
+ customerId: string;
249
+ leadId: string;
250
+ expectedLeadEmail: string;
251
+ idempotencyKey: string;
252
+ operatorId: string;
253
+ }
254
+ export interface ManagedOperatorAuthorityPort {
255
+ applySubscriptionAction(input: OperatorSubscriptionInput): Promise<OperatorSubscriptionResult>;
256
+ correctSubjectEmail(input: OperatorEmailInput): Promise<OperatorEmailCorrectionAdmission>;
257
+ claimSubjectEmail(input: {
258
+ idempotencyKey: string;
259
+ }): Promise<OperatorEmailCorrectionExecution>;
260
+ settleSubjectEmail(input: {
261
+ idempotencyKey: string;
262
+ claimToken: string;
263
+ phase: OperatorEmailCorrectionPhase;
264
+ }): Promise<OperatorEmailCorrectionSettlement>;
265
+ correctSubjectPhone(input: OperatorPhoneInput): Promise<OperatorPhoneCorrectionResult>;
266
+ linkSubjectAccount(input: OperatorLinkInput): Promise<OperatorAccountLinkResult>;
267
+ issueRecovery(input: OperatorRecoveryInput): Promise<CustomerRecoveryAuthorityResult>;
268
+ absorbLead(input: OperatorAbsorptionInput): Promise<OperatorLeadAbsorptionResult>;
269
+ }
270
+ export interface NativeOperatorAuthorityPort {
271
+ applySubscriptionAction(input: OperatorSubscriptionInput): Promise<NativeOperatorSubscriptionResult>;
272
+ correctSubjectEmail(input: OperatorEmailInput): Promise<OperatorAuthorityUnavailable>;
273
+ correctSubjectPhone(input: OperatorPhoneInput): Promise<OperatorAuthorityUnavailable>;
274
+ linkSubjectAccount(input: OperatorLinkInput): Promise<OperatorAuthorityUnavailable>;
275
+ issueRecovery(input: OperatorRecoveryInput): Promise<CustomerRecoveryAuthorityResult>;
276
+ absorbLead(input: OperatorAbsorptionInput): Promise<OperatorLeadAbsorptionResult>;
277
+ }
278
+ export type LinkThenSubscriptionResult = {
279
+ outcome: "link_refused";
280
+ link: OperatorAccountLinkResult | OperatorAuthorityUnavailable;
281
+ } | {
282
+ outcome: "completed";
283
+ link: OperatorAccountLinkResult;
284
+ subscription: NativeOperatorSubscriptionResult;
285
+ } | {
286
+ outcome: "partial";
287
+ link: OperatorAccountLinkResult;
288
+ subscription: NativeOperatorSubscriptionResult | null;
289
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { AccountSupportCapabilities } from "./contracts/index.js";
2
+ import type { AccountSupportCallable, AccountSupportOperations } from "./bindings.js";
3
+ export type AccountSupportBindingRefusal = {
4
+ readonly kind: "refused";
5
+ readonly code: "disabled" | "schema_unknown" | "schema_unsatisfied";
6
+ };
7
+ export type AccountSupportReadiness = {
8
+ readonly kind: "ready";
9
+ } | AccountSupportBindingRefusal;
10
+ export type GuardedAccountSupportOperation<F> = F extends AccountSupportCallable ? (...args: Parameters<F>) => Promise<Awaited<ReturnType<F>> | AccountSupportBindingRefusal> : null;
11
+ export type GuardedAccountSupportOperations<O extends AccountSupportOperations> = {
12
+ readonly [K in keyof O]: GuardedAccountSupportOperation<O[K]>;
13
+ };
14
+ /** One guard per callable identity, shared by service, port and route aliases. */
15
+ export declare function createAccountSupportServices<O extends AccountSupportOperations>(input: {
16
+ enabled?: boolean;
17
+ capabilities: AccountSupportCapabilities;
18
+ operations: O;
19
+ readiness(): Promise<AccountSupportReadiness>;
20
+ }): Readonly<{
21
+ capabilities: AccountSupportCapabilities;
22
+ readiness: () => Promise<AccountSupportReadiness>;
23
+ operations: Readonly<GuardedAccountSupportOperations<O>>;
24
+ }>;
@@ -0,0 +1,22 @@
1
+ /** One guard per callable identity, shared by service, port and route aliases. */
2
+ export function createAccountSupportServices(input) {
3
+ const enabled = input.enabled === true, observe = input.readiness;
4
+ async function readiness() {
5
+ return enabled ? observe() : { kind: "refused", code: "disabled" };
6
+ }
7
+ const guards = new Map();
8
+ const operations = Object.fromEntries(Object.entries(input.operations).map(([key, operation]) => {
9
+ if (operation === null)
10
+ return [key, null];
11
+ let guarded = guards.get(operation);
12
+ if (!guarded) {
13
+ guarded = async (...args) => {
14
+ const state = await readiness();
15
+ return state.kind === "ready" ? operation(...args) : state;
16
+ };
17
+ guards.set(operation, guarded);
18
+ }
19
+ return [key, guarded];
20
+ }));
21
+ return Object.freeze({ capabilities: input.capabilities, readiness, operations: Object.freeze(operations) });
22
+ }
@@ -0,0 +1,106 @@
1
+ import type { Customer360Snapshot, Customer360Subject, Customer360ContactHealth, Customer360ReadPort, Customer360Lookup, Customer360SummaryReadPort, DiagnosticHistoryReadPort } from "./contracts/customer360.js";
2
+ export declare const SUPPORT_CUSTOMER_360_CONTRACT_VERSION: "support.customer_360.v2";
3
+ type Row = Record<string, unknown>;
4
+ export type CustomerJourneyAuditEvent = {
5
+ eventId: string;
6
+ occurredAt: string;
7
+ action: string;
8
+ outcome: string;
9
+ entity: {
10
+ kind: "subject" | "order" | "subscription" | "dunning_case";
11
+ id: string;
12
+ };
13
+ };
14
+ export interface ContactHealthDelivery {
15
+ readonly status: string;
16
+ readonly occurredAt: string | null;
17
+ readonly permanentBounce: boolean | null;
18
+ }
19
+ export interface Customer360ProjectionInput<Extension = unknown> {
20
+ subject: {
21
+ subjectId: string;
22
+ displayName: string | null;
23
+ email: string | null;
24
+ phone?: string | null;
25
+ lifecycleStage: Customer360Subject["lifecycleStage"];
26
+ createdAt: string | null;
27
+ lastActivityAt: string | null;
28
+ };
29
+ legacy: {
30
+ lookup: {
31
+ warnings: string[];
32
+ };
33
+ customer: {
34
+ authUserLinked: boolean;
35
+ };
36
+ orders: {
37
+ orderId: string;
38
+ orderNumber: string | null;
39
+ status: string | null;
40
+ omsProjection: Row;
41
+ }[];
42
+ subscriptions: {
43
+ subscriptionId: string;
44
+ status: string | null;
45
+ nextCycleAt: string | null;
46
+ templateVersion?: number | null;
47
+ paymentMethodStatus?: string | null;
48
+ renewalOrders: Row[];
49
+ }[];
50
+ payment: {
51
+ status: string | null;
52
+ intents: Row[];
53
+ };
54
+ timeline: Row[];
55
+ } | null;
56
+ cases: Row[];
57
+ notices: Row[];
58
+ tokens: Row[];
59
+ recoveryAudit: Row[];
60
+ orderTruth: Row[];
61
+ now: number;
62
+ contactHealth?: Customer360ContactHealth | null;
63
+ signedIn?: boolean;
64
+ addresses?: Customer360Snapshot["addresses"] | null;
65
+ subscriptionPointers?: Map<string, {
66
+ shippingAddressId: string | null;
67
+ paymentMethodKind: string | null;
68
+ }> | null;
69
+ /** false means failed/unavailable; null means no enrichment selected. */
70
+ profile?: {
71
+ extension: Extension;
72
+ partial: boolean;
73
+ } | false | null;
74
+ }
75
+ export interface Customer360ProjectionPolicy {
76
+ lifecycle(subject: Customer360ProjectionInput["subject"]): Customer360Subject["lifecycle"];
77
+ /** Stable audit identity hashing remains in the server host. */
78
+ timelineAudit(event: Row, subjectId: string): CustomerJourneyAuditEvent[];
79
+ }
80
+ export declare function projectCustomer360Snapshot<Extension>(input: Customer360ProjectionInput<Extension>, policy: Customer360ProjectionPolicy): Customer360Snapshot<Extension>;
81
+ export declare function boundedCustomerJourneyAudit(events: CustomerJourneyAuditEvent[]): {
82
+ events: CustomerJourneyAuditEvent[];
83
+ windowed: boolean;
84
+ };
85
+ export declare function deriveContactHealth(deliveries: readonly ContactHealthDelivery[]): Customer360ContactHealth;
86
+ /** Authorize before every source read; retain host defaults, ambiguity, null and failure semantics. */
87
+ export declare function createCustomer360ReadFacade<Search, Snapshot, SummaryRequest, SummaryResult, DiagnosticRequest, DiagnosticResult>(ports: {
88
+ authorize(): Promise<boolean>;
89
+ journey: Customer360ReadPort<Search, Snapshot>;
90
+ summary: Customer360SummaryReadPort<SummaryRequest, SummaryResult>;
91
+ diagnostics: DiagnosticHistoryReadPort<DiagnosticRequest, DiagnosticResult>;
92
+ }): {
93
+ searchCustomerJourney: (request: Customer360Lookup) => Promise<Search | {
94
+ kind: "denied";
95
+ }>;
96
+ getCustomerJourney: (request: Customer360Lookup) => Promise<Snapshot | {
97
+ kind: "denied";
98
+ } | null>;
99
+ getPortableSummary: (request: SummaryRequest) => Promise<SummaryResult | {
100
+ kind: "denied";
101
+ }>;
102
+ history: (request: DiagnosticRequest) => Promise<DiagnosticResult | {
103
+ kind: "denied";
104
+ }>;
105
+ };
106
+ export {};
@@ -0,0 +1,163 @@
1
+ export const SUPPORT_CUSTOMER_360_CONTRACT_VERSION = "support.customer_360.v2";
2
+ export function projectCustomer360Snapshot(input, policy) {
3
+ const { subject, legacy, cases, notices, tokens, recoveryAudit, orderTruth, now } = input;
4
+ const orders = legacy?.orders.map(order => ({
5
+ orderId: order.orderId,
6
+ orderNumber: order.orderNumber,
7
+ status: order.status ?? "unknown",
8
+ subscriptionId: string(order.omsProjection.subscriptionId),
9
+ totalMinor: integer(order.omsProjection.totalMinor),
10
+ currency: string(order.omsProjection.currency),
11
+ createdAt: date(order.omsProjection.createdAt)
12
+ })) ?? [];
13
+ const pending = (row) => orderTruth.some(order => order.id === row.order_id && order.client_id === row.client_id && order.status === "pending_payment");
14
+ const eligible = (row) => subject.lifecycleStage === "customer" && row.status === "open" && pending(row);
15
+ const audit = boundedCustomerJourneyAudit([
16
+ ...(legacy?.timeline ?? []).flatMap(event => policy.timelineAudit(event, subject.subjectId)),
17
+ ...recoveryAudit.flatMap(recoveryEvent)
18
+ ]);
19
+ const profile = input.profile;
20
+ const warnings = [...new Set([
21
+ ...(legacy?.lookup.warnings ?? []),
22
+ ...(audit.windowed ? ["evidence_window_limited:customer_360_audit_trail"] : []),
23
+ ...(recoveryAudit.length === 80 ? ["evidence_window_limited:customer_recovery_audit"] : []),
24
+ ...(profile === false ? ["evidence_unavailable:customer_profile"] : profile?.partial ? ["evidence_partial:customer_profile"] : [])
25
+ ])];
26
+ return {
27
+ contractVersion: SUPPORT_CUSTOMER_360_CONTRACT_VERSION,
28
+ lookup: { query: subject.subjectId, matchedBy: "subject_id", confidence: "exact", warnings },
29
+ ...(input.contactHealth ? { contactHealth: input.contactHealth } : {}),
30
+ ...(input.addresses ? { addresses: input.addresses } : {}),
31
+ ...(profile ? { extension: profile.extension } : {}),
32
+ subject: {
33
+ subjectId: subject.subjectId,
34
+ displayName: subject.displayName,
35
+ email: subject.email,
36
+ phone: subject.phone ?? null,
37
+ lifecycleStage: subject.lifecycleStage,
38
+ lifecycle: policy.lifecycle(subject),
39
+ firstSeenAt: subject.createdAt,
40
+ lastActivityAt: latest([...(legacy?.timeline ?? []), ...cases, ...recoveryAudit]) ?? subject.lastActivityAt,
41
+ ...(legacy ? { authUserLinked: legacy.customer.authUserLinked } : {}),
42
+ ...(input.signedIn === undefined ? {} : { hasSignedIn: input.signedIn })
43
+ },
44
+ orders,
45
+ subscriptions: legacy?.subscriptions.map(value => ({
46
+ subscriptionId: value.subscriptionId,
47
+ status: value.status ?? "unknown",
48
+ nextCycleAt: value.nextCycleAt,
49
+ latestOrderId: string(value.renewalOrders[0]?.orderId),
50
+ ...(typeof value.templateVersion === "number" ? { templateVersion: value.templateVersion } : {}),
51
+ ...(input.subscriptionPointers
52
+ ? {
53
+ shippingAddressId: input.subscriptionPointers.get(value.subscriptionId)?.shippingAddressId ?? null,
54
+ paymentMethodKind: input.subscriptionPointers.get(value.subscriptionId)?.paymentMethodKind ?? null
55
+ }
56
+ : {}),
57
+ paymentMethodStatus: string(value.paymentMethodStatus)
58
+ })) ?? [],
59
+ payment: orders.map(order => {
60
+ const intent = legacy?.payment.intents.find(value => string(value.orderId) === order.orderId);
61
+ return {
62
+ orderId: order.orderId,
63
+ status: string(intent?.status) ?? legacy?.payment.status ?? "unknown",
64
+ recoverable: cases.some(row => row.order_id === order.orderId && eligible(row)),
65
+ amountMinor: order.totalMinor,
66
+ currency: order.currency,
67
+ lastAttemptAt: date(intent?.updatedAt)
68
+ };
69
+ }),
70
+ dunningCases: cases.map(row => ({
71
+ caseId: string(row.id),
72
+ subscriptionId: string(row.subscription_id),
73
+ orderId: string(row.order_id),
74
+ status: string(row.status) ?? "unknown",
75
+ retryAttempt: integer(row.retry_attempt) ?? 0,
76
+ nextRetryAt: date(row.next_retry_at),
77
+ notificationStatus: string(latestNotice(notices, string(row.id))?.status),
78
+ recoveryAvailable: eligible(row)
79
+ })),
80
+ recovery: cases.map(row => {
81
+ const id = string(row.id), related = tokens.filter(value => value.case_id === id);
82
+ return {
83
+ caseId: id,
84
+ status: pending(row) && related.some(value => {
85
+ const expiry = date(value.expires_at);
86
+ return Boolean(expiry && Date.parse(expiry) > now && !value.used_at && !value.revoked_at);
87
+ }) ? "available" : "unavailable",
88
+ actionAvailable: eligible(row),
89
+ lastIssuedAt: related.map(value => date(value.created_at)).filter(value => value !== null).sort().at(-1) ?? null,
90
+ lastDeliveryStatus: string(latestNotice(notices, id, "payment_recovery")?.status)
91
+ };
92
+ }),
93
+ auditTrail: audit.events
94
+ };
95
+ }
96
+ export function boundedCustomerJourneyAudit(events) {
97
+ const instant = (value) => Number.isFinite(Date.parse(value)) ? Date.parse(value) : 0;
98
+ const newest = [...events].sort((a, b) => instant(b.occurredAt) - instant(a.occurredAt) || a.eventId.localeCompare(b.eventId));
99
+ return {
100
+ events: newest.slice(0, 80).sort((a, b) => instant(a.occurredAt) - instant(b.occurredAt) || a.eventId.localeCompare(b.eventId)),
101
+ windowed: newest.length > 80
102
+ };
103
+ }
104
+ export function deriveContactHealth(deliveries) {
105
+ const terminal = ["delivered", "bounced", "complained", "failed"];
106
+ const newest = deliveries.filter((value) => terminal.includes(value.status) && typeof value.occurredAt === "string")
107
+ .reduce((best, value) => best === null || value.occurredAt > best.occurredAt ? value : best, null);
108
+ if (!newest)
109
+ return { lastTerminalStatus: null, lastTerminalAt: null, reachable: true };
110
+ return {
111
+ lastTerminalStatus: newest.status,
112
+ lastTerminalAt: newest.occurredAt,
113
+ reachable: newest.status === "delivered" || (newest.status === "bounced" && newest.permanentBounce === false)
114
+ };
115
+ }
116
+ /** Authorize before every source read; retain host defaults, ambiguity, null and failure semantics. */
117
+ export function createCustomer360ReadFacade(ports) {
118
+ async function read(query) {
119
+ if (!await ports.authorize())
120
+ return { kind: "denied" };
121
+ return query();
122
+ }
123
+ return {
124
+ searchCustomerJourney: (request) => read(() => ports.journey.searchCustomerJourney(request)),
125
+ getCustomerJourney: (request) => read(() => ports.journey.getCustomerJourney(request)),
126
+ getPortableSummary: (request) => read(() => ports.summary.getPortableSummary(request)),
127
+ history: (request) => read(() => ports.diagnostics.history(request))
128
+ };
129
+ }
130
+ function recoveryEvent(row) {
131
+ const eventId = string(row.id), occurredAt = date(row.occurred_at), id = string(row.case_id), outcome = string(row.outcome);
132
+ if (!eventId || !occurredAt || !id || !outcome)
133
+ return [];
134
+ const refusal = string(row.refusal_code);
135
+ return [{
136
+ eventId,
137
+ occurredAt,
138
+ action: "recovery.issue",
139
+ outcome: refusal ? `${outcome}:${refusal}` : outcome,
140
+ entity: { kind: "dunning_case", id }
141
+ }];
142
+ }
143
+ function latestNotice(rows, id, kind) {
144
+ return rows.filter(row => row.case_id === id && row.recipient_kind === "customer" && (!kind || row.notification_kind === kind))
145
+ .sort((a, b) => `${string(b.created_at) ?? ""}:${string(b.id) ?? ""}`.localeCompare(`${string(a.created_at) ?? ""}:${string(a.id) ?? ""}`))[0];
146
+ }
147
+ function string(value) {
148
+ return typeof value === "string" && value.trim() ? value.trim() : null;
149
+ }
150
+ function integer(value) {
151
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null;
152
+ }
153
+ function date(value) {
154
+ const text = string(value);
155
+ return text && !Number.isNaN(Date.parse(text)) ? text : null;
156
+ }
157
+ function latest(rows) {
158
+ return rows.map(row => date(row.updated_at ?? row.updatedAt ?? row.created_at ?? row.createdAt ?? row.occurred_at ?? row.occurredAt))
159
+ .filter(value => value !== null)
160
+ .sort((a, b) => Date.parse(a) - Date.parse(b))
161
+ .at(-1)
162
+ ?? null;
163
+ }
@@ -0,0 +1,11 @@
1
+ export type CustomerAccountReadStage = "account_aggregate";
2
+ type DiagnosticRecord = Record<string, unknown>;
3
+ export declare class CustomerAccountReadModelError extends Error {
4
+ readonly stage: CustomerAccountReadStage;
5
+ readonly cause: unknown;
6
+ constructor(stage: CustomerAccountReadStage, cause: unknown);
7
+ }
8
+ export declare function withCustomerAccountReadStage<T>(stage: CustomerAccountReadStage, operation: () => Promise<T>): Promise<T>;
9
+ export declare function customerAccountReadFailureDetails(error: unknown): DiagnosticRecord;
10
+ export declare function logCustomerAccountReadFailure(error: unknown): void;
11
+ export {};