@dotbep/core 0.2.7 → 0.2.9
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/dist/index.d.ts +7 -40
- package/dist/index.js +1142 -1179
- package/package.json +4 -1
- package/examples/01-participants.ts +0 -127
- package/examples/02-files.ts +0 -100
- package/examples/03-workflows.ts +0 -149
- package/examples/04-bim-uses.ts +0 -70
- package/examples/05-standards.ts +0 -60
- package/examples/06-schedule.ts +0 -124
- package/examples/07-loin.ts +0 -133
- package/examples/08-deliverables.ts +0 -126
- package/examples/09-notes.ts +0 -73
- package/examples/10-llm.ts +0 -109
- package/examples/11-resolved.ts +0 -133
- package/examples/12-history.ts +0 -166
- package/examples/13-engine.ts +0 -152
- package/examples/bep.d.ts +0 -38
- package/examples/example.bep +0 -0
- package/examples/run-all.ts +0 -38
- package/src/base/entity.ts +0 -148
- package/src/base/history.ts +0 -497
- package/src/base/index.ts +0 -5
- package/src/base/singleton.ts +0 -26
- package/src/entities/actions.ts +0 -25
- package/src/entities/adapters.ts +0 -16
- package/src/entities/annexes.ts +0 -17
- package/src/entities/assetTypes.ts +0 -30
- package/src/entities/automations.ts +0 -24
- package/src/entities/bimUses.ts +0 -50
- package/src/entities/deliverables.ts +0 -66
- package/src/entities/disciplines.ts +0 -21
- package/src/entities/effects.ts +0 -28
- package/src/entities/env.ts +0 -17
- package/src/entities/events.ts +0 -24
- package/src/entities/extensions.ts +0 -16
- package/src/entities/flags.ts +0 -17
- package/src/entities/guides.ts +0 -26
- package/src/entities/index.ts +0 -32
- package/src/entities/lbsNodes.ts +0 -193
- package/src/entities/lods.ts +0 -22
- package/src/entities/loin.ts +0 -127
- package/src/entities/lois.ts +0 -22
- package/src/entities/members.ts +0 -137
- package/src/entities/milestones.ts +0 -32
- package/src/entities/notes.ts +0 -27
- package/src/entities/objectives.ts +0 -17
- package/src/entities/phases.ts +0 -17
- package/src/entities/remoteData.ts +0 -17
- package/src/entities/resolvers.ts +0 -20
- package/src/entities/roles.ts +0 -29
- package/src/entities/softwares.ts +0 -26
- package/src/entities/standards.ts +0 -68
- package/src/entities/teams.ts +0 -42
- package/src/entities/workflows.ts +0 -256
- package/src/index.ts +0 -464
- package/src/runtime/Engine.ts +0 -352
- package/src/runtime/MemoryStorage.ts +0 -31
- package/src/runtime/Runtime.ts +0 -106
- package/src/runtime/index.ts +0 -4
- package/src/runtime/transitions.ts +0 -456
- package/src/runtime/types.ts +0 -279
- package/src/types/history.ts +0 -37
- package/src/types/index.ts +0 -24
- package/src/types/resolved.ts +0 -137
- package/src/types/schema.ts +0 -757
- package/src/utils/diff.ts +0 -109
- package/src/utils/index.ts +0 -9
- package/src/utils/integrity.ts +0 -108
- package/src/utils/lbs.ts +0 -116
- package/src/utils/mermaid.ts +0 -110
- package/src/utils/naming.ts +0 -62
- package/src/utils/nomenclature.ts +0 -107
- package/src/utils/normalize.ts +0 -35
- package/src/utils/raci.ts +0 -25
- package/src/utils/textFile.ts +0 -24
- package/tsconfig.json +0 -12
- 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
|
-
}
|