business-as-code 0.2.1 → 2.0.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.
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +17 -0
- package/IMPLEMENTATION.md +226 -0
- package/README.md +1133 -193
- package/dist/business.d.ts +62 -0
- package/dist/business.d.ts.map +1 -0
- package/dist/business.js +109 -0
- package/dist/business.js.map +1 -0
- package/dist/dollar.d.ts +60 -0
- package/dist/dollar.d.ts.map +1 -0
- package/dist/dollar.js +107 -0
- package/dist/dollar.js.map +1 -0
- package/dist/entities/assets.d.ts +21 -0
- package/dist/entities/assets.d.ts.map +1 -0
- package/dist/entities/assets.js +323 -0
- package/dist/entities/assets.js.map +1 -0
- package/dist/entities/business.d.ts +36 -0
- package/dist/entities/business.d.ts.map +1 -0
- package/dist/entities/business.js +370 -0
- package/dist/entities/business.js.map +1 -0
- package/dist/entities/communication.d.ts +21 -0
- package/dist/entities/communication.d.ts.map +1 -0
- package/dist/entities/communication.js +255 -0
- package/dist/entities/communication.js.map +1 -0
- package/dist/entities/customers.d.ts +58 -0
- package/dist/entities/customers.d.ts.map +1 -0
- package/dist/entities/customers.js +989 -0
- package/dist/entities/customers.js.map +1 -0
- package/dist/entities/financials.d.ts +59 -0
- package/dist/entities/financials.d.ts.map +1 -0
- package/dist/entities/financials.js +932 -0
- package/dist/entities/financials.js.map +1 -0
- package/dist/entities/goals.d.ts +58 -0
- package/dist/entities/goals.d.ts.map +1 -0
- package/dist/entities/goals.js +800 -0
- package/dist/entities/goals.js.map +1 -0
- package/dist/entities/index.d.ts +299 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +198 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/legal.d.ts +21 -0
- package/dist/entities/legal.d.ts.map +1 -0
- package/dist/entities/legal.js +301 -0
- package/dist/entities/legal.js.map +1 -0
- package/dist/entities/market.d.ts +21 -0
- package/dist/entities/market.d.ts.map +1 -0
- package/dist/entities/market.js +301 -0
- package/dist/entities/market.js.map +1 -0
- package/dist/entities/marketing.d.ts +67 -0
- package/dist/entities/marketing.d.ts.map +1 -0
- package/dist/entities/marketing.js +1157 -0
- package/dist/entities/marketing.js.map +1 -0
- package/dist/entities/offerings.d.ts +51 -0
- package/dist/entities/offerings.d.ts.map +1 -0
- package/dist/entities/offerings.js +727 -0
- package/dist/entities/offerings.js.map +1 -0
- package/dist/entities/operations.d.ts +58 -0
- package/dist/entities/operations.d.ts.map +1 -0
- package/dist/entities/operations.js +787 -0
- package/dist/entities/operations.js.map +1 -0
- package/dist/entities/organization.d.ts +57 -0
- package/dist/entities/organization.d.ts.map +1 -0
- package/dist/entities/organization.js +807 -0
- package/dist/entities/organization.js.map +1 -0
- package/dist/entities/partnerships.d.ts +21 -0
- package/dist/entities/partnerships.d.ts.map +1 -0
- package/dist/entities/partnerships.js +300 -0
- package/dist/entities/partnerships.js.map +1 -0
- package/dist/entities/planning.d.ts +87 -0
- package/dist/entities/planning.d.ts.map +1 -0
- package/dist/entities/planning.js +271 -0
- package/dist/entities/planning.js.map +1 -0
- package/dist/entities/projects.d.ts +25 -0
- package/dist/entities/projects.d.ts.map +1 -0
- package/dist/entities/projects.js +349 -0
- package/dist/entities/projects.js.map +1 -0
- package/dist/entities/risk.d.ts +21 -0
- package/dist/entities/risk.d.ts.map +1 -0
- package/dist/entities/risk.js +293 -0
- package/dist/entities/risk.js.map +1 -0
- package/dist/entities/sales.d.ts +72 -0
- package/dist/entities/sales.d.ts.map +1 -0
- package/dist/entities/sales.js +1248 -0
- package/dist/entities/sales.js.map +1 -0
- package/dist/financials.d.ts +130 -0
- package/dist/financials.d.ts.map +1 -0
- package/dist/financials.js +297 -0
- package/dist/financials.js.map +1 -0
- package/dist/goals.d.ts +87 -0
- package/dist/goals.d.ts.map +1 -0
- package/dist/goals.js +215 -0
- package/dist/goals.js.map +1 -0
- package/dist/index.d.ts +97 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +131 -1079
- package/dist/index.js.map +1 -1
- package/dist/kpis.d.ts +118 -0
- package/dist/kpis.d.ts.map +1 -0
- package/dist/kpis.js +232 -0
- package/dist/kpis.js.map +1 -0
- package/dist/metrics.d.ts +448 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +325 -0
- package/dist/metrics.js.map +1 -0
- package/dist/okrs.d.ts +123 -0
- package/dist/okrs.d.ts.map +1 -0
- package/dist/okrs.js +269 -0
- package/dist/okrs.js.map +1 -0
- package/dist/organization.d.ts +585 -0
- package/dist/organization.d.ts.map +1 -0
- package/dist/organization.js +173 -0
- package/dist/organization.js.map +1 -0
- package/dist/process.d.ts +112 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +241 -0
- package/dist/process.js.map +1 -0
- package/dist/product.d.ts +85 -0
- package/dist/product.d.ts.map +1 -0
- package/dist/product.js +145 -0
- package/dist/product.js.map +1 -0
- package/dist/queries.d.ts +304 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +415 -0
- package/dist/queries.js.map +1 -0
- package/dist/roles.d.ts +340 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +255 -0
- package/dist/roles.js.map +1 -0
- package/dist/service.d.ts +61 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +140 -0
- package/dist/service.js.map +1 -0
- package/dist/types.d.ts +459 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/vision.d.ts +38 -0
- package/dist/vision.d.ts.map +1 -0
- package/dist/vision.js +68 -0
- package/dist/vision.js.map +1 -0
- package/dist/workflow.d.ts +115 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +247 -0
- package/dist/workflow.js.map +1 -0
- package/examples/basic-usage.ts +307 -0
- package/package.json +19 -60
- package/src/business.ts +121 -0
- package/src/dollar.ts +132 -0
- package/src/entities/assets.ts +332 -0
- package/src/entities/business.ts +406 -0
- package/src/entities/communication.ts +264 -0
- package/src/entities/customers.ts +1072 -0
- package/src/entities/financials.ts +1011 -0
- package/src/entities/goals.ts +871 -0
- package/src/entities/index.ts +383 -0
- package/src/entities/legal.ts +310 -0
- package/src/entities/market.ts +310 -0
- package/src/entities/marketing.ts +1249 -0
- package/src/entities/offerings.ts +789 -0
- package/src/entities/operations.ts +861 -0
- package/src/entities/organization.ts +876 -0
- package/src/entities/partnerships.ts +309 -0
- package/src/entities/planning.ts +307 -0
- package/src/entities/projects.ts +360 -0
- package/src/entities/risk.ts +302 -0
- package/src/entities/sales.ts +1352 -0
- package/src/financials.ts +352 -0
- package/src/goals.ts +250 -0
- package/src/index.test.ts +336 -0
- package/src/index.ts +530 -0
- package/src/kpis.ts +275 -0
- package/src/metrics.ts +825 -0
- package/src/okrs.ts +325 -0
- package/src/organization.ts +909 -0
- package/src/process.ts +272 -0
- package/src/product.ts +178 -0
- package/src/queries.ts +767 -0
- package/src/roles.ts +686 -0
- package/src/service.ts +164 -0
- package/src/types.ts +493 -0
- package/src/vision.ts +88 -0
- package/src/workflow.ts +280 -0
- package/tsconfig.json +9 -0
- package/dist/loaders/index.d.ts +0 -174
- package/dist/loaders/index.js +0 -366
- package/dist/loaders/index.js.map +0 -1
- package/dist/schema/index.d.ts +0 -146
- package/dist/schema/index.js +0 -716
- package/dist/schema/index.js.map +0 -1
- package/dist/types-CJ9eGS_C.d.ts +0 -86
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Financial metrics and calculations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { FinancialMetrics, FinancialStatement, Currency, TimePeriod } from './types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Calculate financial metrics from basic inputs
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const metrics = financials({
|
|
13
|
+
* revenue: 1000000,
|
|
14
|
+
* cogs: 300000,
|
|
15
|
+
* operatingExpenses: 500000,
|
|
16
|
+
* currency: 'USD',
|
|
17
|
+
* period: 'monthly',
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* console.log(metrics.grossMargin) // 70%
|
|
21
|
+
* console.log(metrics.operatingMargin) // 20%
|
|
22
|
+
* console.log(metrics.netMargin) // 20%
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function financials(metrics: Partial<FinancialMetrics>): FinancialMetrics {
|
|
26
|
+
const revenue = metrics.revenue || 0
|
|
27
|
+
const cogs = metrics.cogs || 0
|
|
28
|
+
const operatingExpenses = metrics.operatingExpenses || 0
|
|
29
|
+
|
|
30
|
+
// Calculate derived metrics
|
|
31
|
+
const grossProfit = revenue - cogs
|
|
32
|
+
const grossMargin = revenue > 0 ? (grossProfit / revenue) * 100 : 0
|
|
33
|
+
|
|
34
|
+
const operatingIncome = grossProfit - operatingExpenses
|
|
35
|
+
const operatingMargin = revenue > 0 ? (operatingIncome / revenue) * 100 : 0
|
|
36
|
+
|
|
37
|
+
const netIncome = metrics.netIncome ?? operatingIncome
|
|
38
|
+
const netMargin = revenue > 0 ? (netIncome / revenue) * 100 : 0
|
|
39
|
+
|
|
40
|
+
// EBITDA (simplified - would need D&A for accurate calculation)
|
|
41
|
+
const ebitda = metrics.ebitda ?? operatingIncome
|
|
42
|
+
const ebitdaMargin = revenue > 0 ? (ebitda / revenue) * 100 : 0
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
...metrics,
|
|
46
|
+
revenue,
|
|
47
|
+
cogs,
|
|
48
|
+
grossProfit,
|
|
49
|
+
grossMargin,
|
|
50
|
+
operatingExpenses,
|
|
51
|
+
operatingIncome,
|
|
52
|
+
operatingMargin,
|
|
53
|
+
netIncome,
|
|
54
|
+
netMargin,
|
|
55
|
+
ebitda,
|
|
56
|
+
ebitdaMargin,
|
|
57
|
+
currency: metrics.currency || 'USD',
|
|
58
|
+
period: metrics.period || 'monthly',
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Calculate gross margin
|
|
64
|
+
*/
|
|
65
|
+
export function calculateGrossMargin(revenue: number, cogs: number): number {
|
|
66
|
+
if (revenue === 0) return 0
|
|
67
|
+
return ((revenue - cogs) / revenue) * 100
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Calculate operating margin
|
|
72
|
+
*/
|
|
73
|
+
export function calculateOperatingMargin(
|
|
74
|
+
revenue: number,
|
|
75
|
+
cogs: number,
|
|
76
|
+
operatingExpenses: number
|
|
77
|
+
): number {
|
|
78
|
+
if (revenue === 0) return 0
|
|
79
|
+
const operatingIncome = revenue - cogs - operatingExpenses
|
|
80
|
+
return (operatingIncome / revenue) * 100
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Calculate net margin
|
|
85
|
+
*/
|
|
86
|
+
export function calculateNetMargin(revenue: number, netIncome: number): number {
|
|
87
|
+
if (revenue === 0) return 0
|
|
88
|
+
return (netIncome / revenue) * 100
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Calculate EBITDA margin
|
|
93
|
+
*/
|
|
94
|
+
export function calculateEBITDAMargin(revenue: number, ebitda: number): number {
|
|
95
|
+
if (revenue === 0) return 0
|
|
96
|
+
return (ebitda / revenue) * 100
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Calculate burn rate (monthly cash burn)
|
|
101
|
+
*/
|
|
102
|
+
export function calculateBurnRate(
|
|
103
|
+
cashStart: number,
|
|
104
|
+
cashEnd: number,
|
|
105
|
+
months: number
|
|
106
|
+
): number {
|
|
107
|
+
if (months === 0) return 0
|
|
108
|
+
return (cashStart - cashEnd) / months
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Calculate runway (months until cash runs out)
|
|
113
|
+
*/
|
|
114
|
+
export function calculateRunway(cash: number, monthlyBurnRate: number): number {
|
|
115
|
+
if (monthlyBurnRate === 0) return Infinity
|
|
116
|
+
if (monthlyBurnRate < 0) return Infinity // Company is profitable
|
|
117
|
+
return cash / monthlyBurnRate
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Calculate customer acquisition cost (CAC)
|
|
122
|
+
*/
|
|
123
|
+
export function calculateCAC(marketingSpend: number, newCustomers: number): number {
|
|
124
|
+
if (newCustomers === 0) return 0
|
|
125
|
+
return marketingSpend / newCustomers
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Calculate customer lifetime value (LTV)
|
|
130
|
+
*/
|
|
131
|
+
export function calculateLTV(
|
|
132
|
+
averageRevenuePerCustomer: number,
|
|
133
|
+
averageCustomerLifetimeMonths: number,
|
|
134
|
+
grossMarginPercent: number
|
|
135
|
+
): number {
|
|
136
|
+
return averageRevenuePerCustomer * averageCustomerLifetimeMonths * (grossMarginPercent / 100)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Calculate LTV:CAC ratio
|
|
141
|
+
*/
|
|
142
|
+
export function calculateLTVtoCAC(ltv: number, cac: number): number {
|
|
143
|
+
if (cac === 0) return 0
|
|
144
|
+
return ltv / cac
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Calculate payback period (months to recover CAC)
|
|
149
|
+
*/
|
|
150
|
+
export function calculatePaybackPeriod(cac: number, monthlyRevPerCustomer: number): number {
|
|
151
|
+
if (monthlyRevPerCustomer === 0) return 0
|
|
152
|
+
return cac / monthlyRevPerCustomer
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Calculate annual recurring revenue (ARR)
|
|
157
|
+
*/
|
|
158
|
+
export function calculateARR(mrr: number): number {
|
|
159
|
+
return mrr * 12
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Calculate monthly recurring revenue (MRR)
|
|
164
|
+
*/
|
|
165
|
+
export function calculateMRR(arr: number): number {
|
|
166
|
+
return arr / 12
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Calculate revenue growth rate
|
|
171
|
+
*/
|
|
172
|
+
export function calculateGrowthRate(currentRevenue: number, previousRevenue: number): number {
|
|
173
|
+
if (previousRevenue === 0) return 0
|
|
174
|
+
return ((currentRevenue - previousRevenue) / previousRevenue) * 100
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Calculate compound annual growth rate (CAGR)
|
|
179
|
+
*/
|
|
180
|
+
export function calculateCAGR(
|
|
181
|
+
beginningValue: number,
|
|
182
|
+
endingValue: number,
|
|
183
|
+
years: number
|
|
184
|
+
): number {
|
|
185
|
+
if (beginningValue === 0 || years === 0) return 0
|
|
186
|
+
return (Math.pow(endingValue / beginningValue, 1 / years) - 1) * 100
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Calculate return on investment (ROI)
|
|
191
|
+
*/
|
|
192
|
+
export function calculateROI(gain: number, cost: number): number {
|
|
193
|
+
if (cost === 0) return 0
|
|
194
|
+
return ((gain - cost) / cost) * 100
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Calculate return on equity (ROE)
|
|
199
|
+
*/
|
|
200
|
+
export function calculateROE(netIncome: number, shareholderEquity: number): number {
|
|
201
|
+
if (shareholderEquity === 0) return 0
|
|
202
|
+
return (netIncome / shareholderEquity) * 100
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Calculate return on assets (ROA)
|
|
207
|
+
*/
|
|
208
|
+
export function calculateROA(netIncome: number, totalAssets: number): number {
|
|
209
|
+
if (totalAssets === 0) return 0
|
|
210
|
+
return (netIncome / totalAssets) * 100
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Calculate quick ratio (liquidity)
|
|
215
|
+
*/
|
|
216
|
+
export function calculateQuickRatio(
|
|
217
|
+
currentAssets: number,
|
|
218
|
+
inventory: number,
|
|
219
|
+
currentLiabilities: number
|
|
220
|
+
): number {
|
|
221
|
+
if (currentLiabilities === 0) return 0
|
|
222
|
+
return (currentAssets - inventory) / currentLiabilities
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Calculate current ratio (liquidity)
|
|
227
|
+
*/
|
|
228
|
+
export function calculateCurrentRatio(currentAssets: number, currentLiabilities: number): number {
|
|
229
|
+
if (currentLiabilities === 0) return 0
|
|
230
|
+
return currentAssets / currentLiabilities
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Calculate debt-to-equity ratio
|
|
235
|
+
*/
|
|
236
|
+
export function calculateDebtToEquity(totalDebt: number, totalEquity: number): number {
|
|
237
|
+
if (totalEquity === 0) return 0
|
|
238
|
+
return totalDebt / totalEquity
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Format currency value
|
|
243
|
+
*/
|
|
244
|
+
export function formatCurrency(amount: number, currency: Currency = 'USD'): string {
|
|
245
|
+
const symbol = getCurrencySymbol(currency)
|
|
246
|
+
const formatted = Math.abs(amount).toLocaleString(undefined, {
|
|
247
|
+
minimumFractionDigits: 0,
|
|
248
|
+
maximumFractionDigits: 2,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
const sign = amount < 0 ? '-' : ''
|
|
252
|
+
return `${sign}${symbol}${formatted}`
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get currency symbol
|
|
257
|
+
*/
|
|
258
|
+
function getCurrencySymbol(currency: Currency): string {
|
|
259
|
+
const symbols: Record<string, string> = {
|
|
260
|
+
USD: '$',
|
|
261
|
+
EUR: '€',
|
|
262
|
+
GBP: '£',
|
|
263
|
+
JPY: '¥',
|
|
264
|
+
CNY: '¥',
|
|
265
|
+
CAD: 'C$',
|
|
266
|
+
AUD: 'A$',
|
|
267
|
+
}
|
|
268
|
+
return symbols[currency] || currency + ' '
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Create a financial statement
|
|
273
|
+
*/
|
|
274
|
+
export function createStatement(
|
|
275
|
+
type: FinancialStatement['type'],
|
|
276
|
+
period: string,
|
|
277
|
+
lineItems: Record<string, number>,
|
|
278
|
+
currency: Currency = 'USD'
|
|
279
|
+
): FinancialStatement {
|
|
280
|
+
return {
|
|
281
|
+
type,
|
|
282
|
+
period,
|
|
283
|
+
lineItems,
|
|
284
|
+
currency,
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get line item from financial statement
|
|
290
|
+
*/
|
|
291
|
+
export function getLineItem(statement: FinancialStatement, name: string): number {
|
|
292
|
+
return statement.lineItems[name] || 0
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Compare financial metrics between periods
|
|
297
|
+
*/
|
|
298
|
+
export function compareMetrics(
|
|
299
|
+
current: FinancialMetrics,
|
|
300
|
+
previous: FinancialMetrics
|
|
301
|
+
): Record<string, { change: number; changePercent: number }> {
|
|
302
|
+
const comparison: Record<string, { change: number; changePercent: number }> = {}
|
|
303
|
+
|
|
304
|
+
const keys: (keyof FinancialMetrics)[] = [
|
|
305
|
+
'revenue',
|
|
306
|
+
'grossProfit',
|
|
307
|
+
'operatingIncome',
|
|
308
|
+
'netIncome',
|
|
309
|
+
'ebitda',
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
for (const key of keys) {
|
|
313
|
+
const currentVal = (current[key] as number) || 0
|
|
314
|
+
const previousVal = (previous[key] as number) || 0
|
|
315
|
+
const change = currentVal - previousVal
|
|
316
|
+
const changePercent = previousVal !== 0 ? (change / previousVal) * 100 : 0
|
|
317
|
+
|
|
318
|
+
comparison[key] = { change, changePercent }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return comparison
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Validate financial metrics
|
|
326
|
+
*/
|
|
327
|
+
export function validateFinancials(
|
|
328
|
+
metrics: FinancialMetrics
|
|
329
|
+
): { valid: boolean; errors: string[] } {
|
|
330
|
+
const errors: string[] = []
|
|
331
|
+
|
|
332
|
+
if (metrics.revenue && metrics.revenue < 0) {
|
|
333
|
+
errors.push('Revenue cannot be negative')
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (metrics.cogs && metrics.cogs < 0) {
|
|
337
|
+
errors.push('COGS cannot be negative')
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (metrics.operatingExpenses && metrics.operatingExpenses < 0) {
|
|
341
|
+
errors.push('Operating expenses cannot be negative')
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (metrics.revenue && metrics.cogs && metrics.cogs > metrics.revenue) {
|
|
345
|
+
errors.push('COGS cannot exceed revenue')
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
valid: errors.length === 0,
|
|
350
|
+
errors,
|
|
351
|
+
}
|
|
352
|
+
}
|
package/src/goals.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business goals definition and tracking
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { GoalDefinition } from './types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Define a business goal with metrics and tracking
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const goals = Goals([
|
|
13
|
+
* {
|
|
14
|
+
* name: 'Launch MVP',
|
|
15
|
+
* description: 'Ship minimum viable product to early customers',
|
|
16
|
+
* category: 'strategic',
|
|
17
|
+
* targetDate: new Date('2024-06-30'),
|
|
18
|
+
* owner: 'Product Team',
|
|
19
|
+
* metrics: ['User signups', 'Feature completion rate'],
|
|
20
|
+
* status: 'in-progress',
|
|
21
|
+
* progress: 65,
|
|
22
|
+
* },
|
|
23
|
+
* {
|
|
24
|
+
* name: 'Achieve Product-Market Fit',
|
|
25
|
+
* description: 'Validate product value with target customers',
|
|
26
|
+
* category: 'strategic',
|
|
27
|
+
* targetDate: new Date('2024-12-31'),
|
|
28
|
+
* owner: 'CEO',
|
|
29
|
+
* metrics: ['NPS > 50', 'Churn < 5%', '100+ paying customers'],
|
|
30
|
+
* status: 'in-progress',
|
|
31
|
+
* progress: 30,
|
|
32
|
+
* dependencies: ['Launch MVP'],
|
|
33
|
+
* },
|
|
34
|
+
* ])
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function Goals(definitions: GoalDefinition[]): GoalDefinition[] {
|
|
38
|
+
return definitions.map(goal => validateAndNormalizeGoal(goal))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Define a single goal
|
|
43
|
+
*/
|
|
44
|
+
export function Goal(definition: GoalDefinition): GoalDefinition {
|
|
45
|
+
return validateAndNormalizeGoal(definition)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate and normalize a goal definition
|
|
50
|
+
*/
|
|
51
|
+
function validateAndNormalizeGoal(goal: GoalDefinition): GoalDefinition {
|
|
52
|
+
if (!goal.name) {
|
|
53
|
+
throw new Error('Goal name is required')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...goal,
|
|
58
|
+
category: goal.category || 'operational',
|
|
59
|
+
status: goal.status || 'not-started',
|
|
60
|
+
progress: goal.progress || 0,
|
|
61
|
+
metrics: goal.metrics || [],
|
|
62
|
+
dependencies: goal.dependencies || [],
|
|
63
|
+
metadata: goal.metadata || {},
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Update goal progress
|
|
69
|
+
*/
|
|
70
|
+
export function updateProgress(goal: GoalDefinition, progress: number): GoalDefinition {
|
|
71
|
+
if (progress < 0 || progress > 100) {
|
|
72
|
+
throw new Error('Progress must be between 0 and 100')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Auto-update status based on progress
|
|
76
|
+
let status = goal.status
|
|
77
|
+
if (progress === 0) {
|
|
78
|
+
status = 'not-started'
|
|
79
|
+
} else if (progress === 100) {
|
|
80
|
+
status = 'completed'
|
|
81
|
+
} else if (progress > 0) {
|
|
82
|
+
status = 'in-progress'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
...goal,
|
|
87
|
+
progress,
|
|
88
|
+
status,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Mark goal as at-risk
|
|
94
|
+
*/
|
|
95
|
+
export function markAtRisk(goal: GoalDefinition): GoalDefinition {
|
|
96
|
+
return {
|
|
97
|
+
...goal,
|
|
98
|
+
status: 'at-risk',
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Mark goal as completed
|
|
104
|
+
*/
|
|
105
|
+
export function complete(goal: GoalDefinition): GoalDefinition {
|
|
106
|
+
return {
|
|
107
|
+
...goal,
|
|
108
|
+
status: 'completed',
|
|
109
|
+
progress: 100,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if goal is overdue
|
|
115
|
+
*/
|
|
116
|
+
export function isOverdue(goal: GoalDefinition): boolean {
|
|
117
|
+
if (!goal.targetDate) return false
|
|
118
|
+
return new Date() > goal.targetDate && goal.status !== 'completed'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get goals by category
|
|
123
|
+
*/
|
|
124
|
+
export function getGoalsByCategory(
|
|
125
|
+
goals: GoalDefinition[],
|
|
126
|
+
category: GoalDefinition['category']
|
|
127
|
+
): GoalDefinition[] {
|
|
128
|
+
return goals.filter(g => g.category === category)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get goals by status
|
|
133
|
+
*/
|
|
134
|
+
export function getGoalsByStatus(
|
|
135
|
+
goals: GoalDefinition[],
|
|
136
|
+
status: GoalDefinition['status']
|
|
137
|
+
): GoalDefinition[] {
|
|
138
|
+
return goals.filter(g => g.status === status)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get goals by owner
|
|
143
|
+
*/
|
|
144
|
+
export function getGoalsByOwner(goals: GoalDefinition[], owner: string): GoalDefinition[] {
|
|
145
|
+
return goals.filter(g => g.owner === owner)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Calculate overall progress across all goals
|
|
150
|
+
*/
|
|
151
|
+
export function calculateOverallProgress(goals: GoalDefinition[]): number {
|
|
152
|
+
if (goals.length === 0) return 0
|
|
153
|
+
|
|
154
|
+
const totalProgress = goals.reduce((sum, goal) => sum + (goal.progress || 0), 0)
|
|
155
|
+
return totalProgress / goals.length
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Check for circular dependencies
|
|
160
|
+
*/
|
|
161
|
+
export function hasCircularDependencies(goals: GoalDefinition[]): boolean {
|
|
162
|
+
const goalMap = new Map(goals.map(g => [g.name, g]))
|
|
163
|
+
|
|
164
|
+
function checkCircular(goalName: string, visited = new Set<string>()): boolean {
|
|
165
|
+
if (visited.has(goalName)) return true
|
|
166
|
+
|
|
167
|
+
const goal = goalMap.get(goalName)
|
|
168
|
+
if (!goal || !goal.dependencies) return false
|
|
169
|
+
|
|
170
|
+
visited.add(goalName)
|
|
171
|
+
|
|
172
|
+
for (const dep of goal.dependencies) {
|
|
173
|
+
if (checkCircular(dep, new Set(visited))) {
|
|
174
|
+
return true
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return goals.some(goal => checkCircular(goal.name))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get goals in dependency order
|
|
186
|
+
*/
|
|
187
|
+
export function sortByDependencies(goals: GoalDefinition[]): GoalDefinition[] {
|
|
188
|
+
const goalMap = new Map(goals.map(g => [g.name, g]))
|
|
189
|
+
const sorted: GoalDefinition[] = []
|
|
190
|
+
const visited = new Set<string>()
|
|
191
|
+
|
|
192
|
+
function visit(goalName: string) {
|
|
193
|
+
if (visited.has(goalName)) return
|
|
194
|
+
|
|
195
|
+
const goal = goalMap.get(goalName)
|
|
196
|
+
if (!goal) return
|
|
197
|
+
|
|
198
|
+
visited.add(goalName)
|
|
199
|
+
|
|
200
|
+
// Visit dependencies first
|
|
201
|
+
if (goal.dependencies) {
|
|
202
|
+
for (const dep of goal.dependencies) {
|
|
203
|
+
visit(dep)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
sorted.push(goal)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const goal of goals) {
|
|
211
|
+
visit(goal.name)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return sorted
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Validate goals
|
|
219
|
+
*/
|
|
220
|
+
export function validateGoals(goals: GoalDefinition[]): { valid: boolean; errors: string[] } {
|
|
221
|
+
const errors: string[] = []
|
|
222
|
+
|
|
223
|
+
for (const goal of goals) {
|
|
224
|
+
if (!goal.name) {
|
|
225
|
+
errors.push('Goal name is required')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (goal.progress && (goal.progress < 0 || goal.progress > 100)) {
|
|
229
|
+
errors.push(`Goal ${goal.name} progress must be between 0 and 100`)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (goal.dependencies) {
|
|
233
|
+
const goalNames = new Set(goals.map(g => g.name))
|
|
234
|
+
for (const dep of goal.dependencies) {
|
|
235
|
+
if (!goalNames.has(dep)) {
|
|
236
|
+
errors.push(`Goal ${goal.name} depends on unknown goal: ${dep}`)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (hasCircularDependencies(goals)) {
|
|
243
|
+
errors.push('Circular dependencies detected in goals')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
valid: errors.length === 0,
|
|
248
|
+
errors,
|
|
249
|
+
}
|
|
250
|
+
}
|