@telorun/sdk 0.58.0 → 0.59.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,10 +1,20 @@
1
1
  import type { InvokeContext } from "./cancellation.js";
2
2
  import type { ModuleContext } from "./module-context.js";
3
+ import type { KindRef } from "./ref.js";
4
+ import { type RefResolveContext } from "./resolve-ref-instance.js";
3
5
  import { type ResourceInstance } from "./resource-instance.js";
4
6
  /** The context a decorator kind composes to dispatch its wrapped target. */
5
- export interface DispatchContext {
7
+ export interface DispatchContext extends RefResolveContext {
6
8
  invokeResolved<TInputs>(kind: string, name: string, instance: ResourceInstance, inputs: TInputs, ctx?: InvokeContext): Promise<unknown>;
7
9
  readonly moduleContext: ModuleContext;
10
+ /**
11
+ * Normalize a slot value to a {@link KindRef}: a `!ref` sentinel, an inline
12
+ * definition, or an already-normalized ref. Optional only so a caller holding
13
+ * a hand-built context still satisfies the interface — supply it whenever the
14
+ * slot can hold a value Phase 2.5 did not rewrite, which is every slot inside
15
+ * an `x-telo-scope` array.
16
+ */
17
+ ensureKindRef?(value: unknown): KindRef;
8
18
  }
9
19
  /**
10
20
  * Resolve a decorator's `invoke:` field to a live invocable and return a thunk
@@ -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;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
+ {"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,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/E,4EAA4E;AAC5E,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,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;IACtC;;;;;;OAMG;IACH,aAAa,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACzC;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,CAyBlF"}
@@ -12,10 +12,18 @@ import { getRefIdentity } from "./resource-instance.js";
12
12
  * invocation context applies unchanged.
13
13
  */
14
14
  export function resolveInvocableDispatcher(field, ctx, describe) {
15
- const target = resolveRefInstance(field, ctx, isInvocableInstance, () => `${describe()}: 'invoke'`, "Telo.Invocable");
15
+ // A `!ref` inside an `x-telo-scope` array is never rewritten by Phase 2.5, so it
16
+ // arrives as the raw sentinel — which has no `name` and would otherwise fail with
17
+ // a message pointing nowhere. `ensureKindRef` is the same rescue `ctx.resolveRef`
18
+ // performs; both resolution paths have to normalize, or a slot works only
19
+ // depending on which one its kind happens to use.
20
+ const normalized = ctx.ensureKindRef && field !== null && typeof field === "object" && !isInvocableInstance(field)
21
+ ? ctx.ensureKindRef(field)
22
+ : field;
23
+ const target = resolveRefInstance(normalized, ctx, isInvocableInstance, () => `${describe()}: 'invoke'`, "Telo.Invocable");
16
24
  // 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;
25
+ // the `!ref` identity the kernel stamped at injection, else from the ref.
26
+ const id = getRefIdentity(target) ?? normalized;
19
27
  if (!id || typeof id.kind !== "string" || typeof id.name !== "string") {
20
28
  return (inputs, invokeCtx) => target.invoke(inputs, invokeCtx);
21
29
  }
@@ -1,7 +1,17 @@
1
1
  import type { ModuleContext } from "./module-context.js";
2
+ import type { ResourceInstance } from "./resource-instance.js";
2
3
  /** The slice of `ResourceContext` needed to resolve a reference. */
3
4
  export interface RefResolveContext {
4
5
  readonly moduleContext: ModuleContext;
6
+ /**
7
+ * Resolve a bare name with the caller's own precedence — scope-local first,
8
+ * enclosing module as the fallback. Optional: a caller holding only a
9
+ * `{ moduleContext }` slice has no scope to prefer, and falls back to the
10
+ * module. Supplying it is what lets a `with:`-scoped resource reference a
11
+ * scoped sibling, and keeps `!ref` agreeing with the CEL `resources` layering
12
+ * about what a name means inside a scope.
13
+ */
14
+ resolveLocalInstance?(name: string): ResourceInstance | undefined;
5
15
  }
6
16
  /**
7
17
  * Resolve a `!ref` config field to a live instance of `T`. Controllers reach
@@ -1 +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"}
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;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC;;;;;;;OAOG;IACH,oBAAoB,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;CACnE;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"}
@@ -47,7 +47,7 @@ export function resolveRefInstance(value, ctx, guard, describe, expects) {
47
47
  // it is an alias that crosses no import boundary.
48
48
  const instance = ref.alias && ref.alias !== "Self"
49
49
  ? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
50
- : ctx.moduleContext.getInstance(ref.name);
50
+ : (ctx.resolveLocalInstance?.(ref.name) ?? ctx.moduleContext.getInstance(ref.name));
51
51
  if (!guard(instance)) {
52
52
  const label = ref.alias ? `${ref.alias}.${ref.name}` : ref.name;
53
53
  throw new RuntimeError("ERR_REF_UNRESOLVED", `${describe()} reference '${label}' did not resolve to a ${target}` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/sdk",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "description": "Telo SDK - Public API for Telo module authors.",
5
5
  "keywords": [
6
6
  "telo",
@@ -2,11 +2,11 @@ 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
4
  import type { KindRef } from "./ref.js";
5
- import { resolveRefInstance } from "./resolve-ref-instance.js";
5
+ import { resolveRefInstance, type RefResolveContext } from "./resolve-ref-instance.js";
6
6
  import { getRefIdentity, type ResourceInstance } from "./resource-instance.js";
7
7
 
8
8
  /** The context a decorator kind composes to dispatch its wrapped target. */
9
- export interface DispatchContext {
9
+ export interface DispatchContext extends RefResolveContext {
10
10
  invokeResolved<TInputs>(
11
11
  kind: string,
12
12
  name: string,
@@ -15,6 +15,14 @@ export interface DispatchContext {
15
15
  ctx?: InvokeContext,
16
16
  ): Promise<unknown>;
17
17
  readonly moduleContext: ModuleContext;
18
+ /**
19
+ * Normalize a slot value to a {@link KindRef}: a `!ref` sentinel, an inline
20
+ * definition, or an already-normalized ref. Optional only so a caller holding
21
+ * a hand-built context still satisfies the interface — supply it whenever the
22
+ * slot can hold a value Phase 2.5 did not rewrite, which is every slot inside
23
+ * an `x-telo-scope` array.
24
+ */
25
+ ensureKindRef?(value: unknown): KindRef;
18
26
  }
19
27
 
20
28
  /**
@@ -33,16 +41,25 @@ export function resolveInvocableDispatcher(
33
41
  ctx: DispatchContext,
34
42
  describe: () => string,
35
43
  ): (inputs: Record<string, unknown>, invokeCtx?: InvokeContext) => Promise<unknown> {
44
+ // A `!ref` inside an `x-telo-scope` array is never rewritten by Phase 2.5, so it
45
+ // arrives as the raw sentinel — which has no `name` and would otherwise fail with
46
+ // a message pointing nowhere. `ensureKindRef` is the same rescue `ctx.resolveRef`
47
+ // performs; both resolution paths have to normalize, or a slot works only
48
+ // depending on which one its kind happens to use.
49
+ const normalized =
50
+ ctx.ensureKindRef && field !== null && typeof field === "object" && !isInvocableInstance(field)
51
+ ? ctx.ensureKindRef(field)
52
+ : field;
36
53
  const target = resolveRefInstance(
37
- field,
54
+ normalized,
38
55
  ctx,
39
56
  isInvocableInstance,
40
57
  () => `${describe()}: 'invoke'`,
41
58
  "Telo.Invocable",
42
59
  );
43
60
  // 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);
61
+ // the `!ref` identity the kernel stamped at injection, else from the ref.
62
+ const id = getRefIdentity(target as object) ?? (normalized as Partial<KindRef> | undefined);
46
63
  if (!id || typeof id.kind !== "string" || typeof id.name !== "string") {
47
64
  return (inputs, invokeCtx) => target.invoke(inputs, invokeCtx);
48
65
  }
@@ -1,10 +1,20 @@
1
1
  import type { ModuleContext } from "./module-context.js";
2
2
  import type { KindRef } from "./ref.js";
3
+ import type { ResourceInstance } from "./resource-instance.js";
3
4
  import { RuntimeError } from "./types.js";
4
5
 
5
6
  /** The slice of `ResourceContext` needed to resolve a reference. */
6
7
  export interface RefResolveContext {
7
8
  readonly moduleContext: ModuleContext;
9
+ /**
10
+ * Resolve a bare name with the caller's own precedence — scope-local first,
11
+ * enclosing module as the fallback. Optional: a caller holding only a
12
+ * `{ moduleContext }` slice has no scope to prefer, and falls back to the
13
+ * module. Supplying it is what lets a `with:`-scoped resource reference a
14
+ * scoped sibling, and keeps `!ref` agreeing with the CEL `resources` layering
15
+ * about what a name means inside a scope.
16
+ */
17
+ resolveLocalInstance?(name: string): ResourceInstance | undefined;
8
18
  }
9
19
 
10
20
  /**
@@ -67,7 +77,7 @@ export function resolveRefInstance<T>(
67
77
  const instance =
68
78
  ref.alias && ref.alias !== "Self"
69
79
  ? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
70
- : ctx.moduleContext.getInstance(ref.name);
80
+ : (ctx.resolveLocalInstance?.(ref.name) ?? ctx.moduleContext.getInstance(ref.name));
71
81
 
72
82
  if (!guard(instance)) {
73
83
  const label = ref.alias ? `${ref.alias}.${ref.name}` : ref.name;