@telorun/kernel 0.2.4 → 0.2.6

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 (192) hide show
  1. package/dist/base-definition.d.ts +14 -0
  2. package/dist/base-definition.d.ts.map +1 -0
  3. package/dist/base-definition.js +17 -0
  4. package/dist/base-definition.js.map +1 -0
  5. package/dist/boot-context-registry.d.ts.map +1 -1
  6. package/dist/boot-context-registry.js +6 -6
  7. package/dist/boot-context-registry.js.map +1 -1
  8. package/dist/capabilities/capabilities/component.yaml +2 -1
  9. package/dist/capabilities/capabilities/executable.yaml +2 -1
  10. package/dist/capabilities/capabilities/handler.yaml +2 -1
  11. package/dist/capabilities/capabilities/listener.yaml +2 -1
  12. package/dist/capabilities/capabilities/provider.yaml +2 -1
  13. package/dist/capabilities/capabilities/template.yaml +2 -1
  14. package/dist/capabilities/capabilities/type.yaml +2 -1
  15. package/dist/capabilities/component.d.ts +3 -0
  16. package/dist/capabilities/component.d.ts.map +1 -0
  17. package/dist/capabilities/component.js +4 -0
  18. package/dist/capabilities/component.js.map +1 -0
  19. package/dist/capabilities/component.yaml +1 -1
  20. package/dist/capabilities/executable.d.ts +3 -0
  21. package/dist/capabilities/executable.d.ts.map +1 -0
  22. package/dist/capabilities/executable.js +5 -0
  23. package/dist/capabilities/executable.js.map +1 -0
  24. package/dist/capabilities/executable.yaml +1 -1
  25. package/dist/capabilities/handler.d.ts +3 -0
  26. package/dist/capabilities/handler.d.ts.map +1 -0
  27. package/dist/capabilities/handler.js +4 -0
  28. package/dist/capabilities/handler.js.map +1 -0
  29. package/dist/capabilities/handler.yaml +1 -1
  30. package/dist/capabilities/invokable.d.ts +3 -0
  31. package/dist/capabilities/invokable.d.ts.map +1 -0
  32. package/dist/capabilities/invokable.js +5 -0
  33. package/dist/capabilities/invokable.js.map +1 -0
  34. package/dist/capabilities/listener.d.ts +3 -0
  35. package/dist/capabilities/listener.d.ts.map +1 -0
  36. package/dist/capabilities/listener.js +5 -0
  37. package/dist/capabilities/listener.js.map +1 -0
  38. package/dist/capabilities/listener.yaml +1 -1
  39. package/dist/capabilities/mount.d.ts +3 -0
  40. package/dist/capabilities/mount.d.ts.map +1 -0
  41. package/dist/capabilities/mount.js +5 -0
  42. package/dist/capabilities/mount.js.map +1 -0
  43. package/dist/capabilities/provider.d.ts +3 -0
  44. package/dist/capabilities/provider.d.ts.map +1 -0
  45. package/dist/capabilities/provider.js +8 -0
  46. package/dist/capabilities/provider.js.map +1 -0
  47. package/dist/capabilities/provider.yaml +1 -1
  48. package/dist/capabilities/runnable.d.ts +3 -0
  49. package/dist/capabilities/runnable.d.ts.map +1 -0
  50. package/dist/capabilities/runnable.js +5 -0
  51. package/dist/capabilities/runnable.js.map +1 -0
  52. package/dist/capabilities/service.d.ts +3 -0
  53. package/dist/capabilities/service.d.ts.map +1 -0
  54. package/dist/capabilities/service.js +5 -0
  55. package/dist/capabilities/service.js.map +1 -0
  56. package/dist/capabilities/template.d.ts +3 -0
  57. package/dist/capabilities/template.d.ts.map +1 -0
  58. package/dist/capabilities/template.js +5 -0
  59. package/dist/capabilities/template.js.map +1 -0
  60. package/dist/capabilities/template.yaml +1 -1
  61. package/dist/capabilities/type.d.ts +3 -0
  62. package/dist/capabilities/type.d.ts.map +1 -0
  63. package/dist/capabilities/type.js +5 -0
  64. package/dist/capabilities/type.js.map +1 -0
  65. package/dist/capabilities/type.yaml +1 -1
  66. package/dist/controller-loader.d.ts +1 -1
  67. package/dist/controller-loader.d.ts.map +1 -1
  68. package/dist/controller-loader.js +6 -3
  69. package/dist/controller-loader.js.map +1 -1
  70. package/dist/controller-registry.d.ts +1 -6
  71. package/dist/controller-registry.d.ts.map +1 -1
  72. package/dist/controller-registry.js +11 -22
  73. package/dist/controller-registry.js.map +1 -1
  74. package/dist/controllers/capability/capability-controller.d.ts +0 -5
  75. package/dist/controllers/capability/capability-controller.d.ts.map +1 -1
  76. package/dist/controllers/capability/capability-controller.js +1 -5
  77. package/dist/controllers/capability/capability-controller.js.map +1 -1
  78. package/dist/controllers/module/import-controller.d.ts +35 -0
  79. package/dist/controllers/module/import-controller.d.ts.map +1 -0
  80. package/dist/controllers/module/import-controller.js +113 -0
  81. package/dist/controllers/module/import-controller.js.map +1 -0
  82. package/dist/controllers/module/module-controller.d.ts +2 -15
  83. package/dist/controllers/module/module-controller.d.ts.map +1 -1
  84. package/dist/controllers/module/module-controller.js +9 -90
  85. package/dist/controllers/module/module-controller.js.map +1 -1
  86. package/dist/controllers/resource-definition/resource-definition-controller.d.ts +3 -4
  87. package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
  88. package/dist/controllers/resource-definition/resource-definition-controller.js +17 -11
  89. package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
  90. package/dist/controllers/resource-definition/resource-template-controller.d.ts +12 -0
  91. package/dist/controllers/resource-definition/resource-template-controller.d.ts.map +1 -0
  92. package/dist/controllers/resource-definition/resource-template-controller.js +112 -0
  93. package/dist/controllers/resource-definition/resource-template-controller.js.map +1 -0
  94. package/dist/evaluation-context.d.ts +91 -0
  95. package/dist/evaluation-context.d.ts.map +1 -0
  96. package/dist/evaluation-context.js +220 -0
  97. package/dist/evaluation-context.js.map +1 -0
  98. package/dist/execution-context.d.ts +13 -0
  99. package/dist/execution-context.d.ts.map +1 -0
  100. package/dist/execution-context.js +14 -0
  101. package/dist/execution-context.js.map +1 -0
  102. package/dist/index.d.ts +1 -4
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +0 -3
  105. package/dist/index.js.map +1 -1
  106. package/dist/kernel.d.ts +31 -42
  107. package/dist/kernel.d.ts.map +1 -1
  108. package/dist/kernel.js +315 -371
  109. package/dist/kernel.js.map +1 -1
  110. package/dist/loader.d.ts +11 -10
  111. package/dist/loader.d.ts.map +1 -1
  112. package/dist/loader.js +50 -112
  113. package/dist/loader.js.map +1 -1
  114. package/dist/manifest-adapters/http-adapter.d.ts.map +1 -1
  115. package/dist/manifest-adapters/http-adapter.js +3 -1
  116. package/dist/manifest-adapters/http-adapter.js.map +1 -1
  117. package/dist/manifest-adapters/local-file-adapter.d.ts +7 -5
  118. package/dist/manifest-adapters/local-file-adapter.d.ts.map +1 -1
  119. package/dist/manifest-adapters/local-file-adapter.js +28 -24
  120. package/dist/manifest-adapters/local-file-adapter.js.map +1 -1
  121. package/dist/manifest-adapters/manifest-adapter.d.ts +2 -0
  122. package/dist/manifest-adapters/manifest-adapter.d.ts.map +1 -1
  123. package/dist/manifest-adapters/registry-adapter.d.ts.map +1 -1
  124. package/dist/manifest-adapters/registry-adapter.js +3 -1
  125. package/dist/manifest-adapters/registry-adapter.js.map +1 -1
  126. package/dist/manifest-schemas.d.ts +61 -49
  127. package/dist/manifest-schemas.d.ts.map +1 -1
  128. package/dist/manifest-schemas.js +58 -37
  129. package/dist/manifest-schemas.js.map +1 -1
  130. package/dist/module-context-registry.d.ts +48 -0
  131. package/dist/module-context-registry.d.ts.map +1 -0
  132. package/dist/module-context-registry.js +91 -0
  133. package/dist/module-context-registry.js.map +1 -0
  134. package/dist/module-context.d.ts +31 -0
  135. package/dist/module-context.d.ts.map +1 -0
  136. package/dist/module-context.js +67 -0
  137. package/dist/module-context.js.map +1 -0
  138. package/dist/registry.d.ts +1 -2
  139. package/dist/registry.d.ts.map +1 -1
  140. package/dist/registry.js +3 -3
  141. package/dist/registry.js.map +1 -1
  142. package/dist/resource-context.d.ts +27 -10
  143. package/dist/resource-context.d.ts.map +1 -1
  144. package/dist/resource-context.js +100 -44
  145. package/dist/resource-context.js.map +1 -1
  146. package/dist/schema-valiator.d.ts.map +1 -1
  147. package/dist/schema-valiator.js +16 -3
  148. package/dist/schema-valiator.js.map +1 -1
  149. package/dist/snapshot-serializer.d.ts +1 -2
  150. package/dist/snapshot-serializer.d.ts.map +1 -1
  151. package/dist/snapshot-serializer.js.map +1 -1
  152. package/dist/types.d.ts +3 -0
  153. package/dist/types.d.ts.map +1 -1
  154. package/dist/types.js.map +1 -1
  155. package/package.json +11 -9
  156. package/src/boot-context-registry.ts +169 -0
  157. package/src/controller-loader.ts +299 -0
  158. package/src/controller-registry.ts +191 -0
  159. package/src/controllers/module/import-controller.ts +143 -0
  160. package/src/controllers/module/module-controller.ts +16 -0
  161. package/src/controllers/resource-definition/resource-definition-controller.ts +86 -0
  162. package/src/controllers/resource-definition/resource-definition.json +18 -0
  163. package/src/controllers/resource-definition/resource-template-controller.ts +138 -0
  164. package/src/event-stream.ts +121 -0
  165. package/src/events.ts +99 -0
  166. package/src/index.ts +7 -0
  167. package/src/kernel.ts +647 -0
  168. package/src/loader.ts +134 -0
  169. package/src/manifest-adapters/local-file-adapter.ts +62 -0
  170. package/src/manifest-adapters/manifest-adapter.ts +35 -0
  171. package/src/manifest-schemas.ts +85 -0
  172. package/src/registry.ts +137 -0
  173. package/src/resource-context.ts +267 -0
  174. package/src/resource-uri.ts +200 -0
  175. package/src/schema-valiator.ts +68 -0
  176. package/dist/cli.d.ts +0 -3
  177. package/dist/cli.d.ts.map +0 -1
  178. package/dist/cli.js +0 -109
  179. package/dist/cli.js.map +0 -1
  180. package/dist/expressions.d.ts +0 -20
  181. package/dist/expressions.d.ts.map +0 -1
  182. package/dist/expressions.js +0 -253
  183. package/dist/expressions.js.map +0 -1
  184. package/dist/src/controllers/module/module.yaml +0 -32
  185. package/dist/template-definition.d.ts +0 -38
  186. package/dist/template-definition.d.ts.map +0 -1
  187. package/dist/template-definition.js +0 -26
  188. package/dist/template-definition.js.map +0 -1
  189. package/dist/template-expander.d.ts +0 -19
  190. package/dist/template-expander.d.ts.map +0 -1
  191. package/dist/template-expander.js +0 -425
  192. package/dist/template-expander.js.map +0 -1
package/src/loader.ts ADDED
@@ -0,0 +1,134 @@
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
+ }
@@ -0,0 +1,62 @@
1
+ import type { ManifestAdapter } from "@telorun/analyzer";
2
+ import * as fs from "fs/promises";
3
+ import * as path from "path";
4
+
5
+ export class LocalFileAdapter implements ManifestAdapter {
6
+ supports(pathOrUrl: string): boolean {
7
+ return (
8
+ pathOrUrl.startsWith("file://") ||
9
+ pathOrUrl.startsWith("/") ||
10
+ pathOrUrl.startsWith("./") ||
11
+ pathOrUrl.startsWith("../") ||
12
+ (!pathOrUrl.includes("://") && !pathOrUrl.includes("@"))
13
+ );
14
+ }
15
+
16
+ async read(pathOrUrl: string): Promise<{ text: string; source: string }> {
17
+ const normalizedPath = pathOrUrl.startsWith("file://")
18
+ ? new URL(pathOrUrl).pathname
19
+ : pathOrUrl;
20
+ const resolvedPath = path.resolve(normalizedPath);
21
+ const stat = await fs.stat(resolvedPath);
22
+ const filePath = stat.isDirectory() ? path.join(resolvedPath, "module.yaml") : resolvedPath;
23
+ const text = await fs.readFile(filePath, "utf-8");
24
+ return { text, source: `file://${filePath}` };
25
+ }
26
+
27
+ async readAll(pathOrUrl: string): Promise<string[]> {
28
+ const normalizedPath = pathOrUrl.startsWith("file://")
29
+ ? new URL(pathOrUrl).pathname
30
+ : pathOrUrl;
31
+ const resolvedPath = path.resolve(normalizedPath);
32
+ const stat = await fs.stat(resolvedPath);
33
+ if (stat.isDirectory()) {
34
+ return this.collectYamlSources(resolvedPath);
35
+ }
36
+ return [`file://${resolvedPath}`];
37
+ }
38
+
39
+ resolveRelative(base: string, relative: string): string {
40
+ const basePath = base.startsWith("file://") ? new URL(base).pathname : base;
41
+ const baseDir = basePath.endsWith("/") ? basePath : path.dirname(basePath);
42
+ return `file://${path.resolve(baseDir, relative)}`;
43
+ }
44
+
45
+ private async collectYamlSources(dirPath: string): Promise<string[]> {
46
+ const sources: string[] = [];
47
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
48
+ for (const entry of entries) {
49
+ const fullPath = path.join(dirPath, entry.name);
50
+ if (entry.isDirectory()) {
51
+ sources.push(...(await this.collectYamlSources(fullPath)));
52
+ } else if (entry.isFile() && this.isYamlFile(entry.name)) {
53
+ sources.push(`file://${fullPath}`);
54
+ }
55
+ }
56
+ return sources;
57
+ }
58
+
59
+ private isYamlFile(filename: string): boolean {
60
+ return filename.endsWith(".yaml") || filename.endsWith(".yml");
61
+ }
62
+ }
@@ -0,0 +1,35 @@
1
+ export interface ManifestSourceData {
2
+ /** Raw YAML text */
3
+ text: string;
4
+ /** Parsed YAML documents (result of yaml.loadAll) */
5
+ documents: any[];
6
+ /** Stored as metadata.source (file path or URL) */
7
+ source: string;
8
+ /** Base directory for resolving relative controller entrypoints */
9
+ baseDir: string;
10
+ /** URI prefix — full resource URI is `${uriBase}#${kind}.${name}` */
11
+ uriBase: string;
12
+ }
13
+
14
+ export interface ManifestAdapter {
15
+ /** Returns true if this adapter can handle the given path/URL */
16
+ supports(pathOrUrl: string): boolean;
17
+ /**
18
+ * Read a single manifest entry point.
19
+ * - File path or URL → read that file/URL.
20
+ * - Directory path → find and read `module.yaml` within it.
21
+ */
22
+ read(pathOrUrl: string): Promise<ManifestSourceData>;
23
+ /**
24
+ * Read all manifest files reachable from the given path/URL.
25
+ * Used for module imports.
26
+ * - Directory path → recursive walk, one entry per .yaml/.yml file found.
27
+ * - File path or URL → single-item array.
28
+ */
29
+ readAll(pathOrUrl: string): Promise<ManifestSourceData[]>;
30
+ /**
31
+ * Resolve a potentially relative path/URL against a base directory/URL.
32
+ * For absolute inputs the base is ignored.
33
+ */
34
+ resolveRelative(base: string, relative: string): string;
35
+ }
@@ -0,0 +1,85 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import AjvModule from "ajv";
3
+ import addFormats from "ajv-formats";
4
+ const Ajv = AjvModule.default ?? AjvModule;
5
+
6
+ export const RuntimeResourceSchema = Type.Object(
7
+ {
8
+ kind: Type.String(),
9
+ metadata: Type.Object({ name: Type.String() }, { additionalProperties: true }),
10
+ },
11
+ { additionalProperties: true },
12
+ );
13
+
14
+ const metadataSchema = {
15
+ type: "object",
16
+ required: ["name"],
17
+ properties: {
18
+ name: { type: "string" },
19
+ module: { type: "string" },
20
+ },
21
+ additionalProperties: true,
22
+ };
23
+
24
+ const baseDefinition = {
25
+ type: "object",
26
+ required: ["kind", "metadata"],
27
+ properties: {
28
+ kind: { const: "Kernel.Definition" },
29
+ metadata: metadataSchema,
30
+ capability: { type: "string" },
31
+ schema: { type: "object", additionalProperties: true },
32
+ controllers: { type: "array", items: { type: "string" } },
33
+ },
34
+ unevaluatedProperties: false,
35
+ };
36
+
37
+ const KNOWN_CAPABILITIES = [
38
+ "Kernel.Service",
39
+ "Kernel.Runnable",
40
+ "Kernel.Invocable",
41
+ "Kernel.Provider",
42
+ "Kernel.Type",
43
+ "Kernel.Mount",
44
+ ] as const;
45
+
46
+ export const ResourceDefinitionSchema = {
47
+ ...baseDefinition,
48
+ oneOf: [
49
+ { required: ["capability"], properties: { capability: { const: "Kernel.Service" } } },
50
+ { required: ["capability"], properties: { capability: { const: "Kernel.Runnable" } } },
51
+ { required: ["capability"], properties: { capability: { const: "Kernel.Invocable" } } },
52
+ { required: ["capability"], properties: { capability: { const: "Kernel.Provider" } } },
53
+ { required: ["capability"], properties: { capability: { const: "Kernel.Type" } } },
54
+ {
55
+ required: ["capability"],
56
+ properties: {
57
+ capability: { const: "Kernel.Mount" },
58
+ },
59
+ },
60
+ // Unknown/absent capability: open schema for third-party extensibility
61
+ {
62
+ not: {
63
+ required: ["capability"],
64
+ properties: { capability: { enum: KNOWN_CAPABILITIES } },
65
+ },
66
+ unevaluatedProperties: true,
67
+ },
68
+ ],
69
+ };
70
+
71
+ const ajv = new Ajv({ allErrors: true, strict: false });
72
+ addFormats.default(ajv);
73
+
74
+ export const validateRuntimeResource = ajv.compile(RuntimeResourceSchema);
75
+ export const validateResourceDefinition = ajv.compile(ResourceDefinitionSchema);
76
+
77
+ export function formatAjvErrors(errors: any[] | null | undefined): string {
78
+ if (!errors || errors.length === 0) return "Unknown schema error";
79
+ return errors
80
+ .map((err) => {
81
+ const p = err.instancePath || "/";
82
+ return `${p} ${err.message ?? "is invalid"}`;
83
+ })
84
+ .join("; ");
85
+ }
@@ -0,0 +1,137 @@
1
+ import { ResourceManifest, RuntimeError, RuntimeResource } from "@telorun/sdk";
2
+ import { ResourceURI } from "./resource-uri.js";
3
+
4
+ /**
5
+ * Registry: Indexes resources by composite key of Kind and Name
6
+ * Maintains URI-based lookup for tracking resource origins and lineage
7
+ */
8
+ export class ManifestRegistry {
9
+ private resources: Map<string, Map<string, ResourceManifest>> = new Map();
10
+ private kindInheritance: Map<string, string> = new Map(); // derivedKind -> parentKind
11
+ private uriIndex: Map<string, ResourceManifest> = new Map(); // URI -> Resource
12
+ private sourceIndex: Map<string, ResourceManifest[]> = new Map(); // source path -> Resources
13
+ private depthIndex: Map<number, ResourceManifest[]> = new Map(); // generation depth -> Resources
14
+
15
+ register(resource: RuntimeResource): void {
16
+ const { kind, metadata } = resource;
17
+ const { name } = metadata;
18
+ console.log("Registering resource:", kind, name);
19
+ if (!this.resources.has(kind)) {
20
+ this.resources.set(kind, new Map());
21
+ }
22
+
23
+ const kindMap = this.resources.get(kind)!;
24
+
25
+ if (kindMap.has(name)) {
26
+ throw new RuntimeError("ERR_DUPLICATE_RESOURCE", `Duplicate resource: ${kind}.${name}`);
27
+ }
28
+
29
+ kindMap.set(name, resource);
30
+
31
+ // Index by URI if available
32
+ if (metadata.uri) {
33
+ this.uriIndex.set(metadata.uri, resource);
34
+
35
+ // Index by source file/path
36
+ try {
37
+ const uri = ResourceURI.parse(metadata.uri);
38
+ if (uri.isFileSource()) {
39
+ const sourcePath = uri.path;
40
+ if (!this.sourceIndex.has(sourcePath)) {
41
+ this.sourceIndex.set(sourcePath, []);
42
+ }
43
+ this.sourceIndex.get(sourcePath)!.push(resource);
44
+ }
45
+ } catch {
46
+ // URI parsing failed, skip indexing
47
+ }
48
+ }
49
+
50
+ // Index by generation depth
51
+ const depth = metadata.generationDepth ?? 0;
52
+ if (!this.depthIndex.has(depth)) {
53
+ this.depthIndex.set(depth, []);
54
+ }
55
+ this.depthIndex.get(depth)!.push(resource);
56
+
57
+ // Check if this is a Kernel.KindDefinition that creates a new kind
58
+ // if (kind === 'Kernel.KindDefinition') {
59
+ // const newKind = name;
60
+ // const parentKind = resource?.extends;
61
+ // if (parentKind) {
62
+ // this.kindInheritance.set(newKind, parentKind);
63
+ // }
64
+ // }
65
+ }
66
+
67
+ getParentKind(kind: string): string | undefined {
68
+ return this.kindInheritance.get(kind);
69
+ }
70
+
71
+ resolveKindChain(kind: string): string[] {
72
+ const chain: string[] = [kind];
73
+ let current = kind;
74
+ while (this.kindInheritance.has(current)) {
75
+ current = this.kindInheritance.get(current)!;
76
+ chain.push(current);
77
+ }
78
+ return chain;
79
+ }
80
+
81
+ get(kind: string, name: string): ResourceManifest | undefined {
82
+ return this.resources.get(kind)?.get(name);
83
+ }
84
+
85
+ getByKind(kind: string): ResourceManifest[] {
86
+ const kindMap = this.resources.get(kind);
87
+ return kindMap ? Array.from(kindMap.values()) : [];
88
+ }
89
+
90
+ /**
91
+ * Get resource by its URI
92
+ */
93
+ getByUri(uri: string): ResourceManifest | undefined {
94
+ return this.uriIndex.get(uri);
95
+ }
96
+
97
+ /**
98
+ * Get all resources from a specific source file
99
+ */
100
+ getBySourceFile(sourceFilePath: string): ResourceManifest[] {
101
+ return this.sourceIndex.get(sourceFilePath) ?? [];
102
+ }
103
+
104
+ /**
105
+ * Get all resources at a specific generation depth
106
+ * 0 = directly from files, 1+ = template-generated
107
+ */
108
+ getByGenerationDepth(depth: number): ResourceManifest[] {
109
+ return this.depthIndex.get(depth) ?? [];
110
+ }
111
+
112
+ /**
113
+ * Get all template-generated resources (depth > 0)
114
+ */
115
+ getTemplateGenerated(): ResourceManifest[] {
116
+ const results: ResourceManifest[] = [];
117
+ for (const [depth, resources] of this.depthIndex) {
118
+ if (depth > 0) {
119
+ results.push(...resources);
120
+ }
121
+ }
122
+ return results;
123
+ }
124
+
125
+ /**
126
+ * Get all directly-loaded resources (depth = 0)
127
+ */
128
+ getDirectlyLoaded(): ResourceManifest[] {
129
+ return this.depthIndex.get(0) ?? [];
130
+ }
131
+
132
+ getAll(): ResourceManifest[] {
133
+ return Array.from(this.resources.values())
134
+ .map((kindMap) => Array.from(kindMap.values()))
135
+ .flat();
136
+ }
137
+ }