@seljs/checker 1.0.0 → 1.0.1-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/checker/checker.cjs +482 -0
  3. package/dist/checker/checker.d.cts +176 -0
  4. package/dist/checker/checker.d.mts +176 -0
  5. package/dist/checker/checker.mjs +481 -0
  6. package/dist/checker/diagnostics.cjs +66 -0
  7. package/dist/checker/diagnostics.mjs +66 -0
  8. package/dist/checker/index.cjs +2 -0
  9. package/dist/checker/index.d.mts +2 -0
  10. package/dist/checker/index.mjs +3 -0
  11. package/dist/checker/type-compatibility.cjs +70 -0
  12. package/dist/checker/type-compatibility.d.cts +8 -0
  13. package/dist/checker/type-compatibility.d.mts +8 -0
  14. package/dist/checker/type-compatibility.mjs +69 -0
  15. package/dist/constants.cjs +14 -0
  16. package/dist/constants.d.cts +7 -0
  17. package/dist/constants.d.mts +7 -0
  18. package/dist/constants.mjs +13 -0
  19. package/dist/debug.cjs +7 -0
  20. package/dist/debug.mjs +5 -0
  21. package/dist/environment/codec-registry.cjs +109 -0
  22. package/dist/environment/codec-registry.d.cts +45 -0
  23. package/dist/environment/codec-registry.d.mts +45 -0
  24. package/dist/environment/codec-registry.mjs +108 -0
  25. package/dist/environment/hydrate.cjs +163 -0
  26. package/dist/environment/hydrate.d.cts +52 -0
  27. package/dist/environment/hydrate.d.mts +52 -0
  28. package/dist/environment/hydrate.mjs +160 -0
  29. package/dist/environment/index.cjs +4 -0
  30. package/dist/environment/index.d.mts +4 -0
  31. package/dist/environment/index.mjs +5 -0
  32. package/dist/environment/register-types.cjs +107 -0
  33. package/dist/environment/register-types.d.cts +15 -0
  34. package/dist/environment/register-types.d.mts +15 -0
  35. package/dist/environment/register-types.mjs +106 -0
  36. package/dist/environment/value-wrappers.cjs +44 -0
  37. package/dist/environment/value-wrappers.d.cts +20 -0
  38. package/dist/environment/value-wrappers.d.mts +20 -0
  39. package/dist/environment/value-wrappers.mjs +41 -0
  40. package/dist/index.cjs +27 -0
  41. package/dist/index.d.cts +11 -0
  42. package/dist/index.d.mts +11 -0
  43. package/dist/index.mjs +13 -0
  44. package/dist/rules/defaults/deferred-call.cjs +107 -0
  45. package/dist/rules/defaults/deferred-call.mjs +106 -0
  46. package/dist/rules/defaults/index.cjs +6 -0
  47. package/dist/rules/defaults/index.mjs +7 -0
  48. package/dist/rules/defaults/no-constant-condition.cjs +31 -0
  49. package/dist/rules/defaults/no-constant-condition.mjs +31 -0
  50. package/dist/rules/defaults/no-mixed-operators.cjs +39 -0
  51. package/dist/rules/defaults/no-mixed-operators.mjs +39 -0
  52. package/dist/rules/defaults/no-redundant-bool.cjs +26 -0
  53. package/dist/rules/defaults/no-redundant-bool.mjs +26 -0
  54. package/dist/rules/defaults/no-self-comparison.cjs +44 -0
  55. package/dist/rules/defaults/no-self-comparison.mjs +43 -0
  56. package/dist/rules/defaults/require-type.cjs +18 -0
  57. package/dist/rules/defaults/require-type.mjs +18 -0
  58. package/dist/rules/facade.cjs +31 -0
  59. package/dist/rules/facade.d.cts +20 -0
  60. package/dist/rules/facade.d.mts +20 -0
  61. package/dist/rules/facade.mjs +31 -0
  62. package/dist/rules/index.cjs +2 -0
  63. package/dist/rules/index.d.mts +3 -0
  64. package/dist/rules/index.mjs +3 -0
  65. package/dist/rules/runner.cjs +40 -0
  66. package/dist/rules/runner.d.cts +27 -0
  67. package/dist/rules/runner.d.mts +27 -0
  68. package/dist/rules/runner.mjs +40 -0
  69. package/dist/rules/types.d.cts +77 -0
  70. package/dist/rules/types.d.mts +77 -0
  71. package/dist/utils/ast-utils.cjs +164 -0
  72. package/dist/utils/ast-utils.mjs +162 -0
  73. package/package.json +23 -16
  74. package/dist/checker/checker.d.ts +0 -173
  75. package/dist/checker/checker.js +0 -567
  76. package/dist/checker/diagnostics.d.ts +0 -10
  77. package/dist/checker/diagnostics.js +0 -80
  78. package/dist/checker/index.d.ts +0 -2
  79. package/dist/checker/index.js +0 -2
  80. package/dist/checker/type-compatibility.d.ts +0 -16
  81. package/dist/checker/type-compatibility.js +0 -59
  82. package/dist/constants.d.ts +0 -4
  83. package/dist/constants.js +0 -10
  84. package/dist/debug.d.ts +0 -2
  85. package/dist/debug.js +0 -2
  86. package/dist/environment/codec-registry.d.ts +0 -42
  87. package/dist/environment/codec-registry.js +0 -146
  88. package/dist/environment/hydrate.d.ts +0 -48
  89. package/dist/environment/hydrate.js +0 -198
  90. package/dist/environment/index.d.ts +0 -4
  91. package/dist/environment/index.js +0 -4
  92. package/dist/environment/register-types.d.ts +0 -14
  93. package/dist/environment/register-types.js +0 -154
  94. package/dist/environment/value-wrappers.d.ts +0 -17
  95. package/dist/environment/value-wrappers.js +0 -65
  96. package/dist/index.d.ts +0 -4
  97. package/dist/index.js +0 -4
  98. package/dist/rules/defaults/deferred-call.d.ts +0 -13
  99. package/dist/rules/defaults/deferred-call.js +0 -162
  100. package/dist/rules/defaults/index.d.ts +0 -6
  101. package/dist/rules/defaults/index.js +0 -6
  102. package/dist/rules/defaults/no-constant-condition.d.ts +0 -7
  103. package/dist/rules/defaults/no-constant-condition.js +0 -36
  104. package/dist/rules/defaults/no-mixed-operators.d.ts +0 -9
  105. package/dist/rules/defaults/no-mixed-operators.js +0 -44
  106. package/dist/rules/defaults/no-redundant-bool.d.ts +0 -5
  107. package/dist/rules/defaults/no-redundant-bool.js +0 -27
  108. package/dist/rules/defaults/no-self-comparison.d.ts +0 -9
  109. package/dist/rules/defaults/no-self-comparison.js +0 -31
  110. package/dist/rules/defaults/require-type.d.ts +0 -7
  111. package/dist/rules/defaults/require-type.js +0 -19
  112. package/dist/rules/facade.d.ts +0 -22
  113. package/dist/rules/facade.js +0 -29
  114. package/dist/rules/index.d.ts +0 -3
  115. package/dist/rules/index.js +0 -3
  116. package/dist/rules/runner.d.ts +0 -16
  117. package/dist/rules/runner.js +0 -30
  118. package/dist/rules/types.d.ts +0 -73
  119. package/dist/rules/types.js +0 -1
  120. package/dist/utils/ast-utils.d.ts +0 -55
  121. package/dist/utils/ast-utils.js +0 -255
  122. package/dist/utils/index.d.ts +0 -1
  123. package/dist/utils/index.js +0 -1
@@ -0,0 +1,66 @@
1
+ //#region src/checker/diagnostics.ts
2
+ /**
3
+ * Extract position information from a cel-js error message.
4
+ *
5
+ * cel-js error messages embed a caret (`^`) on a separate line indicating
6
+ * the error column. The format is:
7
+ * ```
8
+ * Error description
9
+ *
10
+ * > 1 | expression text
11
+ * ^
12
+ * ```
13
+ *
14
+ * We parse the caret offset relative to the code line prefix (`> N | `)
15
+ * to derive the zero-based character offset within the expression.
16
+ */
17
+ const extractPositionFromMessage = (message) => {
18
+ const lines = message.split("\n");
19
+ const caretLine = lines.find((line) => /^\s*\^+\s*$/.test(line));
20
+ if (!caretLine) return;
21
+ const codeLine = lines.find((line) => line.startsWith(">"));
22
+ if (!codeLine) return;
23
+ const pipeIndex = codeLine.indexOf("|");
24
+ if (pipeIndex === -1) return;
25
+ const prefixLength = pipeIndex + 2;
26
+ const caretStart = caretLine.indexOf("^");
27
+ const caretEnd = caretLine.lastIndexOf("^");
28
+ const from = caretStart - prefixLength;
29
+ const to = caretEnd - prefixLength + 1;
30
+ if (from < 0) return;
31
+ return {
32
+ from,
33
+ to
34
+ };
35
+ };
36
+ /**
37
+ * Extract the plain error message without the caret/code-line decoration.
38
+ */
39
+ const extractPlainMessage = (message) => {
40
+ return message.split("\n").filter((line) => !line.startsWith(">") && !/^\s*\^+\s*$/.test(line) && line.trim().length > 0).join(" ").trim() || message;
41
+ };
42
+ /**
43
+ * Convert a cel-js error into position-aware SEL diagnostics.
44
+ *
45
+ * The error's `name` property distinguishes parse errors (`"ParseError"`)
46
+ * from type errors (`"TypeError"`). Position information is extracted from
47
+ * the caret annotation embedded in the error message; when absent the
48
+ * diagnostic spans the entire expression.
49
+ */
50
+ const extractDiagnostics = (expression, error) => {
51
+ if (!(error instanceof Error)) return [{
52
+ message: String(error),
53
+ severity: "error",
54
+ from: 0,
55
+ to: expression.length
56
+ }];
57
+ const position = extractPositionFromMessage(error.message);
58
+ return [{
59
+ message: extractPlainMessage(error.message),
60
+ severity: "error",
61
+ from: position?.from ?? 0,
62
+ to: position?.to ?? expression.length
63
+ }];
64
+ };
65
+ //#endregion
66
+ exports.extractDiagnostics = extractDiagnostics;
@@ -0,0 +1,66 @@
1
+ //#region src/checker/diagnostics.ts
2
+ /**
3
+ * Extract position information from a cel-js error message.
4
+ *
5
+ * cel-js error messages embed a caret (`^`) on a separate line indicating
6
+ * the error column. The format is:
7
+ * ```
8
+ * Error description
9
+ *
10
+ * > 1 | expression text
11
+ * ^
12
+ * ```
13
+ *
14
+ * We parse the caret offset relative to the code line prefix (`> N | `)
15
+ * to derive the zero-based character offset within the expression.
16
+ */
17
+ const extractPositionFromMessage = (message) => {
18
+ const lines = message.split("\n");
19
+ const caretLine = lines.find((line) => /^\s*\^+\s*$/.test(line));
20
+ if (!caretLine) return;
21
+ const codeLine = lines.find((line) => line.startsWith(">"));
22
+ if (!codeLine) return;
23
+ const pipeIndex = codeLine.indexOf("|");
24
+ if (pipeIndex === -1) return;
25
+ const prefixLength = pipeIndex + 2;
26
+ const caretStart = caretLine.indexOf("^");
27
+ const caretEnd = caretLine.lastIndexOf("^");
28
+ const from = caretStart - prefixLength;
29
+ const to = caretEnd - prefixLength + 1;
30
+ if (from < 0) return;
31
+ return {
32
+ from,
33
+ to
34
+ };
35
+ };
36
+ /**
37
+ * Extract the plain error message without the caret/code-line decoration.
38
+ */
39
+ const extractPlainMessage = (message) => {
40
+ return message.split("\n").filter((line) => !line.startsWith(">") && !/^\s*\^+\s*$/.test(line) && line.trim().length > 0).join(" ").trim() || message;
41
+ };
42
+ /**
43
+ * Convert a cel-js error into position-aware SEL diagnostics.
44
+ *
45
+ * The error's `name` property distinguishes parse errors (`"ParseError"`)
46
+ * from type errors (`"TypeError"`). Position information is extracted from
47
+ * the caret annotation embedded in the error message; when absent the
48
+ * diagnostic spans the entire expression.
49
+ */
50
+ const extractDiagnostics = (expression, error) => {
51
+ if (!(error instanceof Error)) return [{
52
+ message: String(error),
53
+ severity: "error",
54
+ from: 0,
55
+ to: expression.length
56
+ }];
57
+ const position = extractPositionFromMessage(error.message);
58
+ return [{
59
+ message: extractPlainMessage(error.message),
60
+ severity: "error",
61
+ from: position?.from ?? 0,
62
+ to: position?.to ?? expression.length
63
+ }];
64
+ };
65
+ //#endregion
66
+ export { extractDiagnostics };
@@ -0,0 +1,2 @@
1
+ require("./type-compatibility.cjs");
2
+ require("./checker.cjs");
@@ -0,0 +1,2 @@
1
+ import { CompletionItem, SELChecker, SELCheckerOptions, SELDiagnostic } from "./checker.mjs";
2
+ import { isTypeCompatible } from "./type-compatibility.mjs";
@@ -0,0 +1,3 @@
1
+ import "./type-compatibility.mjs";
2
+ import "./checker.mjs";
3
+ export {};
@@ -0,0 +1,70 @@
1
+ //#region src/checker/type-compatibility.ts
2
+ const LOGICAL_OPERATORS = new Set(["&&", "||"]);
3
+ const ARITHMETIC_OPERATORS = new Set([
4
+ "+",
5
+ "-",
6
+ "*",
7
+ "/",
8
+ "%"
9
+ ]);
10
+ const COMPARISON_OPERATORS = new Set([
11
+ "<",
12
+ "<=",
13
+ ">",
14
+ ">="
15
+ ]);
16
+ const EQUALITY_OPERATORS = new Set(["==", "!="]);
17
+ /** Types that support arithmetic operators (same-type only). */
18
+ const ARITHMETIC_TYPES = new Set([
19
+ "int",
20
+ "uint",
21
+ "double",
22
+ "sol_int"
23
+ ]);
24
+ /** Types that support the + operator for concatenation. */
25
+ const CONCATENATION_TYPES = new Set([
26
+ "string",
27
+ "list",
28
+ "bytes"
29
+ ]);
30
+ /** Types that support comparison operators (same-type only). */
31
+ const COMPARISON_TYPES = new Set([
32
+ "int",
33
+ "uint",
34
+ "double",
35
+ "string",
36
+ "bytes",
37
+ "sol_address",
38
+ "sol_int"
39
+ ]);
40
+ /**
41
+ * Given the type of the left operand and the operator,
42
+ * returns the expected type for the right operand.
43
+ *
44
+ * Based on the registered operators in register-types.ts.
45
+ * Type compatibility is strictly same-type (no implicit coercion).
46
+ *
47
+ * Returns undefined when the type/operator combination is unknown,
48
+ * signaling that no narrowing should occur.
49
+ */
50
+ const expectedTypeForOperator = (leftType, operator) => {
51
+ if (LOGICAL_OPERATORS.has(operator)) return "bool";
52
+ if (ARITHMETIC_OPERATORS.has(operator)) {
53
+ if (ARITHMETIC_TYPES.has(leftType)) return leftType;
54
+ if (operator === "+" && CONCATENATION_TYPES.has(leftType)) return leftType;
55
+ return;
56
+ }
57
+ if (COMPARISON_OPERATORS.has(operator)) return COMPARISON_TYPES.has(leftType) ? leftType : void 0;
58
+ if (EQUALITY_OPERATORS.has(operator)) return leftType;
59
+ };
60
+ /**
61
+ * Check if a candidate type is compatible with an expected type.
62
+ * "dyn" is a wildcard — compatible with anything in either direction.
63
+ */
64
+ const isTypeCompatible = (candidateType, expectedType) => {
65
+ if (expectedType === "dyn" || candidateType === "dyn") return true;
66
+ return candidateType === expectedType;
67
+ };
68
+ //#endregion
69
+ exports.expectedTypeForOperator = expectedTypeForOperator;
70
+ exports.isTypeCompatible = isTypeCompatible;
@@ -0,0 +1,8 @@
1
+ //#region src/checker/type-compatibility.d.ts
2
+ /**
3
+ * Check if a candidate type is compatible with an expected type.
4
+ * "dyn" is a wildcard — compatible with anything in either direction.
5
+ */
6
+ declare const isTypeCompatible: (candidateType: string, expectedType: string) => boolean;
7
+ //#endregion
8
+ export { isTypeCompatible };
@@ -0,0 +1,8 @@
1
+ //#region src/checker/type-compatibility.d.ts
2
+ /**
3
+ * Check if a candidate type is compatible with an expected type.
4
+ * "dyn" is a wildcard — compatible with anything in either direction.
5
+ */
6
+ declare const isTypeCompatible: (candidateType: string, expectedType: string) => boolean;
7
+ //#endregion
8
+ export { isTypeCompatible };
@@ -0,0 +1,69 @@
1
+ //#region src/checker/type-compatibility.ts
2
+ const LOGICAL_OPERATORS = new Set(["&&", "||"]);
3
+ const ARITHMETIC_OPERATORS = new Set([
4
+ "+",
5
+ "-",
6
+ "*",
7
+ "/",
8
+ "%"
9
+ ]);
10
+ const COMPARISON_OPERATORS = new Set([
11
+ "<",
12
+ "<=",
13
+ ">",
14
+ ">="
15
+ ]);
16
+ const EQUALITY_OPERATORS = new Set(["==", "!="]);
17
+ /** Types that support arithmetic operators (same-type only). */
18
+ const ARITHMETIC_TYPES = new Set([
19
+ "int",
20
+ "uint",
21
+ "double",
22
+ "sol_int"
23
+ ]);
24
+ /** Types that support the + operator for concatenation. */
25
+ const CONCATENATION_TYPES = new Set([
26
+ "string",
27
+ "list",
28
+ "bytes"
29
+ ]);
30
+ /** Types that support comparison operators (same-type only). */
31
+ const COMPARISON_TYPES = new Set([
32
+ "int",
33
+ "uint",
34
+ "double",
35
+ "string",
36
+ "bytes",
37
+ "sol_address",
38
+ "sol_int"
39
+ ]);
40
+ /**
41
+ * Given the type of the left operand and the operator,
42
+ * returns the expected type for the right operand.
43
+ *
44
+ * Based on the registered operators in register-types.ts.
45
+ * Type compatibility is strictly same-type (no implicit coercion).
46
+ *
47
+ * Returns undefined when the type/operator combination is unknown,
48
+ * signaling that no narrowing should occur.
49
+ */
50
+ const expectedTypeForOperator = (leftType, operator) => {
51
+ if (LOGICAL_OPERATORS.has(operator)) return "bool";
52
+ if (ARITHMETIC_OPERATORS.has(operator)) {
53
+ if (ARITHMETIC_TYPES.has(leftType)) return leftType;
54
+ if (operator === "+" && CONCATENATION_TYPES.has(leftType)) return leftType;
55
+ return;
56
+ }
57
+ if (COMPARISON_OPERATORS.has(operator)) return COMPARISON_TYPES.has(leftType) ? leftType : void 0;
58
+ if (EQUALITY_OPERATORS.has(operator)) return leftType;
59
+ };
60
+ /**
61
+ * Check if a candidate type is compatible with an expected type.
62
+ * "dyn" is a wildcard — compatible with anything in either direction.
63
+ */
64
+ const isTypeCompatible = (candidateType, expectedType) => {
65
+ if (expectedType === "dyn" || candidateType === "dyn") return true;
66
+ return candidateType === expectedType;
67
+ };
68
+ //#endregion
69
+ export { expectedTypeForOperator, isTypeCompatible };
@@ -0,0 +1,14 @@
1
+ //#region src/constants.ts
2
+ /** Comprehension macro names that introduce scoped iteration variables. */
3
+ const COMPREHENSION_MACROS = new Set([
4
+ "map",
5
+ "filter",
6
+ "exists",
7
+ "all",
8
+ "exists_one"
9
+ ]);
10
+ /** Solidity scalar wrapper functions (pass-through for arg classification). */
11
+ const SCALAR_WRAPPER_FUNCTIONS = new Set(["solInt", "solAddress"]);
12
+ //#endregion
13
+ exports.COMPREHENSION_MACROS = COMPREHENSION_MACROS;
14
+ exports.SCALAR_WRAPPER_FUNCTIONS = SCALAR_WRAPPER_FUNCTIONS;
@@ -0,0 +1,7 @@
1
+ //#region src/constants.d.ts
2
+ /** Comprehension macro names that introduce scoped iteration variables. */
3
+ declare const COMPREHENSION_MACROS: Set<string>;
4
+ /** Solidity scalar wrapper functions (pass-through for arg classification). */
5
+ declare const SCALAR_WRAPPER_FUNCTIONS: Set<string>;
6
+ //#endregion
7
+ export { COMPREHENSION_MACROS, SCALAR_WRAPPER_FUNCTIONS };
@@ -0,0 +1,7 @@
1
+ //#region src/constants.d.ts
2
+ /** Comprehension macro names that introduce scoped iteration variables. */
3
+ declare const COMPREHENSION_MACROS: Set<string>;
4
+ /** Solidity scalar wrapper functions (pass-through for arg classification). */
5
+ declare const SCALAR_WRAPPER_FUNCTIONS: Set<string>;
6
+ //#endregion
7
+ export { COMPREHENSION_MACROS, SCALAR_WRAPPER_FUNCTIONS };
@@ -0,0 +1,13 @@
1
+ //#region src/constants.ts
2
+ /** Comprehension macro names that introduce scoped iteration variables. */
3
+ const COMPREHENSION_MACROS = new Set([
4
+ "map",
5
+ "filter",
6
+ "exists",
7
+ "all",
8
+ "exists_one"
9
+ ]);
10
+ /** Solidity scalar wrapper functions (pass-through for arg classification). */
11
+ const SCALAR_WRAPPER_FUNCTIONS = new Set(["solInt", "solAddress"]);
12
+ //#endregion
13
+ export { COMPREHENSION_MACROS, SCALAR_WRAPPER_FUNCTIONS };
package/dist/debug.cjs ADDED
@@ -0,0 +1,7 @@
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
+ const createLogger = (namespace) => (0, debug.default)(`sel:checker:${namespace}`);
6
+ //#endregion
7
+ exports.createLogger = createLogger;
package/dist/debug.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import createDebug from "debug";
2
+ //#region src/debug.ts
3
+ const createLogger = (namespace) => createDebug(`sel:checker:${namespace}`);
4
+ //#endregion
5
+ export { createLogger };
@@ -0,0 +1,109 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ let _seljs_types = require("@seljs/types");
3
+ let zod = require("zod");
4
+ //#region src/environment/codec-registry.ts
5
+ const solidityAddressCodec = zod.z.codec(zod.z.string(), zod.z.instanceof(_seljs_types.SolidityAddressTypeWrapper), {
6
+ decode: (s) => new _seljs_types.SolidityAddressTypeWrapper(s),
7
+ encode: (w) => w.value
8
+ });
9
+ const solidityIntCodec = zod.z.codec(zod.z.union([zod.z.bigint(), zod.z.number().transform((n) => BigInt(n))]), zod.z.instanceof(_seljs_types.SolidityIntTypeWrapper), {
10
+ decode: (n) => new _seljs_types.SolidityIntTypeWrapper(n),
11
+ encode: (w) => w.value
12
+ });
13
+ /**
14
+ * Registry that maps CEL type strings to bidirectional Zod 4 codecs.
15
+ * Immutable after construction — all codecs resolved eagerly.
16
+ */
17
+ var CelCodecRegistry = class {
18
+ codecs;
19
+ structMetas;
20
+ constructor(options) {
21
+ this.codecs = /* @__PURE__ */ new Map();
22
+ this.structMetas = /* @__PURE__ */ new Map();
23
+ this.codecs.set("sol_address", solidityAddressCodec);
24
+ this.codecs.set("sol_int", solidityIntCodec);
25
+ this.codecs.set("bool", zod.z.boolean());
26
+ this.codecs.set("string", zod.z.string());
27
+ this.codecs.set("int", zod.z.bigint());
28
+ this.codecs.set("bytes", zod.z.union([zod.z.instanceof(Uint8Array), zod.z.string().transform((s) => {
29
+ const hex = s.startsWith("0x") ? s.slice(2) : s;
30
+ const bytes = new Uint8Array(hex.length / 2);
31
+ for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
32
+ return bytes;
33
+ })]));
34
+ this.codecs.set("dyn", zod.z.unknown().transform((v) => typeof v === "bigint" ? new _seljs_types.SolidityIntTypeWrapper(v) : v));
35
+ for (const desc of options?.structs ?? []) {
36
+ this.codecs.set(desc.name, this.buildStructCodec(desc));
37
+ this.structMetas.set(desc.name, {
38
+ fieldNames: desc.fieldNames,
39
+ fieldTypes: desc.fieldTypes
40
+ });
41
+ }
42
+ }
43
+ /**
44
+ * Resolve a codec for the given CEL type string.
45
+ * Handles `list<T>` container types by composing element codecs.
46
+ * Unknown types fall back to z.unknown().
47
+ */
48
+ resolve(celType) {
49
+ const listMatch = /^list<(.+)>$/.exec(celType);
50
+ if (listMatch) {
51
+ const elementCodec = this.resolve(listMatch[1]);
52
+ return zod.z.array(elementCodec);
53
+ }
54
+ return this.codecs.get(celType) ?? zod.z.unknown();
55
+ }
56
+ /**
57
+ * Encode a CEL value back to its raw form using the codec for the given type.
58
+ * For bidirectional codecs (SolidityAddress, SolidityInt) this calls .encode().
59
+ * For structs, it recursively encodes each field.
60
+ * For passthrough types (bool, string, int), returns the value as-is.
61
+ */
62
+ encode(celType, value) {
63
+ if (celType === "bytes") {
64
+ if (value instanceof Uint8Array) return "0x" + Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
65
+ return value;
66
+ }
67
+ if (celType === "dyn") {
68
+ if (value instanceof _seljs_types.SolidityIntTypeWrapper) return value.value;
69
+ if (value instanceof _seljs_types.SolidityAddressTypeWrapper) return value.value;
70
+ return value;
71
+ }
72
+ const listMatch = /^list<(.+)>$/.exec(celType);
73
+ if (listMatch && Array.isArray(value)) {
74
+ const elementType = listMatch[1];
75
+ return value.map((item) => this.encode(elementType, item));
76
+ }
77
+ const structMeta = this.structMetas.get(celType);
78
+ if (structMeta) {
79
+ const raw = value;
80
+ const result = {};
81
+ for (const name of structMeta.fieldNames) result[name] = this.encode(structMeta.fieldTypes[name] ?? "dyn", raw[name]);
82
+ return result;
83
+ }
84
+ const codec = this.codecs.get(celType);
85
+ if (!codec) return value;
86
+ try {
87
+ return codec.encode(value);
88
+ } catch {
89
+ return value;
90
+ }
91
+ }
92
+ buildStructCodec(desc) {
93
+ const { ctor, fieldNames, fieldTypes } = desc;
94
+ return zod.z.unknown().transform((input) => {
95
+ let data;
96
+ if (Array.isArray(input)) data = Object.fromEntries(fieldNames.map((name, i) => [name, this.decodeField(fieldTypes[name] ?? "dyn", input[i])]));
97
+ else {
98
+ const raw = input;
99
+ data = Object.fromEntries(fieldNames.map((name) => [name, this.decodeField(fieldTypes[name] ?? "dyn", raw[name])]));
100
+ }
101
+ return Object.assign(new ctor(), data);
102
+ });
103
+ }
104
+ decodeField(celType, value) {
105
+ return this.resolve(celType).parse(value);
106
+ }
107
+ };
108
+ //#endregion
109
+ exports.CelCodecRegistry = CelCodecRegistry;
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/environment/codec-registry.d.ts
4
+ type AnyCodec = z.ZodType;
5
+ /**
6
+ * Struct descriptor provided to CelCodecRegistry.
7
+ */
8
+ interface StructCodecDescriptor {
9
+ name: string;
10
+ ctor: new () => object;
11
+ fieldNames: string[];
12
+ fieldTypes: Record<string, string>;
13
+ }
14
+ /**
15
+ * Options for constructing a CelCodecRegistry.
16
+ */
17
+ interface CelCodecRegistryOptions {
18
+ structs?: StructCodecDescriptor[];
19
+ }
20
+ /**
21
+ * Registry that maps CEL type strings to bidirectional Zod 4 codecs.
22
+ * Immutable after construction — all codecs resolved eagerly.
23
+ */
24
+ declare class CelCodecRegistry {
25
+ private readonly codecs;
26
+ private readonly structMetas;
27
+ constructor(options?: CelCodecRegistryOptions);
28
+ /**
29
+ * Resolve a codec for the given CEL type string.
30
+ * Handles `list<T>` container types by composing element codecs.
31
+ * Unknown types fall back to z.unknown().
32
+ */
33
+ resolve(celType: string): AnyCodec;
34
+ /**
35
+ * Encode a CEL value back to its raw form using the codec for the given type.
36
+ * For bidirectional codecs (SolidityAddress, SolidityInt) this calls .encode().
37
+ * For structs, it recursively encodes each field.
38
+ * For passthrough types (bool, string, int), returns the value as-is.
39
+ */
40
+ encode(celType: string, value: unknown): unknown;
41
+ private buildStructCodec;
42
+ private decodeField;
43
+ }
44
+ //#endregion
45
+ export { CelCodecRegistry, CelCodecRegistryOptions, StructCodecDescriptor };
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/environment/codec-registry.d.ts
4
+ type AnyCodec = z.ZodType;
5
+ /**
6
+ * Struct descriptor provided to CelCodecRegistry.
7
+ */
8
+ interface StructCodecDescriptor {
9
+ name: string;
10
+ ctor: new () => object;
11
+ fieldNames: string[];
12
+ fieldTypes: Record<string, string>;
13
+ }
14
+ /**
15
+ * Options for constructing a CelCodecRegistry.
16
+ */
17
+ interface CelCodecRegistryOptions {
18
+ structs?: StructCodecDescriptor[];
19
+ }
20
+ /**
21
+ * Registry that maps CEL type strings to bidirectional Zod 4 codecs.
22
+ * Immutable after construction — all codecs resolved eagerly.
23
+ */
24
+ declare class CelCodecRegistry {
25
+ private readonly codecs;
26
+ private readonly structMetas;
27
+ constructor(options?: CelCodecRegistryOptions);
28
+ /**
29
+ * Resolve a codec for the given CEL type string.
30
+ * Handles `list<T>` container types by composing element codecs.
31
+ * Unknown types fall back to z.unknown().
32
+ */
33
+ resolve(celType: string): AnyCodec;
34
+ /**
35
+ * Encode a CEL value back to its raw form using the codec for the given type.
36
+ * For bidirectional codecs (SolidityAddress, SolidityInt) this calls .encode().
37
+ * For structs, it recursively encodes each field.
38
+ * For passthrough types (bool, string, int), returns the value as-is.
39
+ */
40
+ encode(celType: string, value: unknown): unknown;
41
+ private buildStructCodec;
42
+ private decodeField;
43
+ }
44
+ //#endregion
45
+ export { CelCodecRegistry, CelCodecRegistryOptions, StructCodecDescriptor };
@@ -0,0 +1,108 @@
1
+ import { SolidityAddressTypeWrapper, SolidityIntTypeWrapper } from "@seljs/types";
2
+ import { z } from "zod";
3
+ //#region src/environment/codec-registry.ts
4
+ const solidityAddressCodec = z.codec(z.string(), z.instanceof(SolidityAddressTypeWrapper), {
5
+ decode: (s) => new SolidityAddressTypeWrapper(s),
6
+ encode: (w) => w.value
7
+ });
8
+ const solidityIntCodec = z.codec(z.union([z.bigint(), z.number().transform((n) => BigInt(n))]), z.instanceof(SolidityIntTypeWrapper), {
9
+ decode: (n) => new SolidityIntTypeWrapper(n),
10
+ encode: (w) => w.value
11
+ });
12
+ /**
13
+ * Registry that maps CEL type strings to bidirectional Zod 4 codecs.
14
+ * Immutable after construction — all codecs resolved eagerly.
15
+ */
16
+ var CelCodecRegistry = class {
17
+ codecs;
18
+ structMetas;
19
+ constructor(options) {
20
+ this.codecs = /* @__PURE__ */ new Map();
21
+ this.structMetas = /* @__PURE__ */ new Map();
22
+ this.codecs.set("sol_address", solidityAddressCodec);
23
+ this.codecs.set("sol_int", solidityIntCodec);
24
+ this.codecs.set("bool", z.boolean());
25
+ this.codecs.set("string", z.string());
26
+ this.codecs.set("int", z.bigint());
27
+ this.codecs.set("bytes", z.union([z.instanceof(Uint8Array), z.string().transform((s) => {
28
+ const hex = s.startsWith("0x") ? s.slice(2) : s;
29
+ const bytes = new Uint8Array(hex.length / 2);
30
+ for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
31
+ return bytes;
32
+ })]));
33
+ this.codecs.set("dyn", z.unknown().transform((v) => typeof v === "bigint" ? new SolidityIntTypeWrapper(v) : v));
34
+ for (const desc of options?.structs ?? []) {
35
+ this.codecs.set(desc.name, this.buildStructCodec(desc));
36
+ this.structMetas.set(desc.name, {
37
+ fieldNames: desc.fieldNames,
38
+ fieldTypes: desc.fieldTypes
39
+ });
40
+ }
41
+ }
42
+ /**
43
+ * Resolve a codec for the given CEL type string.
44
+ * Handles `list<T>` container types by composing element codecs.
45
+ * Unknown types fall back to z.unknown().
46
+ */
47
+ resolve(celType) {
48
+ const listMatch = /^list<(.+)>$/.exec(celType);
49
+ if (listMatch) {
50
+ const elementCodec = this.resolve(listMatch[1]);
51
+ return z.array(elementCodec);
52
+ }
53
+ return this.codecs.get(celType) ?? z.unknown();
54
+ }
55
+ /**
56
+ * Encode a CEL value back to its raw form using the codec for the given type.
57
+ * For bidirectional codecs (SolidityAddress, SolidityInt) this calls .encode().
58
+ * For structs, it recursively encodes each field.
59
+ * For passthrough types (bool, string, int), returns the value as-is.
60
+ */
61
+ encode(celType, value) {
62
+ if (celType === "bytes") {
63
+ if (value instanceof Uint8Array) return "0x" + Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
64
+ return value;
65
+ }
66
+ if (celType === "dyn") {
67
+ if (value instanceof SolidityIntTypeWrapper) return value.value;
68
+ if (value instanceof SolidityAddressTypeWrapper) return value.value;
69
+ return value;
70
+ }
71
+ const listMatch = /^list<(.+)>$/.exec(celType);
72
+ if (listMatch && Array.isArray(value)) {
73
+ const elementType = listMatch[1];
74
+ return value.map((item) => this.encode(elementType, item));
75
+ }
76
+ const structMeta = this.structMetas.get(celType);
77
+ if (structMeta) {
78
+ const raw = value;
79
+ const result = {};
80
+ for (const name of structMeta.fieldNames) result[name] = this.encode(structMeta.fieldTypes[name] ?? "dyn", raw[name]);
81
+ return result;
82
+ }
83
+ const codec = this.codecs.get(celType);
84
+ if (!codec) return value;
85
+ try {
86
+ return codec.encode(value);
87
+ } catch {
88
+ return value;
89
+ }
90
+ }
91
+ buildStructCodec(desc) {
92
+ const { ctor, fieldNames, fieldTypes } = desc;
93
+ return z.unknown().transform((input) => {
94
+ let data;
95
+ if (Array.isArray(input)) data = Object.fromEntries(fieldNames.map((name, i) => [name, this.decodeField(fieldTypes[name] ?? "dyn", input[i])]));
96
+ else {
97
+ const raw = input;
98
+ data = Object.fromEntries(fieldNames.map((name) => [name, this.decodeField(fieldTypes[name] ?? "dyn", raw[name])]));
99
+ }
100
+ return Object.assign(new ctor(), data);
101
+ });
102
+ }
103
+ decodeField(celType, value) {
104
+ return this.resolve(celType).parse(value);
105
+ }
106
+ };
107
+ //#endregion
108
+ export { CelCodecRegistry };