@openlup/platform 0.27.0 → 0.28.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 (115) hide show
  1. package/CHANGELOG.md +167 -109
  2. package/README.md +111 -8
  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 +1 -0
  78. package/dist/composition/index.js +1 -0
  79. package/dist/fulfillment/handlerSchemas.d.ts +40 -40
  80. package/dist/identity/customerContracts.d.ts +2 -2
  81. package/dist/invoicing/snapshot.d.ts +1 -1
  82. package/dist/pricing/acquisitionQuoteBinding.d.ts +1 -1
  83. package/dist/pricing/catalogFactsSchemas.d.ts +1 -1
  84. package/dist/pricing/quotePromotionEvidence.d.ts +1 -1
  85. package/dist/pricing/quoteSchemas.d.ts +56 -56
  86. package/dist/promotions/adminPromotionCodesContracts.d.ts +30 -30
  87. package/dist/promotions/adminPromotionsContracts.d.ts +14 -14
  88. package/package.json +12 -2
  89. package/release-gates.json +30 -3
  90. package/sql/20261026120000_account_support_operator_authority.sql +693 -0
  91. package/src/account-support/accountReadFacade.ts +15 -0
  92. package/src/account-support/accountReadModels.ts +371 -0
  93. package/src/account-support/bindings.ts +28 -0
  94. package/src/account-support/contracts/account.ts +306 -0
  95. package/src/account-support/contracts/capabilities.ts +30 -0
  96. package/src/account-support/contracts/commands.ts +243 -0
  97. package/src/account-support/contracts/customer360.ts +150 -0
  98. package/src/account-support/contracts/index.ts +141 -0
  99. package/src/account-support/contracts/invoice.ts +44 -0
  100. package/src/account-support/contracts/operator.ts +123 -0
  101. package/src/account-support/contribution.ts +35 -0
  102. package/src/account-support/customer360.ts +268 -0
  103. package/src/account-support/customerAccountReadDiagnostics.ts +83 -0
  104. package/src/account-support/index.ts +72 -0
  105. package/src/account-support/invoiceAccess.ts +63 -0
  106. package/src/account-support/migrations.ts +7 -0
  107. package/src/account-support/operatorAuthority.ts +393 -0
  108. package/src/account-support/operatorRecovery.ts +136 -0
  109. package/src/account-support/orderHistory.ts +309 -0
  110. package/src/account-support/paymentMethodComposition.ts +8 -0
  111. package/src/account-support/postgresOperatorAuthority.ts +92 -0
  112. package/src/account-support/selfService.ts +83 -0
  113. package/src/account-support/subscriptionCommandCodec.ts +48 -0
  114. package/src/composition/accountSupportContribution.ts +28 -0
  115. package/src/composition/index.ts +1 -0
@@ -0,0 +1,150 @@
1
+ /** Verified human administrator context, supplied by the host. */
2
+ export interface OperatorContext { operatorId: string; role: "admin"; isMachineActor: false; graph: "managed" | "native" }
3
+ /** Verbatim parsed customer-360 lookup vocabulary; defaults belong to the host codec. */
4
+ export interface Customer360Lookup {
5
+ query?: string; subjectId?: string; orderId?: string; subscriptionId?: string;
6
+ email?: string; pageSize: number; mode?: "search" | "snapshot";
7
+ }
8
+ /** The supplied search/snapshot DTOs preserve owner coverage and omitted enrichments. */
9
+ export interface Customer360ReadPort<Search, Snapshot> {
10
+ searchCustomerJourney(request: Customer360Lookup): Promise<Search>;
11
+ getCustomerJourney(request: Customer360Lookup): Promise<Snapshot | null>;
12
+ }
13
+
14
+ export type Customer360LifecycleStage = "lead" | "waitlist" | "tester" | "customer" | "inactive";
15
+
16
+ export interface Customer360Subject {
17
+ subjectId: string;
18
+ displayName: string | null;
19
+ email: string | null;
20
+ phone?: string | null;
21
+ lifecycleStage: Customer360LifecycleStage;
22
+ lifecycle: { stage: Customer360LifecycleStage; enteredAt: string | null; exitedAt: string | null }[];
23
+ firstSeenAt: string | null;
24
+ lastActivityAt: string | null;
25
+ authUserLinked?: boolean;
26
+ hasSignedIn?: boolean;
27
+ }
28
+
29
+ export interface Customer360Order {
30
+ orderId: string;
31
+ orderNumber: string | null;
32
+ status: string;
33
+ subscriptionId: string | null;
34
+ totalMinor: number | null;
35
+ currency: string | null;
36
+ createdAt: string | null
37
+ }
38
+
39
+ export interface Customer360Subscription {
40
+ subscriptionId: string;
41
+ status: string;
42
+ nextCycleAt: string | null;
43
+ latestOrderId: string | null;
44
+ templateVersion?: number;
45
+ shippingAddressId?: string | null;
46
+ paymentMethodKind?: string | null;
47
+ paymentMethodStatus?: string | null
48
+ }
49
+
50
+ export interface Customer360PaymentTruth {
51
+ orderId: string;
52
+ status: string;
53
+ recoverable: boolean;
54
+ amountMinor: number | null;
55
+ currency: string | null;
56
+ lastAttemptAt: string | null
57
+ }
58
+
59
+ export interface Customer360DunningCase {
60
+ caseId: string;
61
+ subscriptionId: string;
62
+ orderId: string | null;
63
+ status: string;
64
+ retryAttempt: number;
65
+ nextRetryAt: string | null;
66
+ notificationStatus: string | null;
67
+ recoveryAvailable: boolean
68
+ }
69
+
70
+ export interface Customer360Recovery {
71
+ caseId: string;
72
+ status: "available" | "unavailable";
73
+ actionAvailable: boolean;
74
+ lastIssuedAt: string | null;
75
+ lastDeliveryStatus: string | null
76
+ }
77
+
78
+ export interface Customer360ContactHealth {
79
+ lastTerminalStatus: "delivered" | "bounced" | "complained" | "failed" | null;
80
+ lastTerminalAt: string | null;
81
+ reachable: boolean
82
+ }
83
+
84
+ export interface Customer360Address {
85
+ addressId: string;
86
+ label: string | null;
87
+ recipientName: string | null;
88
+ line1: string;
89
+ line2: string | null;
90
+ postalCode: string;
91
+ city: string;
92
+ country: string;
93
+ contactPhone: string | null;
94
+ isDefault: boolean;
95
+ kind: "shipping" | "both"
96
+ }
97
+
98
+ export interface Customer360AuditEvent {
99
+ eventId: string;
100
+ occurredAt: string;
101
+ action: string;
102
+ outcome: string;
103
+ entity: { kind: "subject" | "order" | "subscription" | "dunning_case" | "recovery"; id: string }
104
+ }
105
+
106
+ export interface Customer360LookupResult {
107
+ query: string;
108
+ matchedBy: "subject_id" | "email" | "order_id" | "order_number" | "subscription_id" | "text";
109
+ confidence: "exact" | "high" | "medium";
110
+ warnings: string[]
111
+ }
112
+
113
+ export interface Customer360SearchResponse {
114
+ contractVersion: "support.customer_360.v2";
115
+ query: string;
116
+ candidates: {
117
+ subjectId: string;
118
+ displayName: string | null;
119
+ email: string | null;
120
+ lifecycleStage: Customer360LifecycleStage;
121
+ matchedBy: Customer360LookupResult["matchedBy"];
122
+ confidence: Customer360LookupResult["confidence"];
123
+ lastActivityAt: string | null;
124
+ snapshotLookup: { subjectId: string }
125
+ }[];
126
+ warnings: string[];
127
+ }
128
+
129
+ export interface Customer360Snapshot<Extension = unknown> {
130
+ contractVersion: "support.customer_360.v2";
131
+ lookup: Customer360LookupResult;
132
+ subject: Customer360Subject;
133
+ orders: Customer360Order[];
134
+ subscriptions: Customer360Subscription[];
135
+ payment: Customer360PaymentTruth[];
136
+ dunningCases: Customer360DunningCase[];
137
+ recovery: Customer360Recovery[];
138
+ auditTrail: Customer360AuditEvent[];
139
+ contactHealth?: Customer360ContactHealth;
140
+ addresses?: Customer360Address[];
141
+ extension?: Extension;
142
+ }
143
+
144
+ export interface Customer360SummaryReadPort<Request, Result> {
145
+ getPortableSummary(request: Request): Promise<Result>
146
+ }
147
+
148
+ export interface DiagnosticHistoryReadPort<Request, Result> {
149
+ history(request: Request): Promise<Result>
150
+ }
@@ -0,0 +1,141 @@
1
+ export type {
2
+ CustomerContext,
3
+ AccountReadRefusal,
4
+ AccountReadPort,
5
+ AccountProfile,
6
+ AccountAddress,
7
+ AccountBillingProfile,
8
+ AccountPaymentPreference,
9
+ AccountPickupPoint,
10
+ AccountDeliverySelection,
11
+ AccountDeliveryPreference,
12
+ AccountEvent,
13
+ AccountSubscriptionLine,
14
+ AccountSubscription,
15
+ AccountTrackingReference,
16
+ AccountTrackingEvent,
17
+ AccountInvoiceSummary,
18
+ AccountInvoiceDocument,
19
+ AccountOrderSummary,
20
+ AccountOrderLine,
21
+ AccountFulfillmentSummary,
22
+ AccountOrderDetail,
23
+ AccountOrderList,
24
+ AccountOrderDetailResponse,
25
+ AccountActionRequired,
26
+ AccountActionMessage,
27
+ AccountSnapshot
28
+ } from "./account.js";
29
+ export type {
30
+ OperatorContext,
31
+ Customer360Lookup,
32
+ Customer360ReadPort,
33
+ Customer360LifecycleStage,
34
+ Customer360Subject,
35
+ Customer360Order,
36
+ Customer360Subscription,
37
+ Customer360PaymentTruth,
38
+ Customer360DunningCase,
39
+ Customer360Recovery,
40
+ Customer360ContactHealth,
41
+ Customer360Address,
42
+ Customer360AuditEvent,
43
+ Customer360LookupResult,
44
+ Customer360SearchResponse,
45
+ Customer360Snapshot,
46
+ Customer360SummaryReadPort,
47
+ DiagnosticHistoryReadPort
48
+ } from "./customer360.js";
49
+ export type {
50
+ SubscriptionCommandContext,
51
+ SubscriptionCommand,
52
+ SubscriptionBundleLine,
53
+ SubscriptionCommandReceipt,
54
+ SubscriptionCommandRefusal,
55
+ SubscriptionUnsupportedCode,
56
+ SubscriptionUnsupported,
57
+ SubscriptionCapabilities,
58
+ NativeSubscriptionCommand,
59
+ SelfServiceCommandFacade,
60
+ AccountSupportMoney,
61
+ SubscriptionPaymentMethodStatus,
62
+ SubscriptionRenewalBlockStatus,
63
+ EligibilityRefusal,
64
+ LegacySubscriptionPreviewResponse
65
+ } from "./commands.js";
66
+ export type {
67
+ OperatorSubscriptionAction,
68
+ OperatorNewShippingAddress,
69
+ OperatorShippingAddressPayload,
70
+ OperatorSubscriptionPayload,
71
+ OperatorSubscriptionRefusal,
72
+ RecoveryRefusal,
73
+ EmailCorrectionRefusal,
74
+ PhoneCorrectionRefusal,
75
+ LeadRefusal,
76
+ OperatorContactCommand,
77
+ OperatorLeadCommand,
78
+ OperatorRecoveryCommand,
79
+ OperatorRecoveryCommandEnvelope,
80
+ OperatorSubscriptionResult
81
+ } from "./operator.js";
82
+ export type {
83
+ InvoiceDocumentIdentity,
84
+ InvoiceUnsupported,
85
+ InvoiceClearanceState,
86
+ InvoiceAccessPort,
87
+ CustomerDocumentDeliveryStatus,
88
+ CustomerInvoiceDocumentRole,
89
+ CustomerInvoiceDownloadArtifact,
90
+ CustomerInvoiceDownloadRequest,
91
+ AccountInvoiceHistoryResult,
92
+ AccountInvoiceHistoryDocument
93
+ } from "./invoice.js";
94
+ export type { AccountSupportCapability, AccountSupportCapabilities } from "./capabilities.js";
95
+ export type {
96
+ LegacySubscriptionRequest,
97
+ RawSubscriptionCommands,
98
+ NativeSubscriptionPreview,
99
+ NativePreviewProjection,
100
+ SubscriptionDriver,
101
+ MethodSetupInput,
102
+ MethodSetupResult,
103
+ MethodSetupPort,
104
+ NativeLegacySubscriptionParts,
105
+ LegacySubscriptionPort,
106
+ SelfServiceCommandFacadeInput
107
+ } from "./commands.js";
108
+ export type { AccountSupportLaneUnavailable } from "./capabilities.js";
109
+ // R4's additive native result and graph-specific authority ports.
110
+ export type {
111
+ OperatorOwnerReceipt,
112
+ NativeOperatorSubscriptionResult,
113
+ OperatorAuthorityUnavailable,
114
+ OperatorEmailCorrectionResult,
115
+ OperatorPhoneCorrectionResult,
116
+ CustomerRecoveryAuthorityResult,
117
+ LeadAbsorptionCarried,
118
+ OperatorLeadAbsorptionResult,
119
+ OperatorEmailCorrectionClaim,
120
+ OperatorIdentityMovePort,
121
+ OperatorEmailCorrectionAdmission,
122
+ OperatorEmailCorrectionPhase,
123
+ OperatorEmailCorrectionSettlement,
124
+ OperatorEmailCorrectionExecution,
125
+ OperatorAccountLinkRefusal,
126
+ OperatorAccountLinkResult,
127
+ OperatorAccountLinkRequest,
128
+ OperatorAccountLinkIdentityPort,
129
+ OperatorSubscriptionInput,
130
+ OperatorEmailInput,
131
+ OperatorPhoneInput,
132
+ OperatorLinkInput,
133
+ OperatorRecoveryInput,
134
+ OperatorAbsorptionInput,
135
+ ManagedOperatorAuthorityPort,
136
+ NativeOperatorAuthorityPort,
137
+ LinkThenSubscriptionResult
138
+ } from "./operator.js";
139
+ export type {
140
+ OperatorAuthorityCapabilities
141
+ } from "./capabilities.js";
@@ -0,0 +1,44 @@
1
+ /** Exact browser-safe transcriptions of M09 account-history inputs and outcomes. */
2
+ export interface InvoiceDocumentIdentity {
3
+ readonly providerRef: string;
4
+ readonly documentRef: string;
5
+ readonly providerDocumentRef: string | null;
6
+ readonly orderRef: string;
7
+ readonly invoiceRef: string;
8
+ readonly parentProviderDocumentRef: string | null;
9
+ }
10
+ export interface InvoiceUnsupported {
11
+ readonly kind: "unsupported";
12
+ readonly code: "host_binding_required" | "unsupported";
13
+ readonly retryable: false;
14
+ readonly effectMayHaveSucceeded: false;
15
+ }
16
+ export type InvoiceClearanceState = "accepted" | "not_submitted" | "pending" | "rejected" | "not_required" | "unsupported";
17
+ export type InvoiceAccessPort = {
18
+ read(input: { readonly accountRef: string; readonly orderId: string }): Promise<
19
+ | { readonly kind: "found"; readonly accountRef: string; readonly orderId: string; readonly documents: readonly { readonly documentRef: string; readonly invoiceRef: string; readonly parentDocumentRef: string | null; readonly role: "original" | "correction" | "replacement"; readonly status: string; readonly issuedAt: string | null; readonly clearance: { readonly state: InvoiceClearanceState; readonly reference: string | null } | null; readonly documentKind: string | null; readonly artifact: "invoice" | "correction"; readonly providerDocumentNumber: string | null; readonly totalGrossMinor: number | null; readonly currency: string; readonly emailState: "not_requested" | "pending" | "provider_accepted" | "failed"; readonly isCurrent: boolean; readonly createdAt: string; readonly downloadAvailable: boolean }[] }
20
+ | { readonly kind: "denied" }
21
+ | { readonly kind: "unknown"; readonly code: "unknown" }
22
+ | InvoiceUnsupported
23
+ >;
24
+ resolveDownload(input: { readonly accountRef: string; readonly orderId: string; readonly documentRef: string }): Promise<{ readonly kind: "authorized"; readonly identity: InvoiceDocumentIdentity; readonly artifactRef: string; readonly fileName: string; readonly parentDocumentRef: string | null } | { readonly kind: "denied" } | { readonly kind: "unknown"; readonly code: "unknown" } | InvoiceUnsupported>;
25
+ };
26
+
27
+ export type CustomerDocumentDeliveryStatus = "not_ready"
28
+ | "pdf_ready"
29
+ | "ksef_pending"
30
+ | "email_pending"
31
+ | "email_provider_accepted"
32
+ | "email_sent"
33
+ | "requires_correction"
34
+ | "delivery_failed";
35
+ export type CustomerInvoiceDocumentRole = "original" | "correction" | "replacement";
36
+ export type CustomerInvoiceDownloadArtifact = "invoice" | "correction";
37
+
38
+ export interface CustomerInvoiceDownloadRequest {
39
+ invoiceId: string;
40
+ artifact: CustomerInvoiceDownloadArtifact
41
+ }
42
+
43
+ export type AccountInvoiceHistoryResult = Awaited<ReturnType<InvoiceAccessPort["read"]>>;
44
+ export type AccountInvoiceHistoryDocument = Extract<AccountInvoiceHistoryResult, { kind: "found" }>["documents"][number];
@@ -0,0 +1,123 @@
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; line1: string; line2?: string; postalCode: string;
5
+ city: string; country: string; contactPhone?: string;
6
+ }
7
+ export type OperatorShippingAddressPayload = { shippingAddressId: string; reason?: string }
8
+ | { newAddress: OperatorNewShippingAddress; reason?: string };
9
+ export type OperatorSubscriptionPayload = {
10
+ pausePreset?: "2_weeks" | "1_month" | "indefinite"; reason?: string;
11
+ newNextCycleAt?: string; slideMinDays?: number; confirmedChargeTiming?: boolean;
12
+ } | OperatorShippingAddressPayload;
13
+ export type OperatorSubscriptionRefusal = "subscription_not_found" | "subject_not_found"
14
+ | "subject_account_unlinked" | "version_conflict" | "slide_target_invalid" | "slide_not_available"
15
+ | "slide_out_of_window" | "payment_blocked" | "invalid_transition" | "charge_timing_not_confirmed"
16
+ | "payment_method_not_chargeable" | "invalid_address";
17
+ export type RecoveryRefusal = "subject_not_found" | "case_not_found" | "lifecycle_not_eligible"
18
+ | "dunning_authority_unavailable" | "case_not_open" | "recoverable_order_unavailable";
19
+ export type EmailCorrectionRefusal = "subject_not_found" | "subject_account_linked" | "email_expectation_conflict" | "email_already_in_use";
20
+ export type PhoneCorrectionRefusal = "subject_not_found" | "phone_expectation_conflict";
21
+ export type LeadRefusal = "lead_not_found" | "customer_not_found" | "lead_email_expectation_conflict"
22
+ | "lead_has_identity" | "lead_has_commercial_footprint" | "unclassified_referencing_table";
23
+ export type OperatorContactCommand =
24
+ | { action: "correct_email"; subjectId: string; expectedEmail: string; newEmail: string; idempotencyKey: string }
25
+ | { action: "correct_phone"; subjectId: string; expectedPhone: string; newPhone: string; idempotencyKey: string };
26
+ export interface OperatorLeadCommand { action: "absorb_lead"; subjectId: string; leadId: string; expectedLeadEmail: string; idempotencyKey: string }
27
+ export type OperatorRecoveryCommand =
28
+ | { action: "issue_recovery"; subjectId: string; caseId: string; idempotencyKey: string }
29
+ | { action: "apply_subscription_action"; subscriptionId: string; subscriptionAction: OperatorSubscriptionAction;
30
+ expectedVersion: number; idempotencyKey: string; payload?: OperatorSubscriptionPayload }
31
+ | OperatorContactCommand | OperatorLeadCommand;
32
+ /** Carries trusted context and the original command; performs no authorization or writes.
33
+ * The host preserves the command key, optional reason and expected version.
34
+ */
35
+ export interface OperatorRecoveryCommandEnvelope<Command = OperatorRecoveryCommand> {
36
+ actor: OperatorContext;
37
+ subject: { subjectId: string; subscriptionId?: string; orderId?: string; caseId?: string };
38
+ command: Command; reason: string | null; idempotencyKey: string; expectedVersion: number | null;
39
+ evidence: readonly { owner: string; resourceId: string; revision: string | null }[];
40
+ }
41
+ export type OperatorSubscriptionResult =
42
+ | { outcome: "applied" | "noop" | "replayed"; subscriptionStatus: string; nextCycleAt: string | null;
43
+ templateVersion: number; eventId: string | null; appliedBy: "operator_reschedule_band" | "customer_self_service_delegate" | null }
44
+ | { outcome: "refused" | "conflict"; refusalCode: OperatorSubscriptionRefusal };
45
+
46
+ /** Actual M10 receipt evidence; support never manufactures an audit identifier. */
47
+ export interface OperatorOwnerReceipt {
48
+ id: string; actorId: string; idempotencyKey: string; commandFingerprint: string;
49
+ result: Record<string, unknown>;
50
+ }
51
+ /** Additive native superset. The managed transcription above remains unchanged. */
52
+ export type NativeOperatorSubscriptionResult =
53
+ | (OperatorSubscriptionResult & { ownerReceipt?: OperatorOwnerReceipt })
54
+ | { outcome: "refused"; refusalCode: "operator_subscription_authority_unavailable"; ownerReceipt?: OperatorOwnerReceipt };
55
+ export type OperatorAuthorityUnavailable = { outcome: "refused"; refusalCode: "operator_subscription_authority_unavailable" };
56
+ export type OperatorEmailCorrectionResult =
57
+ | { outcome: "applied" | "noop" | "replayed"; authUserLinked: boolean }
58
+ | { outcome: "refused" | "conflict"; refusalCode: EmailCorrectionRefusal };
59
+ export type OperatorPhoneCorrectionResult = { outcome: "applied" | "noop" | "replayed" }
60
+ | { outcome: "refused" | "conflict"; refusalCode: PhoneCorrectionRefusal };
61
+ export type CustomerRecoveryAuthorityResult =
62
+ | { outcome: "issued"; deliveryStatus: string; auditEventId: string }
63
+ | { outcome: "replayed"; deliveryStatus: string; auditEventId: string }
64
+ | { outcome: "refused"; refusalCode: RecoveryRefusal }
65
+ | { outcome: "conflict"; conflictCode: "idempotency_conflict" };
66
+ export interface LeadAbsorptionCarried { personalization: number; consents: number; sourceLinks: number; deliveries: number }
67
+ export type OperatorLeadAbsorptionResult =
68
+ | { outcome: "applied" | "noop" | "replayed"; leadId: string; customerId: string; carried: LeadAbsorptionCarried }
69
+ | { outcome: "refused" | "conflict"; leadId: string; refusalCode: LeadRefusal; blockingTables: string[] };
70
+ export interface OperatorEmailCorrectionClaim { idempotencyKey: string; claimToken: string; sequence: string }
71
+ export interface OperatorIdentityMovePort {
72
+ findClientHoldingEmail(email: string): Promise<{ clientId: string } | null>;
73
+ readIdentityEmail(authUserId: string): Promise<string>;
74
+ moveIdentityEmail(authUserId: string, email: string, claim: OperatorEmailCorrectionClaim): Promise<void>;
75
+ }
76
+ export type OperatorEmailCorrectionAdmission = OperatorEmailCorrectionResult | {
77
+ outcome: "admitted"; authUserId: string | null; originalEmail: string; newEmail: string;
78
+ };
79
+ export type OperatorEmailCorrectionPhase = "complete" | "release" | "fail";
80
+ export type OperatorEmailCorrectionSettlement = OperatorEmailCorrectionResult | { outcome: "pending" } | { outcome: "failed" };
81
+ export type OperatorEmailCorrectionExecution = OperatorEmailCorrectionSettlement | (OperatorEmailCorrectionClaim & {
82
+ outcome: "claimed"; authUserId: string | null; originalEmail: string; newEmail: string;
83
+ });
84
+ export type OperatorAccountLinkRefusal = "subject_not_found" | "subject_not_customer" | "subject_email_missing"
85
+ | "email_expectation_conflict" | "subject_account_linked" | "identity_not_linkable" | "identity_linked_elsewhere";
86
+ export type OperatorAccountLinkResult = { outcome: "applied" | "noop" | "replayed" }
87
+ | { outcome: "refused" | "conflict"; refusalCode: OperatorAccountLinkRefusal };
88
+ export interface OperatorAccountLinkRequest { action: "link_account"; subjectId: string; expectedEmail: string; idempotencyKey: string }
89
+ export interface OperatorAccountLinkIdentityPort {
90
+ findSubject(clientId: string): Promise<{ identityKind: string | null; email: string | null; authUserId: string | null } | null>;
91
+ resolveIdentityForEmail(email: string): Promise<string>;
92
+ }
93
+ export interface OperatorSubscriptionInput {
94
+ subscriptionId: string; subscriptionAction: OperatorSubscriptionAction; expectedVersion: number;
95
+ payload: OperatorSubscriptionPayload; idempotencyKey: string; operatorId: string;
96
+ }
97
+ export interface OperatorEmailInput { subjectId: string; expectedEmail: string; newEmail: string; idempotencyKey: string; operatorId: string }
98
+ export interface OperatorPhoneInput { subjectId: string; expectedPhone: string; newPhone: string; idempotencyKey: string; operatorId: string }
99
+ export interface OperatorLinkInput { subjectId: string; expectedEmail: string; authUserId: string | null; idempotencyKey: string; operatorId: string }
100
+ export interface OperatorRecoveryInput { subjectId: string; caseId: string; idempotencyKey: string; operatorId: string }
101
+ export interface OperatorAbsorptionInput { customerId: string; leadId: string; expectedLeadEmail: string; idempotencyKey: string; operatorId: string }
102
+ export interface ManagedOperatorAuthorityPort {
103
+ applySubscriptionAction(input: OperatorSubscriptionInput): Promise<OperatorSubscriptionResult>;
104
+ correctSubjectEmail(input: OperatorEmailInput): Promise<OperatorEmailCorrectionAdmission>;
105
+ claimSubjectEmail(input: { idempotencyKey: string }): Promise<OperatorEmailCorrectionExecution>;
106
+ settleSubjectEmail(input: { idempotencyKey: string; claimToken: string; phase: OperatorEmailCorrectionPhase }): Promise<OperatorEmailCorrectionSettlement>;
107
+ correctSubjectPhone(input: OperatorPhoneInput): Promise<OperatorPhoneCorrectionResult>;
108
+ linkSubjectAccount(input: OperatorLinkInput): Promise<OperatorAccountLinkResult>;
109
+ issueRecovery(input: OperatorRecoveryInput): Promise<CustomerRecoveryAuthorityResult>;
110
+ absorbLead(input: OperatorAbsorptionInput): Promise<OperatorLeadAbsorptionResult>;
111
+ }
112
+ export interface NativeOperatorAuthorityPort {
113
+ applySubscriptionAction(input: OperatorSubscriptionInput): Promise<NativeOperatorSubscriptionResult>;
114
+ correctSubjectEmail(input: OperatorEmailInput): Promise<OperatorAuthorityUnavailable>;
115
+ correctSubjectPhone(input: OperatorPhoneInput): Promise<OperatorAuthorityUnavailable>;
116
+ linkSubjectAccount(input: OperatorLinkInput): Promise<OperatorAuthorityUnavailable>;
117
+ issueRecovery(input: OperatorRecoveryInput): Promise<CustomerRecoveryAuthorityResult>;
118
+ absorbLead(input: OperatorAbsorptionInput): Promise<OperatorLeadAbsorptionResult>;
119
+ }
120
+ export type LinkThenSubscriptionResult =
121
+ | { outcome: "link_refused"; link: OperatorAccountLinkResult | OperatorAuthorityUnavailable }
122
+ | { outcome: "completed"; link: OperatorAccountLinkResult; subscription: NativeOperatorSubscriptionResult }
123
+ | { outcome: "partial"; link: OperatorAccountLinkResult; subscription: NativeOperatorSubscriptionResult | null };
@@ -0,0 +1,35 @@
1
+ import type { AccountSupportCapabilities } from "./contracts/index.js";
2
+ import type { AccountSupportCallable, AccountSupportOperations } from "./bindings.js";
3
+
4
+ export type AccountSupportBindingRefusal = { readonly kind: "refused"; readonly code: "disabled" | "schema_unknown" | "schema_unsatisfied" };
5
+ export type AccountSupportReadiness = { readonly kind: "ready" } | AccountSupportBindingRefusal;
6
+ export type GuardedAccountSupportOperation<F> = F extends AccountSupportCallable
7
+ ? (...args: Parameters<F>) => Promise<Awaited<ReturnType<F>> | AccountSupportBindingRefusal> : null;
8
+ export type GuardedAccountSupportOperations<O extends AccountSupportOperations> = { readonly [K in keyof O]: GuardedAccountSupportOperation<O[K]> };
9
+
10
+ /** One guard per callable identity, shared by service, port and route aliases. */
11
+ export function createAccountSupportServices<O extends AccountSupportOperations>(input: {
12
+ enabled?: boolean;
13
+ capabilities: AccountSupportCapabilities;
14
+ operations: O;
15
+ readiness(): Promise<AccountSupportReadiness>;
16
+ }) {
17
+ const enabled = input.enabled === true, observe = input.readiness;
18
+ async function readiness(): Promise<AccountSupportReadiness> {
19
+ return enabled ? observe() : { kind: "refused", code: "disabled" };
20
+ }
21
+ const guards = new Map<AccountSupportCallable, AccountSupportCallable>();
22
+ const operations = Object.fromEntries(Object.entries(input.operations).map(([key, operation]) => {
23
+ if (operation === null) return [key, null];
24
+ let guarded = guards.get(operation);
25
+ if (!guarded) {
26
+ guarded = async (...args: never[]) => {
27
+ const state = await readiness();
28
+ return state.kind === "ready" ? operation(...args) : state;
29
+ };
30
+ guards.set(operation, guarded);
31
+ }
32
+ return [key, guarded];
33
+ })) as GuardedAccountSupportOperations<O>;
34
+ return Object.freeze({ capabilities: input.capabilities, readiness, operations: Object.freeze(operations) });
35
+ }