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/queries.ts CHANGED
@@ -17,14 +17,7 @@ import type { TimePeriod } from './types.js'
17
17
  /**
18
18
  * Time granularity for aggregations
19
19
  */
20
- export type Granularity =
21
- | 'minute'
22
- | 'hour'
23
- | 'day'
24
- | 'week'
25
- | 'month'
26
- | 'quarter'
27
- | 'year'
20
+ export type Granularity = 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'
28
21
 
29
22
  /**
30
23
  * Aggregation function
@@ -50,18 +43,18 @@ export type AggregateFunction =
50
43
  * Comparison operator
51
44
  */
52
45
  export type Operator =
53
- | 'eq' // =
54
- | 'ne' // !=
55
- | 'gt' // >
56
- | 'gte' // >=
57
- | 'lt' // <
58
- | 'lte' // <=
59
- | 'in' // IN
60
- | 'notIn' // NOT IN
61
- | 'like' // LIKE
46
+ | 'eq' // =
47
+ | 'ne' // !=
48
+ | 'gt' // >
49
+ | 'gte' // >=
50
+ | 'lt' // <
51
+ | 'lte' // <=
52
+ | 'in' // IN
53
+ | 'notIn' // NOT IN
54
+ | 'like' // LIKE
62
55
  | 'notLike' // NOT LIKE
63
56
  | 'between' // BETWEEN
64
- | 'isNull' // IS NULL
57
+ | 'isNull' // IS NULL
65
58
  | 'isNotNull' // IS NOT NULL
66
59
 
67
60
  /**
@@ -78,11 +71,11 @@ export type SortDirection = 'asc' | 'desc'
78
71
  */
79
72
  export interface Dimension {
80
73
  name: string
81
- field: string // Source field in database
74
+ field: string // Source field in database
82
75
  type: 'string' | 'number' | 'date' | 'boolean'
83
76
  description?: string
84
- granularity?: Granularity // For time dimensions
85
- format?: string // Display format
77
+ granularity?: Granularity // For time dimensions
78
+ format?: string // Display format
86
79
  }
87
80
 
88
81
  /**
@@ -90,7 +83,7 @@ export interface Dimension {
90
83
  */
91
84
  export interface Measure {
92
85
  name: string
93
- field: string // Source field or expression
86
+ field: string // Source field or expression
94
87
  aggregate: AggregateFunction
95
88
  type?: 'number' | 'currency' | 'percent'
96
89
  description?: string
@@ -103,8 +96,8 @@ export interface Measure {
103
96
  */
104
97
  export interface CalculatedMeasure {
105
98
  name: string
106
- expression: string // e.g., "revenue - cogs" or "revenue / customers"
107
- measures: string[] // Dependencies
99
+ expression: string // e.g., "revenue - cogs" or "revenue / customers"
100
+ measures: string[] // Dependencies
108
101
  type?: 'number' | 'currency' | 'percent'
109
102
  description?: string
110
103
  format?: string
@@ -137,8 +130,8 @@ export interface Sort {
137
130
  * Time range filter
138
131
  */
139
132
  export interface TimeRange {
140
- field: string // The timestamp field
141
- start?: Date | string // Absolute or relative (e.g., '-30d')
133
+ field: string // The timestamp field
134
+ start?: Date | string // Absolute or relative (e.g., '-30d')
142
135
  end?: Date | string
143
136
  granularity?: Granularity
144
137
  }
@@ -155,11 +148,11 @@ export interface Query {
155
148
  description?: string
156
149
 
157
150
  // Data source
158
- source: string // Table or view name
151
+ source: string // Table or view name
159
152
 
160
153
  // What to select
161
- dimensions?: string[] // Dimension names to group by
162
- measures?: string[] // Measure names to aggregate
154
+ dimensions?: string[] // Dimension names to group by
155
+ measures?: string[] // Measure names to aggregate
163
156
 
164
157
  // Filtering
165
158
  filters?: Filter[]
@@ -197,8 +190,8 @@ export interface View {
197
190
 
198
191
  // Materialization options
199
192
  materialized?: boolean
200
- refreshInterval?: string // e.g., '5m', '1h', '1d'
201
- retention?: string // How long to keep data
193
+ refreshInterval?: string // e.g., '5m', '1h', '1d'
194
+ retention?: string // How long to keep data
202
195
 
203
196
  // Access
204
197
  public?: boolean
@@ -244,16 +237,16 @@ export interface DashboardItem {
244
237
  * Visualization type
245
238
  */
246
239
  export type Visualization =
247
- | 'number' // Single big number
248
- | 'trend' // Number with sparkline
249
- | 'table' // Data table
250
- | 'bar' // Bar chart
251
- | 'line' // Line chart
252
- | 'area' // Area chart
253
- | 'pie' // Pie chart
254
- | 'funnel' // Funnel chart
255
- | 'cohort' // Cohort matrix
256
- | 'heatmap' // Heatmap
240
+ | 'number' // Single big number
241
+ | 'trend' // Number with sparkline
242
+ | 'table' // Data table
243
+ | 'bar' // Bar chart
244
+ | 'line' // Line chart
245
+ | 'area' // Area chart
246
+ | 'pie' // Pie chart
247
+ | 'funnel' // Funnel chart
248
+ | 'cohort' // Cohort matrix
249
+ | 'heatmap' // Heatmap
257
250
 
258
251
  // =============================================================================
259
252
  // Metric Definitions (Standard SaaS Metrics as Queries)
@@ -266,18 +259,39 @@ export const StandardDimensions: Record<string, Dimension> = {
266
259
  // Time
267
260
  date: { name: 'date', field: 'date', type: 'date', description: 'Event date' },
268
261
  month: { name: 'month', field: 'date', type: 'date', granularity: 'month', description: 'Month' },
269
- quarter: { name: 'quarter', field: 'date', type: 'date', granularity: 'quarter', description: 'Quarter' },
262
+ quarter: {
263
+ name: 'quarter',
264
+ field: 'date',
265
+ type: 'date',
266
+ granularity: 'quarter',
267
+ description: 'Quarter',
268
+ },
270
269
  year: { name: 'year', field: 'date', type: 'date', granularity: 'year', description: 'Year' },
271
270
 
272
271
  // Customer
273
- customerId: { name: 'customerId', field: 'customer_id', type: 'string', description: 'Customer ID' },
274
- customerSegment: { name: 'customerSegment', field: 'customer_segment', type: 'string', description: 'Customer segment' },
272
+ customerId: {
273
+ name: 'customerId',
274
+ field: 'customer_id',
275
+ type: 'string',
276
+ description: 'Customer ID',
277
+ },
278
+ customerSegment: {
279
+ name: 'customerSegment',
280
+ field: 'customer_segment',
281
+ type: 'string',
282
+ description: 'Customer segment',
283
+ },
275
284
  plan: { name: 'plan', field: 'plan', type: 'string', description: 'Subscription plan' },
276
285
  cohort: { name: 'cohort', field: 'cohort', type: 'string', description: 'Customer cohort' },
277
286
 
278
287
  // Product
279
288
  productId: { name: 'productId', field: 'product_id', type: 'string', description: 'Product ID' },
280
- productName: { name: 'productName', field: 'product_name', type: 'string', description: 'Product name' },
289
+ productName: {
290
+ name: 'productName',
291
+ field: 'product_name',
292
+ type: 'string',
293
+ description: 'Product name',
294
+ },
281
295
  feature: { name: 'feature', field: 'feature', type: 'string', description: 'Feature name' },
282
296
 
283
297
  // Geography
@@ -285,9 +299,19 @@ export const StandardDimensions: Record<string, Dimension> = {
285
299
  region: { name: 'region', field: 'region', type: 'string', description: 'Region' },
286
300
 
287
301
  // Channel
288
- channel: { name: 'channel', field: 'channel', type: 'string', description: 'Acquisition channel' },
302
+ channel: {
303
+ name: 'channel',
304
+ field: 'channel',
305
+ type: 'string',
306
+ description: 'Acquisition channel',
307
+ },
289
308
  source: { name: 'source', field: 'source', type: 'string', description: 'Traffic source' },
290
- campaign: { name: 'campaign', field: 'campaign', type: 'string', description: 'Marketing campaign' },
309
+ campaign: {
310
+ name: 'campaign',
311
+ field: 'campaign',
312
+ type: 'string',
313
+ description: 'Marketing campaign',
314
+ },
291
315
  }
292
316
 
293
317
  /**
@@ -295,27 +319,117 @@ export const StandardDimensions: Record<string, Dimension> = {
295
319
  */
296
320
  export const StandardMeasures: Record<string, Measure> = {
297
321
  // Revenue
298
- revenue: { name: 'revenue', field: 'revenue', aggregate: 'sum', type: 'currency', description: 'Total revenue' },
299
- mrr: { name: 'mrr', field: 'mrr', aggregate: 'sum', type: 'currency', description: 'Monthly recurring revenue' },
300
- newMrr: { name: 'newMrr', field: 'new_mrr', aggregate: 'sum', type: 'currency', description: 'New MRR' },
301
- expansionMrr: { name: 'expansionMrr', field: 'expansion_mrr', aggregate: 'sum', type: 'currency', description: 'Expansion MRR' },
302
- contractionMrr: { name: 'contractionMrr', field: 'contraction_mrr', aggregate: 'sum', type: 'currency', description: 'Contraction MRR' },
303
- churnedMrr: { name: 'churnedMrr', field: 'churned_mrr', aggregate: 'sum', type: 'currency', description: 'Churned MRR' },
322
+ revenue: {
323
+ name: 'revenue',
324
+ field: 'revenue',
325
+ aggregate: 'sum',
326
+ type: 'currency',
327
+ description: 'Total revenue',
328
+ },
329
+ mrr: {
330
+ name: 'mrr',
331
+ field: 'mrr',
332
+ aggregate: 'sum',
333
+ type: 'currency',
334
+ description: 'Monthly recurring revenue',
335
+ },
336
+ newMrr: {
337
+ name: 'newMrr',
338
+ field: 'new_mrr',
339
+ aggregate: 'sum',
340
+ type: 'currency',
341
+ description: 'New MRR',
342
+ },
343
+ expansionMrr: {
344
+ name: 'expansionMrr',
345
+ field: 'expansion_mrr',
346
+ aggregate: 'sum',
347
+ type: 'currency',
348
+ description: 'Expansion MRR',
349
+ },
350
+ contractionMrr: {
351
+ name: 'contractionMrr',
352
+ field: 'contraction_mrr',
353
+ aggregate: 'sum',
354
+ type: 'currency',
355
+ description: 'Contraction MRR',
356
+ },
357
+ churnedMrr: {
358
+ name: 'churnedMrr',
359
+ field: 'churned_mrr',
360
+ aggregate: 'sum',
361
+ type: 'currency',
362
+ description: 'Churned MRR',
363
+ },
304
364
 
305
365
  // Customers
306
- customers: { name: 'customers', field: 'customer_id', aggregate: 'countDistinct', type: 'number', description: 'Unique customers' },
307
- newCustomers: { name: 'newCustomers', field: 'new_customer_id', aggregate: 'countDistinct', type: 'number', description: 'New customers' },
308
- churnedCustomers: { name: 'churnedCustomers', field: 'churned_customer_id', aggregate: 'countDistinct', type: 'number', description: 'Churned customers' },
366
+ customers: {
367
+ name: 'customers',
368
+ field: 'customer_id',
369
+ aggregate: 'countDistinct',
370
+ type: 'number',
371
+ description: 'Unique customers',
372
+ },
373
+ newCustomers: {
374
+ name: 'newCustomers',
375
+ field: 'new_customer_id',
376
+ aggregate: 'countDistinct',
377
+ type: 'number',
378
+ description: 'New customers',
379
+ },
380
+ churnedCustomers: {
381
+ name: 'churnedCustomers',
382
+ field: 'churned_customer_id',
383
+ aggregate: 'countDistinct',
384
+ type: 'number',
385
+ description: 'Churned customers',
386
+ },
309
387
 
310
388
  // Usage
311
- events: { name: 'events', field: 'event_id', aggregate: 'count', type: 'number', description: 'Event count' },
312
- sessions: { name: 'sessions', field: 'session_id', aggregate: 'countDistinct', type: 'number', description: 'Unique sessions' },
313
- activeUsers: { name: 'activeUsers', field: 'user_id', aggregate: 'countDistinct', type: 'number', description: 'Active users' },
389
+ events: {
390
+ name: 'events',
391
+ field: 'event_id',
392
+ aggregate: 'count',
393
+ type: 'number',
394
+ description: 'Event count',
395
+ },
396
+ sessions: {
397
+ name: 'sessions',
398
+ field: 'session_id',
399
+ aggregate: 'countDistinct',
400
+ type: 'number',
401
+ description: 'Unique sessions',
402
+ },
403
+ activeUsers: {
404
+ name: 'activeUsers',
405
+ field: 'user_id',
406
+ aggregate: 'countDistinct',
407
+ type: 'number',
408
+ description: 'Active users',
409
+ },
314
410
 
315
411
  // Costs
316
- cogs: { name: 'cogs', field: 'cogs', aggregate: 'sum', type: 'currency', description: 'Cost of goods sold' },
317
- salesSpend: { name: 'salesSpend', field: 'sales_spend', aggregate: 'sum', type: 'currency', description: 'Sales spend' },
318
- marketingSpend: { name: 'marketingSpend', field: 'marketing_spend', aggregate: 'sum', type: 'currency', description: 'Marketing spend' },
412
+ cogs: {
413
+ name: 'cogs',
414
+ field: 'cogs',
415
+ aggregate: 'sum',
416
+ type: 'currency',
417
+ description: 'Cost of goods sold',
418
+ },
419
+ salesSpend: {
420
+ name: 'salesSpend',
421
+ field: 'sales_spend',
422
+ aggregate: 'sum',
423
+ type: 'currency',
424
+ description: 'Sales spend',
425
+ },
426
+ marketingSpend: {
427
+ name: 'marketingSpend',
428
+ field: 'marketing_spend',
429
+ aggregate: 'sum',
430
+ type: 'currency',
431
+ description: 'Marketing spend',
432
+ },
319
433
  }
320
434
 
321
435
  /**
@@ -471,8 +585,17 @@ export class QueryBuilder {
471
585
  return this
472
586
  }
473
587
 
474
- timeRange(field: string, start?: Date | string, end?: Date | string, granularity?: Granularity): this {
475
- this._query.timeRange = { field, start, end, granularity }
588
+ timeRange(
589
+ field: string,
590
+ start?: Date | string,
591
+ end?: Date | string,
592
+ granularity?: Granularity
593
+ ): this {
594
+ const timeRange: TimeRange = { field }
595
+ if (start !== undefined) timeRange.start = start
596
+ if (end !== undefined) timeRange.end = end
597
+ if (granularity !== undefined) timeRange.granularity = granularity
598
+ this._query.timeRange = timeRange
476
599
  return this
477
600
  }
478
601
 
@@ -610,8 +733,8 @@ export class ViewBuilder {
610
733
 
611
734
  materialize(refreshInterval?: string, retention?: string): this {
612
735
  this._view.materialized = true
613
- this._view.refreshInterval = refreshInterval
614
- this._view.retention = retention
736
+ if (refreshInterval !== undefined) this._view.refreshInterval = refreshInterval
737
+ if (retention !== undefined) this._view.retention = retention
615
738
  return this
616
739
  }
617
740
 
@@ -661,17 +784,27 @@ export class DashboardBuilder {
661
784
  return this
662
785
  }
663
786
 
664
- add(viewDef: View, options?: { x?: number; y?: number; width?: number; height?: number; visualization?: Visualization }): this {
787
+ add(
788
+ viewDef: View,
789
+ options?: {
790
+ x?: number
791
+ y?: number
792
+ width?: number
793
+ height?: number
794
+ visualization?: Visualization
795
+ }
796
+ ): this {
665
797
  this._dashboard.views.push(viewDef)
666
798
  if (options && this._dashboard.layout) {
667
- this._dashboard.layout.items.push({
799
+ const item: DashboardItem = {
668
800
  viewName: viewDef.name,
669
801
  x: options.x || 0,
670
802
  y: options.y || 0,
671
803
  width: options.width || 1,
672
804
  height: options.height || 1,
673
- visualization: options.visualization,
674
- })
805
+ }
806
+ if (options.visualization !== undefined) item.visualization = options.visualization
807
+ this._dashboard.layout.items.push(item)
675
808
  }
676
809
  return this
677
810
  }
@@ -711,11 +844,41 @@ export class DashboardBuilder {
711
844
  export const ExecutiveDashboard = dashboard('executive')
712
845
  .describe('Executive overview of key SaaS metrics')
713
846
  .layout(4, 3)
714
- .add(view('mrr', MrrOverview).build(), { x: 0, y: 0, width: 2, height: 1, visualization: 'trend' })
715
- .add(view('arr_segments', ArrBySegment).build(), { x: 2, y: 0, width: 2, height: 1, visualization: 'bar' })
716
- .add(view('unit_econ', UnitEconomics).build(), { x: 0, y: 1, width: 2, height: 1, visualization: 'table' })
717
- .add(view('growth', GrowthMetrics).build(), { x: 2, y: 1, width: 2, height: 1, visualization: 'line' })
718
- .add(view('cohorts', CohortRetention).build(), { x: 0, y: 2, width: 4, height: 1, visualization: 'cohort' })
847
+ .add(view('mrr', MrrOverview).build(), {
848
+ x: 0,
849
+ y: 0,
850
+ width: 2,
851
+ height: 1,
852
+ visualization: 'trend',
853
+ })
854
+ .add(view('arr_segments', ArrBySegment).build(), {
855
+ x: 2,
856
+ y: 0,
857
+ width: 2,
858
+ height: 1,
859
+ visualization: 'bar',
860
+ })
861
+ .add(view('unit_econ', UnitEconomics).build(), {
862
+ x: 0,
863
+ y: 1,
864
+ width: 2,
865
+ height: 1,
866
+ visualization: 'table',
867
+ })
868
+ .add(view('growth', GrowthMetrics).build(), {
869
+ x: 2,
870
+ y: 1,
871
+ width: 2,
872
+ height: 1,
873
+ visualization: 'line',
874
+ })
875
+ .add(view('cohorts', CohortRetention).build(), {
876
+ x: 0,
877
+ y: 2,
878
+ width: 4,
879
+ height: 1,
880
+ visualization: 'cohort',
881
+ })
719
882
  .refresh('5m')
720
883
  .tags('executive', 'saas', 'metrics')
721
884
  .build()
package/src/roles.ts CHANGED
@@ -9,6 +9,13 @@
9
9
  * @packageDocumentation
10
10
  */
11
11
 
12
+ import type { Role as OrgRole, RoleType as OrgRoleType, RoleWorkerType } from 'org.ai'
13
+ import type { Worker, WorkerRef } from 'digital-workers'
14
+
15
+ // Re-export for convenience
16
+ export type { Worker, WorkerRef } from 'digital-workers'
17
+ export type { OrgRole, OrgRoleType, RoleWorkerType }
18
+
12
19
  // =============================================================================
13
20
  // Business Role - Bridges Worker Role and Authorization
14
21
  // =============================================================================
@@ -43,7 +50,10 @@ export type BusinessRoleType =
43
50
  | string
44
51
 
45
52
  /**
46
- * Business Role - extends WorkerRole with authorization and task capabilities
53
+ * Business Role - extends org.ai Role with authorization and task capabilities
54
+ *
55
+ * Composes with org.ai Role to provide additional business-specific properties
56
+ * like authorization permissions, task capabilities, and compensation.
47
57
  *
48
58
  * @example
49
59
  * ```ts
@@ -54,6 +64,9 @@ export type BusinessRoleType =
54
64
  * department: 'Engineering',
55
65
  * description: 'Leads engineering team and makes technical decisions',
56
66
  *
67
+ * // From org.ai Role
68
+ * skills: ['TypeScript', 'Architecture', 'Team Leadership'],
69
+ *
57
70
  * // Business responsibilities
58
71
  * responsibilities: [
59
72
  * 'Lead engineering team',
@@ -78,28 +91,10 @@ export type BusinessRoleType =
78
91
  * }
79
92
  * ```
80
93
  */
81
- export interface BusinessRole {
82
- /** Unique role identifier */
83
- id: string
84
-
85
- /** Role display name */
86
- name: string
87
-
88
- /** Role type classification */
94
+ export interface BusinessRole extends Omit<OrgRole, 'permissions'> {
95
+ /** Role type classification (overrides OrgRole.type with business-specific types) */
89
96
  type: BusinessRoleType
90
97
 
91
- /** Department or team */
92
- department?: string
93
-
94
- /** Human-readable description */
95
- description?: string
96
-
97
- /** Key responsibilities */
98
- responsibilities?: string[]
99
-
100
- /** Required skills */
101
- skills?: string[]
102
-
103
98
  /**
104
99
  * Authorization permissions by resource type
105
100
  *
@@ -107,6 +102,8 @@ export interface BusinessRole {
107
102
  * - 'read', 'edit', 'delete', 'manage' (standard)
108
103
  * - 'act:*' or 'act:verb' (domain-specific verbs)
109
104
  *
105
+ * Note: This differs from OrgRole.permissions which is string[]
106
+ *
110
107
  * @example
111
108
  * ```ts
112
109
  * permissions: {
@@ -118,32 +115,8 @@ export interface BusinessRole {
118
115
  */
119
116
  permissions?: Record<string, string[]>
120
117
 
121
- /** Task types this role can handle */
122
- canHandle?: string[]
123
-
124
- /** Task types this role can delegate to others */
125
- canDelegate?: string[]
126
-
127
- /** Request types this role can approve */
128
- canApprove?: string[]
129
-
130
- /** Escalation path - role to escalate to */
131
- escalateTo?: string
132
-
133
- /** Reports to - manager role */
134
- reportsTo?: string
135
-
136
- /** Worker type preference: 'ai' | 'human' | 'hybrid' */
137
- workerType?: 'ai' | 'human' | 'hybrid'
138
-
139
- /** Level in hierarchy (1 = entry, higher = more senior) */
140
- level?: number
141
-
142
118
  /** Compensation band */
143
119
  compensationBand?: string
144
-
145
- /** Additional metadata */
146
- metadata?: Record<string, unknown>
147
120
  }
148
121
 
149
122
  // =============================================================================
@@ -263,6 +236,8 @@ export interface TaskAssignment {
263
236
 
264
237
  /**
265
238
  * Reference to an assignee (worker, team, or role)
239
+ *
240
+ * Compatible with digital-workers WorkerRef for worker assignments.
266
241
  */
267
242
  export interface AssigneeRef {
268
243
  /** Type of assignee */
@@ -273,6 +248,34 @@ export interface AssigneeRef {
273
248
  name?: string
274
249
  }
275
250
 
251
+ /**
252
+ * Convert a digital-workers WorkerRef to an AssigneeRef
253
+ */
254
+ export function workerRefToAssignee(workerRef: WorkerRef): AssigneeRef {
255
+ const result: AssigneeRef = {
256
+ type: 'worker',
257
+ id: workerRef.id,
258
+ }
259
+ if (workerRef.name !== undefined) {
260
+ result.name = workerRef.name
261
+ }
262
+ return result
263
+ }
264
+
265
+ /**
266
+ * Convert an AssigneeRef to a digital-workers WorkerRef (if type is 'worker')
267
+ */
268
+ export function assigneeToWorkerRef(assignee: AssigneeRef): WorkerRef | null {
269
+ if (assignee.type !== 'worker') return null
270
+ const result: WorkerRef = {
271
+ id: assignee.id,
272
+ }
273
+ if (assignee.name !== undefined) {
274
+ result.name = assignee.name
275
+ }
276
+ return result
277
+ }
278
+
276
279
  // =============================================================================
277
280
  // Role-Based Task Routing
278
281
  // =============================================================================
@@ -549,8 +552,8 @@ export function createBusinessRole(
549
552
 
550
553
  return {
551
554
  id,
552
- name: standard.name || template,
553
- type: standard.type || template,
555
+ name: standard['name'] || template,
556
+ type: standard['type'] || template,
554
557
  ...standard,
555
558
  ...overrides,
556
559
  } as BusinessRole
@@ -559,11 +562,7 @@ export function createBusinessRole(
559
562
  /**
560
563
  * Check if a role has permission for an action on a resource type
561
564
  */
562
- export function hasPermission(
563
- role: BusinessRole,
564
- resourceType: string,
565
- action: string
566
- ): boolean {
565
+ export function hasPermission(role: BusinessRole, resourceType: string, action: string): boolean {
567
566
  if (!role.permissions) return false
568
567
 
569
568
  // Check wildcard permissions
@@ -595,24 +594,24 @@ export function hasPermission(
595
594
  * Check if a role can handle a task type
596
595
  */
597
596
  export function canHandleTask(role: BusinessRole, taskType: string): boolean {
598
- if (!role.canHandle) return false
599
- return role.canHandle.includes(taskType) || role.canHandle.includes('*')
597
+ if (!role['canHandle']) return false
598
+ return role['canHandle'].includes(taskType) || role['canHandle'].includes('*')
600
599
  }
601
600
 
602
601
  /**
603
602
  * Check if a role can approve a request type
604
603
  */
605
604
  export function canApproveRequest(role: BusinessRole, requestType: string): boolean {
606
- if (!role.canApprove) return false
607
- return role.canApprove.includes(requestType) || role.canApprove.includes('*')
605
+ if (!role['canApprove']) return false
606
+ return role['canApprove'].includes(requestType) || role['canApprove'].includes('*')
608
607
  }
609
608
 
610
609
  /**
611
610
  * Check if a role can delegate a task type
612
611
  */
613
612
  export function canDelegateTask(role: BusinessRole, taskType: string): boolean {
614
- if (!role.canDelegate) return false
615
- return role.canDelegate.includes(taskType) || role.canDelegate.includes('*')
613
+ if (!role['canDelegate']) return false
614
+ return role['canDelegate'].includes(taskType) || role['canDelegate'].includes('*')
616
615
  }
617
616
 
618
617
  /**
@@ -623,7 +622,7 @@ export function findRoleForTask(
623
622
  rules: TaskRoutingRule[],
624
623
  context?: { amount?: number; skills?: string[] }
625
624
  ): TaskRoutingRule | undefined {
626
- const matchingRules = rules.filter(rule => rule.taskType === taskType)
625
+ const matchingRules = rules.filter((rule) => rule.taskType === taskType)
627
626
 
628
627
  if (matchingRules.length === 0) return undefined
629
628
 
@@ -633,7 +632,7 @@ export function findRoleForTask(
633
632
  if (rule.escalateAbove && context.amount > rule.escalateAbove) {
634
633
  // Find the escalated rule
635
634
  const escalatedRule = rules.find(
636
- r => r.taskType === taskType && r.requiredRole === rule.escalateTo
635
+ (r) => r.taskType === taskType && r.requiredRole === rule.escalateTo
637
636
  )
638
637
  if (escalatedRule) return escalatedRule
639
638
  }
@@ -651,7 +650,9 @@ export function createTaskAssignment(
651
650
  taskId: string,
652
651
  taskType: string,
653
652
  assignee: AssigneeRef,
654
- options?: Partial<Omit<TaskAssignment, 'id' | 'taskId' | 'taskType' | 'assignee' | 'status' | 'assignedAt'>>
653
+ options?: Partial<
654
+ Omit<TaskAssignment, 'id' | 'taskId' | 'taskType' | 'assignee' | 'status' | 'assignedAt'>
655
+ >
655
656
  ): TaskAssignment {
656
657
  return {
657
658
  id: `assign_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,