business-as-code 2.1.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.turbo/turbo-build.log +4 -5
  2. package/CHANGELOG.md +53 -0
  3. package/README.md +2 -0
  4. package/dist/dollar.d.ts.map +1 -1
  5. package/dist/dollar.js +2 -2
  6. package/dist/dollar.js.map +1 -1
  7. package/dist/entities/organization.d.ts +4 -0
  8. package/dist/entities/organization.d.ts.map +1 -1
  9. package/dist/entities/organization.js +27 -18
  10. package/dist/entities/organization.js.map +1 -1
  11. package/dist/entities/planning.d.ts +87 -0
  12. package/dist/finance/account.d.ts +44 -0
  13. package/dist/finance/account.d.ts.map +1 -0
  14. package/dist/finance/account.js +6 -0
  15. package/dist/finance/account.js.map +1 -0
  16. package/dist/finance/authority.d.ts +78 -0
  17. package/dist/finance/authority.d.ts.map +1 -0
  18. package/dist/finance/authority.js +27 -0
  19. package/dist/finance/authority.js.map +1 -0
  20. package/dist/finance/card.d.ts +36 -0
  21. package/dist/finance/card.d.ts.map +1 -0
  22. package/dist/finance/card.js +6 -0
  23. package/dist/finance/card.js.map +1 -0
  24. package/dist/finance/identity.d.ts +30 -0
  25. package/dist/finance/identity.d.ts.map +1 -0
  26. package/dist/finance/identity.js +8 -0
  27. package/dist/finance/identity.js.map +1 -0
  28. package/dist/finance/index.d.ts +36 -0
  29. package/dist/finance/index.d.ts.map +1 -0
  30. package/dist/finance/index.js +22 -0
  31. package/dist/finance/index.js.map +1 -0
  32. package/dist/finance/ledger.d.ts +24 -0
  33. package/dist/finance/ledger.d.ts.map +1 -0
  34. package/dist/finance/ledger.js +8 -0
  35. package/dist/finance/ledger.js.map +1 -0
  36. package/dist/finance/merchant.d.ts +129 -0
  37. package/dist/finance/merchant.d.ts.map +1 -0
  38. package/dist/finance/merchant.js +21 -0
  39. package/dist/finance/merchant.js.map +1 -0
  40. package/dist/finance/outcome-contract.d.ts +139 -0
  41. package/dist/finance/outcome-contract.d.ts.map +1 -0
  42. package/dist/finance/outcome-contract.js +27 -0
  43. package/dist/finance/outcome-contract.js.map +1 -0
  44. package/dist/finance/port.d.ts +121 -0
  45. package/dist/finance/port.d.ts.map +1 -0
  46. package/dist/finance/port.js +10 -0
  47. package/dist/finance/port.js.map +1 -0
  48. package/dist/finance/pricing.d.ts +154 -0
  49. package/dist/finance/pricing.d.ts.map +1 -0
  50. package/dist/finance/pricing.js +79 -0
  51. package/dist/finance/pricing.js.map +1 -0
  52. package/dist/finance/proof-predicate.d.ts +92 -0
  53. package/dist/finance/proof-predicate.d.ts.map +1 -0
  54. package/dist/finance/proof-predicate.js +80 -0
  55. package/dist/finance/proof-predicate.js.map +1 -0
  56. package/dist/finance/refund.d.ts +44 -0
  57. package/dist/finance/refund.d.ts.map +1 -0
  58. package/dist/finance/refund.js +41 -0
  59. package/dist/finance/refund.js.map +1 -0
  60. package/dist/finance/sla.d.ts +25 -0
  61. package/dist/finance/sla.d.ts.map +1 -0
  62. package/dist/finance/sla.js +7 -0
  63. package/dist/finance/sla.js.map +1 -0
  64. package/dist/finance/types.d.ts +79 -0
  65. package/dist/finance/types.d.ts.map +1 -0
  66. package/dist/finance/types.js +8 -0
  67. package/dist/{canvas → finance}/types.js.map +1 -1
  68. package/dist/goals.d.ts +19 -0
  69. package/dist/goals.d.ts.map +1 -1
  70. package/dist/goals.js +81 -12
  71. package/dist/goals.js.map +1 -1
  72. package/dist/index.d.ts +12 -8
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +19 -7
  75. package/dist/index.js.map +1 -1
  76. package/dist/kpis.d.ts +19 -0
  77. package/dist/kpis.d.ts.map +1 -1
  78. package/dist/kpis.js +71 -6
  79. package/dist/kpis.js.map +1 -1
  80. package/dist/metrics.d.ts.map +1 -1
  81. package/dist/metrics.js +29 -24
  82. package/dist/metrics.js.map +1 -1
  83. package/dist/okrs.d.ts +34 -0
  84. package/dist/okrs.d.ts.map +1 -1
  85. package/dist/okrs.js +135 -13
  86. package/dist/okrs.js.map +1 -1
  87. package/dist/organization.d.ts.map +1 -1
  88. package/dist/organization.js +11 -11
  89. package/dist/organization.js.map +1 -1
  90. package/dist/process.d.ts.map +1 -1
  91. package/dist/process.js +13 -12
  92. package/dist/process.js.map +1 -1
  93. package/dist/product.d.ts.map +1 -1
  94. package/dist/product.js +9 -9
  95. package/dist/product.js.map +1 -1
  96. package/dist/queries.d.ts.map +1 -1
  97. package/dist/queries.js +194 -32
  98. package/dist/queries.js.map +1 -1
  99. package/dist/roles.d.ts +25 -31
  100. package/dist/roles.d.ts.map +1 -1
  101. package/dist/roles.js +37 -10
  102. package/dist/roles.js.map +1 -1
  103. package/dist/workflow.d.ts.map +1 -1
  104. package/dist/workflow.js +13 -12
  105. package/dist/workflow.js.map +1 -1
  106. package/package.json +20 -13
  107. package/src/dollar.ts +5 -2
  108. package/src/entities/organization.ts +31 -18
  109. package/src/finance/account.ts +48 -0
  110. package/src/finance/authority.ts +42 -0
  111. package/src/finance/card.ts +38 -0
  112. package/src/finance/identity.ts +31 -0
  113. package/src/finance/index.ts +117 -0
  114. package/src/finance/ledger.ts +26 -0
  115. package/src/finance/merchant.ts +127 -0
  116. package/src/finance/outcome-contract.ts +157 -0
  117. package/src/finance/port.ts +144 -0
  118. package/src/finance/pricing.ts +197 -0
  119. package/src/finance/proof-predicate.ts +106 -0
  120. package/src/finance/refund.ts +52 -0
  121. package/src/finance/sla.ts +33 -0
  122. package/src/finance/types.ts +75 -0
  123. package/src/goals.ts +78 -12
  124. package/src/index.ts +48 -18
  125. package/src/kpis.ts +62 -8
  126. package/src/metrics.ts +92 -79
  127. package/src/okrs.ts +120 -20
  128. package/src/organization.ts +12 -15
  129. package/src/process.ts +11 -12
  130. package/src/product.ts +8 -9
  131. package/src/queries.ts +238 -75
  132. package/src/roles.ts +62 -61
  133. package/src/workflow.ts +22 -15
  134. package/test/business.test.ts +282 -0
  135. package/test/dollar.test.ts +270 -0
  136. package/test/entities.test.ts +628 -0
  137. package/test/financials.test.ts +539 -0
  138. package/test/goals.test.ts +451 -0
  139. package/{src → test}/index.test.ts +1 -1
  140. package/test/kpis.test.ts +440 -0
  141. package/test/metrics.test.ts +744 -0
  142. package/test/okrs.test.ts +741 -0
  143. package/test/organization.test.ts +548 -0
  144. package/test/process.test.ts +503 -0
  145. package/test/product.test.ts +430 -0
  146. package/test/queries.test.ts +556 -0
  147. package/test/roles.test.ts +546 -0
  148. package/test/service.test.ts +450 -0
  149. package/test/types.test.ts +1141 -0
  150. package/test/vision.test.ts +214 -0
  151. package/test/workflow.test.ts +501 -0
  152. package/vitest.config.ts +47 -0
  153. package/LICENSE +0 -21
  154. package/dist/canvas/activities.d.ts +0 -19
  155. package/dist/canvas/activities.d.ts.map +0 -1
  156. package/dist/canvas/activities.js +0 -20
  157. package/dist/canvas/activities.js.map +0 -1
  158. package/dist/canvas/channels.d.ts +0 -20
  159. package/dist/canvas/channels.d.ts.map +0 -1
  160. package/dist/canvas/channels.js +0 -21
  161. package/dist/canvas/channels.js.map +0 -1
  162. package/dist/canvas/relationships.d.ts +0 -20
  163. package/dist/canvas/relationships.d.ts.map +0 -1
  164. package/dist/canvas/relationships.js +0 -21
  165. package/dist/canvas/relationships.js.map +0 -1
  166. package/dist/canvas/resources.d.ts +0 -20
  167. package/dist/canvas/resources.d.ts.map +0 -1
  168. package/dist/canvas/resources.js +0 -30
  169. package/dist/canvas/resources.js.map +0 -1
  170. package/dist/canvas/revenue.d.ts +0 -22
  171. package/dist/canvas/revenue.d.ts.map +0 -1
  172. package/dist/canvas/revenue.js +0 -30
  173. package/dist/canvas/revenue.js.map +0 -1
  174. package/dist/canvas/segments.d.ts +0 -20
  175. package/dist/canvas/segments.d.ts.map +0 -1
  176. package/dist/canvas/segments.js +0 -28
  177. package/dist/canvas/segments.js.map +0 -1
  178. package/dist/canvas/types.d.ts +0 -232
  179. package/dist/canvas/types.d.ts.map +0 -1
  180. package/dist/canvas/types.js +0 -8
  181. package/dist/canvas/value.d.ts +0 -20
  182. package/dist/canvas/value.d.ts.map +0 -1
  183. package/dist/canvas/value.js +0 -21
  184. package/dist/canvas/value.js.map +0 -1
  185. package/src/business.js +0 -108
  186. package/src/canvas/activities.ts +0 -32
  187. package/src/canvas/canvas.ts +0 -482
  188. package/src/canvas/channels.ts +0 -34
  189. package/src/canvas/costs.ts +0 -43
  190. package/src/canvas/economics.ts +0 -99
  191. package/src/canvas/index.ts +0 -206
  192. package/src/canvas/partnerships.ts +0 -34
  193. package/src/canvas/projections.ts +0 -141
  194. package/src/canvas/relationships.ts +0 -34
  195. package/src/canvas/resources.ts +0 -43
  196. package/src/canvas/revenue.ts +0 -56
  197. package/src/canvas/segments.ts +0 -42
  198. package/src/canvas/types.ts +0 -363
  199. package/src/canvas/value.ts +0 -34
  200. package/src/dollar.js +0 -106
  201. package/src/entities/assets.js +0 -322
  202. package/src/entities/business.js +0 -369
  203. package/src/entities/communication.js +0 -254
  204. package/src/entities/customers.js +0 -988
  205. package/src/entities/financials.js +0 -931
  206. package/src/entities/goals.js +0 -799
  207. package/src/entities/index.js +0 -197
  208. package/src/entities/legal.js +0 -300
  209. package/src/entities/market.js +0 -300
  210. package/src/entities/marketing.js +0 -1156
  211. package/src/entities/offerings.js +0 -726
  212. package/src/entities/operations.js +0 -786
  213. package/src/entities/organization.js +0 -806
  214. package/src/entities/partnerships.js +0 -299
  215. package/src/entities/planning.js +0 -270
  216. package/src/entities/projects.js +0 -348
  217. package/src/entities/risk.js +0 -292
  218. package/src/entities/sales.js +0 -1247
  219. package/src/financials.js +0 -296
  220. package/src/goals.js +0 -214
  221. package/src/index.js +0 -131
  222. package/src/index.test.js +0 -274
  223. package/src/kpis.js +0 -231
  224. package/src/metrics.js +0 -324
  225. package/src/okrs.js +0 -268
  226. package/src/organization.js +0 -172
  227. package/src/process.js +0 -240
  228. package/src/product.js +0 -144
  229. package/src/queries.js +0 -414
  230. package/src/roles.js +0 -254
  231. package/src/service.js +0 -139
  232. package/src/types.js +0 -4
  233. package/src/vision.js +0 -67
  234. package/src/workflow.js +0 -246
  235. package/tests/canvas.test.ts +0 -842
package/src/index.ts CHANGED
@@ -114,12 +114,7 @@ export {
114
114
  } from './business.js'
115
115
 
116
116
  // Export vision functions
117
- export {
118
- Vision,
119
- checkIndicator,
120
- calculateProgress,
121
- validateVision,
122
- } from './vision.js'
117
+ export { Vision, checkIndicator, calculateProgress, validateVision } from './vision.js'
123
118
 
124
119
  // Export goal functions
125
120
  export {
@@ -136,6 +131,22 @@ export {
136
131
  hasCircularDependencies,
137
132
  sortByDependencies,
138
133
  validateGoals,
134
+ // org.ai conversions
135
+ toOrgGoal,
136
+ fromOrgGoal,
137
+ } from './goals.js'
138
+
139
+ // Re-export org.ai goal types
140
+ export type {
141
+ OrgGoal,
142
+ OrgGoals,
143
+ GoalStatus,
144
+ GoalCategory,
145
+ GoalPriority,
146
+ // Aliased versions for backward compatibility
147
+ GoalStatus as OrgGoalStatus,
148
+ GoalCategory as OrgGoalCategory,
149
+ GoalPriority as OrgGoalPriority,
139
150
  } from './goals.js'
140
151
 
141
152
  // Export product functions
@@ -218,8 +229,14 @@ export {
218
229
  formatValue,
219
230
  comparePerformance,
220
231
  validateKPIs,
232
+ // org.ai conversions
233
+ toOrgKPI,
234
+ fromOrgKPI,
221
235
  } from './kpis.js'
222
236
 
237
+ // Re-export org.ai KPI types
238
+ export type { OrgKPI, KPICategory, KPITrend, KPIFrequency, KPIHistoryEntry } from './kpis.js'
239
+
223
240
  // Export OKR functions
224
241
  export {
225
242
  okrs,
@@ -239,8 +256,16 @@ export {
239
256
  formatKeyResult,
240
257
  compareOKRPerformance,
241
258
  validateOKRs,
259
+ // org.ai conversions
260
+ toOrgOKR,
261
+ fromOrgOKR,
262
+ toOrgKeyResult,
263
+ fromOrgKeyResult,
242
264
  } from './okrs.js'
243
265
 
266
+ // Re-export org.ai OKR types
267
+ export type { OrgOKR, OrgKeyResult, OKRStatus, KeyResultStatus } from './okrs.js'
268
+
244
269
  // Export financial functions
245
270
  export {
246
271
  financials,
@@ -272,13 +297,7 @@ export {
272
297
  } from './financials.js'
273
298
 
274
299
  // Export $ helper and context management
275
- export {
276
- $,
277
- createBusinessOperations,
278
- updateContext,
279
- getContext,
280
- resetContext,
281
- } from './dollar.js'
300
+ export { $, createBusinessOperations, updateContext, getContext, resetContext } from './dollar.js'
282
301
 
283
302
  // Export SaaS metrics
284
303
  export type {
@@ -419,8 +438,15 @@ export type {
419
438
  AssigneeRef,
420
439
  TaskRoutingRule,
421
440
  WorkflowRole,
441
+ // Re-exported from org.ai and digital-workers
442
+ OrgRole,
443
+ OrgRoleType,
444
+ RoleWorkerType,
422
445
  } from './roles.js'
423
446
 
447
+ // Re-export Worker types from digital-workers via roles.ts
448
+ export type { Worker, WorkerRef } from './roles.js'
449
+
424
450
  export {
425
451
  StandardBusinessRoles,
426
452
  createBusinessRole,
@@ -431,6 +457,9 @@ export {
431
457
  findRoleForTask,
432
458
  createTaskAssignment,
433
459
  transitionTaskStatus,
460
+ // Conversion helpers
461
+ workerRefToAssignee,
462
+ assigneeToWorkerRef,
434
463
  } from './roles.js'
435
464
 
436
465
  // Export organization (full hierarchy to FGA/RBAC)
@@ -455,11 +484,7 @@ export type {
455
484
  ResolvedPermissions,
456
485
  } from './organization.js'
457
486
 
458
- export {
459
- resolvePermissions,
460
- getApprovalChainForRequest,
461
- findManager,
462
- } from './organization.js'
487
+ export { resolvePermissions, getApprovalChainForRequest, findManager } from './organization.js'
463
488
 
464
489
  // =============================================================================
465
490
  // Entity Definitions (Noun pattern with Properties, Actions, Events)
@@ -528,3 +553,8 @@ export {
528
553
  Entities,
529
554
  type BusinessEntityCategory,
530
555
  } from './entities/index.js'
556
+
557
+ // =============================================================================
558
+ // Business Model Canvas (TODO: implement canvas module)
559
+ // =============================================================================
560
+ // Canvas functionality is planned but not yet implemented
package/src/kpis.ts CHANGED
@@ -1,8 +1,63 @@
1
1
  /**
2
2
  * Key Performance Indicators (KPIs) management
3
+ *
4
+ * Uses org.ai KPI types for standardized KPI definitions across the ecosystem.
3
5
  */
4
6
 
5
7
  import type { KPIDefinition, TimePeriod } from './types.js'
8
+ import type { KPI as OrgKPI, KPICategory, KPITrend, KPIFrequency, KPIHistoryEntry } from 'org.ai'
9
+
10
+ // Re-export org.ai KPI types for convenience
11
+ export type { OrgKPI, KPICategory, KPITrend, KPIFrequency, KPIHistoryEntry }
12
+
13
+ /**
14
+ * Convert a business-as-code KPIDefinition to an org.ai KPI
15
+ *
16
+ * @param definition - Business KPI definition
17
+ * @param id - Unique identifier for the KPI
18
+ * @returns org.ai KPI object
19
+ */
20
+ export function toOrgKPI(definition: KPIDefinition, id: string): OrgKPI {
21
+ const result: OrgKPI = {
22
+ id,
23
+ name: definition.name,
24
+ value: definition.current ?? 0,
25
+ target: definition.target ?? 0,
26
+ unit: definition.unit || '',
27
+ }
28
+ if (definition.description !== undefined) result.description = definition.description
29
+ if (definition.current !== undefined) result.current = definition.current
30
+ if (definition.category !== undefined) result.category = definition.category as KPICategory
31
+ if (definition.frequency !== undefined) result.frequency = definition.frequency as KPIFrequency
32
+ if (definition.dataSource !== undefined) result.dataSource = definition.dataSource
33
+ if (definition.formula !== undefined) result.formula = definition.formula
34
+ if (definition.metadata !== undefined) result.metadata = definition.metadata
35
+ return result
36
+ }
37
+
38
+ /**
39
+ * Convert an org.ai KPI to a business-as-code KPIDefinition
40
+ *
41
+ * @param kpi - org.ai KPI object
42
+ * @returns Business KPI definition
43
+ */
44
+ export function fromOrgKPI(kpi: OrgKPI): KPIDefinition {
45
+ const result: KPIDefinition = {
46
+ name: kpi.name,
47
+ unit: kpi.unit,
48
+ }
49
+ if (kpi.description !== undefined) result.description = kpi.description
50
+ const cat = kpi.category
51
+ if (cat !== undefined) result.category = cat as NonNullable<KPIDefinition['category']>
52
+ if (typeof kpi.target === 'number') result.target = kpi.target
53
+ if (typeof kpi.value === 'number') result.current = kpi.value
54
+ else if (typeof kpi.current === 'number') result.current = kpi.current
55
+ if (kpi.frequency !== undefined) result.frequency = kpi.frequency as TimePeriod
56
+ if (kpi.dataSource !== undefined) result.dataSource = kpi.dataSource
57
+ if (kpi.formula !== undefined) result.formula = kpi.formula
58
+ if (kpi.metadata !== undefined) result.metadata = kpi.metadata
59
+ return result
60
+ }
6
61
 
7
62
  /**
8
63
  * Define Key Performance Indicators for tracking business metrics
@@ -46,7 +101,7 @@ import type { KPIDefinition, TimePeriod } from './types.js'
46
101
  * ```
47
102
  */
48
103
  export function kpis(definitions: KPIDefinition[]): KPIDefinition[] {
49
- return definitions.map(kpi => validateAndNormalizeKPI(kpi))
104
+ return definitions.map((kpi) => validateAndNormalizeKPI(kpi))
50
105
  }
51
106
 
52
107
  /**
@@ -88,7 +143,7 @@ export function meetsTarget(kpi: KPIDefinition): boolean {
88
143
  if (kpi.target === undefined || kpi.current === undefined) return false
89
144
 
90
145
  // For metrics where lower is better (like churn rate)
91
- const lowerIsBetter = ['churn', 'cost', 'time', 'error', 'downtime'].some(term =>
146
+ const lowerIsBetter = ['churn', 'cost', 'time', 'error', 'downtime'].some((term) =>
92
147
  kpi.name.toLowerCase().includes(term)
93
148
  )
94
149
 
@@ -126,14 +181,14 @@ export function getKPIsByCategory(
126
181
  kpis: KPIDefinition[],
127
182
  category: KPIDefinition['category']
128
183
  ): KPIDefinition[] {
129
- return kpis.filter(k => k.category === category)
184
+ return kpis.filter((k) => k.category === category)
130
185
  }
131
186
 
132
187
  /**
133
188
  * Get KPIs by frequency
134
189
  */
135
190
  export function getKPIsByFrequency(kpis: KPIDefinition[], frequency: TimePeriod): KPIDefinition[] {
136
- return kpis.filter(k => k.frequency === frequency)
191
+ return kpis.filter((k) => k.frequency === frequency)
137
192
  }
138
193
 
139
194
  /**
@@ -147,7 +202,7 @@ export function getKPIsOnTarget(kpis: KPIDefinition[]): KPIDefinition[] {
147
202
  * Get KPIs that don't meet their targets
148
203
  */
149
204
  export function getKPIsOffTarget(kpis: KPIDefinition[]): KPIDefinition[] {
150
- return kpis.filter(kpi => !meetsTarget(kpi))
205
+ return kpis.filter((kpi) => !meetsTarget(kpi))
151
206
  }
152
207
 
153
208
  /**
@@ -235,11 +290,10 @@ export function comparePerformance(
235
290
  }
236
291
 
237
292
  const change = current.current - previous.current
238
- const changePercent =
239
- previous.current !== 0 ? (change / previous.current) * 100 : 0
293
+ const changePercent = previous.current !== 0 ? (change / previous.current) * 100 : 0
240
294
 
241
295
  // Determine if change is an improvement
242
- const lowerIsBetter = ['churn', 'cost', 'time', 'error', 'downtime'].some(term =>
296
+ const lowerIsBetter = ['churn', 'cost', 'time', 'error', 'downtime'].some((term) =>
243
297
  current.name.toLowerCase().includes(term)
244
298
  )
245
299
 
package/src/metrics.ts CHANGED
@@ -58,12 +58,12 @@ export interface TimeSeries<T = number> {
58
58
  */
59
59
  export interface MRR {
60
60
  total: number
61
- newMRR: number // From new customers
62
- expansionMRR: number // From upgrades
63
- contractionMRR: number // From downgrades
64
- churnedMRR: number // From cancellations
65
- reactivationMRR: number // From reactivations
66
- netNewMRR: number // newMRR + expansionMRR - contractionMRR - churnedMRR + reactivationMRR
61
+ newMRR: number // From new customers
62
+ expansionMRR: number // From upgrades
63
+ contractionMRR: number // From downgrades
64
+ churnedMRR: number // From cancellations
65
+ reactivationMRR: number // From reactivations
66
+ netNewMRR: number // newMRR + expansionMRR - contractionMRR - churnedMRR + reactivationMRR
67
67
  currency: Currency
68
68
  period: MetricPeriod
69
69
  }
@@ -73,8 +73,8 @@ export interface MRR {
73
73
  */
74
74
  export interface ARR {
75
75
  total: number
76
- fromMRR?: number // MRR * 12
77
- contracted?: number // From annual contracts
76
+ fromMRR?: number // MRR * 12
77
+ contracted?: number // From annual contracts
78
78
  currency: Currency
79
79
  asOf: Date
80
80
  }
@@ -83,7 +83,7 @@ export interface ARR {
83
83
  * Net Revenue Retention (NRR) / Dollar-based Net Retention (DBNR)
84
84
  */
85
85
  export interface NRR {
86
- rate: number // Percentage (e.g., 115 = 115%)
86
+ rate: number // Percentage (e.g., 115 = 115%)
87
87
  startingMRR: number
88
88
  endingMRR: number
89
89
  expansion: number
@@ -96,7 +96,7 @@ export interface NRR {
96
96
  * Gross Revenue Retention (GRR)
97
97
  */
98
98
  export interface GRR {
99
- rate: number // Percentage (max 100%)
99
+ rate: number // Percentage (max 100%)
100
100
  startingMRR: number
101
101
  endingMRR: number
102
102
  contraction: number
@@ -113,7 +113,7 @@ export interface ARPU {
113
113
  totalUsers: number
114
114
  currency: Currency
115
115
  period: MetricPeriod
116
- segment?: string // Optional segment (e.g., "enterprise", "smb")
116
+ segment?: string // Optional segment (e.g., "enterprise", "smb")
117
117
  }
118
118
 
119
119
  /**
@@ -125,7 +125,7 @@ export interface RevenueSegment {
125
125
  arr: number
126
126
  customers: number
127
127
  arpu: number
128
- growth: number // MoM or YoY growth rate
128
+ growth: number // MoM or YoY growth rate
129
129
  currency: Currency
130
130
  }
131
131
 
@@ -151,8 +151,8 @@ export interface CAC {
151
151
  export interface LTV {
152
152
  value: number
153
153
  arpu: number
154
- grossMargin: number // Percentage
155
- churnRate: number // Monthly churn rate
154
+ grossMargin: number // Percentage
155
+ churnRate: number // Monthly churn rate
156
156
  averageLifetimeMonths: number
157
157
  currency: Currency
158
158
  }
@@ -164,8 +164,8 @@ export interface LTVtoCAC {
164
164
  ratio: number
165
165
  ltv: number
166
166
  cac: number
167
- paybackMonths: number // CAC / (ARPU * Gross Margin)
168
- healthy: boolean // > 3 is generally healthy
167
+ paybackMonths: number // CAC / (ARPU * Gross Margin)
168
+ healthy: boolean // > 3 is generally healthy
169
169
  }
170
170
 
171
171
  /**
@@ -173,12 +173,12 @@ export interface LTVtoCAC {
173
173
  */
174
174
  export interface Churn {
175
175
  // Customer churn (logo churn)
176
- customerChurnRate: number // Percentage
176
+ customerChurnRate: number // Percentage
177
177
  customersLost: number
178
178
  customersStart: number
179
179
 
180
180
  // Revenue churn
181
- revenueChurnRate: number // Percentage (gross churn)
181
+ revenueChurnRate: number // Percentage (gross churn)
182
182
  mrrChurned: number
183
183
 
184
184
  // Net revenue churn (can be negative with good expansion)
@@ -192,11 +192,11 @@ export interface Churn {
192
192
  */
193
193
  export interface RetentionCohort {
194
194
  cohortDate: Date
195
- cohortLabel: string // e.g., "Jan 2024"
195
+ cohortLabel: string // e.g., "Jan 2024"
196
196
  initialCustomers: number
197
197
  initialMRR: number
198
- retentionByMonth: number[] // Array of retention rates by month
199
- revenueByMonth: number[] // Array of MRR by month
198
+ retentionByMonth: number[] // Array of retention rates by month
199
+ revenueByMonth: number[] // Array of MRR by month
200
200
  }
201
201
 
202
202
  // =============================================================================
@@ -207,11 +207,11 @@ export interface RetentionCohort {
207
207
  * Growth rate metrics
208
208
  */
209
209
  export interface GrowthRate {
210
- mom: number // Month-over-month
211
- qoq: number // Quarter-over-quarter
212
- yoy: number // Year-over-year
213
- cagr?: number // Compound annual growth rate
214
- metric: string // What metric this growth rate is for
210
+ mom: number // Month-over-month
211
+ qoq: number // Quarter-over-quarter
212
+ yoy: number // Year-over-year
213
+ cagr?: number // Compound annual growth rate
214
+ metric: string // What metric this growth rate is for
215
215
  period: MetricPeriod
216
216
  }
217
217
 
@@ -225,7 +225,7 @@ export interface QuickRatio {
225
225
  expansionMRR: number
226
226
  churnedMRR: number
227
227
  contractionMRR: number
228
- healthy: boolean // > 4 is good, > 1 means growing
228
+ healthy: boolean // > 4 is good, > 1 means growing
229
229
  period: MetricPeriod
230
230
  }
231
231
 
@@ -241,7 +241,7 @@ export interface MagicNumber {
241
241
  value: number
242
242
  netNewARR: number
243
243
  salesMarketingSpend: number
244
- efficient: boolean // > 0.75 is efficient
244
+ efficient: boolean // > 0.75 is efficient
245
245
  period: MetricPeriod
246
246
  }
247
247
 
@@ -253,7 +253,7 @@ export interface BurnMultiple {
253
253
  value: number
254
254
  netBurn: number
255
255
  netNewARR: number
256
- efficient: boolean // < 1.5 is good
256
+ efficient: boolean // < 1.5 is good
257
257
  period: MetricPeriod
258
258
  }
259
259
 
@@ -264,8 +264,8 @@ export interface BurnMultiple {
264
264
  export interface RuleOf40 {
265
265
  score: number
266
266
  revenueGrowthRate: number
267
- profitMargin: number // Or EBITDA margin
268
- passing: boolean // >= 40 is passing
267
+ profitMargin: number // Or EBITDA margin
268
+ passing: boolean // >= 40 is passing
269
269
  period: MetricPeriod
270
270
  }
271
271
 
@@ -274,7 +274,7 @@ export interface RuleOf40 {
274
274
  * Combines multiple efficiency metrics
275
275
  */
276
276
  export interface EfficiencyScore {
277
- overall: number // 0-100 score
277
+ overall: number // 0-100 score
278
278
  components: {
279
279
  ltvCacRatio: number
280
280
  magicNumber: number
@@ -294,10 +294,10 @@ export interface EfficiencyScore {
294
294
  */
295
295
  export interface Pipeline {
296
296
  totalValue: number
297
- weightedValue: number // Probability-adjusted
297
+ weightedValue: number // Probability-adjusted
298
298
  stages: PipelineStage[]
299
- velocity: number // Average days to close
300
- conversionRate: number // Win rate
299
+ velocity: number // Average days to close
300
+ conversionRate: number // Win rate
301
301
  currency: Currency
302
302
  asOf: Date
303
303
  }
@@ -318,7 +318,7 @@ export interface PipelineStage {
318
318
  * (Opportunities * Win Rate * Average Deal Size) / Sales Cycle Length
319
319
  */
320
320
  export interface SalesVelocity {
321
- value: number // Revenue per day
321
+ value: number // Revenue per day
322
322
  opportunities: number
323
323
  winRate: number
324
324
  averageDealSize: number
@@ -335,10 +335,10 @@ export interface SalesVelocity {
335
335
  * Net Promoter Score
336
336
  */
337
337
  export interface NPS {
338
- score: number // -100 to 100
339
- promoters: number // 9-10
340
- passives: number // 7-8
341
- detractors: number // 0-6
338
+ score: number // -100 to 100
339
+ promoters: number // 9-10
340
+ passives: number // 7-8
341
+ detractors: number // 0-6
342
342
  responses: number
343
343
  responseRate?: number
344
344
  asOf: Date
@@ -348,10 +348,10 @@ export interface NPS {
348
348
  * Customer health score
349
349
  */
350
350
  export interface CustomerHealth {
351
- averageScore: number // 0-100
352
- healthy: number // Count
353
- atRisk: number // Count
354
- critical: number // Count
351
+ averageScore: number // 0-100
352
+ healthy: number // Count
353
+ atRisk: number // Count
354
+ critical: number // Count
355
355
  factors: HealthFactor[]
356
356
  asOf: Date
357
357
  }
@@ -422,7 +422,8 @@ export function calculateMRR(input: {
422
422
  period: MetricPeriod
423
423
  }): MRR {
424
424
  const reactivationMRR = input.reactivationMRR || 0
425
- const netNewMRR = input.newMRR + input.expansionMRR - input.contractionMRR - input.churnedMRR + reactivationMRR
425
+ const netNewMRR =
426
+ input.newMRR + input.expansionMRR - input.contractionMRR - input.churnedMRR + reactivationMRR
426
427
  const total = input.previousMRR + netNewMRR
427
428
 
428
429
  return {
@@ -508,22 +509,23 @@ export function calculateCACMetric(input: {
508
509
  }): CAC {
509
510
  const value = input.newCustomers > 0 ? input.salesMarketingSpend / input.newCustomers : 0
510
511
 
511
- let byChannel: Record<string, number> | undefined
512
- if (input.byChannel) {
513
- byChannel = {}
514
- for (const [channel, data] of Object.entries(input.byChannel)) {
515
- byChannel[channel] = data.customers > 0 ? data.spend / data.customers : 0
516
- }
517
- }
518
-
519
- return {
512
+ const result: CAC = {
520
513
  value,
521
514
  totalSalesMarketingSpend: input.salesMarketingSpend,
522
515
  newCustomersAcquired: input.newCustomers,
523
516
  currency: input.currency || 'USD',
524
517
  period: input.period,
525
- byChannel,
526
518
  }
519
+
520
+ if (input.byChannel) {
521
+ const byChannel: Record<string, number> = {}
522
+ for (const [channel, data] of Object.entries(input.byChannel)) {
523
+ byChannel[channel] = data.customers > 0 ? data.spend / data.customers : 0
524
+ }
525
+ result.byChannel = byChannel
526
+ }
527
+
528
+ return result
527
529
  }
528
530
 
529
531
  /**
@@ -537,7 +539,7 @@ export function calculateLTVMetric(input: {
537
539
  }): LTV {
538
540
  // LTV = (ARPU * Gross Margin) / Churn Rate
539
541
  const averageLifetimeMonths = input.churnRate > 0 ? 1 / input.churnRate : 0
540
- const value = input.churnRate > 0 ? (input.arpu * input.grossMargin / 100) / input.churnRate : 0
542
+ const value = input.churnRate > 0 ? (input.arpu * input.grossMargin) / 100 / input.churnRate : 0
541
543
 
542
544
  return {
543
545
  value,
@@ -554,9 +556,8 @@ export function calculateLTVMetric(input: {
554
556
  */
555
557
  export function calculateLTVtoCACRatio(ltv: LTV, cac: CAC): LTVtoCAC {
556
558
  const ratio = cac.value > 0 ? ltv.value / cac.value : 0
557
- const paybackMonths = ltv.arpu > 0 && ltv.grossMargin > 0
558
- ? cac.value / (ltv.arpu * ltv.grossMargin / 100)
559
- : 0
559
+ const paybackMonths =
560
+ ltv.arpu > 0 && ltv.grossMargin > 0 ? cac.value / ((ltv.arpu * ltv.grossMargin) / 100) : 0
560
561
 
561
562
  return {
562
563
  ratio,
@@ -654,15 +655,18 @@ export function calculateGrowthRates(input: {
654
655
  metric: string
655
656
  period: MetricPeriod
656
657
  }): GrowthRate {
657
- const mom = input.previousMonth && input.previousMonth > 0
658
- ? ((input.current - input.previousMonth) / input.previousMonth) * 100
659
- : 0
660
- const qoq = input.previousQuarter && input.previousQuarter > 0
661
- ? ((input.current - input.previousQuarter) / input.previousQuarter) * 100
662
- : 0
663
- const yoy = input.previousYear && input.previousYear > 0
664
- ? ((input.current - input.previousYear) / input.previousYear) * 100
665
- : 0
658
+ const mom =
659
+ input.previousMonth && input.previousMonth > 0
660
+ ? ((input.current - input.previousMonth) / input.previousMonth) * 100
661
+ : 0
662
+ const qoq =
663
+ input.previousQuarter && input.previousQuarter > 0
664
+ ? ((input.current - input.previousQuarter) / input.previousQuarter) * 100
665
+ : 0
666
+ const yoy =
667
+ input.previousYear && input.previousYear > 0
668
+ ? ((input.current - input.previousYear) / input.previousYear) * 100
669
+ : 0
666
670
 
667
671
  return {
668
672
  mom,
@@ -684,15 +688,11 @@ export function calculateChurnMetrics(input: {
684
688
  expansionMRR: number
685
689
  period: MetricPeriod
686
690
  }): Churn {
687
- const customerChurnRate = input.customersStart > 0
688
- ? (input.customersLost / input.customersStart) * 100
689
- : 0
690
- const revenueChurnRate = input.mrrStart > 0
691
- ? (input.mrrChurned / input.mrrStart) * 100
692
- : 0
693
- const netRevenueChurnRate = input.mrrStart > 0
694
- ? ((input.mrrChurned - input.expansionMRR) / input.mrrStart) * 100
695
- : 0
691
+ const customerChurnRate =
692
+ input.customersStart > 0 ? (input.customersLost / input.customersStart) * 100 : 0
693
+ const revenueChurnRate = input.mrrStart > 0 ? (input.mrrChurned / input.mrrStart) * 100 : 0
694
+ const netRevenueChurnRate =
695
+ input.mrrStart > 0 ? ((input.mrrChurned - input.expansionMRR) / input.mrrStart) * 100 : 0
696
696
 
697
697
  return {
698
698
  customerChurnRate,
@@ -728,7 +728,7 @@ export function aggregateTimeSeries<T extends number>(
728
728
  const aggregation = series.aggregation || 'sum'
729
729
 
730
730
  for (const [key, points] of buckets) {
731
- const values = points.map(p => p.value as number)
731
+ const values = points.map((p) => p.value as number)
732
732
  let aggregatedValue: number
733
733
 
734
734
  switch (aggregation) {
@@ -809,7 +809,20 @@ export function createMetricPeriod(
809
809
  * Format period label
810
810
  */
811
811
  function formatPeriodLabel(period: TimePeriod, start: Date, end: Date): string {
812
- const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
812
+ const monthNames = [
813
+ 'Jan',
814
+ 'Feb',
815
+ 'Mar',
816
+ 'Apr',
817
+ 'May',
818
+ 'Jun',
819
+ 'Jul',
820
+ 'Aug',
821
+ 'Sep',
822
+ 'Oct',
823
+ 'Nov',
824
+ 'Dec',
825
+ ]
813
826
 
814
827
  switch (period) {
815
828
  case 'monthly':