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.
- package/.turbo/turbo-build.log +4 -5
- package/CHANGELOG.md +53 -0
- package/README.md +2 -0
- package/dist/dollar.d.ts.map +1 -1
- package/dist/dollar.js +2 -2
- package/dist/dollar.js.map +1 -1
- package/dist/entities/organization.d.ts +4 -0
- package/dist/entities/organization.d.ts.map +1 -1
- package/dist/entities/organization.js +27 -18
- package/dist/entities/organization.js.map +1 -1
- package/dist/entities/planning.d.ts +87 -0
- package/dist/finance/account.d.ts +44 -0
- package/dist/finance/account.d.ts.map +1 -0
- package/dist/finance/account.js +6 -0
- package/dist/finance/account.js.map +1 -0
- package/dist/finance/authority.d.ts +78 -0
- package/dist/finance/authority.d.ts.map +1 -0
- package/dist/finance/authority.js +27 -0
- package/dist/finance/authority.js.map +1 -0
- package/dist/finance/card.d.ts +36 -0
- package/dist/finance/card.d.ts.map +1 -0
- package/dist/finance/card.js +6 -0
- package/dist/finance/card.js.map +1 -0
- package/dist/finance/identity.d.ts +30 -0
- package/dist/finance/identity.d.ts.map +1 -0
- package/dist/finance/identity.js +8 -0
- package/dist/finance/identity.js.map +1 -0
- package/dist/finance/index.d.ts +36 -0
- package/dist/finance/index.d.ts.map +1 -0
- package/dist/finance/index.js +22 -0
- package/dist/finance/index.js.map +1 -0
- package/dist/finance/ledger.d.ts +24 -0
- package/dist/finance/ledger.d.ts.map +1 -0
- package/dist/finance/ledger.js +8 -0
- package/dist/finance/ledger.js.map +1 -0
- package/dist/finance/merchant.d.ts +129 -0
- package/dist/finance/merchant.d.ts.map +1 -0
- package/dist/finance/merchant.js +21 -0
- package/dist/finance/merchant.js.map +1 -0
- package/dist/finance/outcome-contract.d.ts +139 -0
- package/dist/finance/outcome-contract.d.ts.map +1 -0
- package/dist/finance/outcome-contract.js +27 -0
- package/dist/finance/outcome-contract.js.map +1 -0
- package/dist/finance/port.d.ts +121 -0
- package/dist/finance/port.d.ts.map +1 -0
- package/dist/finance/port.js +10 -0
- package/dist/finance/port.js.map +1 -0
- package/dist/finance/pricing.d.ts +154 -0
- package/dist/finance/pricing.d.ts.map +1 -0
- package/dist/finance/pricing.js +79 -0
- package/dist/finance/pricing.js.map +1 -0
- package/dist/finance/proof-predicate.d.ts +92 -0
- package/dist/finance/proof-predicate.d.ts.map +1 -0
- package/dist/finance/proof-predicate.js +80 -0
- package/dist/finance/proof-predicate.js.map +1 -0
- package/dist/finance/refund.d.ts +44 -0
- package/dist/finance/refund.d.ts.map +1 -0
- package/dist/finance/refund.js +41 -0
- package/dist/finance/refund.js.map +1 -0
- package/dist/finance/sla.d.ts +25 -0
- package/dist/finance/sla.d.ts.map +1 -0
- package/dist/finance/sla.js +7 -0
- package/dist/finance/sla.js.map +1 -0
- package/dist/finance/types.d.ts +79 -0
- package/dist/finance/types.d.ts.map +1 -0
- package/dist/finance/types.js +8 -0
- package/dist/{canvas → finance}/types.js.map +1 -1
- package/dist/goals.d.ts +19 -0
- package/dist/goals.d.ts.map +1 -1
- package/dist/goals.js +81 -12
- package/dist/goals.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -1
- package/dist/kpis.d.ts +19 -0
- package/dist/kpis.d.ts.map +1 -1
- package/dist/kpis.js +71 -6
- package/dist/kpis.js.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +29 -24
- package/dist/metrics.js.map +1 -1
- package/dist/okrs.d.ts +34 -0
- package/dist/okrs.d.ts.map +1 -1
- package/dist/okrs.js +135 -13
- package/dist/okrs.js.map +1 -1
- package/dist/organization.d.ts.map +1 -1
- package/dist/organization.js +11 -11
- package/dist/organization.js.map +1 -1
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +13 -12
- package/dist/process.js.map +1 -1
- package/dist/product.d.ts.map +1 -1
- package/dist/product.js +9 -9
- package/dist/product.js.map +1 -1
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +194 -32
- package/dist/queries.js.map +1 -1
- package/dist/roles.d.ts +25 -31
- package/dist/roles.d.ts.map +1 -1
- package/dist/roles.js +37 -10
- package/dist/roles.js.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +13 -12
- package/dist/workflow.js.map +1 -1
- package/package.json +20 -13
- package/src/dollar.ts +5 -2
- package/src/entities/organization.ts +31 -18
- package/src/finance/account.ts +48 -0
- package/src/finance/authority.ts +42 -0
- package/src/finance/card.ts +38 -0
- package/src/finance/identity.ts +31 -0
- package/src/finance/index.ts +117 -0
- package/src/finance/ledger.ts +26 -0
- package/src/finance/merchant.ts +127 -0
- package/src/finance/outcome-contract.ts +157 -0
- package/src/finance/port.ts +144 -0
- package/src/finance/pricing.ts +197 -0
- package/src/finance/proof-predicate.ts +106 -0
- package/src/finance/refund.ts +52 -0
- package/src/finance/sla.ts +33 -0
- package/src/finance/types.ts +75 -0
- package/src/goals.ts +78 -12
- package/src/index.ts +48 -18
- package/src/kpis.ts +62 -8
- package/src/metrics.ts +92 -79
- package/src/okrs.ts +120 -20
- package/src/organization.ts +12 -15
- package/src/process.ts +11 -12
- package/src/product.ts +8 -9
- package/src/queries.ts +238 -75
- package/src/roles.ts +62 -61
- package/src/workflow.ts +22 -15
- package/test/business.test.ts +282 -0
- package/test/dollar.test.ts +270 -0
- package/test/entities.test.ts +628 -0
- package/test/financials.test.ts +539 -0
- package/test/goals.test.ts +451 -0
- package/{src → test}/index.test.ts +1 -1
- package/test/kpis.test.ts +440 -0
- package/test/metrics.test.ts +744 -0
- package/test/okrs.test.ts +741 -0
- package/test/organization.test.ts +548 -0
- package/test/process.test.ts +503 -0
- package/test/product.test.ts +430 -0
- package/test/queries.test.ts +556 -0
- package/test/roles.test.ts +546 -0
- package/test/service.test.ts +450 -0
- package/test/types.test.ts +1141 -0
- package/test/vision.test.ts +214 -0
- package/test/workflow.test.ts +501 -0
- package/vitest.config.ts +47 -0
- package/LICENSE +0 -21
- package/dist/canvas/activities.d.ts +0 -19
- package/dist/canvas/activities.d.ts.map +0 -1
- package/dist/canvas/activities.js +0 -20
- package/dist/canvas/activities.js.map +0 -1
- package/dist/canvas/channels.d.ts +0 -20
- package/dist/canvas/channels.d.ts.map +0 -1
- package/dist/canvas/channels.js +0 -21
- package/dist/canvas/channels.js.map +0 -1
- package/dist/canvas/relationships.d.ts +0 -20
- package/dist/canvas/relationships.d.ts.map +0 -1
- package/dist/canvas/relationships.js +0 -21
- package/dist/canvas/relationships.js.map +0 -1
- package/dist/canvas/resources.d.ts +0 -20
- package/dist/canvas/resources.d.ts.map +0 -1
- package/dist/canvas/resources.js +0 -30
- package/dist/canvas/resources.js.map +0 -1
- package/dist/canvas/revenue.d.ts +0 -22
- package/dist/canvas/revenue.d.ts.map +0 -1
- package/dist/canvas/revenue.js +0 -30
- package/dist/canvas/revenue.js.map +0 -1
- package/dist/canvas/segments.d.ts +0 -20
- package/dist/canvas/segments.d.ts.map +0 -1
- package/dist/canvas/segments.js +0 -28
- package/dist/canvas/segments.js.map +0 -1
- package/dist/canvas/types.d.ts +0 -232
- package/dist/canvas/types.d.ts.map +0 -1
- package/dist/canvas/types.js +0 -8
- package/dist/canvas/value.d.ts +0 -20
- package/dist/canvas/value.d.ts.map +0 -1
- package/dist/canvas/value.js +0 -21
- package/dist/canvas/value.js.map +0 -1
- package/src/business.js +0 -108
- package/src/canvas/activities.ts +0 -32
- package/src/canvas/canvas.ts +0 -482
- package/src/canvas/channels.ts +0 -34
- package/src/canvas/costs.ts +0 -43
- package/src/canvas/economics.ts +0 -99
- package/src/canvas/index.ts +0 -206
- package/src/canvas/partnerships.ts +0 -34
- package/src/canvas/projections.ts +0 -141
- package/src/canvas/relationships.ts +0 -34
- package/src/canvas/resources.ts +0 -43
- package/src/canvas/revenue.ts +0 -56
- package/src/canvas/segments.ts +0 -42
- package/src/canvas/types.ts +0 -363
- package/src/canvas/value.ts +0 -34
- package/src/dollar.js +0 -106
- package/src/entities/assets.js +0 -322
- package/src/entities/business.js +0 -369
- package/src/entities/communication.js +0 -254
- package/src/entities/customers.js +0 -988
- package/src/entities/financials.js +0 -931
- package/src/entities/goals.js +0 -799
- package/src/entities/index.js +0 -197
- package/src/entities/legal.js +0 -300
- package/src/entities/market.js +0 -300
- package/src/entities/marketing.js +0 -1156
- package/src/entities/offerings.js +0 -726
- package/src/entities/operations.js +0 -786
- package/src/entities/organization.js +0 -806
- package/src/entities/partnerships.js +0 -299
- package/src/entities/planning.js +0 -270
- package/src/entities/projects.js +0 -348
- package/src/entities/risk.js +0 -292
- package/src/entities/sales.js +0 -1247
- package/src/financials.js +0 -296
- package/src/goals.js +0 -214
- package/src/index.js +0 -131
- package/src/index.test.js +0 -274
- package/src/kpis.js +0 -231
- package/src/metrics.js +0 -324
- package/src/okrs.js +0 -268
- package/src/organization.js +0 -172
- package/src/process.js +0 -240
- package/src/product.js +0 -144
- package/src/queries.js +0 -414
- package/src/roles.js +0 -254
- package/src/service.js +0 -139
- package/src/types.js +0 -4
- package/src/vision.js +0 -67
- package/src/workflow.js +0 -246
- 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'
|
|
60
|
-
| 'notIn'
|
|
61
|
-
| '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'
|
|
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
|
|
74
|
+
field: string // Source field in database
|
|
82
75
|
type: 'string' | 'number' | 'date' | 'boolean'
|
|
83
76
|
description?: string
|
|
84
|
-
granularity?: Granularity
|
|
85
|
-
format?: string
|
|
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
|
|
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
|
|
107
|
-
measures: string[]
|
|
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
|
|
141
|
-
start?: Date | string
|
|
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
|
|
151
|
+
source: string // Table or view name
|
|
159
152
|
|
|
160
153
|
// What to select
|
|
161
|
-
dimensions?: string[]
|
|
162
|
-
measures?: string[]
|
|
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
|
|
201
|
-
retention?: string
|
|
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'
|
|
248
|
-
| 'trend'
|
|
249
|
-
| 'table'
|
|
250
|
-
| 'bar'
|
|
251
|
-
| 'line'
|
|
252
|
-
| 'area'
|
|
253
|
-
| 'pie'
|
|
254
|
-
| 'funnel'
|
|
255
|
-
| 'cohort'
|
|
256
|
-
| '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: {
|
|
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: {
|
|
274
|
-
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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: {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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: {
|
|
307
|
-
|
|
308
|
-
|
|
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: {
|
|
312
|
-
|
|
313
|
-
|
|
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: {
|
|
317
|
-
|
|
318
|
-
|
|
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(
|
|
475
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(), {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
553
|
-
type: standard
|
|
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
|
|
599
|
-
return role
|
|
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
|
|
607
|
-
return role
|
|
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
|
|
615
|
-
return role
|
|
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<
|
|
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)}`,
|