@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
@@ -0,0 +1,143 @@
1
+ import { DiagnosticSeverity, Loader, StaticAnalyzer } from "@telorun/analyzer";
2
+ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
3
+ import { EvaluationContext, RuntimeError } from "@telorun/sdk";
4
+ import { LocalFileAdapter } from "../../manifest-adapters/local-file-adapter.js";
5
+
6
+ export async function create(resource: any, ctx: ResourceContext): Promise<ResourceInstance> {
7
+ const alias = resource.metadata.name as string;
8
+ const loader = new Loader([new LocalFileAdapter()]);
9
+
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.
28
+ // No env — child modules are isolated from host environment.
29
+ const manifests = await loader.loadModule(
30
+ new URL(moduleSource, ctx.moduleContext.source).toString(),
31
+ {
32
+ compile: true,
33
+ },
34
+ );
35
+ // Find the kind: Module manifest to learn the target module name and contract.
36
+ const moduleManifest = manifests.find((m: any) => m.kind === "Kernel.Module");
37
+ if (!moduleManifest) {
38
+ throw new Error(`No kind: Module manifest found in source "${resource.source as string}"`);
39
+ }
40
+ const targetModule: string = moduleManifest.metadata.name;
41
+
42
+ // Validate required inputs before injecting.
43
+ validateRequiredInputs(moduleManifest.variables ?? {}, resource.variables ?? {}, "variables");
44
+ validateRequiredInputs(moduleManifest.secrets ?? {}, resource.secrets ?? {}, "secrets");
45
+
46
+ // Create child context with the imported variables/secrets baked in, so that
47
+ // ${{ variables.x }} / ${{ secrets.y }} templates resolve correctly at runtime.
48
+ const child = ctx.moduleContext.spawnChild(
49
+ new EvaluationContext(
50
+ ctx.moduleContext.source,
51
+ {
52
+ variables: (resource.variables as Record<string, unknown>) ?? {},
53
+ secrets: (resource.secrets as Record<string, unknown>) ?? {},
54
+ resources: {},
55
+ },
56
+ ctx.moduleContext.createInstance,
57
+ ctx.moduleContext.secretValues,
58
+ ctx.moduleContext.emit,
59
+ ),
60
+ );
61
+
62
+ for (const manifest of manifests) {
63
+ child.registerManifest(manifest);
64
+ }
65
+
66
+ // Link the target module context as a child of the declaring module context in
67
+ // the lifecycle tree. This enables cascading teardown (parent → child order)
68
+ // and makes the import hierarchy visible at runtime.
69
+ // const declaringCtx: ModuleContext = ctx.getModuleContext(declaringModule);
70
+ // const targetCtx: ModuleContext = (ctx as any).getModuleContext(targetModule);
71
+ // if (!targetCtx.parent) {
72
+ // declaringCtx.spawnChild(targetCtx);
73
+ // }
74
+
75
+ // Try to evaluate the target module's exports.
76
+ // Throws if resources.X is not yet populated — the kernel retry loop catches this and retries.
77
+ // const evaluatedExports: any = child.expand(moduleManifest.exports ?? {});
78
+
79
+ const exportedKinds: string[] = moduleManifest.exports?.kinds ?? [];
80
+ ctx.registerModuleImport(alias, targetModule, exportedKinds);
81
+ // Return a ResourceInstance whose snapshot() surfaces the exported values.
82
+ // The kernel's generic setResource() call stores them under resources.<alias>
83
+ // in the declaring module's evaluation context — no separate imports namespace needed.
84
+ return {
85
+ snapshot: () => ({
86
+ variables: ctx.expandValue(resource.variables, {}) ?? {},
87
+ secrets: ctx.expandValue(resource.secrets, {}) ?? {},
88
+ }),
89
+ run: async () => {
90
+ // Proxy run to target module
91
+ for (const target of (moduleManifest.targets as string[]) ?? []) {
92
+ await child.run(target);
93
+ }
94
+ },
95
+ invoke: async () => {
96
+ // Proxy run to target module
97
+ // for (const target of (moduleManifest.targets as string[]) ?? []) {
98
+ // child.invoke(target);
99
+ // }
100
+ console.log("invoking");
101
+ },
102
+ init: async () => {
103
+ await child.initializeResources();
104
+ },
105
+ teardown: async () => {
106
+ await child.teardownResources();
107
+ },
108
+ };
109
+ }
110
+
111
+ function validateRequiredInputs(
112
+ schemaDefs: Record<string, any>,
113
+ provided: Record<string, unknown>,
114
+ kind: "variables" | "secrets",
115
+ ): void {
116
+ for (const [key, def] of Object.entries(schemaDefs)) {
117
+ const isRequired = typeof def === "object" && def !== null && !("default" in def);
118
+ if (isRequired && !(key in provided)) {
119
+ throw new Error(`Required ${kind} input "${key}" not provided for module import`);
120
+ }
121
+ }
122
+ }
123
+
124
+ export const schema = {
125
+ type: "object",
126
+ properties: {
127
+ kind: { type: "string" },
128
+ metadata: {
129
+ type: "object",
130
+ properties: {
131
+ name: { type: "string" },
132
+ module: { type: "string" },
133
+ },
134
+ required: ["name"],
135
+ additionalProperties: true,
136
+ },
137
+ source: { type: "string" },
138
+ variables: { type: "object" },
139
+ secrets: { type: "object" },
140
+ },
141
+ required: ["metadata", "source"],
142
+ additionalProperties: false,
143
+ };
@@ -0,0 +1,16 @@
1
+ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
2
+
3
+ export async function create(resource: any, ctx: ResourceContext): Promise<ResourceInstance> {
4
+ return {
5
+ run: async () => {
6
+ for (const target of (resource.targets as string[]) ?? []) {
7
+ const [kind, name] = target.split(".");
8
+ if (!kind || !name) {
9
+ throw new Error(`Invalid target format: "${target}". Expected "Kind.Name"`);
10
+ }
11
+ await ctx.invoke(kind, name, {});
12
+ }
13
+ },
14
+ };
15
+ }
16
+
@@ -0,0 +1,86 @@
1
+ import type {
2
+ ControllerContext,
3
+ ResourceContext,
4
+ ResourceInstance,
5
+ RuntimeResource,
6
+ } from "@telorun/sdk";
7
+ import { ControllerLoader } from "../../controller-loader.js";
8
+ import { formatAjvErrors, validateResourceDefinition } from "../../manifest-schemas.js";
9
+ import { createTemplateController } from "./resource-template-controller.js";
10
+
11
+ type ResourceDefinitionResource = RuntimeResource & {
12
+ kind: "Kernel.Definition";
13
+ metadata: {
14
+ [key: string]: any;
15
+ name: string;
16
+ module?: string;
17
+ };
18
+ schema: Record<string, any>;
19
+ capability?: string;
20
+ controllers?: Array<string>;
21
+ };
22
+
23
+ /**
24
+ * ResourceDefinition resource - acts as metadata holder for resource type definitions
25
+ * Validates incoming definitions against schema and maintains definition metadata
26
+ */
27
+ class ResourceDefinition implements ResourceInstance {
28
+ readonly kind: "ResourceDefinition" = "ResourceDefinition";
29
+
30
+ constructor(
31
+ readonly resource: ResourceDefinitionResource,
32
+ private controllerLoader: ControllerLoader,
33
+ ) {}
34
+
35
+ async init(ctx: ResourceContext) {
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;
45
+ }
46
+ ctx.emit("ControllerLoading", { controllers: this.resource.controllers });
47
+ try {
48
+ const controllerInstance = await this.controllerLoader.load(
49
+ this.resource.controllers,
50
+ this.resource.metadata.source,
51
+ );
52
+ ctx.emit("ControllerLoaded", { schema: controllerInstance.schema });
53
+ ctx.registerDefinition(this.resource);
54
+ await ctx.registerController(
55
+ this.resource.metadata.module,
56
+ this.resource.metadata.name,
57
+ controllerInstance,
58
+ );
59
+ } catch (err) {
60
+ ctx.emit("ControllerLoadFailed", { error: (err as Error).message });
61
+ throw err;
62
+ }
63
+ }
64
+ }
65
+
66
+ export function register(ctx: ControllerContext): void {
67
+ // ResourceDefinition is a passive resource - no registration needed
68
+ }
69
+
70
+ export async function create(resource: any, ctx: ResourceContext): Promise<ResourceDefinition> {
71
+ // Validate incoming resource definition against schema
72
+ if (!validateResourceDefinition(resource)) {
73
+ throw new Error(
74
+ `Invalid ResourceDefinition "${resource.metadata.name}": ${formatAjvErrors(validateResourceDefinition.errors)}`,
75
+ );
76
+ }
77
+
78
+ // Return a fully-formed ResourceDefinition instance
79
+ const definition = resource as unknown as ResourceDefinitionResource;
80
+ return new ResourceDefinition(definition, new ControllerLoader());
81
+ }
82
+
83
+ export const schema = {
84
+ type: "object",
85
+ additionalProperties: true,
86
+ };
@@ -0,0 +1,18 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "metadata": {
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string"
9
+ }
10
+ },
11
+ "required": ["name"]
12
+ },
13
+ "schema": {
14
+ "type": "object"
15
+ }
16
+ },
17
+ "required": ["metadata", "schema"]
18
+ }
@@ -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
+ }
@@ -0,0 +1,121 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+
4
+ /**
5
+ * Streaming event log for debugging and testing
6
+ * Records all events in JSONL format (one JSON object per line)
7
+ * Useful for observing runtime execution, testing, and debugging
8
+ */
9
+ export class EventStream {
10
+ private filePath: string;
11
+ private isEnabled: boolean = false;
12
+
13
+ constructor(filePath?: string) {
14
+ this.filePath = filePath || '';
15
+ this.isEnabled = !!filePath;
16
+ }
17
+
18
+ /**
19
+ * Enable event streaming to a file
20
+ */
21
+ async enable(filePath: string): Promise<void> {
22
+ this.filePath = filePath;
23
+ this.isEnabled = true;
24
+
25
+ // Ensure directory exists
26
+ const dir = path.dirname(filePath);
27
+ if (dir !== '.' && dir !== '') {
28
+ await fs.mkdir(dir, { recursive: true });
29
+ }
30
+
31
+ // Initialize file (truncate if exists)
32
+ await fs.writeFile(filePath, '', 'utf-8');
33
+ }
34
+
35
+ /**
36
+ * Log an event to the stream
37
+ */
38
+ async log(
39
+ event: string,
40
+ payload?: any,
41
+ metadata?: {
42
+ namespace?: string;
43
+ resource?: string;
44
+ kind?: string;
45
+ name?: string;
46
+ },
47
+ ): Promise<void> {
48
+ if (!this.isEnabled) {
49
+ return;
50
+ }
51
+
52
+ const logEntry = {
53
+ timestamp: new Date().toISOString(),
54
+ event,
55
+ payload,
56
+ ...metadata,
57
+ };
58
+
59
+ try {
60
+ const line = JSON.stringify(logEntry) + '\n';
61
+ await fs.appendFile(this.filePath, line, 'utf-8');
62
+ } catch (error) {
63
+ console.error('Failed to write event to stream:', error);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Read all events from the stream as an array
69
+ * Useful for testing
70
+ */
71
+ async readAll(): Promise<Array<Record<string, any>>> {
72
+ if (!this.isEnabled || !this.filePath) {
73
+ return [];
74
+ }
75
+
76
+ try {
77
+ const content = await fs.readFile(this.filePath, 'utf-8');
78
+ if (!content.trim()) {
79
+ return [];
80
+ }
81
+ return content
82
+ .trim()
83
+ .split('\n')
84
+ .map((line) => JSON.parse(line));
85
+ } catch (error) {
86
+ console.error('Failed to read event stream:', error);
87
+ return [];
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Filter events by type
93
+ */
94
+ async getEventsByType(
95
+ eventType: string,
96
+ ): Promise<Array<Record<string, any>>> {
97
+ const all = await this.readAll();
98
+ return all.filter((e) => e.event === eventType);
99
+ }
100
+
101
+ /**
102
+ * Get the file path for the event stream
103
+ */
104
+ getFilePath(): string {
105
+ return this.filePath;
106
+ }
107
+
108
+ /**
109
+ * Check if event streaming is enabled
110
+ */
111
+ isEnabledStream(): boolean {
112
+ return this.isEnabled;
113
+ }
114
+
115
+ /**
116
+ * Disable event streaming
117
+ */
118
+ disable(): void {
119
+ this.isEnabled = false;
120
+ }
121
+ }
package/src/events.ts ADDED
@@ -0,0 +1,99 @@
1
+ import { RuntimeEvent } from "@telorun/sdk";
2
+
3
+ type EventHandler = (payload?: any) => void | Promise<void>;
4
+
5
+ export class EventBus {
6
+ private handlers: Map<string, Set<EventHandler>> = new Map();
7
+
8
+ private validateEventName(event: string): void {
9
+ if (
10
+ event === "" ||
11
+ !/^(\*|[A-Za-z_][A-Za-z0-9_]*)(\.(\*|[A-Za-z_][A-Za-z0-9_]*))*$/.test(event)
12
+ ) {
13
+ throw new Error(`Invalid event name "${event}". Expected format "Text.Text" with no spaces.`);
14
+ }
15
+ }
16
+
17
+ private matchesPattern(pattern: string, event: string): boolean {
18
+ if (pattern === "*") {
19
+ return true;
20
+ }
21
+ if (pattern === event) {
22
+ return true;
23
+ }
24
+ if (!pattern.includes("*")) {
25
+ return false;
26
+ }
27
+ const patternParts = pattern.split(".");
28
+ const eventParts = event.split(".");
29
+ if (patternParts.length !== eventParts.length) {
30
+ return false;
31
+ }
32
+ for (let i = 0; i < patternParts.length; i += 1) {
33
+ const part = patternParts[i];
34
+ if (part === "*") {
35
+ continue;
36
+ }
37
+ if (part !== eventParts[i]) {
38
+ return false;
39
+ }
40
+ }
41
+ return true;
42
+ }
43
+
44
+ on(event: string, handler: EventHandler): void {
45
+ this.validateEventName(event);
46
+ const set = this.handlers.get(event) || new Set();
47
+ set.add(handler);
48
+ this.handlers.set(event, set);
49
+ }
50
+
51
+ once(event: string, handler: EventHandler): void {
52
+ this.validateEventName(event);
53
+ const wrapper: EventHandler = async (payload?: any) => {
54
+ this.off(event, wrapper);
55
+ await handler(payload);
56
+ };
57
+ this.on(event, wrapper);
58
+ }
59
+
60
+ off(event: string, handler: EventHandler): void {
61
+ this.validateEventName(event);
62
+ const set = this.handlers.get(event);
63
+ if (!set) {
64
+ return;
65
+ }
66
+ set.delete(handler);
67
+ if (set.size === 0) {
68
+ this.handlers.delete(event);
69
+ }
70
+ }
71
+
72
+ async emit(event: string, payload?: any, metadata?: any): Promise<void> {
73
+ const handlers: EventHandler[] = [];
74
+ for (const [pattern, set] of this.handlers.entries()) {
75
+ if (!this.matchesPattern(pattern, event)) {
76
+ continue;
77
+ }
78
+ for (const handler of set) {
79
+ handlers.push(handler);
80
+ }
81
+ }
82
+ if (handlers.length === 0) {
83
+ return;
84
+ }
85
+ const evt: RuntimeEvent = { name: event, payload, metadata };
86
+ for (const handler of handlers) {
87
+ await handler(evt);
88
+ }
89
+ }
90
+
91
+ hasHandlers(event: string): boolean {
92
+ for (const [pattern, set] of this.handlers.entries()) {
93
+ if (set.size > 0 && this.matchesPattern(pattern, event)) {
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ }
99
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { ControllerRegistry } from "./controller-registry.js";
2
+ export { EventStream } from "./event-stream.js";
3
+ export { Kernel } from "./kernel.js";
4
+ export { ManifestRegistry as Registry } from "./registry.js";
5
+ export { ResourceURI } from "./resource-uri.js";
6
+ export type { RuntimeDiagnostic } from "@telorun/sdk";
7
+