@callcium/sdk 0.11.0 → 0.12.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.
@@ -1,3 +1,5 @@
1
+ import { concatBytes } from "@noble/hashes/utils.js";
2
+
1
3
  import { readU16 } from "./bytes";
2
4
  import { DescriptorFormat as DF, TypeCode } from "./constants";
3
5
  import { Descriptor, readNode } from "./descriptor";
@@ -12,8 +14,7 @@ import type { DecodedParam, Hex } from "./types";
12
14
  ///////////////////////////////////////////////////////////////////////////
13
15
 
14
16
  /**
15
- * Collect the positions of depth-0 commas in input[start..end).
16
- * Commas inside `()` or `[]` are skipped.
17
+ * Collect the positions of depth-0 commas in input[start..end). Commas inside `()` or `[]` are skipped.
17
18
  */
18
19
  function commaPositions(input: string, start: number, end: number): number[] {
19
20
  const positions: number[] = [];
@@ -47,8 +48,8 @@ const MAX_TYPE_STRING_NUMBER = 256 ** DF.ARRAY_LENGTH_SIZE - 1;
47
48
  /**
48
49
  * Parse a decimal integer from input[start..end).
49
50
  *
50
- * Rejects an empty span, a non-digit, a leading zero, and a value too wide for the fields a type
51
- * string feeds. Width and length validity are out of scope here.
51
+ * Rejects an empty span, a non-digit, a leading zero, and a value too wide for the fields a type string feeds. Width
52
+ * and length validity are out of scope here.
52
53
  */
53
54
  function parseUint(input: string, start: number, end: number): number {
54
55
  if (start >= end) {
@@ -77,8 +78,7 @@ function parseUint(input: string, start: number, end: number): number {
77
78
  ///////////////////////////////////////////////////////////////////////////
78
79
 
79
80
  /**
80
- * Parse a tuple literal `(field0,field1,...)` where the opening `(` is at
81
- * `start` and the substring to parse ends before `end`.
81
+ * Parse a tuple literal `(field0,field1,...)` whose opening `(` is at `start` and whose content ends before `end`.
82
82
  */
83
83
  function parseTuple(input: string, start: number, end: number): Uint8Array {
84
84
  if (input[start] !== "(") {
@@ -137,8 +137,8 @@ function parseBaseType(input: string, start: number, end: number): Uint8Array {
137
137
  if (segment === "string") return string_();
138
138
  if (segment.startsWith("(")) return parseTuple(input, start, end);
139
139
 
140
- // A prefix with no width after it spells no type, so it falls through to the unknown-type report
141
- // rather than being read as a width that is not there.
140
+ // A prefix with no width after it spells no type, so it falls through to the unknown-type report rather than being
141
+ // read as a width that is not there.
142
142
  if (segment.length > 4 && segment.startsWith("uint")) return uintN(parseUint(input, start + 4, end));
143
143
  if (segment.length > 3 && segment.startsWith("int")) return intN(parseUint(input, start + 3, end));
144
144
  if (segment.length > 5 && segment.startsWith("bytes")) return bytesN(parseUint(input, start + 5, end));
@@ -153,16 +153,16 @@ function parseBaseType(input: string, start: number, end: number): Uint8Array {
153
153
  /**
154
154
  * Parse a type segment from input[start..end).
155
155
  *
156
- * Scans backward from end to collect all `[...]` suffixes, identifies the
157
- * base type extent, parses the base, then applies suffixes left-to-right.
156
+ * Scans backward from end to collect all `[...]` suffixes, identifies the base type extent, parses the base, then
157
+ * applies suffixes left-to-right.
158
158
  */
159
159
  function parseType(input: string, start: number, end: number): Uint8Array {
160
160
  if (start >= end) {
161
161
  throw new CallciumError("MALFORMED_TYPE_STRING", "Empty type segment");
162
162
  }
163
163
 
164
- // Locate array suffixes by scanning backward, reading no length yet: the base names the type, so
165
- // an unrecognised one is reported before a suffix it carries is parsed.
164
+ // Locate array suffixes by scanning backward, reading no length yet: the base names the type, so an unrecognised one
165
+ // is reported before a suffix it carries is parsed.
166
166
  const spans: Array<[number, number]> = [];
167
167
  let baseEnd = end;
168
168
 
@@ -182,8 +182,7 @@ function parseType(input: string, start: number, end: number): Uint8Array {
182
182
 
183
183
  let desc = parseBaseType(input, start, baseEnd);
184
184
 
185
- // Apply suffixes left-to-right: the leftmost suffix is the outermost array.
186
- // A dynamic array spells no length at all.
185
+ // Apply suffixes left-to-right: the leftmost suffix is the innermost array. A dynamic array spells no length at all.
187
186
  for (const [innerStart, innerEnd] of spans) {
188
187
  desc = array(desc, innerStart === innerEnd ? undefined : parseUint(input, innerStart, innerEnd));
189
188
  }
@@ -223,15 +222,7 @@ function fromTypes(typesCsv: string): Uint8Array {
223
222
  if (paramDescs.length > DF.MAX_PARAMS) {
224
223
  throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DF.MAX_PARAMS}`);
225
224
  }
226
- const totalBytes = paramDescs.reduce((sum, d) => sum + d.length, 0);
227
- const result = new Uint8Array(DF.HEADER_SIZE + totalBytes);
228
- result[0] = DF.VERSION;
229
- result[1] = paramDescs.length;
230
- let offset = DF.HEADER_SIZE;
231
- for (const desc of paramDescs) {
232
- result.set(desc, offset);
233
- offset += desc.length;
234
- }
225
+ const result = concatBytes(Uint8Array.of(DF.VERSION, paramDescs.length), ...paramDescs);
235
226
  // Nesting depth is a property of the assembled descriptor, not of any single node.
236
227
  decodeDescriptor(result);
237
228
  return result;
package/src/descriptor.ts CHANGED
@@ -46,11 +46,10 @@ type HintWalk = {
46
46
  type Node = { typeCode: number; staticWords: number; nodeLength: number };
47
47
 
48
48
  /**
49
- * Read the node at `offset`.
50
- * Elementary nodes occupy exactly one byte; composite nodes encode their static
51
- * word count and length in the 24-bit meta field that follows the type code.
52
- * @throws {CallciumError} When the node starts past the descriptor, carries an undefined type code,
53
- * or declares a length below its own header or beyond the descriptor.
49
+ * Read the node at `offset`. Elementary nodes occupy exactly one byte; composite nodes encode their static word count
50
+ * and length in the 24-bit meta field that follows the type code.
51
+ * @throws {CallciumError} When the node starts past the descriptor, carries an undefined type code, or declares a
52
+ * length below its own header or beyond the descriptor.
54
53
  */
55
54
  export function readNode(desc: Uint8Array, offset: number): Node {
56
55
  if (offset >= desc.length) {
@@ -90,8 +89,8 @@ function nodeLength(desc: Uint8Array, offset: number): number {
90
89
  }
91
90
 
92
91
  /**
93
- * Return the byte offset of the `fieldIndex`-th field inside a tuple node.
94
- * Starts at the first field and skips `fieldIndex` nodes using `nodeLength`.
92
+ * Return the byte offset of the `fieldIndex`-th field inside a tuple node. Starts at the first field and skips
93
+ * `fieldIndex` nodes using `nodeLength`.
95
94
  */
96
95
  function tupleFieldOffset(desc: Uint8Array, tupleOffset: number, fieldIndex: number): number {
97
96
  let cursor = tupleOffset + DF.TUPLE_HEADER_SIZE;
@@ -144,27 +143,27 @@ function inspect(desc: Uint8Array, offset: number): TypeInfo {
144
143
  /**
145
144
  * Resolve the type at a calldata path (array of step indices).
146
145
  *
147
- * The first step selects a top-level param. Each subsequent step descends
148
- * into the current node: for tuples it selects a field, for arrays it
149
- * advances to the element descriptor.
146
+ * The first step selects a top-level param. Each subsequent step descends into the current node: for tuples it selects
147
+ * a field, for arrays it advances to the element descriptor.
150
148
  *
151
149
  * @param desc - Raw descriptor bytes.
152
150
  * @param steps - Path steps, length >= 1.
153
151
  * @returns TypeInfo for the node at the resolved path.
154
- * @throws {CallciumError} On empty steps, out-of-bounds param or array index, or descent into elementary type.
152
+ * @throws {CallciumError} On empty steps, an out-of-bounds param index, tuple field or static array index, or descent
153
+ * into an elementary type.
155
154
  */
156
155
  function typeAt(desc: Uint8Array, steps: number[]): TypeInfo {
157
156
  return walkPath(desc, steps).typeInfo;
158
157
  }
159
158
 
160
159
  /**
161
- * Resolve the type at the path and the declared length of the static array a
162
- * quantifier step descends into (zero when the path holds no quantifier step
163
- * over a static array).
160
+ * Resolve the type at the path and the declared length of the static array a quantifier step descends into (zero when
161
+ * the path holds no quantifier step over a static array).
164
162
  * @param desc - Raw descriptor bytes.
165
163
  * @param steps - Path steps, length >= 1.
166
164
  * @returns TypeInfo at the resolved path and the quantified static array length.
167
- * @throws {CallciumError} On empty steps, out-of-bounds param or array index, or descent into elementary type.
165
+ * @throws {CallciumError} On empty steps, an out-of-bounds param index, tuple field or static array index, or descent
166
+ * into an elementary type.
168
167
  */
169
168
  function walkPath(desc: Uint8Array, steps: number[]): { typeInfo: TypeInfo; quantifiedStaticLength: number } {
170
169
  if (steps.length === 0) {
@@ -188,8 +187,8 @@ function walkPath(desc: Uint8Array, steps: number[]): { typeInfo: TypeInfo; quan
188
187
  }
189
188
  cursor = tupleFieldOffset(desc, cursor, step);
190
189
  } else if (typeCode === TypeCode.STATIC_ARRAY || typeCode === TypeCode.DYNAMIC_ARRAY) {
191
- // Quantifier sentinels descend into the element type; only concrete
192
- // indices are bounds-checked against the declared array length.
190
+ // Quantifier sentinels descend into the element type; only concrete indices are bounds-checked against the
191
+ // declared array length.
193
192
  if (typeCode === TypeCode.STATIC_ARRAY) {
194
193
  if (isQuantifier(step)) {
195
194
  quantifiedStaticLength = staticArrayLength(desc, cursor);
@@ -377,8 +376,8 @@ function encodeHint(walk: HintWalk, targetMeta: number, typeCode: number, depth:
377
376
  * @param desc - Raw descriptor bytes.
378
377
  * @param steps - Path steps, length >= 1.
379
378
  * @returns The hint block bytes.
380
- * @throws {CallciumError} When a step leaves the structure the descriptor declares, quantifies over
381
- * a non-array node, or repeats a quantifier.
379
+ * @throws {CallciumError} When a step leaves the structure the descriptor declares, quantifies over a non-array node,
380
+ * or repeats a quantifier.
382
381
  */
383
382
  function compileHint(desc: Uint8Array, steps: number[]): Uint8Array {
384
383
  if (steps.length === 0) {
package/src/errors.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * Machine-readable error code for structural decoding and validation failures.
3
3
  *
4
- * Each code names one invariant. Where Solidity detects the same invariant, the code is its error
5
- * name transliterated from `PascalCase` to `SCREAMING_SNAKE_CASE`. The codes under "Inputs and
6
- * validation" have no counterpart: they name input a Solidity signature cannot express.
7
- * `OUT_OF_PHYSICAL_BOUNDS` names an invariant Solidity carries twice, as the error
8
- * `PolicyBuilder.OutOfPhysicalBounds` and as an issue code, so the name is shared with `IssueCode`.
4
+ * Each code names one invariant. Where Solidity detects the same invariant, the code is its error name transliterated
5
+ * from `PascalCase` to `SCREAMING_SNAKE_CASE`. The codes under "Inputs and validation" have no counterpart: they name
6
+ * input a Solidity signature cannot express. `OUT_OF_PHYSICAL_BOUNDS` names an invariant Solidity carries twice, as the
7
+ * error `PolicyBuilder.OutOfPhysicalBounds` and as an issue code, so the name is shared with `IssueCode`.
9
8
  */
10
9
  export type CallciumErrorCode =
11
10
  // Policy and descriptor framing.
@@ -73,18 +72,18 @@ export type CallciumErrorCode =
73
72
  | "RULE_COUNT_OVERFLOW"
74
73
  | "RULE_SIZE_OVERFLOW"
75
74
  | "DESC_LENGTH_OVERFLOW"
75
+ // Strict build.
76
+ | "VALIDATION_ERROR"
76
77
  // Inputs and validation.
77
78
  | "UNKNOWN_QUANTIFIER"
78
79
  | "INVALID_HEX"
79
80
  | "MALFORMED_SELECTOR"
80
81
  | "PATH_STEP_OVERFLOW"
81
82
  | "OPERAND_OVERFLOW"
82
- | "CONTEXT_VALUE_OVERFLOW"
83
- | "VALIDATION_ERROR";
83
+ | "CONTEXT_VALUE_OVERFLOW";
84
84
 
85
85
  /**
86
- * Thrown when a policy or descriptor blob is structurally malformed.
87
- * The optional `offset` indicates the byte position in the source blob where the error was detected.
86
+ * Thrown on malformed or inadmissible SDK input. `offset`, when set, is the error's byte position in the source blob.
88
87
  */
89
88
  export class CallciumError extends Error {
90
89
  public readonly code: CallciumErrorCode;
@@ -104,9 +103,8 @@ export class CallciumError extends Error {
104
103
  /**
105
104
  * Thrown by `PolicyBuilder.build` when validation reports any issue.
106
105
  *
107
- * Carries every issue the validator found. The `Error.message` is the first issue's
108
- * message — a diagnostic summary, not a presentation contract. Consumers rendering
109
- * issues to users should iterate `issues` directly.
106
+ * Carries every issue the validator found. The `Error.message` is the first issue's message — a diagnostic summary, not
107
+ * a presentation contract. Consumers rendering issues to users should iterate `issues` directly.
110
108
  */
111
109
  export class ValidationError extends CallciumError {
112
110
  public readonly issues: import("./types").Issue[];
@@ -119,12 +117,11 @@ export class ValidationError extends CallciumError {
119
117
  }
120
118
 
121
119
  /**
122
- * Thrown by `PolicyEnforcer.enforce` when calldata fails policy enforcement.
123
- * Distinct from `ValidationError`, which signals static issues in the policy itself.
120
+ * Thrown by `PolicyEnforcer.enforce` when calldata fails policy enforcement. Distinct from `ValidationError`, which
121
+ * signals static issues in the policy itself.
124
122
  *
125
- * Carries the full list of structured violations (one per failed group). The
126
- * `Error.message` is a minimal non-lossy diagnostic summary built from the
127
- * first violation's structured fields — not a presentation contract. Consumers
123
+ * Carries the full list of structured violations (one per failed group). The `Error.message` is a minimal non-lossy
124
+ * diagnostic summary built from the first violation's structured fields — not a presentation contract. Consumers
128
125
  * rendering violations to users should iterate `violations` directly.
129
126
  */
130
127
  export class PolicyViolationError extends Error {
package/src/index.ts CHANGED
@@ -30,7 +30,7 @@ export { PolicyEnforcer } from "./policy-enforcer";
30
30
  // Utilities
31
31
  ///////////////////////////////////////////////////////////////////////////
32
32
 
33
- export { toAddress, hexToBytes, bytesToHex } from "./bytes";
33
+ export { hexToBytes, bytesToHex } from "./bytes";
34
34
  export { Descriptor } from "./descriptor";
35
35
  export { DescriptorCoder } from "./descriptor-coder";
36
36
  export type { TypeInfo } from "./descriptor";
@@ -45,13 +45,12 @@ export {
45
45
  PolicyFormat,
46
46
  Scope,
47
47
  ContextProperty,
48
- MAX_CONTEXT_PROPERTY_ID,
49
48
  findContextProperty,
50
49
  lookupOp,
51
50
  lookupScope,
52
51
  lookupContextProperty,
53
52
  } from "./constants";
54
- export { isLengthOp, lookupTypeCode } from "./operators";
53
+ export { isContextOp, isLengthOp, lookupTypeCode } from "./operators";
55
54
  export type { TypeCodeInfo, TypeClassInfo, TypeClass } from "./operators";
56
55
  export type { Operands, OpInfo, ScopeInfo, ContextPropertyInfo } from "./constants";
57
56
 
package/src/operators.ts CHANGED
@@ -21,8 +21,8 @@ export function isSigned(typeCode: number): boolean {
21
21
  }
22
22
 
23
23
  /**
24
- * Return true when the type code represents a left-aligned type (fixed bytes or function),
25
- * whose value occupies the high bytes of the word with zero padding below.
24
+ * Return true when the type code represents a left-aligned type (fixed bytes or function), whose value occupies the
25
+ * high bytes of the word with zero padding below.
26
26
  */
27
27
  export function isLeftAligned(typeCode: number): boolean {
28
28
  return (
@@ -31,11 +31,9 @@ export function isLeftAligned(typeCode: number): boolean {
31
31
  }
32
32
 
33
33
  /**
34
- * Canonicalize a raw 256-bit calldata word to its ABI value for the declared type.
35
- * A scalar loaded from untrusted calldata may carry bits outside the declared width; masking
36
- * unsigned/address/bool/function/bytesN to width and sign-extending signed integers makes the
37
- * comparison use the canonical ABI value rather than the raw bytes.
38
- * @param value - The raw 256-bit word loaded from calldata.
34
+ * Return the canonical encoding of a 256-bit word for the declared type. Masks unsigned/address/bool to width, clears
35
+ * bytesN/function padding, and sign-extends signed integers. A word is canonical when this leaves it unchanged.
36
+ * @param value - The 256-bit word.
39
37
  * @param typeCode - The declared type code of the value.
40
38
  * @returns The canonicalized 256-bit value.
41
39
  */
@@ -78,14 +76,10 @@ export function isLengthOp(opCode: number): boolean {
78
76
  return base >= Op.LENGTH_EQ && base <= Op.LENGTH_BETWEEN;
79
77
  }
80
78
 
81
- /** Map a LENGTH_* opcode base to its core value-comparison twin. */
82
- function lengthToValueOp(base: number): number {
83
- if (base === Op.LENGTH_EQ) return Op.EQ;
84
- if (base === Op.LENGTH_GT) return Op.GT;
85
- if (base === Op.LENGTH_LT) return Op.LT;
86
- if (base === Op.LENGTH_GTE) return Op.GTE;
87
- if (base === Op.LENGTH_LTE) return Op.LTE;
88
- return Op.BETWEEN;
79
+ /** Return true when the operator code (with or without NOT flag) is a *_CTX variant. */
80
+ export function isContextOp(opCode: number): boolean {
81
+ const base = opCode & ~Op.NOT;
82
+ return base >= Op.EQ_CTX && base <= Op.LTE_CTX;
89
83
  }
90
84
 
91
85
  /** Return true when LENGTH_* operators are valid for the given type code (bytes, string, or dynamic array). */
@@ -119,12 +113,12 @@ function isIn(value: bigint, data: Uint8Array): boolean {
119
113
  }
120
114
 
121
115
  /**
122
- * Apply a single operator against a value and operand data.
123
- * Handles the NOT flag internally by inverting the base operator result.
116
+ * Apply a single operator against a value and operand data. Handles the NOT flag by inverting the base result.
124
117
  * @param opCode - Operator code, potentially OR'd with the NOT flag.
125
118
  * @param value - The 256-bit value to test (ignored for LENGTH_* operators).
126
119
  * @param valueLength - Runtime byte or element count, only meaningful for LENGTH_* operators.
127
- * @param operandData - Raw operand bytes (32 per operand, 64 for range operators).
120
+ * @param operandData - Raw operand bytes (32 per operand, 64 for range operators); for a context reference operator,
121
+ * the referenced property's value as one word.
128
122
  * @param typeCode - ABI type code, used to select signed vs. unsigned comparison.
129
123
  * @returns Whether the value satisfies the operator.
130
124
  */
@@ -135,17 +129,7 @@ export function applyOperator(
135
129
  operandData: Uint8Array,
136
130
  typeCode: number,
137
131
  ): boolean {
138
- let base = opCode & ~Op.NOT;
139
-
140
- // Length operators reuse the value-comparison core: the runtime count becomes the compared
141
- // value and the opcode maps onto its EQ/GT/LT/GTE/LTE/BETWEEN twin. Counts are non-negative,
142
- // so the comparison is always unsigned — force an unsigned type code regardless of the target.
143
- if (isLengthOp(base)) {
144
- value = valueLength;
145
- typeCode = TypeCode.UINT_MAX;
146
- base = lengthToValueOp(base);
147
- }
148
-
132
+ const base = opCode & ~Op.NOT;
149
133
  let result: boolean;
150
134
 
151
135
  switch (base) {
@@ -198,6 +182,53 @@ export function applyOperator(
198
182
  break;
199
183
  }
200
184
 
185
+ case Op.LENGTH_EQ:
186
+ result = valueLength === toBigInt(operandData, 0);
187
+ break;
188
+
189
+ case Op.LENGTH_GT:
190
+ result = valueLength > toBigInt(operandData, 0);
191
+ break;
192
+
193
+ case Op.LENGTH_LT:
194
+ result = valueLength < toBigInt(operandData, 0);
195
+ break;
196
+
197
+ case Op.LENGTH_GTE:
198
+ result = valueLength >= toBigInt(operandData, 0);
199
+ break;
200
+
201
+ case Op.LENGTH_LTE:
202
+ result = valueLength <= toBigInt(operandData, 0);
203
+ break;
204
+
205
+ case Op.LENGTH_BETWEEN: {
206
+ const lower = toBigInt(operandData, 0);
207
+ const upper = toBigInt(operandData, 32);
208
+ result = valueLength >= lower && valueLength <= upper;
209
+ break;
210
+ }
211
+
212
+ case Op.EQ_CTX:
213
+ result = value === toBigInt(operandData, 0);
214
+ break;
215
+
216
+ case Op.GT_CTX:
217
+ result = value > toBigInt(operandData, 0);
218
+ break;
219
+
220
+ case Op.LT_CTX:
221
+ result = value < toBigInt(operandData, 0);
222
+ break;
223
+
224
+ case Op.GTE_CTX:
225
+ result = value >= toBigInt(operandData, 0);
226
+ break;
227
+
228
+ case Op.LTE_CTX:
229
+ result = value <= toBigInt(operandData, 0);
230
+ break;
231
+
201
232
  default:
202
233
  throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code ${formatCode(base, PF.RULE_OPCODE_SIZE)}`);
203
234
  }
@@ -260,10 +291,9 @@ const DYNAMIC_ARR = { typeClass: "dynamicArray", isDynamic: true } as const;
260
291
  const TUPLE = { typeClass: "tuple", isDynamic: false } as const;
261
292
 
262
293
  /**
263
- * Classify a type code into its structural category and dynamism.
264
- * Lightweight variant of `lookupTypeCode` that skips label computation.
294
+ * Classify a type code into its structural category and dynamism. A `lookupTypeCode` variant without labels.
265
295
  * @param code - A single-byte descriptor type code.
266
- * @returns Type class and whether the type is ABI-dynamic.
296
+ * @returns Type class, and whether the code alone implies ABI dynamism (false for tuples and static arrays).
267
297
  * @throws {CallciumError} If the code is not a recognised type code.
268
298
  */
269
299
  export function classifyTypeCode(code: number): TypeClassInfo {
@@ -296,8 +326,8 @@ export function isElementary(code: number): boolean {
296
326
  }
297
327
 
298
328
  /**
299
- * Determine whether a type code names a value an operator can read: a scalar word or a declared
300
- * length. Tuples, static arrays, and undefined codes address nothing.
329
+ * Determine whether a type code names a value an operator can read: a scalar word or a declared length. Tuples, static
330
+ * arrays, and undefined codes address nothing.
301
331
  * @param code - A single-byte descriptor type code.
302
332
  * @returns True when an operator can be applied to a target of this type.
303
333
  */
@@ -332,7 +362,7 @@ function typeCodeLabel(code: number): string {
332
362
  /**
333
363
  * Map a raw type code byte to its ABI type label, structural category, and dynamism.
334
364
  * @param code - A single-byte descriptor type code.
335
- * @returns Label, type class, and whether the type is ABI-dynamic.
365
+ * @returns Label, type class, and whether the code alone implies ABI dynamism (false for tuples and static arrays).
336
366
  * @throws {CallciumError} If the code is not a recognised type code.
337
367
  */
338
368
  export function lookupTypeCode(code: number): TypeCodeInfo {
package/src/path.ts CHANGED
@@ -47,8 +47,7 @@ export function lookupQuantifier(code: number): QuantifierInfo {
47
47
 
48
48
  /**
49
49
  * Encode a sequence of uint16 path steps as a big-endian hex string.
50
- * @throws {CallciumError} When a step is not an integer the field holds. A wrapped step would
51
- * address a different argument.
50
+ * @throws {CallciumError} When a step is not an integer the field holds; a wrapped step addresses another argument.
52
51
  */
53
52
  export function encodePath(steps: readonly number[]): Hex {
54
53
  const buffer = new Uint8Array(steps.length * PF.PATH_STEP_SIZE);
@@ -124,26 +124,24 @@ function reject(written: bigint, folded: bigint): never {
124
124
  }
125
125
 
126
126
  /**
127
- * Reject an operand that two's complement folds onto a value the target admits.
128
- * Such an operand and the value it folds onto encode to one word, so the operands as written are
129
- * the only place the two stay distinct. An operand spans no more than one word, which leaves two
130
- * ways across: a negative's alias clears every target narrower than the whole word, and a positive
131
- * reaches a signed range only at that type's own minimum word.
127
+ * Reject an operand that two's complement folds onto a value the target admits. Such an operand and the value it folds
128
+ * onto encode to one word, so the operands as written are the only place the two stay distinct. An operand spans no
129
+ * more than one word, which leaves two ways across: a negative's alias clears every target narrower than the whole
130
+ * word, and a positive reaches a signed range only at that type's own minimum word.
132
131
  */
133
132
  function checkOperandDomain(constraint: Constraint, typeCode: number): void {
134
133
  const bounds = targetBounds(typeCode);
135
134
  const extremes = readOperandExtremes(constraint);
136
135
  if (bounds === null || extremes === undefined) return;
137
136
 
138
- // A negative operand occupies the word its unsigned alias does, which only the target holding
139
- // the whole word can hold too.
137
+ // A negative operand encodes as its unsigned alias's word, which only a whole-word target can hold too.
140
138
  if (extremes.leastNegative < 0n && typeCode === TypeCode.UINT_MAX) {
141
139
  reject(extremes.leastNegative, extremes.leastNegative + WORD_VALUES);
142
140
  }
143
141
 
144
142
  if (!isSigned(typeCode)) return;
145
- // An operand at or above the signed minimum's own word denotes that negative, and every such
146
- // operand already exceeds the signed maximum.
143
+ // An operand at or above the signed minimum's own word denotes that negative, and every such operand already exceeds
144
+ // the signed maximum.
147
145
  if (extremes.greatest >= WORD_VALUES + bounds.min) {
148
146
  reject(extremes.greatest, extremes.greatest - WORD_VALUES);
149
147
  }
@@ -195,9 +193,9 @@ export class PolicyBuilder {
195
193
  /**
196
194
  * Add a constraint to the current group.
197
195
  * @param constraint - A `Constraint` object or a `ConstraintBuilder` instance.
198
- * @throws {CallciumError} With code `OUT_OF_PHYSICAL_BOUNDS` when a numeric operand lies outside
199
- * the target type's range and encodes as a value inside it. Operands are read as written, so a
200
- * `Constraint` carrying encoded operator bytes is left to {@link validate}.
196
+ * @throws {CallciumError} With code `OUT_OF_PHYSICAL_BOUNDS` when a numeric operand lies outside the target type's
197
+ * range and encodes as a value inside it. Operands are read as written, so a `Constraint` carrying encoded operator
198
+ * bytes is left to {@link validate}.
201
199
  */
202
200
  add(constraint: Constraint): this {
203
201
  // A builder compiles no hint; one arrives only on a constraint that came already encoded.
@@ -251,14 +249,12 @@ export class PolicyBuilder {
251
249
  }
252
250
 
253
251
  /**
254
- * Build the policy into an encoded binary blob with strict validation.
255
- * Throws on any issue, regardless of severity. Use {@link validate} to
256
- * inspect issues, or {@link buildUnsafe} to encode without validation.
252
+ * Build the policy into an encoded binary blob with strict validation. Throws on any issue, regardless of severity.
253
+ * Use {@link validate} to inspect issues, or {@link buildUnsafe} to encode without validation.
257
254
  * @returns The policy as a 0x-prefixed hex string.
258
255
  * @throws {CallciumError} If any group is empty.
259
256
  * @throws {ValidationError} If validation finds any issue.
260
- * @throws {CallciumError} With code `RULE_SIZE_OVERFLOW` when a rule's encoded bytes exceed the
261
- * width of the rule size field.
257
+ * @throws {CallciumError} With code `RULE_SIZE_OVERFLOW` when a rule's encoded bytes exceed the rule size field.
262
258
  */
263
259
  build(): Hex {
264
260
  this.checkGroups();
@@ -271,9 +267,8 @@ export class PolicyBuilder {
271
267
  }
272
268
 
273
269
  /**
274
- * Build the policy into an encoded binary blob without validation.
275
- * The resulting policy may be invalid. Prefer {@link build}; use this
276
- * only to knowingly bypass a reported issue.
270
+ * Build the policy into an encoded binary blob without validation. The resulting policy may be invalid. Prefer {@link
271
+ * build}; use this only to knowingly bypass a reported issue.
277
272
  * @returns The policy as a 0x-prefixed hex string.
278
273
  * @throws {CallciumError} If any group is empty.
279
274
  */