@seljs/runtime 1.0.0 → 1.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.
- package/CHANGELOG.md +7 -0
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/analysis/call-collector.cjs +233 -0
- package/dist/analysis/call-collector.mjs +232 -0
- package/dist/analysis/dependency-analyzer.cjs +68 -0
- package/dist/analysis/dependency-analyzer.mjs +68 -0
- package/dist/analysis/round-planner.cjs +65 -0
- package/dist/analysis/round-planner.mjs +65 -0
- package/dist/analysis/types.d.cts +115 -0
- package/dist/analysis/types.d.mts +115 -0
- package/dist/debug.cjs +17 -0
- package/dist/debug.mjs +15 -0
- package/dist/environment/context.cjs +11 -0
- package/dist/environment/context.mjs +11 -0
- package/dist/environment/contract-caller.cjs +81 -0
- package/dist/environment/contract-caller.mjs +77 -0
- package/dist/environment/environment.cjs +254 -0
- package/dist/environment/environment.d.cts +84 -0
- package/dist/environment/environment.d.mts +84 -0
- package/dist/environment/environment.mjs +252 -0
- package/dist/environment/error-wrapper.cjs +29 -0
- package/dist/environment/error-wrapper.mjs +27 -0
- package/dist/environment/index.cjs +1 -0
- package/dist/environment/index.d.mts +2 -0
- package/dist/environment/index.mjs +2 -0
- package/dist/environment/replay-cache.cjs +48 -0
- package/dist/environment/replay-cache.mjs +47 -0
- package/dist/environment/types.d.cts +60 -0
- package/dist/environment/types.d.mts +60 -0
- package/dist/errors/errors.cjs +68 -0
- package/dist/errors/errors.d.cts +64 -0
- package/dist/errors/errors.d.mts +64 -0
- package/dist/errors/errors.mjs +63 -0
- package/dist/errors/index.cjs +3 -0
- package/dist/errors/index.d.mts +1 -0
- package/dist/errors/index.mjs +2 -0
- package/dist/execution/multi-round-executor.cjs +45 -0
- package/dist/execution/multi-round-executor.mjs +44 -0
- package/dist/execution/multicall-batcher.cjs +51 -0
- package/dist/execution/multicall-batcher.mjs +50 -0
- package/dist/execution/multicall.cjs +39 -0
- package/dist/execution/multicall.mjs +38 -0
- package/dist/execution/result-cache.cjs +63 -0
- package/dist/execution/result-cache.mjs +63 -0
- package/dist/execution/round-executor.cjs +81 -0
- package/dist/execution/round-executor.mjs +80 -0
- package/dist/execution/types.d.cts +58 -0
- package/dist/execution/types.d.mts +58 -0
- package/dist/factory.cjs +6 -0
- package/dist/factory.d.cts +7 -0
- package/dist/factory.d.mts +6 -0
- package/dist/factory.mjs +6 -0
- package/dist/index.cjs +18 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +6 -0
- package/package.json +26 -19
- package/dist/analysis/call-collector.d.ts +0 -20
- package/dist/analysis/call-collector.d.ts.map +0 -1
- package/dist/analysis/call-collector.js +0 -272
- package/dist/analysis/dependency-analyzer.d.ts +0 -14
- package/dist/analysis/dependency-analyzer.d.ts.map +0 -1
- package/dist/analysis/dependency-analyzer.js +0 -76
- package/dist/analysis/index.d.ts +0 -2
- package/dist/analysis/index.d.ts.map +0 -1
- package/dist/analysis/index.js +0 -1
- package/dist/analysis/round-planner.d.ts +0 -32
- package/dist/analysis/round-planner.d.ts.map +0 -1
- package/dist/analysis/round-planner.js +0 -69
- package/dist/analysis/types.d.ts +0 -113
- package/dist/analysis/types.d.ts.map +0 -1
- package/dist/analysis/types.js +0 -1
- package/dist/debug.d.ts +0 -13
- package/dist/debug.d.ts.map +0 -1
- package/dist/debug.js +0 -12
- package/dist/environment/context.d.ts +0 -3
- package/dist/environment/context.d.ts.map +0 -1
- package/dist/environment/context.js +0 -8
- package/dist/environment/contract-caller.d.ts +0 -25
- package/dist/environment/contract-caller.d.ts.map +0 -1
- package/dist/environment/contract-caller.js +0 -85
- package/dist/environment/environment.d.ts +0 -81
- package/dist/environment/environment.d.ts.map +0 -1
- package/dist/environment/environment.js +0 -279
- package/dist/environment/error-wrapper.d.ts +0 -11
- package/dist/environment/error-wrapper.d.ts.map +0 -1
- package/dist/environment/error-wrapper.js +0 -33
- package/dist/environment/index.d.ts +0 -3
- package/dist/environment/index.d.ts.map +0 -1
- package/dist/environment/index.js +0 -2
- package/dist/environment/replay-cache.d.ts +0 -23
- package/dist/environment/replay-cache.d.ts.map +0 -1
- package/dist/environment/replay-cache.js +0 -51
- package/dist/environment/types.d.ts +0 -57
- package/dist/environment/types.d.ts.map +0 -1
- package/dist/environment/types.js +0 -1
- package/dist/errors/errors.d.ts +0 -63
- package/dist/errors/errors.d.ts.map +0 -1
- package/dist/errors/errors.js +0 -63
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js +0 -1
- package/dist/execution/index.d.ts +0 -2
- package/dist/execution/index.d.ts.map +0 -1
- package/dist/execution/index.js +0 -1
- package/dist/execution/multi-round-executor.d.ts +0 -17
- package/dist/execution/multi-round-executor.d.ts.map +0 -1
- package/dist/execution/multi-round-executor.js +0 -47
- package/dist/execution/multicall-batcher.d.ts +0 -14
- package/dist/execution/multicall-batcher.d.ts.map +0 -1
- package/dist/execution/multicall-batcher.js +0 -53
- package/dist/execution/multicall.d.ts +0 -42
- package/dist/execution/multicall.d.ts.map +0 -1
- package/dist/execution/multicall.js +0 -29
- package/dist/execution/result-cache.d.ts +0 -47
- package/dist/execution/result-cache.d.ts.map +0 -1
- package/dist/execution/result-cache.js +0 -65
- package/dist/execution/round-executor.d.ts +0 -18
- package/dist/execution/round-executor.d.ts.map +0 -1
- package/dist/execution/round-executor.js +0 -95
- package/dist/execution/types.d.ts +0 -55
- package/dist/execution/types.d.ts.map +0 -1
- package/dist/execution/types.js +0 -1
- package/dist/factory.d.ts +0 -3
- package/dist/factory.d.ts.map +0 -1
- package/dist/factory.js +0 -2
- package/dist/index.d.ts +0 -10
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -7
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const require_debug = require("../debug.cjs");
|
|
2
|
+
const require_errors = require("../errors/errors.cjs");
|
|
3
|
+
require("../errors/index.cjs");
|
|
4
|
+
//#region src/analysis/round-planner.ts
|
|
5
|
+
const debug = require_debug.createLogger("analysis:plan");
|
|
6
|
+
/**
|
|
7
|
+
* Plans execution rounds from a dependency graph using topological sorting.
|
|
8
|
+
*
|
|
9
|
+
* Groups independent calls into parallel rounds, ensuring all dependencies
|
|
10
|
+
* of a call are executed in previous rounds. Enforces limits on the number
|
|
11
|
+
* of rounds and total calls to prevent excessive execution.
|
|
12
|
+
*
|
|
13
|
+
* @param graph Dependency graph representing calls and their relationships.
|
|
14
|
+
* @param limits Optional limits for maximum rounds and total calls.
|
|
15
|
+
* @returns Execution plan containing planned rounds, total calls, and maximum depth.
|
|
16
|
+
* @throws ExecutionLimitError If total calls exceed maxCalls or rounds exceed maxRounds.
|
|
17
|
+
*/
|
|
18
|
+
const planRounds = (graph, limits = {}) => {
|
|
19
|
+
const maxRounds = limits.maxRounds ?? 10;
|
|
20
|
+
const maxCalls = limits.maxCalls ?? 100;
|
|
21
|
+
const totalCalls = graph.nodes.size;
|
|
22
|
+
if (totalCalls > maxCalls) throw new require_errors.ExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, {
|
|
23
|
+
limitType: "maxCalls",
|
|
24
|
+
limit: maxCalls,
|
|
25
|
+
actual: totalCalls
|
|
26
|
+
});
|
|
27
|
+
if (totalCalls === 0) return {
|
|
28
|
+
rounds: [],
|
|
29
|
+
totalCalls: 0,
|
|
30
|
+
maxDepth: 0
|
|
31
|
+
};
|
|
32
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const nodeId of graph.nodes.keys()) inDegree.set(nodeId, graph.edges.get(nodeId)?.size ?? 0);
|
|
34
|
+
const rounds = [];
|
|
35
|
+
const processed = /* @__PURE__ */ new Set();
|
|
36
|
+
while (processed.size < totalCalls) {
|
|
37
|
+
const readyNodes = [];
|
|
38
|
+
for (const [nodeId, degree] of inDegree) if (degree === 0 && !processed.has(nodeId)) readyNodes.push(nodeId);
|
|
39
|
+
if (readyNodes.length === 0) break;
|
|
40
|
+
if (rounds.length >= maxRounds) throw new require_errors.ExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
|
|
41
|
+
limitType: "maxRounds",
|
|
42
|
+
limit: maxRounds,
|
|
43
|
+
actual: rounds.length + 1
|
|
44
|
+
});
|
|
45
|
+
const round = {
|
|
46
|
+
roundNumber: rounds.length,
|
|
47
|
+
calls: readyNodes.map((id) => graph.nodes.get(id)?.call).filter((c) => c !== void 0)
|
|
48
|
+
};
|
|
49
|
+
rounds.push(round);
|
|
50
|
+
for (const nodeId of readyNodes) {
|
|
51
|
+
processed.add(nodeId);
|
|
52
|
+
const node = graph.nodes.get(nodeId);
|
|
53
|
+
if (!node) continue;
|
|
54
|
+
for (const dependentId of node.dependedOnBy) inDegree.set(dependentId, (inDegree.get(dependentId) ?? 0) - 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
debug("planned %d rounds for %d calls (max depth=%d)", rounds.length, totalCalls, rounds.length);
|
|
58
|
+
return {
|
|
59
|
+
rounds,
|
|
60
|
+
totalCalls,
|
|
61
|
+
maxDepth: rounds.length
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
exports.planRounds = planRounds;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createLogger } from "../debug.mjs";
|
|
2
|
+
import { ExecutionLimitError } from "../errors/errors.mjs";
|
|
3
|
+
import "../errors/index.mjs";
|
|
4
|
+
//#region src/analysis/round-planner.ts
|
|
5
|
+
const debug = createLogger("analysis:plan");
|
|
6
|
+
/**
|
|
7
|
+
* Plans execution rounds from a dependency graph using topological sorting.
|
|
8
|
+
*
|
|
9
|
+
* Groups independent calls into parallel rounds, ensuring all dependencies
|
|
10
|
+
* of a call are executed in previous rounds. Enforces limits on the number
|
|
11
|
+
* of rounds and total calls to prevent excessive execution.
|
|
12
|
+
*
|
|
13
|
+
* @param graph Dependency graph representing calls and their relationships.
|
|
14
|
+
* @param limits Optional limits for maximum rounds and total calls.
|
|
15
|
+
* @returns Execution plan containing planned rounds, total calls, and maximum depth.
|
|
16
|
+
* @throws ExecutionLimitError If total calls exceed maxCalls or rounds exceed maxRounds.
|
|
17
|
+
*/
|
|
18
|
+
const planRounds = (graph, limits = {}) => {
|
|
19
|
+
const maxRounds = limits.maxRounds ?? 10;
|
|
20
|
+
const maxCalls = limits.maxCalls ?? 100;
|
|
21
|
+
const totalCalls = graph.nodes.size;
|
|
22
|
+
if (totalCalls > maxCalls) throw new ExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, {
|
|
23
|
+
limitType: "maxCalls",
|
|
24
|
+
limit: maxCalls,
|
|
25
|
+
actual: totalCalls
|
|
26
|
+
});
|
|
27
|
+
if (totalCalls === 0) return {
|
|
28
|
+
rounds: [],
|
|
29
|
+
totalCalls: 0,
|
|
30
|
+
maxDepth: 0
|
|
31
|
+
};
|
|
32
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const nodeId of graph.nodes.keys()) inDegree.set(nodeId, graph.edges.get(nodeId)?.size ?? 0);
|
|
34
|
+
const rounds = [];
|
|
35
|
+
const processed = /* @__PURE__ */ new Set();
|
|
36
|
+
while (processed.size < totalCalls) {
|
|
37
|
+
const readyNodes = [];
|
|
38
|
+
for (const [nodeId, degree] of inDegree) if (degree === 0 && !processed.has(nodeId)) readyNodes.push(nodeId);
|
|
39
|
+
if (readyNodes.length === 0) break;
|
|
40
|
+
if (rounds.length >= maxRounds) throw new ExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
|
|
41
|
+
limitType: "maxRounds",
|
|
42
|
+
limit: maxRounds,
|
|
43
|
+
actual: rounds.length + 1
|
|
44
|
+
});
|
|
45
|
+
const round = {
|
|
46
|
+
roundNumber: rounds.length,
|
|
47
|
+
calls: readyNodes.map((id) => graph.nodes.get(id)?.call).filter((c) => c !== void 0)
|
|
48
|
+
};
|
|
49
|
+
rounds.push(round);
|
|
50
|
+
for (const nodeId of readyNodes) {
|
|
51
|
+
processed.add(nodeId);
|
|
52
|
+
const node = graph.nodes.get(nodeId);
|
|
53
|
+
if (!node) continue;
|
|
54
|
+
for (const dependentId of node.dependedOnBy) inDegree.set(dependentId, (inDegree.get(dependentId) ?? 0) - 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
debug("planned %d rounds for %d calls (max depth=%d)", rounds.length, totalCalls, rounds.length);
|
|
58
|
+
return {
|
|
59
|
+
rounds,
|
|
60
|
+
totalCalls,
|
|
61
|
+
maxDepth: rounds.length
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { planRounds };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
//#region src/analysis/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Represents a contract call found in AST traversal.
|
|
4
|
+
* Contains all information needed to execute the call and track dependencies.
|
|
5
|
+
*/
|
|
6
|
+
interface CollectedCall {
|
|
7
|
+
/**
|
|
8
|
+
* Unique identifier: "contract:method:serializedArgs"
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
/**
|
|
12
|
+
* Contract name: e.g., "erc20_usdc"
|
|
13
|
+
*/
|
|
14
|
+
contract: string;
|
|
15
|
+
/**
|
|
16
|
+
* Method name: e.g., "balanceOf"
|
|
17
|
+
*/
|
|
18
|
+
method: string;
|
|
19
|
+
/**
|
|
20
|
+
* Arguments with type and dependency information
|
|
21
|
+
*/
|
|
22
|
+
args: CallArgument[];
|
|
23
|
+
/**
|
|
24
|
+
* Reference to original AST node for error reporting
|
|
25
|
+
*/
|
|
26
|
+
astNode: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Argument to a contract call with type information.
|
|
30
|
+
* Tracks whether the argument is a literal, variable, or result of another call.
|
|
31
|
+
*/
|
|
32
|
+
interface CallArgument {
|
|
33
|
+
/**
|
|
34
|
+
* Type of argument: literal value, variable reference, or call result
|
|
35
|
+
*/
|
|
36
|
+
type: "literal" | "variable" | "call_result";
|
|
37
|
+
/**
|
|
38
|
+
* Literal value (when type is "literal")
|
|
39
|
+
*/
|
|
40
|
+
value?: unknown;
|
|
41
|
+
/**
|
|
42
|
+
* Variable name (when type is "variable")
|
|
43
|
+
*/
|
|
44
|
+
variableName?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Call ID this depends on (when type is "call_result")
|
|
47
|
+
*/
|
|
48
|
+
dependsOnCallId?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Dependency graph for all collected calls.
|
|
52
|
+
* Maps call IDs to their nodes and dependency relationships.
|
|
53
|
+
*/
|
|
54
|
+
interface DependencyGraph {
|
|
55
|
+
/**
|
|
56
|
+
* Call ID to graph node mapping
|
|
57
|
+
*/
|
|
58
|
+
nodes: Map<string, GraphNode>;
|
|
59
|
+
/**
|
|
60
|
+
* Call ID to set of call IDs it depends on
|
|
61
|
+
*/
|
|
62
|
+
edges: Map<string, Set<string>>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Single node in the dependency graph.
|
|
66
|
+
* Tracks both incoming and outgoing dependencies.
|
|
67
|
+
*/
|
|
68
|
+
interface GraphNode {
|
|
69
|
+
/**
|
|
70
|
+
* The original collected call
|
|
71
|
+
*/
|
|
72
|
+
call: CollectedCall;
|
|
73
|
+
/**
|
|
74
|
+
* Call IDs this call depends on (must complete first)
|
|
75
|
+
*/
|
|
76
|
+
dependsOn: string[];
|
|
77
|
+
/**
|
|
78
|
+
* Call IDs that depend on this call (will execute after)
|
|
79
|
+
*/
|
|
80
|
+
dependedOnBy: string[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Execution plan with rounds of parallel calls.
|
|
84
|
+
* Result of topological sorting the dependency graph.
|
|
85
|
+
*/
|
|
86
|
+
interface ExecutionPlan {
|
|
87
|
+
/**
|
|
88
|
+
* Rounds of calls to execute in sequence
|
|
89
|
+
*/
|
|
90
|
+
rounds: ExecutionRound[];
|
|
91
|
+
/**
|
|
92
|
+
* Total number of calls across all rounds
|
|
93
|
+
*/
|
|
94
|
+
totalCalls: number;
|
|
95
|
+
/**
|
|
96
|
+
* Number of rounds (maximum dependency depth)
|
|
97
|
+
*/
|
|
98
|
+
maxDepth: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Single round of calls that can execute in parallel.
|
|
102
|
+
* All calls in a round have no dependencies on each other.
|
|
103
|
+
*/
|
|
104
|
+
interface ExecutionRound {
|
|
105
|
+
/**
|
|
106
|
+
* Round number (0-indexed)
|
|
107
|
+
*/
|
|
108
|
+
roundNumber: number;
|
|
109
|
+
/**
|
|
110
|
+
* Calls that can execute in parallel this round
|
|
111
|
+
*/
|
|
112
|
+
calls: CollectedCall[];
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { CallArgument, CollectedCall, DependencyGraph, ExecutionPlan, ExecutionRound, GraphNode };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
//#region src/analysis/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Represents a contract call found in AST traversal.
|
|
4
|
+
* Contains all information needed to execute the call and track dependencies.
|
|
5
|
+
*/
|
|
6
|
+
interface CollectedCall {
|
|
7
|
+
/**
|
|
8
|
+
* Unique identifier: "contract:method:serializedArgs"
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
/**
|
|
12
|
+
* Contract name: e.g., "erc20_usdc"
|
|
13
|
+
*/
|
|
14
|
+
contract: string;
|
|
15
|
+
/**
|
|
16
|
+
* Method name: e.g., "balanceOf"
|
|
17
|
+
*/
|
|
18
|
+
method: string;
|
|
19
|
+
/**
|
|
20
|
+
* Arguments with type and dependency information
|
|
21
|
+
*/
|
|
22
|
+
args: CallArgument[];
|
|
23
|
+
/**
|
|
24
|
+
* Reference to original AST node for error reporting
|
|
25
|
+
*/
|
|
26
|
+
astNode: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Argument to a contract call with type information.
|
|
30
|
+
* Tracks whether the argument is a literal, variable, or result of another call.
|
|
31
|
+
*/
|
|
32
|
+
interface CallArgument {
|
|
33
|
+
/**
|
|
34
|
+
* Type of argument: literal value, variable reference, or call result
|
|
35
|
+
*/
|
|
36
|
+
type: "literal" | "variable" | "call_result";
|
|
37
|
+
/**
|
|
38
|
+
* Literal value (when type is "literal")
|
|
39
|
+
*/
|
|
40
|
+
value?: unknown;
|
|
41
|
+
/**
|
|
42
|
+
* Variable name (when type is "variable")
|
|
43
|
+
*/
|
|
44
|
+
variableName?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Call ID this depends on (when type is "call_result")
|
|
47
|
+
*/
|
|
48
|
+
dependsOnCallId?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Dependency graph for all collected calls.
|
|
52
|
+
* Maps call IDs to their nodes and dependency relationships.
|
|
53
|
+
*/
|
|
54
|
+
interface DependencyGraph {
|
|
55
|
+
/**
|
|
56
|
+
* Call ID to graph node mapping
|
|
57
|
+
*/
|
|
58
|
+
nodes: Map<string, GraphNode>;
|
|
59
|
+
/**
|
|
60
|
+
* Call ID to set of call IDs it depends on
|
|
61
|
+
*/
|
|
62
|
+
edges: Map<string, Set<string>>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Single node in the dependency graph.
|
|
66
|
+
* Tracks both incoming and outgoing dependencies.
|
|
67
|
+
*/
|
|
68
|
+
interface GraphNode {
|
|
69
|
+
/**
|
|
70
|
+
* The original collected call
|
|
71
|
+
*/
|
|
72
|
+
call: CollectedCall;
|
|
73
|
+
/**
|
|
74
|
+
* Call IDs this call depends on (must complete first)
|
|
75
|
+
*/
|
|
76
|
+
dependsOn: string[];
|
|
77
|
+
/**
|
|
78
|
+
* Call IDs that depend on this call (will execute after)
|
|
79
|
+
*/
|
|
80
|
+
dependedOnBy: string[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Execution plan with rounds of parallel calls.
|
|
84
|
+
* Result of topological sorting the dependency graph.
|
|
85
|
+
*/
|
|
86
|
+
interface ExecutionPlan {
|
|
87
|
+
/**
|
|
88
|
+
* Rounds of calls to execute in sequence
|
|
89
|
+
*/
|
|
90
|
+
rounds: ExecutionRound[];
|
|
91
|
+
/**
|
|
92
|
+
* Total number of calls across all rounds
|
|
93
|
+
*/
|
|
94
|
+
totalCalls: number;
|
|
95
|
+
/**
|
|
96
|
+
* Number of rounds (maximum dependency depth)
|
|
97
|
+
*/
|
|
98
|
+
maxDepth: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Single round of calls that can execute in parallel.
|
|
102
|
+
* All calls in a round have no dependencies on each other.
|
|
103
|
+
*/
|
|
104
|
+
interface ExecutionRound {
|
|
105
|
+
/**
|
|
106
|
+
* Round number (0-indexed)
|
|
107
|
+
*/
|
|
108
|
+
roundNumber: number;
|
|
109
|
+
/**
|
|
110
|
+
* Calls that can execute in parallel this round
|
|
111
|
+
*/
|
|
112
|
+
calls: CollectedCall[];
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { CallArgument, CollectedCall, DependencyGraph, ExecutionPlan, ExecutionRound, GraphNode };
|
package/dist/debug.cjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let debug = require("debug");
|
|
3
|
+
debug = require_runtime.__toESM(debug);
|
|
4
|
+
//#region src/debug.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates a namespaced debug logger for a SEL module.
|
|
7
|
+
*
|
|
8
|
+
* Enable via the `DEBUG` environment variable:
|
|
9
|
+
* - `DEBUG=sel:*` — all SEL logging
|
|
10
|
+
* - `DEBUG=sel:evaluate` — only evaluate flow
|
|
11
|
+
* - `DEBUG=sel:execute:*` — all execution sub-loggers
|
|
12
|
+
*
|
|
13
|
+
* @param namespace - Module namespace (e.g. "evaluate", "execute:round")
|
|
14
|
+
*/
|
|
15
|
+
const createLogger = (namespace) => (0, debug.default)(`sel:${namespace}`);
|
|
16
|
+
//#endregion
|
|
17
|
+
exports.createLogger = createLogger;
|
package/dist/debug.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import createDebug from "debug";
|
|
2
|
+
//#region src/debug.ts
|
|
3
|
+
/**
|
|
4
|
+
* Creates a namespaced debug logger for a SEL module.
|
|
5
|
+
*
|
|
6
|
+
* Enable via the `DEBUG` environment variable:
|
|
7
|
+
* - `DEBUG=sel:*` — all SEL logging
|
|
8
|
+
* - `DEBUG=sel:evaluate` — only evaluate flow
|
|
9
|
+
* - `DEBUG=sel:execute:*` — all execution sub-loggers
|
|
10
|
+
*
|
|
11
|
+
* @param namespace - Module namespace (e.g. "evaluate", "execute:round")
|
|
12
|
+
*/
|
|
13
|
+
const createLogger = (namespace) => createDebug(`sel:${namespace}`);
|
|
14
|
+
//#endregion
|
|
15
|
+
export { createLogger };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/environment/context.ts
|
|
2
|
+
const normalizeContextForEvaluation = (context, variableTypes, codecRegistry) => {
|
|
3
|
+
const normalized = {};
|
|
4
|
+
for (const [key, value] of Object.entries(context)) {
|
|
5
|
+
const type = variableTypes.get(key);
|
|
6
|
+
normalized[key] = codecRegistry.resolve(type ?? "dyn").parse(value);
|
|
7
|
+
}
|
|
8
|
+
return normalized;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
exports.normalizeContextForEvaluation = normalizeContextForEvaluation;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/environment/context.ts
|
|
2
|
+
const normalizeContextForEvaluation = (context, variableTypes, codecRegistry) => {
|
|
3
|
+
const normalized = {};
|
|
4
|
+
for (const [key, value] of Object.entries(context)) {
|
|
5
|
+
const type = variableTypes.get(key);
|
|
6
|
+
normalized[key] = codecRegistry.resolve(type ?? "dyn").parse(value);
|
|
7
|
+
}
|
|
8
|
+
return normalized;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { normalizeContextForEvaluation };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_replay_cache = require("./replay-cache.cjs");
|
|
3
|
+
const require_debug = require("../debug.cjs");
|
|
4
|
+
const require_errors = require("../errors/errors.cjs");
|
|
5
|
+
require("../errors/index.cjs");
|
|
6
|
+
let viem_actions = require("viem/actions");
|
|
7
|
+
//#region src/environment/contract-caller.ts
|
|
8
|
+
const debug = require_debug.createLogger("contract-caller");
|
|
9
|
+
/**
|
|
10
|
+
* Tracks total RPC calls (pre-executed + live) across a single evaluation.
|
|
11
|
+
* Create one per `evaluate()` call and pass it through the handler closure.
|
|
12
|
+
*/
|
|
13
|
+
var CallCounter = class {
|
|
14
|
+
count = 0;
|
|
15
|
+
constructor(maxCalls, initialCount = 0) {
|
|
16
|
+
this.maxCalls = maxCalls;
|
|
17
|
+
this.count = initialCount;
|
|
18
|
+
}
|
|
19
|
+
increment(contractName, methodName) {
|
|
20
|
+
this.count++;
|
|
21
|
+
if (this.count > this.maxCalls) throw new require_errors.ExecutionLimitError(`Execution limit exceeded: ${String(this.count)} calls exceeds maxCalls (${String(this.maxCalls)})`, {
|
|
22
|
+
limitType: "maxCalls",
|
|
23
|
+
limit: this.maxCalls,
|
|
24
|
+
actual: this.count
|
|
25
|
+
});
|
|
26
|
+
debug("call count: %d/%d (%s.%s)", this.count, this.maxCalls, contractName, methodName);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const executeContractCall = async (contract, method, args, options) => {
|
|
30
|
+
const normalizedArgs = options.codecRegistry ? args.map((arg, i) => options.codecRegistry.encode(method.params[i]?.type ?? "dyn", arg)) : args;
|
|
31
|
+
if (options.executionCache) {
|
|
32
|
+
const callId = require_replay_cache.createReplayCallId(contract.name, method.name, normalizedArgs);
|
|
33
|
+
if (options.executionCache.has(callId)) {
|
|
34
|
+
debug("cache hit: %s.%s", contract.name, method.name);
|
|
35
|
+
return options.executionCache.get(callId);
|
|
36
|
+
}
|
|
37
|
+
debug("cache miss: %s.%s — executing live", contract.name, method.name);
|
|
38
|
+
}
|
|
39
|
+
if (!options.client) throw new require_errors.SELContractError("No client provided for contract call. Provide a client in SELRuntime config or evaluate() context.", {
|
|
40
|
+
contractName: contract.name,
|
|
41
|
+
methodName: method.name
|
|
42
|
+
});
|
|
43
|
+
options.callCounter?.increment(contract.name, method.name);
|
|
44
|
+
try {
|
|
45
|
+
return await (0, viem_actions.readContract)(options.client, {
|
|
46
|
+
address: contract.address,
|
|
47
|
+
abi: [method.abi],
|
|
48
|
+
functionName: method.name,
|
|
49
|
+
args: normalizedArgs
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof require_errors.SELContractError) throw error;
|
|
53
|
+
throw new require_errors.SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
|
|
54
|
+
cause: error,
|
|
55
|
+
contractName: contract.name,
|
|
56
|
+
methodName: method.name
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const resolveExecutionBlockNumber = async (client) => {
|
|
61
|
+
const clientWithGetBlockNumber = client;
|
|
62
|
+
if (typeof clientWithGetBlockNumber.getBlockNumber === "function") return await clientWithGetBlockNumber.getBlockNumber();
|
|
63
|
+
if (typeof clientWithGetBlockNumber.request === "function") return;
|
|
64
|
+
return 0n;
|
|
65
|
+
};
|
|
66
|
+
const buildContractInfoMap = (contracts) => {
|
|
67
|
+
const map = /* @__PURE__ */ new Map();
|
|
68
|
+
for (const contract of contracts) {
|
|
69
|
+
const abi = contract.methods.map((m) => m.abi);
|
|
70
|
+
map.set(contract.name, {
|
|
71
|
+
abi,
|
|
72
|
+
address: contract.address
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return map;
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
exports.CallCounter = CallCounter;
|
|
79
|
+
exports.buildContractInfoMap = buildContractInfoMap;
|
|
80
|
+
exports.executeContractCall = executeContractCall;
|
|
81
|
+
exports.resolveExecutionBlockNumber = resolveExecutionBlockNumber;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createReplayCallId } from "./replay-cache.mjs";
|
|
2
|
+
import { createLogger } from "../debug.mjs";
|
|
3
|
+
import { ExecutionLimitError, SELContractError } from "../errors/errors.mjs";
|
|
4
|
+
import "../errors/index.mjs";
|
|
5
|
+
import { readContract } from "viem/actions";
|
|
6
|
+
//#region src/environment/contract-caller.ts
|
|
7
|
+
const debug = createLogger("contract-caller");
|
|
8
|
+
/**
|
|
9
|
+
* Tracks total RPC calls (pre-executed + live) across a single evaluation.
|
|
10
|
+
* Create one per `evaluate()` call and pass it through the handler closure.
|
|
11
|
+
*/
|
|
12
|
+
var CallCounter = class {
|
|
13
|
+
count = 0;
|
|
14
|
+
constructor(maxCalls, initialCount = 0) {
|
|
15
|
+
this.maxCalls = maxCalls;
|
|
16
|
+
this.count = initialCount;
|
|
17
|
+
}
|
|
18
|
+
increment(contractName, methodName) {
|
|
19
|
+
this.count++;
|
|
20
|
+
if (this.count > this.maxCalls) throw new ExecutionLimitError(`Execution limit exceeded: ${String(this.count)} calls exceeds maxCalls (${String(this.maxCalls)})`, {
|
|
21
|
+
limitType: "maxCalls",
|
|
22
|
+
limit: this.maxCalls,
|
|
23
|
+
actual: this.count
|
|
24
|
+
});
|
|
25
|
+
debug("call count: %d/%d (%s.%s)", this.count, this.maxCalls, contractName, methodName);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const executeContractCall = async (contract, method, args, options) => {
|
|
29
|
+
const normalizedArgs = options.codecRegistry ? args.map((arg, i) => options.codecRegistry.encode(method.params[i]?.type ?? "dyn", arg)) : args;
|
|
30
|
+
if (options.executionCache) {
|
|
31
|
+
const callId = createReplayCallId(contract.name, method.name, normalizedArgs);
|
|
32
|
+
if (options.executionCache.has(callId)) {
|
|
33
|
+
debug("cache hit: %s.%s", contract.name, method.name);
|
|
34
|
+
return options.executionCache.get(callId);
|
|
35
|
+
}
|
|
36
|
+
debug("cache miss: %s.%s — executing live", contract.name, method.name);
|
|
37
|
+
}
|
|
38
|
+
if (!options.client) throw new SELContractError("No client provided for contract call. Provide a client in SELRuntime config or evaluate() context.", {
|
|
39
|
+
contractName: contract.name,
|
|
40
|
+
methodName: method.name
|
|
41
|
+
});
|
|
42
|
+
options.callCounter?.increment(contract.name, method.name);
|
|
43
|
+
try {
|
|
44
|
+
return await readContract(options.client, {
|
|
45
|
+
address: contract.address,
|
|
46
|
+
abi: [method.abi],
|
|
47
|
+
functionName: method.name,
|
|
48
|
+
args: normalizedArgs
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error instanceof SELContractError) throw error;
|
|
52
|
+
throw new SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
|
|
53
|
+
cause: error,
|
|
54
|
+
contractName: contract.name,
|
|
55
|
+
methodName: method.name
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const resolveExecutionBlockNumber = async (client) => {
|
|
60
|
+
const clientWithGetBlockNumber = client;
|
|
61
|
+
if (typeof clientWithGetBlockNumber.getBlockNumber === "function") return await clientWithGetBlockNumber.getBlockNumber();
|
|
62
|
+
if (typeof clientWithGetBlockNumber.request === "function") return;
|
|
63
|
+
return 0n;
|
|
64
|
+
};
|
|
65
|
+
const buildContractInfoMap = (contracts) => {
|
|
66
|
+
const map = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const contract of contracts) {
|
|
68
|
+
const abi = contract.methods.map((m) => m.abi);
|
|
69
|
+
map.set(contract.name, {
|
|
70
|
+
abi,
|
|
71
|
+
address: contract.address
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return map;
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { CallCounter, buildContractInfoMap, executeContractCall, resolveExecutionBlockNumber };
|