@telorun/kernel 0.2.6 → 0.2.8

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 (57) hide show
  1. package/README.md +100 -0
  2. package/dist/controller-registry.js +2 -2
  3. package/dist/controller-registry.js.map +1 -1
  4. package/dist/controllers/module/import-controller.d.ts.map +1 -1
  5. package/dist/controllers/module/import-controller.js +18 -9
  6. package/dist/controllers/module/import-controller.js.map +1 -1
  7. package/dist/evaluation-context.d.ts +76 -38
  8. package/dist/evaluation-context.d.ts.map +1 -1
  9. package/dist/evaluation-context.js +254 -89
  10. package/dist/evaluation-context.js.map +1 -1
  11. package/dist/execution-context.d.ts +1 -1
  12. package/dist/execution-context.d.ts.map +1 -1
  13. package/dist/execution-context.js +1 -1
  14. package/dist/execution-context.js.map +1 -1
  15. package/dist/index.d.ts +4 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +3 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/kernel.d.ts +25 -2
  20. package/dist/kernel.d.ts.map +1 -1
  21. package/dist/kernel.js +100 -27
  22. package/dist/kernel.js.map +1 -1
  23. package/dist/manifest-adapters/local-file-adapter.d.ts +1 -1
  24. package/dist/manifest-adapters/local-file-adapter.d.ts.map +1 -1
  25. package/dist/manifest-adapters/local-file-adapter.js +2 -1
  26. package/dist/manifest-adapters/local-file-adapter.js.map +1 -1
  27. package/dist/manifest-adapters/manifest-adapter.d.ts +1 -1
  28. package/dist/module-context.d.ts +28 -5
  29. package/dist/module-context.d.ts.map +1 -1
  30. package/dist/module-context.js +107 -4
  31. package/dist/module-context.js.map +1 -1
  32. package/dist/resource-context.d.ts +14 -10
  33. package/dist/resource-context.d.ts.map +1 -1
  34. package/dist/resource-context.js +44 -8
  35. package/dist/resource-context.js.map +1 -1
  36. package/dist/schema-valiator.d.ts +7 -1
  37. package/dist/schema-valiator.d.ts.map +1 -1
  38. package/dist/schema-valiator.js +75 -4
  39. package/dist/schema-valiator.js.map +1 -1
  40. package/dist/schema-validator.d.ts +15 -0
  41. package/dist/schema-validator.d.ts.map +1 -0
  42. package/dist/schema-validator.js +127 -0
  43. package/dist/schema-validator.js.map +1 -0
  44. package/package.json +21 -10
  45. package/src/controller-registry.ts +2 -2
  46. package/src/controllers/module/import-controller.ts +27 -11
  47. package/src/evaluation-context.ts +490 -0
  48. package/src/execution-context.ts +21 -0
  49. package/src/index.ts +4 -1
  50. package/src/kernel.ts +144 -38
  51. package/src/manifest-adapters/local-file-adapter.ts +2 -2
  52. package/src/manifest-adapters/manifest-adapter.ts +1 -1
  53. package/src/module-context.ts +211 -0
  54. package/src/resource-context.ts +67 -12
  55. package/src/schema-validator.ts +146 -0
  56. package/src/loader.ts +0 -134
  57. package/src/schema-valiator.ts +0 -68
@@ -0,0 +1,146 @@
1
+ import { evaluate } from "@marcbachmann/cel-js";
2
+ import { DataValidator, RuntimeError, TypeRule } from "@telorun/sdk";
3
+ import AjvModule from "ajv";
4
+ import addFormats from "ajv-formats";
5
+ import { formatAjvErrors } from "./manifest-schemas.js";
6
+
7
+ const Ajv = AjvModule.default ?? AjvModule;
8
+
9
+ export class SchemaValidator {
10
+ private ajv: InstanceType<typeof Ajv>;
11
+ private typeRules = new Map<string, TypeRule[]>();
12
+ private rawSchemas = new Map<string, object>();
13
+ private compiledValidators = new WeakMap<object, DataValidator>();
14
+
15
+ constructor() {
16
+ this.ajv = new Ajv({
17
+ strict: false,
18
+ removeAdditional: false,
19
+ useDefaults: true,
20
+ });
21
+ addFormats.default(this.ajv);
22
+ for (const kw of [
23
+ "x-telo-ref",
24
+ "x-telo-eval",
25
+ "x-telo-scope",
26
+ "x-telo-context",
27
+ "x-telo-context-from",
28
+ "x-telo-context-ref-from",
29
+ "x-telo-schema-from",
30
+ "x-telo-topology-role",
31
+ "x-telo-step-context",
32
+ ]) {
33
+ this.ajv.addKeyword(kw);
34
+ }
35
+ }
36
+
37
+ addSchema(name: string, schema: object): void {
38
+ if (!this.ajv.getSchema(name)) {
39
+ this.ajv.addSchema(schema, name);
40
+ }
41
+ this.rawSchemas.set(name, schema);
42
+ }
43
+
44
+ getSchema(name: string): object | undefined {
45
+ return this.rawSchemas.get(name);
46
+ }
47
+
48
+ addTypeRules(name: string, rules: TypeRule[]): void {
49
+ this.typeRules.set(name, rules);
50
+ }
51
+
52
+ getTypeRules(name: string): TypeRule[] | undefined {
53
+ return this.typeRules.get(name);
54
+ }
55
+
56
+ compile(schema: any): DataValidator {
57
+ if (schema && typeof schema === "object") {
58
+ const cached = this.compiledValidators.get(schema as object);
59
+ if (cached) return cached;
60
+ }
61
+
62
+ const isFullSchema =
63
+ ("type" in schema && typeof schema.type === "string") ||
64
+ "allOf" in schema ||
65
+ "anyOf" in schema ||
66
+ "oneOf" in schema ||
67
+ "$ref" in schema;
68
+ const normalized = isFullSchema
69
+ ? schema
70
+ : {
71
+ type: "object",
72
+ properties: schema,
73
+ required: Object.keys(schema),
74
+ additionalProperties: false,
75
+ };
76
+ const injected =
77
+ normalized.additionalProperties === false
78
+ ? {
79
+ ...normalized,
80
+ properties: {
81
+ kind: { type: "string" },
82
+ metadata: { type: "object" },
83
+ ...normalized.properties,
84
+ },
85
+ }
86
+ : normalized;
87
+ const validate = this.ajv.compile(injected);
88
+
89
+ const validator = {
90
+ validate: (data: any) => {
91
+ const isValid = validate(data);
92
+ if (!isValid) {
93
+ throw new RuntimeError(
94
+ "ERR_RESOURCE_SCHEMA_VALIDATION_FAILED",
95
+ `Invalid value passed: ${JSON.stringify(data)}. Error: ${formatAjvErrors(validate.errors)}`,
96
+ );
97
+ }
98
+ },
99
+ isValid: (data: any) => {
100
+ return validate(data);
101
+ },
102
+ };
103
+
104
+ if (schema && typeof schema === "object") {
105
+ this.compiledValidators.set(schema as object, validator);
106
+ }
107
+
108
+ return validator;
109
+ }
110
+
111
+ composeWithRules(base: DataValidator, typeName: string, rules: TypeRule[]): DataValidator {
112
+ return {
113
+ validate: (data: any) => {
114
+ base.validate(data);
115
+ for (const rule of rules) {
116
+ let result: unknown;
117
+ try {
118
+ result = evaluate(rule.condition, { this: data });
119
+ } catch (err) {
120
+ throw new RuntimeError(
121
+ "ERR_TYPE_VALIDATION_FAILED",
122
+ `Type "${typeName}" rule evaluation failed: ${err instanceof Error ? err.message : String(err)}`,
123
+ );
124
+ }
125
+ if (result !== true) {
126
+ throw new RuntimeError(
127
+ rule.code ?? "ERR_TYPE_VALIDATION_FAILED",
128
+ rule.message ?? `Type "${typeName}" validation failed: rule "${rule.code}" not satisfied`,
129
+ );
130
+ }
131
+ }
132
+ },
133
+ isValid: (data: any) => {
134
+ if (!base.isValid(data)) return false;
135
+ for (const rule of rules) {
136
+ try {
137
+ if (evaluate(rule.condition, { this: data }) !== true) return false;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+ return true;
143
+ },
144
+ };
145
+ }
146
+ }
package/src/loader.ts DELETED
@@ -1,134 +0,0 @@
1
- import { Loader as BaseLoader } from "@telorun/analyzer";
2
- import { ResourceManifest, RuntimeResource } from "@telorun/sdk";
3
- import * as path from "path";
4
- import { LocalFileAdapter } from "./manifest-adapters/local-file-adapter.js";
5
- import { formatAjvErrors, validateRuntimeResource } from "./manifest-schemas.js";
6
-
7
- export class Loader extends BaseLoader {
8
- private static projectRoot: string | null = null;
9
- private readonly localAdapter: LocalFileAdapter;
10
-
11
- constructor() {
12
- super();
13
- this.localAdapter = new LocalFileAdapter();
14
- this.register(this.localAdapter);
15
- }
16
-
17
- private static ensureProjectRoot(baseDir: string): void {
18
- if (!Loader.projectRoot) {
19
- Loader.projectRoot = path.resolve(baseDir);
20
- }
21
- }
22
-
23
- resolvePath(base: string, relative: string): string {
24
- return this.localAdapter.resolveRelative(base, relative);
25
- }
26
-
27
- /**
28
- * Resolve a path-or-URL to its canonical file source URL.
29
- * If the path refers to a directory, returns the URL of module.yaml inside it.
30
- */
31
- async resolveEntryPoint(pathOrUrl: string): Promise<string> {
32
- const { source } = await this.localAdapter.read(pathOrUrl);
33
- return source;
34
- }
35
-
36
- async loadDirectory(pathOrUrl: string): Promise<ResourceManifest[]> {
37
- const sources = await this.localAdapter.readAll(pathOrUrl);
38
- const firstPath = sources[0] ? new URL(sources[0]).pathname : process.cwd();
39
- Loader.ensureProjectRoot(path.dirname(firstPath));
40
- const resources: RuntimeResource[] = [];
41
- for (const source of sources) {
42
- const manifests = await this.loadModule(source);
43
- for (const m of manifests) {
44
- if (!m.metadata?.name) continue;
45
- const resource = m as RuntimeResource;
46
- if (!validateRuntimeResource(resource)) {
47
- throw new Error(
48
- `Resource validation failed for ${m.kind}.${m.metadata.name}: ${formatAjvErrors(validateRuntimeResource.errors)}`,
49
- );
50
- }
51
- const filePath = new URL(m.metadata.source as string).pathname;
52
- const uriBase = `file://localhost${filePath.replace(/\\/g, "/")}`;
53
- resource.metadata.uri = `${uriBase}#${m.kind}.${m.metadata.name as string}`;
54
- resource.metadata.generationDepth = 0;
55
- resources.push(resource);
56
- }
57
- }
58
- return this.orderResourcesByKindDependencies(resources);
59
- }
60
-
61
- async loadManifest(
62
- pathOrUrl: string,
63
- baseUrl: string,
64
- compileContext: Record<string, unknown> = {},
65
- ): Promise<ResourceManifest[]> {
66
- if (!baseUrl) {
67
- throw new Error("Base URL is required to load target manifest");
68
- }
69
- const url = new URL(pathOrUrl, baseUrl).toString();
70
- if (!Loader.projectRoot) {
71
- Loader.ensureProjectRoot(path.dirname(new URL(url).pathname));
72
- }
73
- return this.loadModule(url, { compile: true });
74
- }
75
-
76
- private orderResourcesByKindDependencies(resources: RuntimeResource[]): RuntimeResource[] {
77
- if (resources.length <= 1) return resources;
78
-
79
- const indicesByName = new Map<string, number[]>();
80
- for (let i = 0; i < resources.length; i++) {
81
- const name = resources[i]?.metadata?.name;
82
- if (!name) continue;
83
- const list = indicesByName.get(name);
84
- if (list) list.push(i);
85
- else indicesByName.set(name, [i]);
86
- }
87
-
88
- const edges = new Map<number, Set<number>>();
89
- const indegree = new Map<number, number>();
90
- for (let i = 0; i < resources.length; i++) indegree.set(i, 0);
91
-
92
- for (let i = 0; i < resources.length; i++) {
93
- const kind = resources[i]?.kind;
94
- if (!kind) continue;
95
- const definers = indicesByName.get(kind);
96
- if (!definers) continue;
97
- for (const definerIndex of definers) {
98
- if (definerIndex === i) continue;
99
- let set = edges.get(definerIndex);
100
- if (!set) {
101
- set = new Set();
102
- edges.set(definerIndex, set);
103
- }
104
- if (!set.has(i)) {
105
- set.add(i);
106
- indegree.set(i, (indegree.get(i) || 0) + 1);
107
- }
108
- }
109
- }
110
-
111
- const ready: number[] = [];
112
- for (let i = 0; i < resources.length; i++) {
113
- if ((indegree.get(i) || 0) === 0) ready.push(i);
114
- }
115
- ready.sort((a, b) => a - b);
116
-
117
- const ordered: RuntimeResource[] = [];
118
- while (ready.length > 0) {
119
- const index = ready.shift() as number;
120
- ordered.push(resources[index]);
121
- const next = edges.get(index);
122
- if (!next) continue;
123
- for (const dependent of next) {
124
- const count = (indegree.get(dependent) || 0) - 1;
125
- indegree.set(dependent, count);
126
- if (count === 0) ready.push(dependent);
127
- }
128
- if (ready.length > 1) ready.sort((a, b) => a - b);
129
- }
130
-
131
- if (ordered.length !== resources.length) throw new Error("Resource dependency cycle detected");
132
- return ordered;
133
- }
134
- }
@@ -1,68 +0,0 @@
1
- import { DataValidator, RuntimeError } from "@telorun/sdk";
2
- import AjvModule from "ajv";
3
- import addFormats from "ajv-formats";
4
- import { formatAjvErrors } from "./manifest-schemas.js";
5
-
6
- const Ajv = AjvModule.default ?? AjvModule;
7
-
8
- export class SchemaValidator {
9
- private ajv: InstanceType<typeof Ajv>;
10
-
11
- constructor() {
12
- this.ajv = new Ajv({
13
- strict: false,
14
- removeAdditional: false,
15
- useDefaults: true,
16
- });
17
- addFormats.default(this.ajv);
18
- }
19
-
20
- addSchema(name: string, schema: object): void {
21
- if (!this.ajv.getSchema(name)) {
22
- this.ajv.addSchema(schema, name);
23
- }
24
- }
25
-
26
- getSchema(name: string): object | undefined {
27
- return this.ajv.getSchema(name) as object | undefined;
28
- }
29
-
30
- compile(schema: any): DataValidator {
31
- const normalized =
32
- "type" in schema && typeof schema.type === "string"
33
- ? schema
34
- : {
35
- type: "object",
36
- properties: schema,
37
- required: Object.keys(schema),
38
- additionalProperties: false,
39
- };
40
- const injected =
41
- normalized.additionalProperties === false
42
- ? {
43
- ...normalized,
44
- properties: {
45
- kind: { type: "string" },
46
- metadata: { type: "object" },
47
- ...normalized.properties,
48
- },
49
- }
50
- : normalized;
51
- const validate = this.ajv.compile(injected);
52
-
53
- return {
54
- validate: (data: any) => {
55
- const isValid = validate(data);
56
- if (!isValid) {
57
- throw new RuntimeError(
58
- "ERR_RESOURCE_NOT_FOUND",
59
- `Invalid value passed: ${JSON.stringify(data)}. Error: ${formatAjvErrors(validate.errors)}`,
60
- );
61
- }
62
- },
63
- isValid: (data: any) => {
64
- return validate(data);
65
- },
66
- };
67
- }
68
- }