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.
Files changed (190) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +17 -0
  3. package/IMPLEMENTATION.md +226 -0
  4. package/README.md +1133 -193
  5. package/dist/business.d.ts +62 -0
  6. package/dist/business.d.ts.map +1 -0
  7. package/dist/business.js +109 -0
  8. package/dist/business.js.map +1 -0
  9. package/dist/dollar.d.ts +60 -0
  10. package/dist/dollar.d.ts.map +1 -0
  11. package/dist/dollar.js +107 -0
  12. package/dist/dollar.js.map +1 -0
  13. package/dist/entities/assets.d.ts +21 -0
  14. package/dist/entities/assets.d.ts.map +1 -0
  15. package/dist/entities/assets.js +323 -0
  16. package/dist/entities/assets.js.map +1 -0
  17. package/dist/entities/business.d.ts +36 -0
  18. package/dist/entities/business.d.ts.map +1 -0
  19. package/dist/entities/business.js +370 -0
  20. package/dist/entities/business.js.map +1 -0
  21. package/dist/entities/communication.d.ts +21 -0
  22. package/dist/entities/communication.d.ts.map +1 -0
  23. package/dist/entities/communication.js +255 -0
  24. package/dist/entities/communication.js.map +1 -0
  25. package/dist/entities/customers.d.ts +58 -0
  26. package/dist/entities/customers.d.ts.map +1 -0
  27. package/dist/entities/customers.js +989 -0
  28. package/dist/entities/customers.js.map +1 -0
  29. package/dist/entities/financials.d.ts +59 -0
  30. package/dist/entities/financials.d.ts.map +1 -0
  31. package/dist/entities/financials.js +932 -0
  32. package/dist/entities/financials.js.map +1 -0
  33. package/dist/entities/goals.d.ts +58 -0
  34. package/dist/entities/goals.d.ts.map +1 -0
  35. package/dist/entities/goals.js +800 -0
  36. package/dist/entities/goals.js.map +1 -0
  37. package/dist/entities/index.d.ts +299 -0
  38. package/dist/entities/index.d.ts.map +1 -0
  39. package/dist/entities/index.js +198 -0
  40. package/dist/entities/index.js.map +1 -0
  41. package/dist/entities/legal.d.ts +21 -0
  42. package/dist/entities/legal.d.ts.map +1 -0
  43. package/dist/entities/legal.js +301 -0
  44. package/dist/entities/legal.js.map +1 -0
  45. package/dist/entities/market.d.ts +21 -0
  46. package/dist/entities/market.d.ts.map +1 -0
  47. package/dist/entities/market.js +301 -0
  48. package/dist/entities/market.js.map +1 -0
  49. package/dist/entities/marketing.d.ts +67 -0
  50. package/dist/entities/marketing.d.ts.map +1 -0
  51. package/dist/entities/marketing.js +1157 -0
  52. package/dist/entities/marketing.js.map +1 -0
  53. package/dist/entities/offerings.d.ts +51 -0
  54. package/dist/entities/offerings.d.ts.map +1 -0
  55. package/dist/entities/offerings.js +727 -0
  56. package/dist/entities/offerings.js.map +1 -0
  57. package/dist/entities/operations.d.ts +58 -0
  58. package/dist/entities/operations.d.ts.map +1 -0
  59. package/dist/entities/operations.js +787 -0
  60. package/dist/entities/operations.js.map +1 -0
  61. package/dist/entities/organization.d.ts +57 -0
  62. package/dist/entities/organization.d.ts.map +1 -0
  63. package/dist/entities/organization.js +807 -0
  64. package/dist/entities/organization.js.map +1 -0
  65. package/dist/entities/partnerships.d.ts +21 -0
  66. package/dist/entities/partnerships.d.ts.map +1 -0
  67. package/dist/entities/partnerships.js +300 -0
  68. package/dist/entities/partnerships.js.map +1 -0
  69. package/dist/entities/planning.d.ts +87 -0
  70. package/dist/entities/planning.d.ts.map +1 -0
  71. package/dist/entities/planning.js +271 -0
  72. package/dist/entities/planning.js.map +1 -0
  73. package/dist/entities/projects.d.ts +25 -0
  74. package/dist/entities/projects.d.ts.map +1 -0
  75. package/dist/entities/projects.js +349 -0
  76. package/dist/entities/projects.js.map +1 -0
  77. package/dist/entities/risk.d.ts +21 -0
  78. package/dist/entities/risk.d.ts.map +1 -0
  79. package/dist/entities/risk.js +293 -0
  80. package/dist/entities/risk.js.map +1 -0
  81. package/dist/entities/sales.d.ts +72 -0
  82. package/dist/entities/sales.d.ts.map +1 -0
  83. package/dist/entities/sales.js +1248 -0
  84. package/dist/entities/sales.js.map +1 -0
  85. package/dist/financials.d.ts +130 -0
  86. package/dist/financials.d.ts.map +1 -0
  87. package/dist/financials.js +297 -0
  88. package/dist/financials.js.map +1 -0
  89. package/dist/goals.d.ts +87 -0
  90. package/dist/goals.d.ts.map +1 -0
  91. package/dist/goals.js +215 -0
  92. package/dist/goals.js.map +1 -0
  93. package/dist/index.d.ts +97 -4
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +131 -1079
  96. package/dist/index.js.map +1 -1
  97. package/dist/kpis.d.ts +118 -0
  98. package/dist/kpis.d.ts.map +1 -0
  99. package/dist/kpis.js +232 -0
  100. package/dist/kpis.js.map +1 -0
  101. package/dist/metrics.d.ts +448 -0
  102. package/dist/metrics.d.ts.map +1 -0
  103. package/dist/metrics.js +325 -0
  104. package/dist/metrics.js.map +1 -0
  105. package/dist/okrs.d.ts +123 -0
  106. package/dist/okrs.d.ts.map +1 -0
  107. package/dist/okrs.js +269 -0
  108. package/dist/okrs.js.map +1 -0
  109. package/dist/organization.d.ts +585 -0
  110. package/dist/organization.d.ts.map +1 -0
  111. package/dist/organization.js +173 -0
  112. package/dist/organization.js.map +1 -0
  113. package/dist/process.d.ts +112 -0
  114. package/dist/process.d.ts.map +1 -0
  115. package/dist/process.js +241 -0
  116. package/dist/process.js.map +1 -0
  117. package/dist/product.d.ts +85 -0
  118. package/dist/product.d.ts.map +1 -0
  119. package/dist/product.js +145 -0
  120. package/dist/product.js.map +1 -0
  121. package/dist/queries.d.ts +304 -0
  122. package/dist/queries.d.ts.map +1 -0
  123. package/dist/queries.js +415 -0
  124. package/dist/queries.js.map +1 -0
  125. package/dist/roles.d.ts +340 -0
  126. package/dist/roles.d.ts.map +1 -0
  127. package/dist/roles.js +255 -0
  128. package/dist/roles.js.map +1 -0
  129. package/dist/service.d.ts +61 -0
  130. package/dist/service.d.ts.map +1 -0
  131. package/dist/service.js +140 -0
  132. package/dist/service.js.map +1 -0
  133. package/dist/types.d.ts +459 -0
  134. package/dist/types.d.ts.map +1 -0
  135. package/dist/types.js +5 -0
  136. package/dist/types.js.map +1 -0
  137. package/dist/vision.d.ts +38 -0
  138. package/dist/vision.d.ts.map +1 -0
  139. package/dist/vision.js +68 -0
  140. package/dist/vision.js.map +1 -0
  141. package/dist/workflow.d.ts +115 -0
  142. package/dist/workflow.d.ts.map +1 -0
  143. package/dist/workflow.js +247 -0
  144. package/dist/workflow.js.map +1 -0
  145. package/examples/basic-usage.ts +307 -0
  146. package/package.json +19 -60
  147. package/src/business.ts +121 -0
  148. package/src/dollar.ts +132 -0
  149. package/src/entities/assets.ts +332 -0
  150. package/src/entities/business.ts +406 -0
  151. package/src/entities/communication.ts +264 -0
  152. package/src/entities/customers.ts +1072 -0
  153. package/src/entities/financials.ts +1011 -0
  154. package/src/entities/goals.ts +871 -0
  155. package/src/entities/index.ts +383 -0
  156. package/src/entities/legal.ts +310 -0
  157. package/src/entities/market.ts +310 -0
  158. package/src/entities/marketing.ts +1249 -0
  159. package/src/entities/offerings.ts +789 -0
  160. package/src/entities/operations.ts +861 -0
  161. package/src/entities/organization.ts +876 -0
  162. package/src/entities/partnerships.ts +309 -0
  163. package/src/entities/planning.ts +307 -0
  164. package/src/entities/projects.ts +360 -0
  165. package/src/entities/risk.ts +302 -0
  166. package/src/entities/sales.ts +1352 -0
  167. package/src/financials.ts +352 -0
  168. package/src/goals.ts +250 -0
  169. package/src/index.test.ts +336 -0
  170. package/src/index.ts +530 -0
  171. package/src/kpis.ts +275 -0
  172. package/src/metrics.ts +825 -0
  173. package/src/okrs.ts +325 -0
  174. package/src/organization.ts +909 -0
  175. package/src/process.ts +272 -0
  176. package/src/product.ts +178 -0
  177. package/src/queries.ts +767 -0
  178. package/src/roles.ts +686 -0
  179. package/src/service.ts +164 -0
  180. package/src/types.ts +493 -0
  181. package/src/vision.ts +88 -0
  182. package/src/workflow.ts +280 -0
  183. package/tsconfig.json +9 -0
  184. package/dist/loaders/index.d.ts +0 -174
  185. package/dist/loaders/index.js +0 -366
  186. package/dist/loaders/index.js.map +0 -1
  187. package/dist/schema/index.d.ts +0 -146
  188. package/dist/schema/index.js +0 -716
  189. package/dist/schema/index.js.map +0 -1
  190. 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
+ }