@toolproof-npm/shared 0.1.110 → 0.1.112
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.
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export type GetNewIdentity = (identifiable: TerminalConst) => string | Promise<string>;
|
|
4
|
-
export declare function normalizeExecutionInputBindingMap(execution: ExecutionJson, strategyState: StrategyStateJson): ExecutionJson;
|
|
5
|
-
export declare function statefulStrategyToStrategyRun(statefulStrategy: StatefulStrategyJson, opts?: {
|
|
6
|
-
getNewIdentity: GetNewIdentity;
|
|
7
|
-
status?: StrategyRunStatusJson;
|
|
8
|
-
}): Promise<StrategyRunJson>;
|
|
1
|
+
import type { StepJson, StrategyStateJson } from '@toolproof-npm/schema';
|
|
2
|
+
export declare function getIndependentThreads(steps: StepJson[], strategyState: StrategyStateJson): StepJson[][];
|
|
@@ -1,247 +1,139 @@
|
|
|
1
1
|
import { CONSTANTS } from '../../constants.js';
|
|
2
|
-
export function
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
export function getIndependentThreads(steps, strategyState) {
|
|
3
|
+
const getOwnerLabel = (ownerIndex) => {
|
|
4
|
+
const step = steps[ownerIndex];
|
|
5
|
+
return `steps[${ownerIndex}] kind=${step?.kind ?? 'unknown'}`;
|
|
6
|
+
};
|
|
7
|
+
// Collect all executions (including macro-nested WorkSteps) and map them back to their
|
|
8
|
+
// owning top-level step index. We keep macro steps atomic at the outer step level.
|
|
9
|
+
const executionIdToOwner = new Map();
|
|
10
|
+
const ownerToExecutionIds = new Map();
|
|
11
|
+
const executionById = new Map();
|
|
12
|
+
const addExecution = (execution, ownerIndex) => {
|
|
13
|
+
if (!execution?.identity)
|
|
14
|
+
return;
|
|
15
|
+
const existingOwner = executionIdToOwner.get(execution.identity);
|
|
16
|
+
if (existingOwner !== undefined) {
|
|
17
|
+
throw new Error(`Duplicate execution.identity '${execution.identity}' found in ${getOwnerLabel(ownerIndex)} and ${getOwnerLabel(existingOwner)}`);
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
...execution,
|
|
23
|
-
roleBindings: {
|
|
24
|
-
inputBindingMap: nextInputBindingMap,
|
|
25
|
-
outputBindingMap,
|
|
26
|
-
},
|
|
19
|
+
executionIdToOwner.set(execution.identity, ownerIndex);
|
|
20
|
+
executionById.set(execution.identity, execution);
|
|
21
|
+
const bucket = ownerToExecutionIds.get(ownerIndex) ?? [];
|
|
22
|
+
bucket.push(execution.identity);
|
|
23
|
+
ownerToExecutionIds.set(ownerIndex, bucket);
|
|
27
24
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
? normalizeExecutionInputBindingMap(whenExecution, strategyState)
|
|
51
|
-
: whenExecution;
|
|
52
|
-
if (nextWhatExecution === whatExecution && nextWhenExecution === whenExecution)
|
|
53
|
-
return step;
|
|
54
|
-
return {
|
|
55
|
-
...loop,
|
|
56
|
-
case: {
|
|
57
|
-
...loop.case,
|
|
58
|
-
what: loop.case?.what
|
|
59
|
-
? {
|
|
60
|
-
...loop.case.what,
|
|
61
|
-
execution: nextWhatExecution,
|
|
62
|
-
}
|
|
63
|
-
: loop.case?.what,
|
|
64
|
-
when: loop.case?.when
|
|
65
|
-
? {
|
|
66
|
-
...loop.case.when,
|
|
67
|
-
execution: nextWhenExecution,
|
|
68
|
-
}
|
|
69
|
-
: loop.case?.when,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
if (step.kind === CONSTANTS.STEPS.while) {
|
|
74
|
-
const loop = step;
|
|
75
|
-
const whatExecution = loop.case?.what?.execution;
|
|
76
|
-
const whenExecution = loop.case?.when?.execution;
|
|
77
|
-
const nextWhatExecution = whatExecution
|
|
78
|
-
? normalizeExecutionInputBindingMap(whatExecution, strategyState)
|
|
79
|
-
: whatExecution;
|
|
80
|
-
const nextWhenExecution = whenExecution
|
|
81
|
-
? normalizeExecutionInputBindingMap(whenExecution, strategyState)
|
|
82
|
-
: whenExecution;
|
|
83
|
-
if (nextWhatExecution === whatExecution && nextWhenExecution === whenExecution)
|
|
84
|
-
return step;
|
|
85
|
-
return {
|
|
86
|
-
...loop,
|
|
87
|
-
case: {
|
|
88
|
-
...loop.case,
|
|
89
|
-
what: loop.case?.what
|
|
90
|
-
? {
|
|
91
|
-
...loop.case.what,
|
|
92
|
-
execution: nextWhatExecution,
|
|
93
|
-
}
|
|
94
|
-
: loop.case?.what,
|
|
95
|
-
when: loop.case?.when
|
|
96
|
-
? {
|
|
97
|
-
...loop.case.when,
|
|
98
|
-
execution: nextWhenExecution,
|
|
99
|
-
}
|
|
100
|
-
: loop.case?.when,
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (step.kind === CONSTANTS.STEPS.branch) {
|
|
105
|
-
const branch = step;
|
|
106
|
-
const cases = branch.cases ?? [];
|
|
107
|
-
let changed = false;
|
|
108
|
-
const nextCases = cases.map((caseItem) => {
|
|
109
|
-
const whatExecution = caseItem?.what?.execution;
|
|
110
|
-
const whenExecution = caseItem?.when?.execution;
|
|
111
|
-
const nextWhatExecution = whatExecution
|
|
112
|
-
? normalizeExecutionInputBindingMap(whatExecution, strategyState)
|
|
113
|
-
: whatExecution;
|
|
114
|
-
const nextWhenExecution = whenExecution
|
|
115
|
-
? normalizeExecutionInputBindingMap(whenExecution, strategyState)
|
|
116
|
-
: whenExecution;
|
|
117
|
-
if (nextWhatExecution !== whatExecution || nextWhenExecution !== whenExecution) {
|
|
118
|
-
changed = true;
|
|
25
|
+
steps.forEach((step, ownerIndex) => {
|
|
26
|
+
if (step.kind === CONSTANTS.STEPS.work) {
|
|
27
|
+
addExecution(step.execution, ownerIndex);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (step.kind === CONSTANTS.STEPS.for) {
|
|
31
|
+
const loop = step;
|
|
32
|
+
addExecution(loop.case?.what?.execution, ownerIndex);
|
|
33
|
+
addExecution(loop.case?.when?.execution, ownerIndex);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (step.kind === CONSTANTS.STEPS.while) {
|
|
37
|
+
const loop = step;
|
|
38
|
+
addExecution(loop.case?.what?.execution, ownerIndex);
|
|
39
|
+
addExecution(loop.case?.when?.execution, ownerIndex);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (step.kind === CONSTANTS.STEPS.branch) {
|
|
43
|
+
const branch = step;
|
|
44
|
+
for (const caseItem of branch.cases ?? []) {
|
|
45
|
+
addExecution(caseItem?.what?.execution, ownerIndex);
|
|
46
|
+
addExecution(caseItem?.when?.execution, ownerIndex);
|
|
119
47
|
}
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
what: caseItem?.what
|
|
123
|
-
? {
|
|
124
|
-
...caseItem.what,
|
|
125
|
-
execution: nextWhatExecution,
|
|
126
|
-
}
|
|
127
|
-
: caseItem?.what,
|
|
128
|
-
when: caseItem?.when
|
|
129
|
-
? {
|
|
130
|
-
...caseItem.when,
|
|
131
|
-
execution: nextWhenExecution,
|
|
132
|
-
}
|
|
133
|
-
: caseItem?.when,
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
if (!changed)
|
|
137
|
-
return step;
|
|
138
|
-
return {
|
|
139
|
-
...branch,
|
|
140
|
-
cases: nextCases,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
return step;
|
|
144
|
-
}
|
|
145
|
-
function normalizeStatefulStrategyInputBindingMaps(statefulStrategy) {
|
|
146
|
-
const steps = statefulStrategy.statelessStrategy?.steps;
|
|
147
|
-
if (!steps || steps.length === 0)
|
|
148
|
-
return statefulStrategy;
|
|
149
|
-
const strategyState = (statefulStrategy.strategyState ?? {});
|
|
150
|
-
let changed = false;
|
|
151
|
-
const nextSteps = steps.map((step) => {
|
|
152
|
-
const nextStep = normalizeStepRoleBindings(step, strategyState);
|
|
153
|
-
if (nextStep !== step)
|
|
154
|
-
changed = true;
|
|
155
|
-
return nextStep;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
156
50
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
51
|
+
// Undirected adjacency used for connected components (thread groups).
|
|
52
|
+
const ownerAdj = new Map();
|
|
53
|
+
// Directed dependencies used for strict ordering validation: owner -> producers it depends on.
|
|
54
|
+
const ownerDeps = new Map();
|
|
55
|
+
const ensureOwner = (ownerIndex) => {
|
|
56
|
+
if (!ownerAdj.has(ownerIndex))
|
|
57
|
+
ownerAdj.set(ownerIndex, new Set());
|
|
58
|
+
if (!ownerDeps.has(ownerIndex))
|
|
59
|
+
ownerDeps.set(ownerIndex, new Set());
|
|
165
60
|
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// Dependency exists
|
|
183
|
-
adj.get(id).push(creatorId);
|
|
184
|
-
if (!adj.has(creatorId))
|
|
185
|
-
adj.set(creatorId, []);
|
|
186
|
-
adj.get(creatorId).push(id);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
61
|
+
for (let i = 0; i < steps.length; i++) {
|
|
62
|
+
ensureOwner(i);
|
|
63
|
+
}
|
|
64
|
+
// Discover dependencies from strategyState. Note: strategyState is keyed by execution.identity.
|
|
65
|
+
for (const [executionId, ownerIndex] of executionIdToOwner) {
|
|
66
|
+
ensureOwner(ownerIndex);
|
|
67
|
+
const bucket = (strategyState?.[executionId] ?? {});
|
|
68
|
+
const execution = executionById.get(executionId);
|
|
69
|
+
const inputBindingMap = execution?.roleBindings?.inputBindingMap ?? {};
|
|
70
|
+
for (const inputRoleId of Object.keys(inputBindingMap)) {
|
|
71
|
+
const entry = bucket?.[inputRoleId];
|
|
72
|
+
if (entry?.kind !== 'potential-input')
|
|
73
|
+
continue;
|
|
74
|
+
const creatorExecutionRef = entry.creationContext?.executionRef;
|
|
75
|
+
if (!creatorExecutionRef) {
|
|
76
|
+
throw new Error(`Unresolvable potential-input in execution '${executionId}' (${getOwnerLabel(ownerIndex)}): missing creationContext.executionRef for role '${inputRoleId}'`);
|
|
189
77
|
}
|
|
78
|
+
const producerOwner = executionIdToOwner.get(creatorExecutionRef);
|
|
79
|
+
if (producerOwner === undefined) {
|
|
80
|
+
throw new Error(`Unresolvable potential-input in execution '${executionId}' (${getOwnerLabel(ownerIndex)}): creator executionRef '${creatorExecutionRef}' not found in strategy steps`);
|
|
81
|
+
}
|
|
82
|
+
ensureOwner(producerOwner);
|
|
83
|
+
ownerAdj.get(ownerIndex).add(producerOwner);
|
|
84
|
+
ownerAdj.get(producerOwner).add(ownerIndex);
|
|
85
|
+
ownerDeps.get(ownerIndex).add(producerOwner);
|
|
190
86
|
}
|
|
191
|
-
}
|
|
87
|
+
}
|
|
88
|
+
// Connected components over owners.
|
|
192
89
|
const visited = new Set();
|
|
193
90
|
const components = [];
|
|
194
|
-
for (
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
91
|
+
for (let ownerIndex = 0; ownerIndex < steps.length; ownerIndex++) {
|
|
92
|
+
if (visited.has(ownerIndex))
|
|
93
|
+
continue;
|
|
94
|
+
const component = [];
|
|
95
|
+
const queue = [ownerIndex];
|
|
96
|
+
visited.add(ownerIndex);
|
|
97
|
+
while (queue.length > 0) {
|
|
98
|
+
const node = queue.shift();
|
|
99
|
+
component.push(node);
|
|
100
|
+
for (const neighbor of ownerAdj.get(node) ?? []) {
|
|
101
|
+
if (!visited.has(neighbor)) {
|
|
102
|
+
visited.add(neighbor);
|
|
103
|
+
queue.push(neighbor);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
components.push(component);
|
|
108
|
+
}
|
|
109
|
+
// Strict validation: within a thread, the linear order must not contain forward refs.
|
|
110
|
+
// The engine does not "wait" for dependencies; a consumer-before-producer will fail.
|
|
111
|
+
for (const comp of components) {
|
|
112
|
+
const compSet = new Set(comp);
|
|
113
|
+
const ownersInOrder = steps.map((_, i) => i).filter((i) => compSet.has(i));
|
|
114
|
+
const position = new Map();
|
|
115
|
+
ownersInOrder.forEach((owner, idx) => position.set(owner, idx));
|
|
116
|
+
for (const consumerOwner of ownersInOrder) {
|
|
117
|
+
const deps = ownerDeps.get(consumerOwner);
|
|
118
|
+
if (!deps || deps.size === 0)
|
|
119
|
+
continue;
|
|
120
|
+
const consumerPos = position.get(consumerOwner);
|
|
121
|
+
for (const producerOwner of deps) {
|
|
122
|
+
const producerPos = position.get(producerOwner);
|
|
123
|
+
if (producerPos === undefined) {
|
|
124
|
+
// Should not happen since deps are within steps, but keep strict.
|
|
125
|
+
throw new Error(`Internal error: dependency producer ${getOwnerLabel(producerOwner)} missing from computed thread for ${getOwnerLabel(consumerOwner)}`);
|
|
126
|
+
}
|
|
127
|
+
if (producerPos >= consumerPos) {
|
|
128
|
+
throw new Error(`Invalid step order in thread: ${getOwnerLabel(consumerOwner)} depends on ${getOwnerLabel(producerOwner)}, but producer is not scheduled before consumer`);
|
|
207
129
|
}
|
|
208
130
|
}
|
|
209
|
-
components.push(component);
|
|
210
131
|
}
|
|
211
132
|
}
|
|
212
133
|
return components
|
|
213
|
-
.map((
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return compIds.includes(sId);
|
|
217
|
-
});
|
|
134
|
+
.map((compOwners) => {
|
|
135
|
+
const ownerSet = new Set(compOwners);
|
|
136
|
+
return steps.filter((_, i) => ownerSet.has(i));
|
|
218
137
|
})
|
|
219
138
|
.filter((group) => group.length > 0);
|
|
220
139
|
}
|
|
221
|
-
export async function statefulStrategyToStrategyRun(statefulStrategy, opts) {
|
|
222
|
-
if (!opts?.getNewIdentity) {
|
|
223
|
-
throw new Error('statefulStrategyToStrategyRun: opts.getNewIdentity is required');
|
|
224
|
-
}
|
|
225
|
-
// Normalize roleBindings.inputBindingMap to reflect the identities present in strategyState.
|
|
226
|
-
// This is intentionally a pure transformation (no mutation of the provided statefulStrategy object graph).
|
|
227
|
-
const normalizedStatefulStrategy = normalizeStatefulStrategyInputBindingMaps(statefulStrategy);
|
|
228
|
-
const strategyRunIdentity = (await opts.getNewIdentity(CONSTANTS.TERMINALS.strategy_run));
|
|
229
|
-
const steps = normalizedStatefulStrategy.statelessStrategy?.steps ?? [];
|
|
230
|
-
const strategyState = (normalizedStatefulStrategy.strategyState ?? {});
|
|
231
|
-
// Algorithm to find what steps can run in parallel
|
|
232
|
-
const threadStepsGroups = getIndependentThreads(steps, strategyState);
|
|
233
|
-
const strategyThreadMap = {};
|
|
234
|
-
for (const group of threadStepsGroups) {
|
|
235
|
-
const threadIdentity = await opts.getNewIdentity(CONSTANTS.TERMINALS.strategy_thread);
|
|
236
|
-
strategyThreadMap[threadIdentity] = group;
|
|
237
|
-
}
|
|
238
|
-
return {
|
|
239
|
-
identity: strategyRunIdentity,
|
|
240
|
-
statefulStrategyRef: normalizedStatefulStrategy.identity,
|
|
241
|
-
strategyState: strategyState,
|
|
242
|
-
strategyRunContext: {
|
|
243
|
-
status: opts.status ?? 'running',
|
|
244
|
-
},
|
|
245
|
-
strategyThreadMap,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExecutionJson, StatefulStrategyJson, StrategyRunJson, StrategyRunStatusJson, StrategyStateJson } from '@toolproof-npm/schema';
|
|
2
|
+
import type { TerminalConst } from '../types.js';
|
|
3
|
+
export type GetNewIdentity = (identifiable: TerminalConst) => string | Promise<string>;
|
|
4
|
+
export declare function normalizeExecutionInputBindingMap(execution: ExecutionJson, strategyState: StrategyStateJson): ExecutionJson;
|
|
5
|
+
export declare function createStrategyRun(statefulStrategy: StatefulStrategyJson, opts?: {
|
|
6
|
+
getNewIdentity: GetNewIdentity;
|
|
7
|
+
status?: StrategyRunStatusJson;
|
|
8
|
+
}): Promise<StrategyRunJson>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { CONSTANTS } from '../../constants.js';
|
|
2
|
+
import { getIndependentThreads } from './parallelization.js';
|
|
3
|
+
export function normalizeExecutionInputBindingMap(execution, strategyState) {
|
|
4
|
+
const inputBindingMap = execution.roleBindings?.inputBindingMap ?? {};
|
|
5
|
+
const outputBindingMap = execution.roleBindings?.outputBindingMap ?? {};
|
|
6
|
+
const bucket = strategyState?.[execution.identity];
|
|
7
|
+
if (!bucket) {
|
|
8
|
+
return execution;
|
|
9
|
+
}
|
|
10
|
+
let changed = false;
|
|
11
|
+
const nextInputBindingMap = { ...inputBindingMap };
|
|
12
|
+
for (const [roleRef, currentResourceId] of Object.entries(inputBindingMap)) {
|
|
13
|
+
const entryIdentity = bucket?.[roleRef]?.identity;
|
|
14
|
+
if (typeof entryIdentity === 'string' && entryIdentity.length > 0 && entryIdentity !== currentResourceId) {
|
|
15
|
+
nextInputBindingMap[roleRef] = entryIdentity;
|
|
16
|
+
changed = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (!changed) {
|
|
20
|
+
return execution;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
...execution,
|
|
24
|
+
roleBindings: {
|
|
25
|
+
inputBindingMap: nextInputBindingMap,
|
|
26
|
+
outputBindingMap,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function normalizeStepRoleBindings(step, strategyState) {
|
|
31
|
+
if (step.kind === CONSTANTS.STEPS.work) {
|
|
32
|
+
const work = step;
|
|
33
|
+
if (!work.execution)
|
|
34
|
+
return step;
|
|
35
|
+
const nextExecution = normalizeExecutionInputBindingMap(work.execution, strategyState);
|
|
36
|
+
if (nextExecution === work.execution)
|
|
37
|
+
return step;
|
|
38
|
+
return {
|
|
39
|
+
...work,
|
|
40
|
+
execution: nextExecution,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (step.kind === CONSTANTS.STEPS.for) {
|
|
44
|
+
const loop = step;
|
|
45
|
+
const whatExecution = loop.case?.what?.execution;
|
|
46
|
+
const whenExecution = loop.case?.when?.execution;
|
|
47
|
+
const nextWhatExecution = whatExecution
|
|
48
|
+
? normalizeExecutionInputBindingMap(whatExecution, strategyState)
|
|
49
|
+
: whatExecution;
|
|
50
|
+
const nextWhenExecution = whenExecution
|
|
51
|
+
? normalizeExecutionInputBindingMap(whenExecution, strategyState)
|
|
52
|
+
: whenExecution;
|
|
53
|
+
if (nextWhatExecution === whatExecution && nextWhenExecution === whenExecution)
|
|
54
|
+
return step;
|
|
55
|
+
return {
|
|
56
|
+
...loop,
|
|
57
|
+
case: {
|
|
58
|
+
...loop.case,
|
|
59
|
+
what: loop.case?.what
|
|
60
|
+
? {
|
|
61
|
+
...loop.case.what,
|
|
62
|
+
execution: nextWhatExecution,
|
|
63
|
+
}
|
|
64
|
+
: loop.case?.what,
|
|
65
|
+
when: loop.case?.when
|
|
66
|
+
? {
|
|
67
|
+
...loop.case.when,
|
|
68
|
+
execution: nextWhenExecution,
|
|
69
|
+
}
|
|
70
|
+
: loop.case?.when,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (step.kind === CONSTANTS.STEPS.while) {
|
|
75
|
+
const loop = step;
|
|
76
|
+
const whatExecution = loop.case?.what?.execution;
|
|
77
|
+
const whenExecution = loop.case?.when?.execution;
|
|
78
|
+
const nextWhatExecution = whatExecution
|
|
79
|
+
? normalizeExecutionInputBindingMap(whatExecution, strategyState)
|
|
80
|
+
: whatExecution;
|
|
81
|
+
const nextWhenExecution = whenExecution
|
|
82
|
+
? normalizeExecutionInputBindingMap(whenExecution, strategyState)
|
|
83
|
+
: whenExecution;
|
|
84
|
+
if (nextWhatExecution === whatExecution && nextWhenExecution === whenExecution)
|
|
85
|
+
return step;
|
|
86
|
+
return {
|
|
87
|
+
...loop,
|
|
88
|
+
case: {
|
|
89
|
+
...loop.case,
|
|
90
|
+
what: loop.case?.what
|
|
91
|
+
? {
|
|
92
|
+
...loop.case.what,
|
|
93
|
+
execution: nextWhatExecution,
|
|
94
|
+
}
|
|
95
|
+
: loop.case?.what,
|
|
96
|
+
when: loop.case?.when
|
|
97
|
+
? {
|
|
98
|
+
...loop.case.when,
|
|
99
|
+
execution: nextWhenExecution,
|
|
100
|
+
}
|
|
101
|
+
: loop.case?.when,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (step.kind === CONSTANTS.STEPS.branch) {
|
|
106
|
+
const branch = step;
|
|
107
|
+
const cases = branch.cases ?? [];
|
|
108
|
+
let changed = false;
|
|
109
|
+
const nextCases = cases.map((caseItem) => {
|
|
110
|
+
const whatExecution = caseItem?.what?.execution;
|
|
111
|
+
const whenExecution = caseItem?.when?.execution;
|
|
112
|
+
const nextWhatExecution = whatExecution
|
|
113
|
+
? normalizeExecutionInputBindingMap(whatExecution, strategyState)
|
|
114
|
+
: whatExecution;
|
|
115
|
+
const nextWhenExecution = whenExecution
|
|
116
|
+
? normalizeExecutionInputBindingMap(whenExecution, strategyState)
|
|
117
|
+
: whenExecution;
|
|
118
|
+
if (nextWhatExecution !== whatExecution || nextWhenExecution !== whenExecution) {
|
|
119
|
+
changed = true;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
...caseItem,
|
|
123
|
+
what: caseItem?.what
|
|
124
|
+
? {
|
|
125
|
+
...caseItem.what,
|
|
126
|
+
execution: nextWhatExecution,
|
|
127
|
+
}
|
|
128
|
+
: caseItem?.what,
|
|
129
|
+
when: caseItem?.when
|
|
130
|
+
? {
|
|
131
|
+
...caseItem.when,
|
|
132
|
+
execution: nextWhenExecution,
|
|
133
|
+
}
|
|
134
|
+
: caseItem?.when,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
if (!changed)
|
|
138
|
+
return step;
|
|
139
|
+
return {
|
|
140
|
+
...branch,
|
|
141
|
+
cases: nextCases,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return step;
|
|
145
|
+
}
|
|
146
|
+
function normalizeStatefulStrategyInputBindingMaps(statefulStrategy) {
|
|
147
|
+
const steps = statefulStrategy.statelessStrategy?.steps;
|
|
148
|
+
if (!steps || steps.length === 0)
|
|
149
|
+
return statefulStrategy;
|
|
150
|
+
const strategyState = (statefulStrategy.strategyState ?? {});
|
|
151
|
+
let changed = false;
|
|
152
|
+
const nextSteps = steps.map((step) => {
|
|
153
|
+
const nextStep = normalizeStepRoleBindings(step, strategyState);
|
|
154
|
+
if (nextStep !== step)
|
|
155
|
+
changed = true;
|
|
156
|
+
return nextStep;
|
|
157
|
+
});
|
|
158
|
+
if (!changed)
|
|
159
|
+
return statefulStrategy;
|
|
160
|
+
return {
|
|
161
|
+
...statefulStrategy,
|
|
162
|
+
statelessStrategy: {
|
|
163
|
+
...statefulStrategy.statelessStrategy,
|
|
164
|
+
steps: nextSteps,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export async function createStrategyRun(statefulStrategy, opts) {
|
|
169
|
+
if (!opts?.getNewIdentity) {
|
|
170
|
+
throw new Error('statefulStrategyToStrategyRun: opts.getNewIdentity is required');
|
|
171
|
+
}
|
|
172
|
+
// Normalize roleBindings.inputBindingMap to reflect the identities present in strategyState.
|
|
173
|
+
// This is intentionally a pure transformation (no mutation of the provided statefulStrategy object graph).
|
|
174
|
+
const normalizedStatefulStrategy = normalizeStatefulStrategyInputBindingMaps(statefulStrategy);
|
|
175
|
+
const strategyRunIdentity = (await opts.getNewIdentity(CONSTANTS.TERMINALS.strategy_run));
|
|
176
|
+
const steps = normalizedStatefulStrategy.statelessStrategy?.steps ?? [];
|
|
177
|
+
const strategyState = (normalizedStatefulStrategy.strategyState ?? {});
|
|
178
|
+
// Algorithm to find what steps can run in parallel
|
|
179
|
+
const threadStepsGroups = getIndependentThreads(steps, strategyState);
|
|
180
|
+
const strategyThreadMap = {};
|
|
181
|
+
for (const group of threadStepsGroups) {
|
|
182
|
+
const threadIdentity = await opts.getNewIdentity(CONSTANTS.TERMINALS.strategy_thread);
|
|
183
|
+
strategyThreadMap[threadIdentity] = group;
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
identity: strategyRunIdentity,
|
|
187
|
+
statefulStrategyRef: normalizedStatefulStrategy.identity,
|
|
188
|
+
strategyState: strategyState,
|
|
189
|
+
strategyRunContext: {
|
|
190
|
+
status: opts.status ?? 'running',
|
|
191
|
+
},
|
|
192
|
+
strategyThreadMap,
|
|
193
|
+
};
|
|
194
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * as TYPES from './_lib/types.js';
|
|
|
3
3
|
export * as UTILS from './_lib/utils/utils.js';
|
|
4
4
|
export * as RESOURCE_CREATION from './_lib/utils/resourceCreation.js';
|
|
5
5
|
export * as STEP_CREATION from './_lib/utils/stepCreation.js';
|
|
6
|
-
export * as
|
|
6
|
+
export * as STRATEGY_RUN_CREATION from './_lib/utils/strategyRunCreation.js';
|
|
7
7
|
export * as ROLE_RESOURCE_BINDINGS from './_lib/utils/roleResourceBinding.js';
|
|
8
8
|
export * as RESOURCE_CHAIN from './_lib/utils/resourceChain.js';
|
|
9
9
|
export * from './firebaseAdminHelpers.js';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export * as TYPES from './_lib/types.js';
|
|
|
3
3
|
export * as UTILS from './_lib/utils/utils.js';
|
|
4
4
|
export * as RESOURCE_CREATION from './_lib/utils/resourceCreation.js';
|
|
5
5
|
export * as STEP_CREATION from './_lib/utils/stepCreation.js';
|
|
6
|
-
export * as
|
|
6
|
+
export * as STRATEGY_RUN_CREATION from './_lib/utils/strategyRunCreation.js';
|
|
7
7
|
export * as ROLE_RESOURCE_BINDINGS from './_lib/utils/roleResourceBinding.js';
|
|
8
8
|
export * as RESOURCE_CHAIN from './_lib/utils/resourceChain.js';
|
|
9
9
|
export * from './firebaseAdminHelpers.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolproof-npm/shared",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.112",
|
|
4
4
|
"description": "Core library utilities for ToolProof",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"toolproof",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"typescript": "^5.9.3"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@toolproof-npm/schema": "^0.1.
|
|
59
|
+
"@toolproof-npm/schema": "^0.1.75",
|
|
60
60
|
"firebase-admin": "^13.6.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|