@telorun/sdk 0.50.0 → 0.54.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 +1 @@
1
- {"version":3,"file":"dispatch-invoke-ref.d.ts","sourceRoot":"","sources":["../src/dispatch-invoke-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/E,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,OAAO,EACf,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;CACvC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,MAAM,MAAM,GACrB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAsBlF"}
1
+ {"version":3,"file":"dispatch-invoke-ref.d.ts","sourceRoot":"","sources":["../src/dispatch-invoke-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/E,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,OAAO,EACf,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;CACvC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,MAAM,MAAM,GACrB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAgBlF"}
@@ -1,3 +1,4 @@
1
+ import { resolveRefInstance } from "./resolve-ref-instance.js";
1
2
  import { getRefIdentity } from "./resource-instance.js";
2
3
  /**
3
4
  * Resolve a decorator's `invoke:` field to a live invocable and return a thunk
@@ -11,22 +12,16 @@ import { getRefIdentity } from "./resource-instance.js";
11
12
  * invocation context applies unchanged.
12
13
  */
13
14
  export function resolveInvocableDispatcher(field, ctx, describe) {
14
- if (field && typeof field.invoke === "function") {
15
- const instance = field;
16
- const id = getRefIdentity(field);
17
- return (inputs, invokeCtx) => id
18
- ? ctx.invokeResolved(id.kind, id.name, instance, inputs, invokeCtx)
19
- : instance.invoke(inputs, invokeCtx);
15
+ const target = resolveRefInstance(field, ctx, isInvocableInstance, () => `${describe()}: 'invoke'`, "telo#Invocable");
16
+ // Dispatch through the traced chokepoint needs the target's kind+name: from
17
+ // the `!ref` identity the kernel stamped at injection, else from the raw ref.
18
+ const id = getRefIdentity(target) ?? field;
19
+ if (!id || typeof id.kind !== "string" || typeof id.name !== "string") {
20
+ return (inputs, invokeCtx) => target.invoke(inputs, invokeCtx);
20
21
  }
21
- const ref = field;
22
- if (!ref || typeof ref.name !== "string") {
23
- throw new Error(`${describe()}: 'invoke' must reference an invocable.`);
24
- }
25
- const resolved = (ref.alias && ref.alias !== "Self"
26
- ? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
27
- : ctx.moduleContext.getInstance(ref.name));
28
- if (!resolved || typeof resolved.invoke !== "function") {
29
- throw new Error(`${describe()}: 'invoke' reference '${ref.name}' did not resolve to an invocable.`);
30
- }
31
- return (inputs, invokeCtx) => ctx.invokeResolved(ref.kind, ref.name, resolved, inputs, invokeCtx);
22
+ const { kind, name } = id;
23
+ return (inputs, invokeCtx) => ctx.invokeResolved(kind, name, target, inputs, invokeCtx);
24
+ }
25
+ function isInvocableInstance(value) {
26
+ return typeof value?.invoke === "function";
32
27
  }
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ export * from "./capabilities/provider.js";
9
9
  export * from "./capabilities/runnable.js";
10
10
  export * from "./context-provider.js";
11
11
  export * from "./duration.js";
12
+ export * from "./json-value.js";
13
+ export * from "./resolve-ref-instance.js";
12
14
  export * from "./controller-context.js";
13
15
  export * from "./controller-policy.js";
14
16
  export * from "./evaluation-context.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,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,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,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,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,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -9,6 +9,8 @@ export * from "./capabilities/provider.js";
9
9
  export * from "./capabilities/runnable.js";
10
10
  export * from "./context-provider.js";
11
11
  export * from "./duration.js";
12
+ export * from "./json-value.js";
13
+ export * from "./resolve-ref-instance.js";
12
14
  export * from "./controller-context.js";
13
15
  export * from "./controller-policy.js";
14
16
  export * from "./evaluation-context.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * JSON encoding for values that cross a persistence boundary.
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.
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.
15
+ */
16
+ /** Serialize a value to JSON text, preserving BigInt exactly. */
17
+ export declare function encodeJsonValue(value: unknown): string;
18
+ /** Inverse of {@link encodeJsonValue}; BigInt values are restored as BigInt. */
19
+ export declare function decodeJsonValue(text: string): unknown;
20
+ //# sourceMappingURL=json-value.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * JSON encoding for values that cross a persistence boundary.
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.
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.
15
+ */
16
+ const BIGINT_TAG = "$bigint";
17
+ function isTaggedBigInt(value) {
18
+ return (typeof value === "object" &&
19
+ value !== null &&
20
+ !Array.isArray(value) &&
21
+ typeof value[BIGINT_TAG] === "string" &&
22
+ Object.keys(value).length === 1);
23
+ }
24
+ /** Serialize a value to JSON text, preserving BigInt exactly. */
25
+ export function encodeJsonValue(value) {
26
+ return JSON.stringify(value ?? null, (_k, v) => typeof v === "bigint" ? { [BIGINT_TAG]: v.toString() } : v);
27
+ }
28
+ /** Inverse of {@link encodeJsonValue}; BigInt values are restored as BigInt. */
29
+ export function decodeJsonValue(text) {
30
+ return JSON.parse(text, (_k, v) => (isTaggedBigInt(v) ? BigInt(v[BIGINT_TAG]) : v));
31
+ }
@@ -0,0 +1,39 @@
1
+ import type { ModuleContext } from "./module-context.js";
2
+ /** The slice of `ResourceContext` needed to resolve a reference. */
3
+ export interface RefResolveContext {
4
+ readonly moduleContext: ModuleContext;
5
+ }
6
+ /**
7
+ * Resolve a `!ref` config field to a live instance of `T`. Controllers reach
8
+ * this as `ctx.resolveRef(value, guard, describe, expects)`; the standalone form
9
+ * is for callers holding only a `{ moduleContext }` slice rather than a full
10
+ * `ResourceContext`.
11
+ *
12
+ * Phase 5 injection normally replaces the slot with the live `ResourceInstance`
13
+ * before `init()` — local and cross-module refs alike, since injection resolves
14
+ * an aliased ref through the import's export table (and defers, rather than
15
+ * leaving a raw ref, when the import hasn't published its exports yet). So the
16
+ * common path here is the guard short-circuit.
17
+ *
18
+ * A raw {@link KindRef} still reaches a controller where injection does not
19
+ * reach the slot: a kind whose definition yields no field map, or a ref the
20
+ * controller obtained itself via `ctx.ensureKindRef`. Both are gaps worth
21
+ * closing in the kernel — until they are, both shapes must be accepted here, and
22
+ * an aliased ref routes through the import's exported scope because a bare local
23
+ * lookup would miss it.
24
+ *
25
+ * `guard` decides what counts as the right kind of instance — a duck-type check
26
+ * on the methods the caller will actually invoke, so a mis-wired ref fails with a
27
+ * clear message here rather than as `undefined is not a function` later.
28
+ * `describe` labels the owning resource and slot; `expects` names the contract
29
+ * the slot wants — the slot's own `x-telo-ref` string (`std/cache#Store`) — so
30
+ * the message says what was missing, not just that something was.
31
+ *
32
+ * @example
33
+ * const store = resolveRefInstance(
34
+ * this.resource.store, this.ctx, isKvStore,
35
+ * () => `Idempotency.Once "${name}": 'store'`, "std/kv-store#Store",
36
+ * );
37
+ */
38
+ export declare function resolveRefInstance<T>(value: unknown, ctx: RefResolveContext, guard: (candidate: unknown) => candidate is T, describe: () => string, expects?: string): T;
39
+ //# sourceMappingURL=resolve-ref-instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-ref-instance.d.ts","sourceRoot":"","sources":["../src/resolve-ref-instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,IAAI,CAAC,EAC7C,QAAQ,EAAE,MAAM,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,CAAC,CAiCH"}
@@ -0,0 +1,57 @@
1
+ import { RuntimeError } from "./types.js";
2
+ /**
3
+ * Resolve a `!ref` config field to a live instance of `T`. Controllers reach
4
+ * this as `ctx.resolveRef(value, guard, describe, expects)`; the standalone form
5
+ * is for callers holding only a `{ moduleContext }` slice rather than a full
6
+ * `ResourceContext`.
7
+ *
8
+ * Phase 5 injection normally replaces the slot with the live `ResourceInstance`
9
+ * before `init()` — local and cross-module refs alike, since injection resolves
10
+ * an aliased ref through the import's export table (and defers, rather than
11
+ * leaving a raw ref, when the import hasn't published its exports yet). So the
12
+ * common path here is the guard short-circuit.
13
+ *
14
+ * A raw {@link KindRef} still reaches a controller where injection does not
15
+ * reach the slot: a kind whose definition yields no field map, or a ref the
16
+ * controller obtained itself via `ctx.ensureKindRef`. Both are gaps worth
17
+ * closing in the kernel — until they are, both shapes must be accepted here, and
18
+ * an aliased ref routes through the import's exported scope because a bare local
19
+ * lookup would miss it.
20
+ *
21
+ * `guard` decides what counts as the right kind of instance — a duck-type check
22
+ * on the methods the caller will actually invoke, so a mis-wired ref fails with a
23
+ * clear message here rather than as `undefined is not a function` later.
24
+ * `describe` labels the owning resource and slot; `expects` names the contract
25
+ * the slot wants — the slot's own `x-telo-ref` string (`std/cache#Store`) — so
26
+ * the message says what was missing, not just that something was.
27
+ *
28
+ * @example
29
+ * const store = resolveRefInstance(
30
+ * this.resource.store, this.ctx, isKvStore,
31
+ * () => `Idempotency.Once "${name}": 'store'`, "std/kv-store#Store",
32
+ * );
33
+ */
34
+ export function resolveRefInstance(value, ctx, guard, describe, expects) {
35
+ // Phase-5-injected: already the instance.
36
+ if (guard(value))
37
+ return value;
38
+ const target = expects ? `resource satisfying \`${expects}\`` : "resource";
39
+ if (value === undefined || value === null) {
40
+ throw new RuntimeError("ERR_REF_REQUIRED", `${describe()} is required — reference a ${target}.`);
41
+ }
42
+ const ref = value;
43
+ if (typeof ref.name !== "string") {
44
+ throw new RuntimeError("ERR_REF_UNRESOLVED", `${describe()} must be a \`!ref\` to a ${target}.`);
45
+ }
46
+ // `Self` names the declaring library's own scope, so it resolves locally —
47
+ // it is an alias that crosses no import boundary.
48
+ const instance = ref.alias && ref.alias !== "Self"
49
+ ? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
50
+ : ctx.moduleContext.getInstance(ref.name);
51
+ if (!guard(instance)) {
52
+ const label = ref.alias ? `${ref.alias}.${ref.name}` : ref.name;
53
+ throw new RuntimeError("ERR_REF_UNRESOLVED", `${describe()} reference '${label}' did not resolve to a ${target}` +
54
+ `${instance === undefined ? " (nothing is registered under that name)" : ""}.`);
55
+ }
56
+ return instance;
57
+ }
@@ -5,6 +5,7 @@ import type { LoggingHost } from "./log-sink.js";
5
5
  import { ControllerPolicy } from "./controller-policy.js";
6
6
  import { EvaluationContext } from "./evaluation-context.js";
7
7
  import { ModuleContext } from "./module-context.js";
8
+ import type { KindRef } from "./ref.js";
8
9
  import { ResourceInstance } from "./resource-instance.js";
9
10
  import { ResourceManifest } from "./resource-manifest.js";
10
11
  import { RuntimeResource } from "./runtime-resource.js";
@@ -71,10 +72,33 @@ export interface ResourceContext extends ControllerContext {
71
72
  spawnChildContext(): EvaluationContext;
72
73
  transientChild(context: Record<string, any>): EvaluationContext;
73
74
  withManifests<T>(manifests: any[], fn: () => T): T;
75
+ /**
76
+ * Normalize a nested slot value to a {@link KindRef}. The value is an inline
77
+ * definition (`{ kind, …config }`), an already-normalized `{ kind, name }`
78
+ * ref, or a `!ref` sentinel. An inline definition is *registered* into this
79
+ * module's scope first — minting `resourceName` (or a generated one) as its
80
+ * name — so the returned ref always points at a resource the kernel knows.
81
+ *
82
+ * The inverse of {@link resolveRef}: this goes slot value → ref, that goes
83
+ * ref → live instance. Controllers that dispatch through
84
+ * `invokeResolved(kind, name, …)` want the ref, so the invocation keeps its
85
+ * identity for tracing and error wrapping.
86
+ */
87
+ ensureKindRef(value: any, resourceName?: string): KindRef;
88
+ /** @deprecated Renamed to {@link ensureKindRef} — it produces a reference
89
+ * (registering an inline definition on the way), it does not resolve one. */
74
90
  resolveChildren(resource: any, resourceName?: string): {
75
91
  kind: string;
76
92
  name: string;
77
93
  };
94
+ /**
95
+ * Resolve a `!ref` config field to a live instance of `T`. See
96
+ * {@link resolveRefInstance} — this is the same resolution, reached through
97
+ * the context a controller already holds. `expects` names the contract the
98
+ * slot wants — its `x-telo-ref` string (`std/cache#Store`) — so a mis-wire
99
+ * says what was missing.
100
+ */
101
+ resolveRef<T>(value: unknown, guard: (candidate: unknown) => candidate is T, describe: () => string, expects?: string): T;
78
102
  validateSchema(value: any, schema: any): void;
79
103
  createSchemaValidator(schema: any): DataValidator;
80
104
  registerSchema(name: string, schema: object): void;
@@ -1 +1 @@
1
- {"version":3,"file":"resource-context.d.ts","sourceRoot":"","sources":["../src/resource-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resource-context.d.ts","sourceRoot":"","sources":["../src/resource-context.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/sdk",
3
- "version": "0.50.0",
3
+ "version": "0.54.0",
4
4
  "description": "Telo SDK - Public API for Telo module authors.",
5
5
  "keywords": [
6
6
  "telo",
@@ -1,6 +1,8 @@
1
1
  import type { InvokeContext } from "./cancellation.js";
2
2
  import type { Invocable } from "./capabilities/invokable.js";
3
3
  import type { ModuleContext } from "./module-context.js";
4
+ import type { KindRef } from "./ref.js";
5
+ import { resolveRefInstance } from "./resolve-ref-instance.js";
4
6
  import { getRefIdentity, type ResourceInstance } from "./resource-instance.js";
5
7
 
6
8
  /** The context a decorator kind composes to dispatch its wrapped target. */
@@ -31,25 +33,23 @@ export function resolveInvocableDispatcher(
31
33
  ctx: DispatchContext,
32
34
  describe: () => string,
33
35
  ): (inputs: Record<string, unknown>, invokeCtx?: InvokeContext) => Promise<unknown> {
34
- if (field && typeof (field as Invocable).invoke === "function") {
35
- const instance = field as ResourceInstance & Invocable;
36
- const id = getRefIdentity(field as object);
37
- return (inputs, invokeCtx) =>
38
- id
39
- ? ctx.invokeResolved(id.kind, id.name, instance, inputs, invokeCtx)
40
- : instance.invoke(inputs, invokeCtx);
36
+ const target = resolveRefInstance(
37
+ field,
38
+ ctx,
39
+ isInvocableInstance,
40
+ () => `${describe()}: 'invoke'`,
41
+ "telo#Invocable",
42
+ );
43
+ // Dispatch through the traced chokepoint needs the target's kind+name: from
44
+ // the `!ref` identity the kernel stamped at injection, else from the raw ref.
45
+ const id = getRefIdentity(target as object) ?? (field as Partial<KindRef> | undefined);
46
+ if (!id || typeof id.kind !== "string" || typeof id.name !== "string") {
47
+ return (inputs, invokeCtx) => target.invoke(inputs, invokeCtx);
41
48
  }
42
- const ref = field as { kind: string; name: string; alias?: string } | undefined;
43
- if (!ref || typeof ref.name !== "string") {
44
- throw new Error(`${describe()}: 'invoke' must reference an invocable.`);
45
- }
46
- const resolved = (
47
- ref.alias && ref.alias !== "Self"
48
- ? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
49
- : ctx.moduleContext.getInstance(ref.name)
50
- ) as ResourceInstance | undefined;
51
- if (!resolved || typeof resolved.invoke !== "function") {
52
- throw new Error(`${describe()}: 'invoke' reference '${ref.name}' did not resolve to an invocable.`);
53
- }
54
- return (inputs, invokeCtx) => ctx.invokeResolved(ref.kind, ref.name, resolved, inputs, invokeCtx);
49
+ const { kind, name } = id;
50
+ return (inputs, invokeCtx) => ctx.invokeResolved(kind, name, target, inputs, invokeCtx);
51
+ }
52
+
53
+ function isInvocableInstance(value: unknown): value is ResourceInstance & Invocable {
54
+ return typeof (value as Invocable | undefined)?.invoke === "function";
55
55
  }
package/src/index.ts CHANGED
@@ -9,6 +9,8 @@ export * from "./capabilities/provider.js";
9
9
  export * from "./capabilities/runnable.js";
10
10
  export * from "./context-provider.js";
11
11
  export * from "./duration.js";
12
+ export * from "./json-value.js";
13
+ export * from "./resolve-ref-instance.js";
12
14
  export * from "./controller-context.js";
13
15
  export * from "./controller-policy.js";
14
16
  export * from "./evaluation-context.js";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * JSON encoding for values that cross a persistence boundary.
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.
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.
15
+ */
16
+
17
+ const BIGINT_TAG = "$bigint";
18
+
19
+ interface TaggedBigInt {
20
+ [BIGINT_TAG]: string;
21
+ }
22
+
23
+ function isTaggedBigInt(value: unknown): value is TaggedBigInt {
24
+ return (
25
+ typeof value === "object" &&
26
+ value !== null &&
27
+ !Array.isArray(value) &&
28
+ typeof (value as TaggedBigInt)[BIGINT_TAG] === "string" &&
29
+ Object.keys(value).length === 1
30
+ );
31
+ }
32
+
33
+ /** Serialize a value to JSON text, preserving BigInt exactly. */
34
+ export function encodeJsonValue(value: unknown): string {
35
+ return JSON.stringify(value ?? null, (_k, v) =>
36
+ typeof v === "bigint" ? { [BIGINT_TAG]: v.toString() } : v,
37
+ );
38
+ }
39
+
40
+ /** Inverse of {@link encodeJsonValue}; BigInt values are restored as BigInt. */
41
+ export function decodeJsonValue(text: string): unknown {
42
+ return JSON.parse(text, (_k, v) => (isTaggedBigInt(v) ? BigInt(v[BIGINT_TAG]) : v));
43
+ }
@@ -0,0 +1,81 @@
1
+ import type { ModuleContext } from "./module-context.js";
2
+ import type { KindRef } from "./ref.js";
3
+ import { RuntimeError } from "./types.js";
4
+
5
+ /** The slice of `ResourceContext` needed to resolve a reference. */
6
+ export interface RefResolveContext {
7
+ readonly moduleContext: ModuleContext;
8
+ }
9
+
10
+ /**
11
+ * Resolve a `!ref` config field to a live instance of `T`. Controllers reach
12
+ * this as `ctx.resolveRef(value, guard, describe, expects)`; the standalone form
13
+ * is for callers holding only a `{ moduleContext }` slice rather than a full
14
+ * `ResourceContext`.
15
+ *
16
+ * Phase 5 injection normally replaces the slot with the live `ResourceInstance`
17
+ * before `init()` — local and cross-module refs alike, since injection resolves
18
+ * an aliased ref through the import's export table (and defers, rather than
19
+ * leaving a raw ref, when the import hasn't published its exports yet). So the
20
+ * common path here is the guard short-circuit.
21
+ *
22
+ * A raw {@link KindRef} still reaches a controller where injection does not
23
+ * reach the slot: a kind whose definition yields no field map, or a ref the
24
+ * controller obtained itself via `ctx.ensureKindRef`. Both are gaps worth
25
+ * closing in the kernel — until they are, both shapes must be accepted here, and
26
+ * an aliased ref routes through the import's exported scope because a bare local
27
+ * lookup would miss it.
28
+ *
29
+ * `guard` decides what counts as the right kind of instance — a duck-type check
30
+ * on the methods the caller will actually invoke, so a mis-wired ref fails with a
31
+ * clear message here rather than as `undefined is not a function` later.
32
+ * `describe` labels the owning resource and slot; `expects` names the contract
33
+ * the slot wants — the slot's own `x-telo-ref` string (`std/cache#Store`) — so
34
+ * the message says what was missing, not just that something was.
35
+ *
36
+ * @example
37
+ * const store = resolveRefInstance(
38
+ * this.resource.store, this.ctx, isKvStore,
39
+ * () => `Idempotency.Once "${name}": 'store'`, "std/kv-store#Store",
40
+ * );
41
+ */
42
+ export function resolveRefInstance<T>(
43
+ value: unknown,
44
+ ctx: RefResolveContext,
45
+ guard: (candidate: unknown) => candidate is T,
46
+ describe: () => string,
47
+ expects?: string,
48
+ ): T {
49
+ // Phase-5-injected: already the instance.
50
+ if (guard(value)) return value;
51
+
52
+ const target = expects ? `resource satisfying \`${expects}\`` : "resource";
53
+ if (value === undefined || value === null) {
54
+ throw new RuntimeError("ERR_REF_REQUIRED", `${describe()} is required — reference a ${target}.`);
55
+ }
56
+
57
+ const ref = value as Partial<KindRef<T>>;
58
+ if (typeof ref.name !== "string") {
59
+ throw new RuntimeError(
60
+ "ERR_REF_UNRESOLVED",
61
+ `${describe()} must be a \`!ref\` to a ${target}.`,
62
+ );
63
+ }
64
+
65
+ // `Self` names the declaring library's own scope, so it resolves locally —
66
+ // it is an alias that crosses no import boundary.
67
+ const instance =
68
+ ref.alias && ref.alias !== "Self"
69
+ ? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
70
+ : ctx.moduleContext.getInstance(ref.name);
71
+
72
+ if (!guard(instance)) {
73
+ const label = ref.alias ? `${ref.alias}.${ref.name}` : ref.name;
74
+ throw new RuntimeError(
75
+ "ERR_REF_UNRESOLVED",
76
+ `${describe()} reference '${label}' did not resolve to a ${target}` +
77
+ `${instance === undefined ? " (nothing is registered under that name)" : ""}.`,
78
+ );
79
+ }
80
+ return instance;
81
+ }
@@ -5,6 +5,7 @@ import type { LoggingHost } from "./log-sink.js";
5
5
  import { ControllerPolicy } from "./controller-policy.js";
6
6
  import { EvaluationContext } from "./evaluation-context.js";
7
7
  import { ModuleContext } from "./module-context.js";
8
+ import type { KindRef } from "./ref.js";
8
9
  import { ResourceInstance } from "./resource-instance.js";
9
10
  import { ResourceManifest } from "./resource-manifest.js";
10
11
  import { RuntimeResource } from "./runtime-resource.js";
@@ -86,7 +87,35 @@ export interface ResourceContext extends ControllerContext {
86
87
  spawnChildContext(): EvaluationContext;
87
88
  transientChild(context: Record<string, any>): EvaluationContext;
88
89
  withManifests<T>(manifests: any[], fn: () => T): T;
90
+ /**
91
+ * Normalize a nested slot value to a {@link KindRef}. The value is an inline
92
+ * definition (`{ kind, …config }`), an already-normalized `{ kind, name }`
93
+ * ref, or a `!ref` sentinel. An inline definition is *registered* into this
94
+ * module's scope first — minting `resourceName` (or a generated one) as its
95
+ * name — so the returned ref always points at a resource the kernel knows.
96
+ *
97
+ * The inverse of {@link resolveRef}: this goes slot value → ref, that goes
98
+ * ref → live instance. Controllers that dispatch through
99
+ * `invokeResolved(kind, name, …)` want the ref, so the invocation keeps its
100
+ * identity for tracing and error wrapping.
101
+ */
102
+ ensureKindRef(value: any, resourceName?: string): KindRef;
103
+ /** @deprecated Renamed to {@link ensureKindRef} — it produces a reference
104
+ * (registering an inline definition on the way), it does not resolve one. */
89
105
  resolveChildren(resource: any, resourceName?: string): { kind: string; name: string };
106
+ /**
107
+ * Resolve a `!ref` config field to a live instance of `T`. See
108
+ * {@link resolveRefInstance} — this is the same resolution, reached through
109
+ * the context a controller already holds. `expects` names the contract the
110
+ * slot wants — its `x-telo-ref` string (`std/cache#Store`) — so a mis-wire
111
+ * says what was missing.
112
+ */
113
+ resolveRef<T>(
114
+ value: unknown,
115
+ guard: (candidate: unknown) => candidate is T,
116
+ describe: () => string,
117
+ expects?: string,
118
+ ): T;
90
119
  validateSchema(value: any, schema: any): void;
91
120
  createSchemaValidator(schema: any): DataValidator;
92
121
  registerSchema(name: string, schema: object): void;