@telorun/sdk 0.2.7 → 0.3.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/README.md +54 -0
- package/dist/compiled-value.d.ts +2 -0
- package/dist/compiled-value.d.ts.map +1 -1
- package/dist/evaluation-context.d.ts +16 -102
- package/dist/evaluation-context.d.ts.map +1 -1
- package/dist/evaluation-context.js +0 -390
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/invoke-error.d.ts +14 -0
- package/dist/invoke-error.d.ts.map +1 -0
- package/dist/invoke-error.js +34 -0
- package/dist/module-context.d.ts +12 -36
- package/dist/module-context.d.ts.map +1 -1
- package/dist/module-context.js +1 -177
- package/dist/ref.d.ts +1 -1
- package/dist/ref.d.ts.map +1 -1
- package/dist/ref.js +1 -1
- package/dist/resource-context.d.ts +17 -0
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -1
- package/src/compiled-value.ts +2 -0
- package/src/evaluation-context.ts +45 -461
- package/src/index.ts +1 -0
- package/src/invoke-error.ts +39 -0
- package/src/module-context.ts +22 -203
- package/src/ref.ts +1 -1
- package/src/resource-context.ts +23 -0
- package/src/types.ts +18 -0
- package/dist/capability-definition.d.ts +0 -8
- package/dist/capability-definition.d.ts.map +0 -1
- package/dist/capability-definition.js +0 -21
- package/dist/cel-environment.d.ts +0 -3
- package/dist/cel-environment.d.ts.map +0 -1
- package/dist/cel-environment.js +0 -13
- package/dist/execution-context.d.ts +0 -13
- package/dist/execution-context.d.ts.map +0 -1
- package/dist/execution-context.js +0 -13
- package/src/execution-context.ts +0 -21
package/README.md
CHANGED
|
@@ -16,6 +16,60 @@ Early prototype. APIs and contracts are still evolving. The API surface - includ
|
|
|
16
16
|
|
|
17
17
|
Use the SDK when building or extending Telo modules. It is not the kernel itself; it is the contract layer that keeps module behavior consistent and predictable.
|
|
18
18
|
|
|
19
|
+
## Errors
|
|
20
|
+
|
|
21
|
+
Telo distinguishes two kinds of failure from an `Invocable` / `Runnable`:
|
|
22
|
+
|
|
23
|
+
- **Operational failures** — plain `Error` or `RuntimeError` throws. Propagate to the kernel's infrastructure layer (HTTP → Fastify 5xx, sequence → bubbles up). These represent bugs or environment failure.
|
|
24
|
+
- **Domain failures** — `InvokeError` throws. Part of the invocable's public contract. Route handlers match on `error.code` via `catches:` entries; sequences handle them in `try`/`catch`.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { InvokeError, isInvokeError } from "@telorun/sdk";
|
|
28
|
+
|
|
29
|
+
// In a controller:
|
|
30
|
+
throw new InvokeError("UNAUTHORIZED", "Token missing or invalid", {
|
|
31
|
+
reason: "expired",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Anywhere a thrown value crosses a boundary:
|
|
35
|
+
if (isInvokeError(err)) {
|
|
36
|
+
// err.code, err.message, err.data
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `isInvokeError(err)` for recognition — it's dual-realm-safe (survives pnpm hoist splits, registry-loaded modules with their own SDK copy, and future sandbox isolation). `instanceof InvokeError` is not reliable across package boundaries.
|
|
41
|
+
|
|
42
|
+
Controllers that throw `InvokeError` **must** declare their codes in their `Telo.Definition`:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
kind: Telo.Definition
|
|
46
|
+
metadata: { name: VerifyToken }
|
|
47
|
+
capability: Telo.Invocable
|
|
48
|
+
throws:
|
|
49
|
+
codes:
|
|
50
|
+
UNAUTHORIZED: { description: Missing or invalid token. }
|
|
51
|
+
EXPIRED:
|
|
52
|
+
description: Token is past its expires_at.
|
|
53
|
+
data:
|
|
54
|
+
type: object
|
|
55
|
+
properties:
|
|
56
|
+
expiredAt: { type: string, format: date-time }
|
|
57
|
+
required: [expiredAt]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Undeclared codes emit an `${kind}.${name}.InvokeRejected.Undeclared` observability event — the analyzer catches these statically.
|
|
61
|
+
|
|
62
|
+
Composers that propagate rather than originate codes can declare:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
throws:
|
|
66
|
+
inherit: true # union of everything I call (requires x-telo-step-context)
|
|
67
|
+
# or
|
|
68
|
+
passthrough: true # union is whatever my inputs.code resolves to (Run.Throw-style)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`inherit` is driven by the analyzer's dataflow pass over `x-telo-step-context` arrays; future composers opt in by declaring both the annotation and `inherit: true`. See [modules/run/docs/structured-errors.md](../../modules/run/docs/structured-errors.md) for the end-to-end flow.
|
|
72
|
+
|
|
19
73
|
## Related Docs
|
|
20
74
|
|
|
21
75
|
- Kernel overview: [kernel/README.md](../../kernel/README.md)
|
package/dist/compiled-value.d.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* The SDK has no knowledge of CEL — it only calls .call(). */
|
|
4
4
|
export interface CompiledValue {
|
|
5
5
|
readonly __compiled: true;
|
|
6
|
+
/** Original expression source text (e.g. "env.PORT"), if available. */
|
|
7
|
+
readonly source?: string;
|
|
6
8
|
call(ctx: Record<string, unknown>): unknown;
|
|
7
9
|
}
|
|
8
10
|
export declare function isCompiledValue(v: unknown): v is CompiledValue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiled-value.d.ts","sourceRoot":"","sources":["../src/compiled-value.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAE9D"}
|
|
1
|
+
{"version":3,"file":"compiled-value.d.ts","sourceRoot":"","sources":["../src/compiled-value.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAE9D"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { ModuleContext } from "./module-context.js";
|
|
2
1
|
import type { ScopeHandle } from "./ref.js";
|
|
3
|
-
import { ResourceInstance } from "./resource-instance.js";
|
|
4
|
-
import { ResourceManifest } from "./resource-manifest.js";
|
|
2
|
+
import type { ResourceInstance } from "./resource-instance.js";
|
|
3
|
+
import type { ResourceManifest } from "./resource-manifest.js";
|
|
4
|
+
import type { ResourceDefinition } from "./types.js";
|
|
5
5
|
export type EmitEvent = (event: string, payload?: any) => void | Promise<void>;
|
|
6
6
|
/** Four-stage resource lifecycle defined in resource-lifecycle.md */
|
|
7
7
|
export type LifecycleState = "Pending" | "Validated" | "Initialized" | "Draining" | "Teardown";
|
|
@@ -20,7 +20,7 @@ export type CreatedResource = {
|
|
|
20
20
|
* Returns a CreatedResource (instance + ctx) so initializeResources can run
|
|
21
21
|
* init() separately in a second phase.
|
|
22
22
|
*/
|
|
23
|
-
export type InstanceFactory = (
|
|
23
|
+
export type InstanceFactory = (context: EvaluationContext, resource: ResourceManifest) => Promise<CreatedResource | null>;
|
|
24
24
|
/**
|
|
25
25
|
* Hook called after controller.create() and before controller.init() for each resource.
|
|
26
26
|
* Implementations (e.g. the kernel) use this to inject live instances into reference
|
|
@@ -34,129 +34,43 @@ export type PreInitHook = (resource: ResourceManifest, getInstance: (name: strin
|
|
|
34
34
|
/** Canonical key for a resource instance: "<module>.<kind>.<name>" */
|
|
35
35
|
export declare function resourceKey(r: ResourceManifest): string;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
* expansion, secrets redaction, and the generic resource lifecycle tree.
|
|
37
|
+
* Public contract for the base evaluation context.
|
|
39
38
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* - Queue resources for initialization (pendingResources)
|
|
43
|
-
* - Spawn child contexts (spawnChild) forming a lifecycle tree
|
|
44
|
-
* - Run a multi-pass initialization loop (initializeResources)
|
|
45
|
-
* - Cascade teardown depth-first through the tree (teardownResources)
|
|
39
|
+
* Owns template expansion, secrets redaction, and the generic resource lifecycle tree.
|
|
40
|
+
* The class implementation lives in `@telorun/kernel`.
|
|
46
41
|
*/
|
|
47
|
-
export
|
|
48
|
-
readonly source: string;
|
|
42
|
+
export interface EvaluationContext {
|
|
49
43
|
readonly id: string;
|
|
50
|
-
|
|
51
|
-
protected _secretValues: Set<string>;
|
|
52
|
-
protected _createInstance: InstanceFactory;
|
|
44
|
+
readonly source: string;
|
|
53
45
|
readonly emit: EmitEvent;
|
|
54
|
-
/** Position in the lifecycle tree. */
|
|
55
46
|
parent: EvaluationContext | undefined;
|
|
56
47
|
readonly children: EvaluationContext[];
|
|
57
|
-
/** Current lifecycle state of this context node. */
|
|
58
48
|
state: LifecycleState;
|
|
59
|
-
/** Resource instances owned by this context node, keyed by resourceKey(). */
|
|
60
49
|
readonly resourceInstances: Map<string, {
|
|
61
50
|
resource: ResourceManifest;
|
|
62
51
|
instance: ResourceInstance;
|
|
63
52
|
}>;
|
|
64
|
-
/** Resources that have been created but not yet initialized (between phases). */
|
|
65
|
-
protected readonly createdInstances: Map<string, {
|
|
66
|
-
resource: ResourceManifest;
|
|
67
|
-
instance: ResourceInstance;
|
|
68
|
-
ctx: any;
|
|
69
|
-
}>;
|
|
70
|
-
/** Resources queued for initialization on this context node. */
|
|
71
|
-
private pendingResources;
|
|
72
|
-
/**
|
|
73
|
-
* Optional hook called between create() and init() for each resource.
|
|
74
|
-
* Set by the kernel to inject live instances into reference fields.
|
|
75
|
-
*/
|
|
76
53
|
preInitHook?: PreInitHook;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Reorder pending resources to match the given name sequence (topo order from Phase 4).
|
|
85
|
-
* Resources not present in `names` are left at the end in their original order.
|
|
86
|
-
* Call before initializeResources() so the create/init sub-phases run in dependency order,
|
|
87
|
-
* guaranteeing that Phase 5 injection always finds initialized dependencies.
|
|
88
|
-
*/
|
|
54
|
+
/** Looks up a registered resource definition by fully-qualified kind.
|
|
55
|
+
* Set by the kernel; used for declared-throw-union checks. */
|
|
56
|
+
getDefinition?: (kind: string) => ResourceDefinition | undefined;
|
|
57
|
+
readonly createInstance: InstanceFactory;
|
|
58
|
+
readonly context: Record<string, unknown>;
|
|
59
|
+
readonly secretValues: Set<string>;
|
|
89
60
|
setInitOrder(names: string[]): void;
|
|
90
|
-
/**
|
|
91
|
-
* Queue a resource manifest for initialization on this context.
|
|
92
|
-
*/
|
|
93
61
|
hasManifest(name: string): boolean;
|
|
94
62
|
registerManifest(resource: ResourceManifest): void;
|
|
95
|
-
/**
|
|
96
|
-
* Attach a child context to this node. The child's parent is set to this
|
|
97
|
-
* context and the child is registered under the given name.
|
|
98
|
-
*/
|
|
99
63
|
spawnChild<T extends EvaluationContext>(child: T): T;
|
|
100
|
-
/**
|
|
101
|
-
* Interleaved create/init loop.
|
|
102
|
-
*
|
|
103
|
-
* Each pass has two sub-phases run back-to-back:
|
|
104
|
-
* 1. Create sub-phase: call controller.create() for each pending resource that
|
|
105
|
-
* hasn't been created yet. Successful results go into createdInstances.
|
|
106
|
-
* 2. Init sub-phase: call instance.init(ctx) for each created-but-not-inited
|
|
107
|
-
* resource. Successful results go into resourceInstances.
|
|
108
|
-
*
|
|
109
|
-
* Interleaving is necessary because some resources' create() depends on effects
|
|
110
|
-
* produced by other resources' init() (e.g. Kernel.Import.init() runs
|
|
111
|
-
* child.initializeResources() which registers controllers needed by sibling
|
|
112
|
-
* resources' create()). Running both sub-phases each pass lets those effects
|
|
113
|
-
* propagate before the next create attempt.
|
|
114
|
-
*
|
|
115
|
-
* Each resource is created at most once and inited at most once.
|
|
116
|
-
* ERR_VISIBILITY_DENIED is fatal and re-thrown immediately.
|
|
117
|
-
* All other errors are tracked and retried until no progress is made.
|
|
118
|
-
*/
|
|
119
64
|
initializeResources(): Promise<void>;
|
|
120
65
|
withManifests<T>(manifests: any[], fn: () => T): T;
|
|
121
|
-
/**
|
|
122
|
-
* Returns a ScopeHandle that initializes `manifests` in a fresh child context each time
|
|
123
|
-
* `run()` is called, executes the callback with a ScopeContext, and tears down when done.
|
|
124
|
-
*
|
|
125
|
-
* The child inherits the parent's preInitHook (if any), extended so that `getInstance`
|
|
126
|
-
* also checks the parent's already-initialized singleton instances. This lets scoped
|
|
127
|
-
* resources hold x-telo-ref slots pointing to outer resources — those deps are already
|
|
128
|
-
* live when the scope opens.
|
|
129
|
-
*/
|
|
130
66
|
createScopeHandle(manifests: ResourceManifest[]): ScopeHandle;
|
|
131
|
-
/**
|
|
132
|
-
* Cascade teardown depth-first through the tree:
|
|
133
|
-
* 1. Tear down child contexts in reverse registration order.
|
|
134
|
-
* 2. Tear down own resource instances in reverse registration order,
|
|
135
|
-
* emitting a Teardown event for each via the injected emit callback.
|
|
136
|
-
*/
|
|
137
67
|
teardownResources(): Promise<void>;
|
|
138
68
|
transientChild(context: Record<string, any>): EvaluationContext;
|
|
139
|
-
/**
|
|
140
|
-
* Invoke a resource by kind and name within this context's resourceInstances.
|
|
141
|
-
* Emits a scoped Invoked event via the injected emit callback after invocation.
|
|
142
|
-
*/
|
|
143
69
|
invoke<TInputs>(kind: string, name: string, inputs: TInputs): Promise<any>;
|
|
70
|
+
invokeResolved<TInputs>(kind: string, name: string, instance: ResourceInstance, inputs: TInputs): Promise<any>;
|
|
144
71
|
run(name: string): Promise<void>;
|
|
145
|
-
/**
|
|
146
|
-
* Expand a value that may contain precompiled ${{ }} templates.
|
|
147
|
-
* Works recursively over CompiledValues, arrays, and objects.
|
|
148
|
-
*/
|
|
149
72
|
expand(value: unknown): unknown;
|
|
150
|
-
/**
|
|
151
|
-
* Expand a value using this context merged with additional properties.
|
|
152
|
-
* Equivalent to merge(extraContext).expand(value) without allocating a context object.
|
|
153
|
-
*/
|
|
154
73
|
expandWith(value: unknown, extraContext: Record<string, unknown>): unknown;
|
|
155
|
-
/**
|
|
156
|
-
* Expand specific dot-paths within an object. '**' expands the entire object.
|
|
157
|
-
* Paths listed in excludePaths are left untouched (runtime takes precedence).
|
|
158
|
-
* Always throws if an expression cannot be resolved.
|
|
159
|
-
*/
|
|
160
74
|
expandPaths(value: Record<string, unknown>, paths: string[], excludePaths?: string[]): Record<string, unknown>;
|
|
161
75
|
}
|
|
162
76
|
//# sourceMappingURL=evaluation-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,394 +1,4 @@
|
|
|
1
|
-
import { isCompiledValue } from "./compiled-value.js";
|
|
2
|
-
import { RuntimeError } from "./types.js";
|
|
3
1
|
/** Canonical key for a resource instance: "<module>.<kind>.<name>" */
|
|
4
2
|
export function resourceKey(r) {
|
|
5
3
|
return `${r.kind}.${r.metadata.name}`;
|
|
6
4
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Base class for all evaluation contexts. Owns template
|
|
9
|
-
* expansion, secrets redaction, and the generic resource lifecycle tree.
|
|
10
|
-
*
|
|
11
|
-
* Every EvaluationContext node can:
|
|
12
|
-
* - Hold its own resource instances (resourceInstances)
|
|
13
|
-
* - Queue resources for initialization (pendingResources)
|
|
14
|
-
* - Spawn child contexts (spawnChild) forming a lifecycle tree
|
|
15
|
-
* - Run a multi-pass initialization loop (initializeResources)
|
|
16
|
-
* - Cascade teardown depth-first through the tree (teardownResources)
|
|
17
|
-
*/
|
|
18
|
-
export class EvaluationContext {
|
|
19
|
-
source;
|
|
20
|
-
id = Math.random().toString(16).slice(2, 8);
|
|
21
|
-
_context;
|
|
22
|
-
_secretValues;
|
|
23
|
-
_createInstance;
|
|
24
|
-
emit;
|
|
25
|
-
/** Position in the lifecycle tree. */
|
|
26
|
-
parent = undefined;
|
|
27
|
-
children = [];
|
|
28
|
-
/** Current lifecycle state of this context node. */
|
|
29
|
-
state = "Pending";
|
|
30
|
-
/** Resource instances owned by this context node, keyed by resourceKey(). */
|
|
31
|
-
resourceInstances = new Map();
|
|
32
|
-
/** Resources that have been created but not yet initialized (between phases). */
|
|
33
|
-
createdInstances = new Map();
|
|
34
|
-
/** Resources queued for initialization on this context node. */
|
|
35
|
-
pendingResources = [];
|
|
36
|
-
/**
|
|
37
|
-
* Optional hook called between create() and init() for each resource.
|
|
38
|
-
* Set by the kernel to inject live instances into reference fields.
|
|
39
|
-
*/
|
|
40
|
-
preInitHook;
|
|
41
|
-
constructor(source, context, createInstance = async () => null, secretValues, emit) {
|
|
42
|
-
this.source = source;
|
|
43
|
-
this._context = context;
|
|
44
|
-
this._createInstance = createInstance;
|
|
45
|
-
this._secretValues = secretValues ?? new Set();
|
|
46
|
-
this.emit = emit;
|
|
47
|
-
}
|
|
48
|
-
get createInstance() {
|
|
49
|
-
return this._createInstance;
|
|
50
|
-
}
|
|
51
|
-
/** Called after init() when a resource snapshot is available. Overridden by ModuleContext. */
|
|
52
|
-
onResourceSnapshotted(_name, _snap) { }
|
|
53
|
-
get context() {
|
|
54
|
-
return this._context;
|
|
55
|
-
}
|
|
56
|
-
get secretValues() {
|
|
57
|
-
return this._secretValues;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Reorder pending resources to match the given name sequence (topo order from Phase 4).
|
|
61
|
-
* Resources not present in `names` are left at the end in their original order.
|
|
62
|
-
* Call before initializeResources() so the create/init sub-phases run in dependency order,
|
|
63
|
-
* guaranteeing that Phase 5 injection always finds initialized dependencies.
|
|
64
|
-
*/
|
|
65
|
-
setInitOrder(names) {
|
|
66
|
-
const rank = new Map(names.map((n, i) => [n, i]));
|
|
67
|
-
this.pendingResources.sort((a, b) => {
|
|
68
|
-
const ra = rank.get(a.metadata.name) ?? Infinity;
|
|
69
|
-
const rb = rank.get(b.metadata.name) ?? Infinity;
|
|
70
|
-
return ra - rb;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Queue a resource manifest for initialization on this context.
|
|
75
|
-
*/
|
|
76
|
-
hasManifest(name) {
|
|
77
|
-
return (this.resourceInstances.has(name) ||
|
|
78
|
-
this.createdInstances.has(name) ||
|
|
79
|
-
this.pendingResources.some((r) => r.metadata.name === name));
|
|
80
|
-
}
|
|
81
|
-
registerManifest(resource) {
|
|
82
|
-
if (!resource.metadata) {
|
|
83
|
-
resource.metadata = { name: `__unnamed_${Math.random().toString(16).slice(2, 8)}` };
|
|
84
|
-
}
|
|
85
|
-
const name = resource.metadata.name;
|
|
86
|
-
if (this.hasManifest(name)) {
|
|
87
|
-
throw new RuntimeError("ERR_DUPLICATE_RESOURCE", `Resource '${name}' is already registered`);
|
|
88
|
-
}
|
|
89
|
-
this.pendingResources.push(resource);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Attach a child context to this node. The child's parent is set to this
|
|
93
|
-
* context and the child is registered under the given name.
|
|
94
|
-
*/
|
|
95
|
-
spawnChild(child) {
|
|
96
|
-
child.parent = this;
|
|
97
|
-
this.children.push(child);
|
|
98
|
-
// Propagate injection hook so all child contexts (module imports, scopes) participate
|
|
99
|
-
// in Phase 5 injection. createScopeHandle overrides this with an extended version.
|
|
100
|
-
if (this.preInitHook && !child.preInitHook) {
|
|
101
|
-
child.preInitHook = this.preInitHook;
|
|
102
|
-
}
|
|
103
|
-
return child;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Interleaved create/init loop.
|
|
107
|
-
*
|
|
108
|
-
* Each pass has two sub-phases run back-to-back:
|
|
109
|
-
* 1. Create sub-phase: call controller.create() for each pending resource that
|
|
110
|
-
* hasn't been created yet. Successful results go into createdInstances.
|
|
111
|
-
* 2. Init sub-phase: call instance.init(ctx) for each created-but-not-inited
|
|
112
|
-
* resource. Successful results go into resourceInstances.
|
|
113
|
-
*
|
|
114
|
-
* Interleaving is necessary because some resources' create() depends on effects
|
|
115
|
-
* produced by other resources' init() (e.g. Kernel.Import.init() runs
|
|
116
|
-
* child.initializeResources() which registers controllers needed by sibling
|
|
117
|
-
* resources' create()). Running both sub-phases each pass lets those effects
|
|
118
|
-
* propagate before the next create attempt.
|
|
119
|
-
*
|
|
120
|
-
* Each resource is created at most once and inited at most once.
|
|
121
|
-
* ERR_VISIBILITY_DENIED is fatal and re-thrown immediately.
|
|
122
|
-
* All other errors are tracked and retried until no progress is made.
|
|
123
|
-
*/
|
|
124
|
-
async initializeResources() {
|
|
125
|
-
const MAX_PASSES = 10;
|
|
126
|
-
const errors = new Map();
|
|
127
|
-
let pass = 1;
|
|
128
|
-
do {
|
|
129
|
-
let progress = false;
|
|
130
|
-
// Create sub-phase
|
|
131
|
-
for (const resource of [...this.pendingResources]) {
|
|
132
|
-
const name = resource.metadata.name;
|
|
133
|
-
if (this.createdInstances.has(name))
|
|
134
|
-
continue;
|
|
135
|
-
try {
|
|
136
|
-
// const expanded = this.expand(resource) as ResourceManifest;
|
|
137
|
-
// FIXME: Cannot expand it for all resources, needs to be selective
|
|
138
|
-
const created = await this._createInstance(this, resource);
|
|
139
|
-
if (created) {
|
|
140
|
-
this.createdInstances.set(name, {
|
|
141
|
-
resource,
|
|
142
|
-
instance: created.instance,
|
|
143
|
-
ctx: created.ctx,
|
|
144
|
-
});
|
|
145
|
-
const idx = this.pendingResources.findIndex((m) => m.metadata.name === name);
|
|
146
|
-
if (idx >= 0)
|
|
147
|
-
this.pendingResources.splice(idx, 1);
|
|
148
|
-
errors.delete(name);
|
|
149
|
-
progress = true;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
if (error instanceof RuntimeError && error.code === "ERR_VISIBILITY_DENIED")
|
|
154
|
-
throw error;
|
|
155
|
-
errors.set(name, error instanceof Error ? error.message : String(error));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// Init sub-phase
|
|
159
|
-
for (const [name, { resource, instance, ctx }] of [...this.createdInstances]) {
|
|
160
|
-
if (this.resourceInstances.has(name))
|
|
161
|
-
continue;
|
|
162
|
-
try {
|
|
163
|
-
if (this.preInitHook) {
|
|
164
|
-
this.preInitHook(resource, (n) => this.resourceInstances.get(n)?.instance);
|
|
165
|
-
}
|
|
166
|
-
if (instance.init)
|
|
167
|
-
await instance.init(ctx);
|
|
168
|
-
if (instance.snapshot) {
|
|
169
|
-
const snap = await Promise.resolve(instance.snapshot()).catch(() => ({}));
|
|
170
|
-
this.onResourceSnapshotted(name, snap ?? {});
|
|
171
|
-
}
|
|
172
|
-
this.resourceInstances.set(name, { resource, instance });
|
|
173
|
-
this.createdInstances.delete(name);
|
|
174
|
-
errors.delete(name);
|
|
175
|
-
progress = true;
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
if (error instanceof RuntimeError && error.code === "ERR_VISIBILITY_DENIED")
|
|
179
|
-
throw error;
|
|
180
|
-
errors.set(name, error instanceof Error ? error.message : String(error));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
pass++;
|
|
184
|
-
if (!progress)
|
|
185
|
-
break;
|
|
186
|
-
} while (pass <= MAX_PASSES);
|
|
187
|
-
if (this.pendingResources.length > 0 || this.createdInstances.size > 0) {
|
|
188
|
-
const diagnostics = [
|
|
189
|
-
...this.pendingResources.map((r) => ({
|
|
190
|
-
resource: r.metadata.name,
|
|
191
|
-
message: errors.get(r.metadata.name) ?? "Unknown error",
|
|
192
|
-
})),
|
|
193
|
-
...[...this.createdInstances.keys()].map((name) => ({
|
|
194
|
-
resource: name,
|
|
195
|
-
message: errors.get(name) ?? "Unknown error",
|
|
196
|
-
})),
|
|
197
|
-
];
|
|
198
|
-
throw new RuntimeError("ERR_RESOURCE_INITIALIZATION_FAILED", "Unable to process resources", diagnostics);
|
|
199
|
-
}
|
|
200
|
-
this.state = "Initialized";
|
|
201
|
-
}
|
|
202
|
-
withManifests(manifests, fn) {
|
|
203
|
-
const child = this.spawnChild(new EvaluationContext(this.source, this._context, this._createInstance, this._secretValues, this.emit));
|
|
204
|
-
try {
|
|
205
|
-
for (const manifest of manifests) {
|
|
206
|
-
child.registerManifest(manifest);
|
|
207
|
-
}
|
|
208
|
-
return fn();
|
|
209
|
-
}
|
|
210
|
-
finally {
|
|
211
|
-
// Tear down child context and its resources immediately after fn() completes.
|
|
212
|
-
// Note that this does NOT emit Kernel-level events (e.g. Teardown events) —
|
|
213
|
-
// they remain the Kernel's responsibility.
|
|
214
|
-
child.teardownResources();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Returns a ScopeHandle that initializes `manifests` in a fresh child context each time
|
|
219
|
-
* `run()` is called, executes the callback with a ScopeContext, and tears down when done.
|
|
220
|
-
*
|
|
221
|
-
* The child inherits the parent's preInitHook (if any), extended so that `getInstance`
|
|
222
|
-
* also checks the parent's already-initialized singleton instances. This lets scoped
|
|
223
|
-
* resources hold x-telo-ref slots pointing to outer resources — those deps are already
|
|
224
|
-
* live when the scope opens.
|
|
225
|
-
*/
|
|
226
|
-
createScopeHandle(manifests) {
|
|
227
|
-
const parent = this;
|
|
228
|
-
return {
|
|
229
|
-
async run(fn) {
|
|
230
|
-
const child = parent.spawnChild(new EvaluationContext(parent.source, parent._context, parent._createInstance, parent._secretValues, parent.emit));
|
|
231
|
-
// Propagate injection hook: extend getInstance to also resolve parent singleton instances.
|
|
232
|
-
if (parent.preInitHook) {
|
|
233
|
-
const parentHook = parent.preInitHook;
|
|
234
|
-
child.preInitHook = (resource, childGetInstance) => {
|
|
235
|
-
parentHook(resource, (name) => childGetInstance(name) ?? parent.resourceInstances.get(name)?.instance);
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
try {
|
|
239
|
-
for (const manifest of manifests) {
|
|
240
|
-
child.registerManifest(manifest);
|
|
241
|
-
}
|
|
242
|
-
await child.initializeResources();
|
|
243
|
-
const scope = {
|
|
244
|
-
getInstance(name) {
|
|
245
|
-
const childEntry = child.resourceInstances.get(name);
|
|
246
|
-
if (childEntry)
|
|
247
|
-
return childEntry.instance;
|
|
248
|
-
const parentEntry = parent.resourceInstances.get(name);
|
|
249
|
-
if (parentEntry)
|
|
250
|
-
return parentEntry.instance;
|
|
251
|
-
throw new RuntimeError("ERR_SCOPE_RESOURCE_NOT_FOUND", `Resource '${name}' not found in scope or outer context. Available scoped: ${[...child.resourceInstances.keys()].join(", ")}`);
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
return await fn(scope);
|
|
255
|
-
}
|
|
256
|
-
finally {
|
|
257
|
-
await child.teardownResources();
|
|
258
|
-
const idx = parent.children.indexOf(child);
|
|
259
|
-
if (idx >= 0)
|
|
260
|
-
parent.children.splice(idx, 1);
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Cascade teardown depth-first through the tree:
|
|
267
|
-
* 1. Tear down child contexts in reverse registration order.
|
|
268
|
-
* 2. Tear down own resource instances in reverse registration order,
|
|
269
|
-
* emitting a Teardown event for each via the injected emit callback.
|
|
270
|
-
*/
|
|
271
|
-
async teardownResources() {
|
|
272
|
-
this.state = "Draining";
|
|
273
|
-
for (const child of [...this.children].reverse()) {
|
|
274
|
-
await child.teardownResources();
|
|
275
|
-
}
|
|
276
|
-
const entries = [...this.resourceInstances.entries()].reverse();
|
|
277
|
-
for (const [key, { resource, instance }] of entries) {
|
|
278
|
-
if (instance.teardown)
|
|
279
|
-
await instance.teardown();
|
|
280
|
-
await this.emit(`${resource.kind}.${resource.metadata.name}.Teardown`, {
|
|
281
|
-
resource: { kind: resource.kind, name: resource.metadata.name },
|
|
282
|
-
});
|
|
283
|
-
this.resourceInstances.delete(key);
|
|
284
|
-
}
|
|
285
|
-
this.state = "Teardown";
|
|
286
|
-
}
|
|
287
|
-
transientChild(context) {
|
|
288
|
-
return new EvaluationContext(this.source, { ...this.context, ...context }, this._createInstance, this._secretValues, this.emit);
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Invoke a resource by kind and name within this context's resourceInstances.
|
|
292
|
-
* Emits a scoped Invoked event via the injected emit callback after invocation.
|
|
293
|
-
*/
|
|
294
|
-
async invoke(kind, name, inputs) {
|
|
295
|
-
const entry = this.resourceInstances.get(name);
|
|
296
|
-
if (entry) {
|
|
297
|
-
if (typeof entry.instance.invoke !== "function") {
|
|
298
|
-
throw new RuntimeError("ERR_RESOURCE_NOT_INVOKABLE", `Resource ${kind}.${name} does not have an invoke method`);
|
|
299
|
-
}
|
|
300
|
-
const outputs = await entry.instance.invoke(inputs);
|
|
301
|
-
await this.emit(`${kind}.${name}.Invoked`, { outputs });
|
|
302
|
-
return outputs;
|
|
303
|
-
}
|
|
304
|
-
throw new RuntimeError("ERR_RESOURCE_NOT_FOUND", `Resource not found for invocation: ${kind}.${name}. Available resources: ${[...this.resourceInstances.keys()].join(", ")}`);
|
|
305
|
-
}
|
|
306
|
-
async run(name) {
|
|
307
|
-
const entry = this.resourceInstances.get(name);
|
|
308
|
-
if (entry && typeof entry.instance.run === "function") {
|
|
309
|
-
return entry.instance.run();
|
|
310
|
-
}
|
|
311
|
-
throw new RuntimeError("ERR_RESOURCE_NOT_RUNNABLE", `Resource ${name} is not runnable or not found. Available resources: ${[...this.resourceInstances.keys()].join(", ")}`);
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Expand a value that may contain precompiled ${{ }} templates.
|
|
315
|
-
* Works recursively over CompiledValues, arrays, and objects.
|
|
316
|
-
*/
|
|
317
|
-
expand(value) {
|
|
318
|
-
if (isCompiledValue(value)) {
|
|
319
|
-
return value.call(this._context);
|
|
320
|
-
}
|
|
321
|
-
if (Array.isArray(value)) {
|
|
322
|
-
return value.map((entry) => this.expand(entry));
|
|
323
|
-
}
|
|
324
|
-
if (value !== null && typeof value === "object") {
|
|
325
|
-
const resolved = {};
|
|
326
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
327
|
-
resolved[key] = this.expand(entry);
|
|
328
|
-
}
|
|
329
|
-
return resolved;
|
|
330
|
-
}
|
|
331
|
-
return value;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Expand a value using this context merged with additional properties.
|
|
335
|
-
* Equivalent to merge(extraContext).expand(value) without allocating a context object.
|
|
336
|
-
*/
|
|
337
|
-
expandWith(value, extraContext) {
|
|
338
|
-
const saved = this._context;
|
|
339
|
-
this._context = Object.assign(Object.create(null), saved, extraContext);
|
|
340
|
-
try {
|
|
341
|
-
return this.expand(value);
|
|
342
|
-
}
|
|
343
|
-
finally {
|
|
344
|
-
this._context = saved;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Expand specific dot-paths within an object. '**' expands the entire object.
|
|
349
|
-
* Paths listed in excludePaths are left untouched (runtime takes precedence).
|
|
350
|
-
* Always throws if an expression cannot be resolved.
|
|
351
|
-
*/
|
|
352
|
-
expandPaths(value, paths, excludePaths = []) {
|
|
353
|
-
if (paths.includes("**")) {
|
|
354
|
-
const result = {};
|
|
355
|
-
for (const [key, v] of Object.entries(value)) {
|
|
356
|
-
result[key] = isExcluded(key, excludePaths) ? v : this.expand(v);
|
|
357
|
-
}
|
|
358
|
-
return result;
|
|
359
|
-
}
|
|
360
|
-
const result = { ...value };
|
|
361
|
-
for (const path of paths) {
|
|
362
|
-
if (isExcluded(path, excludePaths))
|
|
363
|
-
continue;
|
|
364
|
-
const parts = path.split(".");
|
|
365
|
-
const current = getNestedValue(result, parts);
|
|
366
|
-
if (current !== undefined) {
|
|
367
|
-
setNestedValue(result, parts, this.expand(current));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return result;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
function isExcluded(path, excludePaths) {
|
|
374
|
-
return excludePaths.some((ep) => ep === path || ep === "**" || path.startsWith(ep + ".") || ep.startsWith(path + "."));
|
|
375
|
-
}
|
|
376
|
-
function getNestedValue(obj, parts) {
|
|
377
|
-
let current = obj;
|
|
378
|
-
for (const part of parts) {
|
|
379
|
-
if (current === null || typeof current !== "object")
|
|
380
|
-
return undefined;
|
|
381
|
-
current = current[part];
|
|
382
|
-
}
|
|
383
|
-
return current;
|
|
384
|
-
}
|
|
385
|
-
function setNestedValue(obj, parts, value) {
|
|
386
|
-
let current = obj;
|
|
387
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
388
|
-
const next = current[parts[i]];
|
|
389
|
-
if (next === null || typeof next !== "object")
|
|
390
|
-
return;
|
|
391
|
-
current = next;
|
|
392
|
-
}
|
|
393
|
-
current[parts[parts.length - 1]] = value;
|
|
394
|
-
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./module-context.js";
|
|
|
10
10
|
export * from "./resource-context.js";
|
|
11
11
|
export * from "./resource-instance.js";
|
|
12
12
|
export * from "./resource-manifest.js";
|
|
13
|
+
export * from "./invoke-error.js";
|
|
13
14
|
export * from "./runtime-error.js";
|
|
14
15
|
export * from "./runtime-event.js";
|
|
15
16
|
export * from "./runtime-resource.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,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,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,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|