business-as-code 0.2.1 → 2.0.1

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 +9 -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,909 @@
1
+ /**
2
+ * Organization Structure - Flows to FGA/RBAC
3
+ *
4
+ * Defines the complete organizational hierarchy:
5
+ *
6
+ * Organization
7
+ * └── Department
8
+ * └── Team
9
+ * └── Position (Role + Worker)
10
+ * └── Permissions (FGA/RBAC)
11
+ *
12
+ * This structure enables:
13
+ * - Hierarchical permission inheritance
14
+ * - Role-based task assignment
15
+ * - Approval chains based on org structure
16
+ * - Resource access control based on department/team
17
+ *
18
+ * @packageDocumentation
19
+ */
20
+
21
+ import type { BusinessRole, TaskRoutingRule, WorkflowRole } from './roles.js'
22
+
23
+ // =============================================================================
24
+ // Organization Hierarchy
25
+ // =============================================================================
26
+
27
+ /**
28
+ * Organization - top-level business entity
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const acme: Organization = {
33
+ * id: 'org_acme',
34
+ * name: 'Acme Corp',
35
+ * domain: 'acme.com',
36
+ * industry: 'technology',
37
+ *
38
+ * // Hierarchy
39
+ * departments: [engineering, sales, support],
40
+ *
41
+ * // Global settings
42
+ * settings: {
43
+ * defaultCurrency: 'USD',
44
+ * timezone: 'America/Los_Angeles',
45
+ * workWeek: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
46
+ * },
47
+ *
48
+ * // Resource hierarchy for FGA
49
+ * resourceHierarchy: {
50
+ * organization: { children: ['department', 'project', 'repository'] },
51
+ * department: { parent: 'organization', children: ['team'] },
52
+ * team: { parent: 'department', children: ['position'] },
53
+ * },
54
+ * }
55
+ * ```
56
+ */
57
+ export interface Organization {
58
+ /** Unique identifier */
59
+ id: string
60
+
61
+ /** Organization name */
62
+ name: string
63
+
64
+ /** Primary domain */
65
+ domain?: string
66
+
67
+ /** Legal name (if different) */
68
+ legalName?: string
69
+
70
+ /** Industry/sector */
71
+ industry?: string
72
+
73
+ /** Mission statement */
74
+ mission?: string
75
+
76
+ /** Core values */
77
+ values?: string[]
78
+
79
+ /** Founded date */
80
+ foundedAt?: Date
81
+
82
+ /** Headquarters location */
83
+ headquarters?: Address
84
+
85
+ /** Organization settings */
86
+ settings?: OrganizationSettings
87
+
88
+ /** Departments */
89
+ departments?: Department[]
90
+
91
+ /** Standalone teams (not in departments) */
92
+ teams?: Team[]
93
+
94
+ /** Organization-wide roles */
95
+ roles?: BusinessRole[]
96
+
97
+ /** Resource hierarchy for FGA */
98
+ resourceHierarchy?: ResourceHierarchy
99
+
100
+ /** Global approval chains */
101
+ approvalChains?: ApprovalChain[]
102
+
103
+ /** Task routing rules */
104
+ routingRules?: TaskRoutingRule[]
105
+
106
+ /** Metadata */
107
+ metadata?: Record<string, unknown>
108
+ }
109
+
110
+ /**
111
+ * Organization settings
112
+ */
113
+ export interface OrganizationSettings {
114
+ /** Default currency */
115
+ defaultCurrency?: string
116
+
117
+ /** Default timezone */
118
+ timezone?: string
119
+
120
+ /** Work week days */
121
+ workWeek?: string[]
122
+
123
+ /** Business hours */
124
+ businessHours?: {
125
+ start: string
126
+ end: string
127
+ timezone?: string
128
+ }
129
+
130
+ /** Fiscal year start month (1-12) */
131
+ fiscalYearStart?: number
132
+
133
+ /** Default language */
134
+ language?: string
135
+
136
+ /** Date format */
137
+ dateFormat?: string
138
+ }
139
+
140
+ /**
141
+ * Address
142
+ */
143
+ export interface Address {
144
+ street?: string
145
+ city?: string
146
+ state?: string
147
+ postalCode?: string
148
+ country?: string
149
+ }
150
+
151
+ // =============================================================================
152
+ // Department
153
+ // =============================================================================
154
+
155
+ /**
156
+ * Department - major organizational division
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const engineering: Department = {
161
+ * id: 'dept_eng',
162
+ * name: 'Engineering',
163
+ * code: 'ENG',
164
+ *
165
+ * // Leadership
166
+ * head: { roleId: 'role_cto', positionId: 'pos_cto_jane' },
167
+ *
168
+ * // Teams
169
+ * teams: [platformTeam, productTeam, infraTeam],
170
+ *
171
+ * // Budget
172
+ * budget: {
173
+ * annual: 5000000,
174
+ * currency: 'USD',
175
+ * categories: {
176
+ * salaries: 3500000,
177
+ * tools: 200000,
178
+ * infrastructure: 800000,
179
+ * training: 100000,
180
+ * },
181
+ * },
182
+ *
183
+ * // FGA: Department-level permissions
184
+ * defaultPermissions: {
185
+ * repository: ['read'],
186
+ * project: ['read'],
187
+ * },
188
+ * }
189
+ * ```
190
+ */
191
+ export interface Department {
192
+ /** Unique identifier */
193
+ id: string
194
+
195
+ /** Department name */
196
+ name: string
197
+
198
+ /** Short code (e.g., 'ENG', 'SALES') */
199
+ code?: string
200
+
201
+ /** Description */
202
+ description?: string
203
+
204
+ /** Department head position */
205
+ head?: PositionRef
206
+
207
+ /** Parent department (for sub-departments) */
208
+ parentId?: string
209
+
210
+ /** Teams within department */
211
+ teams?: Team[]
212
+
213
+ /** Department budget */
214
+ budget?: Budget
215
+
216
+ /** Cost center code */
217
+ costCenter?: string
218
+
219
+ /** Department goals */
220
+ goals?: string[]
221
+
222
+ /** Default permissions for department members */
223
+ defaultPermissions?: Record<string, string[]>
224
+
225
+ /** Department-specific roles */
226
+ roles?: BusinessRole[]
227
+
228
+ /** Department-specific routing rules */
229
+ routingRules?: TaskRoutingRule[]
230
+
231
+ /** Metadata */
232
+ metadata?: Record<string, unknown>
233
+ }
234
+
235
+ // =============================================================================
236
+ // Team
237
+ // =============================================================================
238
+
239
+ /**
240
+ * Team - working group within a department
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * const platformTeam: Team = {
245
+ * id: 'team_platform',
246
+ * name: 'Platform Team',
247
+ * departmentId: 'dept_eng',
248
+ *
249
+ * // Leadership
250
+ * lead: { roleId: 'role_lead', positionId: 'pos_lead_bob' },
251
+ *
252
+ * // Members
253
+ * positions: [
254
+ * { id: 'pos_1', roleId: 'role_engineer', workerId: 'worker_alice' },
255
+ * { id: 'pos_2', roleId: 'role_engineer', workerId: 'worker_charlie' },
256
+ * { id: 'pos_3', roleId: 'role_engineer', workerId: null }, // Open position
257
+ * ],
258
+ *
259
+ * // Team resources (for FGA scoping)
260
+ * resources: {
261
+ * repositories: ['platform-core', 'platform-api'],
262
+ * projects: ['platform-v2'],
263
+ * },
264
+ *
265
+ * // Team-level permissions (inherited by members)
266
+ * defaultPermissions: {
267
+ * repository: ['read', 'edit', 'act:merge'],
268
+ * project: ['read', 'edit'],
269
+ * },
270
+ * }
271
+ * ```
272
+ */
273
+ export interface Team {
274
+ /** Unique identifier */
275
+ id: string
276
+
277
+ /** Team name */
278
+ name: string
279
+
280
+ /** Parent department */
281
+ departmentId?: string
282
+
283
+ /** Description */
284
+ description?: string
285
+
286
+ /** Team lead position */
287
+ lead?: PositionRef
288
+
289
+ /** Team positions */
290
+ positions?: Position[]
291
+
292
+ /** Team objectives */
293
+ objectives?: string[]
294
+
295
+ /** Team resources (for FGA scoping) */
296
+ resources?: TeamResources
297
+
298
+ /** Team budget (if separate from department) */
299
+ budget?: Budget
300
+
301
+ /** Default permissions for team members */
302
+ defaultPermissions?: Record<string, string[]>
303
+
304
+ /** Workflow roles for this team */
305
+ workflowRoles?: WorkflowRole[]
306
+
307
+ /** Communication channels */
308
+ channels?: TeamChannels
309
+
310
+ /** Metadata */
311
+ metadata?: Record<string, unknown>
312
+ }
313
+
314
+ /**
315
+ * Team resources - scopes FGA permissions
316
+ */
317
+ export interface TeamResources {
318
+ /** Owned repositories */
319
+ repositories?: string[]
320
+
321
+ /** Owned projects */
322
+ projects?: string[]
323
+
324
+ /** Owned products */
325
+ products?: string[]
326
+
327
+ /** Custom resource types */
328
+ [resourceType: string]: string[] | undefined
329
+ }
330
+
331
+ /**
332
+ * Team communication channels
333
+ */
334
+ export interface TeamChannels {
335
+ slack?: string
336
+ teams?: string
337
+ email?: string
338
+ discord?: string
339
+ }
340
+
341
+ // =============================================================================
342
+ // Position - Role + Worker Assignment
343
+ // =============================================================================
344
+
345
+ /**
346
+ * Position - a role filled by a worker
347
+ *
348
+ * The position is the link between:
349
+ * - Business Role (responsibilities, permissions)
350
+ * - Worker (human or AI agent)
351
+ * - Location in org hierarchy (team → department → org)
352
+ *
353
+ * @example
354
+ * ```ts
355
+ * const seniorEngineerPosition: Position = {
356
+ * id: 'pos_se_123',
357
+ * title: 'Senior Software Engineer',
358
+ *
359
+ * // Role defines what this position can do
360
+ * roleId: 'role_senior_engineer',
361
+ *
362
+ * // Worker currently filling this position
363
+ * workerId: 'worker_alice',
364
+ * workerType: 'human',
365
+ *
366
+ * // Hierarchy
367
+ * teamId: 'team_platform',
368
+ * reportsTo: 'pos_lead_bob',
369
+ *
370
+ * // Position-specific permissions (added to role permissions)
371
+ * additionalPermissions: {
372
+ * 'repository:platform-core': ['manage'],
373
+ * },
374
+ *
375
+ * // Employment details
376
+ * startDate: new Date('2023-01-15'),
377
+ * status: 'active',
378
+ * }
379
+ * ```
380
+ */
381
+ export interface Position {
382
+ /** Unique identifier */
383
+ id: string
384
+
385
+ /** Position title */
386
+ title: string
387
+
388
+ /** Business role this position requires */
389
+ roleId: string
390
+
391
+ /** Worker filling this position (null = open position) */
392
+ workerId?: string | null
393
+
394
+ /** Type of worker (human, agent, or either) */
395
+ workerType?: 'human' | 'agent' | 'any'
396
+
397
+ /** Team this position belongs to */
398
+ teamId?: string
399
+
400
+ /** Position this reports to */
401
+ reportsTo?: string
402
+
403
+ /** Direct reports (position IDs) */
404
+ directReports?: string[]
405
+
406
+ /** Position-specific additional permissions */
407
+ additionalPermissions?: Record<string, string[]>
408
+
409
+ /** Start date */
410
+ startDate?: Date
411
+
412
+ /** End date (for contractors/temporary) */
413
+ endDate?: Date
414
+
415
+ /** Position status */
416
+ status?: 'active' | 'open' | 'on-leave' | 'terminated'
417
+
418
+ /** Full-time equivalent (1.0 = full-time) */
419
+ fte?: number
420
+
421
+ /** Location */
422
+ location?: string
423
+
424
+ /** Remote/hybrid/onsite */
425
+ workModel?: 'remote' | 'hybrid' | 'onsite'
426
+
427
+ /** Compensation */
428
+ compensation?: Compensation
429
+
430
+ /** Metadata */
431
+ metadata?: Record<string, unknown>
432
+ }
433
+
434
+ /**
435
+ * Position reference
436
+ */
437
+ export interface PositionRef {
438
+ positionId?: string
439
+ roleId?: string
440
+ workerId?: string
441
+ }
442
+
443
+ /**
444
+ * Compensation details
445
+ */
446
+ export interface Compensation {
447
+ /** Base salary */
448
+ baseSalary?: number
449
+
450
+ /** Currency */
451
+ currency?: string
452
+
453
+ /** Pay frequency */
454
+ frequency?: 'hourly' | 'weekly' | 'biweekly' | 'monthly' | 'annual'
455
+
456
+ /** Bonus target percentage */
457
+ bonusTarget?: number
458
+
459
+ /** Equity grants */
460
+ equity?: {
461
+ type: 'options' | 'rsu' | 'shares'
462
+ amount: number
463
+ vestingSchedule?: string
464
+ }
465
+
466
+ /** Band/level */
467
+ band?: string
468
+ }
469
+
470
+ // =============================================================================
471
+ // Budget
472
+ // =============================================================================
473
+
474
+ /**
475
+ * Budget allocation
476
+ */
477
+ export interface Budget {
478
+ /** Total annual budget */
479
+ annual?: number
480
+
481
+ /** Currency */
482
+ currency?: string
483
+
484
+ /** Budget period */
485
+ period?: string
486
+
487
+ /** Budget categories */
488
+ categories?: Record<string, number>
489
+
490
+ /** Spent to date */
491
+ spent?: number
492
+
493
+ /** Remaining */
494
+ remaining?: number
495
+ }
496
+
497
+ // =============================================================================
498
+ // Resource Hierarchy - FGA Integration
499
+ // =============================================================================
500
+
501
+ /**
502
+ * Resource hierarchy definition for FGA
503
+ *
504
+ * Defines how resources relate to each other for permission inheritance.
505
+ *
506
+ * @example
507
+ * ```ts
508
+ * const hierarchy: ResourceHierarchy = {
509
+ * // Organization is root
510
+ * organization: {
511
+ * children: ['department', 'project', 'repository'],
512
+ * },
513
+ *
514
+ * // Department inherits from org
515
+ * department: {
516
+ * parent: 'organization',
517
+ * children: ['team'],
518
+ * },
519
+ *
520
+ * // Team inherits from department
521
+ * team: {
522
+ * parent: 'department',
523
+ * children: ['position'],
524
+ * },
525
+ *
526
+ * // Project can be org-level or team-level
527
+ * project: {
528
+ * parent: 'organization',
529
+ * alternateParents: ['team'],
530
+ * children: ['document', 'task'],
531
+ * },
532
+ *
533
+ * // Repository can be org-level or team-level
534
+ * repository: {
535
+ * parent: 'organization',
536
+ * alternateParents: ['team'],
537
+ * },
538
+ * }
539
+ * ```
540
+ */
541
+ export interface ResourceHierarchy {
542
+ [resourceType: string]: ResourceHierarchyNode
543
+ }
544
+
545
+ /**
546
+ * Node in resource hierarchy
547
+ */
548
+ export interface ResourceHierarchyNode {
549
+ /** Primary parent resource type */
550
+ parent?: string
551
+
552
+ /** Alternative parent types */
553
+ alternateParents?: string[]
554
+
555
+ /** Child resource types */
556
+ children?: string[]
557
+
558
+ /** Whether permissions cascade down */
559
+ inheritPermissions?: boolean
560
+
561
+ /** Maximum depth (for nested resources) */
562
+ maxDepth?: number
563
+ }
564
+
565
+ // =============================================================================
566
+ // Approval Chains
567
+ // =============================================================================
568
+
569
+ /**
570
+ * Approval chain - defines who approves what
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * const expenseApprovalChain: ApprovalChain = {
575
+ * id: 'chain_expense',
576
+ * name: 'Expense Approval',
577
+ * type: 'expense',
578
+ *
579
+ * levels: [
580
+ * {
581
+ * threshold: 1000,
582
+ * approvers: [{ type: 'direct-manager' }],
583
+ * },
584
+ * {
585
+ * threshold: 5000,
586
+ * approvers: [
587
+ * { type: 'direct-manager' },
588
+ * { type: 'role', roleId: 'director' },
589
+ * ],
590
+ * },
591
+ * {
592
+ * threshold: 25000,
593
+ * approvers: [
594
+ * { type: 'direct-manager' },
595
+ * { type: 'role', roleId: 'vp' },
596
+ * { type: 'role', roleId: 'cfo' },
597
+ * ],
598
+ * },
599
+ * ],
600
+ *
601
+ * // Escalation settings
602
+ * escalation: {
603
+ * afterHours: 24,
604
+ * escalateTo: 'skip-level-manager',
605
+ * },
606
+ * }
607
+ * ```
608
+ */
609
+ export interface ApprovalChain {
610
+ /** Chain identifier */
611
+ id: string
612
+
613
+ /** Chain name */
614
+ name: string
615
+
616
+ /** What type of requests this chain handles */
617
+ type: string
618
+
619
+ /** Approval levels */
620
+ levels: ApprovalLevel[]
621
+
622
+ /** Escalation rules */
623
+ escalation?: EscalationRule
624
+
625
+ /** Active/inactive */
626
+ active?: boolean
627
+
628
+ /** Metadata */
629
+ metadata?: Record<string, unknown>
630
+ }
631
+
632
+ /**
633
+ * Approval level within a chain
634
+ */
635
+ export interface ApprovalLevel {
636
+ /** Threshold amount (requests above this need this level) */
637
+ threshold?: number
638
+
639
+ /** Approvers at this level */
640
+ approvers: ApproverSpec[]
641
+
642
+ /** How many approvers needed */
643
+ requiredApprovals?: number
644
+
645
+ /** Whether approvals at this level are sequential or parallel */
646
+ approvalMode?: 'sequential' | 'parallel' | 'any'
647
+
648
+ /** SLA for this level (hours) */
649
+ slaHours?: number
650
+ }
651
+
652
+ /**
653
+ * Approver specification
654
+ */
655
+ export interface ApproverSpec {
656
+ /** Approver type */
657
+ type: 'direct-manager' | 'skip-level-manager' | 'role' | 'position' | 'worker' | 'team'
658
+
659
+ /** Role ID (if type is 'role') */
660
+ roleId?: string
661
+
662
+ /** Position ID (if type is 'position') */
663
+ positionId?: string
664
+
665
+ /** Worker ID (if type is 'worker') */
666
+ workerId?: string
667
+
668
+ /** Team ID (if type is 'team') */
669
+ teamId?: string
670
+ }
671
+
672
+ /**
673
+ * Escalation rule
674
+ */
675
+ export interface EscalationRule {
676
+ /** Hours before escalation */
677
+ afterHours: number
678
+
679
+ /** Who to escalate to */
680
+ escalateTo: 'skip-level-manager' | 'department-head' | 'role' | 'position'
681
+
682
+ /** Role ID if escalating to role */
683
+ roleId?: string
684
+
685
+ /** Position ID if escalating to position */
686
+ positionId?: string
687
+
688
+ /** Maximum escalations */
689
+ maxEscalations?: number
690
+ }
691
+
692
+ // =============================================================================
693
+ // Permission Resolution
694
+ // =============================================================================
695
+
696
+ /**
697
+ * Resolved permissions for a worker
698
+ *
699
+ * Combines:
700
+ * - Organization-level defaults
701
+ * - Department-level permissions
702
+ * - Team-level permissions
703
+ * - Role permissions
704
+ * - Position-specific permissions
705
+ */
706
+ export interface ResolvedPermissions {
707
+ /** Worker ID */
708
+ workerId: string
709
+
710
+ /** Position ID */
711
+ positionId: string
712
+
713
+ /** Effective permissions by resource type */
714
+ permissions: Record<string, string[]>
715
+
716
+ /** Resource-specific permissions */
717
+ resourcePermissions: Record<string, Record<string, string[]>>
718
+
719
+ /** Approval capabilities */
720
+ canApprove: string[]
721
+
722
+ /** Task handling capabilities */
723
+ canHandle: string[]
724
+
725
+ /** Inheritance chain (for debugging) */
726
+ inheritanceChain: string[]
727
+ }
728
+
729
+ /**
730
+ * Resolve permissions for a position in the org hierarchy
731
+ */
732
+ export function resolvePermissions(
733
+ org: Organization,
734
+ positionId: string
735
+ ): ResolvedPermissions | null {
736
+ // Find the position
737
+ let position: Position | undefined
738
+ let team: Team | undefined
739
+ let department: Department | undefined
740
+
741
+ // Search through hierarchy
742
+ for (const dept of org.departments || []) {
743
+ for (const t of dept.teams || []) {
744
+ const pos = t.positions?.find(p => p.id === positionId)
745
+ if (pos) {
746
+ position = pos
747
+ team = t
748
+ department = dept
749
+ break
750
+ }
751
+ }
752
+ if (position) break
753
+ }
754
+
755
+ // Also check standalone teams
756
+ if (!position) {
757
+ for (const t of org.teams || []) {
758
+ const pos = t.positions?.find(p => p.id === positionId)
759
+ if (pos) {
760
+ position = pos
761
+ team = t
762
+ break
763
+ }
764
+ }
765
+ }
766
+
767
+ if (!position) return null
768
+
769
+ // Find the role
770
+ const role = org.roles?.find(r => r.id === position.roleId)
771
+
772
+ // Build inheritance chain
773
+ const inheritanceChain: string[] = []
774
+ const permissions: Record<string, string[]> = {}
775
+ const resourcePermissions: Record<string, Record<string, string[]>> = {}
776
+ const canApprove: string[] = []
777
+ const canHandle: string[] = []
778
+
779
+ // 1. Department defaults
780
+ if (department?.defaultPermissions) {
781
+ inheritanceChain.push(`department:${department.id}`)
782
+ mergePermissions(permissions, department.defaultPermissions)
783
+ }
784
+
785
+ // 2. Team defaults
786
+ if (team?.defaultPermissions) {
787
+ inheritanceChain.push(`team:${team.id}`)
788
+ mergePermissions(permissions, team.defaultPermissions)
789
+ }
790
+
791
+ // 3. Team resources (scoped permissions)
792
+ if (team?.resources) {
793
+ for (const [resourceType, resourceIds] of Object.entries(team.resources)) {
794
+ if (resourceIds) {
795
+ for (const resourceId of resourceIds) {
796
+ const key = `${resourceType}:${resourceId}`
797
+ resourcePermissions[key] = resourcePermissions[key] || {}
798
+ mergePermissions(resourcePermissions[key], team.defaultPermissions || {})
799
+ }
800
+ }
801
+ }
802
+ }
803
+
804
+ // 4. Role permissions
805
+ if (role?.permissions) {
806
+ inheritanceChain.push(`role:${role.id}`)
807
+ mergePermissions(permissions, role.permissions)
808
+ }
809
+
810
+ // 5. Role capabilities
811
+ if (role?.canApprove) {
812
+ canApprove.push(...role.canApprove)
813
+ }
814
+ if (role?.canHandle) {
815
+ canHandle.push(...role.canHandle)
816
+ }
817
+
818
+ // 6. Position-specific permissions
819
+ if (position.additionalPermissions) {
820
+ inheritanceChain.push(`position:${position.id}`)
821
+ mergePermissions(permissions, position.additionalPermissions)
822
+
823
+ // Handle resource-specific permissions
824
+ for (const [key, perms] of Object.entries(position.additionalPermissions)) {
825
+ if (key.includes(':')) {
826
+ resourcePermissions[key] = resourcePermissions[key] || {}
827
+ resourcePermissions[key] = { ...resourcePermissions[key], _direct: perms }
828
+ }
829
+ }
830
+ }
831
+
832
+ return {
833
+ workerId: position.workerId || '',
834
+ positionId: position.id,
835
+ permissions,
836
+ resourcePermissions,
837
+ canApprove: [...new Set(canApprove)],
838
+ canHandle: [...new Set(canHandle)],
839
+ inheritanceChain,
840
+ }
841
+ }
842
+
843
+ /**
844
+ * Merge permissions into target
845
+ */
846
+ function mergePermissions(
847
+ target: Record<string, string[]>,
848
+ source: Record<string, string[]>
849
+ ): void {
850
+ for (const [key, perms] of Object.entries(source)) {
851
+ if (!target[key]) {
852
+ target[key] = []
853
+ }
854
+ for (const perm of perms) {
855
+ if (!target[key].includes(perm)) {
856
+ target[key].push(perm)
857
+ }
858
+ }
859
+ }
860
+ }
861
+
862
+ /**
863
+ * Get approval chain for a request
864
+ */
865
+ export function getApprovalChainForRequest(
866
+ org: Organization,
867
+ requestType: string,
868
+ amount?: number
869
+ ): ApproverSpec[] {
870
+ const chain = org.approvalChains?.find(c => c.type === requestType && c.active !== false)
871
+ if (!chain) return []
872
+
873
+ // Find the appropriate level based on amount
874
+ const levels = [...chain.levels].sort((a, b) => (a.threshold || 0) - (b.threshold || 0))
875
+
876
+ for (const level of levels.reverse()) {
877
+ if (amount === undefined || (level.threshold && amount <= level.threshold)) {
878
+ return level.approvers
879
+ }
880
+ }
881
+
882
+ // Return highest level if amount exceeds all thresholds
883
+ return levels[levels.length - 1]?.approvers || []
884
+ }
885
+
886
+ /**
887
+ * Find manager for a position (follows reportsTo chain)
888
+ */
889
+ export function findManager(
890
+ org: Organization,
891
+ positionId: string
892
+ ): Position | null {
893
+ // Find the position
894
+ for (const dept of org.departments || []) {
895
+ for (const team of dept.teams || []) {
896
+ const position = team.positions?.find(p => p.id === positionId)
897
+ if (position?.reportsTo) {
898
+ // Find the manager position
899
+ for (const d of org.departments || []) {
900
+ for (const t of d.teams || []) {
901
+ const manager = t.positions?.find(p => p.id === position.reportsTo)
902
+ if (manager) return manager
903
+ }
904
+ }
905
+ }
906
+ }
907
+ }
908
+ return null
909
+ }