@dotbep/core 0.2.7 → 0.2.8

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 (77) hide show
  1. package/dist/index.d.ts +7 -5
  2. package/dist/index.js +604 -596
  3. package/package.json +4 -1
  4. package/examples/01-participants.ts +0 -127
  5. package/examples/02-files.ts +0 -100
  6. package/examples/03-workflows.ts +0 -149
  7. package/examples/04-bim-uses.ts +0 -70
  8. package/examples/05-standards.ts +0 -60
  9. package/examples/06-schedule.ts +0 -124
  10. package/examples/07-loin.ts +0 -133
  11. package/examples/08-deliverables.ts +0 -126
  12. package/examples/09-notes.ts +0 -73
  13. package/examples/10-llm.ts +0 -109
  14. package/examples/11-resolved.ts +0 -133
  15. package/examples/12-history.ts +0 -166
  16. package/examples/13-engine.ts +0 -152
  17. package/examples/bep.d.ts +0 -38
  18. package/examples/example.bep +0 -0
  19. package/examples/run-all.ts +0 -38
  20. package/src/base/entity.ts +0 -148
  21. package/src/base/history.ts +0 -497
  22. package/src/base/index.ts +0 -5
  23. package/src/base/singleton.ts +0 -26
  24. package/src/entities/actions.ts +0 -25
  25. package/src/entities/adapters.ts +0 -16
  26. package/src/entities/annexes.ts +0 -17
  27. package/src/entities/assetTypes.ts +0 -30
  28. package/src/entities/automations.ts +0 -24
  29. package/src/entities/bimUses.ts +0 -50
  30. package/src/entities/deliverables.ts +0 -66
  31. package/src/entities/disciplines.ts +0 -21
  32. package/src/entities/effects.ts +0 -28
  33. package/src/entities/env.ts +0 -17
  34. package/src/entities/events.ts +0 -24
  35. package/src/entities/extensions.ts +0 -16
  36. package/src/entities/flags.ts +0 -17
  37. package/src/entities/guides.ts +0 -26
  38. package/src/entities/index.ts +0 -32
  39. package/src/entities/lbsNodes.ts +0 -193
  40. package/src/entities/lods.ts +0 -22
  41. package/src/entities/loin.ts +0 -127
  42. package/src/entities/lois.ts +0 -22
  43. package/src/entities/members.ts +0 -137
  44. package/src/entities/milestones.ts +0 -32
  45. package/src/entities/notes.ts +0 -27
  46. package/src/entities/objectives.ts +0 -17
  47. package/src/entities/phases.ts +0 -17
  48. package/src/entities/remoteData.ts +0 -17
  49. package/src/entities/resolvers.ts +0 -20
  50. package/src/entities/roles.ts +0 -29
  51. package/src/entities/softwares.ts +0 -26
  52. package/src/entities/standards.ts +0 -68
  53. package/src/entities/teams.ts +0 -42
  54. package/src/entities/workflows.ts +0 -256
  55. package/src/index.ts +0 -464
  56. package/src/runtime/Engine.ts +0 -352
  57. package/src/runtime/MemoryStorage.ts +0 -31
  58. package/src/runtime/Runtime.ts +0 -106
  59. package/src/runtime/index.ts +0 -4
  60. package/src/runtime/transitions.ts +0 -456
  61. package/src/runtime/types.ts +0 -279
  62. package/src/types/history.ts +0 -37
  63. package/src/types/index.ts +0 -24
  64. package/src/types/resolved.ts +0 -137
  65. package/src/types/schema.ts +0 -757
  66. package/src/utils/diff.ts +0 -109
  67. package/src/utils/index.ts +0 -9
  68. package/src/utils/integrity.ts +0 -108
  69. package/src/utils/lbs.ts +0 -116
  70. package/src/utils/mermaid.ts +0 -110
  71. package/src/utils/naming.ts +0 -62
  72. package/src/utils/nomenclature.ts +0 -107
  73. package/src/utils/normalize.ts +0 -35
  74. package/src/utils/raci.ts +0 -25
  75. package/src/utils/textFile.ts +0 -24
  76. package/tsconfig.json +0 -12
  77. package/vite.config.ts +0 -24
@@ -1,256 +0,0 @@
1
- import type { BEP, FlowDiagram, Role, Workflow } from '../types/schema.js'
2
- import { Entity } from '../base/entity.js'
3
- import { WorkflowSchema } from '../types/schema.js'
4
- import type { FlowNodeResolved, MemberResolved, RaciAssignment, RaciMatrix, RaciRow, TeamResolved, WorkflowResolved } from '../types/resolved.js'
5
- import type { Members } from './members.js'
6
- import type { Teams } from './teams.js'
7
-
8
- function validateDiagram(diagram: FlowDiagram, bep: BEP, workflowId: string): string[] {
9
- const errors: string[] = []
10
-
11
- // ── Node reference checks ──
12
- for (const [nodeKey, node] of Object.entries(diagram.nodes)) {
13
- if (node.type !== 'process') continue
14
- if (node.actionId && !bep.actions.some(a => a.id === node.actionId))
15
- errors.push(`actions["${node.actionId}"] not found (node: ${nodeKey})`)
16
- for (const field of ['responsibleRoleIds', 'accountableRoleIds', 'consultedRoleIds', 'informedRoleIds'] as const) {
17
- for (const roleId of node[field] ?? []) {
18
- if (!bep.roles.some(r => r.id === roleId))
19
- errors.push(`roles["${roleId}"] not found (node: ${nodeKey}.${field})`)
20
- }
21
- }
22
- }
23
-
24
- // ── Edge node reference checks ──
25
- for (const [edgeKey, edge] of Object.entries(diagram.edges)) {
26
- if (!diagram.nodes[edge.from])
27
- errors.push(`edge "${edgeKey}": from node "${edge.from}" not found`)
28
- if (!diagram.nodes[edge.to])
29
- errors.push(`edge "${edgeKey}": to node "${edge.to}" not found`)
30
- if ('triggerEventId' in edge && !bep.events.some(e => e.id === edge.triggerEventId))
31
- errors.push(`events["${edge.triggerEventId}"] not found (edge: ${edgeKey})`)
32
- for (const effectId of edge.effectIds ?? []) {
33
- if (!bep.effects.some(e => e.id === effectId))
34
- errors.push(`effects["${effectId}"] not found (edge: ${edgeKey})`)
35
- }
36
- }
37
-
38
- // ── Node catalog reference checks ──
39
- for (const [nodeKey, node] of Object.entries(diagram.nodes)) {
40
- if (node.type === 'automation' && !bep.automations.some(a => a.id === node.automationId))
41
- errors.push(`automations["${node.automationId}"] not found (node: ${nodeKey})`)
42
-
43
- if (node.type === 'process' && node.workflowId) {
44
- if (node.workflowId === workflowId)
45
- errors.push(`node "${nodeKey}" references its own workflow — would cause infinite recursion`)
46
- else if (!bep.workflows.some(w => w.id === node.workflowId))
47
- errors.push(`workflows["${node.workflowId}"] not found (node: ${nodeKey})`)
48
- }
49
-
50
- if ((node.type === 'process' || node.type === 'automation') && node.timeouts) {
51
- for (const timeout of node.timeouts) {
52
- if (!bep.effects.some(e => e.id === timeout.effectId))
53
- errors.push(`effects["${timeout.effectId}"] not found (node: ${nodeKey}.timeouts)`)
54
- }
55
- }
56
- }
57
-
58
- // ── Reachability checks ──
59
- const nodeKeys = Object.keys(diagram.nodes)
60
- const outgoingTargets = new Set(Object.values(diagram.edges).map(e => e.to))
61
- const outgoingSources = new Set(Object.values(diagram.edges).map(e => e.from))
62
-
63
- for (const nodeKey of nodeKeys) {
64
- const node = diagram.nodes[nodeKey]
65
- if (node.type === 'start') continue
66
- if (!outgoingTargets.has(nodeKey))
67
- errors.push(`node "${nodeKey}" is unreachable — no edges point to it`)
68
- }
69
-
70
- for (const nodeKey of nodeKeys) {
71
- const node = diagram.nodes[nodeKey]
72
- if (node.type === 'end') continue
73
- if (!outgoingSources.has(nodeKey))
74
- errors.push(`node "${nodeKey}" has no outgoing edges — workflow would get stuck here`)
75
- }
76
-
77
- // ── Guard field validation against predecessor outputs ──
78
- // For each decision node, collect the context fields its direct predecessors
79
- // produce (automation output or event payload), then verify every guard field
80
- // is declared among them. Skips validation when no field sources are known
81
- // (e.g. predecessor event has no declared payload), since context is cumulative
82
- // and earlier steps may have set the field.
83
- const incomingEdgeKeys: Record<string, string[]> = {}
84
- for (const edgeKey of Object.keys(diagram.edges)) {
85
- const edge = diagram.edges[edgeKey]
86
- incomingEdgeKeys[edge.to] ??= []
87
- incomingEdgeKeys[edge.to].push(edgeKey)
88
- }
89
-
90
- for (const [nodeKey, node] of Object.entries(diagram.nodes)) {
91
- if (node.type !== 'decision') continue
92
-
93
- const availableFields = new Set<string>()
94
- for (const inEdgeKey of incomingEdgeKeys[nodeKey] ?? []) {
95
- const inEdge = diagram.edges[inEdgeKey]
96
- const fromNode = diagram.nodes[inEdge.from]
97
- if (!fromNode) continue
98
-
99
- if (fromNode.type === 'automation') {
100
- const automation = bep.automations.find(a => a.id === fromNode.automationId)
101
- for (const f of automation?.output ?? []) availableFields.add(f.key)
102
- }
103
-
104
- if (fromNode.type === 'process' && 'triggerEventId' in inEdge) {
105
- const event = bep.events.find(e => e.id === inEdge.triggerEventId)
106
- for (const f of event?.payload ?? []) availableFields.add(f.key)
107
- }
108
- }
109
-
110
- if (availableFields.size === 0) continue
111
-
112
- for (const [outEdgeKey, outEdge] of Object.entries(diagram.edges)) {
113
- if (outEdge.from !== nodeKey) continue
114
- if (!('guard' in outEdge)) continue
115
- if (!availableFields.has(outEdge.guard.field))
116
- errors.push(`guard field "${outEdge.guard.field}" on edge "${outEdgeKey}" is not declared in any direct predecessor's output or payload (node: ${nodeKey})`)
117
- }
118
- }
119
-
120
- return errors
121
- }
122
-
123
- export class Workflows extends Entity<Workflow, true> {
124
- constructor(getBep: () => BEP, private readonly getMembers: () => Members, private readonly getTeams: () => Teams) {
125
- super(
126
- () => getBep().workflows,
127
- getBep,
128
- {
129
- key: 'workflows',
130
- schema: WorkflowSchema,
131
- autoId: true,
132
- validate: (item, bep) => validateDiagram(item.diagram, bep, item.id),
133
- },
134
- )
135
- }
136
-
137
- getRaciMatrix(): RaciMatrix {
138
- const bep = this.getBep()
139
- const rows: RaciRow[] = []
140
- const roleIds = new Set<string>()
141
- const resolvedMembers = this.getMembers().listResolved()
142
- const resolvedTeams = this.getTeams().listResolved()
143
-
144
- const resolveAssignments = (ids: Role['id'][] | undefined): RaciAssignment[] =>
145
- (ids ?? []).flatMap(roleId => {
146
- const role = bep.roles.find(r => r.id === roleId)
147
- if (!role) return []
148
- roleIds.add(roleId)
149
- const members = resolvedMembers.filter(m => m.role?.id === roleId) as MemberResolved[]
150
- const team = resolvedTeams.find(t => t.members.some(m => m.role?.id === roleId)) ?? null as TeamResolved | null
151
- return [{ role, members, team } satisfies RaciAssignment]
152
- })
153
-
154
- for (const workflow of bep.workflows) {
155
- for (const [nodeId, node] of Object.entries(workflow.diagram.nodes)) {
156
- if (node.type !== 'process') continue
157
- const action = node.actionId ? bep.actions.find(a => a.id === node.actionId) : undefined
158
- rows.push({
159
- workflow: { id: workflow.id, name: workflow.name },
160
- nodeId,
161
- label: action?.name ?? nodeId,
162
- ...(action?.description ? { description: action.description } : {}),
163
- ...(node.actionId ? { actionId: node.actionId } : {}),
164
- responsible: resolveAssignments(node.responsibleRoleIds),
165
- accountable: resolveAssignments(node.accountableRoleIds),
166
- consulted: resolveAssignments(node.consultedRoleIds),
167
- informed: resolveAssignments(node.informedRoleIds),
168
- } satisfies RaciRow)
169
- }
170
- }
171
-
172
- const roles = [...roleIds]
173
- .map(id => bep.roles.find(r => r.id === id))
174
- .filter(Boolean) as Role[]
175
-
176
- return { roles, rows }
177
- }
178
-
179
- getRaciMatrixForWorkflow(workflowId: Workflow['id']): RaciMatrix {
180
- const full = this.getRaciMatrix()
181
- const rows = full.rows.filter(r => r.workflow.id === workflowId)
182
- const usedRoleIds = new Set(
183
- rows.flatMap(r => [...r.responsible, ...r.accountable, ...r.consulted, ...r.informed].map(a => a.role.id))
184
- )
185
- return { roles: full.roles.filter(r => usedRoleIds.has(r.id)), rows }
186
- }
187
-
188
- getConsolidatedRaciMatrix(): RaciMatrix {
189
- const full = this.getRaciMatrix()
190
- const merged = new Map<string, RaciRow>()
191
-
192
- const mergeAssignments = (existing: RaciAssignment[], incoming: RaciAssignment[]): RaciAssignment[] => {
193
- const map = new Map(existing.map(a => [a.role.id, { ...a, members: [...a.members] }]))
194
- for (const item of incoming) {
195
- if (!map.has(item.role.id)) {
196
- map.set(item.role.id, { ...item, members: [...item.members] })
197
- } else {
198
- const entry = map.get(item.role.id)!
199
- const seen = new Set(entry.members.map(m => m.email))
200
- for (const m of item.members) if (!seen.has(m.email)) entry.members.push(m)
201
- }
202
- }
203
- return [...map.values()]
204
- }
205
-
206
- for (const row of full.rows) {
207
- const key = row.actionId ?? `node:${row.workflow.id}:${row.nodeId}`
208
- if (!merged.has(key)) {
209
- merged.set(key, { ...row, workflow: { id: '', name: '' } })
210
- } else {
211
- const existing = merged.get(key)!
212
- existing.responsible = mergeAssignments(existing.responsible, row.responsible)
213
- existing.accountable = mergeAssignments(existing.accountable, row.accountable)
214
- existing.consulted = mergeAssignments(existing.consulted, row.consulted)
215
- existing.informed = mergeAssignments(existing.informed, row.informed)
216
- }
217
- }
218
-
219
- const rows = [...merged.values()]
220
- const usedRoleIds = new Set(
221
- rows.flatMap(r => [...r.responsible, ...r.accountable, ...r.consulted, ...r.informed].map(a => a.role.id))
222
- )
223
- return { roles: full.roles.filter(r => usedRoleIds.has(r.id)), rows }
224
- }
225
-
226
- listResolved(): WorkflowResolved[] {
227
- const bep = this.getBep()
228
- return bep.workflows.map(w => {
229
- const resolvedNodes: Record<string, FlowNodeResolved> = {}
230
- for (const [key, node] of Object.entries(w.diagram.nodes)) {
231
- const resolveRaciEntry = (roleIds?: string[]) => ({
232
- roles: (roleIds ?? []).map(id => bep.roles.find(r => r.id === id)).filter(Boolean) as Role[],
233
- teams: [] as import('../types/schema.js').Team[],
234
- members: [] as import('../types/schema.js').Member[],
235
- })
236
- resolvedNodes[key] = {
237
- type: node.type,
238
- label: node.type === 'decision' ? node.label : undefined,
239
- timeouts: (node.type === 'process' || node.type === 'automation') ? node.timeouts : undefined,
240
- workflowId: node.type === 'process' ? node.workflowId : undefined,
241
- blocking: node.type === 'process' ? node.blocking : undefined,
242
- action: node.type === 'process' && node.actionId ? bep.actions.find(a => a.id === node.actionId) ?? null : null,
243
- automation: node.type === 'automation' ? bep.automations.find(s => s.id === node.automationId) ?? null : null,
244
- responsible: resolveRaciEntry(node.type === 'process' ? node.responsibleRoleIds : undefined),
245
- accountable: resolveRaciEntry(node.type === 'process' ? node.accountableRoleIds : undefined),
246
- consulted: resolveRaciEntry(node.type === 'process' ? node.consultedRoleIds : undefined),
247
- informed: resolveRaciEntry(node.type === 'process' ? node.informedRoleIds : undefined),
248
- }
249
- }
250
- return {
251
- ...w,
252
- diagram: { ...w.diagram, nodes: resolvedNodes },
253
- }
254
- })
255
- }
256
- }