@telorun/sdk 0.68.0 → 0.72.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.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Reading a wide integer back out of a `JSON.stringify` replacer.
3
+ *
4
+ * CEL models `int` as int64, which this runtime evaluates to a JS BigInt — so
5
+ * `size(group)`, `sum(...)` and integer arithmetic all produce one, and the kernel
6
+ * installs `BigInt.prototype.toJSON` at boot (`enableBigIntJson`,
7
+ * `kernel/nodejs/src/bigint-json.ts`) so every JSON boundary in the process emits
8
+ * it as its exact decimal digits.
9
+ *
10
+ * The installer is a composition-root action and lives in the kernel. What a
11
+ * MODULE AUTHOR needs is the consequence, which is what this file carries: a
12
+ * `toJSON` runs BEFORE a replacer, so a sink or codec that must encode a wide
13
+ * integer differently from the process default can no longer recognise one by
14
+ * `typeof`.
15
+ */
16
+ /** The process-global flag {@link isBigIntJsonEnabled} reads and the kernel's
17
+ * installer sets. Shared through `Symbol.for` so a second `@telorun/sdk` copy in
18
+ * the process (the test suite runs child kernels in-process) sees the first
19
+ * install rather than re-wrapping. */
20
+ export declare const BIGINT_JSON_INSTALLED_KEY: symbol;
21
+ /** True once the kernel has installed `BigInt.prototype.toJSON` in this process. */
22
+ export declare function isBigIntJsonEnabled(): boolean;
23
+ /**
24
+ * The BigInt a `JSON.stringify` replacer was called for, or `undefined` when the
25
+ * value is not one.
26
+ *
27
+ * `JSON.stringify` applies `toJSON` BEFORE the replacer, so once the patch is
28
+ * installed a replacer never sees a `bigint` — it sees the opaque raw-JSON token.
29
+ * A serializer that wants a DIFFERENT encoding than the exact digits (OTLP quotes
30
+ * its 64-bit fields; a round-trippable store tags them; a console encoding renders
31
+ * them as text) has to reach past the token, and `JSON.stringify` hands it the
32
+ * means: the replacer is called with the holder as `this`, and the holder still
33
+ * has the original value.
34
+ *
35
+ * Reading the holder also makes the result independent of whether the patch is
36
+ * installed, so an encoder called outside a booted kernel behaves identically.
37
+ *
38
+ * Only reach for this when the default is genuinely wrong for the destination.
39
+ * Everything that wants exact digits on the wire needs no replacer at all.
40
+ */
41
+ export declare function bigIntAt(holder: unknown, key: string): bigint | undefined;
42
+ //# sourceMappingURL=bigint-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bigint-json.d.ts","sourceRoot":"","sources":["../src/bigint-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;uCAGuC;AACvC,eAAO,MAAM,yBAAyB,QAAgB,CAAC;AAEvD,oFAAoF;AACpF,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzE"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Reading a wide integer back out of a `JSON.stringify` replacer.
3
+ *
4
+ * CEL models `int` as int64, which this runtime evaluates to a JS BigInt — so
5
+ * `size(group)`, `sum(...)` and integer arithmetic all produce one, and the kernel
6
+ * installs `BigInt.prototype.toJSON` at boot (`enableBigIntJson`,
7
+ * `kernel/nodejs/src/bigint-json.ts`) so every JSON boundary in the process emits
8
+ * it as its exact decimal digits.
9
+ *
10
+ * The installer is a composition-root action and lives in the kernel. What a
11
+ * MODULE AUTHOR needs is the consequence, which is what this file carries: a
12
+ * `toJSON` runs BEFORE a replacer, so a sink or codec that must encode a wide
13
+ * integer differently from the process default can no longer recognise one by
14
+ * `typeof`.
15
+ */
16
+ const INSTALLED_KEY = Symbol.for("@telorun/sdk:bigint-json:installed");
17
+ /** The process-global flag {@link isBigIntJsonEnabled} reads and the kernel's
18
+ * installer sets. Shared through `Symbol.for` so a second `@telorun/sdk` copy in
19
+ * the process (the test suite runs child kernels in-process) sees the first
20
+ * install rather than re-wrapping. */
21
+ export const BIGINT_JSON_INSTALLED_KEY = INSTALLED_KEY;
22
+ /** True once the kernel has installed `BigInt.prototype.toJSON` in this process. */
23
+ export function isBigIntJsonEnabled() {
24
+ return globalThis[INSTALLED_KEY] === true;
25
+ }
26
+ /**
27
+ * The BigInt a `JSON.stringify` replacer was called for, or `undefined` when the
28
+ * value is not one.
29
+ *
30
+ * `JSON.stringify` applies `toJSON` BEFORE the replacer, so once the patch is
31
+ * installed a replacer never sees a `bigint` — it sees the opaque raw-JSON token.
32
+ * A serializer that wants a DIFFERENT encoding than the exact digits (OTLP quotes
33
+ * its 64-bit fields; a round-trippable store tags them; a console encoding renders
34
+ * them as text) has to reach past the token, and `JSON.stringify` hands it the
35
+ * means: the replacer is called with the holder as `this`, and the holder still
36
+ * has the original value.
37
+ *
38
+ * Reading the holder also makes the result independent of whether the patch is
39
+ * installed, so an encoder called outside a booted kernel behaves identically.
40
+ *
41
+ * Only reach for this when the default is genuinely wrong for the destination.
42
+ * Everything that wants exact digits on the wire needs no replacer at all.
43
+ */
44
+ export function bigIntAt(holder, key) {
45
+ const source = holder?.[key];
46
+ return typeof source === "bigint" ? source : undefined;
47
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./bigint-json.js";
1
2
  export * from "./cancellation.js";
2
3
  export * from "./compiled-value.js";
3
4
  export * from "./capabilities/invokable.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./bigint-json.js";
1
2
  export * from "./cancellation.js";
2
3
  export * from "./compiled-value.js";
3
4
  export * from "./capabilities/invokable.js";
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * JSON encoding for values that cross a persistence boundary.
3
3
  *
4
- * `JSON.stringify` THROWS on a BigInt, and CEL integers surface as BigInt in
5
- * this runtime so any controller that persists a result computed in CEL
6
- * (`{ charged: 500 }` from a `Run.Sequence` output) hits it. A store that lets
7
- * that throw escape is worse than one that never persisted: the caller sees an
8
- * opaque TypeError, and a decorator built on the store can mistake it for the
9
- * body having failed.
4
+ * BigInt is encoded as a tagged object rather than as a plain string, a Number,
5
+ * or the exact digits every other JSON boundary emits: this codec has to be
6
+ * INVERTIBLE. A replayed value must equal the freshly-produced one including
7
+ * its type or at-most-once execution silently changes its answer on the second
8
+ * call. Digits would come back as a Number (lossy past 2^53), a string would come
9
+ * back a different type than went in.
10
10
  *
11
- * BigInt is encoded as a tagged object rather than a plain string or a Number:
12
- * a string would come back a different type than went in, and Number is lossy
13
- * past 2^53. A replayed value must equal the freshly-produced one, or
14
- * at-most-once execution silently changes its answer on the second call.
11
+ * That is why this file reaches past `BigInt.prototype.toJSON` with
12
+ * {@link bigIntAt} instead of inheriting the process-wide encoding: the wire wants
13
+ * the value, a store wants the value AND its type back.
15
14
  */
16
15
  /** Serialize a value to JSON text, preserving BigInt exactly. */
17
16
  export declare function encodeJsonValue(value: unknown): string;
@@ -1 +1 @@
1
- {"version":3,"file":"json-value.d.ts","sourceRoot":"","sources":["../src/json-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkBH,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAItD;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
1
+ {"version":3,"file":"json-value.d.ts","sourceRoot":"","sources":["../src/json-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoBH,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKtD;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * JSON encoding for values that cross a persistence boundary.
3
3
  *
4
- * `JSON.stringify` THROWS on a BigInt, and CEL integers surface as BigInt in
5
- * this runtime so any controller that persists a result computed in CEL
6
- * (`{ charged: 500 }` from a `Run.Sequence` output) hits it. A store that lets
7
- * that throw escape is worse than one that never persisted: the caller sees an
8
- * opaque TypeError, and a decorator built on the store can mistake it for the
9
- * body having failed.
4
+ * BigInt is encoded as a tagged object rather than as a plain string, a Number,
5
+ * or the exact digits every other JSON boundary emits: this codec has to be
6
+ * INVERTIBLE. A replayed value must equal the freshly-produced one including
7
+ * its type or at-most-once execution silently changes its answer on the second
8
+ * call. Digits would come back as a Number (lossy past 2^53), a string would come
9
+ * back a different type than went in.
10
10
  *
11
- * BigInt is encoded as a tagged object rather than a plain string or a Number:
12
- * a string would come back a different type than went in, and Number is lossy
13
- * past 2^53. A replayed value must equal the freshly-produced one, or
14
- * at-most-once execution silently changes its answer on the second call.
11
+ * That is why this file reaches past `BigInt.prototype.toJSON` with
12
+ * {@link bigIntAt} instead of inheriting the process-wide encoding: the wire wants
13
+ * the value, a store wants the value AND its type back.
15
14
  */
15
+ import { bigIntAt } from "./bigint-json.js";
16
16
  const BIGINT_TAG = "$bigint";
17
17
  function isTaggedBigInt(value) {
18
18
  return (typeof value === "object" &&
@@ -23,7 +23,10 @@ function isTaggedBigInt(value) {
23
23
  }
24
24
  /** Serialize a value to JSON text, preserving BigInt exactly. */
25
25
  export function encodeJsonValue(value) {
26
- return JSON.stringify(value ?? null, (_k, v) => typeof v === "bigint" ? { [BIGINT_TAG]: v.toString() } : v);
26
+ return JSON.stringify(value ?? null, function (key, v) {
27
+ const source = bigIntAt(this, key);
28
+ return source === undefined ? v : { [BIGINT_TAG]: source.toString() };
29
+ });
27
30
  }
28
31
  /** Inverse of {@link encodeJsonValue}; BigInt values are restored as BigInt. */
29
32
  export function decodeJsonValue(text) {
@@ -88,6 +88,17 @@ export interface RuntimeCheckOptions {
88
88
  * detail of one editor transport, and this contract is read by a Rust or Go
89
89
  * kernel too. */
90
90
  export type CheckDiagnosticSeverity = "error" | "warning" | "info" | "hint";
91
+ /** A mechanically applicable repair for a finding: `replacement` is the whole
92
+ * corrected value at `path` — never a fragment — so a consumer applies it
93
+ * without parsing the language inside.
94
+ *
95
+ * That is why `path` travels with it. `source` / `line` / `column` locate a
96
+ * finding for a human reading text; a module applying a repair works on the
97
+ * parsed manifest, where a line number is not an address. A repair without its
98
+ * anchor is one nothing can apply. */
99
+ export interface CheckDiagnosticFix {
100
+ replacement: string;
101
+ }
91
102
  /** One analyzer finding, flattened to data. Positions are zero-based, matching
92
103
  * the analyzer's own range model. */
93
104
  export interface CheckDiagnostic {
@@ -98,6 +109,15 @@ export interface CheckDiagnostic {
98
109
  source?: string;
99
110
  line?: number;
100
111
  column?: number;
112
+ /** `<kind>/<name>` of the resource the finding is pinned to. */
113
+ resource?: string;
114
+ /** Dotted path of the offending value within that resource
115
+ * (`steps[0].inputs.flag`) — the address `fix` applies at. */
116
+ path?: string;
117
+ /** Present only when the repair is decidable — a fix that might not be
118
+ * correct is worse than none, since the point of the field is that it can be
119
+ * applied without review. */
120
+ fix?: CheckDiagnosticFix;
101
121
  }
102
122
  export interface RuntimeCheckResult {
103
123
  diagnostics: CheckDiagnostic[];
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-seam.d.ts","sourceRoot":"","sources":["../src/runtime-seam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;OAYG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtE;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,iBAAiB;IAChC;;qBAEiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC;sCACkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC;;oCAEgC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;kBAEkB;AAClB,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5E;sCACsC;AACtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B;iEAC6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"runtime-seam.d.ts","sourceRoot":"","sources":["../src/runtime-seam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;OAYG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtE;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,iBAAiB;IAChC;;qBAEiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC;sCACkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC;;oCAEgC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;kBAEkB;AAClB,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5E;;;;;;;uCAOuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;sCACsC;AACtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;mEAC+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;kCAE8B;IAC9B,GAAG,CAAC,EAAE,kBAAkB,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B;iEAC6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/sdk",
3
- "version": "0.68.0",
3
+ "version": "0.72.0",
4
4
  "description": "Telo SDK - Public API for Telo module authors.",
5
5
  "keywords": [
6
6
  "telo",
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Reading a wide integer back out of a `JSON.stringify` replacer.
3
+ *
4
+ * CEL models `int` as int64, which this runtime evaluates to a JS BigInt — so
5
+ * `size(group)`, `sum(...)` and integer arithmetic all produce one, and the kernel
6
+ * installs `BigInt.prototype.toJSON` at boot (`enableBigIntJson`,
7
+ * `kernel/nodejs/src/bigint-json.ts`) so every JSON boundary in the process emits
8
+ * it as its exact decimal digits.
9
+ *
10
+ * The installer is a composition-root action and lives in the kernel. What a
11
+ * MODULE AUTHOR needs is the consequence, which is what this file carries: a
12
+ * `toJSON` runs BEFORE a replacer, so a sink or codec that must encode a wide
13
+ * integer differently from the process default can no longer recognise one by
14
+ * `typeof`.
15
+ */
16
+
17
+ const INSTALLED_KEY = Symbol.for("@telorun/sdk:bigint-json:installed");
18
+
19
+ /** The process-global flag {@link isBigIntJsonEnabled} reads and the kernel's
20
+ * installer sets. Shared through `Symbol.for` so a second `@telorun/sdk` copy in
21
+ * the process (the test suite runs child kernels in-process) sees the first
22
+ * install rather than re-wrapping. */
23
+ export const BIGINT_JSON_INSTALLED_KEY = INSTALLED_KEY;
24
+
25
+ /** True once the kernel has installed `BigInt.prototype.toJSON` in this process. */
26
+ export function isBigIntJsonEnabled(): boolean {
27
+ return (globalThis as Record<symbol, unknown>)[INSTALLED_KEY] === true;
28
+ }
29
+
30
+ /**
31
+ * The BigInt a `JSON.stringify` replacer was called for, or `undefined` when the
32
+ * value is not one.
33
+ *
34
+ * `JSON.stringify` applies `toJSON` BEFORE the replacer, so once the patch is
35
+ * installed a replacer never sees a `bigint` — it sees the opaque raw-JSON token.
36
+ * A serializer that wants a DIFFERENT encoding than the exact digits (OTLP quotes
37
+ * its 64-bit fields; a round-trippable store tags them; a console encoding renders
38
+ * them as text) has to reach past the token, and `JSON.stringify` hands it the
39
+ * means: the replacer is called with the holder as `this`, and the holder still
40
+ * has the original value.
41
+ *
42
+ * Reading the holder also makes the result independent of whether the patch is
43
+ * installed, so an encoder called outside a booted kernel behaves identically.
44
+ *
45
+ * Only reach for this when the default is genuinely wrong for the destination.
46
+ * Everything that wants exact digits on the wire needs no replacer at all.
47
+ */
48
+ export function bigIntAt(holder: unknown, key: string): bigint | undefined {
49
+ const source = (holder as Record<string, unknown> | null | undefined)?.[key];
50
+ return typeof source === "bigint" ? source : undefined;
51
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./bigint-json.js";
1
2
  export * from "./cancellation.js";
2
3
  export * from "./compiled-value.js";
3
4
  export * from "./capabilities/invokable.js";
package/src/json-value.ts CHANGED
@@ -1,19 +1,20 @@
1
1
  /**
2
2
  * JSON encoding for values that cross a persistence boundary.
3
3
  *
4
- * `JSON.stringify` THROWS on a BigInt, and CEL integers surface as BigInt in
5
- * this runtime so any controller that persists a result computed in CEL
6
- * (`{ charged: 500 }` from a `Run.Sequence` output) hits it. A store that lets
7
- * that throw escape is worse than one that never persisted: the caller sees an
8
- * opaque TypeError, and a decorator built on the store can mistake it for the
9
- * body having failed.
4
+ * BigInt is encoded as a tagged object rather than as a plain string, a Number,
5
+ * or the exact digits every other JSON boundary emits: this codec has to be
6
+ * INVERTIBLE. A replayed value must equal the freshly-produced one including
7
+ * its type or at-most-once execution silently changes its answer on the second
8
+ * call. Digits would come back as a Number (lossy past 2^53), a string would come
9
+ * back a different type than went in.
10
10
  *
11
- * BigInt is encoded as a tagged object rather than a plain string or a Number:
12
- * a string would come back a different type than went in, and Number is lossy
13
- * past 2^53. A replayed value must equal the freshly-produced one, or
14
- * at-most-once execution silently changes its answer on the second call.
11
+ * That is why this file reaches past `BigInt.prototype.toJSON` with
12
+ * {@link bigIntAt} instead of inheriting the process-wide encoding: the wire wants
13
+ * the value, a store wants the value AND its type back.
15
14
  */
16
15
 
16
+ import { bigIntAt } from "./bigint-json.js";
17
+
17
18
  const BIGINT_TAG = "$bigint";
18
19
 
19
20
  interface TaggedBigInt {
@@ -32,9 +33,10 @@ function isTaggedBigInt(value: unknown): value is TaggedBigInt {
32
33
 
33
34
  /** Serialize a value to JSON text, preserving BigInt exactly. */
34
35
  export function encodeJsonValue(value: unknown): string {
35
- return JSON.stringify(value ?? null, (_k, v) =>
36
- typeof v === "bigint" ? { [BIGINT_TAG]: v.toString() } : v,
37
- );
36
+ return JSON.stringify(value ?? null, function (this: unknown, key, v) {
37
+ const source = bigIntAt(this, key);
38
+ return source === undefined ? v : { [BIGINT_TAG]: source.toString() };
39
+ });
38
40
  }
39
41
 
40
42
  /** Inverse of {@link encodeJsonValue}; BigInt values are restored as BigInt. */
@@ -94,6 +94,18 @@ export interface RuntimeCheckOptions {
94
94
  * kernel too. */
95
95
  export type CheckDiagnosticSeverity = "error" | "warning" | "info" | "hint";
96
96
 
97
+ /** A mechanically applicable repair for a finding: `replacement` is the whole
98
+ * corrected value at `path` — never a fragment — so a consumer applies it
99
+ * without parsing the language inside.
100
+ *
101
+ * That is why `path` travels with it. `source` / `line` / `column` locate a
102
+ * finding for a human reading text; a module applying a repair works on the
103
+ * parsed manifest, where a line number is not an address. A repair without its
104
+ * anchor is one nothing can apply. */
105
+ export interface CheckDiagnosticFix {
106
+ replacement: string;
107
+ }
108
+
97
109
  /** One analyzer finding, flattened to data. Positions are zero-based, matching
98
110
  * the analyzer's own range model. */
99
111
  export interface CheckDiagnostic {
@@ -104,6 +116,15 @@ export interface CheckDiagnostic {
104
116
  source?: string;
105
117
  line?: number;
106
118
  column?: number;
119
+ /** `<kind>/<name>` of the resource the finding is pinned to. */
120
+ resource?: string;
121
+ /** Dotted path of the offending value within that resource
122
+ * (`steps[0].inputs.flag`) — the address `fix` applies at. */
123
+ path?: string;
124
+ /** Present only when the repair is decidable — a fix that might not be
125
+ * correct is worse than none, since the point of the field is that it can be
126
+ * applied without review. */
127
+ fix?: CheckDiagnosticFix;
107
128
  }
108
129
 
109
130
  export interface RuntimeCheckResult {