@happyvertical/smrt-sales 0.39.2

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 (201) hide show
  1. package/AGENTS.md +87 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/chunks/__smrt-register__-kWdT7LOt.js +6 -0
  8. package/dist/chunks/__smrt-register__-kWdT7LOt.js.map +1 -0
  9. package/dist/chunks/commissions-BCxqOKVM.js +2181 -0
  10. package/dist/chunks/commissions-BCxqOKVM.js.map +1 -0
  11. package/dist/chunks/crm-DwEz7E2r.js +1428 -0
  12. package/dist/chunks/crm-DwEz7E2r.js.map +1 -0
  13. package/dist/chunks/referrals-BY47p70T.js +2880 -0
  14. package/dist/chunks/referrals-BY47p70T.js.map +1 -0
  15. package/dist/commissions/collections/CommissionAdjustmentCollection.d.ts +26 -0
  16. package/dist/commissions/collections/CommissionAdjustmentCollection.d.ts.map +1 -0
  17. package/dist/commissions/collections/CommissionCollection.d.ts +43 -0
  18. package/dist/commissions/collections/CommissionCollection.d.ts.map +1 -0
  19. package/dist/commissions/collections/CommissionPayoutCollection.d.ts +19 -0
  20. package/dist/commissions/collections/CommissionPayoutCollection.d.ts.map +1 -0
  21. package/dist/commissions/collections/CommissionPlanCollection.d.ts +46 -0
  22. package/dist/commissions/collections/CommissionPlanCollection.d.ts.map +1 -0
  23. package/dist/commissions/collections/EarnerCollection.d.ts +14 -0
  24. package/dist/commissions/collections/EarnerCollection.d.ts.map +1 -0
  25. package/dist/commissions/collections/EarningEventCollection.d.ts +27 -0
  26. package/dist/commissions/collections/EarningEventCollection.d.ts.map +1 -0
  27. package/dist/commissions/collections/index.d.ts +11 -0
  28. package/dist/commissions/collections/index.d.ts.map +1 -0
  29. package/dist/commissions/index.d.ts +20 -0
  30. package/dist/commissions/index.d.ts.map +1 -0
  31. package/dist/commissions/models/Commission.d.ts +150 -0
  32. package/dist/commissions/models/Commission.d.ts.map +1 -0
  33. package/dist/commissions/models/CommissionAdjustment.d.ts +62 -0
  34. package/dist/commissions/models/CommissionAdjustment.d.ts.map +1 -0
  35. package/dist/commissions/models/CommissionPayout.d.ts +119 -0
  36. package/dist/commissions/models/CommissionPayout.d.ts.map +1 -0
  37. package/dist/commissions/models/CommissionPlan.d.ts +117 -0
  38. package/dist/commissions/models/CommissionPlan.d.ts.map +1 -0
  39. package/dist/commissions/models/Earner.d.ts +49 -0
  40. package/dist/commissions/models/Earner.d.ts.map +1 -0
  41. package/dist/commissions/models/EarningEvent.d.ts +89 -0
  42. package/dist/commissions/models/EarningEvent.d.ts.map +1 -0
  43. package/dist/commissions/models/index.d.ts +11 -0
  44. package/dist/commissions/models/index.d.ts.map +1 -0
  45. package/dist/commissions/money.d.ts +42 -0
  46. package/dist/commissions/money.d.ts.map +1 -0
  47. package/dist/commissions/services/CommissionBalanceService.d.ts +14 -0
  48. package/dist/commissions/services/CommissionBalanceService.d.ts.map +1 -0
  49. package/dist/commissions/services/CommissionCalculationService.d.ts +142 -0
  50. package/dist/commissions/services/CommissionCalculationService.d.ts.map +1 -0
  51. package/dist/commissions/services/CommissionPayoutService.d.ts +134 -0
  52. package/dist/commissions/services/CommissionPayoutService.d.ts.map +1 -0
  53. package/dist/commissions/services/CommissionSettlementService.d.ts +42 -0
  54. package/dist/commissions/services/CommissionSettlementService.d.ts.map +1 -0
  55. package/dist/commissions/services/index.d.ts +9 -0
  56. package/dist/commissions/services/index.d.ts.map +1 -0
  57. package/dist/commissions/types.d.ts +243 -0
  58. package/dist/commissions/types.d.ts.map +1 -0
  59. package/dist/commissions.d.ts +2 -0
  60. package/dist/commissions.d.ts.map +1 -0
  61. package/dist/commissions.js +3 -0
  62. package/dist/crm/collections/LeadCollection.d.ts +79 -0
  63. package/dist/crm/collections/LeadCollection.d.ts.map +1 -0
  64. package/dist/crm/collections/OpportunityCollection.d.ts +42 -0
  65. package/dist/crm/collections/OpportunityCollection.d.ts.map +1 -0
  66. package/dist/crm/collections/OpportunityConversionCollection.d.ts +26 -0
  67. package/dist/crm/collections/OpportunityConversionCollection.d.ts.map +1 -0
  68. package/dist/crm/collections/PipelineDefinitionCollection.d.ts +46 -0
  69. package/dist/crm/collections/PipelineDefinitionCollection.d.ts.map +1 -0
  70. package/dist/crm/collections/PipelineStageCollection.d.ts +22 -0
  71. package/dist/crm/collections/PipelineStageCollection.d.ts.map +1 -0
  72. package/dist/crm/collections/SalesActivityCollection.d.ts +24 -0
  73. package/dist/crm/collections/SalesActivityCollection.d.ts.map +1 -0
  74. package/dist/crm/collections/SalesRepresentativeCollection.d.ts +19 -0
  75. package/dist/crm/collections/SalesRepresentativeCollection.d.ts.map +1 -0
  76. package/dist/crm/index.d.ts +27 -0
  77. package/dist/crm/index.d.ts.map +1 -0
  78. package/dist/crm/models/Lead.d.ts +135 -0
  79. package/dist/crm/models/Lead.d.ts.map +1 -0
  80. package/dist/crm/models/Opportunity.d.ts +111 -0
  81. package/dist/crm/models/Opportunity.d.ts.map +1 -0
  82. package/dist/crm/models/OpportunityConversion.d.ts +66 -0
  83. package/dist/crm/models/OpportunityConversion.d.ts.map +1 -0
  84. package/dist/crm/models/PipelineDefinition.d.ts +58 -0
  85. package/dist/crm/models/PipelineDefinition.d.ts.map +1 -0
  86. package/dist/crm/models/PipelineStage.d.ts +66 -0
  87. package/dist/crm/models/PipelineStage.d.ts.map +1 -0
  88. package/dist/crm/models/SalesActivity.d.ts +76 -0
  89. package/dist/crm/models/SalesActivity.d.ts.map +1 -0
  90. package/dist/crm/models/SalesRepresentative.d.ts +60 -0
  91. package/dist/crm/models/SalesRepresentative.d.ts.map +1 -0
  92. package/dist/crm/types.d.ts +228 -0
  93. package/dist/crm/types.d.ts.map +1 -0
  94. package/dist/crm.d.ts +2 -0
  95. package/dist/crm.d.ts.map +1 -0
  96. package/dist/crm.js +3 -0
  97. package/dist/index.d.ts +4 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +5 -0
  100. package/dist/manifest.json +9340 -0
  101. package/dist/referrals/collections/AttributionExceptionCollection.d.ts +17 -0
  102. package/dist/referrals/collections/AttributionExceptionCollection.d.ts.map +1 -0
  103. package/dist/referrals/collections/AttributionPolicyCollection.d.ts +50 -0
  104. package/dist/referrals/collections/AttributionPolicyCollection.d.ts.map +1 -0
  105. package/dist/referrals/collections/ReferralAgreementCollection.d.ts +48 -0
  106. package/dist/referrals/collections/ReferralAgreementCollection.d.ts.map +1 -0
  107. package/dist/referrals/collections/ReferralCollection.d.ts +25 -0
  108. package/dist/referrals/collections/ReferralCollection.d.ts.map +1 -0
  109. package/dist/referrals/collections/ReferralLinkCollection.d.ts +96 -0
  110. package/dist/referrals/collections/ReferralLinkCollection.d.ts.map +1 -0
  111. package/dist/referrals/collections/ReferralProgramCollection.d.ts +18 -0
  112. package/dist/referrals/collections/ReferralProgramCollection.d.ts.map +1 -0
  113. package/dist/referrals/collections/ReferralTermSnapshotCollection.d.ts +13 -0
  114. package/dist/referrals/collections/ReferralTermSnapshotCollection.d.ts.map +1 -0
  115. package/dist/referrals/collections/ReferralTouchCollection.d.ts +30 -0
  116. package/dist/referrals/collections/ReferralTouchCollection.d.ts.map +1 -0
  117. package/dist/referrals/collections/ReferrerCollection.d.ts +17 -0
  118. package/dist/referrals/collections/ReferrerCollection.d.ts.map +1 -0
  119. package/dist/referrals/index.d.ts +38 -0
  120. package/dist/referrals/index.d.ts.map +1 -0
  121. package/dist/referrals/models/AttributionException.d.ts +85 -0
  122. package/dist/referrals/models/AttributionException.d.ts.map +1 -0
  123. package/dist/referrals/models/AttributionPolicy.d.ts +128 -0
  124. package/dist/referrals/models/AttributionPolicy.d.ts.map +1 -0
  125. package/dist/referrals/models/Referral.d.ts +112 -0
  126. package/dist/referrals/models/Referral.d.ts.map +1 -0
  127. package/dist/referrals/models/ReferralAgreement.d.ts +125 -0
  128. package/dist/referrals/models/ReferralAgreement.d.ts.map +1 -0
  129. package/dist/referrals/models/ReferralLink.d.ts +71 -0
  130. package/dist/referrals/models/ReferralLink.d.ts.map +1 -0
  131. package/dist/referrals/models/ReferralProgram.d.ts +57 -0
  132. package/dist/referrals/models/ReferralProgram.d.ts.map +1 -0
  133. package/dist/referrals/models/ReferralTermSnapshot.d.ts +61 -0
  134. package/dist/referrals/models/ReferralTermSnapshot.d.ts.map +1 -0
  135. package/dist/referrals/models/ReferralTouch.d.ts +55 -0
  136. package/dist/referrals/models/ReferralTouch.d.ts.map +1 -0
  137. package/dist/referrals/models/Referrer.d.ts +69 -0
  138. package/dist/referrals/models/Referrer.d.ts.map +1 -0
  139. package/dist/referrals/services/AttributionService.d.ts +248 -0
  140. package/dist/referrals/services/AttributionService.d.ts.map +1 -0
  141. package/dist/referrals/services/ReferralCommissionService.d.ts +91 -0
  142. package/dist/referrals/services/ReferralCommissionService.d.ts.map +1 -0
  143. package/dist/referrals/services/ReferralQualificationService.d.ts +135 -0
  144. package/dist/referrals/services/ReferralQualificationService.d.ts.map +1 -0
  145. package/dist/referrals/types.d.ts +226 -0
  146. package/dist/referrals/types.d.ts.map +1 -0
  147. package/dist/referrals.d.ts +2 -0
  148. package/dist/referrals.d.ts.map +1 -0
  149. package/dist/referrals.js +3 -0
  150. package/dist/smrt-knowledge.json +5501 -0
  151. package/dist/svelte/__tests__/format.test.js +68 -0
  152. package/dist/svelte/__tests__/types.test.js +349 -0
  153. package/dist/svelte/components/AttributionConflictQueue.svelte +361 -0
  154. package/dist/svelte/components/AttributionConflictQueue.svelte.d.ts +15 -0
  155. package/dist/svelte/components/AttributionConflictQueue.svelte.d.ts.map +1 -0
  156. package/dist/svelte/components/CommissionBreakdown.svelte +251 -0
  157. package/dist/svelte/components/CommissionBreakdown.svelte.d.ts +11 -0
  158. package/dist/svelte/components/CommissionBreakdown.svelte.d.ts.map +1 -0
  159. package/dist/svelte/components/CommissionExpenseSummary.svelte +186 -0
  160. package/dist/svelte/components/CommissionExpenseSummary.svelte.d.ts +13 -0
  161. package/dist/svelte/components/CommissionExpenseSummary.svelte.d.ts.map +1 -0
  162. package/dist/svelte/components/ExecutedAgreementsList.svelte +136 -0
  163. package/dist/svelte/components/ExecutedAgreementsList.svelte.d.ts +11 -0
  164. package/dist/svelte/components/ExecutedAgreementsList.svelte.d.ts.map +1 -0
  165. package/dist/svelte/components/LeadList.svelte +224 -0
  166. package/dist/svelte/components/LeadList.svelte.d.ts +19 -0
  167. package/dist/svelte/components/LeadList.svelte.d.ts.map +1 -0
  168. package/dist/svelte/components/OpportunityBoard.svelte +230 -0
  169. package/dist/svelte/components/OpportunityBoard.svelte.d.ts +17 -0
  170. package/dist/svelte/components/OpportunityBoard.svelte.d.ts.map +1 -0
  171. package/dist/svelte/components/OpportunityDetail.svelte +472 -0
  172. package/dist/svelte/components/OpportunityDetail.svelte.d.ts +27 -0
  173. package/dist/svelte/components/OpportunityDetail.svelte.d.ts.map +1 -0
  174. package/dist/svelte/components/PayoutBatchReview.svelte +285 -0
  175. package/dist/svelte/components/PayoutBatchReview.svelte.d.ts +21 -0
  176. package/dist/svelte/components/PayoutBatchReview.svelte.d.ts.map +1 -0
  177. package/dist/svelte/components/PayoutHistoryList.svelte +245 -0
  178. package/dist/svelte/components/PayoutHistoryList.svelte.d.ts +11 -0
  179. package/dist/svelte/components/PayoutHistoryList.svelte.d.ts.map +1 -0
  180. package/dist/svelte/components/ReferralLinkManager.svelte +216 -0
  181. package/dist/svelte/components/ReferralLinkManager.svelte.d.ts +17 -0
  182. package/dist/svelte/components/ReferralLinkManager.svelte.d.ts.map +1 -0
  183. package/dist/svelte/components/ReferralStatusList.svelte +118 -0
  184. package/dist/svelte/components/ReferralStatusList.svelte.d.ts +11 -0
  185. package/dist/svelte/components/ReferralStatusList.svelte.d.ts.map +1 -0
  186. package/dist/svelte/components/ReferrerEarningsSummary.svelte +106 -0
  187. package/dist/svelte/components/ReferrerEarningsSummary.svelte.d.ts +11 -0
  188. package/dist/svelte/components/ReferrerEarningsSummary.svelte.d.ts.map +1 -0
  189. package/dist/svelte/components/SalesDashboard.svelte +192 -0
  190. package/dist/svelte/components/SalesDashboard.svelte.d.ts +29 -0
  191. package/dist/svelte/components/SalesDashboard.svelte.d.ts.map +1 -0
  192. package/dist/svelte/format.d.ts +46 -0
  193. package/dist/svelte/format.d.ts.map +1 -0
  194. package/dist/svelte/format.js +75 -0
  195. package/dist/svelte/index.d.ts +50 -0
  196. package/dist/svelte/index.d.ts.map +1 -0
  197. package/dist/svelte/index.js +37 -0
  198. package/dist/svelte/types.d.ts +422 -0
  199. package/dist/svelte/types.d.ts.map +1 -0
  200. package/dist/svelte/types.js +376 -0
  201. package/package.json +89 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"referrals-BY47p70T.js","names":["policy","snapshot"],"sources":["../../src/referrals/models/AttributionException.ts","../../src/referrals/collections/AttributionExceptionCollection.ts","../../src/referrals/types.ts","../../src/referrals/models/AttributionPolicy.ts","../../src/referrals/collections/AttributionPolicyCollection.ts","../../src/referrals/models/ReferralAgreement.ts","../../src/referrals/collections/ReferralAgreementCollection.ts","../../src/referrals/models/Referral.ts","../../src/referrals/collections/ReferralCollection.ts","../../src/referrals/models/ReferralLink.ts","../../src/referrals/models/ReferralTouch.ts","../../src/referrals/collections/ReferralTouchCollection.ts","../../src/referrals/collections/ReferralLinkCollection.ts","../../src/referrals/models/ReferralProgram.ts","../../src/referrals/collections/ReferralProgramCollection.ts","../../src/referrals/models/ReferralTermSnapshot.ts","../../src/referrals/collections/ReferralTermSnapshotCollection.ts","../../src/referrals/models/Referrer.ts","../../src/referrals/collections/ReferrerCollection.ts","../../src/referrals/services/AttributionService.ts","../../src/referrals/services/ReferralCommissionService.ts","../../src/referrals/services/ReferralQualificationService.ts"],"sourcesContent":["/**\n * AttributionException — the conflict-review queue and override audit trail.\n *\n * When attribution cannot conclude automatically (exact-timestamp ties,\n * competing manual assignments, or a policy whose `conflictBehavior` is\n * `review`), `AttributionService.resolve()` creates an OPEN exception\n * carrying the candidate touches and creates NO referral rows. A human (or\n * policy re-run) resolves it via `AttributionService.resolveException()`,\n * which requires a `resolutionReason`, creates the awarded referral(s), and\n * stamps the resolution audit fields here. `AttributionService.override()`\n * writes an already-RESOLVED exception row as the audit record of a\n * re-attribution.\n *\n * The generated surface is read-only (`list`/`get`): exceptions are minted\n * and resolved exclusively by the attribution service so the audit fields\n * stay trustworthy.\n *\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n AttributionExceptionCandidate,\n AttributionExceptionOptions,\n AttributionExceptionStatus,\n} from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Read-only generated surface: resolution happens through\n // AttributionService so reasons/actors are always recorded.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: false,\n})\nexport class AttributionException extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global rows). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Target the conflicting attribution is about. Required. */\n @field({ required: true })\n targetKind: string = '';\n\n /** Identifier within the {@link targetKind} namespace. Required. */\n @field({ required: true })\n targetId: string = '';\n\n /** The ReferralProgram the contention belongs to. */\n @foreignKey('ReferralProgram')\n programId: string = '';\n\n /** `open` (default, awaiting review) or `resolved`. */\n status: AttributionExceptionStatus = 'open';\n\n /**\n * Why the exception was raised — recommended vocabulary: `'tie'`\n * (exact-timestamp tie between distinct referrers),\n * `'competing_touches'` (competing manual assignments), and\n * `'policy_review'` (`conflictBehavior: 'review'` with multiple\n * referrers). Open string.\n */\n conflictReason: string = '';\n\n /**\n * The competing touches as a JSON-string array of\n * {@link AttributionExceptionCandidate}. Use\n * {@link getCandidates}/{@link setCandidates}.\n */\n candidates: string = '[]';\n\n /**\n * How the exception was resolved — `'override'` (human award) or\n * `'policy'` (policy re-run). Empty while open.\n */\n resolutionMode: string = '';\n\n /** Why credit was awarded the way it was. REQUIRED to resolve. */\n resolutionReason: string = '';\n\n /**\n * Who resolved the exception — cross-package string reference to a\n * smrt-profiles Profile.\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n resolvedByProfileId: string = '';\n\n /**\n * Ids of the Referral rows the resolution created, as a JSON-string\n * array. Use {@link getResolvedReferralIds}/{@link setResolvedReferralIds}.\n */\n resolvedReferralIds: string = '[]';\n\n /** When the exception was resolved. */\n resolvedAt: Date | null = null;\n\n /**\n * Free-form JSON object stored as a string (the attribution service\n * stamps the policy pin here for later resolution). Use\n * {@link getMetadata}/{@link setMetadata}.\n */\n metadata: string = '{}';\n\n constructor(options: AttributionExceptionOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.targetKind !== undefined) this.targetKind = options.targetKind;\n if (options.targetId !== undefined) this.targetId = options.targetId;\n if (options.programId !== undefined) this.programId = options.programId;\n if (options.status !== undefined) this.status = options.status;\n if (options.conflictReason !== undefined)\n this.conflictReason = options.conflictReason;\n if (options.candidates !== undefined) this.candidates = options.candidates;\n if (options.resolutionMode !== undefined)\n this.resolutionMode = options.resolutionMode;\n if (options.resolutionReason !== undefined)\n this.resolutionReason = options.resolutionReason;\n if (options.resolvedByProfileId !== undefined)\n this.resolvedByProfileId = options.resolvedByProfileId;\n if (options.resolvedReferralIds !== undefined)\n this.resolvedReferralIds = options.resolvedReferralIds;\n if (options.resolvedAt !== undefined)\n this.resolvedAt = AttributionException.coerceDate(options.resolvedAt);\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /** Re-coerce date fields after the framework reapplies raw option values. */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.resolvedAt = AttributionException.coerceDate(this.resolvedAt);\n return this;\n }\n\n /** Whether the exception still awaits review. */\n isOpen(): boolean {\n return this.status === 'open';\n }\n\n /** Parse {@link candidates}; returns `[]` on empty/invalid JSON. */\n getCandidates(): AttributionExceptionCandidate[] {\n if (!this.candidates) return [];\n try {\n const parsed = JSON.parse(this.candidates) as unknown;\n return Array.isArray(parsed)\n ? (parsed as AttributionExceptionCandidate[])\n : [];\n } catch {\n return [];\n }\n }\n\n /** Serialize and store {@link candidates}. */\n setCandidates(candidates: AttributionExceptionCandidate[]): void {\n this.candidates = JSON.stringify(candidates ?? []);\n }\n\n /** Parse {@link resolvedReferralIds}; returns `[]` on empty/invalid JSON. */\n getResolvedReferralIds(): string[] {\n if (!this.resolvedReferralIds) return [];\n try {\n const parsed = JSON.parse(this.resolvedReferralIds) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter((entry): entry is string => typeof entry === 'string')\n : [];\n } catch {\n return [];\n }\n }\n\n /** Serialize and store {@link resolvedReferralIds}. */\n setResolvedReferralIds(ids: string[]): void {\n this.resolvedReferralIds = JSON.stringify(ids ?? []);\n }\n\n /** Parse {@link metadata}; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n const parsed = JSON.parse(this.metadata) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link metadata}. */\n setMetadata(data: Record<string, unknown>): void {\n this.metadata = JSON.stringify(data ?? {});\n }\n\n /**\n * Mark the exception resolved (mutates; the caller saves). Requires a\n * non-empty reason — resolutions without a recorded rationale are\n * rejected.\n */\n markResolved(input: {\n mode: string;\n reason: string;\n resolvedByProfileId?: string;\n referralIds: string[];\n at?: Date;\n }): void {\n if (this.status !== 'open') {\n throw new Error(\n `AttributionException ${this.id ?? '<new>'}: cannot resolve from status '${this.status}'`,\n );\n }\n if (!input.reason?.trim()) {\n throw new Error(\n `AttributionException ${this.id ?? '<new>'}: a non-empty resolutionReason is required`,\n );\n }\n this.status = 'resolved';\n this.resolutionMode = input.mode;\n this.resolutionReason = input.reason;\n this.resolvedByProfileId = input.resolvedByProfileId ?? '';\n this.setResolvedReferralIds(input.referralIds);\n this.resolvedAt = input.at ?? new Date();\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default AttributionException;\n","/**\n * AttributionExceptionCollection — collection manager for\n * {@link AttributionException}.\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AttributionException } from '../models/AttributionException.js';\n\nexport class AttributionExceptionCollection extends SmrtCollection<AttributionException> {\n static readonly _itemClass = AttributionException;\n\n /** Every open exception, oldest first (the review queue). */\n async findOpen(): Promise<AttributionException[]> {\n return await this.list({\n where: { status: 'open' },\n orderBy: 'created_at ASC',\n });\n }\n\n /**\n * The open exception for one target within one program, or `null`.\n * `AttributionService.resolve()` returns this instead of minting a\n * duplicate when a conflict is already parked for review.\n */\n async findOpenByTarget(\n targetKind: string,\n targetId: string,\n programId: string,\n ): Promise<AttributionException | null> {\n const results = await this.list({\n where: { targetKind, targetId, programId, status: 'open' },\n orderBy: 'created_at ASC',\n limit: 1,\n });\n return results[0] ?? null;\n }\n\n /** Every exception (open and resolved) for one target, newest first. */\n async findByTarget(\n targetKind: string,\n targetId: string,\n ): Promise<AttributionException[]> {\n return await this.list({\n where: { targetKind, targetId },\n orderBy: 'created_at DESC',\n });\n }\n}\n\nexport default AttributionExceptionCollection;\n","/**\n * Shared types, status unions, and contracts for the referrals module.\n *\n * Statuses are string-literal unions derived from `as const` arrays (no TS\n * enums) so the runtime lists (validation, UI pickers) and the compile-time\n * unions stay in lockstep. All monetary fields across the module are integer\n * cents with a `*Cents` suffix; rates/fractions are decimals in `0`–`1`.\n *\n * The referrals module depends on the `commissions` module (same package)\n * for the neutral financial core — Earners, CommissionPlans, EarningEvents,\n * Commissions — and NEVER on `crm`. Qualifying targets are generic\n * `(targetKind, targetId)` string pairs so a referral can point at a lead,\n * opportunity, client, project, subscription, or any application-defined\n * record without this module knowing its semantics.\n *\n * @packageDocumentation\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\n\n// ---------------------------------------------------------------------------\n// Status / kind vocabularies\n// ---------------------------------------------------------------------------\n\n/** Lifecycle of a {@link ReferrerOptions | Referrer} role. */\nexport const REFERRER_STATUSES = ['pending', 'active', 'suspended'] as const;\nexport type ReferrerStatus = (typeof REFERRER_STATUSES)[number];\n\n/** Lifecycle of a {@link ReferralProgramOptions | ReferralProgram}. */\nexport const REFERRAL_PROGRAM_STATUSES = [\n 'draft',\n 'active',\n 'paused',\n 'archived',\n] as const;\nexport type ReferralProgramStatus = (typeof REFERRAL_PROGRAM_STATUSES)[number];\n\n/**\n * Lifecycle of a versioned AttributionPolicy row.\n *\n * `draft → active | retired`; `active → superseded | retired`;\n * `superseded` / `retired` are terminal. Amendments never mutate an active\n * row — they insert a new `(policyKey, version + 1)` draft (CommissionPlan\n * pattern).\n */\nexport const ATTRIBUTION_POLICY_STATUSES = [\n 'draft',\n 'active',\n 'superseded',\n 'retired',\n] as const;\nexport type AttributionPolicyStatus =\n (typeof ATTRIBUTION_POLICY_STATUSES)[number];\n\n/**\n * How an AttributionPolicy credits touches:\n *\n * - `first_touch` — the earliest eligible touch wins.\n * - `last_touch` — the latest eligible touch wins.\n * - `assigned` — only `manual_assignment` touches count (most recent wins;\n * competing assignments from distinct referrers raise an exception).\n * - `split` — every distinct eligible referrer receives an attributed\n * Referral with an equal credit fraction sharing one `splitGroupId`.\n */\nexport const ATTRIBUTION_CREDIT_MODES = [\n 'first_touch',\n 'last_touch',\n 'assigned',\n 'split',\n] as const;\nexport type AttributionCreditMode = (typeof ATTRIBUTION_CREDIT_MODES)[number];\n\n/**\n * How an AttributionPolicy behaves when more than one distinct eligible\n * referrer competes: `auto` lets the credit mode decide (only genuine\n * conflicts raise exceptions); `review` forces an AttributionException for\n * any multi-referrer contention.\n */\nexport const ATTRIBUTION_CONFLICT_BEHAVIORS = ['auto', 'review'] as const;\nexport type AttributionConflictBehavior =\n (typeof ATTRIBUTION_CONFLICT_BEHAVIORS)[number];\n\n/** Lifecycle of a ReferralLink — disabled links refuse new clicks. */\nexport const REFERRAL_LINK_STATUSES = ['active', 'disabled'] as const;\nexport type ReferralLinkStatus = (typeof REFERRAL_LINK_STATUSES)[number];\n\n/** Kinds of immutable ReferralTouch attribution evidence. */\nexport const REFERRAL_TOUCH_KINDS = [\n 'click',\n 'code_entry',\n 'manual_assignment',\n 'partner_entry',\n] as const;\nexport type ReferralTouchKind = (typeof REFERRAL_TOUCH_KINDS)[number];\n\n/**\n * Lifecycle of a Referral. Guarded transitions (save-time, authoritative\n * prior re-read — commerce pattern):\n * `pending → attributed | under_review | disqualified | expired`;\n * `under_review → attributed | disqualified`;\n * `attributed → qualified | disqualified | expired`;\n * `qualified → disqualified` (rare clawback path);\n * `disqualified` / `expired` are terminal.\n */\nexport const REFERRAL_STATUSES = [\n 'pending',\n 'attributed',\n 'qualified',\n 'disqualified',\n 'expired',\n 'under_review',\n] as const;\nexport type ReferralStatus = (typeof REFERRAL_STATUSES)[number];\n\n/** Lifecycle of an AttributionException conflict-review row. */\nexport const ATTRIBUTION_EXCEPTION_STATUSES = ['open', 'resolved'] as const;\nexport type AttributionExceptionStatus =\n (typeof ATTRIBUTION_EXCEPTION_STATUSES)[number];\n\n/**\n * How an AttributionException was resolved: `override` (a human awarded\n * credit explicitly) or `policy` (a policy re-run settled it). Stored as an\n * open string on the model; this list is the recommended vocabulary.\n */\nexport const ATTRIBUTION_RESOLUTION_MODES = ['override', 'policy'] as const;\nexport type AttributionResolutionMode =\n (typeof ATTRIBUTION_RESOLUTION_MODES)[number];\n\n/**\n * Lifecycle of a versioned ReferralAgreement row.\n *\n * `draft → active | terminated`; `active → superseded | terminated`;\n * `superseded` / `terminated` are terminal.\n */\nexport const REFERRAL_AGREEMENT_STATUSES = [\n 'draft',\n 'active',\n 'superseded',\n 'terminated',\n] as const;\nexport type ReferralAgreementStatus =\n (typeof REFERRAL_AGREEMENT_STATUSES)[number];\n\n/**\n * How commissions earned under an agreement are approved downstream:\n * `manual` (an operator approves each earning) or `auto`. Recorded on the\n * agreement and frozen into every ReferralTermSnapshot.\n */\nexport const REFERRAL_AGREEMENT_APPROVAL_MODES = ['manual', 'auto'] as const;\nexport type ReferralAgreementApprovalMode =\n (typeof REFERRAL_AGREEMENT_APPROVAL_MODES)[number];\n\n// ---------------------------------------------------------------------------\n// Attribution-exception candidates\n// ---------------------------------------------------------------------------\n\n/**\n * One competing touch recorded in an AttributionException's `candidates`\n * JSON array — enough to review the conflict without re-querying touches.\n */\nexport interface AttributionExceptionCandidate {\n /** Id of the candidate ReferralTouch (empty for touchless awards). */\n touchId: string;\n /** The referrer the touch credits. */\n referrerId: string;\n /** Kind of the candidate touch. */\n kind: ReferralTouchKind;\n /** ISO-8601 timestamp of when the candidate touch occurred. */\n occurredAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Model constructor options\n// ---------------------------------------------------------------------------\n\n/** Options for constructing a Referrer. */\nexport interface ReferrerOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n profileId?: string;\n earnerId?: string;\n displayName?: string;\n status?: ReferrerStatus;\n metadata?: string;\n}\n\n/** Options for constructing a ReferralProgram. */\nexport interface ReferralProgramOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n key?: string;\n name?: string;\n status?: ReferralProgramStatus;\n defaultCommissionPlanKey?: string;\n defaultAttributionPolicyKey?: string;\n metadata?: string;\n}\n\n/** Options for constructing an AttributionPolicy. */\nexport interface AttributionPolicyOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n policyKey?: string;\n version?: number;\n status?: AttributionPolicyStatus;\n effectiveFrom?: Date | string | number | null;\n windowDays?: number;\n creditMode?: AttributionCreditMode;\n conflictBehavior?: AttributionConflictBehavior;\n allowSelfReferral?: boolean;\n allowExistingClients?: boolean;\n eligibleServices?: string;\n eligibleCampaigns?: string;\n eligibleRegions?: string;\n metadata?: string;\n}\n\n/** Options for constructing a ReferralLink. */\nexport interface ReferralLinkOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n referrerId?: string;\n programId?: string;\n code?: string;\n targetUrl?: string;\n label?: string;\n status?: ReferralLinkStatus;\n clickCount?: number;\n metadata?: string;\n}\n\n/** Options for constructing a ReferralTouch. */\nexport interface ReferralTouchOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n linkId?: string;\n code?: string;\n referrerId?: string;\n programId?: string;\n kind?: ReferralTouchKind;\n subjectKind?: string;\n subjectId?: string;\n occurredAt?: Date | string | number;\n evidence?: string;\n metadata?: string;\n}\n\n/** Options for constructing a Referral. */\nexport interface ReferralOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n referrerId?: string;\n programId?: string;\n policyKey?: string;\n policyVersion?: number;\n status?: ReferralStatus;\n targetKind?: string;\n targetId?: string;\n creditFraction?: number;\n splitGroupId?: string;\n primaryTouchId?: string;\n attributedAt?: Date | string | number | null;\n qualifiedAt?: Date | string | number | null;\n expiresAt?: Date | string | number | null;\n snapshotId?: string;\n metadata?: string;\n}\n\n/** Options for constructing an AttributionException. */\nexport interface AttributionExceptionOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n targetKind?: string;\n targetId?: string;\n programId?: string;\n status?: AttributionExceptionStatus;\n conflictReason?: string;\n candidates?: string;\n resolutionMode?: string;\n resolutionReason?: string;\n resolvedByProfileId?: string;\n resolvedReferralIds?: string;\n resolvedAt?: Date | string | number | null;\n metadata?: string;\n}\n\n/** Options for constructing a ReferralAgreement. */\nexport interface ReferralAgreementOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n referrerId?: string;\n programId?: string;\n version?: number;\n status?: ReferralAgreementStatus;\n effectiveFrom?: Date | string | number | null;\n effectiveTo?: Date | string | number | null;\n commissionPlanKey?: string;\n commissionPlanVersion?: number;\n clearingDays?: number;\n approvalMode?: ReferralAgreementApprovalMode;\n contractRef?: string;\n executedArtifactUrl?: string;\n executedArtifactHash?: string;\n acceptanceEvidence?: string;\n metadata?: string;\n}\n\n/** Options for constructing a ReferralTermSnapshot. */\nexport interface ReferralTermSnapshotOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n referralId?: string;\n agreementId?: string;\n agreementVersion?: number;\n planKey?: string;\n planVersion?: number;\n policyKey?: string;\n policyVersion?: number;\n components?: string;\n currency?: string;\n clearingDays?: number;\n approvalMode?: ReferralAgreementApprovalMode;\n metadata?: string;\n}\n","/**\n * AttributionPolicy — versioned attribution terms.\n *\n * A policy is identified by its `(policyKey, version)` natural key. Versions\n * are ROWS, not edits: amending a policy inserts a `version + 1` draft (see\n * `AttributionPolicyCollection.createAmendment`) and the prior version is\n * never rewritten. Once a row has been saved non-draft, its policy-defining\n * identity (window, credit mode, conflict behavior, eligibility flags and\n * lists, key/version, effectiveFrom) is frozen — re-saving with any of them\n * changed throws (same WeakMap-serialize pattern as commerce `LicenseSale`\n * and this package's `CommissionPlan`). Status transitions remain allowed on\n * frozen rows.\n *\n * Generated write surface is deliberately narrow: `create` only (drafts) —\n * amendments are new rows, and status moves go through the guarded\n * transition methods / legal save-time edges.\n *\n * @packageDocumentation\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n ATTRIBUTION_CONFLICT_BEHAVIORS,\n ATTRIBUTION_CREDIT_MODES,\n type AttributionConflictBehavior,\n type AttributionCreditMode,\n type AttributionPolicyOptions,\n type AttributionPolicyStatus,\n} from '../types.js';\n\n/**\n * Legal status transitions, keyed by the prior persisted status.\n * `draft → active | retired`; `active → superseded | retired`;\n * `superseded` / `retired` are terminal. No-op re-saves and brand-new rows\n * are always permitted; this map governs *changes* to persisted rows only.\n */\nconst POLICY_STATUS_TRANSITIONS: Record<\n AttributionPolicyStatus,\n AttributionPolicyStatus[]\n> = {\n draft: ['active', 'retired'],\n active: ['superseded', 'retired'],\n superseded: [],\n retired: [],\n};\n\n/**\n * Module-scoped record of the status each policy instance was loaded with —\n * fallback for the save-time transition guard when the DB re-read is\n * unavailable. WeakMap keeps it out of the schema and GCs with the instance.\n */\nconst loadedPolicyStatus = new WeakMap<\n AttributionPolicy,\n AttributionPolicyStatus\n>();\n\n/**\n * Module-scoped record of the frozen policy-identity snapshot captured when\n * a row is (or becomes) non-draft — same rationale as `CommissionPlan`.\n */\nconst frozenPolicySnapshot = new WeakMap<AttributionPolicy, string>();\n\n/**\n * Validate a policy's terms. Throws a descriptive error on the first\n * violation. Called by {@link AttributionPolicy.activate} and by activating\n * saves so no active policy can carry malformed terms.\n */\nexport function validateAttributionPolicyTerms(\n policy: AttributionPolicy,\n): void {\n if (!Number.isInteger(policy.windowDays) || policy.windowDays <= 0) {\n throw new Error(\n `AttributionPolicy ${policy.policyKey}@${policy.version}: windowDays must be a positive integer`,\n );\n }\n if (!ATTRIBUTION_CREDIT_MODES.includes(policy.creditMode)) {\n throw new Error(\n `AttributionPolicy ${policy.policyKey}@${policy.version}: invalid creditMode '${policy.creditMode}'`,\n );\n }\n if (!ATTRIBUTION_CONFLICT_BEHAVIORS.includes(policy.conflictBehavior)) {\n throw new Error(\n `AttributionPolicy ${policy.policyKey}@${policy.version}: invalid conflictBehavior '${policy.conflictBehavior}'`,\n );\n }\n for (const [name, raw] of [\n ['eligibleServices', policy.eligibleServices],\n ['eligibleCampaigns', policy.eligibleCampaigns],\n ['eligibleRegions', policy.eligibleRegions],\n ] as const) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw || '[]');\n } catch {\n throw new Error(\n `AttributionPolicy ${policy.policyKey}@${policy.version}: ${name} must be a JSON array of strings`,\n );\n }\n if (\n !Array.isArray(parsed) ||\n parsed.some((entry) => typeof entry !== 'string')\n ) {\n throw new Error(\n `AttributionPolicy ${policy.policyKey}@${policy.version}: ${name} must be a JSON array of strings`,\n );\n }\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // (tenantId, policyKey, version) is the natural key — a retried create\n // of the same version upserts instead of duplicating, and two tenants\n // can both own a policy key like 'default' without colliding.\n // NULL-tenant (global) rows opt out of upsert dedup on adapters where\n // NULLs compare distinct — the PaymentIntent natural-key convention.\n conflictColumns: ['tenant_id', 'policy_key', 'version'],\n // NO generated update route: amendments are new rows and status moves go\n // through the guarded transition methods.\n api: { include: ['list', 'get', 'create'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class AttributionPolicy extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global policies). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Stable policy identity shared by every version of the policy. */\n @field({ required: true })\n policyKey: string = '';\n\n /** Monotonic version within `policyKey`. Amendments insert `max + 1`. */\n version: number = 1;\n\n /**\n * Lifecycle status — see {@link POLICY_STATUS_TRANSITIONS}. Mutate via\n * {@link activate} / {@link supersede} / {@link retire} (or a legal\n * single-step assignment; the save-time guard rejects illegal edges).\n */\n status: AttributionPolicyStatus = 'draft';\n\n /** When this version takes effect. Frozen once the policy activates. */\n effectiveFrom: Date | null = null;\n\n /**\n * Attribution window in days: only touches occurring within `windowDays`\n * of the resolution instant are credit candidates, and attributed\n * referrals expire `windowDays` after attribution if never qualified.\n */\n windowDays: number = 30;\n\n /** How competing touches are credited. */\n creditMode: AttributionCreditMode = 'first_touch';\n\n /** Whether multi-referrer contention auto-resolves or forces review. */\n conflictBehavior: AttributionConflictBehavior = 'auto';\n\n /**\n * Whether a touch whose referrer IS the prospect (matching\n * `subjectProfileId`) may earn credit. Default false: self-referrals are\n * dropped from candidate sets.\n */\n allowSelfReferral: boolean = false;\n\n /**\n * Whether referrals of already-existing clients are eligible. Default\n * false: `AttributionService.resolve()` refuses outright when the caller\n * flags the prospect as an existing client.\n */\n allowExistingClients: boolean = false;\n\n /**\n * Eligible service keys as a JSON-string array — empty array means ALL\n * services are eligible. Interpretation belongs to the application layer\n * that maps its offerings onto service keys; this module records and\n * validates the list. Use {@link getEligibleServices}/{@link setEligibleServices}.\n */\n eligibleServices: string = '[]';\n\n /** Eligible campaign keys (JSON-string array; empty = all eligible). */\n eligibleCampaigns: string = '[]';\n\n /** Eligible region codes (JSON-string array; empty = all eligible). */\n eligibleRegions: string = '[]';\n\n /** Additional metadata as a JSON string (not part of the frozen identity). */\n metadata: string = '{}';\n\n constructor(options: AttributionPolicyOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.policyKey !== undefined) this.policyKey = options.policyKey;\n if (options.version !== undefined) this.version = options.version;\n if (options.status !== undefined) this.status = options.status;\n if (options.effectiveFrom !== undefined)\n this.effectiveFrom = AttributionPolicy.coerceDate(options.effectiveFrom);\n if (options.windowDays !== undefined) this.windowDays = options.windowDays;\n if (options.creditMode !== undefined) this.creditMode = options.creditMode;\n if (options.conflictBehavior !== undefined)\n this.conflictBehavior = options.conflictBehavior;\n if (options.allowSelfReferral !== undefined)\n this.allowSelfReferral = options.allowSelfReferral;\n if (options.allowExistingClients !== undefined)\n this.allowExistingClients = options.allowExistingClients;\n if (options.eligibleServices !== undefined)\n this.eligibleServices = options.eligibleServices;\n if (options.eligibleCampaigns !== undefined)\n this.eligibleCampaigns = options.eligibleCampaigns;\n if (options.eligibleRegions !== undefined)\n this.eligibleRegions = options.eligibleRegions;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Re-coerce date fields after the framework reapplies raw option values,\n * record the loaded status for the transition guard, and capture the\n * frozen snapshot when the row arrived already non-draft (superseded and\n * retired versions — history — can't be rewritten either).\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.effectiveFrom = AttributionPolicy.coerceDate(this.effectiveFrom);\n if (await this.isSaved()) {\n loadedPolicyStatus.set(this, this.status);\n if (this.status !== 'draft') {\n frozenPolicySnapshot.set(this, this.serializeFrozenSnapshot());\n }\n }\n return this;\n }\n\n // -------- Status predicates --------\n\n isDraft(): boolean {\n return this.status === 'draft';\n }\n\n isActive(): boolean {\n return this.status === 'active';\n }\n\n // -------- Eligibility-list / metadata helpers --------\n\n /** Parse {@link eligibleServices}; returns `[]` on empty/invalid JSON. */\n getEligibleServices(): string[] {\n return AttributionPolicy.parseStringArray(this.eligibleServices);\n }\n\n /** Serialize and store {@link eligibleServices}. */\n setEligibleServices(services: string[]): void {\n this.eligibleServices = JSON.stringify(services ?? []);\n }\n\n /** Parse {@link eligibleCampaigns}; returns `[]` on empty/invalid JSON. */\n getEligibleCampaigns(): string[] {\n return AttributionPolicy.parseStringArray(this.eligibleCampaigns);\n }\n\n /** Serialize and store {@link eligibleCampaigns}. */\n setEligibleCampaigns(campaigns: string[]): void {\n this.eligibleCampaigns = JSON.stringify(campaigns ?? []);\n }\n\n /** Parse {@link eligibleRegions}; returns `[]` on empty/invalid JSON. */\n getEligibleRegions(): string[] {\n return AttributionPolicy.parseStringArray(this.eligibleRegions);\n }\n\n /** Serialize and store {@link eligibleRegions}. */\n setEligibleRegions(regions: string[]): void {\n this.eligibleRegions = JSON.stringify(regions ?? []);\n }\n\n /** Parse {@link metadata}; returns `{}` on empty/invalid JSON. */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n const parsed = JSON.parse(this.metadata) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link metadata}. */\n setMetadata(data: Record<string, unknown>): void {\n this.metadata = JSON.stringify(data ?? {});\n }\n\n // -------- Status transitions --------\n\n /**\n * Transition `draft → active`. Validates the terms first so no active\n * policy can carry malformed window/mode/eligibility values.\n */\n activate(): void {\n if (this.status !== 'draft') {\n throw new Error(\n `AttributionPolicy ${this.policyKey}@${this.version}: cannot activate from status '${this.status}'`,\n );\n }\n validateAttributionPolicyTerms(this);\n this.status = 'active';\n }\n\n /** Transition `active → superseded` (a newer version took over). */\n supersede(): void {\n if (this.status !== 'active') {\n throw new Error(\n `AttributionPolicy ${this.policyKey}@${this.version}: cannot supersede from status '${this.status}'`,\n );\n }\n this.status = 'superseded';\n }\n\n /** Transition `draft | active → retired` (terminal). */\n retire(): void {\n if (this.status !== 'draft' && this.status !== 'active') {\n throw new Error(\n `AttributionPolicy ${this.policyKey}@${this.version}: cannot retire from status '${this.status}'`,\n );\n }\n this.status = 'retired';\n }\n\n // -------- Save-time guards --------\n\n /**\n * Save with two guards (CommissionPlan pattern):\n *\n * 1. **Status transition** — the about-to-be-written status must be a\n * legal edge from the authoritative prior persisted status (re-read\n * from the DB so a `create({ id: <existing>, _skipLoad: true })` upsert\n * can't sidestep the guard).\n * 2. **Frozen policy identity** — once the row has been saved non-draft,\n * every policy-defining field must match the captured snapshot. Amend\n * by inserting a new version instead.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n this.assertFrozenIdentityUnchanged();\n await this.assertNaturalKeyNotTaken();\n if (this.status === 'active') {\n validateAttributionPolicyTerms(this);\n }\n const result = (await super.save()) as this;\n loadedPolicyStatus.set(this, this.status);\n if (this.status !== 'draft' && !frozenPolicySnapshot.has(this)) {\n frozenPolicySnapshot.set(this, this.serializeFrozenSnapshot());\n }\n return result;\n }\n\n /**\n * Refuse a save whose `(tenantId, policyKey, version)` natural key\n * already belongs to a DIFFERENT row — the frozen-identity guard is\n * instance-local, so a fresh instance would otherwise upsert over the\n * persisted policy (rewriting attribution rules and rotating the row\n * id). Edit drafts by hydrating them; change terms with\n * `AttributionPolicyCollection.createAmendment()`.\n */\n private async assertNaturalKeyNotTaken(): Promise<void> {\n if (!this.policyKey) return;\n try {\n const res = await this.db.query(\n `SELECT id, tenant_id FROM ${this.tableName} WHERE policy_key = $1 AND version = $2`,\n this.policyKey,\n this.version,\n );\n const rows = Array.isArray(res)\n ? (res as Record<string, unknown>[])\n : ((res as { rows?: Record<string, unknown>[] }).rows ?? []);\n const taken = rows.find(\n (row) =>\n (row.tenant_id ?? null) === (this.tenantId ?? null) &&\n row.id !== this.id,\n );\n if (taken) {\n throw new Error(\n `AttributionPolicy ${this.policyKey}@${this.version}: this ` +\n 'version already exists for the tenant — policy versions are ' +\n 'immutable records. Hydrate the existing row to edit a draft, ' +\n 'or create new terms with ' +\n 'AttributionPolicyCollection.createAmendment().',\n );\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('immutable')) {\n throw error;\n }\n // DB not ready / table absent — nothing persisted to collide with.\n }\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status from the database; fall back to\n * the loaded-status WeakMap only when the DB is unavailable. `undefined`\n * means no persisted row exists (genuinely new).\n */\n private async resolvePriorStatus(): Promise<\n AttributionPolicyStatus | undefined\n > {\n if (this.id) {\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row && row.status != null) {\n return row.status as AttributionPolicyStatus;\n }\n } catch {\n // DB not ready — fall through to the in-memory record.\n }\n }\n return loadedPolicyStatus.get(this);\n }\n\n private assertStatusTransition(\n prior: AttributionPolicyStatus | undefined,\n ): void {\n if (prior === undefined) return; // new row — any starting status\n if (prior === this.status) return; // no-op re-save\n const allowed = POLICY_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `AttributionPolicy ${this.policyKey}@${this.version}: illegal status ` +\n `transition '${prior}' → '${this.status}'. Use activate() / ` +\n 'supersede() / retire().',\n );\n }\n }\n\n private assertFrozenIdentityUnchanged(): void {\n const captured = frozenPolicySnapshot.get(this);\n if (!captured) return;\n const current = this.serializeFrozenSnapshot();\n if (captured !== current) {\n throw new Error(\n `AttributionPolicy ${this.policyKey}@${this.version}: the ` +\n 'policy-defining fields are immutable once the policy has been ' +\n 'active. Create an amendment ' +\n '(AttributionPolicyCollection.createAmendment) instead of editing ' +\n 'this version.',\n );\n }\n }\n\n private serializeFrozenSnapshot(): string {\n // Stable key ordering so a no-op re-serialization matches. Booleans are\n // normalized through Boolean() so an adapter hydrating 0/1 never trips\n // the guard against a JS false/true assignment of the same value.\n return JSON.stringify({\n policyKey: this.policyKey,\n version: this.version,\n windowDays: this.windowDays,\n creditMode: this.creditMode,\n conflictBehavior: this.conflictBehavior,\n allowSelfReferral: Boolean(this.allowSelfReferral),\n allowExistingClients: Boolean(this.allowExistingClients),\n eligibleServices: this.eligibleServices,\n eligibleCampaigns: this.eligibleCampaigns,\n eligibleRegions: this.eligibleRegions,\n effectiveFrom: this.effectiveFrom\n ? this.effectiveFrom.toISOString()\n : null,\n });\n }\n\n private static parseStringArray(raw: string): string[] {\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter((entry): entry is string => typeof entry === 'string')\n : [];\n } catch {\n return [];\n }\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default AttributionPolicy;\n","/**\n * AttributionPolicyCollection — collection manager for\n * {@link AttributionPolicy}.\n *\n * Policies are versioned rows: amendments insert `(policyKey, maxVersion +\n * 1)` drafts via {@link createAmendment}; existing versions are never\n * rewritten (CommissionPlan pattern).\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AttributionPolicy } from '../models/AttributionPolicy.js';\nimport type {\n AttributionConflictBehavior,\n AttributionCreditMode,\n AttributionPolicyStatus,\n} from '../types.js';\n\n/**\n * Fields an amendment may change relative to the version it copies.\n * `policyKey` is fixed (it identifies the policy), `version` is computed,\n * and `status` is always `draft` — a caller cannot mint a pre-activated\n * amendment.\n */\nexport interface AttributionPolicyAmendmentChanges {\n windowDays?: number;\n creditMode?: AttributionCreditMode;\n conflictBehavior?: AttributionConflictBehavior;\n allowSelfReferral?: boolean;\n allowExistingClients?: boolean;\n eligibleServices?: string[];\n eligibleCampaigns?: string[];\n eligibleRegions?: string[];\n effectiveFrom?: Date | null;\n metadata?: Record<string, unknown>;\n}\n\nexport class AttributionPolicyCollection extends SmrtCollection<AttributionPolicy> {\n static readonly _itemClass = AttributionPolicy;\n\n /** Every version of a policy, newest version first. */\n async findByPolicyKey(policyKey: string): Promise<AttributionPolicy[]> {\n return await this.list({\n where: { policyKey },\n orderBy: 'version DESC',\n });\n }\n\n /** Policies by status. */\n async findByStatus(\n status: AttributionPolicyStatus,\n ): Promise<AttributionPolicy[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * The highest ACTIVE version of a policy already IN EFFECT at `at`, or\n * `null` when none is. This is what `AttributionService.resolve()` loads\n * when the caller doesn't pin a version. A future-dated amendment can be\n * activated ahead of its effective date without governing attributions\n * resolved before it takes effect (`effectiveFrom: null` = effective\n * immediately).\n */\n async latestActiveByKey(\n policyKey: string,\n at: Date = new Date(),\n tenantId?: string | null,\n ): Promise<AttributionPolicy | null> {\n const results = await this.list({\n where: { policyKey, status: 'active' },\n orderBy: 'version DESC',\n });\n const inEffect = results.filter(\n (policy) => policy.effectiveFrom === null || policy.effectiveFrom <= at,\n );\n if (tenantId === undefined) {\n // No explicit scope: ambient tenant scoping (when present) applies.\n return inEffect[0] ?? null;\n }\n // Explicit scope (system/background paths run without ambient tenant\n // context): the tenant's own versions form their own key-space; global\n // (NULL-tenant) versions are the fallback. Never another tenant's.\n return (\n inEffect.find((policy) => policy.tenantId === tenantId) ??\n inEffect.find((policy) => policy.tenantId === null) ??\n null\n );\n }\n\n /**\n * Create an amendment: insert a new DRAFT row with\n * `version = max(existing versions) + 1`, copying the latest existing\n * version's fields and then applying `changes`. The source version is not\n * touched — activate the draft (and supersede the prior active version)\n * as a separate, explicit step.\n *\n * Throws when no version of `policyKey` exists (nothing to amend — use\n * `create` for a brand-new policy).\n */\n async createAmendment(\n policyKey: string,\n changes: AttributionPolicyAmendmentChanges = {},\n ): Promise<AttributionPolicy> {\n const versions = await this.findByPolicyKey(policyKey);\n const latest = versions[0];\n if (!latest) {\n throw new Error(\n `AttributionPolicyCollection.createAmendment: no versions exist for policy key '${policyKey}' — create the policy first`,\n );\n }\n\n const draft = await this.create({\n tenantId: latest.tenantId,\n policyKey,\n version: latest.version + 1,\n status: 'draft',\n windowDays: changes.windowDays ?? latest.windowDays,\n creditMode: changes.creditMode ?? latest.creditMode,\n conflictBehavior: changes.conflictBehavior ?? latest.conflictBehavior,\n allowSelfReferral: changes.allowSelfReferral ?? latest.allowSelfReferral,\n allowExistingClients:\n changes.allowExistingClients ?? latest.allowExistingClients,\n eligibleServices:\n changes.eligibleServices !== undefined\n ? JSON.stringify(changes.eligibleServices)\n : latest.eligibleServices,\n eligibleCampaigns:\n changes.eligibleCampaigns !== undefined\n ? JSON.stringify(changes.eligibleCampaigns)\n : latest.eligibleCampaigns,\n eligibleRegions:\n changes.eligibleRegions !== undefined\n ? JSON.stringify(changes.eligibleRegions)\n : latest.eligibleRegions,\n effectiveFrom:\n changes.effectiveFrom !== undefined\n ? changes.effectiveFrom\n : latest.effectiveFrom,\n metadata:\n changes.metadata !== undefined\n ? JSON.stringify(changes.metadata)\n : latest.metadata,\n });\n return draft;\n }\n}\n\nexport default AttributionPolicyCollection;\n","/**\n * ReferralAgreement — versioned per-Referrer terms binding.\n *\n * An agreement binds one Referrer to one ReferralProgram under specific\n * commission terms: which CommissionPlan key (optionally pinned to a\n * version), the clearing window, and the approval mode. Natural key\n * `(referrerId, programId, version)` — amendments insert `version + 1`\n * drafts (see `ReferralAgreementCollection.createAmendment`); prior versions\n * are never rewritten.\n *\n * Lifecycle: `draft → active | terminated`; `active → superseded |\n * terminated`; `superseded` / `terminated` are terminal. Activation requires\n * a non-empty {@link commissionPlanKey}.\n *\n * Immutability: once a row has been saved non-draft, its TERMS\n * (`referrerId`, `programId`, `version`, `commissionPlanKey`,\n * `commissionPlanVersion`, `clearingDays`, `approvalMode`, `effectiveFrom`)\n * are frozen via the WeakMap-serialize guard (commerce `LicenseSale`\n * pattern). Status transitions stay allowed, `effectiveTo` stays mutable so\n * an operator can end-date an agreement, and the execution-evidence fields\n * ({@link contractRef}, {@link executedArtifactUrl},\n * {@link executedArtifactHash}, {@link acceptanceEvidence}) stay mutable —\n * e-signature execution completes downstream and its artifacts may arrive\n * after activation. These are reference/evidence fields only.\n *\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ReferralAgreementApprovalMode,\n ReferralAgreementOptions,\n ReferralAgreementStatus,\n} from '../types.js';\n\n/**\n * Legal status transitions, keyed by the prior persisted status. No-op\n * re-saves and brand-new rows are always permitted; this map governs\n * *changes* to persisted rows only.\n */\nconst AGREEMENT_STATUS_TRANSITIONS: Record<\n ReferralAgreementStatus,\n ReferralAgreementStatus[]\n> = {\n draft: ['active', 'terminated'],\n active: ['superseded', 'terminated'],\n superseded: [],\n terminated: [],\n};\n\n/** Loaded-status record for the save-time transition guard (fallback). */\nconst loadedAgreementStatus = new WeakMap<\n ReferralAgreement,\n ReferralAgreementStatus\n>();\n\n/** Frozen terms snapshot, captured when a row is (or becomes) non-draft. */\nconst frozenAgreementSnapshot = new WeakMap<ReferralAgreement, string>();\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // (referrerId, programId, version) is the natural key — a retried create\n // of the same version upserts instead of duplicating.\n conflictColumns: ['referrer_id', 'program_id', 'version'],\n // NO generated update route: amendments are new rows and status moves go\n // through the guarded transition methods.\n api: { include: ['list', 'get', 'create'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ReferralAgreement extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global agreements). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The Referrer bound by this agreement. Required. */\n @foreignKey('Referrer', { required: true })\n referrerId: string = '';\n\n /** The ReferralProgram the agreement applies to. Required. */\n @foreignKey('ReferralProgram', { required: true })\n programId: string = '';\n\n /** Monotonic version within `(referrerId, programId)`. */\n version: number = 1;\n\n /**\n * Lifecycle status — see the module transition map. Mutate via\n * {@link activate} / {@link supersede} / {@link terminate}.\n */\n status: ReferralAgreementStatus = 'draft';\n\n /** Start of the effective window (`null` = effective immediately). */\n effectiveFrom: Date | null = null;\n\n /**\n * End of the effective window (`null` = open-ended). Deliberately NOT\n * frozen: end-dating an active agreement is a legitimate operation.\n */\n effectiveTo: Date | null = null;\n\n /**\n * The commissions-module CommissionPlan key these terms bind. Required to\n * activate.\n */\n commissionPlanKey: string = '';\n\n /**\n * Pinned plan version. `0` (default) means \"resolve the latest ACTIVE\n * version at qualification time and pin it in the ReferralTermSnapshot\";\n * `> 0` pins a specific version now.\n */\n commissionPlanVersion: number = 0;\n\n /**\n * Clearing window in days applied to commissions earned under these\n * terms (refund/chargeback holdback). Frozen into every snapshot.\n */\n clearingDays: number = 0;\n\n /** How earned commissions are approved downstream. */\n approvalMode: ReferralAgreementApprovalMode = 'manual';\n\n /**\n * Optional cross-package string reference to a smrt-commerce Contract\n * representing the executed legal agreement.\n */\n @crossPackageRef('@happyvertical/smrt-commerce:Contract')\n contractRef: string = '';\n\n /** URL of the executed agreement artifact (e-signature PDF, …). */\n executedArtifactUrl: string = '';\n\n /** Content hash of the executed artifact for tamper evidence. */\n executedArtifactHash: string = '';\n\n /**\n * Acceptance evidence as a JSON object string (click-through record, IP,\n * timestamp, signer, …). Use\n * {@link getAcceptanceEvidence}/{@link setAcceptanceEvidence}.\n */\n acceptanceEvidence: string = '{}';\n\n /**\n * Free-form JSON object stored as a string. Use\n * {@link getMetadata}/{@link setMetadata} instead of parsing manually.\n */\n metadata: string = '{}';\n\n constructor(options: ReferralAgreementOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.referrerId !== undefined) this.referrerId = options.referrerId;\n if (options.programId !== undefined) this.programId = options.programId;\n if (options.version !== undefined) this.version = options.version;\n if (options.status !== undefined) this.status = options.status;\n if (options.effectiveFrom !== undefined)\n this.effectiveFrom = ReferralAgreement.coerceDate(options.effectiveFrom);\n if (options.effectiveTo !== undefined)\n this.effectiveTo = ReferralAgreement.coerceDate(options.effectiveTo);\n if (options.commissionPlanKey !== undefined)\n this.commissionPlanKey = options.commissionPlanKey;\n if (options.commissionPlanVersion !== undefined)\n this.commissionPlanVersion = options.commissionPlanVersion;\n if (options.clearingDays !== undefined)\n this.clearingDays = options.clearingDays;\n if (options.approvalMode !== undefined)\n this.approvalMode = options.approvalMode;\n if (options.contractRef !== undefined)\n this.contractRef = options.contractRef;\n if (options.executedArtifactUrl !== undefined)\n this.executedArtifactUrl = options.executedArtifactUrl;\n if (options.executedArtifactHash !== undefined)\n this.executedArtifactHash = options.executedArtifactHash;\n if (options.acceptanceEvidence !== undefined)\n this.acceptanceEvidence = options.acceptanceEvidence;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Re-coerce date fields after the framework reapplies raw option values,\n * record the loaded status for the transition guard, and capture the\n * frozen terms snapshot when the row arrived already non-draft\n * (superseded / terminated history can't be rewritten either).\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.effectiveFrom = ReferralAgreement.coerceDate(this.effectiveFrom);\n this.effectiveTo = ReferralAgreement.coerceDate(this.effectiveTo);\n if (await this.isSaved()) {\n loadedAgreementStatus.set(this, this.status);\n if (this.status !== 'draft') {\n frozenAgreementSnapshot.set(this, this.serializeFrozenTerms());\n }\n }\n return this;\n }\n\n // -------- Status predicates --------\n\n isDraft(): boolean {\n return this.status === 'draft';\n }\n\n isActive(): boolean {\n return this.status === 'active';\n }\n\n /**\n * Whether the effective window contains `at` (`effectiveFrom` null =\n * always started; `effectiveTo` null = open-ended). Status is NOT part of\n * this check — combine with {@link isActive}.\n */\n isEffectiveAt(at: Date): boolean {\n if (this.effectiveFrom && at.getTime() < this.effectiveFrom.getTime()) {\n return false;\n }\n if (this.effectiveTo && at.getTime() > this.effectiveTo.getTime()) {\n return false;\n }\n return true;\n }\n\n // -------- JSON helpers --------\n\n /** Parse {@link acceptanceEvidence}; returns `{}` on malformed content. */\n getAcceptanceEvidence(): Record<string, unknown> {\n if (!this.acceptanceEvidence) return {};\n try {\n const parsed = JSON.parse(this.acceptanceEvidence) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link acceptanceEvidence}. */\n setAcceptanceEvidence(evidence: Record<string, unknown>): void {\n this.acceptanceEvidence = JSON.stringify(evidence ?? {});\n }\n\n /** Parse {@link metadata}; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n const parsed = JSON.parse(this.metadata) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link metadata}. */\n setMetadata(data: Record<string, unknown>): void {\n this.metadata = JSON.stringify(data ?? {});\n }\n\n // -------- Status transitions --------\n\n /**\n * Transition `draft → active`. Requires a non-empty\n * {@link commissionPlanKey} — an agreement without bound terms cannot\n * govern qualification.\n */\n activate(): void {\n if (this.status !== 'draft') {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: cannot activate from status '${this.status}'`,\n );\n }\n if (!this.commissionPlanKey) {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: commissionPlanKey is required to activate`,\n );\n }\n this.status = 'active';\n }\n\n /** Transition `active → superseded` (a newer version took over). */\n supersede(): void {\n if (this.status !== 'active') {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: cannot supersede from status '${this.status}'`,\n );\n }\n this.status = 'superseded';\n }\n\n /** Transition `draft | active → terminated` (terminal). */\n terminate(): void {\n if (this.status !== 'draft' && this.status !== 'active') {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: cannot terminate from status '${this.status}'`,\n );\n }\n this.status = 'terminated';\n }\n\n // -------- Save-time guards --------\n\n /**\n * Save with two guards (CommissionPlan pattern):\n *\n * 1. **Status transition** — the about-to-be-written status must be a\n * legal edge from the authoritative prior persisted status.\n * 2. **Frozen terms** — once the row has been saved non-draft, the terms\n * fields must match the captured snapshot. Amend by inserting a new\n * version instead.\n *\n * Activating saves also re-require {@link commissionPlanKey}.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n this.assertFrozenTermsUnchanged();\n await this.assertNaturalKeyNotTaken();\n if (this.status === 'active' && !this.commissionPlanKey) {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: commissionPlanKey is required while active`,\n );\n }\n const result = (await super.save()) as this;\n loadedAgreementStatus.set(this, this.status);\n if (this.status !== 'draft' && !frozenAgreementSnapshot.has(this)) {\n frozenAgreementSnapshot.set(this, this.serializeFrozenTerms());\n }\n return result;\n }\n\n /**\n * Refuse a save whose `(referrerId, programId, version)` natural key\n * already belongs to a DIFFERENT row — the frozen-terms guard is\n * instance-local, so a fresh instance would otherwise upsert over the\n * persisted agreement (replacing legally frozen referral terms without\n * an amendment, and rotating the row id). Edit drafts by hydrating\n * them; change terms with\n * `ReferralAgreementCollection.createAmendment()`.\n */\n private async assertNaturalKeyNotTaken(): Promise<void> {\n if (!this.referrerId || !this.programId) return;\n try {\n const res = await this.db.query(\n `SELECT id FROM ${this.tableName} WHERE referrer_id = $1 AND program_id = $2 AND version = $3`,\n this.referrerId,\n this.programId,\n this.version,\n );\n const rows = Array.isArray(res)\n ? (res as Record<string, unknown>[])\n : ((res as { rows?: Record<string, unknown>[] }).rows ?? []);\n const taken = rows.find((row) => row.id !== this.id);\n if (taken) {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: ` +\n 'this version already exists — agreement versions are ' +\n 'immutable records. Hydrate the existing row to edit a draft, ' +\n 'or create new terms with ' +\n 'ReferralAgreementCollection.createAmendment().',\n );\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('immutable')) {\n throw error;\n }\n // DB not ready / table absent — nothing persisted to collide with.\n }\n }\n\n private async resolvePriorStatus(): Promise<\n ReferralAgreementStatus | undefined\n > {\n if (this.id) {\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row && row.status != null) {\n return row.status as ReferralAgreementStatus;\n }\n } catch {\n // DB not ready — fall through to the in-memory record.\n }\n }\n return loadedAgreementStatus.get(this);\n }\n\n private assertStatusTransition(\n prior: ReferralAgreementStatus | undefined,\n ): void {\n if (prior === undefined) return; // new row — any starting status\n if (prior === this.status) return; // no-op re-save\n const allowed = AGREEMENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: ` +\n `illegal status transition '${prior}' → '${this.status}'. Use ` +\n 'activate() / supersede() / terminate().',\n );\n }\n }\n\n private assertFrozenTermsUnchanged(): void {\n const captured = frozenAgreementSnapshot.get(this);\n if (!captured) return;\n const current = this.serializeFrozenTerms();\n if (captured !== current) {\n throw new Error(\n `ReferralAgreement ${this.referrerId}/${this.programId}@${this.version}: ` +\n 'terms are immutable once the agreement has been active. Create ' +\n 'an amendment (ReferralAgreementCollection.createAmendment) ' +\n 'instead of editing this version.',\n );\n }\n }\n\n private serializeFrozenTerms(): string {\n // Stable key ordering so a no-op re-serialization matches.\n return JSON.stringify({\n referrerId: this.referrerId,\n programId: this.programId,\n version: this.version,\n commissionPlanKey: this.commissionPlanKey,\n commissionPlanVersion: this.commissionPlanVersion,\n clearingDays: this.clearingDays,\n approvalMode: this.approvalMode,\n effectiveFrom: this.effectiveFrom\n ? this.effectiveFrom.toISOString()\n : null,\n });\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default ReferralAgreement;\n","/**\n * ReferralAgreementCollection — collection manager for\n * {@link ReferralAgreement}.\n *\n * Agreements are versioned rows: amendments insert\n * `(referrerId, programId, maxVersion + 1)` drafts via\n * {@link createAmendment}; existing versions are never rewritten.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ReferralAgreement } from '../models/ReferralAgreement.js';\nimport type { ReferralAgreementApprovalMode } from '../types.js';\n\n/**\n * Fields an amendment may change relative to the version it copies.\n * `referrerId`/`programId` are fixed (they identify the agreement),\n * `version` is computed, and `status` is always `draft`.\n */\nexport interface ReferralAgreementAmendmentChanges {\n commissionPlanKey?: string;\n commissionPlanVersion?: number;\n clearingDays?: number;\n approvalMode?: ReferralAgreementApprovalMode;\n effectiveFrom?: Date | null;\n effectiveTo?: Date | null;\n contractRef?: string;\n executedArtifactUrl?: string;\n executedArtifactHash?: string;\n acceptanceEvidence?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\nexport class ReferralAgreementCollection extends SmrtCollection<ReferralAgreement> {\n static readonly _itemClass = ReferralAgreement;\n\n /** Every version of one referrer+program agreement, newest version first. */\n async findByReferrerAndProgram(\n referrerId: string,\n programId: string,\n ): Promise<ReferralAgreement[]> {\n return await this.list({\n where: { referrerId, programId },\n orderBy: 'version DESC',\n });\n }\n\n /**\n * The agreement governing `(referrerId, programId)` at instant `at`\n * (default now): status `active` AND effective window containing `at`\n * (`effectiveFrom` null = always started; `effectiveTo` null =\n * open-ended). When several active versions overlap — transitional states\n * during amendment activation — the highest version wins. `null` when\n * nothing governs.\n */\n async activeFor(\n referrerId: string,\n programId: string,\n at: Date = new Date(),\n ): Promise<ReferralAgreement | null> {\n const active = await this.list({\n where: { referrerId, programId, status: 'active' },\n orderBy: 'version DESC',\n });\n return active.find((agreement) => agreement.isEffectiveAt(at)) ?? null;\n }\n\n /**\n * Create an amendment: insert a new DRAFT row with\n * `version = max(existing versions) + 1`, copying the latest existing\n * version's fields and then applying `changes`. The source version is not\n * touched — activate the draft (and supersede the prior active version)\n * as a separate, explicit step.\n *\n * Throws when no version exists for the pair (nothing to amend — use\n * `create` for a brand-new agreement).\n */\n async createAmendment(\n referrerId: string,\n programId: string,\n changes: ReferralAgreementAmendmentChanges = {},\n ): Promise<ReferralAgreement> {\n const versions = await this.findByReferrerAndProgram(referrerId, programId);\n const latest = versions[0];\n if (!latest) {\n throw new Error(\n `ReferralAgreementCollection.createAmendment: no versions exist for referrer '${referrerId}' in program '${programId}' — create the agreement first`,\n );\n }\n\n const draft = await this.create({\n tenantId: latest.tenantId,\n referrerId,\n programId,\n version: latest.version + 1,\n status: 'draft',\n commissionPlanKey: changes.commissionPlanKey ?? latest.commissionPlanKey,\n commissionPlanVersion:\n changes.commissionPlanVersion ?? latest.commissionPlanVersion,\n clearingDays: changes.clearingDays ?? latest.clearingDays,\n approvalMode: changes.approvalMode ?? latest.approvalMode,\n effectiveFrom:\n changes.effectiveFrom !== undefined\n ? changes.effectiveFrom\n : latest.effectiveFrom,\n effectiveTo:\n changes.effectiveTo !== undefined\n ? changes.effectiveTo\n : latest.effectiveTo,\n contractRef: changes.contractRef ?? latest.contractRef,\n executedArtifactUrl:\n changes.executedArtifactUrl ?? latest.executedArtifactUrl,\n executedArtifactHash:\n changes.executedArtifactHash ?? latest.executedArtifactHash,\n acceptanceEvidence:\n changes.acceptanceEvidence !== undefined\n ? JSON.stringify(changes.acceptanceEvidence)\n : latest.acceptanceEvidence,\n metadata:\n changes.metadata !== undefined\n ? JSON.stringify(changes.metadata)\n : latest.metadata,\n });\n return draft;\n }\n}\n\nexport default ReferralAgreementCollection;\n","/**\n * Referral — the introduction record.\n *\n * A Referral says \"this referrer introduced this target under this program\"\n * — the target is a GENERIC `(targetKind, targetId)` pair (lead,\n * opportunity, client, project, subscription, …) so the referrals module\n * never depends on CRM or any downstream domain.\n *\n * Lifecycle (save-time guarded against the AUTHORITATIVE prior persisted\n * status — commerce pattern):\n *\n * - `pending → attributed | under_review | disqualified | expired`\n * - `under_review → attributed | disqualified`\n * - `attributed → qualified | disqualified | expired`\n * - `qualified → disqualified` (rare clawback path)\n * - `disqualified` / `expired` are terminal.\n *\n * Transition methods ({@link markAttributed} / {@link markUnderReview} /\n * {@link markQualified} / {@link disqualify} / {@link markExpired}) mutate\n * and stamp timestamps but DO NOT save — the caller saves (one explicit\n * persistence point per mutation, matching the sibling modules).\n *\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ReferralOptions, ReferralStatus } from '../types.js';\n\n/**\n * Legal status transitions, keyed by the prior persisted status. No-op\n * re-saves and brand-new rows are always permitted; this map governs\n * *changes* to persisted rows only.\n */\nconst REFERRAL_STATUS_TRANSITIONS: Record<ReferralStatus, ReferralStatus[]> = {\n pending: ['attributed', 'under_review', 'disqualified', 'expired'],\n under_review: ['attributed', 'disqualified'],\n attributed: ['qualified', 'disqualified', 'expired'],\n qualified: ['disqualified'],\n disqualified: [],\n expired: [],\n};\n\n/**\n * Module-scoped record of the status each Referral instance was loaded with\n * — fallback for the save-time transition guard when the DB re-read is\n * unavailable. WeakMap keeps it out of the schema and GCs with the instance.\n */\nconst loadedReferralStatus = new WeakMap<Referral, ReferralStatus>();\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // No generated update route: lifecycle mutations go through the guarded\n // transition methods and the attribution/qualification services.\n api: { include: ['list', 'get', 'create'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Referral extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global referrals). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The Referrer credited with the introduction. Required. */\n @foreignKey('Referrer', { required: true })\n referrerId: string = '';\n\n /** The ReferralProgram the referral belongs to. Required. */\n @foreignKey('ReferralProgram', { required: true })\n programId: string = '';\n\n /** AttributionPolicy key resolved at attribution ('' until attributed). */\n policyKey: string = '';\n\n /** AttributionPolicy version resolved at attribution (0 until attributed). */\n policyVersion: number = 0;\n\n /**\n * Lifecycle status — see the module transition map. Mutate via the\n * transition methods; the save-time guard rejects illegal edges.\n */\n status: ReferralStatus = 'pending';\n\n /**\n * Generic qualifying target: what the introduction became/points at —\n * `('lead', <id>)`, `('opportunity', <id>)`, `('client', <id>)`,\n * `('subscription', <id>)`, … This module attaches no semantics to it.\n */\n targetKind: string = '';\n\n /** Identifier within the {@link targetKind} namespace. */\n targetId: string = '';\n\n /**\n * Credit share (0–1) this referral carries. `1.0` for sole attribution;\n * split attribution creates sibling Referrals whose fractions sum to 1.0\n * sharing one {@link splitGroupId}. Flows into Commission\n * `shareFraction` at earning time.\n */\n creditFraction: number = 1.0;\n\n /** Groups the sibling referrals of one split. Empty for unsplit rows. */\n splitGroupId: string = '';\n\n /** The winning ReferralTouch behind the attribution ('' for touchless awards). */\n @foreignKey('ReferralTouch')\n primaryTouchId: string = '';\n\n /** When the referral was attributed. */\n attributedAt: Date | null = null;\n\n /** When the referral was qualified. */\n qualifiedAt: Date | null = null;\n\n /**\n * When an attributed-but-unqualified referral lapses. Stamped at\n * attribution (`attributedAt + policy.windowDays`); enforced by\n * `ReferralCollection.sweepExpired()`.\n */\n expiresAt: Date | null = null;\n\n /**\n * The ReferralTermSnapshot frozen at qualification ('' until qualified).\n * Requalification repoints this at a NEW snapshot; old snapshots remain\n * as history.\n *\n * Deliberately a PLAIN string reference (not `@foreignKey`): the\n * snapshot's own `referralId` FK carries the relationship, and declaring\n * both directions would put a cycle in the model graph (breaking the\n * registry's topological initialization order). Services fetch the\n * snapshot by id explicitly.\n */\n snapshotId: string = '';\n\n /**\n * Free-form JSON object stored as a string. Use\n * {@link getMetadata}/{@link setMetadata} instead of parsing manually.\n */\n metadata: string = '{}';\n\n constructor(options: ReferralOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.referrerId !== undefined) this.referrerId = options.referrerId;\n if (options.programId !== undefined) this.programId = options.programId;\n if (options.policyKey !== undefined) this.policyKey = options.policyKey;\n if (options.policyVersion !== undefined)\n this.policyVersion = options.policyVersion;\n if (options.status !== undefined) this.status = options.status;\n if (options.targetKind !== undefined) this.targetKind = options.targetKind;\n if (options.targetId !== undefined) this.targetId = options.targetId;\n if (options.creditFraction !== undefined)\n this.creditFraction = options.creditFraction;\n if (options.splitGroupId !== undefined)\n this.splitGroupId = options.splitGroupId;\n if (options.primaryTouchId !== undefined)\n this.primaryTouchId = options.primaryTouchId;\n if (options.attributedAt !== undefined)\n this.attributedAt = Referral.coerceDate(options.attributedAt);\n if (options.qualifiedAt !== undefined)\n this.qualifiedAt = Referral.coerceDate(options.qualifiedAt);\n if (options.expiresAt !== undefined)\n this.expiresAt = Referral.coerceDate(options.expiresAt);\n if (options.snapshotId !== undefined) this.snapshotId = options.snapshotId;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Re-coerce date fields after the framework reapplies raw option values\n * and record the loaded status for the save-time transition guard.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.attributedAt = Referral.coerceDate(this.attributedAt);\n this.qualifiedAt = Referral.coerceDate(this.qualifiedAt);\n this.expiresAt = Referral.coerceDate(this.expiresAt);\n if (await this.isSaved()) {\n loadedReferralStatus.set(this, this.status);\n }\n return this;\n }\n\n // -------- Status predicates --------\n\n isAttributed(): boolean {\n return this.status === 'attributed';\n }\n\n isQualified(): boolean {\n return this.status === 'qualified';\n }\n\n /** Whether the referral sits in a terminal status. */\n isTerminal(): boolean {\n return this.status === 'disqualified' || this.status === 'expired';\n }\n\n // -------- Metadata helpers --------\n\n /** Parse {@link metadata}; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n const parsed = JSON.parse(this.metadata) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link metadata}. */\n setMetadata(data: Record<string, unknown>): void {\n this.metadata = JSON.stringify(data ?? {});\n }\n\n // -------- Status transitions (mutate; caller saves) --------\n\n /** Transition `pending → under_review` (conflict parked for humans). */\n markUnderReview(): void {\n if (this.status !== 'pending') {\n throw new Error(\n `Referral ${this.id ?? '<new>'}: cannot move to under_review from status '${this.status}'`,\n );\n }\n this.status = 'under_review';\n }\n\n /**\n * Transition `pending | under_review → attributed` and stamp\n * {@link attributedAt}.\n */\n markAttributed(at: Date = new Date()): void {\n if (this.status !== 'pending' && this.status !== 'under_review') {\n throw new Error(\n `Referral ${this.id ?? '<new>'}: cannot attribute from status '${this.status}'`,\n );\n }\n this.status = 'attributed';\n this.attributedAt = at;\n }\n\n /** Transition `attributed → qualified` and stamp {@link qualifiedAt}. */\n markQualified(at: Date = new Date()): void {\n if (this.status !== 'attributed') {\n throw new Error(\n `Referral ${this.id ?? '<new>'}: cannot qualify from status '${this.status}'`,\n );\n }\n this.status = 'qualified';\n this.qualifiedAt = at;\n }\n\n /**\n * Transition to `disqualified` (terminal). Legal from every non-terminal\n * status — including `qualified` (the rare clawback path).\n */\n disqualify(): void {\n if (this.isTerminal()) {\n throw new Error(\n `Referral ${this.id ?? '<new>'}: cannot disqualify from terminal status '${this.status}'`,\n );\n }\n this.status = 'disqualified';\n }\n\n /** Transition `pending | attributed → expired` (terminal). */\n markExpired(): void {\n if (this.status !== 'pending' && this.status !== 'attributed') {\n throw new Error(\n `Referral ${this.id ?? '<new>'}: cannot expire from status '${this.status}'`,\n );\n }\n this.status = 'expired';\n }\n\n // -------- Save-time guard --------\n\n /**\n * Validate the status transition before persisting, then save. A forged\n * `status` (e.g. un-expiring a referral or skipping straight to\n * qualified) is rejected regardless of how the instance was constructed.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n const result = (await super.save()) as this;\n loadedReferralStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status from the database; fall back to\n * the loaded-status WeakMap only when the DB is unavailable (commerce\n * pattern — an upsert via `create({ id, _skipLoad: true })` can't\n * sidestep the guard).\n */\n private async resolvePriorStatus(): Promise<ReferralStatus | undefined> {\n if (this.id) {\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row && row.status != null) {\n return row.status as ReferralStatus;\n }\n } catch {\n // DB not ready — fall through to the in-memory record.\n }\n }\n return loadedReferralStatus.get(this);\n }\n\n private assertStatusTransition(prior: ReferralStatus | undefined): void {\n if (prior === undefined) return; // new row — any starting status\n if (prior === this.status) return; // no-op re-save\n const allowed = REFERRAL_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Referral ${this.id ?? '<new>'}: illegal status transition ` +\n `'${prior}' → '${this.status}'.`,\n );\n }\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default Referral;\n","/**\n * ReferralCollection — collection manager for {@link Referral}.\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Referral } from '../models/Referral.js';\n\nexport class ReferralCollection extends SmrtCollection<Referral> {\n static readonly _itemClass = Referral;\n\n /** All referrals credited to a referrer, newest first. */\n async findByReferrer(referrerId: string): Promise<Referral[]> {\n return await this.list({\n where: { referrerId },\n orderBy: 'created_at DESC',\n });\n }\n\n /** All referrals pointing at one qualifying target, newest first. */\n async findByTarget(\n targetKind: string,\n targetId: string,\n ): Promise<Referral[]> {\n return await this.list({\n where: { targetKind, targetId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * QUALIFIED referrals for one target — what\n * `ReferralCommissionService.processEarningEvent` resolves when given a\n * target instead of explicit referral ids.\n */\n async findQualifiedByTarget(\n targetKind: string,\n targetId: string,\n ): Promise<Referral[]> {\n return await this.list({\n where: { targetKind, targetId, status: 'qualified' },\n orderBy: 'created_at ASC',\n });\n }\n\n /**\n * Expire lapsed attributions: every `attributed` referral whose\n * `expiresAt` is set and `<= now` transitions to `expired` and is saved.\n * Referrals without an `expiresAt` never lapse.\n *\n * @returns The referrals expired by this sweep.\n */\n async sweepExpired(now: Date = new Date()): Promise<Referral[]> {\n const attributed = await this.list({\n where: { status: 'attributed' },\n orderBy: 'created_at ASC',\n });\n const swept: Referral[] = [];\n for (const referral of attributed) {\n const expiresAt = referral.expiresAt;\n if (expiresAt === null || expiresAt.getTime() > now.getTime()) {\n continue; // no deadline, or still inside the window\n }\n referral.markExpired();\n await referral.save();\n swept.push(referral);\n }\n return swept;\n }\n}\n\nexport default ReferralCollection;\n","/**\n * ReferralLink — shareable link/code per Referrer + Program.\n * @packageDocumentation\n */\n\nimport { field, foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ReferralLinkOptions, ReferralLinkStatus } from '../types.js';\n\n/**\n * Assert that a target URL is an absolute http/https URL. Exported for the\n * collection's create path; the model also enforces it at save time so no\n * write path can persist a malformed target.\n */\nexport function assertHttpTargetUrl(targetUrl: string): void {\n let parsed: URL;\n try {\n parsed = new URL(targetUrl);\n } catch {\n throw new Error(\n `ReferralLink targetUrl must be an absolute http(s) URL — got '${targetUrl}'`,\n );\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(\n `ReferralLink targetUrl must use http or https — got '${parsed.protocol}'`,\n );\n }\n}\n\n/**\n * A ReferralLink is the shareable artifact a Referrer distributes: a\n * globally unique `code` (natural key `['code']` — codes resolve without\n * knowing the tenant/program) plus an optional destination `targetUrl`.\n *\n * Codes are minted by `ReferralLinkCollection.createWithUniqueCode()` —\n * crypto-random, 10 lowercase-alphanumeric characters, collision-checked\n * with a capped retry loop. Click traffic lands through\n * `ReferralLinkCollection.recordClick()`, which increments {@link clickCount}\n * and writes an immutable `ReferralTouch` evidence row; disabled links\n * refuse clicks.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Codes are globally unique — the shareable artifact must resolve from\n // the code alone.\n conflictColumns: ['code'],\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class ReferralLink extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global links). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The Referrer this link credits. Required. */\n @foreignKey('Referrer', { required: true })\n referrerId: string = '';\n\n /** The ReferralProgram this link belongs to. Required. */\n @foreignKey('ReferralProgram', { required: true })\n programId: string = '';\n\n /**\n * Globally unique share code (10 lowercase-alphanumeric characters when\n * minted by `createWithUniqueCode`). Natural key.\n */\n @field({ required: true })\n code: string = '';\n\n /**\n * Optional destination URL the shared link forwards to. Must be an\n * absolute http/https URL when non-empty (validated at save time).\n */\n targetUrl: string = '';\n\n /** Human-readable label for the referrer's own bookkeeping. */\n label: string = '';\n\n /** `active` (default) accepts clicks; `disabled` refuses them. */\n status: ReferralLinkStatus = 'active';\n\n /** Number of recorded clicks (incremented by `recordClick`). */\n clickCount: number = 0;\n\n /**\n * Free-form JSON object stored as a string. Use\n * {@link getMetadata}/{@link setMetadata} instead of parsing manually.\n */\n metadata: string = '{}';\n\n constructor(options: ReferralLinkOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.referrerId !== undefined) this.referrerId = options.referrerId;\n if (options.programId !== undefined) this.programId = options.programId;\n if (options.code !== undefined) this.code = options.code;\n if (options.targetUrl !== undefined) this.targetUrl = options.targetUrl;\n if (options.label !== undefined) this.label = options.label;\n if (options.status !== undefined) this.status = options.status;\n if (options.clickCount !== undefined) this.clickCount = options.clickCount;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /** Whether the link currently accepts clicks. */\n isActive(): boolean {\n return this.status === 'active';\n }\n\n /** Parse the metadata JSON string; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n try {\n const parsed: unknown = JSON.parse(this.metadata);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store the metadata object. */\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata);\n }\n\n /**\n * Save with two guards:\n *\n * - a non-empty {@link targetUrl} must be an absolute http/https URL\n * regardless of which write path set it;\n * - the {@link code} must not belong to a DIFFERENT persisted link. Codes\n * are the upsert natural key and they are public — without this check a\n * generated-surface `create` carrying an existing code would silently\n * UPSERT over that link (rewriting its referrer/program/target: a\n * referral-code hijack). Mint codes through\n * `ReferralLinkCollection.createWithUniqueCode()`.\n */\n override async save(): Promise<this> {\n if (this.targetUrl) {\n assertHttpTargetUrl(this.targetUrl);\n }\n if (this.code) {\n try {\n const row = await this.db.get(this.tableName, { code: this.code });\n if (row && row.id !== this.id) {\n throw new Error(\n `ReferralLink (code '${this.code}'): this code already belongs ` +\n 'to another link — referral codes are unique and cannot be ' +\n 'taken over. Mint links through ' +\n 'ReferralLinkCollection.createWithUniqueCode().',\n );\n }\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('cannot be taken over')\n ) {\n throw error;\n }\n // DB not ready / table absent — nothing persisted to protect yet.\n }\n }\n return (await super.save()) as this;\n }\n}\n\nexport default ReferralLink;\n","/**\n * ReferralTouch — immutable attribution evidence.\n *\n * A touch records that a prospect interacted with a referral artifact: a\n * link click, a code entry, a partner-submitted introduction, or an\n * operator's manual assignment. Touches are the CANDIDATE SET attribution\n * resolves over — `AttributionService.resolve()` gathers eligible touches\n * within the policy window and credits per the policy's mode.\n *\n * Immutability contract (same as the commissions module's `EarningEvent`):\n * touches are evidence. The generated surface exposes `create`/`list`/`get`\n * only (no update, no delete), and application code must treat persisted\n * rows as append-only — Referrals point at their winning touch via\n * `primaryTouchId` and AttributionExceptions embed candidate touches, so\n * rewriting a touch would orphan the audit trail. Corrections are modelled\n * as NEW touches (e.g. a `manual_assignment`).\n *\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ReferralTouchKind, ReferralTouchOptions } from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Immutable evidence: create/list/get only — no update or delete on any\n // generated surface.\n api: { include: ['create', 'list', 'get'] },\n mcp: { include: ['list', 'create'] },\n // High-volume evidence rows are not useful from the CLI.\n cli: false,\n})\nexport class ReferralTouch extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global touches). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The ReferralLink that produced this touch (empty for touchpoints\n * without a link, e.g. manual assignments). */\n @foreignKey('ReferralLink')\n linkId: string = '';\n\n /** The share code involved, when the touch came through a code/link. */\n code: string = '';\n\n /** The Referrer this touch credits. Required. */\n @foreignKey('Referrer', { required: true })\n referrerId: string = '';\n\n /** The ReferralProgram the touch belongs to. */\n @foreignKey('ReferralProgram')\n programId: string = '';\n\n /** What kind of interaction this evidence records. */\n kind: ReferralTouchKind = 'click';\n\n /**\n * Prospect identity when known — a generic `(subjectKind, subjectId)`\n * pair (e.g. `('lead', <id>)`, `('email', 'a@b.test')`). Attribution\n * matches touches to a resolution's subject and/or target through these.\n */\n subjectKind: string = '';\n\n /** Identifier within the {@link subjectKind} namespace. */\n subjectId: string = '';\n\n /** When the interaction occurred (not when it was ingested). */\n occurredAt: Date = new Date();\n\n /**\n * Evidence payload as a JSON object string (the code/link/url for clicks,\n * actor + reason for manual assignments, …). Use\n * {@link getEvidence}/{@link setEvidence}.\n */\n evidence: string = '{}';\n\n /**\n * Free-form JSON object stored as a string. Use\n * {@link getMetadata}/{@link setMetadata} instead of parsing manually.\n */\n metadata: string = '{}';\n\n constructor(options: ReferralTouchOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.linkId !== undefined) this.linkId = options.linkId;\n if (options.code !== undefined) this.code = options.code;\n if (options.referrerId !== undefined) this.referrerId = options.referrerId;\n if (options.programId !== undefined) this.programId = options.programId;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.subjectKind !== undefined)\n this.subjectKind = options.subjectKind;\n if (options.subjectId !== undefined) this.subjectId = options.subjectId;\n if (options.occurredAt !== undefined)\n this.occurredAt =\n ReferralTouch.coerceDate(options.occurredAt) ?? new Date();\n if (options.evidence !== undefined) this.evidence = options.evidence;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Re-coerce {@link occurredAt} after the framework reapplies raw option /\n * hydrated row values (SQLite hands back ISO strings).\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.occurredAt = ReferralTouch.coerceDate(this.occurredAt) ?? new Date();\n return this;\n }\n\n /** Parse {@link evidence}; returns `{}` on empty/invalid JSON. */\n getEvidence(): Record<string, unknown> {\n if (!this.evidence) return {};\n try {\n const parsed = JSON.parse(this.evidence) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link evidence}. */\n setEvidence(evidence: Record<string, unknown>): void {\n this.evidence = JSON.stringify(evidence ?? {});\n }\n\n /** Parse {@link metadata}; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n const parsed = JSON.parse(this.metadata) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store {@link metadata}. */\n setMetadata(data: Record<string, unknown>): void {\n this.metadata = JSON.stringify(data ?? {});\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default ReferralTouch;\n","/**\n * ReferralTouchCollection — collection manager for {@link ReferralTouch}.\n *\n * Touches are immutable evidence: this collection only creates and reads.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ReferralTouch } from '../models/ReferralTouch.js';\n\n/** Input for {@link ReferralTouchCollection.findByTargetWindow}. */\nexport interface TouchWindowQuery {\n subjectKind: string;\n subjectId: string;\n /** Restrict to one program's touches. */\n programId: string;\n /** Only touches occurring at or after this instant. */\n since: Date;\n}\n\nexport class ReferralTouchCollection extends SmrtCollection<ReferralTouch> {\n static readonly _itemClass = ReferralTouch;\n\n /** All touches for a referrer, oldest first. */\n async findByReferrer(referrerId: string): Promise<ReferralTouch[]> {\n return await this.list({\n where: { referrerId },\n orderBy: 'occurred_at ASC',\n });\n }\n\n /**\n * Every touch recorded against one subject identity, oldest first —\n * attribution's raw candidate stream.\n */\n async findBySubject(\n subjectKind: string,\n subjectId: string,\n ): Promise<ReferralTouch[]> {\n return await this.list({\n where: { subjectKind, subjectId },\n orderBy: 'occurred_at ASC',\n });\n }\n\n /**\n * Touches for one subject within one program occurring at or after\n * `since`, oldest first. The date bound is applied in memory after the\n * indexed equality filters (sibling-module pattern — the semantics hold\n * whether an adapter returns ISO strings or Dates).\n */\n async findByTargetWindow(query: TouchWindowQuery): Promise<ReferralTouch[]> {\n const touches = await this.list({\n where: {\n subjectKind: query.subjectKind,\n subjectId: query.subjectId,\n programId: query.programId,\n },\n orderBy: 'occurred_at ASC',\n });\n return touches.filter(\n (touch) => touch.occurredAt.getTime() >= query.since.getTime(),\n );\n }\n}\n\nexport default ReferralTouchCollection;\n","/**\n * ReferralLinkCollection — collection manager for {@link ReferralLink}:\n * crypto-random code minting with collision retry, code lookup, and click\n * recording (link counter + immutable ReferralTouch evidence).\n *\n * @packageDocumentation\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { assertHttpTargetUrl, ReferralLink } from '../models/ReferralLink.js';\nimport type { ReferralTouch } from '../models/ReferralTouch.js';\nimport { ReferralTouchCollection } from './ReferralTouchCollection.js';\n\n/** Length of generated share codes. */\nexport const REFERRAL_CODE_LENGTH = 10;\n\n/** Alphabet generated codes draw from (lowercase alphanumeric). */\nexport const REFERRAL_CODE_ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';\n\n/** Attempts before {@link ReferralLinkCollection.createWithUniqueCode} gives up. */\nexport const MAX_CODE_GENERATION_ATTEMPTS = 5;\n\n/**\n * Generate a crypto-random share code: {@link REFERRAL_CODE_LENGTH}\n * characters drawn uniformly from {@link REFERRAL_CODE_ALPHABET} via\n * rejection-sampled `crypto.randomBytes` (no modulo bias).\n */\nexport function generateReferralCode(): string {\n const alphabetSize = REFERRAL_CODE_ALPHABET.length;\n // Largest multiple of the alphabet size below 256 — bytes at or above it\n // are rejected so every character is uniformly likely.\n const rejectionBound = 256 - (256 % alphabetSize);\n let code = '';\n while (code.length < REFERRAL_CODE_LENGTH) {\n const bytes = randomBytes(REFERRAL_CODE_LENGTH * 2);\n for (const byte of bytes) {\n if (byte >= rejectionBound) continue;\n code += REFERRAL_CODE_ALPHABET[byte % alphabetSize];\n if (code.length === REFERRAL_CODE_LENGTH) break;\n }\n }\n return code;\n}\n\n/** Input for {@link ReferralLinkCollection.createWithUniqueCode}. */\nexport interface CreateReferralLinkInput {\n referrerId: string;\n programId: string;\n targetUrl?: string;\n label?: string;\n tenantId?: string | null;\n /**\n * Test seam / customization point: overrides the random code generator.\n * Must return {@link REFERRAL_CODE_LENGTH} lowercase-alphanumeric\n * characters; collisions with existing codes are retried up to\n * {@link MAX_CODE_GENERATION_ATTEMPTS} times.\n */\n generateCode?: () => string;\n}\n\n/** Why {@link ReferralLinkCollection.recordClick} refused to record. */\nexport type RecordClickRefusal = 'unknown_code' | 'link_disabled';\n\n/**\n * Result of {@link ReferralLinkCollection.recordClick}. On success `link`\n * and `touch` are both set and `refused` is absent. On refusal `touch` is\n * `null`, `refused` names the reason, and `link` carries the (disabled) link\n * for `'link_disabled'` / `null` for `'unknown_code'`.\n */\nexport interface RecordClickResult {\n link: ReferralLink | null;\n touch: ReferralTouch | null;\n refused?: RecordClickRefusal;\n}\n\n/** Input for {@link ReferralLinkCollection.recordClick}. */\nexport interface RecordClickInput {\n code: string;\n /** Extra evidence merged into the touch's evidence JSON (UA, IP hash, …). */\n evidence?: Record<string, unknown>;\n /** When the click occurred; defaults to now. */\n occurredAt?: Date;\n /**\n * Prospect identity when the edge already knows it (e.g. a logged-in\n * visitor or a pre-created lead). Without a subject pair the touch is\n * anonymous and `AttributionService.resolve` cannot gather it by subject —\n * the intake handler must then append a second, identified touch once the\n * prospect materializes (same `occurredAt` preserves credit ordering).\n */\n subjectKind?: string;\n subjectId?: string;\n}\n\nexport class ReferralLinkCollection extends SmrtCollection<ReferralLink> {\n static readonly _itemClass = ReferralLink;\n\n /** All links for a referrer, newest first. */\n async findByReferrer(referrerId: string): Promise<ReferralLink[]> {\n return await this.list({\n where: { referrerId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Look up a link by its globally unique code (codes are minted lowercase;\n * the lookup normalizes case so shared codes survive re-typing).\n */\n async findByCode(code: string): Promise<ReferralLink | null> {\n const normalized = code.trim().toLowerCase();\n if (!normalized) return null;\n const results = await this.list({\n where: { code: normalized },\n limit: 1,\n });\n return results[0] ?? null;\n }\n\n /**\n * Create a link with a freshly minted, uniqueness-checked code.\n *\n * Codes come from `crypto.randomBytes` mapped to\n * {@link REFERRAL_CODE_LENGTH} lowercase-alphanumeric characters. The\n * check-then-insert loop retries on collision up to\n * {@link MAX_CODE_GENERATION_ATTEMPTS} times, then throws — at 36^10\n * possible codes repeated collisions mean the generator is broken (or a\n * test seam returns a constant), not bad luck.\n */\n async createWithUniqueCode(\n input: CreateReferralLinkInput,\n ): Promise<ReferralLink> {\n if (!input.referrerId || !input.programId) {\n throw new Error(\n 'ReferralLinkCollection.createWithUniqueCode requires referrerId and programId',\n );\n }\n if (input.targetUrl) {\n // Fail before burning a code attempt on an invalid destination.\n assertHttpTargetUrl(input.targetUrl);\n }\n const generate = input.generateCode ?? generateReferralCode;\n\n for (let attempt = 0; attempt < MAX_CODE_GENERATION_ATTEMPTS; attempt++) {\n const code = generate();\n const existing = await this.findByCode(code);\n if (existing) continue;\n return await this.create({\n tenantId: input.tenantId,\n referrerId: input.referrerId,\n programId: input.programId,\n code,\n targetUrl: input.targetUrl ?? '',\n label: input.label ?? '',\n status: 'active',\n });\n }\n throw new Error(\n `ReferralLinkCollection.createWithUniqueCode: could not mint a unique code after ${MAX_CODE_GENERATION_ATTEMPTS} attempts — the code generator is returning colliding codes`,\n );\n }\n\n /**\n * Record a click on a share code: find the ACTIVE link, increment its\n * {@link ReferralLink.clickCount}, and write an immutable ReferralTouch\n * (kind `'click'`, evidence carrying the code/link/url plus any caller\n * evidence). Returns `{ link, touch }` on success.\n *\n * Refusals return a typed result instead of throwing (clicks arrive from\n * the edge — an unknown or disabled code is an expected outcome, not an\n * exception): `refused: 'unknown_code'` (no such code; `link: null`) or\n * `refused: 'link_disabled'` (link exists but is disabled; the link is\n * returned, nothing is written).\n */\n async recordClick(input: RecordClickInput): Promise<RecordClickResult> {\n const link = await this.findByCode(input.code);\n if (!link) {\n return { link: null, touch: null, refused: 'unknown_code' };\n }\n if (!link.isActive()) {\n return { link, touch: null, refused: 'link_disabled' };\n }\n\n const occurredAt = input.occurredAt ?? new Date();\n const touches = await ReferralTouchCollection.create({ db: this.db });\n const touch = await touches.create({\n tenantId: link.tenantId,\n linkId: link.id ?? '',\n code: link.code,\n referrerId: link.referrerId,\n programId: link.programId,\n kind: 'click',\n subjectKind: input.subjectKind ?? '',\n subjectId: input.subjectId ?? '',\n occurredAt,\n evidence: JSON.stringify({\n ...(input.evidence ?? {}),\n code: link.code,\n linkId: link.id ?? '',\n targetUrl: link.targetUrl,\n }),\n });\n\n // Atomic in-database increment — edge clicks arrive concurrently, and a\n // load-increment-save would lose all but one of a burst's counts. The\n // touch rows remain the authoritative evidence; clickCount is the fast\n // counter derived from them.\n await this.db\n .execute`UPDATE referral_links SET click_count = click_count + 1 WHERE id = ${link.id ?? ''}`;\n const fresh = (await this.get({ id: link.id })) ?? link;\n\n return { link: fresh, touch };\n }\n}\n\nexport default ReferralLinkCollection;\n","/**\n * ReferralProgram — program-level defaults for referral intake.\n * @packageDocumentation\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ReferralProgramOptions,\n ReferralProgramStatus,\n} from '../types.js';\n\n/**\n * A ReferralProgram anchors links, touches, referrals, agreements, and\n * exceptions, and carries the program-level DEFAULTS:\n *\n * - `defaultAttributionPolicyKey` — which versioned AttributionPolicy\n * `AttributionService.resolve()` loads when the caller doesn't override.\n * - `defaultCommissionPlanKey` — the suggested commissions plan for new\n * ReferralAgreements (the agreement's own `commissionPlanKey` is what\n * actually binds terms).\n *\n * Both defaults are KEYS, not pinned versions: the active version is\n * resolved at attribution/qualification time and pinned on the Referral /\n * ReferralTermSnapshot, so program rows stay stable across policy and plan\n * amendments.\n *\n * Natural key `(tenant_id, key)` — each tenant names its programs once.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n conflictColumns: ['tenant_id', 'key'],\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ReferralProgram extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global programs). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Stable program key within the tenant (natural-key component). */\n @field({ required: true })\n key: string = '';\n\n /** Human-readable program name. */\n name: string = '';\n\n /**\n * Program lifecycle: `draft` (default) → `active` → `paused` /\n * `archived`. Applications gate intake on `active`; this module records\n * the status without enforcing transition edges.\n */\n status: ReferralProgramStatus = 'draft';\n\n /**\n * Default commissions-plan key suggested to new ReferralAgreements.\n * Empty means the program has no default — agreements must name a plan.\n */\n defaultCommissionPlanKey: string = '';\n\n /**\n * Default AttributionPolicy key `AttributionService.resolve()` uses when\n * the caller passes no `policyKey` override.\n */\n defaultAttributionPolicyKey: string = '';\n\n /**\n * Free-form JSON object stored as a string. Use\n * {@link getMetadata}/{@link setMetadata} instead of parsing manually.\n */\n metadata: string = '{}';\n\n constructor(options: ReferralProgramOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.key !== undefined) this.key = options.key;\n if (options.name !== undefined) this.name = options.name;\n if (options.status !== undefined) this.status = options.status;\n if (options.defaultCommissionPlanKey !== undefined)\n this.defaultCommissionPlanKey = options.defaultCommissionPlanKey;\n if (options.defaultAttributionPolicyKey !== undefined)\n this.defaultAttributionPolicyKey = options.defaultAttributionPolicyKey;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /** Whether the program is accepting new referral activity. */\n isActive(): boolean {\n return this.status === 'active';\n }\n\n /** Parse the metadata JSON string; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n try {\n const parsed: unknown = JSON.parse(this.metadata);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store the metadata object. */\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata);\n }\n}\n\nexport default ReferralProgram;\n","/**\n * ReferralProgramCollection — collection manager for {@link ReferralProgram}.\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ReferralProgram } from '../models/ReferralProgram.js';\nimport type { ReferralProgramStatus } from '../types.js';\n\nexport class ReferralProgramCollection extends SmrtCollection<ReferralProgram> {\n static readonly _itemClass = ReferralProgram;\n\n /**\n * Look up a program by its natural key within the ambient tenant scope\n * (`(tenant_id, key)` is the conflict key, so under a tenant context the\n * auto-filter narrows this to at most one row).\n */\n async findByKey(key: string): Promise<ReferralProgram | null> {\n const results = await this.list({ where: { key }, limit: 1 });\n return results[0] ?? null;\n }\n\n /** Programs by status. */\n async findByStatus(\n status: ReferralProgramStatus,\n ): Promise<ReferralProgram[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /** All active programs. */\n async findActive(): Promise<ReferralProgram[]> {\n return await this.findByStatus('active');\n }\n}\n\nexport default ReferralProgramCollection;\n","/**\n * ReferralTermSnapshot — the terms frozen at qualification.\n *\n * When a Referral qualifies, `ReferralQualificationService` copies\n * EVERYTHING later earnings depend on into one immutable row: the resolved\n * agreement/plan/policy version references plus the CALCULATION INPUTS\n * themselves (`components`, `currency`, `clearingDays`, `approvalMode`).\n * `ReferralCommissionService` calculates exclusively from this row — never\n * from a live plan — so a plan amendment after qualification can NEVER\n * change what an already-qualified referral earns. Reproducibility over\n * convenience.\n *\n * FULLY immutable post-create: a save guard rejects ANY field change to a\n * persisted row (and blind writes onto an existing id from a non-hydrated\n * instance). Amended terms mean a NEW snapshot via\n * `ReferralQualificationService.requalify()` — the old row stands as\n * history, still referenced by any Commission it produced.\n *\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { CommissionPlanComponent } from '../../commissions/index.js';\nimport type {\n ReferralAgreementApprovalMode,\n ReferralTermSnapshotOptions,\n} from '../types.js';\n\n/**\n * Module-scoped record of each persisted snapshot's serialized state,\n * captured at hydration/first save. The save guard compares against it to\n * reject post-create mutations. WeakMap keeps it out of the schema and GCs\n * with the instance (commerce `LicenseSale` pattern).\n */\nconst persistedSnapshotState = new WeakMap<ReferralTermSnapshot, string>();\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Read-only generated surface: snapshots are minted exclusively by\n // ReferralQualificationService.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: false,\n})\nexport class ReferralTermSnapshot extends SmrtObject {\n /** Tenant ID for multi-tenant isolation (nullable → global rows). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The Referral this snapshot governs. Required. */\n @foreignKey('Referral', { required: true })\n referralId: string = '';\n\n /** Id of the ReferralAgreement the terms came from. */\n agreementId: string = '';\n\n /** Version of the agreement at qualification time. */\n agreementVersion: number = 0;\n\n /** CommissionPlan key the components were copied from. */\n planKey: string = '';\n\n /**\n * CommissionPlan version PINNED at qualification — when the agreement\n * left the version unpinned (`0`), this records the latest-active version\n * that was resolved.\n */\n planVersion: number = 0;\n\n /** AttributionPolicy key stamped on the referral at attribution. */\n policyKey: string = '';\n\n /** AttributionPolicy version stamped on the referral at attribution. */\n policyVersion: number = 0;\n\n /**\n * FROZEN copy of the plan's components JSON — THE calculation inputs.\n * Use {@link getComponents} for the typed array.\n */\n components: string = '[]';\n\n /** ISO 4217 currency the frozen terms are denominated in. */\n currency: string = 'USD';\n\n /** Clearing window (days) copied from the agreement. */\n clearingDays: number = 0;\n\n /** Approval mode copied from the agreement. */\n approvalMode: ReferralAgreementApprovalMode = 'manual';\n\n /** Additional metadata as a JSON string (immutable like every field). */\n metadata: string = '{}';\n\n constructor(options: ReferralTermSnapshotOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.referralId !== undefined) this.referralId = options.referralId;\n if (options.agreementId !== undefined)\n this.agreementId = options.agreementId;\n if (options.agreementVersion !== undefined)\n this.agreementVersion = options.agreementVersion;\n if (options.planKey !== undefined) this.planKey = options.planKey;\n if (options.planVersion !== undefined)\n this.planVersion = options.planVersion;\n if (options.policyKey !== undefined) this.policyKey = options.policyKey;\n if (options.policyVersion !== undefined)\n this.policyVersion = options.policyVersion;\n if (options.components !== undefined) this.components = options.components;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.clearingDays !== undefined)\n this.clearingDays = options.clearingDays;\n if (options.approvalMode !== undefined)\n this.approvalMode = options.approvalMode;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /** Capture the persisted state so the save guard can reject mutations. */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n persistedSnapshotState.set(this, this.serializeState());\n }\n return this;\n }\n\n /**\n * Parse {@link components} into the typed plan-component array the\n * calculation service consumes; returns `[]` on empty/invalid JSON.\n */\n getComponents(): CommissionPlanComponent[] {\n if (!this.components) return [];\n try {\n const parsed = JSON.parse(this.components) as unknown;\n return Array.isArray(parsed) ? (parsed as CommissionPlanComponent[]) : [];\n } catch {\n return [];\n }\n }\n\n /** Parse {@link metadata}; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n const parsed = JSON.parse(this.metadata) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n /**\n * Save with the full-immutability guard:\n *\n * - A hydrated persisted row must serialize IDENTICALLY to its captured\n * state — any field change throws (no-op re-saves pass).\n * - An instance carrying an existing row's id WITHOUT having hydrated it\n * (e.g. `create({ id, _skipLoad: true })`) is rejected outright — blind\n * overwrites can't sidestep the comparison.\n */\n override async save(): Promise<this> {\n const captured = persistedSnapshotState.get(this);\n if (captured !== undefined) {\n if (captured !== this.serializeState()) {\n throw new Error(\n `ReferralTermSnapshot ${this.id ?? '<new>'}: snapshots are ` +\n 'immutable once created. Requalify the referral ' +\n '(ReferralQualificationService.requalify) to snapshot new terms ' +\n 'instead of editing this row.',\n );\n }\n } else if (this.id && (await this.isSaved())) {\n throw new Error(\n `ReferralTermSnapshot ${this.id}: refusing to overwrite an existing ` +\n 'snapshot row from a non-hydrated instance — snapshots are ' +\n 'immutable once created.',\n );\n }\n const result = (await super.save()) as this;\n persistedSnapshotState.set(this, this.serializeState());\n return result;\n }\n\n private serializeState(): string {\n // Stable key ordering so a no-op re-serialization matches.\n return JSON.stringify({\n tenantId: this.tenantId,\n referralId: this.referralId,\n agreementId: this.agreementId,\n agreementVersion: this.agreementVersion,\n planKey: this.planKey,\n planVersion: this.planVersion,\n policyKey: this.policyKey,\n policyVersion: this.policyVersion,\n components: this.components,\n currency: this.currency,\n clearingDays: this.clearingDays,\n approvalMode: this.approvalMode,\n metadata: this.metadata,\n });\n }\n}\n\nexport default ReferralTermSnapshot;\n","/**\n * ReferralTermSnapshotCollection — collection manager for\n * {@link ReferralTermSnapshot}.\n *\n * Snapshots are immutable history: this collection only creates and reads.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ReferralTermSnapshot } from '../models/ReferralTermSnapshot.js';\n\nexport class ReferralTermSnapshotCollection extends SmrtCollection<ReferralTermSnapshot> {\n static readonly _itemClass = ReferralTermSnapshot;\n\n /**\n * Every snapshot minted for one referral, newest first — index 0 is the\n * currently governing snapshot only if the referral's `snapshotId` says\n * so (requalification keeps old rows as history).\n */\n async findByReferral(referralId: string): Promise<ReferralTermSnapshot[]> {\n return await this.list({\n where: { referralId },\n orderBy: 'created_at DESC',\n });\n }\n}\n\nexport default ReferralTermSnapshotCollection;\n","/**\n * Referrer — the referral role model.\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ReferrerOptions, ReferrerStatus } from '../types.js';\n\n/**\n * Referrer is a ROLE model: it links an identity (smrt-profiles Profile) to\n * referral activity (ReferralLinks, ReferralTouches, and Referrals reference\n * `referrerId`).\n *\n * Roles and money stay separate (see `packages/sales/AGENTS.md` \"Roles vs.\n * money\"): a referrer connects to payouts only through the optional\n * `earnerId` pointing at the commissions module's neutral `Earner`. A\n * referral cannot produce Commissions until its referrer carries an\n * `earnerId` — `ReferralCommissionService` skips such referrals with reason\n * `'referrer_missing_earner'`.\n *\n * @example\n * ```typescript\n * const referrers = await ReferrerCollection.create({ db });\n * const referrer = await referrers.create({\n * profileId: 'profile-uuid',\n * displayName: 'Jordan Partner',\n * status: 'active',\n * });\n * const active = await referrers.findActive();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class Referrer extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation. Nullable to support both\n * tenant-scoped and global (operator-level) referrers.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Identity of the person/organization acting as a referrer —\n * cross-package string reference to a smrt-profiles Profile.\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId: string = '';\n\n /**\n * Optional link to the commissions module's `Earner` (the neutral payout\n * identity). String-form same-package FK: the Earner class lives in\n * `src/commissions` and is intentionally NOT imported here — the string\n * form registers the relationship without a module dependency, keeping the\n * `referrals → commissions` boundary reference-only at the model layer.\n * Empty when the referrer is not commission-compensated (yet).\n */\n @foreignKey('Earner')\n earnerId: string = '';\n\n /** Human-readable display name for portals and operator views. */\n displayName: string = '';\n\n /**\n * Role lifecycle: `pending` (default, awaiting approval) → `active` /\n * `suspended`. Only active referrers should receive new links/agreements.\n */\n status: ReferrerStatus = 'pending';\n\n /**\n * Free-form JSON object stored as a string. Use\n * {@link getMetadata}/{@link setMetadata} instead of parsing manually.\n */\n metadata: string = '{}';\n\n constructor(options: ReferrerOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.earnerId !== undefined) this.earnerId = options.earnerId;\n if (options.displayName !== undefined)\n this.displayName = options.displayName;\n if (options.status !== undefined) this.status = options.status;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /** Whether the referrer may receive new links/agreements. */\n isActive(): boolean {\n return this.status === 'active';\n }\n\n isPending(): boolean {\n return this.status === 'pending';\n }\n\n isSuspended(): boolean {\n return this.status === 'suspended';\n }\n\n /** Parse the metadata JSON string; returns `{}` on malformed content. */\n getMetadata(): Record<string, unknown> {\n try {\n const parsed: unknown = JSON.parse(this.metadata);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return {};\n } catch {\n return {};\n }\n }\n\n /** Serialize and store the metadata object. */\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata);\n }\n}\n\nexport default Referrer;\n","/**\n * ReferrerCollection — collection manager for {@link Referrer}.\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Referrer } from '../models/Referrer.js';\nimport type { ReferrerStatus } from '../types.js';\n\nexport class ReferrerCollection extends SmrtCollection<Referrer> {\n static readonly _itemClass = Referrer;\n\n /**\n * Find referrer roles held by a given profile (a profile may hold the\n * role in several tenants/contexts).\n */\n async findByProfile(profileId: string): Promise<Referrer[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'created_at DESC',\n });\n }\n\n /** Referrers by status. */\n async findByStatus(status: ReferrerStatus): Promise<Referrer[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /** All active referrers (eligible for new links/agreements). */\n async findActive(): Promise<Referrer[]> {\n return await this.findByStatus('active');\n }\n}\n\nexport default ReferrerCollection;\n","/**\n * AttributionService — turns ReferralTouch evidence into attributed\n * Referral rows (or AttributionExceptions when the policy cannot conclude),\n * and owns the audited manual paths: assignments, exception resolution, and\n * re-attribution overrides.\n *\n * @packageDocumentation\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { AttributionExceptionCollection } from '../collections/AttributionExceptionCollection.js';\nimport { AttributionPolicyCollection } from '../collections/AttributionPolicyCollection.js';\nimport { ReferralCollection } from '../collections/ReferralCollection.js';\nimport { ReferralProgramCollection } from '../collections/ReferralProgramCollection.js';\nimport { ReferralTouchCollection } from '../collections/ReferralTouchCollection.js';\nimport { ReferrerCollection } from '../collections/ReferrerCollection.js';\nimport type { AttributionException } from '../models/AttributionException.js';\nimport type { AttributionPolicy } from '../models/AttributionPolicy.js';\nimport type { Referral } from '../models/Referral.js';\nimport type { ReferralProgram } from '../models/ReferralProgram.js';\nimport type { ReferralTouch } from '../models/ReferralTouch.js';\nimport type { AttributionExceptionCandidate } from '../types.js';\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\n/**\n * Thrown by {@link AttributionService.override} when the target already has\n * a QUALIFIED referral: qualified credit is governed by its term snapshot\n * and commission adjustments downstream — re-attribution would silently\n * orphan agreed terms.\n */\nexport class QualifiedReferralOverrideError extends Error {\n constructor(referralIds: string[]) {\n super(\n `AttributionService.override: target already has qualified referral(s) ` +\n `[${referralIds.join(', ')}] — qualified credit is governed by term ` +\n 'snapshots and commission adjustments downstream, not re-attribution.',\n );\n this.name = 'QualifiedReferralOverrideError';\n }\n}\n\n/** Collaborators for {@link AttributionService}. */\nexport interface AttributionServiceDeps {\n touches: ReferralTouchCollection;\n referrals: ReferralCollection;\n exceptions: AttributionExceptionCollection;\n policies: AttributionPolicyCollection;\n programs: ReferralProgramCollection;\n referrers: ReferrerCollection;\n}\n\n/** Why {@link AttributionService.resolve} refused to attribute. */\nexport type AttributionRefusalReason =\n | 'existing_client_ineligible'\n | 'no_active_policy'\n | 'no_eligible_touches';\n\n/** Input for {@link AttributionService.resolve}. */\nexport interface ResolveAttributionInput {\n /** Generic qualifying target the referrals will point at. */\n targetKind: string;\n /** Identifier within the `targetKind` namespace. */\n targetId: string;\n /** The program to attribute under. */\n programId: string;\n /** Prospect identity to gather touches by (in addition to the target). */\n subjectKind?: string;\n /** Identifier within the `subjectKind` namespace. */\n subjectId?: string;\n /**\n * The prospect's own profile id, when known — enables the self-referral\n * eligibility check against each candidate referrer's `profileId`.\n */\n subjectProfileId?: string;\n /**\n * Caller's statement that the prospect is already a client. Policies with\n * `allowExistingClients: false` refuse the whole resolution.\n */\n isExistingClient?: boolean;\n /** Overrides the program's `defaultAttributionPolicyKey`. */\n policyKey?: string;\n /** Clock override for deterministic tests. */\n now?: Date;\n}\n\n/** Result of {@link AttributionService.resolve}. */\nexport interface AttributionResolution {\n /**\n * The attributed referral(s): one for sole credit, several siblings for\n * `split` mode. Empty when an exception was raised, the resolution was\n * refused, or nothing was eligible.\n */\n referrals: Referral[];\n /** The conflict parked for review, when the policy could not conclude. */\n exception: AttributionException | null;\n /** Why nothing was attributed, when refused outright. */\n refused?: AttributionRefusalReason;\n}\n\n/** Input for {@link AttributionService.recordManualAssignment}. */\nexport interface RecordManualAssignmentInput {\n referrerId: string;\n programId: string;\n targetKind: string;\n targetId: string;\n /** Prospect identity; defaults to the target pair when omitted. */\n subjectKind?: string;\n subjectId?: string;\n /** Who recorded the assignment (audit). Required. */\n actorProfileId: string;\n /** Free-form rationale recorded in the touch evidence. */\n reason?: string;\n /** When the assignment applies from; defaults to now. */\n occurredAt?: Date;\n}\n\n/** One credit award inside a resolution/override. */\nexport interface AttributionAward {\n referrerId: string;\n /** Credit share (0–1]; awards must sum to 1.0 (±0.0001). */\n creditFraction: number;\n}\n\n/** Input for {@link AttributionService.resolveException}. */\nexport interface ResolveExceptionInput {\n exceptionId: string;\n awards: AttributionAward[];\n /** Non-empty rationale. Required — resolutions are audited. */\n resolutionReason: string;\n /** Who resolved the exception (audit). Required. */\n actorProfileId: string;\n /** Clock override for deterministic tests. */\n now?: Date;\n}\n\n/** Input for {@link AttributionService.override}. */\nexport interface OverrideAttributionInput {\n targetKind: string;\n targetId: string;\n programId: string;\n awards: AttributionAward[];\n /** Non-empty rationale. Required — overrides are audited. */\n resolutionReason: string;\n /** Who performed the override (audit). Required. */\n actorProfileId: string;\n /** Clock override for deterministic tests. */\n now?: Date;\n}\n\n/** Result of {@link AttributionService.resolveException} / {@link AttributionService.override}. */\nexport interface AttributionAwardResult {\n /** The attributed referral(s) the awards created. */\n referrals: Referral[];\n /** The resolved exception row auditing the decision. */\n exception: AttributionException;\n}\n\nexport class AttributionService {\n constructor(private readonly deps: AttributionServiceDeps) {}\n\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<AttributionService> {\n return new AttributionService({\n touches: await ReferralTouchCollection.create(classOptions),\n referrals: await ReferralCollection.create(classOptions),\n exceptions: await AttributionExceptionCollection.create(classOptions),\n policies: await AttributionPolicyCollection.create(classOptions),\n programs: await ReferralProgramCollection.create(classOptions),\n referrers: await ReferrerCollection.create(classOptions),\n });\n }\n\n /**\n * Resolve attribution for one target under one program.\n *\n * IDEMPOTENT: existing non-disqualified `attributed`/`qualified`\n * referrals for the target+program are returned as-is (no new rows), and\n * an already-OPEN exception for the target+program is returned instead of\n * a duplicate.\n *\n * Flow:\n * 1. **Policy** — the program's `defaultAttributionPolicyKey` (or the\n * `policyKey` override) is resolved via `latestActiveByKey`; no active\n * version → `refused: 'no_active_policy'`.\n * 2. **Existing-client gate** — `isExistingClient` with a policy that\n * disallows it → `refused: 'existing_client_ineligible'`.\n * 3. **Candidates** — touches gathered by the subject pair (when given)\n * and by the target pair, deduplicated, restricted to the program and\n * to `occurredAt` within `[now - windowDays, now]`.\n * 4. **Eligibility** — self-referrals (candidate referrer's `profileId`\n * equals `subjectProfileId`) are dropped unless the policy allows\n * them. Nothing left → `refused: 'no_eligible_touches'`.\n * 5. **Conflicts** — `conflictBehavior: 'review'` with more than one\n * distinct eligible referrer, exact-timestamp ties between distinct\n * referrers in `first_touch`/`last_touch`, or competing manual\n * assignments in `assigned` mode → an OPEN AttributionException\n * carrying the candidates; NO referral rows.\n * 6. **Credit** — otherwise the mode elects the winner(s):\n * `first_touch`/`last_touch` (earliest/latest), `assigned` (most\n * recent manual assignment; assignments beat clicks by definition),\n * or `split` (every distinct eligible referrer, equal fractions\n * rounded to 4 dp with the remainder on the last, one shared\n * `splitGroupId`). Referrals are created `pending → attributed` with\n * the policy pin, winning touch, `attributedAt = now`, and\n * `expiresAt = now + windowDays`.\n */\n async resolve(\n input: ResolveAttributionInput,\n ): Promise<AttributionResolution> {\n const now = input.now ?? new Date();\n\n // Idempotency: live credit already exists → return it untouched.\n const existing = (\n await this.deps.referrals.findByTarget(input.targetKind, input.targetId)\n ).filter(\n (referral) =>\n referral.programId === input.programId &&\n (referral.status === 'attributed' || referral.status === 'qualified'),\n );\n if (existing.length > 0) {\n return { referrals: existing, exception: null };\n }\n\n // Idempotency: a conflict already parked for review → return it.\n const openException = await this.deps.exceptions.findOpenByTarget(\n input.targetKind,\n input.targetId,\n input.programId,\n );\n if (openException) {\n return { referrals: [], exception: openException };\n }\n\n const program = await this.requireProgram(input.programId);\n const policy = await this.resolvePolicy(program, input.policyKey, now);\n if (!policy) {\n return { referrals: [], exception: null, refused: 'no_active_policy' };\n }\n\n // Existing clients are refused outright unless the policy allows them.\n if (input.isExistingClient && !policy.allowExistingClients) {\n return {\n referrals: [],\n exception: null,\n refused: 'existing_client_ineligible',\n };\n }\n\n const eligible = await this.gatherEligibleTouches(input, policy, now);\n if (eligible.length === 0) {\n return { referrals: [], exception: null, refused: 'no_eligible_touches' };\n }\n\n const distinctReferrerIds = [\n ...new Set(eligible.map((touch) => touch.referrerId)),\n ];\n\n // Review-first policies park any multi-referrer contention for humans.\n if (\n policy.conflictBehavior === 'review' &&\n distinctReferrerIds.length > 1\n ) {\n const exception = await this.createOpenException({\n input,\n program,\n policy,\n conflictReason: 'policy_review',\n candidates: eligible,\n });\n return { referrals: [], exception };\n }\n\n switch (policy.creditMode) {\n case 'first_touch':\n case 'last_touch': {\n const winnerTime =\n policy.creditMode === 'first_touch'\n ? eligible[0].occurredAt.getTime()\n : eligible[eligible.length - 1].occurredAt.getTime();\n const atWinnerTime = eligible.filter(\n (touch) => touch.occurredAt.getTime() === winnerTime,\n );\n const tiedReferrers = new Set(\n atWinnerTime.map((touch) => touch.referrerId),\n );\n if (tiedReferrers.size > 1) {\n const exception = await this.createOpenException({\n input,\n program,\n policy,\n conflictReason: 'tie',\n candidates: eligible,\n });\n return { referrals: [], exception };\n }\n const winner =\n policy.creditMode === 'first_touch'\n ? atWinnerTime[0]\n : atWinnerTime[atWinnerTime.length - 1];\n const referral = await this.createAttributedReferral({\n tenantId: program.tenantId,\n referrerId: winner.referrerId,\n programId: input.programId,\n targetKind: input.targetKind,\n targetId: input.targetId,\n policy,\n creditFraction: 1.0,\n splitGroupId: '',\n primaryTouchId: winner.id ?? '',\n now,\n });\n return { referrals: [referral], exception: null };\n }\n\n case 'assigned': {\n // Only explicit manual assignments carry credit in assigned mode —\n // clicks and code entries are context, never winners.\n const assignments = eligible.filter(\n (touch) => touch.kind === 'manual_assignment',\n );\n if (assignments.length === 0) {\n return {\n referrals: [],\n exception: null,\n refused: 'no_eligible_touches',\n };\n }\n const assignedReferrers = new Set(\n assignments.map((touch) => touch.referrerId),\n );\n if (assignedReferrers.size > 1) {\n const exception = await this.createOpenException({\n input,\n program,\n policy,\n conflictReason: 'competing_touches',\n candidates: assignments,\n });\n return { referrals: [], exception };\n }\n const winner = assignments[assignments.length - 1]; // most recent\n const referral = await this.createAttributedReferral({\n tenantId: program.tenantId,\n referrerId: winner.referrerId,\n programId: input.programId,\n targetKind: input.targetKind,\n targetId: input.targetId,\n policy,\n creditFraction: 1.0,\n splitGroupId: '',\n primaryTouchId: winner.id ?? '',\n now,\n });\n return { referrals: [referral], exception: null };\n }\n\n case 'split': {\n const fractions = AttributionService.equalFractions(\n distinctReferrerIds.length,\n );\n const splitGroupId = distinctReferrerIds.length > 1 ? randomUUID() : '';\n const referrals: Referral[] = [];\n for (let i = 0; i < distinctReferrerIds.length; i++) {\n const referrerId = distinctReferrerIds[i];\n // The referrer's earliest eligible touch anchors their credit.\n const primaryTouch = eligible.find(\n (touch) => touch.referrerId === referrerId,\n );\n referrals.push(\n await this.createAttributedReferral({\n tenantId: program.tenantId,\n referrerId,\n programId: input.programId,\n targetKind: input.targetKind,\n targetId: input.targetId,\n policy,\n creditFraction: fractions[i],\n splitGroupId,\n primaryTouchId: primaryTouch?.id ?? '',\n now,\n }),\n );\n }\n return { referrals, exception: null };\n }\n }\n }\n\n /**\n * Record an operator's manual assignment as an immutable\n * `manual_assignment` ReferralTouch (evidence carries the actor, reason,\n * and target) and return it. The caller then runs {@link resolve} — under\n * an `assigned`-mode policy the assignment wins over click evidence.\n */\n async recordManualAssignment(\n input: RecordManualAssignmentInput,\n ): Promise<ReferralTouch> {\n if (!input.actorProfileId) {\n throw new Error(\n 'AttributionService.recordManualAssignment requires an actorProfileId',\n );\n }\n const program = await this.requireProgram(input.programId);\n const occurredAt = input.occurredAt ?? new Date();\n const useSubject = Boolean(input.subjectKind && input.subjectId);\n return await this.deps.touches.create({\n tenantId: program.tenantId,\n referrerId: input.referrerId,\n programId: input.programId,\n kind: 'manual_assignment',\n subjectKind: useSubject\n ? (input.subjectKind as string)\n : input.targetKind,\n subjectId: useSubject ? (input.subjectId as string) : input.targetId,\n occurredAt,\n evidence: JSON.stringify({\n actorProfileId: input.actorProfileId,\n reason: input.reason ?? '',\n targetKind: input.targetKind,\n targetId: input.targetId,\n }),\n });\n }\n\n /**\n * Resolve an OPEN exception by awarding credit explicitly.\n *\n * Requires a non-empty `resolutionReason` (throws otherwise) and awards\n * whose `creditFraction`s sum to 1.0 (±0.0001) across distinct referrers.\n * Creates the attributed referral(s) from the exception's target/program\n * (split semantics — shared `splitGroupId` — when several awards), then\n * marks the exception resolved (`resolutionMode: 'override'`, the award\n * audit fields, `resolvedReferralIds`).\n */\n async resolveException(\n input: ResolveExceptionInput,\n ): Promise<AttributionAwardResult> {\n AttributionService.assertResolutionReason(\n input.resolutionReason,\n 'resolveException',\n );\n AttributionService.assertActor(input.actorProfileId, 'resolveException');\n AttributionService.assertAwards(input.awards);\n const now = input.now ?? new Date();\n\n const exception = await this.deps.exceptions.get({ id: input.exceptionId });\n if (!exception) {\n throw new Error(\n `AttributionService.resolveException: exception '${input.exceptionId}' not found`,\n );\n }\n if (!exception.isOpen()) {\n throw new Error(\n `AttributionService.resolveException: exception '${input.exceptionId}' is already resolved`,\n );\n }\n\n const policyPin = await this.resolvePolicyPin(exception);\n const candidates = exception.getCandidates();\n const splitGroupId = input.awards.length > 1 ? randomUUID() : '';\n const referrals: Referral[] = [];\n for (const award of input.awards) {\n const candidate = candidates.find(\n (entry) => entry.referrerId === award.referrerId,\n );\n referrals.push(\n await this.createAttributedReferral({\n tenantId: exception.tenantId,\n referrerId: award.referrerId,\n programId: exception.programId,\n targetKind: exception.targetKind,\n targetId: exception.targetId,\n policy: null,\n policyPin,\n creditFraction: award.creditFraction,\n splitGroupId,\n primaryTouchId: candidate?.touchId ?? '',\n now,\n }),\n );\n }\n\n exception.markResolved({\n mode: 'override',\n reason: input.resolutionReason,\n resolvedByProfileId: input.actorProfileId,\n referralIds: referrals.map((referral) => referral.id ?? ''),\n at: now,\n });\n await exception.save();\n\n return { referrals, exception };\n }\n\n /**\n * Re-attribute an already-attributed target.\n *\n * Requires a non-empty `resolutionReason` and awards summing to 1.0.\n * REFUSES (throws {@link QualifiedReferralOverrideError}) when the target\n * has a QUALIFIED referral — those are governed by term snapshots and\n * commission adjustments downstream. Otherwise: disqualifies the existing\n * `attributed` referrals for the target+program (stamping the audit\n * exception's id into their metadata), creates the newly awarded\n * attributed referral(s), and writes a RESOLVED AttributionException as\n * the audit record (candidates = the displaced + awarded referrers).\n *\n * The new referrals inherit the POLICY PIN the displaced credit carried\n * (an override re-decides WHO earned the introduction, not which policy\n * version governed it); the program's current default active policy is\n * the fallback when nothing was displaced.\n */\n async override(\n input: OverrideAttributionInput,\n ): Promise<AttributionAwardResult> {\n AttributionService.assertResolutionReason(\n input.resolutionReason,\n 'override',\n );\n AttributionService.assertActor(input.actorProfileId, 'override');\n AttributionService.assertAwards(input.awards);\n const now = input.now ?? new Date();\n\n const forTarget = (\n await this.deps.referrals.findByTarget(input.targetKind, input.targetId)\n ).filter((referral) => referral.programId === input.programId);\n\n const qualified = forTarget.filter(\n (referral) => referral.status === 'qualified',\n );\n if (qualified.length > 0) {\n throw new QualifiedReferralOverrideError(\n qualified.map((referral) => referral.id ?? ''),\n );\n }\n const displaced = forTarget.filter(\n (referral) => referral.status === 'attributed',\n );\n\n const program = await this.requireProgram(input.programId);\n const policyPin = await this.resolveOverridePolicyPin(program, displaced);\n\n // Audit exception: candidates are the displaced referrers (through\n // their winning touches where known) plus the awarded referrers.\n const candidates: AttributionExceptionCandidate[] = [];\n for (const referral of displaced) {\n const touch = referral.primaryTouchId\n ? await this.deps.touches.get({ id: referral.primaryTouchId })\n : null;\n candidates.push({\n touchId: referral.primaryTouchId,\n referrerId: referral.referrerId,\n kind: touch?.kind ?? 'manual_assignment',\n occurredAt: (\n touch?.occurredAt ??\n referral.attributedAt ??\n now\n ).toISOString(),\n });\n }\n for (const award of input.awards) {\n candidates.push({\n touchId: '',\n referrerId: award.referrerId,\n kind: 'manual_assignment',\n occurredAt: now.toISOString(),\n });\n }\n\n const exception = await this.deps.exceptions.create({\n tenantId: program.tenantId,\n targetKind: input.targetKind,\n targetId: input.targetId,\n programId: input.programId,\n status: 'open',\n conflictReason: 'override',\n candidates: JSON.stringify(candidates),\n metadata: JSON.stringify(policyPin),\n });\n\n // Displace the previous credit, pointing each row at its audit record.\n for (const referral of displaced) {\n referral.setMetadata({\n ...referral.getMetadata(),\n overriddenByExceptionId: exception.id ?? '',\n overriddenAt: now.toISOString(),\n });\n referral.disqualify();\n await referral.save();\n }\n\n const splitGroupId = input.awards.length > 1 ? randomUUID() : '';\n const referrals: Referral[] = [];\n for (const award of input.awards) {\n referrals.push(\n await this.createAttributedReferral({\n tenantId: program.tenantId,\n referrerId: award.referrerId,\n programId: input.programId,\n targetKind: input.targetKind,\n targetId: input.targetId,\n policy: null,\n policyPin,\n creditFraction: award.creditFraction,\n splitGroupId,\n primaryTouchId: '',\n now,\n }),\n );\n }\n\n exception.markResolved({\n mode: 'override',\n reason: input.resolutionReason,\n resolvedByProfileId: input.actorProfileId,\n referralIds: referrals.map((referral) => referral.id ?? ''),\n at: now,\n });\n await exception.save();\n\n return { referrals, exception };\n }\n\n // -------- Internals --------\n\n private async requireProgram(programId: string): Promise<ReferralProgram> {\n const program = await this.deps.programs.get({ id: programId });\n if (!program) {\n throw new Error(`AttributionService: program '${programId}' not found`);\n }\n return program;\n }\n\n /**\n * The active policy governing a resolution: the explicit `policyKey`\n * override when given, else the program's default key; resolved to its\n * latest ACTIVE version. `null` when no key or no active version.\n */\n private async resolvePolicy(\n program: ReferralProgram,\n policyKeyOverride: string | undefined,\n at: Date = new Date(),\n ): Promise<AttributionPolicy | null> {\n const policyKey = policyKeyOverride ?? program.defaultAttributionPolicyKey;\n if (!policyKey) return null;\n // Resolve the version in effect at the resolution clock, scoped to the\n // PROGRAM's tenant (with global fallback) — system/background paths run\n // without ambient tenant context, and another tenant's same-named\n // policy must never govern this program's attributions.\n return await this.deps.policies.latestActiveByKey(\n policyKey,\n at,\n program.tenantId,\n );\n }\n\n /**\n * Candidate touches for a resolution: gathered by the subject pair (when\n * given) and by the target pair, deduplicated, restricted to the program,\n * to `occurredAt` within `[now - windowDays, now]`, and to referrers that\n * pass the self-referral gate. Sorted by `occurredAt` ascending.\n */\n private async gatherEligibleTouches(\n input: ResolveAttributionInput,\n policy: AttributionPolicy,\n now: Date,\n ): Promise<ReferralTouch[]> {\n const byId = new Map<string, ReferralTouch>();\n if (input.subjectKind && input.subjectId) {\n for (const touch of await this.deps.touches.findBySubject(\n input.subjectKind,\n input.subjectId,\n )) {\n byId.set(touch.id ?? '', touch);\n }\n }\n for (const touch of await this.deps.touches.findBySubject(\n input.targetKind,\n input.targetId,\n )) {\n byId.set(touch.id ?? '', touch);\n }\n\n const windowStart = now.getTime() - policy.windowDays * MS_PER_DAY;\n const inWindow = [...byId.values()].filter((touch) => {\n if (touch.programId !== input.programId) return false;\n const at = touch.occurredAt.getTime();\n return at >= windowStart && at <= now.getTime();\n });\n\n // Self-referral gate: drop touches whose referrer IS the prospect,\n // unless the policy explicitly allows self-referrals.\n let eligible = inWindow;\n if (!policy.allowSelfReferral && input.subjectProfileId) {\n const referrerProfiles = new Map<string, string>();\n for (const touch of inWindow) {\n if (referrerProfiles.has(touch.referrerId)) continue;\n const referrer = await this.deps.referrers.get({\n id: touch.referrerId,\n });\n referrerProfiles.set(touch.referrerId, referrer?.profileId ?? '');\n }\n eligible = inWindow.filter(\n (touch) =>\n referrerProfiles.get(touch.referrerId) !== input.subjectProfileId,\n );\n }\n\n return eligible.sort(\n (a, b) => a.occurredAt.getTime() - b.occurredAt.getTime(),\n );\n }\n\n /** Create an OPEN exception carrying the competing candidates. */\n private async createOpenException(params: {\n input: ResolveAttributionInput;\n program: ReferralProgram;\n policy: AttributionPolicy;\n conflictReason: string;\n candidates: ReferralTouch[];\n }): Promise<AttributionException> {\n const { input, program, policy } = params;\n return await this.deps.exceptions.create({\n tenantId: program.tenantId,\n targetKind: input.targetKind,\n targetId: input.targetId,\n programId: input.programId,\n status: 'open',\n conflictReason: params.conflictReason,\n candidates: JSON.stringify(\n params.candidates.map(\n (touch): AttributionExceptionCandidate => ({\n touchId: touch.id ?? '',\n referrerId: touch.referrerId,\n kind: touch.kind,\n occurredAt: touch.occurredAt.toISOString(),\n }),\n ),\n ),\n // Pin the governing policy so a later resolveException() stamps the\n // SAME policy version the conflict arose under.\n metadata: JSON.stringify({\n policyKey: policy.policyKey,\n policyVersion: policy.version,\n windowDays: policy.windowDays,\n }),\n });\n }\n\n /**\n * The policy pin an exception was raised under (stamped into its\n * metadata at creation); falls back to the program's current default\n * active policy for exceptions created without one.\n */\n private async resolvePolicyPin(exception: AttributionException): Promise<{\n policyKey: string;\n policyVersion: number;\n windowDays: number | null;\n }> {\n const meta = exception.getMetadata();\n if (typeof meta.policyKey === 'string' && meta.policyKey) {\n return {\n policyKey: meta.policyKey,\n policyVersion:\n typeof meta.policyVersion === 'number' ? meta.policyVersion : 0,\n windowDays:\n typeof meta.windowDays === 'number' ? meta.windowDays : null,\n };\n }\n if (exception.programId) {\n const program = await this.deps.programs.get({\n id: exception.programId,\n });\n if (program) {\n const policy = await this.resolvePolicy(program, undefined);\n if (policy) {\n return {\n policyKey: policy.policyKey,\n policyVersion: policy.version,\n windowDays: policy.windowDays,\n };\n }\n }\n }\n return { policyKey: '', policyVersion: 0, windowDays: null };\n }\n\n /**\n * The policy pin an override re-attributes under: the pin the displaced\n * credit carried (looking the exact version's `windowDays` back up for\n * the new expiry), falling back to the program's current default active\n * policy when nothing pinned was displaced.\n */\n private async resolveOverridePolicyPin(\n program: ReferralProgram,\n displaced: Referral[],\n ): Promise<{\n policyKey: string;\n policyVersion: number;\n windowDays: number | null;\n }> {\n const pinned = displaced.find((referral) => referral.policyKey);\n if (pinned) {\n // Program-tenant lane with global fallback — an override running\n // without ambient tenant context must not adopt another tenant's\n // same-keyed policy version for the expiry window.\n const versions = await this.deps.policies.list({\n where: { policyKey: pinned.policyKey, version: pinned.policyVersion },\n });\n const laneVersion =\n versions.find((policy) => policy.tenantId === program.tenantId) ??\n versions.find((policy) => policy.tenantId === null) ??\n null;\n return {\n policyKey: pinned.policyKey,\n policyVersion: pinned.policyVersion,\n windowDays: laneVersion?.windowDays ?? null,\n };\n }\n const policy = await this.resolvePolicy(program, undefined);\n return policy\n ? {\n policyKey: policy.policyKey,\n policyVersion: policy.version,\n windowDays: policy.windowDays,\n }\n : { policyKey: '', policyVersion: 0, windowDays: null };\n }\n\n /**\n * Create one referral as `pending`, transition it `pending → attributed`\n * (stamping `attributedAt`), set the policy pin and expiry, and save.\n */\n private async createAttributedReferral(params: {\n tenantId: string | null;\n referrerId: string;\n programId: string;\n targetKind: string;\n targetId: string;\n /** The live policy (resolve path) … */\n policy: AttributionPolicy | null;\n /** … or an explicit pin (exception/override paths). */\n policyPin?: {\n policyKey: string;\n policyVersion: number;\n windowDays: number | null;\n };\n creditFraction: number;\n splitGroupId: string;\n primaryTouchId: string;\n now: Date;\n }): Promise<Referral> {\n const pin = params.policy\n ? {\n policyKey: params.policy.policyKey,\n policyVersion: params.policy.version,\n windowDays: params.policy.windowDays as number | null,\n }\n : (params.policyPin ?? {\n policyKey: '',\n policyVersion: 0,\n windowDays: null,\n });\n\n const referral = await this.deps.referrals.create({\n tenantId: params.tenantId,\n referrerId: params.referrerId,\n programId: params.programId,\n targetKind: params.targetKind,\n targetId: params.targetId,\n policyKey: pin.policyKey,\n policyVersion: pin.policyVersion,\n creditFraction: params.creditFraction,\n splitGroupId: params.splitGroupId,\n primaryTouchId: params.primaryTouchId,\n status: 'pending',\n });\n referral.markAttributed(params.now);\n referral.expiresAt =\n pin.windowDays !== null\n ? new Date(params.now.getTime() + pin.windowDays * MS_PER_DAY)\n : null;\n await referral.save();\n return referral;\n }\n\n /**\n * Equal split fractions for `n` referrers, each rounded to 4 decimal\n * places, with the LAST adjusted so the set sums to exactly 1.0.\n */\n private static equalFractions(n: number): number[] {\n if (n <= 1) return [1.0];\n const base = Math.floor(10000 / n) / 10000;\n const fractions = new Array<number>(n).fill(base);\n fractions[n - 1] = Math.round((1 - base * (n - 1)) * 10000) / 10000;\n return fractions;\n }\n\n private static assertResolutionReason(reason: string, method: string): void {\n if (!reason?.trim()) {\n throw new Error(\n `AttributionService.${method} requires a non-empty resolutionReason`,\n );\n }\n }\n\n /**\n * Attribution decisions are audited — a blank actor would permanently\n * resolve/override credit with an empty `resolvedByProfileId`, defeating\n * the audit trail.\n */\n private static assertActor(actorProfileId: string, method: string): void {\n if (!actorProfileId?.trim()) {\n throw new Error(\n `AttributionService.${method} requires a non-empty actorProfileId`,\n );\n }\n }\n\n private static assertAwards(awards: AttributionAward[]): void {\n if (!awards || awards.length === 0) {\n throw new Error('AttributionService: at least one award is required');\n }\n const seen = new Set<string>();\n let sum = 0;\n for (const award of awards) {\n if (!award.referrerId) {\n throw new Error('AttributionService: award.referrerId is required');\n }\n if (seen.has(award.referrerId)) {\n throw new Error(\n `AttributionService: duplicate award for referrer '${award.referrerId}'`,\n );\n }\n seen.add(award.referrerId);\n if (\n !Number.isFinite(award.creditFraction) ||\n award.creditFraction <= 0 ||\n award.creditFraction > 1\n ) {\n throw new Error(\n `AttributionService: award.creditFraction must be in (0, 1] — got ${award.creditFraction}`,\n );\n }\n sum += award.creditFraction;\n }\n if (Math.abs(sum - 1) > 0.0001) {\n throw new Error(\n `AttributionService: award creditFractions must sum to 1.0 (±0.0001) — got ${sum}`,\n );\n }\n }\n}\n\nexport default AttributionService;\n","/**\n * ReferralCommissionService — the bridge from qualified Referrals to the\n * commissions financial core (#1932).\n *\n * Given an EarningEvent (e.g. an invoice payment), the service finds the\n * qualified referrals it should pay, loads each one's FROZEN\n * ReferralTermSnapshot, resolves the referrer's Earner, and hands the\n * snapshot's components to `CommissionCalculationService.calculateForEvent`.\n * Calculation NEVER touches a live plan — reproducibility comes from the\n * snapshot, idempotency from the calculation service's dedupe keys.\n *\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n type Commission,\n CommissionCalculationService,\n CommissionCollection,\n type CommissionComponentSkip,\n type EarningEvent,\n} from '../../commissions/index.js';\nimport { ReferralCollection } from '../collections/ReferralCollection.js';\nimport { ReferralTermSnapshotCollection } from '../collections/ReferralTermSnapshotCollection.js';\nimport { ReferrerCollection } from '../collections/ReferrerCollection.js';\nimport type { Referral } from '../models/Referral.js';\n\n/**\n * The `termsSnapshotKind` every referral-bridged Commission carries —\n * points `termsSnapshotId` at a ReferralTermSnapshot row.\n */\nexport const REFERRAL_TERMS_SNAPSHOT_KIND = 'referral_term_snapshot';\n\n/** Collaborators for {@link ReferralCommissionService}. */\nexport interface ReferralCommissionServiceDeps {\n referrals: ReferralCollection;\n snapshots: ReferralTermSnapshotCollection;\n referrers: ReferrerCollection;\n /** The commissions module's calculation service (same package). */\n calculation: CommissionCalculationService;\n /** The commissions module's commission collection (occurrence counting). */\n commissions: CommissionCollection;\n}\n\n/** Input for {@link ReferralCommissionService.processEarningEvent}. */\nexport interface ProcessEarningEventInput {\n /** The (persisted) earning event to pay referrals from. */\n event: EarningEvent;\n /** Resolve qualified referrals by target … */\n targetKind?: string;\n targetId?: string;\n /** … or name them explicitly (takes precedence over the target pair). */\n referralIds?: string[];\n /** Reserved clock override for symmetry with the sibling services. */\n now?: Date;\n}\n\n/** Why a referral produced no calculation. */\nexport type ReferralProcessingSkipReason =\n | 'referral_not_found'\n | 'not_qualified'\n | 'missing_snapshot'\n | 'referrer_missing_earner'\n | 'tenant_mismatch';\n\n/** One referral the bridge skipped, and why. */\nexport interface ReferralProcessingSkip {\n referralId: string;\n reason: ReferralProcessingSkipReason;\n}\n\n/** Per-referral calculation outcome. */\nexport interface ReferralCommissionOutcome {\n referralId: string;\n /** Commissions newly created for this referral by THIS call. */\n created: Commission[];\n /** Idempotent replays — commissions that already existed. */\n existing: Commission[];\n /** Components the calculation declined, with reasons. */\n skipped: CommissionComponentSkip[];\n}\n\n/** Result of {@link ReferralCommissionService.processEarningEvent}. */\nexport interface ProcessEarningEventResult {\n /** One entry per qualified referral that reached calculation. */\n results: ReferralCommissionOutcome[];\n /** Referrals that never reached calculation, with reasons. */\n skippedReferrals: ReferralProcessingSkip[];\n}\n\nexport class ReferralCommissionService {\n constructor(private readonly deps: ReferralCommissionServiceDeps) {}\n\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<ReferralCommissionService> {\n return new ReferralCommissionService({\n referrals: await ReferralCollection.create(classOptions),\n snapshots: await ReferralTermSnapshotCollection.create(classOptions),\n referrers: await ReferrerCollection.create(classOptions),\n calculation: await CommissionCalculationService.create(classOptions),\n commissions: await CommissionCollection.create(classOptions),\n });\n }\n\n /**\n * Turn one earning event into Commissions for every qualified referral it\n * concerns.\n *\n * Referral resolution: explicit `referralIds` when given (rows that are\n * missing / not qualified are reported in `skippedReferrals`), else\n * `findQualifiedByTarget(targetKind, targetId)`. One of the two forms is\n * required.\n *\n * Per referral:\n * 1. Load its ReferralTermSnapshot (`'missing_snapshot'` skip when the\n * pointer is empty/dangling).\n * 2. Resolve the referrer's `earnerId`\n * (`'referrer_missing_earner'` skip when absent — roles without a\n * payout account cannot earn).\n * 3. Delegate to `CommissionCalculationService.calculateForEvent` with\n * the snapshot's plan pin, FROZEN components, currency (mismatches\n * surface as the calc service's `currency_mismatch` skips),\n * clearingDays, the referral's `creditFraction` as the share, its\n * `splitGroupId`, the\n * `('referral_term_snapshot', snapshot.id)` terms reference, and an\n * occurrence resolver counting existing Commissions for\n * `(termsSnapshotId, componentKey)` from PRIOR events.\n *\n * Fully idempotent on replay: the calculation service's dedupe keys\n * return prior rows in each outcome's `existing`.\n */\n async processEarningEvent(\n input: ProcessEarningEventInput,\n ): Promise<ProcessEarningEventResult> {\n const { event } = input;\n if (!event?.id) {\n throw new Error(\n 'ReferralCommissionService.processEarningEvent requires a persisted event (missing id)',\n );\n }\n\n const skippedReferrals: ReferralProcessingSkip[] = [];\n const referrals: Referral[] = [];\n\n if (input.referralIds && input.referralIds.length > 0) {\n for (const referralId of input.referralIds) {\n const referral = await this.deps.referrals.get({ id: referralId });\n if (!referral) {\n skippedReferrals.push({ referralId, reason: 'referral_not_found' });\n continue;\n }\n if (referral.status !== 'qualified') {\n skippedReferrals.push({ referralId, reason: 'not_qualified' });\n continue;\n }\n referrals.push(referral);\n }\n } else if (input.targetKind && input.targetId) {\n referrals.push(\n ...(await this.deps.referrals.findQualifiedByTarget(\n input.targetKind,\n input.targetId,\n )),\n );\n } else {\n throw new Error(\n 'ReferralCommissionService.processEarningEvent requires referralIds or a (targetKind, targetId) pair',\n );\n }\n\n const results: ReferralCommissionOutcome[] = [];\n for (const referral of referrals) {\n const referralId = referral.id ?? '';\n\n const snapshot = referral.snapshotId\n ? await this.deps.snapshots.get({ id: referral.snapshotId })\n : null;\n if (!snapshot?.id) {\n skippedReferrals.push({ referralId, reason: 'missing_snapshot' });\n continue;\n }\n\n // Tenant-lane check: without ambient tenant context, explicit\n // referral ids (or target pairs reused across tenants) could pull a\n // qualified referral from ANOTHER tenant and pay its earner from\n // this event. The event, referral, and snapshot must share one lane\n // (a NULL-tenant event may feed tenant referrals — operator-level\n // ingestion — but a tenant event never crosses into another tenant).\n const lane = referral.tenantId ?? null;\n if (\n (event.tenantId !== null && event.tenantId !== lane) ||\n (snapshot.tenantId ?? null) !== lane\n ) {\n skippedReferrals.push({ referralId, reason: 'tenant_mismatch' });\n continue;\n }\n\n const referrer = await this.deps.referrers.get({\n id: referral.referrerId,\n });\n if (\n referrer &&\n referrer.tenantId !== null &&\n referrer.tenantId !== lane\n ) {\n skippedReferrals.push({ referralId, reason: 'tenant_mismatch' });\n continue;\n }\n if (!referrer?.earnerId) {\n skippedReferrals.push({\n referralId,\n reason: 'referrer_missing_earner',\n });\n continue;\n }\n\n const snapshotId = snapshot.id;\n const calculation = await this.deps.calculation.calculateForEvent({\n event,\n planKey: snapshot.planKey,\n planVersion: snapshot.planVersion,\n components: snapshot.getComponents(),\n earnerId: referrer.earnerId,\n shareFraction: referral.creditFraction,\n splitGroupId: referral.splitGroupId,\n termsSnapshotKind: REFERRAL_TERMS_SNAPSHOT_KIND,\n termsSnapshotId: snapshotId,\n currency: snapshot.currency,\n clearingDays: snapshot.clearingDays,\n // Recurrence windows (`windowMonths`) anchor at QUALIFICATION —\n // the moment the terms bound. Without an anchor the calc service\n // deliberately skips the window check, which would pay\n // time-limited recurring components forever.\n anchorAt: referral.qualifiedAt ?? undefined,\n // Occurrences consumed under THESE terms from PRIOR events — the\n // current event must not count against recurrence limits (its own\n // replay is handled by the calc service's idempotency).\n occurrenceCountResolver: async (componentKey: string) => {\n const prior = await this.deps.commissions.list({\n where: { termsSnapshotId: snapshotId, componentKey },\n });\n return prior.filter(\n (commission) => commission.earningEventId !== event.id,\n ).length;\n },\n });\n\n results.push({\n referralId,\n created: calculation.created,\n existing: calculation.existing,\n skipped: calculation.skipped,\n });\n }\n\n return { results, skippedReferrals };\n }\n}\n\nexport default ReferralCommissionService;\n","/**\n * ReferralQualificationService — turns an attributed Referral into a\n * QUALIFIED one by freezing the governing terms into an immutable\n * ReferralTermSnapshot.\n *\n * Qualification is the moment terms bind: the active ReferralAgreement is\n * resolved, the CommissionPlan version is pinned (the agreement's explicit\n * pin, or the latest active version when unpinned), and the plan's\n * components are COPIED into the snapshot. Everything the commission bridge\n * later needs comes from that frozen row — plan amendments after\n * qualification change nothing unless {@link requalify} is explicitly\n * invoked.\n *\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n type CommissionPlan,\n CommissionPlanCollection,\n} from '../../commissions/index.js';\nimport { AttributionPolicyCollection } from '../collections/AttributionPolicyCollection.js';\nimport { ReferralAgreementCollection } from '../collections/ReferralAgreementCollection.js';\nimport { ReferralCollection } from '../collections/ReferralCollection.js';\nimport { ReferralTermSnapshotCollection } from '../collections/ReferralTermSnapshotCollection.js';\nimport { ReferrerCollection } from '../collections/ReferrerCollection.js';\nimport type { Referral } from '../models/Referral.js';\nimport type { ReferralAgreement } from '../models/ReferralAgreement.js';\nimport type { ReferralTermSnapshot } from '../models/ReferralTermSnapshot.js';\n\n/** Collaborators for {@link ReferralQualificationService}. */\nexport interface ReferralQualificationServiceDeps {\n referrals: ReferralCollection;\n agreements: ReferralAgreementCollection;\n /** The commissions module's plan collection (same package). */\n plans: CommissionPlanCollection;\n policies: AttributionPolicyCollection;\n snapshots: ReferralTermSnapshotCollection;\n referrers: ReferrerCollection;\n}\n\n/** Why {@link ReferralQualificationService.qualify} refused. */\nexport type QualificationRefusalReason =\n | 'not_attributed'\n | 'existing_client_ineligible'\n | 'self_referral_ineligible'\n | 'no_active_agreement'\n | 'no_active_plan';\n\n/** Input for {@link ReferralQualificationService.qualify}. */\nexport interface QualifyReferralInput {\n referralId: string;\n /** Clock override for deterministic tests. */\n now?: Date;\n /** Re-check flag: the prospect is already a client. */\n isExistingClient?: boolean;\n /** Re-check flag: the prospect's own profile id (self-referral gate). */\n subjectProfileId?: string;\n}\n\n/** Result of {@link ReferralQualificationService.qualify}. */\nexport interface QualifyReferralResult {\n /** Whether the referral is (now or already) qualified. */\n qualified: boolean;\n /** `true` only when THIS call minted the snapshot. */\n created: boolean;\n /** The governing snapshot (`null` on refusal). */\n snapshot: ReferralTermSnapshot | null;\n /** The (re)loaded referral (`null` only when the id is unknown). */\n referral: Referral | null;\n /** Why qualification was refused, when it was. */\n reason?: QualificationRefusalReason;\n}\n\n/** Input for {@link ReferralQualificationService.requalify}. */\nexport interface RequalifyReferralInput {\n referralId: string;\n /** Non-empty rationale recorded in the referral metadata. Required. */\n reason: string;\n /** Clock override for deterministic tests. */\n now?: Date;\n}\n\n/** Result of {@link ReferralQualificationService.requalify}. */\nexport interface RequalifyReferralResult {\n /** Whether a new snapshot now governs the referral. */\n requalified: boolean;\n /** The NEW governing snapshot (`null` on refusal). */\n snapshot: ReferralTermSnapshot | null;\n /** The previously governing snapshot's id (kept as history). */\n previousSnapshotId: string;\n /** Why requalification was refused, when it was. */\n reason?: 'no_active_agreement' | 'no_active_plan';\n}\n\nexport class ReferralQualificationService {\n constructor(private readonly deps: ReferralQualificationServiceDeps) {}\n\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<ReferralQualificationService> {\n return new ReferralQualificationService({\n referrals: await ReferralCollection.create(classOptions),\n agreements: await ReferralAgreementCollection.create(classOptions),\n plans: await CommissionPlanCollection.create(classOptions),\n policies: await AttributionPolicyCollection.create(classOptions),\n snapshots: await ReferralTermSnapshotCollection.create(classOptions),\n referrers: await ReferrerCollection.create(classOptions),\n });\n }\n\n /**\n * Qualify an attributed referral.\n *\n * Flow:\n * 1. **Status** — the referral must be `attributed`\n * (`reason: 'not_attributed'` otherwise). Already `qualified` →\n * IDEMPOTENT: the existing snapshot returns with `created: false`.\n * 2. **Eligibility re-check** — the attribution-time policy version's\n * flags are re-applied against the caller's current knowledge:\n * `isExistingClient` (`existing_client_ineligible`) and\n * `subjectProfileId` vs the referrer's profile\n * (`self_referral_ineligible`). Refusals do NOT change the referral's\n * status — disqualification stays an explicit caller decision.\n * 3. **Agreement** — the ACTIVE ReferralAgreement for\n * (referrerId, programId) effective at `now`\n * (`reason: 'no_active_agreement'` when none).\n * 4. **Plan** — the agreement's pinned `commissionPlanVersion`, or the\n * latest active version when the pin is `0`; the resolved version must\n * be ACTIVE (`reason: 'no_active_plan'` otherwise).\n * 5. **Snapshot** — mint the immutable ReferralTermSnapshot (frozen\n * component copy, currency, clearingDays, approvalMode, agreement +\n * plan + policy version refs), point `referral.snapshotId` at it, and\n * transition `attributed → qualified` (stamping `qualifiedAt`).\n */\n async qualify(input: QualifyReferralInput): Promise<QualifyReferralResult> {\n const now = input.now ?? new Date();\n const referral = await this.requireReferral(input.referralId, 'qualify');\n\n // Idempotent replay: already qualified → hand back the governing\n // snapshot without touching anything.\n if (referral.status === 'qualified') {\n const snapshot = referral.snapshotId\n ? await this.deps.snapshots.get({ id: referral.snapshotId })\n : null;\n return { qualified: true, created: false, snapshot, referral };\n }\n\n if (referral.status !== 'attributed') {\n return {\n qualified: false,\n created: false,\n snapshot: null,\n referral,\n reason: 'not_attributed',\n };\n }\n\n // Re-check the policy eligibility flags with the caller's CURRENT\n // knowledge — facts learned between attribution and qualification\n // (existing-client discovery, prospect identity) must still gate.\n const policy = await this.findPolicyVersion(\n referral.policyKey,\n referral.policyVersion,\n referral.tenantId,\n );\n if (policy) {\n if (input.isExistingClient && !policy.allowExistingClients) {\n return {\n qualified: false,\n created: false,\n snapshot: null,\n referral,\n reason: 'existing_client_ineligible',\n };\n }\n if (input.subjectProfileId && !policy.allowSelfReferral) {\n const referrer = await this.deps.referrers.get({\n id: referral.referrerId,\n });\n if (referrer && referrer.profileId === input.subjectProfileId) {\n return {\n qualified: false,\n created: false,\n snapshot: null,\n referral,\n reason: 'self_referral_ineligible',\n };\n }\n }\n }\n\n const agreement = await this.deps.agreements.activeFor(\n referral.referrerId,\n referral.programId,\n now,\n );\n if (!agreement) {\n return {\n qualified: false,\n created: false,\n snapshot: null,\n referral,\n reason: 'no_active_agreement',\n };\n }\n\n const plan = await this.resolvePlan(agreement, now);\n if (!plan) {\n return {\n qualified: false,\n created: false,\n snapshot: null,\n referral,\n reason: 'no_active_plan',\n };\n }\n\n const snapshot = await this.mintSnapshot(referral, agreement, plan);\n referral.snapshotId = snapshot.id ?? '';\n referral.markQualified(now);\n await referral.save();\n\n return { qualified: true, created: true, snapshot, referral };\n }\n\n /**\n * Explicitly re-snapshot a QUALIFIED referral under the CURRENT terms —\n * the \"amendment explicitly applied\" path: after a plan/agreement\n * amendment, earnings keep flowing through the OLD snapshot until an\n * operator invokes this.\n *\n * Creates a NEW snapshot from the currently active agreement/plan,\n * repoints `referral.snapshotId`, leaves the old snapshot row untouched\n * (history — commissions it produced still reference it), and appends\n * `{ at, reason, fromSnapshotId, toSnapshotId }` to the referral\n * metadata's `requalifications` array.\n *\n * Throws on misuse (unknown referral, non-qualified status, empty\n * reason); refuses with a typed reason when no active agreement/plan\n * currently governs.\n */\n async requalify(\n input: RequalifyReferralInput,\n ): Promise<RequalifyReferralResult> {\n if (!input.reason?.trim()) {\n throw new Error(\n 'ReferralQualificationService.requalify requires a non-empty reason',\n );\n }\n const now = input.now ?? new Date();\n const referral = await this.requireReferral(input.referralId, 'requalify');\n if (referral.status !== 'qualified') {\n throw new Error(\n `ReferralQualificationService.requalify: referral '${input.referralId}' is '${referral.status}' — only qualified referrals can be re-snapshotted`,\n );\n }\n\n const agreement = await this.deps.agreements.activeFor(\n referral.referrerId,\n referral.programId,\n now,\n );\n if (!agreement) {\n return {\n requalified: false,\n snapshot: null,\n previousSnapshotId: referral.snapshotId,\n reason: 'no_active_agreement',\n };\n }\n const plan = await this.resolvePlan(agreement, now);\n if (!plan) {\n return {\n requalified: false,\n snapshot: null,\n previousSnapshotId: referral.snapshotId,\n reason: 'no_active_plan',\n };\n }\n\n const previousSnapshotId = referral.snapshotId;\n const snapshot = await this.mintSnapshot(referral, agreement, plan);\n\n const meta = referral.getMetadata();\n const history = Array.isArray(meta.requalifications)\n ? (meta.requalifications as unknown[])\n : [];\n history.push({\n at: now.toISOString(),\n reason: input.reason,\n fromSnapshotId: previousSnapshotId,\n toSnapshotId: snapshot.id ?? '',\n });\n referral.setMetadata({ ...meta, requalifications: history });\n referral.snapshotId = snapshot.id ?? '';\n await referral.save();\n\n return { requalified: true, snapshot, previousSnapshotId };\n }\n\n // -------- Internals --------\n\n private async requireReferral(\n referralId: string,\n method: string,\n ): Promise<Referral> {\n const referral = await this.deps.referrals.get({ id: referralId });\n if (!referral) {\n throw new Error(\n `ReferralQualificationService.${method}: referral '${referralId}' not found`,\n );\n }\n return referral;\n }\n\n /**\n * The exact policy version pinned on the referral, when it exists —\n * resolved in the REFERRAL's tenant lane (global fallback): background\n * qualification runs without ambient tenant context, and another\n * tenant's same-keyed policy must never supply the eligibility flags.\n */\n private async findPolicyVersion(\n policyKey: string,\n version: number,\n tenantId: string | null = null,\n ) {\n if (!policyKey || version <= 0) return null;\n const results = await this.deps.policies.list({\n where: { policyKey, version },\n });\n return (\n results.find((policy) => policy.tenantId === tenantId) ??\n results.find((policy) => policy.tenantId === null) ??\n null\n );\n }\n\n /**\n * The CommissionPlan an agreement binds: the pinned version when\n * `commissionPlanVersion > 0`, else the latest active version. Either\n * way the resolved row must be ACTIVE — superseded/retired terms cannot\n * govern a NEW qualification (an amendment should be activated, or the\n * agreement re-pinned, first).\n */\n private async resolvePlan(\n agreement: ReferralAgreement,\n at: Date = new Date(),\n ): Promise<CommissionPlan | null> {\n if (!agreement.commissionPlanKey) return null;\n if (agreement.commissionPlanVersion > 0) {\n // Pinned lookup, scoped to the AGREEMENT's tenant with global\n // fallback — system/background qualification runs without ambient\n // tenant context, and another tenant's same-named plan must never be\n // frozen into this tenant's snapshot.\n const results = await this.deps.plans.list({\n where: {\n planKey: agreement.commissionPlanKey,\n version: agreement.commissionPlanVersion,\n status: 'active',\n },\n });\n return (\n results.find((plan) => plan.tenantId === agreement.tenantId) ??\n results.find((plan) => plan.tenantId === null) ??\n null\n );\n }\n // Unpinned agreements resolve the version in effect at the\n // qualification clock, in the agreement's tenant lane — a future-dated\n // active amendment must not govern earlier qualifications.\n return await this.deps.plans.latestActiveByKey(\n agreement.commissionPlanKey,\n at,\n agreement.tenantId,\n );\n }\n\n /**\n * Mint the immutable snapshot: agreement/plan/policy refs plus the FROZEN\n * component copy and the calculation inputs. `planVersion` records the\n * RESOLVED version — this is where an unpinned (`0`) agreement version\n * gets pinned.\n */\n private async mintSnapshot(\n referral: Referral,\n agreement: ReferralAgreement,\n plan: CommissionPlan,\n ): Promise<ReferralTermSnapshot> {\n return await this.deps.snapshots.create({\n tenantId: referral.tenantId,\n referralId: referral.id ?? '',\n agreementId: agreement.id ?? '',\n agreementVersion: agreement.version,\n planKey: plan.planKey,\n planVersion: plan.version,\n policyKey: referral.policyKey,\n policyVersion: referral.policyVersion,\n components: plan.components,\n currency: plan.currency,\n clearingDays: agreement.clearingDays,\n approvalMode: agreement.approvalMode,\n });\n }\n}\n\nexport default ReferralQualificationService;\n"],"mappings":"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"}