@seljs/runtime 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE.md +190 -0
  3. package/README.md +5 -0
  4. package/dist/analysis/call-collector.d.ts +20 -0
  5. package/dist/analysis/call-collector.d.ts.map +1 -0
  6. package/dist/analysis/call-collector.js +272 -0
  7. package/dist/analysis/dependency-analyzer.d.ts +14 -0
  8. package/dist/analysis/dependency-analyzer.d.ts.map +1 -0
  9. package/dist/analysis/dependency-analyzer.js +76 -0
  10. package/dist/analysis/index.d.ts +2 -0
  11. package/dist/analysis/index.d.ts.map +1 -0
  12. package/dist/analysis/index.js +1 -0
  13. package/dist/analysis/round-planner.d.ts +32 -0
  14. package/dist/analysis/round-planner.d.ts.map +1 -0
  15. package/dist/analysis/round-planner.js +69 -0
  16. package/dist/analysis/types.d.ts +113 -0
  17. package/dist/analysis/types.d.ts.map +1 -0
  18. package/dist/analysis/types.js +1 -0
  19. package/dist/debug.d.ts +13 -0
  20. package/dist/debug.d.ts.map +1 -0
  21. package/dist/debug.js +12 -0
  22. package/dist/environment/context.d.ts +3 -0
  23. package/dist/environment/context.d.ts.map +1 -0
  24. package/dist/environment/context.js +8 -0
  25. package/dist/environment/contract-caller.d.ts +25 -0
  26. package/dist/environment/contract-caller.d.ts.map +1 -0
  27. package/dist/environment/contract-caller.js +85 -0
  28. package/dist/environment/environment.d.ts +81 -0
  29. package/dist/environment/environment.d.ts.map +1 -0
  30. package/dist/environment/environment.js +279 -0
  31. package/dist/environment/error-wrapper.d.ts +11 -0
  32. package/dist/environment/error-wrapper.d.ts.map +1 -0
  33. package/dist/environment/error-wrapper.js +33 -0
  34. package/dist/environment/index.d.ts +3 -0
  35. package/dist/environment/index.d.ts.map +1 -0
  36. package/dist/environment/index.js +2 -0
  37. package/dist/environment/replay-cache.d.ts +23 -0
  38. package/dist/environment/replay-cache.d.ts.map +1 -0
  39. package/dist/environment/replay-cache.js +51 -0
  40. package/dist/environment/types.d.ts +57 -0
  41. package/dist/environment/types.d.ts.map +1 -0
  42. package/dist/environment/types.js +1 -0
  43. package/dist/errors/errors.d.ts +63 -0
  44. package/dist/errors/errors.d.ts.map +1 -0
  45. package/dist/errors/errors.js +63 -0
  46. package/dist/errors/index.d.ts +2 -0
  47. package/dist/errors/index.d.ts.map +1 -0
  48. package/dist/errors/index.js +1 -0
  49. package/dist/execution/index.d.ts +2 -0
  50. package/dist/execution/index.d.ts.map +1 -0
  51. package/dist/execution/index.js +1 -0
  52. package/dist/execution/multi-round-executor.d.ts +17 -0
  53. package/dist/execution/multi-round-executor.d.ts.map +1 -0
  54. package/dist/execution/multi-round-executor.js +47 -0
  55. package/dist/execution/multicall-batcher.d.ts +14 -0
  56. package/dist/execution/multicall-batcher.d.ts.map +1 -0
  57. package/dist/execution/multicall-batcher.js +53 -0
  58. package/dist/execution/multicall.d.ts +42 -0
  59. package/dist/execution/multicall.d.ts.map +1 -0
  60. package/dist/execution/multicall.js +29 -0
  61. package/dist/execution/result-cache.d.ts +47 -0
  62. package/dist/execution/result-cache.d.ts.map +1 -0
  63. package/dist/execution/result-cache.js +65 -0
  64. package/dist/execution/round-executor.d.ts +18 -0
  65. package/dist/execution/round-executor.d.ts.map +1 -0
  66. package/dist/execution/round-executor.js +95 -0
  67. package/dist/execution/types.d.ts +55 -0
  68. package/dist/execution/types.d.ts.map +1 -0
  69. package/dist/execution/types.js +1 -0
  70. package/dist/factory.d.ts +3 -0
  71. package/dist/factory.d.ts.map +1 -0
  72. package/dist/factory.js +2 -0
  73. package/dist/index.d.ts +10 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +7 -0
  76. package/package.json +76 -0
@@ -0,0 +1,69 @@
1
+ import { createLogger } from "../debug.js";
2
+ import { ExecutionLimitError } from "../errors/index.js";
3
+ const debug = createLogger("analysis:plan");
4
+ /**
5
+ * Plans execution rounds from a dependency graph using topological sorting.
6
+ *
7
+ * Groups independent calls into parallel rounds, ensuring all dependencies
8
+ * of a call are executed in previous rounds. Enforces limits on the number
9
+ * of rounds and total calls to prevent excessive execution.
10
+ *
11
+ * @param graph Dependency graph representing calls and their relationships.
12
+ * @param limits Optional limits for maximum rounds and total calls.
13
+ * @returns Execution plan containing planned rounds, total calls, and maximum depth.
14
+ * @throws ExecutionLimitError If total calls exceed maxCalls or rounds exceed maxRounds.
15
+ */
16
+ export const planRounds = (graph, limits = {}) => {
17
+ const maxRounds = limits.maxRounds ?? 10;
18
+ const maxCalls = limits.maxCalls ?? 100;
19
+ const totalCalls = graph.nodes.size;
20
+ if (totalCalls > maxCalls) {
21
+ throw new ExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, { limitType: "maxCalls", limit: maxCalls, actual: totalCalls });
22
+ }
23
+ if (totalCalls === 0) {
24
+ return { rounds: [], totalCalls: 0, maxDepth: 0 };
25
+ }
26
+ const inDegree = new Map();
27
+ for (const nodeId of graph.nodes.keys()) {
28
+ inDegree.set(nodeId, graph.edges.get(nodeId)?.size ?? 0);
29
+ }
30
+ const rounds = [];
31
+ const processed = new Set();
32
+ while (processed.size < totalCalls) {
33
+ const readyNodes = [];
34
+ for (const [nodeId, degree] of inDegree) {
35
+ if (degree === 0 && !processed.has(nodeId)) {
36
+ readyNodes.push(nodeId);
37
+ }
38
+ }
39
+ if (readyNodes.length === 0) {
40
+ break;
41
+ }
42
+ if (rounds.length >= maxRounds) {
43
+ throw new ExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
44
+ limitType: "maxRounds",
45
+ limit: maxRounds,
46
+ actual: rounds.length + 1,
47
+ });
48
+ }
49
+ const round = {
50
+ roundNumber: rounds.length,
51
+ calls: readyNodes
52
+ .map((id) => graph.nodes.get(id)?.call)
53
+ .filter((c) => c !== undefined),
54
+ };
55
+ rounds.push(round);
56
+ for (const nodeId of readyNodes) {
57
+ processed.add(nodeId);
58
+ const node = graph.nodes.get(nodeId);
59
+ if (!node) {
60
+ continue;
61
+ }
62
+ for (const dependentId of node.dependedOnBy) {
63
+ inDegree.set(dependentId, (inDegree.get(dependentId) ?? 0) - 1);
64
+ }
65
+ }
66
+ }
67
+ debug("planned %d rounds for %d calls (max depth=%d)", rounds.length, totalCalls, rounds.length);
68
+ return { rounds, totalCalls, maxDepth: rounds.length };
69
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Represents a contract call found in AST traversal.
3
+ * Contains all information needed to execute the call and track dependencies.
4
+ */
5
+ export interface CollectedCall {
6
+ /**
7
+ * Unique identifier: "contract:method:serializedArgs"
8
+ */
9
+ id: string;
10
+ /**
11
+ * Contract name: e.g., "erc20_usdc"
12
+ */
13
+ contract: string;
14
+ /**
15
+ * Method name: e.g., "balanceOf"
16
+ */
17
+ method: string;
18
+ /**
19
+ * Arguments with type and dependency information
20
+ */
21
+ args: CallArgument[];
22
+ /**
23
+ * Reference to original AST node for error reporting
24
+ */
25
+ astNode: unknown;
26
+ }
27
+ /**
28
+ * Argument to a contract call with type information.
29
+ * Tracks whether the argument is a literal, variable, or result of another call.
30
+ */
31
+ export interface CallArgument {
32
+ /**
33
+ * Type of argument: literal value, variable reference, or call result
34
+ */
35
+ type: "literal" | "variable" | "call_result";
36
+ /**
37
+ * Literal value (when type is "literal")
38
+ */
39
+ value?: unknown;
40
+ /**
41
+ * Variable name (when type is "variable")
42
+ */
43
+ variableName?: string;
44
+ /**
45
+ * Call ID this depends on (when type is "call_result")
46
+ */
47
+ dependsOnCallId?: string;
48
+ }
49
+ /**
50
+ * Dependency graph for all collected calls.
51
+ * Maps call IDs to their nodes and dependency relationships.
52
+ */
53
+ export interface DependencyGraph {
54
+ /**
55
+ * Call ID to graph node mapping
56
+ */
57
+ nodes: Map<string, GraphNode>;
58
+ /**
59
+ * Call ID to set of call IDs it depends on
60
+ */
61
+ edges: Map<string, Set<string>>;
62
+ }
63
+ /**
64
+ * Single node in the dependency graph.
65
+ * Tracks both incoming and outgoing dependencies.
66
+ */
67
+ export interface GraphNode {
68
+ /**
69
+ * The original collected call
70
+ */
71
+ call: CollectedCall;
72
+ /**
73
+ * Call IDs this call depends on (must complete first)
74
+ */
75
+ dependsOn: string[];
76
+ /**
77
+ * Call IDs that depend on this call (will execute after)
78
+ */
79
+ dependedOnBy: string[];
80
+ }
81
+ /**
82
+ * Execution plan with rounds of parallel calls.
83
+ * Result of topological sorting the dependency graph.
84
+ */
85
+ export interface ExecutionPlan {
86
+ /**
87
+ * Rounds of calls to execute in sequence
88
+ */
89
+ rounds: ExecutionRound[];
90
+ /**
91
+ * Total number of calls across all rounds
92
+ */
93
+ totalCalls: number;
94
+ /**
95
+ * Number of rounds (maximum dependency depth)
96
+ */
97
+ maxDepth: number;
98
+ }
99
+ /**
100
+ * Single round of calls that can execute in parallel.
101
+ * All calls in a round have no dependencies on each other.
102
+ */
103
+ export interface ExecutionRound {
104
+ /**
105
+ * Round number (0-indexed)
106
+ */
107
+ roundNumber: number;
108
+ /**
109
+ * Calls that can execute in parallel this round
110
+ */
111
+ calls: CollectedCall[];
112
+ }
113
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analysis/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,YAAY,EAAE,CAAC;IAErB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,CAAC;IAE7C;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAE9B;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB;;OAEG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;OAEG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB;;OAEG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,MAAM,EAAE,cAAc,EAAE,CAAC;IAEzB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,EAAE,aAAa,EAAE,CAAC;CACvB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import createDebug from "debug";
2
+ /**
3
+ * Creates a namespaced debug logger for a SEL module.
4
+ *
5
+ * Enable via the `DEBUG` environment variable:
6
+ * - `DEBUG=sel:*` — all SEL logging
7
+ * - `DEBUG=sel:evaluate` — only evaluate flow
8
+ * - `DEBUG=sel:execute:*` — all execution sub-loggers
9
+ *
10
+ * @param namespace - Module namespace (e.g. "evaluate", "execute:round")
11
+ */
12
+ export declare const createLogger: (namespace: string) => createDebug.Debugger;
13
+ //# sourceMappingURL=debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,OAAO,CAAC;AAEhC;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,KAAG,WAAW,CAAC,QAC7B,CAAC"}
package/dist/debug.js ADDED
@@ -0,0 +1,12 @@
1
+ import createDebug from "debug";
2
+ /**
3
+ * Creates a namespaced debug logger for a SEL module.
4
+ *
5
+ * Enable via the `DEBUG` environment variable:
6
+ * - `DEBUG=sel:*` — all SEL logging
7
+ * - `DEBUG=sel:evaluate` — only evaluate flow
8
+ * - `DEBUG=sel:execute:*` — all execution sub-loggers
9
+ *
10
+ * @param namespace - Module namespace (e.g. "evaluate", "execute:round")
11
+ */
12
+ export const createLogger = (namespace) => createDebug(`sel:${namespace}`);
@@ -0,0 +1,3 @@
1
+ import type { CelCodecRegistry } from "@seljs/checker";
2
+ export declare const normalizeContextForEvaluation: (context: Record<string, unknown>, variableTypes: Map<string, string>, codecRegistry: CelCodecRegistry) => Record<string, unknown>;
3
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/environment/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,eAAe,gBAAgB,KAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CASxB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export const normalizeContextForEvaluation = (context, variableTypes, codecRegistry) => {
2
+ const normalized = {};
3
+ for (const [key, value] of Object.entries(context)) {
4
+ const type = variableTypes.get(key);
5
+ normalized[key] = codecRegistry.resolve(type ?? "dyn").parse(value);
6
+ }
7
+ return normalized;
8
+ };
@@ -0,0 +1,25 @@
1
+ import type { CelCodecRegistry } from "@seljs/checker";
2
+ import type { ContractSchema, MethodSchema } from "@seljs/schema";
3
+ import type { Abi, Address, PublicClient } from "viem";
4
+ /**
5
+ * Tracks total RPC calls (pre-executed + live) across a single evaluation.
6
+ * Create one per `evaluate()` call and pass it through the handler closure.
7
+ */
8
+ export declare class CallCounter {
9
+ private readonly maxCalls;
10
+ private count;
11
+ constructor(maxCalls: number, initialCount?: number);
12
+ increment(contractName: string, methodName: string): void;
13
+ }
14
+ export declare const executeContractCall: (contract: ContractSchema, method: MethodSchema, args: unknown[], options: {
15
+ executionCache?: Map<string, unknown>;
16
+ client?: PublicClient;
17
+ codecRegistry?: CelCodecRegistry;
18
+ callCounter?: CallCounter;
19
+ }) => Promise<unknown>;
20
+ export declare const resolveExecutionBlockNumber: (client: PublicClient) => Promise<bigint | undefined>;
21
+ export declare const buildContractInfoMap: (contracts: ContractSchema[]) => Map<string, {
22
+ abi: Abi;
23
+ address: Address;
24
+ }>;
25
+ //# sourceMappingURL=contract-caller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-caller.d.ts","sourceRoot":"","sources":["../../src/environment/contract-caller.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAIvD;;;GAGG;AACH,qBAAa,WAAW;IAGtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAF1B,OAAO,CAAC,KAAK,CAAK;gBAEA,QAAQ,EAAE,MAAM,EACjC,YAAY,GAAE,MAAU;IAKlB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;CAqBhE;AAED,eAAO,MAAM,mBAAmB,GAC/B,UAAU,cAAc,EACxB,QAAQ,YAAY,EACpB,MAAM,OAAO,EAAE,EACf,SAAS;IACR,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC1B,KAEC,OAAO,CAAC,OAAO,CAwDjB,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACvC,QAAQ,YAAY,KAClB,OAAO,CAAC,MAAM,GAAG,SAAS,CAe5B,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAChC,WAAW,cAAc,EAAE,KACzB,GAAG,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAQ5C,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { readContract } from "viem/actions";
2
+ import { createReplayCallId } from "./replay-cache.js";
3
+ import { createLogger } from "../debug.js";
4
+ import { ExecutionLimitError, SELContractError } from "../errors/index.js";
5
+ const debug = createLogger("contract-caller");
6
+ /**
7
+ * Tracks total RPC calls (pre-executed + live) across a single evaluation.
8
+ * Create one per `evaluate()` call and pass it through the handler closure.
9
+ */
10
+ export class CallCounter {
11
+ maxCalls;
12
+ count = 0;
13
+ constructor(maxCalls, initialCount = 0) {
14
+ this.maxCalls = maxCalls;
15
+ this.count = initialCount;
16
+ }
17
+ increment(contractName, methodName) {
18
+ this.count++;
19
+ if (this.count > this.maxCalls) {
20
+ 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
+ }
26
+ debug("call count: %d/%d (%s.%s)", this.count, this.maxCalls, contractName, methodName);
27
+ }
28
+ }
29
+ export const executeContractCall = async (contract, method, args, options) => {
30
+ const normalizedArgs = options.codecRegistry
31
+ ? args.map((arg, i) => options.codecRegistry.encode(method.params[i]?.type ?? "dyn", arg))
32
+ : args;
33
+ if (options.executionCache) {
34
+ const callId = createReplayCallId(contract.name, method.name, normalizedArgs);
35
+ if (options.executionCache.has(callId)) {
36
+ debug("cache hit: %s.%s", contract.name, method.name);
37
+ return options.executionCache.get(callId);
38
+ }
39
+ debug("cache miss: %s.%s — executing live", contract.name, method.name);
40
+ }
41
+ if (!options.client) {
42
+ throw new SELContractError("No client provided for contract call. Provide a client in SELRuntime config or evaluate() context.", {
43
+ contractName: contract.name,
44
+ methodName: method.name,
45
+ });
46
+ }
47
+ // Count live RPC calls against the limit
48
+ options.callCounter?.increment(contract.name, method.name);
49
+ try {
50
+ return await readContract(options.client, {
51
+ address: contract.address,
52
+ abi: [method.abi],
53
+ functionName: method.name,
54
+ args: normalizedArgs,
55
+ });
56
+ }
57
+ catch (error) {
58
+ if (error instanceof SELContractError) {
59
+ throw error;
60
+ }
61
+ throw new SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
62
+ cause: error,
63
+ contractName: contract.name,
64
+ methodName: method.name,
65
+ });
66
+ }
67
+ };
68
+ export const resolveExecutionBlockNumber = async (client) => {
69
+ const clientWithGetBlockNumber = client;
70
+ if (typeof clientWithGetBlockNumber.getBlockNumber === "function") {
71
+ return await clientWithGetBlockNumber.getBlockNumber();
72
+ }
73
+ if (typeof clientWithGetBlockNumber.request === "function") {
74
+ return undefined;
75
+ }
76
+ return 0n;
77
+ };
78
+ export const buildContractInfoMap = (contracts) => {
79
+ const map = new Map();
80
+ for (const contract of contracts) {
81
+ const abi = contract.methods.map((m) => m.abi);
82
+ map.set(contract.name, { abi, address: contract.address });
83
+ }
84
+ return map;
85
+ };
@@ -0,0 +1,81 @@
1
+ import type { SELRuntimeConfig } from "./types.js";
2
+ import type { EvaluateOptions, EvaluateResult } from "../execution/types.js";
3
+ import type { TypeCheckResult } from "@marcbachmann/cel-js";
4
+ export declare class SELRuntime {
5
+ private readonly env;
6
+ private readonly client?;
7
+ private readonly schema;
8
+ private readonly variableTypes;
9
+ private readonly maxRounds;
10
+ private readonly maxCalls;
11
+ private readonly multicallOptions?;
12
+ private readonly contractBindings;
13
+ private readonly codecRegistry;
14
+ /**
15
+ * Per-evaluation mutable state.
16
+ *
17
+ * These fields are set before CEL evaluation and cleared in `finally`.
18
+ * They exist because the contract call handler closure (created in the
19
+ * constructor) needs access to per-evaluation context, but CEL's
20
+ * `Environment.parse()` returns a function that doesn't accept extra
21
+ * parameters beyond the variable bindings.
22
+ *
23
+ * Thread safety: The `mutex` field serializes concurrent `evaluate()`
24
+ * calls, ensuring these fields are never accessed concurrently.
25
+ *
26
+ * TODO: Consider refactoring to pass an EvaluationContext object through
27
+ * the handler closure instead of mutating instance state. This would
28
+ * eliminate the need for the mutex and make the code more testable.
29
+ * See .omc/drafts/context-object-spike.md for preliminary analysis.
30
+ */
31
+ private currentCache?;
32
+ private currentClient?;
33
+ private currentCallCounter?;
34
+ /** Mutex to serialize concurrent evaluate() calls (protects current* fields) */
35
+ private mutex;
36
+ /**
37
+ * Creates a new immutable SEL runtime with Solidity types pre-registered.
38
+ *
39
+ * Initializes the underlying CEL runtime, registers all Solidity primitive types,
40
+ * and processes any contracts and variables from the schema. After construction,
41
+ * the environment is fully configured and immutable.
42
+ *
43
+ * @param config - Configuration containing the SEL schema, optional viem client,
44
+ * multicall settings, and CEL/execution limits (maxRounds defaults to 10, maxCalls to 100)
45
+ */
46
+ constructor(config: SELRuntimeConfig);
47
+ /**
48
+ * Type-checks an expression against registered variables and contract methods.
49
+ *
50
+ * @param expression - A CEL expression string to type-check
51
+ * @returns The type-check result containing validity, inferred type, and any errors
52
+ * @throws {@link SELTypeError} If the expression contains unrecoverable type errors
53
+ */
54
+ check(expression: string): TypeCheckResult;
55
+ /**
56
+ * Evaluates a SEL expression, executing any embedded contract calls on-chain.
57
+ *
58
+ * When contract calls are present, the full pipeline runs:
59
+ * 1. Parse the expression and collect contract calls from the AST
60
+ * 2. Type-check against registered variables and contract methods
61
+ * 3. Build a dependency graph and plan execution rounds
62
+ * 4. Execute contract calls via multicall3 batching at a pinned block number
63
+ * 5. Evaluate the CEL expression with resolved contract results and context values
64
+ * 6. Unwrap Solidity wrapper types back to native JS values (BigInt, string, etc.)
65
+ *
66
+ * @param expression A CEL expression string
67
+ * @param context Variable bindings for evaluation
68
+ * @param options Optional client override
69
+ * @returns An {@link EvaluateResult} containing the value and optional execution metadata
70
+ * @throws {@link SELParseError} If the expression has invalid syntax
71
+ * @throws {@link SELTypeError} If type-checking fails
72
+ * @throws {@link SELContractError} If a contract call fails or no client is available
73
+ * @throws {@link SELEvaluationError} If CEL evaluation fails
74
+ */
75
+ evaluate<T = unknown>(expression: string, context?: Record<string, unknown>, options?: EvaluateOptions): Promise<EvaluateResult<T>>;
76
+ private planExecution;
77
+ private executeContractCalls;
78
+ private doEvaluate;
79
+ private findContract;
80
+ }
81
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/environment/environment.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,KAAK,EACX,eAAe,EACf,cAAc,EAEd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAezE,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAmB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IAEjD;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,YAAY,CAAC,CAAuB;IAC5C,OAAO,CAAC,aAAa,CAAC,CAAe;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAc;IAEzC,gFAAgF;IAChF,OAAO,CAAC,KAAK,CAAqB;IAElC;;;;;;;;;OASG;gBACgB,MAAM,EAAE,gBAAgB;IAgE3C;;;;;;OAMG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe;IAQjD;;;;;;;;;;;;;;;;;;;OAmBG;IACU,QAAQ,CAAC,CAAC,GAAG,OAAO,EAChC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAiB7B,OAAO,CAAC,aAAa;YA+CP,oBAAoB;YA0FpB,UAAU;IAyExB,OAAO,CAAC,YAAY;CAGpB"}