@telorun/kernel 0.2.5 → 0.2.7
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/base-definition.d.ts +14 -0
- package/dist/base-definition.d.ts.map +1 -0
- package/dist/base-definition.js +17 -0
- package/dist/base-definition.js.map +1 -0
- package/dist/capabilities/component.d.ts +3 -0
- package/dist/capabilities/component.d.ts.map +1 -0
- package/dist/capabilities/component.js +4 -0
- package/dist/capabilities/component.js.map +1 -0
- package/dist/capabilities/executable.d.ts +3 -0
- package/dist/capabilities/executable.d.ts.map +1 -0
- package/dist/capabilities/executable.js +5 -0
- package/dist/capabilities/executable.js.map +1 -0
- package/dist/capabilities/handler.d.ts +3 -0
- package/dist/capabilities/handler.d.ts.map +1 -0
- package/dist/capabilities/handler.js +4 -0
- package/dist/capabilities/handler.js.map +1 -0
- package/dist/capabilities/invokable.d.ts +3 -0
- package/dist/capabilities/invokable.d.ts.map +1 -0
- package/dist/capabilities/invokable.js +5 -0
- package/dist/capabilities/invokable.js.map +1 -0
- package/dist/capabilities/listener.d.ts +3 -0
- package/dist/capabilities/listener.d.ts.map +1 -0
- package/dist/capabilities/listener.js +5 -0
- package/dist/capabilities/listener.js.map +1 -0
- package/dist/capabilities/mount.d.ts +3 -0
- package/dist/capabilities/mount.d.ts.map +1 -0
- package/dist/capabilities/mount.js +5 -0
- package/dist/capabilities/mount.js.map +1 -0
- package/dist/capabilities/provider.d.ts +3 -0
- package/dist/capabilities/provider.d.ts.map +1 -0
- package/dist/capabilities/provider.js +8 -0
- package/dist/capabilities/provider.js.map +1 -0
- package/dist/capabilities/runnable.d.ts +3 -0
- package/dist/capabilities/runnable.d.ts.map +1 -0
- package/dist/capabilities/runnable.js +5 -0
- package/dist/capabilities/runnable.js.map +1 -0
- package/dist/capabilities/service.d.ts +3 -0
- package/dist/capabilities/service.d.ts.map +1 -0
- package/dist/capabilities/service.js +5 -0
- package/dist/capabilities/service.js.map +1 -0
- package/dist/capabilities/template.d.ts +3 -0
- package/dist/capabilities/template.d.ts.map +1 -0
- package/dist/capabilities/template.js +5 -0
- package/dist/capabilities/template.js.map +1 -0
- package/dist/capabilities/type.d.ts +3 -0
- package/dist/capabilities/type.d.ts.map +1 -0
- package/dist/capabilities/type.js +5 -0
- package/dist/capabilities/type.js.map +1 -0
- package/dist/controller-loader.d.ts.map +1 -1
- package/dist/controller-loader.js +2 -1
- package/dist/controller-loader.js.map +1 -1
- package/dist/controller-registry.d.ts +0 -4
- package/dist/controller-registry.d.ts.map +1 -1
- package/dist/controller-registry.js +11 -22
- package/dist/controller-registry.js.map +1 -1
- package/dist/controllers/capability/capability-controller.d.ts +0 -5
- package/dist/controllers/capability/capability-controller.d.ts.map +1 -1
- package/dist/controllers/capability/capability-controller.js +1 -5
- package/dist/controllers/capability/capability-controller.js.map +1 -1
- package/dist/controllers/module/import-controller.d.ts +0 -3
- package/dist/controllers/module/import-controller.d.ts.map +1 -1
- package/dist/controllers/module/import-controller.js +21 -27
- package/dist/controllers/module/import-controller.js.map +1 -1
- package/dist/controllers/module/module-controller.d.ts +0 -59
- package/dist/controllers/module/module-controller.d.ts.map +1 -1
- package/dist/controllers/module/module-controller.js +0 -45
- package/dist/controllers/module/module-controller.js.map +1 -1
- package/dist/controllers/resource-definition/resource-definition-controller.d.ts +3 -4
- package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
- package/dist/controllers/resource-definition/resource-definition-controller.js +6 -8
- package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
- package/dist/controllers/resource-definition/resource-template-controller.d.ts +12 -0
- package/dist/controllers/resource-definition/resource-template-controller.d.ts.map +1 -0
- package/dist/controllers/resource-definition/resource-template-controller.js +112 -0
- package/dist/controllers/resource-definition/resource-template-controller.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/kernel.d.ts +38 -24
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +296 -201
- package/dist/kernel.js.map +1 -1
- package/dist/loader.d.ts +9 -8
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +50 -132
- package/dist/loader.js.map +1 -1
- package/dist/manifest-adapters/http-adapter.d.ts.map +1 -1
- package/dist/manifest-adapters/http-adapter.js +3 -1
- package/dist/manifest-adapters/http-adapter.js.map +1 -1
- package/dist/manifest-adapters/local-file-adapter.d.ts +7 -5
- package/dist/manifest-adapters/local-file-adapter.d.ts.map +1 -1
- package/dist/manifest-adapters/local-file-adapter.js +21 -26
- package/dist/manifest-adapters/local-file-adapter.js.map +1 -1
- package/dist/manifest-adapters/manifest-adapter.d.ts +2 -0
- package/dist/manifest-adapters/manifest-adapter.d.ts.map +1 -1
- package/dist/manifest-adapters/registry-adapter.d.ts.map +1 -1
- package/dist/manifest-adapters/registry-adapter.js +3 -1
- package/dist/manifest-adapters/registry-adapter.js.map +1 -1
- package/dist/manifest-schemas.d.ts +61 -25
- package/dist/manifest-schemas.d.ts.map +1 -1
- package/dist/manifest-schemas.js +56 -16
- package/dist/manifest-schemas.js.map +1 -1
- package/dist/resource-context.d.ts +11 -10
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-context.js +70 -25
- package/dist/resource-context.js.map +1 -1
- package/dist/schema-valiator.d.ts +6 -1
- package/dist/schema-valiator.d.ts.map +1 -1
- package/dist/schema-valiator.js +76 -4
- package/dist/schema-valiator.js.map +1 -1
- package/package.json +6 -7
- package/src/controller-loader.ts +2 -1
- package/src/controller-registry.ts +11 -26
- package/src/controllers/module/import-controller.ts +30 -30
- package/src/controllers/module/module-controller.ts +0 -51
- package/src/controllers/resource-definition/resource-definition-controller.ts +14 -15
- package/src/controllers/resource-definition/resource-template-controller.ts +138 -0
- package/src/index.ts +2 -2
- package/src/kernel.ts +364 -226
- package/src/manifest-adapters/local-file-adapter.ts +24 -31
- package/src/manifest-adapters/manifest-adapter.ts +2 -0
- package/src/manifest-schemas.ts +60 -24
- package/src/resource-context.ts +90 -35
- package/src/schema-valiator.ts +90 -13
- package/src/capabilities/component.yaml +0 -4
- package/src/capabilities/executable.yaml +0 -8
- package/src/capabilities/handler.yaml +0 -4
- package/src/capabilities/listener.yaml +0 -4
- package/src/capabilities/provider.yaml +0 -4
- package/src/capabilities/template.yaml +0 -4
- package/src/capabilities/type.yaml +0 -4
- package/src/controllers/capability/capability-controller.ts +0 -41
- package/src/controllers/module/module.json +0 -48
- package/src/controllers/module/module.yaml +0 -32
- package/src/loader.ts +0 -245
- package/src/manifest-adapters/http-adapter.ts +0 -35
- package/src/manifest-adapters/registry-adapter.ts +0 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/kernel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Telo Runtime - A lightweight, polyglot execution host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,19 +27,18 @@
|
|
|
27
27
|
"@sinclair/typebox": "^0.34.48",
|
|
28
28
|
"ajv": "^8.17.1",
|
|
29
29
|
"ajv-formats": "^3.0.1",
|
|
30
|
-
"cel-js": "^
|
|
31
|
-
"js-yaml": "^4.1.0",
|
|
30
|
+
"@marcbachmann/cel-js": "^7.5.3",
|
|
32
31
|
"packageurl-js": "^2.0.1",
|
|
33
|
-
"@telorun/
|
|
34
|
-
"@telorun/
|
|
32
|
+
"@telorun/analyzer": "0.1.2",
|
|
33
|
+
"@telorun/sdk": "0.2.7",
|
|
34
|
+
"@telorun/yaml-cel-templating": "1.0.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/js-yaml": "^4.0.5",
|
|
38
37
|
"@types/node": "^20.0.0",
|
|
39
38
|
"typescript": "^5.0.0"
|
|
40
39
|
},
|
|
41
40
|
"scripts": {
|
|
42
|
-
"build": "tsc
|
|
41
|
+
"build": "tsc",
|
|
43
42
|
"dev": "tsc --watch"
|
|
44
43
|
}
|
|
45
44
|
}
|
package/src/controller-loader.ts
CHANGED
|
@@ -39,7 +39,8 @@ export class ControllerLoader {
|
|
|
39
39
|
const isLocalManifest =
|
|
40
40
|
baseUri && !baseUri.startsWith("http://") && !baseUri.startsWith("https://");
|
|
41
41
|
if (localPath && isLocalManifest) {
|
|
42
|
-
const
|
|
42
|
+
const baseUriPath = baseUri.startsWith("file://") ? baseUri.slice("file://".length) : baseUri;
|
|
43
|
+
const manifestDir = path.dirname(baseUriPath);
|
|
43
44
|
const resolvedLocalPath = path.resolve(manifestDir, localPath);
|
|
44
45
|
if (await this.pathExists(resolvedLocalPath)) {
|
|
45
46
|
packageRoot = resolvedLocalPath;
|
|
@@ -9,20 +9,6 @@ export class ControllerRegistry {
|
|
|
9
9
|
private controllersByKind: Map<string, ControllerInstance> = new Map();
|
|
10
10
|
private definitionsByKind: Map<string, ResourceDefinition> = new Map();
|
|
11
11
|
private controllerLoaders: Map<string, () => Promise<ControllerInstance>> = new Map();
|
|
12
|
-
private capabilitySchemas: Map<string, Record<string, any> | null> = new Map();
|
|
13
|
-
|
|
14
|
-
registerCapability(name: string, schema?: Record<string, any>): void {
|
|
15
|
-
this.capabilitySchemas.set(name, schema ?? null);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
isCapabilityRegistered(name: string): boolean {
|
|
19
|
-
return this.capabilitySchemas.has(name);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
getCapabilitySchema(name: string): Record<string, any> | null | undefined {
|
|
23
|
-
return this.capabilitySchemas.get(name);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
12
|
/**
|
|
27
13
|
* Register a controller definition
|
|
28
14
|
*/
|
|
@@ -64,8 +50,10 @@ export class ControllerRegistry {
|
|
|
64
50
|
// this.controllersByKind.set(kind, controller);
|
|
65
51
|
// return controller;
|
|
66
52
|
// }
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
return {
|
|
54
|
+
schema: { type: "object", additionalProperties: false },
|
|
55
|
+
};
|
|
56
|
+
// throw new Error(`No controller registered for kind: ${kind}`);
|
|
69
57
|
}
|
|
70
58
|
|
|
71
59
|
/**
|
|
@@ -124,16 +112,13 @@ export class ControllerRegistry {
|
|
|
124
112
|
}
|
|
125
113
|
// Ensure controller has schema from definition
|
|
126
114
|
const definition = this.definitionsByKind.get(kind);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} else {
|
|
135
|
-
this.controllersByKind.set(kind, controller);
|
|
136
|
-
}
|
|
115
|
+
const wrappedController: ControllerInstance = {
|
|
116
|
+
...controller,
|
|
117
|
+
schema: controller.schema ?? definition?.schema,
|
|
118
|
+
inputType: controller.inputType,
|
|
119
|
+
outputType: controller.outputType,
|
|
120
|
+
};
|
|
121
|
+
this.controllersByKind.set(kind, wrappedController);
|
|
137
122
|
}
|
|
138
123
|
|
|
139
124
|
/**
|
|
@@ -1,20 +1,37 @@
|
|
|
1
|
+
import { DiagnosticSeverity, Loader, StaticAnalyzer } from "@telorun/analyzer";
|
|
1
2
|
import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
|
|
2
|
-
import { EvaluationContext } from "@telorun/sdk";
|
|
3
|
-
import {
|
|
3
|
+
import { EvaluationContext, RuntimeError } from "@telorun/sdk";
|
|
4
|
+
import { LocalFileAdapter } from "../../manifest-adapters/local-file-adapter.js";
|
|
4
5
|
|
|
5
6
|
export async function create(resource: any, ctx: ResourceContext): Promise<ResourceInstance> {
|
|
6
7
|
const alias = resource.metadata.name as string;
|
|
7
|
-
const
|
|
8
|
-
const loader = new Loader();
|
|
8
|
+
const loader = new Loader([new LocalFileAdapter()]);
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const moduleSource: string = resource.module ?? resource.source;
|
|
11
|
+
|
|
12
|
+
// Validate the imported module and all its transitive imports before loading for runtime.
|
|
13
|
+
// loadManifests() follows Kernel.Import chains so definitions from sub-imports are present,
|
|
14
|
+
// preventing false UNDEFINED_KIND errors for kinds that come from the module's own imports.
|
|
15
|
+
const resolvedUrl = new URL(moduleSource, ctx.moduleContext.source).toString();
|
|
16
|
+
const analysisManifests = await loader.loadManifests(resolvedUrl);
|
|
17
|
+
const diagnostics = new StaticAnalyzer().analyze(analysisManifests);
|
|
18
|
+
const errors = diagnostics.filter((d) => d.severity === DiagnosticSeverity.Error);
|
|
19
|
+
if (errors.length > 0) {
|
|
20
|
+
throw new RuntimeError(
|
|
21
|
+
"ERR_MANIFEST_VALIDATION_FAILED",
|
|
22
|
+
errors.map((d) => d.message).join("\n"),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Load target module manifests for runtime. Inject variables/secrets as compile context so
|
|
27
|
+
// that ${{ variables.x }} / ${{ secrets.y }} templates in the child module resolve correctly.
|
|
12
28
|
// No env — child modules are isolated from host environment.
|
|
13
|
-
const manifests = await loader.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
const manifests = await loader.loadModule(
|
|
30
|
+
new URL(moduleSource, ctx.moduleContext.source).toString(),
|
|
31
|
+
{
|
|
32
|
+
compile: true,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
18
35
|
// Find the kind: Module manifest to learn the target module name and contract.
|
|
19
36
|
const moduleManifest = manifests.find((m: any) => m.kind === "Kernel.Module");
|
|
20
37
|
if (!moduleManifest) {
|
|
@@ -22,22 +39,6 @@ export async function create(resource: any, ctx: ResourceContext): Promise<Resou
|
|
|
22
39
|
}
|
|
23
40
|
const targetModule: string = moduleManifest.metadata.name;
|
|
24
41
|
|
|
25
|
-
// Validate that every non-module manifest's metadata.module (when explicitly set)
|
|
26
|
-
// matches the target module name. A mismatch means the author put the wrong module
|
|
27
|
-
// name in a resource's metadata, which would silently give it an empty context and
|
|
28
|
-
// produce a confusing CEL "Identifier not found" error at runtime.
|
|
29
|
-
for (const manifest of manifests) {
|
|
30
|
-
if (manifest.kind === "Kernel.Module") continue;
|
|
31
|
-
const declaredModule: string | undefined = manifest.metadata?.module;
|
|
32
|
-
if (declaredModule && declaredModule !== targetModule) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Resource '${manifest.metadata?.name ?? "(unnamed)"}' (kind: ${manifest.kind}) inside module '${targetModule}' ` +
|
|
35
|
-
`has metadata.module: '${declaredModule}', but the module is named '${targetModule}'. ` +
|
|
36
|
-
`Update metadata.module to '${targetModule}'.`,
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
42
|
// Validate required inputs before injecting.
|
|
42
43
|
validateRequiredInputs(moduleManifest.variables ?? {}, resource.variables ?? {}, "variables");
|
|
43
44
|
validateRequiredInputs(moduleManifest.secrets ?? {}, resource.secrets ?? {}, "secrets");
|
|
@@ -82,8 +83,8 @@ export async function create(resource: any, ctx: ResourceContext): Promise<Resou
|
|
|
82
83
|
// in the declaring module's evaluation context — no separate imports namespace needed.
|
|
83
84
|
return {
|
|
84
85
|
snapshot: () => ({
|
|
85
|
-
variables: (resource.variables
|
|
86
|
-
secrets: (resource.secrets
|
|
86
|
+
variables: ctx.expandValue(resource.variables, {}) ?? {},
|
|
87
|
+
secrets: ctx.expandValue(resource.secrets, {}) ?? {},
|
|
87
88
|
}),
|
|
88
89
|
run: async () => {
|
|
89
90
|
// Proxy run to target module
|
|
@@ -128,7 +129,6 @@ export const schema = {
|
|
|
128
129
|
type: "object",
|
|
129
130
|
properties: {
|
|
130
131
|
name: { type: "string" },
|
|
131
|
-
source: { type: "string" },
|
|
132
132
|
module: { type: "string" },
|
|
133
133
|
},
|
|
134
134
|
required: ["name"],
|
|
@@ -1,27 +1,6 @@
|
|
|
1
1
|
import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
|
|
2
|
-
import { Loader } from "../../loader.js";
|
|
3
2
|
|
|
4
3
|
export async function create(resource: any, ctx: ResourceContext): Promise<ResourceInstance> {
|
|
5
|
-
const moduleName = resource.metadata.name as string;
|
|
6
|
-
const loader = new Loader();
|
|
7
|
-
|
|
8
|
-
for (const includePath of (resource.include as string[]) ?? []) {
|
|
9
|
-
const manifests = await loader.loadManifest(
|
|
10
|
-
includePath,
|
|
11
|
-
resource.metadata.source as string,
|
|
12
|
-
{},
|
|
13
|
-
);
|
|
14
|
-
for (const manifest of manifests) {
|
|
15
|
-
if (manifest.kind === "Kernel.Module") {
|
|
16
|
-
throw new Error(`Included file "${includePath}" must not declare kind: Module`);
|
|
17
|
-
}
|
|
18
|
-
if (!manifest.metadata.module) {
|
|
19
|
-
manifest.metadata.module = moduleName;
|
|
20
|
-
}
|
|
21
|
-
ctx.registerManifest(manifest);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
4
|
return {
|
|
26
5
|
run: async () => {
|
|
27
6
|
for (const target of (resource.targets as string[]) ?? []) {
|
|
@@ -35,33 +14,3 @@ export async function create(resource: any, ctx: ResourceContext): Promise<Resou
|
|
|
35
14
|
};
|
|
36
15
|
}
|
|
37
16
|
|
|
38
|
-
export const schema = {
|
|
39
|
-
type: "object",
|
|
40
|
-
properties: {
|
|
41
|
-
kind: { type: "string" },
|
|
42
|
-
metadata: {
|
|
43
|
-
type: "object",
|
|
44
|
-
properties: {
|
|
45
|
-
name: { type: "string" },
|
|
46
|
-
version: { type: "string" },
|
|
47
|
-
source: { type: "string" },
|
|
48
|
-
module: { type: "string" },
|
|
49
|
-
},
|
|
50
|
-
required: ["name"],
|
|
51
|
-
additionalProperties: true,
|
|
52
|
-
},
|
|
53
|
-
include: { type: "array", items: { type: "string" } },
|
|
54
|
-
variables: { type: "object" },
|
|
55
|
-
secrets: { type: "object" },
|
|
56
|
-
targets: { type: "array", items: { type: "string" } },
|
|
57
|
-
exports: {
|
|
58
|
-
type: "object",
|
|
59
|
-
properties: {
|
|
60
|
-
kinds: { type: "array", items: { type: "string" } },
|
|
61
|
-
},
|
|
62
|
-
additionalProperties: true,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
required: ["metadata"],
|
|
66
|
-
additionalProperties: false,
|
|
67
|
-
};
|
|
@@ -6,18 +6,18 @@ import type {
|
|
|
6
6
|
} from "@telorun/sdk";
|
|
7
7
|
import { ControllerLoader } from "../../controller-loader.js";
|
|
8
8
|
import { formatAjvErrors, validateResourceDefinition } from "../../manifest-schemas.js";
|
|
9
|
+
import { createTemplateController } from "./resource-template-controller.js";
|
|
9
10
|
|
|
10
11
|
type ResourceDefinitionResource = RuntimeResource & {
|
|
11
|
-
kind: "Definition";
|
|
12
|
+
kind: "Kernel.Definition";
|
|
12
13
|
metadata: {
|
|
13
14
|
[key: string]: any;
|
|
14
15
|
name: string;
|
|
15
16
|
module?: string;
|
|
16
17
|
};
|
|
17
18
|
schema: Record<string, any>;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
controllers: Array<string>;
|
|
19
|
+
capability?: string;
|
|
20
|
+
controllers?: Array<string>;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -33,16 +33,15 @@ class ResourceDefinition implements ResourceInstance {
|
|
|
33
33
|
) {}
|
|
34
34
|
|
|
35
35
|
async init(ctx: ResourceContext) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
36
|
+
if (!this.resource.controllers?.length) {
|
|
37
|
+
const controllerInstance = createTemplateController(this.resource as any);
|
|
38
|
+
ctx.registerDefinition(this.resource);
|
|
39
|
+
await ctx.registerController(
|
|
40
|
+
this.resource.metadata.module,
|
|
41
|
+
this.resource.metadata.name,
|
|
42
|
+
controllerInstance,
|
|
43
|
+
);
|
|
44
|
+
return;
|
|
46
45
|
}
|
|
47
46
|
ctx.emit("ControllerLoading", { controllers: this.resource.controllers });
|
|
48
47
|
try {
|
|
@@ -77,7 +76,7 @@ export async function create(resource: any, ctx: ResourceContext): Promise<Resou
|
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
// Return a fully-formed ResourceDefinition instance
|
|
80
|
-
const definition = resource as ResourceDefinitionResource;
|
|
79
|
+
const definition = resource as unknown as ResourceDefinitionResource;
|
|
81
80
|
return new ResourceDefinition(definition, new ControllerLoader());
|
|
82
81
|
}
|
|
83
82
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { ControllerInstance, ResourceContext, ResourceInstance } from "@telorun/sdk";
|
|
2
|
+
import { isCompiledValue } from "@telorun/sdk";
|
|
3
|
+
|
|
4
|
+
export function createTemplateController(definition: {
|
|
5
|
+
schema: Record<string, any>;
|
|
6
|
+
resources?: any[];
|
|
7
|
+
invoke?: string | { kind?: string; name: string; inputs?: Record<string, any> };
|
|
8
|
+
run?: string;
|
|
9
|
+
}): ControllerInstance {
|
|
10
|
+
return {
|
|
11
|
+
schema: definition.schema ?? { type: "object", additionalProperties: true },
|
|
12
|
+
|
|
13
|
+
create: async (resource: any, ctx: ResourceContext): Promise<ResourceInstance> => {
|
|
14
|
+
const self = { ...resource, name: resource.metadata.name };
|
|
15
|
+
|
|
16
|
+
// Old string form: invoke is a plain string or a CompiledValue (after precompile).
|
|
17
|
+
// New object form: invoke is a plain object (non-CompiledValue) with at least `name`.
|
|
18
|
+
const objectInvoke =
|
|
19
|
+
definition.invoke !== null &&
|
|
20
|
+
typeof definition.invoke === "object" &&
|
|
21
|
+
!isCompiledValue(definition.invoke)
|
|
22
|
+
? (definition.invoke as { kind?: string; name: string; inputs?: Record<string, any> })
|
|
23
|
+
: null;
|
|
24
|
+
const invokeNameTemplate = objectInvoke ? objectInvoke.name : (definition.invoke ?? null);
|
|
25
|
+
const invokeTarget = invokeNameTemplate
|
|
26
|
+
? (ctx.moduleContext.expandWith(invokeNameTemplate, { self }) as string)
|
|
27
|
+
: null;
|
|
28
|
+
const runTarget = definition.run
|
|
29
|
+
? (ctx.moduleContext.expandWith(definition.run, { self }) as string)
|
|
30
|
+
: null;
|
|
31
|
+
|
|
32
|
+
const persistentManifests: any[] = [];
|
|
33
|
+
let ephemeralTemplate: any = null;
|
|
34
|
+
|
|
35
|
+
for (const template of definition.resources ?? []) {
|
|
36
|
+
const expandedName = ctx.moduleContext.expandWith(template.metadata?.name ?? "", {
|
|
37
|
+
self,
|
|
38
|
+
}) as string;
|
|
39
|
+
const isTarget = expandedName === invokeTarget || expandedName === runTarget;
|
|
40
|
+
if (isTarget) {
|
|
41
|
+
ephemeralTemplate = template;
|
|
42
|
+
} else {
|
|
43
|
+
persistentManifests.push(ctx.moduleContext.expandWith(template, { self }));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const childContext = ctx.spawnChildContext();
|
|
48
|
+
|
|
49
|
+
// Registers an ephemeral manifest on ctx.moduleContext so it shares the same
|
|
50
|
+
// resource scope (and can access connections, etc. via getInstance).
|
|
51
|
+
// Tears down and removes the resource after fn() completes.
|
|
52
|
+
const withEphemeral = async (expandedManifest: any, fn: (name: string) => Promise<any>) => {
|
|
53
|
+
const uniqueName = `${expandedManifest.metadata?.name ?? "eph"}__${Math.random().toString(16).slice(2, 8)}`;
|
|
54
|
+
const manifest = {
|
|
55
|
+
...expandedManifest,
|
|
56
|
+
metadata: {
|
|
57
|
+
...expandedManifest.metadata,
|
|
58
|
+
name: uniqueName,
|
|
59
|
+
module: resource.metadata.module,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
ctx.moduleContext.registerManifest(manifest);
|
|
63
|
+
await ctx.moduleContext.initializeResources();
|
|
64
|
+
const entry = ctx.moduleContext.resourceInstances.get(uniqueName);
|
|
65
|
+
try {
|
|
66
|
+
return await fn(uniqueName);
|
|
67
|
+
} finally {
|
|
68
|
+
if (entry?.instance?.teardown) await entry.instance.teardown();
|
|
69
|
+
ctx.moduleContext.resourceInstances.delete(uniqueName);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
init: async () => {
|
|
75
|
+
for (const m of persistentManifests) childContext.registerManifest(m);
|
|
76
|
+
await childContext.initializeResources();
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
...(invokeTarget && {
|
|
80
|
+
invoke: async (inputs: any) => {
|
|
81
|
+
if (!ephemeralTemplate) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Template '${resource.metadata.name}': no ephemeral resource for invoke target '${invokeTarget}'`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const extraContext = { self, inputs };
|
|
87
|
+
const expanded = ctx.moduleContext.expandWith(
|
|
88
|
+
ctx.moduleContext.expandWith(ephemeralTemplate, extraContext),
|
|
89
|
+
extraContext,
|
|
90
|
+
) as any;
|
|
91
|
+
return withEphemeral(expanded, async (name) => {
|
|
92
|
+
const entry = ctx.moduleContext.resourceInstances.get(name);
|
|
93
|
+
if (!entry?.instance?.invoke) {
|
|
94
|
+
throw new Error(`Ephemeral resource '${name}' is not invocable`);
|
|
95
|
+
}
|
|
96
|
+
// New object form: expand objectInvoke.inputs with invoke context and pass as arg.
|
|
97
|
+
// Old string form: the manifest inputs were computed during template expansion;
|
|
98
|
+
// pass expanded.inputs so Sql.Exec/Query controllers receive { sql, bindings }.
|
|
99
|
+
const invokeInputs = objectInvoke?.inputs != null
|
|
100
|
+
? ctx.moduleContext.expandWith(
|
|
101
|
+
ctx.moduleContext.expandWith(objectInvoke.inputs, extraContext),
|
|
102
|
+
extraContext,
|
|
103
|
+
)
|
|
104
|
+
: expanded.inputs ?? inputs;
|
|
105
|
+
return entry.instance.invoke(invokeInputs);
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
}),
|
|
109
|
+
|
|
110
|
+
...(runTarget && {
|
|
111
|
+
run: async () => {
|
|
112
|
+
if (!ephemeralTemplate) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Template '${resource.metadata.name}': no ephemeral resource for run target '${runTarget}'`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const extraContext = { self };
|
|
118
|
+
const expanded = ctx.moduleContext.expandWith(
|
|
119
|
+
ctx.moduleContext.expandWith(ephemeralTemplate, extraContext),
|
|
120
|
+
extraContext,
|
|
121
|
+
);
|
|
122
|
+
return withEphemeral(expanded, async (name) => {
|
|
123
|
+
const entry = ctx.moduleContext.resourceInstances.get(name);
|
|
124
|
+
if (!entry?.instance?.run) {
|
|
125
|
+
throw new Error(`Ephemeral resource '${name}' is not runnable`);
|
|
126
|
+
}
|
|
127
|
+
return entry.instance.run();
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
|
|
132
|
+
teardown: async () => {
|
|
133
|
+
await childContext.teardownResources();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { ControllerRegistry } from "./controller-registry.js";
|
|
2
2
|
export { EventStream } from "./event-stream.js";
|
|
3
|
-
export { Kernel } from "./kernel.js";
|
|
4
|
-
export { Loader } from "./loader.js";
|
|
3
|
+
export { Kernel, type KernelOptions } from "./kernel.js";
|
|
5
4
|
export { ManifestRegistry as Registry } from "./registry.js";
|
|
6
5
|
export { ResourceURI } from "./resource-uri.js";
|
|
6
|
+
export type { RuntimeDiagnostic } from "@telorun/sdk";
|
|
7
7
|
|