@telorun/sdk 0.2.3 → 0.2.5

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.
Files changed (56) hide show
  1. package/README.md +5 -5
  2. package/dist/capabilities/invokable.d.ts +4 -0
  3. package/dist/capabilities/invokable.d.ts.map +1 -0
  4. package/dist/capabilities/invokable.js +1 -0
  5. package/dist/capabilities/provider.d.ts +4 -0
  6. package/dist/capabilities/provider.d.ts.map +1 -0
  7. package/dist/capabilities/provider.js +1 -0
  8. package/dist/capabilities/runnable.d.ts +4 -0
  9. package/dist/capabilities/runnable.d.ts.map +1 -0
  10. package/dist/capabilities/runnable.js +1 -0
  11. package/dist/context-provider.d.ts +18 -0
  12. package/dist/context-provider.d.ts.map +1 -0
  13. package/dist/context-provider.js +11 -0
  14. package/dist/controller-context.d.ts +2 -2
  15. package/dist/controller-context.d.ts.map +1 -0
  16. package/dist/evaluation-context.d.ts +103 -0
  17. package/dist/evaluation-context.d.ts.map +1 -0
  18. package/dist/evaluation-context.js +256 -0
  19. package/dist/execution-context.d.ts +13 -0
  20. package/dist/execution-context.d.ts.map +1 -0
  21. package/dist/execution-context.js +13 -0
  22. package/dist/index.d.ts +15 -7
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +14 -7
  25. package/dist/module-context.d.ts +51 -0
  26. package/dist/module-context.d.ts.map +1 -0
  27. package/dist/module-context.js +150 -0
  28. package/dist/resource-context.d.ts +21 -1
  29. package/dist/resource-context.d.ts.map +1 -0
  30. package/dist/resource-instance.d.ts +1 -0
  31. package/dist/resource-instance.d.ts.map +1 -0
  32. package/dist/resource-manifest.d.ts +2 -1
  33. package/dist/resource-manifest.d.ts.map +1 -0
  34. package/dist/runtime-error.d.ts +2 -1
  35. package/dist/runtime-error.d.ts.map +1 -0
  36. package/dist/runtime-event.d.ts +1 -0
  37. package/dist/runtime-event.d.ts.map +1 -0
  38. package/dist/runtime-resource.d.ts +1 -0
  39. package/dist/runtime-resource.d.ts.map +1 -0
  40. package/dist/types.d.ts +62 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +8 -0
  43. package/package.json +5 -1
  44. package/src/capabilities/invokable.ts +3 -0
  45. package/src/capabilities/provider.ts +3 -0
  46. package/src/capabilities/runnable.ts +3 -0
  47. package/src/context-provider.ts +25 -0
  48. package/src/controller-context.ts +4 -14
  49. package/src/evaluation-context.ts +337 -0
  50. package/src/execution-context.ts +21 -0
  51. package/src/index.ts +14 -7
  52. package/src/module-context.ts +187 -0
  53. package/src/resource-context.ts +17 -5
  54. package/src/resource-manifest.ts +1 -1
  55. package/src/runtime-error.ts +11 -7
  56. package/src/types.ts +83 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Telo SDK (Node.js)
2
2
 
3
- The Node.js SDK provides the authoring surface for Telo modules. It defines the shared contracts (types and lifecycle interfaces) that modules use to plug into the runtime, so module code stays consistent across implementations.
3
+ The Node.js SDK provides the authoring surface for Telo modules. It defines the shared contracts (types and lifecycle interfaces) that modules use to plug into the kernel, so module code stays consistent across implementations.
4
4
 
5
5
  ## What It Provides
6
6
 
@@ -14,10 +14,10 @@ Early prototype. APIs and contracts are still evolving. The API surface - includ
14
14
 
15
15
  ## When to Use It
16
16
 
17
- Use the SDK when building or extending Telo modules. It is not the runtime itself; it is the contract layer that keeps module behavior consistent and predictable.
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
19
  ## Related Docs
20
20
 
21
- - Runtime overview: `runtime/README.md`
22
- - Built‑in modules: `modiles/`
23
- - SDKs index: `sdk/README.md`
21
+ - Kernel overview: [kernel/README.md](../../kernel/README.md)
22
+ - Built‑in modules: [modules/](../../modules/)
23
+ - SDKs index: [sdk/README.md](../README.md)
@@ -0,0 +1,4 @@
1
+ export interface Invokable {
2
+ invoke(inputs: Record<string, any>): Promise<any>;
3
+ }
4
+ //# sourceMappingURL=invokable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invokable.d.ts","sourceRoot":"","sources":["../../src/capabilities/invokable.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACnD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export interface Provider {
2
+ init(): Promise<void>;
3
+ }
4
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/capabilities/provider.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export interface Runnable {
2
+ run(): Promise<void>;
3
+ }
4
+ //# sourceMappingURL=runnable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runnable.d.ts","sourceRoot":"","sources":["../../src/capabilities/runnable.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Optional interface that a ResourceInstance can implement to expose
3
+ * stable, boot-time key/value pairs into the shared CEL context.
4
+ *
5
+ * Values are captured once after init() completes and cached for the
6
+ * lifetime of the initialization phase. Do not return request-specific
7
+ * or mutable data — this is AOT (Ahead-of-Time) static context only.
8
+ */
9
+ export interface ContextProvider {
10
+ provideContext(): Record<string, unknown>;
11
+ }
12
+ /**
13
+ * Duck-type guard: returns true when `instance` has a callable `provideContext` method.
14
+ * The kernel uses this to detect providers without coupling to any concrete class,
15
+ * keeping the Core 100% generic.
16
+ */
17
+ export declare function isContextProvider(instance: unknown): instance is ContextProvider;
18
+ //# sourceMappingURL=context-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-provider.d.ts","sourceRoot":"","sources":["../src/context-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,eAAe,CAOhF"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Duck-type guard: returns true when `instance` has a callable `provideContext` method.
3
+ * The kernel uses this to detect providers without coupling to any concrete class,
4
+ * keeping the Core 100% generic.
5
+ */
6
+ export function isContextProvider(instance) {
7
+ return (typeof instance === 'object' &&
8
+ instance !== null &&
9
+ 'provideContext' in instance &&
10
+ typeof instance['provideContext'] === 'function');
11
+ }
@@ -1,4 +1,4 @@
1
- import { RuntimeEvent } from './runtime-event.js';
1
+ import { RuntimeEvent } from "./runtime-event.js";
2
2
  export interface ControllerContext {
3
3
  on(event: string, handler: (event: RuntimeEvent) => void | Promise<void>): void;
4
4
  once(event: string, handler: (event: RuntimeEvent) => void | Promise<void>): void;
@@ -6,6 +6,6 @@ export interface ControllerContext {
6
6
  emit(event: string, payload?: any, metadata?: Record<string, any>): void;
7
7
  acquireHold(reason?: string): () => void;
8
8
  requestExit(code: number): void;
9
- evaluateCel(expression: string, context: Record<string, any>): unknown;
10
9
  expandValue(value: any, context: Record<string, any>): any;
11
10
  }
11
+ //# sourceMappingURL=controller-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-context.d.ts","sourceRoot":"","sources":["../src/controller-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChF,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClF,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjF,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACzE,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;IACzC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;CAC5D"}
@@ -0,0 +1,103 @@
1
+ import { ModuleContext } from "./module-context.js";
2
+ import { ResourceInstance } from "./resource-instance.js";
3
+ import { ResourceManifest } from "./resource-manifest.js";
4
+ export type EmitEvent = (event: string, payload?: any) => void | Promise<void>;
5
+ /** Four-stage resource lifecycle defined in resource-lifecycle.md */
6
+ export type LifecycleState = "Pending" | "Validated" | "Initialized" | "Draining" | "Teardown";
7
+ /**
8
+ * Creates a ResourceInstance for the given manifest, or returns null if not yet
9
+ * ready (e.g. a dependency is still initializing). Injected at construction so
10
+ * every EvaluationContext node owns its full resource lifecycle.
11
+ */
12
+ export type InstanceFactory = (moduleContext: ModuleContext, resource: ResourceManifest) => Promise<ResourceInstance | null>;
13
+ /** Canonical key for a resource instance: "<module>.<kind>.<name>" */
14
+ export declare function resourceKey(r: ResourceManifest): string;
15
+ /**
16
+ * Base class for all evaluation contexts. Owns CEL evaluation, template
17
+ * expansion, secrets redaction, and the generic resource lifecycle tree.
18
+ *
19
+ * Every EvaluationContext node can:
20
+ * - Hold its own resource instances (resourceInstances)
21
+ * - Queue resources for initialization (pendingResources)
22
+ * - Spawn child contexts (spawnChild) forming a lifecycle tree
23
+ * - Run a multi-pass initialization loop (initializeResources)
24
+ * - Cascade teardown depth-first through the tree (teardownResources)
25
+ */
26
+ export declare class EvaluationContext {
27
+ readonly source: string;
28
+ readonly id: string;
29
+ protected _context: Record<string, unknown>;
30
+ protected _secretValues: Set<string>;
31
+ protected _createInstance: InstanceFactory;
32
+ readonly emit: EmitEvent;
33
+ /** Position in the lifecycle tree. */
34
+ parent: EvaluationContext | undefined;
35
+ readonly children: EvaluationContext[];
36
+ /** Current lifecycle state of this context node. */
37
+ state: LifecycleState;
38
+ /** Resource instances owned by this context node, keyed by resourceKey(). */
39
+ readonly resourceInstances: Map<string, {
40
+ resource: ResourceManifest;
41
+ instance: ResourceInstance;
42
+ }>;
43
+ /** Resources queued for initialization on this context node. */
44
+ private pendingResources;
45
+ constructor(source: string, context: Record<string, unknown>, createInstance: InstanceFactory | undefined, secretValues: Set<string>, emit: EmitEvent);
46
+ get createInstance(): InstanceFactory;
47
+ get context(): Record<string, unknown>;
48
+ get secretValues(): Set<string>;
49
+ /**
50
+ * Queue a resource manifest for initialization on this context.
51
+ */
52
+ registerManifest(resource: ResourceManifest): void;
53
+ /**
54
+ * Attach a child context to this node. The child's parent is set to this
55
+ * context and the child is registered under the given name.
56
+ */
57
+ spawnChild<T extends EvaluationContext>(child: T): T;
58
+ /**
59
+ * Multi-pass initialization loop. Processes pendingResources by calling the
60
+ * supplied instantiator for each resource, retrying failures across up to 10
61
+ * passes (handles dependency ordering without explicit topological sort).
62
+ *
63
+ * ERR_VISIBILITY_DENIED errors are fatal and re-thrown immediately.
64
+ * All other errors are tracked and retried until no progress is made.
65
+ */
66
+ initializeResources(): Promise<void>;
67
+ withManifests<T>(manifests: any[], fn: () => T): T;
68
+ /**
69
+ * Cascade teardown depth-first through the tree:
70
+ * 1. Tear down child contexts in reverse registration order.
71
+ * 2. Tear down own resource instances in reverse registration order.
72
+ *
73
+ * Note: Kernel-level events (e.g. Teardown events) are NOT emitted here —
74
+ * they remain the Kernel's responsibility.
75
+ */
76
+ teardownResources(): Promise<void>;
77
+ /**
78
+ * Invoke a resource by kind and name within this context's resourceInstances.
79
+ * Emits a scoped Invoked event via the injected emit callback after invocation.
80
+ */
81
+ invoke(kind: string, name: string, ...args: any[]): Promise<any>;
82
+ run(name: string): Promise<void>;
83
+ /**
84
+ * Evaluate a single CEL expression string against the context.
85
+ * Secret values are redacted from any thrown error message.
86
+ */
87
+ evaluate(expression: string): unknown;
88
+ /**
89
+ * Expand a value that may contain ${{ }} templates.
90
+ * Works recursively over strings, arrays, and objects.
91
+ * Templates whose identifiers are not present in the context are left
92
+ * unchanged (deferred) — they will be resolved at execution time when a
93
+ * richer ExecutionContext is available. All other CEL errors are propagated.
94
+ */
95
+ expand(value: unknown): unknown;
96
+ /**
97
+ * Merge another context on top of this one.
98
+ * Returns a new base EvaluationContext — 'other' wins on key conflict.
99
+ */
100
+ merge(other: EvaluationContext | Record<string, unknown>): EvaluationContext;
101
+ private expandString;
102
+ }
103
+ //# sourceMappingURL=evaluation-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,256 @@
1
+ import { evaluate } from "cel-js";
2
+ import { RuntimeError } from "./types.js";
3
+ const TEMPLATE_REGEX = /\$\{\{\s*([^}]+?)\s*\}\}/g;
4
+ const EXACT_TEMPLATE_REGEX = /^\s*\$\{\{\s*([^}]+?)\s*\}\}\s*$/;
5
+ /** Canonical key for a resource instance: "<module>.<kind>.<name>" */
6
+ export function resourceKey(r) {
7
+ return `${r.kind}.${r.metadata.name}`;
8
+ }
9
+ function redactSecrets(message, secretValues) {
10
+ if (secretValues.size === 0)
11
+ return message;
12
+ const sorted = Array.from(secretValues).sort((a, b) => b.length - a.length);
13
+ let result = message;
14
+ for (const secret of sorted) {
15
+ result = result.split(secret).join("[REDACTED]");
16
+ }
17
+ return result;
18
+ }
19
+ /**
20
+ * Base class for all evaluation contexts. Owns CEL evaluation, template
21
+ * expansion, secrets redaction, and the generic resource lifecycle tree.
22
+ *
23
+ * Every EvaluationContext node can:
24
+ * - Hold its own resource instances (resourceInstances)
25
+ * - Queue resources for initialization (pendingResources)
26
+ * - Spawn child contexts (spawnChild) forming a lifecycle tree
27
+ * - Run a multi-pass initialization loop (initializeResources)
28
+ * - Cascade teardown depth-first through the tree (teardownResources)
29
+ */
30
+ export class EvaluationContext {
31
+ source;
32
+ id = Math.random().toString(16).slice(2, 8);
33
+ _context;
34
+ _secretValues;
35
+ _createInstance;
36
+ emit;
37
+ /** Position in the lifecycle tree. */
38
+ parent = undefined;
39
+ children = [];
40
+ /** Current lifecycle state of this context node. */
41
+ state = "Pending";
42
+ /** Resource instances owned by this context node, keyed by resourceKey(). */
43
+ resourceInstances = new Map();
44
+ /** Resources queued for initialization on this context node. */
45
+ pendingResources = [];
46
+ constructor(source, context, createInstance = async () => null, secretValues, emit) {
47
+ this.source = source;
48
+ this._context = context;
49
+ this._createInstance = createInstance;
50
+ this._secretValues = secretValues ?? new Set();
51
+ this.emit = emit;
52
+ }
53
+ get createInstance() {
54
+ return this._createInstance;
55
+ }
56
+ get context() {
57
+ return this._context;
58
+ }
59
+ get secretValues() {
60
+ return this._secretValues;
61
+ }
62
+ /**
63
+ * Queue a resource manifest for initialization on this context.
64
+ */
65
+ registerManifest(resource) {
66
+ if (!resource.metadata) {
67
+ resource.metadata = { name: `__unnamed_${Math.random().toString(16).slice(2, 8)}` };
68
+ }
69
+ this.pendingResources.push(resource);
70
+ }
71
+ /**
72
+ * Attach a child context to this node. The child's parent is set to this
73
+ * context and the child is registered under the given name.
74
+ */
75
+ spawnChild(child) {
76
+ child.parent = this;
77
+ this.children.push(child);
78
+ return child;
79
+ }
80
+ /**
81
+ * Multi-pass initialization loop. Processes pendingResources by calling the
82
+ * supplied instantiator for each resource, retrying failures across up to 10
83
+ * passes (handles dependency ordering without explicit topological sort).
84
+ *
85
+ * ERR_VISIBILITY_DENIED errors are fatal and re-thrown immediately.
86
+ * All other errors are tracked and retried until no progress is made.
87
+ */
88
+ async initializeResources() {
89
+ const MAX_PASSES = 10;
90
+ let pass = 1;
91
+ const errors = new Map();
92
+ do {
93
+ const handled = [];
94
+ for (const resource of [...this.pendingResources]) {
95
+ // const rkey = resourceKey(resource);
96
+ // const displayKey = rkey;
97
+ const name = resource.metadata.name;
98
+ if (this.resourceInstances.has(name))
99
+ continue;
100
+ try {
101
+ const instance = await this._createInstance(this, resource);
102
+ if (instance) {
103
+ this.resourceInstances.set(name, { resource, instance });
104
+ handled.push(name);
105
+ errors.delete(name);
106
+ }
107
+ }
108
+ catch (error) {
109
+ if (error instanceof RuntimeError && error.code === "ERR_VISIBILITY_DENIED")
110
+ throw error;
111
+ errors.set(name, error instanceof Error ? (error.stack ?? error.message) : String(error));
112
+ }
113
+ }
114
+ for (const name of handled) {
115
+ const resource = this.pendingResources.find((m) => m.metadata.name === name);
116
+ const idx = this.pendingResources.indexOf(resource);
117
+ if (idx >= 0)
118
+ this.pendingResources.splice(idx, 1);
119
+ }
120
+ pass++;
121
+ if (handled.length === 0)
122
+ break;
123
+ } while (pass <= MAX_PASSES);
124
+ if (this.pendingResources.length > 0) {
125
+ const unhandledList = this.pendingResources
126
+ .reverse()
127
+ .map((r) => `- ${r.metadata.name}: ${errors.get(r.metadata.name) ?? "Unknown error"}`)
128
+ .join("\n");
129
+ throw new RuntimeError("ERR_RESOURCE_INITIALIZATION_FAILED", `Unable to process resources:\n\n${unhandledList}`);
130
+ }
131
+ this.state = "Initialized";
132
+ }
133
+ withManifests(manifests, fn) {
134
+ const child = this.spawnChild(new EvaluationContext(this.source, this._context, this._createInstance, this._secretValues, this.emit));
135
+ try {
136
+ for (const manifest of manifests) {
137
+ child.registerManifest(manifest);
138
+ }
139
+ return fn();
140
+ }
141
+ finally {
142
+ // Tear down child context and its resources immediately after fn() completes.
143
+ // Note that this does NOT emit Kernel-level events (e.g. Teardown events) —
144
+ // they remain the Kernel's responsibility.
145
+ child.teardownResources();
146
+ }
147
+ }
148
+ /**
149
+ * Cascade teardown depth-first through the tree:
150
+ * 1. Tear down child contexts in reverse registration order.
151
+ * 2. Tear down own resource instances in reverse registration order.
152
+ *
153
+ * Note: Kernel-level events (e.g. Teardown events) are NOT emitted here —
154
+ * they remain the Kernel's responsibility.
155
+ */
156
+ async teardownResources() {
157
+ this.state = "Draining";
158
+ for (const child of [...this.children].reverse()) {
159
+ await child.teardownResources();
160
+ }
161
+ const entries = [...this.resourceInstances.entries()].reverse();
162
+ for (const [key, { instance }] of entries) {
163
+ if (instance.teardown)
164
+ await instance.teardown();
165
+ this.resourceInstances.delete(key);
166
+ }
167
+ this.state = "Teardown";
168
+ }
169
+ /**
170
+ * Invoke a resource by kind and name within this context's resourceInstances.
171
+ * Emits a scoped Invoked event via the injected emit callback after invocation.
172
+ */
173
+ async invoke(kind, name, ...args) {
174
+ const entry = this.resourceInstances.get(name);
175
+ if (entry) {
176
+ if (typeof entry.instance.invoke !== "function") {
177
+ throw new RuntimeError("ERR_RESOURCE_NOT_INVOKABLE", `Resource ${kind}.${name} does not have an invoke method`);
178
+ }
179
+ const outputs = await entry.instance.invoke(args[0]);
180
+ await this.emit(`${kind}.${name}.Invoked`, { outputs });
181
+ return outputs;
182
+ }
183
+ throw new RuntimeError("ERR_RESOURCE_NOT_FOUND", `Resource not found for invocation: ${kind}.${name}. Available resources: ${[...this.resourceInstances.keys()].join(", ")}`);
184
+ }
185
+ async run(name) {
186
+ const entry = this.resourceInstances.get(name);
187
+ if (entry && typeof entry.instance.run === "function") {
188
+ return entry.instance.run();
189
+ }
190
+ throw new RuntimeError("ERR_RESOURCE_NOT_RUNNABLE", `Resource ${name} is not runnable or not found. Available resources: ${[...this.resourceInstances.keys()].join(", ")}`);
191
+ }
192
+ /**
193
+ * Evaluate a single CEL expression string against the context.
194
+ * Secret values are redacted from any thrown error message.
195
+ */
196
+ evaluate(expression) {
197
+ try {
198
+ return evaluate(expression, this._context);
199
+ }
200
+ catch (error) {
201
+ const raw = error instanceof Error ? error.message : String(error);
202
+ const safe = redactSecrets(raw, this._secretValues);
203
+ throw new Error(`CEL evaluation failed: "${expression}": ${safe}`);
204
+ }
205
+ }
206
+ /**
207
+ * Expand a value that may contain ${{ }} templates.
208
+ * Works recursively over strings, arrays, and objects.
209
+ * Templates whose identifiers are not present in the context are left
210
+ * unchanged (deferred) — they will be resolved at execution time when a
211
+ * richer ExecutionContext is available. All other CEL errors are propagated.
212
+ */
213
+ expand(value) {
214
+ if (typeof value === "string") {
215
+ return this.expandString(value);
216
+ }
217
+ if (Array.isArray(value)) {
218
+ return value.map((entry) => this.expand(entry));
219
+ }
220
+ if (value !== null && typeof value === "object") {
221
+ const resolved = {};
222
+ for (const [key, entry] of Object.entries(value)) {
223
+ resolved[key] = this.expand(entry);
224
+ }
225
+ return resolved;
226
+ }
227
+ return value;
228
+ }
229
+ /**
230
+ * Merge another context on top of this one.
231
+ * Returns a new base EvaluationContext — 'other' wins on key conflict.
232
+ */
233
+ merge(other) {
234
+ const otherCtx = other instanceof EvaluationContext ? other.context : other;
235
+ const otherSecrets = other instanceof EvaluationContext ? other.secretValues : new Set();
236
+ const merged = Object.assign(Object.create(null), this._context, otherCtx);
237
+ const mergedSecrets = new Set([...this._secretValues, ...otherSecrets]);
238
+ return new EvaluationContext(this.source, merged, this._createInstance, mergedSecrets, this.emit);
239
+ }
240
+ expandString(value) {
241
+ if (!value.includes("${{")) {
242
+ return value;
243
+ }
244
+ const exact = value.match(EXACT_TEMPLATE_REGEX);
245
+ if (exact) {
246
+ return this.evaluate(exact[1]);
247
+ }
248
+ return value.replace(TEMPLATE_REGEX, (_match, expr) => {
249
+ const resolved = this.evaluate(expr);
250
+ if (resolved === null || resolved === undefined) {
251
+ return "";
252
+ }
253
+ return String(resolved);
254
+ });
255
+ }
256
+ }
@@ -0,0 +1,13 @@
1
+ import { EvaluationContext } from "./evaluation-context.js";
2
+ import { ModuleContext } from "./module-context.js";
3
+ /**
4
+ * The ephemeral, per-trigger context layer. Merges a ModuleContext with
5
+ * arbitrary execution-time properties (e.g. { request, inputs } for HTTP;
6
+ * any shape is valid — determined by the trigger type).
7
+ *
8
+ * Execution props overlay the module namespaces on key conflict.
9
+ */
10
+ export declare class ExecutionContext extends EvaluationContext {
11
+ constructor(moduleCtx: ModuleContext, execProps: Record<string, unknown>);
12
+ }
13
+ //# sourceMappingURL=execution-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../src/execution-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;GAMG;AACH,qBAAa,gBAAiB,SAAQ,iBAAiB;gBACzC,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASzE"}
@@ -0,0 +1,13 @@
1
+ import { EvaluationContext } from "./evaluation-context.js";
2
+ /**
3
+ * The ephemeral, per-trigger context layer. Merges a ModuleContext with
4
+ * arbitrary execution-time properties (e.g. { request, inputs } for HTTP;
5
+ * any shape is valid — determined by the trigger type).
6
+ *
7
+ * Execution props overlay the module namespaces on key conflict.
8
+ */
9
+ export class ExecutionContext extends EvaluationContext {
10
+ constructor(moduleCtx, execProps) {
11
+ super(moduleCtx.source, Object.assign(Object.create(null), moduleCtx.context, execProps), moduleCtx.createInstance, moduleCtx.secretValues, moduleCtx.emit);
12
+ }
13
+ }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
- export * from './controller-context.js';
2
- export * from './resource-context.js';
3
- export * from './resource-instance.js';
4
- export * from './resource-manifest.js';
5
- export * from './runtime-error.js';
6
- export * from './runtime-event.js';
7
- export * from './runtime-resource.js';
1
+ export * from "./capabilities/invokable.js";
2
+ export * from "./capabilities/provider.js";
3
+ export * from "./capabilities/runnable.js";
4
+ export * from "./context-provider.js";
5
+ export * from "./controller-context.js";
6
+ export * from "./evaluation-context.js";
7
+ export * from "./module-context.js";
8
+ export * from "./resource-context.js";
9
+ export * from "./resource-instance.js";
10
+ export * from "./resource-manifest.js";
11
+ export * from "./runtime-error.js";
12
+ export * from "./runtime-event.js";
13
+ export * from "./runtime-resource.js";
14
+ export * from "./types.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,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"}
package/dist/index.js CHANGED
@@ -1,7 +1,14 @@
1
- export * from './controller-context.js';
2
- export * from './resource-context.js';
3
- export * from './resource-instance.js';
4
- export * from './resource-manifest.js';
5
- export * from './runtime-error.js';
6
- export * from './runtime-event.js';
7
- export * from './runtime-resource.js';
1
+ export * from "./capabilities/invokable.js";
2
+ export * from "./capabilities/provider.js";
3
+ export * from "./capabilities/runnable.js";
4
+ export * from "./context-provider.js";
5
+ export * from "./controller-context.js";
6
+ export * from "./evaluation-context.js";
7
+ export * from "./module-context.js";
8
+ export * from "./resource-context.js";
9
+ export * from "./resource-instance.js";
10
+ export * from "./resource-manifest.js";
11
+ export * from "./runtime-error.js";
12
+ export * from "./runtime-event.js";
13
+ export * from "./runtime-resource.js";
14
+ export * from "./types.js";
@@ -0,0 +1,51 @@
1
+ import { Invokable } from "./capabilities/invokable.js";
2
+ import { EmitEvent, EvaluationContext, InstanceFactory } from "./evaluation-context.js";
3
+ /**
4
+ * Persistent, module-scoped context. Three reserved CEL namespaces:
5
+ * variables, secrets, resources.
6
+ *
7
+ * Unlike the base EvaluationContext, ModuleContext is stateful and mutable:
8
+ * variables/secrets/resources accumulate during multi-pass initialization and
9
+ * the context record is rebuilt on each mutation. Import aliases are tracked
10
+ * here for alias-prefixed kind resolution (e.g. MyImport.Http.Route).
11
+ *
12
+ * Imported modules are surfaced under resources.<alias> alongside local
13
+ * resources — no separate imports namespace needed.
14
+ */
15
+ export declare class ModuleContext extends EvaluationContext {
16
+ private targets;
17
+ private _variables;
18
+ private _secrets;
19
+ private _resources;
20
+ /** Maps import alias → real module name for kind resolution. */
21
+ readonly importAliases: Map<string, string>;
22
+ /** Maps import alias → allowed kind names. Absent entry = unrestricted (e.g. Kernel). */
23
+ private readonly importedKinds;
24
+ constructor(source: string, variables: Record<string, unknown> | undefined, secrets: Record<string, unknown> | undefined, resources: Record<string, unknown> | undefined, targets: string[] | undefined, createInstance: InstanceFactory | undefined, emit: EmitEvent);
25
+ get variables(): Record<string, unknown>;
26
+ get secrets(): Record<string, unknown>;
27
+ get resources(): Record<string, unknown>;
28
+ setVariables(vars: Record<string, unknown>): void;
29
+ setTargets(vars: string[]): void;
30
+ setSecrets(secrets: Record<string, unknown>): void;
31
+ setResource(name: string, props: Record<string, unknown>): void;
32
+ /**
33
+ * Register an imported module under the given alias, with the list of kind names
34
+ * it exports. An empty kinds array means no restriction (used for built-ins like Kernel).
35
+ */
36
+ registerImport(alias: string, targetModule: string, kinds: string[]): void;
37
+ getInstance(name: string): unknown;
38
+ getInvokable(name: string): Invokable;
39
+ /**
40
+ * Resolve a fully-qualified kind like "Http.Server" to its real kind "http-server.Server".
41
+ * Splits on the first dot, looks up the prefix in importAliases, validates against
42
+ * importedKinds (if set), and reconstructs the resolved kind.
43
+ * Throws with a clear message if the alias is unknown or the kind is not exported.
44
+ */
45
+ resolveKind(kind: string): string;
46
+ private _rebuildContext;
47
+ invoke(kind: string, name: string, ...args: any[]): Promise<any>;
48
+ run(name: string): Promise<void>;
49
+ runTargets(): Promise<void>;
50
+ }
51
+ //# sourceMappingURL=module-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-context.d.ts","sourceRoot":"","sources":["../src/module-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAYxF;;;;;;;;;;;GAWG;AACH,qBAAa,aAAc,SAAQ,iBAAiB;IAgBhD,OAAO,CAAC,OAAO;IAfjB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,UAAU,CAA0B;IAE5C,gEAAgE;IAChE,QAAQ,CAAC,aAAa,sBAA6B;IAEnD,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;gBAG9D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAK,EACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAK,EACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAK,EAC/B,OAAO,EAAE,MAAM,EAAE,YAAK,EAC9B,cAAc,EAAE,eAAe,YAAmB,EAClD,IAAI,EAAE,SAAS;IASjB,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEvC;IAED,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAErC;IAED,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEvC;IAED,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAKjD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAIhC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAKlD,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK/D;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAO1E,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAUlC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAcrC;;;;;OAKG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAwBjC,OAAO,CAAC,eAAe;IASR,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;IAUzE,GAAG,CAAC,IAAI,EAAE,MAAM;IAchB,UAAU;CAKjB"}