@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.
- package/dist/index.d.ts +7 -5
- package/dist/index.js +604 -596
- 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
package/src/runtime/Engine.ts
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import type { BEP } from '../types/schema.js'
|
|
2
|
-
import { createInstance as _createInstance, processEvent, getNodeConfig as _getNodeConfig } from './transitions.js'
|
|
3
|
-
import { MemoryStorage } from './MemoryStorage.js'
|
|
4
|
-
import type { Runtime } from './Runtime.js'
|
|
5
|
-
import type {
|
|
6
|
-
WorkflowInstance,
|
|
7
|
-
IncomingEvent,
|
|
8
|
-
InstanceFilter,
|
|
9
|
-
NodeConfig,
|
|
10
|
-
EffectOutcome,
|
|
11
|
-
EventResult,
|
|
12
|
-
InstanceStore,
|
|
13
|
-
TransitionListener,
|
|
14
|
-
LifecycleListener,
|
|
15
|
-
EffectFailedListener,
|
|
16
|
-
} from './types.js'
|
|
17
|
-
|
|
18
|
-
export interface EngineInitConfig {
|
|
19
|
-
/** The runtime that accompanies the BEP — declares effects, automations, etc. */
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
-
runtime: Runtime<any>
|
|
22
|
-
/** Storage backend for workflow instances. Defaults to in-memory. */
|
|
23
|
-
storage?: InstanceStore
|
|
24
|
-
/** Event-processing options. */
|
|
25
|
-
events?: {
|
|
26
|
-
/** Skip RACI authorization checks on emit(). Intended for local testing only. */
|
|
27
|
-
skipRaci?: boolean
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Serializes a caught error to a plain string, safe across VM realm boundaries.
|
|
33
|
-
* In Node.js vm.createContext(), thrown Error objects have a different prototype
|
|
34
|
-
* chain than the host's Error — instanceof checks fail. Access .message and .name
|
|
35
|
-
* as plain properties instead.
|
|
36
|
-
*/
|
|
37
|
-
function serializeError(err: unknown): string {
|
|
38
|
-
if (err == null) return 'Unknown error'
|
|
39
|
-
if (typeof err === 'string') return err
|
|
40
|
-
const e = err as Record<string, unknown>
|
|
41
|
-
const name = typeof e['name'] === 'string' ? e['name'] : 'Error'
|
|
42
|
-
const msg = typeof e['message'] === 'string' ? e['message'] : undefined
|
|
43
|
-
if (msg !== undefined) return msg ? `${name}: ${msg}` : name
|
|
44
|
-
try { return String(err) } catch { return 'Unknown error' }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class Engine {
|
|
48
|
-
private readonly getBep: () => BEP
|
|
49
|
-
private readonly getHistoricalBep?: (version: string) => Promise<BEP>
|
|
50
|
-
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
-
private runtime!: Runtime<any>
|
|
53
|
-
private storage!: InstanceStore
|
|
54
|
-
private skipRaci = false
|
|
55
|
-
|
|
56
|
-
private readonly transitionListeners: TransitionListener[] = []
|
|
57
|
-
private readonly createdListeners: LifecycleListener[] = []
|
|
58
|
-
private readonly completedListeners: LifecycleListener[] = []
|
|
59
|
-
private readonly effectFailedListeners: EffectFailedListener[] = []
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Called internally by Bep — injects the BEP data getter and history resolver.
|
|
63
|
-
* Use bep.engine.init() to configure the runtime and storage before operating.
|
|
64
|
-
*/
|
|
65
|
-
constructor(getBep: () => BEP, getHistoricalBep?: (version: string) => Promise<BEP>) {
|
|
66
|
-
this.getBep = getBep
|
|
67
|
-
this.getHistoricalBep = getHistoricalBep
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Configures the engine with a runtime and storage backend.
|
|
72
|
-
* Must be called before any operations (createInstance, emit, etc.).
|
|
73
|
-
* Returns `this` for chaining.
|
|
74
|
-
*/
|
|
75
|
-
init(config: EngineInitConfig): this {
|
|
76
|
-
this.runtime = config.runtime
|
|
77
|
-
this.storage = config.storage ?? new MemoryStorage()
|
|
78
|
-
this.skipRaci = config.events?.skipRaci ?? false
|
|
79
|
-
return this
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ─── Lifecycle listeners ─────────────────────────────────────────────────────
|
|
83
|
-
|
|
84
|
-
/** Fires after every successful emit() — all listeners run concurrently. */
|
|
85
|
-
onTransition(listener: TransitionListener): this {
|
|
86
|
-
this.transitionListeners.push(listener)
|
|
87
|
-
return this
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Fires after createInstance() persists the new instance. */
|
|
91
|
-
onInstanceCreated(listener: LifecycleListener): this {
|
|
92
|
-
this.createdListeners.push(listener)
|
|
93
|
-
return this
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Fires when instance.status becomes 'completed'. */
|
|
97
|
-
onInstanceCompleted(listener: LifecycleListener): this {
|
|
98
|
-
this.completedListeners.push(listener)
|
|
99
|
-
return this
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Fires when an effect handler throws or returns status 'failed'. */
|
|
103
|
-
onEffectFailed(listener: EffectFailedListener): this {
|
|
104
|
-
this.effectFailedListeners.push(listener)
|
|
105
|
-
return this
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ─── Operations ─────────────────────────────────────────────────────────────
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Creates a new workflow instance positioned at the first node after start and persists it.
|
|
112
|
-
* Records the current BEP version on the instance for historical resolution.
|
|
113
|
-
* Returns null if the workflowId does not exist or has no start node.
|
|
114
|
-
*/
|
|
115
|
-
async createInstance(
|
|
116
|
-
workflowId: string,
|
|
117
|
-
trackedAsset: WorkflowInstance['trackedAsset'],
|
|
118
|
-
initiatedBy: string,
|
|
119
|
-
): Promise<WorkflowInstance | null> {
|
|
120
|
-
this._assertInit()
|
|
121
|
-
const bep = this.getBep()
|
|
122
|
-
const bepVersion = 'unversioned'
|
|
123
|
-
const result = _createInstance(bep, workflowId, trackedAsset, initiatedBy, bepVersion)
|
|
124
|
-
if (!result) return null
|
|
125
|
-
const { instance, startEffects } = result
|
|
126
|
-
for (const ef of startEffects) {
|
|
127
|
-
await this._executeEffect(instance, ef)
|
|
128
|
-
}
|
|
129
|
-
await this.storage.saveInstance(instance)
|
|
130
|
-
await this._fire(this.createdListeners, instance)
|
|
131
|
-
return instance
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Emits an event against a workflow instance.
|
|
136
|
-
*
|
|
137
|
-
* 1. Loads the instance from storage.
|
|
138
|
-
* 2. Resolves the BEP version the instance was created against.
|
|
139
|
-
* 3. Processes the event (pure transition logic — transitions + decision auto-traversal).
|
|
140
|
-
* 4. Persists the updated instance.
|
|
141
|
-
* 5. Executes effect handlers declared in the runtime.
|
|
142
|
-
* 6. Fires lifecycle listeners concurrently.
|
|
143
|
-
* 7. Returns the result with the updated instance and effect outcomes.
|
|
144
|
-
*/
|
|
145
|
-
async emit(instanceId: string, event: IncomingEvent): Promise<EventResult> {
|
|
146
|
-
this._assertInit()
|
|
147
|
-
const instance = await this.storage.getInstance(instanceId)
|
|
148
|
-
if (!instance) return { ok: false, error: 'NO_MATCHING_EDGE' }
|
|
149
|
-
|
|
150
|
-
const bep = await this._resolveBep(instance.bepVersion)
|
|
151
|
-
let result = processEvent(bep, instance, event, { skipRaci: this.skipRaci })
|
|
152
|
-
if (!result.ok) return { ok: false, error: result.error, payloadErrors: result.payloadErrors }
|
|
153
|
-
|
|
154
|
-
const allTransitions = [...(result.transitionsApplied ?? [])]
|
|
155
|
-
const allEffects: EffectOutcome[] = []
|
|
156
|
-
|
|
157
|
-
let current = result.instance!
|
|
158
|
-
|
|
159
|
-
for (const ef of result.effectsToFire ?? []) {
|
|
160
|
-
allEffects.push(await this._executeEffect(current, ef))
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Auto-execute automation nodes — loop in case an automation leads to another automation
|
|
164
|
-
const MAX_SERVICE_DEPTH = 10
|
|
165
|
-
let serviceDepth = 0
|
|
166
|
-
while (result.automationNodePending && serviceDepth++ < MAX_SERVICE_DEPTH) {
|
|
167
|
-
const { automationId } = result.automationNodePending
|
|
168
|
-
const { eventId, ...automationPayload } = await this._executeAutomationNode(current, automationId)
|
|
169
|
-
|
|
170
|
-
result = processEvent(bep, current, {
|
|
171
|
-
eventId,
|
|
172
|
-
actor: '_system',
|
|
173
|
-
softwareId: '_system',
|
|
174
|
-
payload: automationPayload,
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
if (!result.ok) break
|
|
178
|
-
|
|
179
|
-
current = result.instance!
|
|
180
|
-
allTransitions.push(...(result.transitionsApplied ?? []))
|
|
181
|
-
for (const ef of result.effectsToFire ?? []) {
|
|
182
|
-
allEffects.push(await this._executeEffect(current, ef))
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
await this.storage.saveInstance(current)
|
|
187
|
-
|
|
188
|
-
await this._fire(this.transitionListeners, current, allTransitions, allEffects)
|
|
189
|
-
if (current.status === 'completed') {
|
|
190
|
-
await this._fire(this.completedListeners, current)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
ok: true,
|
|
195
|
-
instance: current,
|
|
196
|
-
transitionsApplied: allTransitions,
|
|
197
|
-
effects: allEffects,
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ─── Read ────────────────────────────────────────────────────────────────────
|
|
202
|
-
|
|
203
|
-
async getInstance(instanceId: string): Promise<WorkflowInstance | null> {
|
|
204
|
-
this._assertInit()
|
|
205
|
-
return this.storage.getInstance(instanceId)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Returns instances matching the filter.
|
|
210
|
-
* `pendingActionFor` (Member.email) is resolved at the Engine level using
|
|
211
|
-
* the BEP RACI data — the storage layer does not need to understand it.
|
|
212
|
-
*/
|
|
213
|
-
async getInstances(filter?: InstanceFilter): Promise<WorkflowInstance[]> {
|
|
214
|
-
this._assertInit()
|
|
215
|
-
const { pendingActionFor, ...storageFilter } = filter ?? {}
|
|
216
|
-
const instances = await this.storage.listInstances(storageFilter)
|
|
217
|
-
if (!pendingActionFor) return instances
|
|
218
|
-
|
|
219
|
-
const bep = this.getBep()
|
|
220
|
-
const member = bep.members.find(m => m.email === pendingActionFor)
|
|
221
|
-
if (!member) return []
|
|
222
|
-
|
|
223
|
-
return instances.filter(instance => {
|
|
224
|
-
const workflow = bep.workflows.find(w => w.id === instance.workflowId)
|
|
225
|
-
if (!workflow) return false
|
|
226
|
-
const node = workflow.diagram.nodes[instance.currentNodeId]
|
|
227
|
-
if (!node) return false
|
|
228
|
-
const raciNode = node.type === 'process' ? node : null
|
|
229
|
-
const requiredRoleIds = [
|
|
230
|
-
...(raciNode?.responsibleRoleIds ?? []),
|
|
231
|
-
...(raciNode?.accountableRoleIds ?? []),
|
|
232
|
-
]
|
|
233
|
-
return requiredRoleIds.length === 0 || requiredRoleIds.includes(member.roleId)
|
|
234
|
-
})
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Returns what a specific actor can do from the current node of an instance.
|
|
239
|
-
* Returns null if the instance does not exist.
|
|
240
|
-
*/
|
|
241
|
-
async getNodeConfig(instanceId: string, actorEmail: string): Promise<NodeConfig | null> {
|
|
242
|
-
this._assertInit()
|
|
243
|
-
const instance = await this.storage.getInstance(instanceId)
|
|
244
|
-
if (!instance) return null
|
|
245
|
-
const bep = await this._resolveBep(instance.bepVersion)
|
|
246
|
-
return _getNodeConfig(bep, instance, actorEmail)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async deleteInstance(instanceId: string): Promise<void> {
|
|
250
|
-
this._assertInit()
|
|
251
|
-
await this.storage.deleteInstance(instanceId)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Runs the resolver declared for a remote data source and returns the raw payload.
|
|
256
|
-
* Throws if the remoteDataId does not exist in the BEP or has no resolver assigned.
|
|
257
|
-
*/
|
|
258
|
-
async getRemoteData(remoteDataId: string): Promise<unknown> {
|
|
259
|
-
this._assertInit()
|
|
260
|
-
const bep = this.getBep()
|
|
261
|
-
const remote = bep.remoteData.find(r => r.id === remoteDataId)
|
|
262
|
-
if (!remote) throw new Error(`Remote data "${remoteDataId}" not found in BEP`)
|
|
263
|
-
if (!remote.resolverId) throw new Error(`Remote data "${remoteDataId}" has no resolver assigned`)
|
|
264
|
-
return this.runtime._runResolver(remote.resolverId, remote.url)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Runs an adapter to transform data into a lens-compatible format.
|
|
269
|
-
* Throws if the adapterId has no registered handler.
|
|
270
|
-
*/
|
|
271
|
-
useAdapter(adapterId: string, data: unknown): unknown {
|
|
272
|
-
this._assertInit()
|
|
273
|
-
return this.runtime._runAdapter(adapterId, data)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// ─── Internal ────────────────────────────────────────────────────────────────
|
|
277
|
-
|
|
278
|
-
private _assertInit(): void {
|
|
279
|
-
if (!this.runtime || !this.storage) {
|
|
280
|
-
throw new Error('Engine not initialized — call bep.engine.init({ runtime, storage }) first.')
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
private async _resolveBep(bepVersion: string): Promise<BEP> {
|
|
285
|
-
if (this.getHistoricalBep && bepVersion !== 'unversioned') {
|
|
286
|
-
return this.getHistoricalBep(bepVersion)
|
|
287
|
-
}
|
|
288
|
-
return this.getBep()
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
private async _fire<A extends unknown[]>(
|
|
292
|
-
listeners: ((...args: A) => Promise<void>)[],
|
|
293
|
-
...args: A
|
|
294
|
-
): Promise<void> {
|
|
295
|
-
await Promise.allSettled(listeners.map(fn => fn(...args)))
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
private _resolveFromHistory(key: string, history: WorkflowInstance['history']): unknown {
|
|
299
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
300
|
-
const payload = history[i]!.trigger.payload ?? {}
|
|
301
|
-
if (key in payload) return payload[key]
|
|
302
|
-
}
|
|
303
|
-
return undefined
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
private async _executeAutomationNode(
|
|
307
|
-
instance: WorkflowInstance,
|
|
308
|
-
automationId: string,
|
|
309
|
-
): Promise<{ eventId: string } & Record<string, unknown>> {
|
|
310
|
-
const bep = this.getBep()
|
|
311
|
-
const automationDef = bep.automations.find(s => s.id === automationId)
|
|
312
|
-
const fields = automationDef?.payload ?? []
|
|
313
|
-
const payload = Object.fromEntries(fields.map(f => [f.key, this._resolveFromHistory(f.key, instance.history)]))
|
|
314
|
-
|
|
315
|
-
const handler = this.runtime.automations[automationId]
|
|
316
|
-
if (!handler) throw new Error(`No handler declared for automation "${automationId}"`)
|
|
317
|
-
return handler(instance, payload)
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private async _executeEffect(
|
|
321
|
-
instance: WorkflowInstance,
|
|
322
|
-
ef: { effectId: string; fromEdgeId: string },
|
|
323
|
-
): Promise<EffectOutcome> {
|
|
324
|
-
const bep = this.getBep()
|
|
325
|
-
const effectDef = bep.effects.find(e => e.id === ef.effectId)
|
|
326
|
-
const fields = effectDef?.payload ?? []
|
|
327
|
-
|
|
328
|
-
const missing = fields
|
|
329
|
-
.filter(f => f.required && this._resolveFromHistory(f.key, instance.history) === undefined)
|
|
330
|
-
.map(f => f.key)
|
|
331
|
-
|
|
332
|
-
if (missing.length > 0) {
|
|
333
|
-
return { effectId: ef.effectId, fromEdgeId: ef.fromEdgeId, status: 'skipped', missingFields: missing }
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const handler = this.runtime.effects[ef.effectId]
|
|
337
|
-
if (!handler) {
|
|
338
|
-
return { effectId: ef.effectId, fromEdgeId: ef.fromEdgeId, status: 'skipped' }
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const payload = Object.fromEntries(fields.map(f => [f.key, this._resolveFromHistory(f.key, instance.history)]))
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
await handler(instance, payload)
|
|
345
|
-
return { effectId: ef.effectId, fromEdgeId: ef.fromEdgeId, status: 'executed' }
|
|
346
|
-
} catch (error) {
|
|
347
|
-
const outcome: EffectOutcome = { effectId: ef.effectId, fromEdgeId: ef.fromEdgeId, status: 'failed', error: serializeError(error) }
|
|
348
|
-
await this._fire(this.effectFailedListeners, instance, outcome)
|
|
349
|
-
return outcome
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { InstanceStore, WorkflowInstance, InstanceFilter } from './types.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* In-memory InstanceStore implementation.
|
|
5
|
-
* Default storage for local development and testing.
|
|
6
|
-
* State is lost when the process exits.
|
|
7
|
-
*/
|
|
8
|
-
export class MemoryStorage implements InstanceStore {
|
|
9
|
-
private readonly instances = new Map<string, WorkflowInstance>()
|
|
10
|
-
|
|
11
|
-
async listInstances(filter?: InstanceFilter): Promise<WorkflowInstance[]> {
|
|
12
|
-
let results = [...this.instances.values()]
|
|
13
|
-
if (filter?.workflowId) results = results.filter(i => i.workflowId === filter.workflowId)
|
|
14
|
-
if (filter?.status) results = results.filter(i => i.status === filter.status)
|
|
15
|
-
if (filter?.trackedAssetTypeId) results = results.filter(i => i.trackedAsset.assetTypeId === filter.trackedAssetTypeId)
|
|
16
|
-
if (filter?.trackedAssetId) results = results.filter(i => i.trackedAsset.id === filter.trackedAssetId)
|
|
17
|
-
return results
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async getInstance(instanceId: string): Promise<WorkflowInstance | null> {
|
|
21
|
-
return this.instances.get(instanceId) ?? null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async saveInstance(instance: WorkflowInstance): Promise<void> {
|
|
25
|
-
this.instances.set(instance.id, instance)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async deleteInstance(instanceId: string): Promise<void> {
|
|
29
|
-
this.instances.delete(instanceId)
|
|
30
|
-
}
|
|
31
|
-
}
|
package/src/runtime/Runtime.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import type { WorkflowInstance, EffectHandler, AutomationHandler, ResolverHandler, AdapterHandler } from './types.js'
|
|
2
|
-
|
|
3
|
-
export interface BepTypes {
|
|
4
|
-
effects: Record<string, (...args: any[]) => void>
|
|
5
|
-
automations: Record<string, (...args: any[]) => { eventId: string } & Record<string, unknown>>
|
|
6
|
-
resolvers: Record<string, (url: string, ...args: any[]) => unknown>
|
|
7
|
-
adapters: Record<string, (data: unknown) => unknown>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Base class for the runtime that accompanies a BEP.
|
|
12
|
-
* Extend this class and register handlers in the constructor.
|
|
13
|
-
* Pass the generated BepTypes as the generic parameter for full type safety.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* import type { BepTypes } from './bep.js'
|
|
17
|
-
* import * as BEP from '@dotbep/core'
|
|
18
|
-
*
|
|
19
|
-
* class MyRuntime extends BEP.Runtime<BepTypes> {
|
|
20
|
-
* constructor(options: BEP.RuntimeOptions) {
|
|
21
|
-
* super(options)
|
|
22
|
-
* this.effect('send-email', async (instance, payload) => {
|
|
23
|
-
* await sendEmail(this.env.SENDGRID_KEY, payload.to)
|
|
24
|
-
* })
|
|
25
|
-
* this.automation('check-approval', async (instance) => {
|
|
26
|
-
* return { eventId: 'approved' }
|
|
27
|
-
* })
|
|
28
|
-
* this.resolver('fetch-data', async (url) => {
|
|
29
|
-
* return fetch(url).then(r => r.json())
|
|
30
|
-
* })
|
|
31
|
-
* this.adapter('to-chart', (data) => data)
|
|
32
|
-
* }
|
|
33
|
-
* }
|
|
34
|
-
*
|
|
35
|
-
* bep.engine.init({ runtime: new MyRuntime({ env: process.env }) })
|
|
36
|
-
*/
|
|
37
|
-
export interface RuntimeOptions {
|
|
38
|
-
env?: Record<string, string>
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export class Runtime<T extends {
|
|
42
|
-
effects: Record<string, any>
|
|
43
|
-
automations: Record<string, any>
|
|
44
|
-
resolvers: Record<string, any>
|
|
45
|
-
adapters: Record<string, any>
|
|
46
|
-
} = BepTypes> {
|
|
47
|
-
protected readonly env: Record<string, string>
|
|
48
|
-
|
|
49
|
-
readonly effects: Record<string, EffectHandler> = {}
|
|
50
|
-
readonly automations: Record<string, AutomationHandler> = {}
|
|
51
|
-
readonly resolvers: Record<string, ResolverHandler> = {}
|
|
52
|
-
readonly adapters: Record<string, AdapterHandler> = {}
|
|
53
|
-
|
|
54
|
-
constructor({ env = {} }: RuntimeOptions = {}) {
|
|
55
|
-
this.env = env
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
protected effect<K extends keyof T['effects'] & string>(
|
|
59
|
-
key: K,
|
|
60
|
-
handler: (instance: WorkflowInstance, ...args: Parameters<T['effects'][K]>) => Promise<void>,
|
|
61
|
-
): this {
|
|
62
|
-
this.effects[key] = handler as unknown as EffectHandler
|
|
63
|
-
return this
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
protected automation<K extends keyof T['automations'] & string>(
|
|
67
|
-
key: K,
|
|
68
|
-
handler: (instance: WorkflowInstance, ...args: Parameters<T['automations'][K]>) => Promise<ReturnType<T['automations'][K]>>,
|
|
69
|
-
): this {
|
|
70
|
-
this.automations[key] = handler as unknown as AutomationHandler
|
|
71
|
-
return this
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
protected resolver<K extends keyof T['resolvers'] & string>(
|
|
75
|
-
key: K,
|
|
76
|
-
handler: (...args: Parameters<T['resolvers'][K]>) => Promise<ReturnType<T['resolvers'][K]>>,
|
|
77
|
-
): this {
|
|
78
|
-
this.resolvers[key] = handler as unknown as ResolverHandler
|
|
79
|
-
return this
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
protected adapter<K extends keyof T['adapters'] & string>(
|
|
83
|
-
key: K,
|
|
84
|
-
handler: (...args: Parameters<T['adapters'][K]>) => ReturnType<T['adapters'][K]>,
|
|
85
|
-
): this {
|
|
86
|
-
this.adapters[key] = handler as unknown as AdapterHandler
|
|
87
|
-
return this
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @internal Called by Engine.getRemoteData — keeps env encapsulated inside the Runtime. */
|
|
91
|
-
_runResolver(id: string, url: string): Promise<unknown> {
|
|
92
|
-
const handler = this.resolvers[id]
|
|
93
|
-
if (!handler) throw new Error(`No handler declared for resolver "${id}"`)
|
|
94
|
-
return handler(url, this.env)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** @internal Called by Engine.useAdapter — keeps handler lookup inside the Runtime. */
|
|
98
|
-
_runAdapter(id: string, data: unknown): unknown {
|
|
99
|
-
const handler = this.adapters[id]
|
|
100
|
-
if (!handler) throw new Error(`No handler declared for adapter "${id}"`)
|
|
101
|
-
return handler(data)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Untyped aliases used internally by Engine (which works with the base contract)
|
|
106
|
-
export type { EffectHandler, AutomationHandler, ResolverHandler, AdapterHandler }
|
package/src/runtime/index.ts
DELETED