@telorun/sdk 0.50.0 → 0.56.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.
- package/dist/dispatch-invoke-ref.d.ts.map +1 -1
- package/dist/dispatch-invoke-ref.js +12 -17
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/json-value.d.ts +20 -0
- package/dist/json-value.d.ts.map +1 -0
- package/dist/json-value.js +31 -0
- package/dist/ref.d.ts +1 -1
- package/dist/ref.d.ts.map +1 -1
- package/dist/ref.js +1 -1
- package/dist/resolve-ref-instance.d.ts +39 -0
- package/dist/resolve-ref-instance.d.ts.map +1 -0
- package/dist/resolve-ref-instance.js +57 -0
- package/dist/resource-context.d.ts +24 -0
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/dispatch-invoke-ref.ts +20 -20
- package/src/index.ts +2 -0
- package/src/json-value.ts +43 -0
- package/src/ref.ts +1 -1
- package/src/resolve-ref-instance.ts +81 -0
- package/src/resource-context.ts +29 -0
- package/src/types.ts +1 -1
|
@@ -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;
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
+
}
|
package/dist/ref.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export type Injected<T> = {
|
|
|
44
44
|
/** Returns a schema node that emits `x-telo-ref` for buildReferenceFieldMap and carries
|
|
45
45
|
* KindRef<T> as its TypeScript type. For TypeBox schemas use Type.Unsafe<KindRef<T>>(Ref(...)).
|
|
46
46
|
*
|
|
47
|
-
* @param ref
|
|
47
|
+
* @param ref The slot's `x-telo-ref` constraint, e.g. "Sql.Connection" or "Telo.Invocable" */
|
|
48
48
|
export declare const Ref: <T = ResourceInstance>(ref: string) => KindRef<T>;
|
|
49
49
|
/** Returns a schema node that emits `x-telo-scope` for buildReferenceFieldMap and carries
|
|
50
50
|
* ScopeRef as its TypeScript type. For TypeBox schemas use Type.Unsafe<ScopeRef>(Scope(...)).
|
package/dist/ref.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../src/ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;;;gFAOgF;AAChF,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,gBAAgB;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;CACrB;AAED;6DAC6D;AAC7D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B;;;;;uEAKmE;IACnE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC7D;AAED;oFACoF;AACpF,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7D;AAED;;;;qCAIqC;AACrC,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KACvB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GACzC,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,GAC7B,CAAC,EAAE,GACH,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GAChC,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,GACrC,CAAC,CAAC,CAAC,CAAC;CACb,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../src/ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;;;gFAOgF;AAChF,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,gBAAgB;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;CACrB;AAED;6DAC6D;AAC7D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B;;;;;uEAKmE;IACnE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC7D;AAED;oFACoF;AACpF,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7D;AAED;;;;qCAIqC;AACrC,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KACvB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GACzC,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,GAC7B,CAAC,EAAE,GACH,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GAChC,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,GACrC,CAAC,CAAC,CAAC,CAAC;CACb,CAAC;AAEF;;;+FAG+F;AAC/F,eAAO,MAAM,GAAG,GAAI,CAAC,GAAG,gBAAgB,EAAE,KAAK,MAAM,KAAG,OAAO,CAAC,CAAC,CACf,CAAC;AAEnD;;;;wEAIwE;AACxE,eAAO,MAAM,KAAK,GAAI,gBAAgB,MAAM,GAAG,MAAM,EAAE,KAAG,QACG,CAAC"}
|
package/dist/ref.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Returns a schema node that emits `x-telo-ref` for buildReferenceFieldMap and carries
|
|
2
2
|
* KindRef<T> as its TypeScript type. For TypeBox schemas use Type.Unsafe<KindRef<T>>(Ref(...)).
|
|
3
3
|
*
|
|
4
|
-
* @param ref
|
|
4
|
+
* @param ref The slot's `x-telo-ref` constraint, e.g. "Sql.Connection" or "Telo.Invocable" */
|
|
5
5
|
export const Ref = (ref) => ({ "x-telo-ref": ref });
|
|
6
6
|
/** Returns a schema node that emits `x-telo-scope` for buildReferenceFieldMap and carries
|
|
7
7
|
* ScopeRef as its TypeScript type. For TypeBox schemas use Type.Unsafe<ScopeRef>(Scope(...)).
|
|
@@ -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 (`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'`, "KvStore.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 (`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'`, "KvStore.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 (`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/dist/types.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface ResourceDefinition {
|
|
|
45
45
|
* Resolved against the declaring file's `Telo.Import` declarations — same pattern as
|
|
46
46
|
* kind prefixes (`kind: Http.Api`). Orthogonal to `capability` (lifecycle role); the
|
|
47
47
|
* analyzer populates its `extendedBy` index from this field so references typed via
|
|
48
|
-
* `x-telo-ref:
|
|
48
|
+
* `x-telo-ref: <Alias>.<Abstract>` accept this definition. */
|
|
49
49
|
extends?: string;
|
|
50
50
|
/** Type reference (named, inline `Type.JsonSchema`, or raw schema) for the
|
|
51
51
|
* resource's `invoke()` inputs / outputs. Read by the analyzer's CEL typing
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;0CAE0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
}
|
package/src/ref.ts
CHANGED
|
@@ -56,7 +56,7 @@ export type Injected<T> = {
|
|
|
56
56
|
/** Returns a schema node that emits `x-telo-ref` for buildReferenceFieldMap and carries
|
|
57
57
|
* KindRef<T> as its TypeScript type. For TypeBox schemas use Type.Unsafe<KindRef<T>>(Ref(...)).
|
|
58
58
|
*
|
|
59
|
-
* @param ref
|
|
59
|
+
* @param ref The slot's `x-telo-ref` constraint, e.g. "Sql.Connection" or "Telo.Invocable" */
|
|
60
60
|
export const Ref = <T = ResourceInstance>(ref: string): KindRef<T> =>
|
|
61
61
|
({ "x-telo-ref": ref } as unknown as KindRef<T>);
|
|
62
62
|
|
|
@@ -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 (`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'`, "KvStore.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
|
+
}
|
package/src/resource-context.ts
CHANGED
|
@@ -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 (`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;
|
package/src/types.ts
CHANGED
|
@@ -51,7 +51,7 @@ export interface ResourceDefinition {
|
|
|
51
51
|
* Resolved against the declaring file's `Telo.Import` declarations — same pattern as
|
|
52
52
|
* kind prefixes (`kind: Http.Api`). Orthogonal to `capability` (lifecycle role); the
|
|
53
53
|
* analyzer populates its `extendedBy` index from this field so references typed via
|
|
54
|
-
* `x-telo-ref:
|
|
54
|
+
* `x-telo-ref: <Alias>.<Abstract>` accept this definition. */
|
|
55
55
|
extends?: string;
|
|
56
56
|
/** Type reference (named, inline `Type.JsonSchema`, or raw schema) for the
|
|
57
57
|
* resource's `invoke()` inputs / outputs. Read by the analyzer's CEL typing
|