@telorun/kernel 0.56.0 → 0.58.0

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 (52) hide show
  1. package/dist/controllers/module/import-controller.d.ts.map +1 -1
  2. package/dist/controllers/module/import-controller.js +7 -3
  3. package/dist/controllers/module/import-controller.js.map +1 -1
  4. package/dist/controllers/resource-definition/resource-definition-controller.d.ts +1 -0
  5. package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
  6. package/dist/controllers/resource-definition/resource-definition-controller.js +10 -1
  7. package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
  8. package/dist/evaluation-context.d.ts +57 -2
  9. package/dist/evaluation-context.d.ts.map +1 -1
  10. package/dist/evaluation-context.js +209 -8
  11. package/dist/evaluation-context.js.map +1 -1
  12. package/dist/kernel.d.ts.map +1 -1
  13. package/dist/kernel.js +6 -3
  14. package/dist/kernel.js.map +1 -1
  15. package/dist/manifest-schemas.d.ts +8 -0
  16. package/dist/manifest-schemas.d.ts.map +1 -1
  17. package/dist/manifest-schemas.js +6 -0
  18. package/dist/manifest-schemas.js.map +1 -1
  19. package/dist/module-context.d.ts +8 -0
  20. package/dist/module-context.d.ts.map +1 -1
  21. package/dist/module-context.js +20 -5
  22. package/dist/module-context.js.map +1 -1
  23. package/dist/observed-state.d.ts +94 -0
  24. package/dist/observed-state.d.ts.map +1 -0
  25. package/dist/observed-state.js +148 -0
  26. package/dist/observed-state.js.map +1 -0
  27. package/dist/resource-context.d.ts +50 -1
  28. package/dist/resource-context.d.ts.map +1 -1
  29. package/dist/resource-context.js +56 -12
  30. package/dist/resource-context.js.map +1 -1
  31. package/dist/transports/oci/oci-ref.d.ts +1 -1
  32. package/dist/transports/oci/oci-ref.d.ts.map +1 -1
  33. package/dist/transports/oci/oci-ref.js +1 -1
  34. package/dist/transports/oci/oci-ref.js.map +1 -1
  35. package/dist/transports/oci/oci-transport.d.ts.map +1 -1
  36. package/dist/transports/oci/oci-transport.js +7 -10
  37. package/dist/transports/oci/oci-transport.js.map +1 -1
  38. package/dist/transports/registry-transport.d.ts.map +1 -1
  39. package/dist/transports/registry-transport.js +3 -8
  40. package/dist/transports/registry-transport.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/controllers/module/import-controller.ts +12 -3
  43. package/src/controllers/resource-definition/resource-definition-controller.ts +14 -0
  44. package/src/evaluation-context.ts +253 -8
  45. package/src/kernel.ts +5 -0
  46. package/src/manifest-schemas.ts +6 -0
  47. package/src/module-context.ts +19 -5
  48. package/src/observed-state.ts +210 -0
  49. package/src/resource-context.ts +55 -11
  50. package/src/transports/oci/oci-ref.ts +8 -1
  51. package/src/transports/oci/oci-transport.ts +12 -9
  52. package/src/transports/registry-transport.ts +4 -6
@@ -104,6 +104,23 @@ export class ResourceContextImpl implements ResourceContext {
104
104
  stderr?: NodeJS.WritableStream,
105
105
  args?: ParsedArgs,
106
106
  ownerPrefix = "",
107
+ /**
108
+ * The context that OWNS this instance — the module context for a top-level
109
+ * resource, the per-run scope child for a `with:`-scoped one.
110
+ *
111
+ * Everything resource-scoped goes through here: registering a manifest,
112
+ * resolving a sibling by name, expanding CEL, spawning a child context,
113
+ * dispatching, publishing. `moduleContext` is reserved for what genuinely
114
+ * belongs to the MODULE rather than to this resource's context — imports
115
+ * (`registerImport` / `resolveImported*`), the controller policy, and the
116
+ * logging scope, all of which a scope child inherits rather than owns.
117
+ *
118
+ * Getting this wrong is not cosmetic: a scoped resource that registers an
119
+ * inline definition into the module lands it in a pending queue the module's
120
+ * init loop has already drained, so the resource is never created and the
121
+ * dispatch fails.
122
+ */
123
+ private readonly owningContext: IEvaluationContext = moduleContext,
107
124
  ) {
108
125
  // `ctx.env` is the sanctioned host-env channel for controllers — always the
109
126
  // real environment (kernel passes its snapshot), never the locked Proxy.
@@ -115,6 +132,22 @@ export class ResourceContextImpl implements ResourceContext {
115
132
  this.ownerPrefix = ownerPrefix;
116
133
  }
117
134
 
135
+ /**
136
+ * Where a NAME resolves from: the owning context first, then the enclosing
137
+ * module. Same order as `ScopeContext.getInstance` and the CEL `resources`
138
+ * layering — scope-local wins, outer is the fallback — so a `with:`-scoped
139
+ * resource can still dispatch a module-level one by name (an `Http.Server`
140
+ * whose `notFoundHandler` targets a module-level invocable).
141
+ *
142
+ * Registration deliberately does NOT use this: a new manifest belongs to the
143
+ * context that owns the resource creating it, never to the module.
144
+ */
145
+ private contextForName(name: string): IEvaluationContext {
146
+ return this.owningContext.resourceInstances.has(name)
147
+ ? this.owningContext
148
+ : this.moduleContext;
149
+ }
150
+
118
151
  createSchemaValidator(schema: any) {
119
152
  if (!schema) {
120
153
  return new NoopValidator();
@@ -228,7 +261,7 @@ export class ResourceContextImpl implements ResourceContext {
228
261
  // failure to the EventBus rather than letting it go unhandled. We track the
229
262
  // error-handled chain (not the raw promise) so teardown drains a task whose
230
263
  // settlement is already observed here.
231
- const tracked = this.moduleContext
264
+ const tracked = this.owningContext
232
265
  .runDetached(fn) // bare scope-detach primitive
233
266
  .catch(async (err: unknown) => {
234
267
  const detail =
@@ -266,11 +299,11 @@ export class ResourceContextImpl implements ResourceContext {
266
299
  }
267
300
 
268
301
  openSpan(base: InvokeContext | undefined, opts: OpenSpanOptions): Promise<OpenSpan> {
269
- return this.moduleContext.openSpan(base, opts);
302
+ return this.owningContext.openSpan(base, opts);
270
303
  }
271
304
 
272
305
  invoke<TInputs>(kind: string, name: string, inputs: TInputs): Promise<any> {
273
- return this.moduleContext.invoke(kind, name, inputs);
306
+ return this.contextForName(name).invoke(kind, name, inputs);
274
307
  }
275
308
 
276
309
  invokeResolved<TInputs>(
@@ -280,7 +313,7 @@ export class ResourceContextImpl implements ResourceContext {
280
313
  inputs: TInputs,
281
314
  ctx?: InvokeContext,
282
315
  ): Promise<any> {
283
- return this.moduleContext.invokeResolved(kind, name, instance, inputs, ctx);
316
+ return this.owningContext.invokeResolved(kind, name, instance, inputs, ctx);
284
317
  }
285
318
 
286
319
  resolveImportedInstance(alias: string, name: string): ResourceInstance | undefined {
@@ -297,11 +330,22 @@ export class ResourceContextImpl implements ResourceContext {
297
330
  }
298
331
 
299
332
  async run(name: string) {
300
- await this.moduleContext.run(name);
333
+ await this.contextForName(name).run(name);
334
+ }
335
+
336
+ /** Report what this resource has observed. Configured state stays on
337
+ * `snapshot()`, which the kernel pulls; only what the resource LEARNS is
338
+ * pushed, because nothing but the controller knows when it learned it. */
339
+ async setStatus(status: Record<string, unknown>): Promise<void> {
340
+ // `metadata` is the resource's `metadata` block, not the resource — every
341
+ // other member here reads `this.metadata.name`.
342
+ const name = this.metadata?.name as string | undefined;
343
+ if (!name) return;
344
+ await this.owningContext.setResourceStatus(name, status);
301
345
  }
302
346
 
303
347
  registerManifest(resource: any): void {
304
- this.moduleContext.registerManifest(resource);
348
+ this.owningContext.registerManifest(resource);
305
349
  }
306
350
 
307
351
  loadModule(url: string, options?: LoadOptions): Promise<ResourceManifest[]> {
@@ -392,7 +436,7 @@ export class ResourceContextImpl implements ResourceContext {
392
436
  // initialized yet when this resolves, and scope-local resources never enter
393
437
  // `resourceInstances`).
394
438
  const kind =
395
- (this.moduleContext.resourceInstances.get(refName)?.resource.kind as string | undefined) ??
439
+ (this.contextForName(refName).resourceInstances.get(refName)?.resource.kind as string | undefined) ??
396
440
  this.kernel.resourceKindByName(refName) ??
397
441
  "";
398
442
  return { kind, name: refName };
@@ -451,7 +495,7 @@ export class ResourceContextImpl implements ResourceContext {
451
495
  }
452
496
 
453
497
  getResourcesByName(_kind: string, name: string): RuntimeResource | null {
454
- const entry = this.moduleContext.resourceInstances.get(name);
498
+ const entry = this.contextForName(name).resourceInstances.get(name);
455
499
  return (entry?.resource ?? null) as RuntimeResource | null;
456
500
  }
457
501
 
@@ -526,7 +570,7 @@ export class ResourceContextImpl implements ResourceContext {
526
570
  }
527
571
 
528
572
  expandValue(value: any, context: Record<string, any>) {
529
- return this.moduleContext.expandWith(value, context);
573
+ return this.owningContext.expandWith(value, context);
530
574
  }
531
575
 
532
576
  async emitEvent(event: string, payload?: any) {
@@ -549,11 +593,11 @@ export class ResourceContextImpl implements ResourceContext {
549
593
  // spawnChildContext(). Rooted on this resource's module context (the
550
594
  // consumer scope); a templated definition that needs library-scoped
551
595
  // resolution calls the defining library's context directly instead.
552
- return this.moduleContext.spawnChildContext();
596
+ return this.owningContext.spawnChildContext();
553
597
  }
554
598
 
555
599
  transientChild(context: Record<string, any>): IEvaluationContext {
556
- return this.moduleContext.transientChild(context);
600
+ return this.owningContext.transientChild(context);
557
601
  }
558
602
 
559
603
  /**
@@ -1,4 +1,11 @@
1
1
  // The OCI ref grammar is pure string parsing shared with the browser-safe
2
2
  // manifest-cache key helper, so it lives in `@telorun/analyzer`; this shim
3
3
  // keeps the kernel-internal import sites stable.
4
- export { OCI_SCHEME, isOciRef, parseOciRef, type ParsedOciRef } from "@telorun/analyzer";
4
+ export {
5
+ OCI_SCHEME,
6
+ isOciRef,
7
+ parseOciRef,
8
+ parseVersionedRef,
9
+ withRefVersion,
10
+ type ParsedOciRef,
11
+ } from "@telorun/analyzer";
@@ -2,7 +2,6 @@ import {
2
2
  DEFAULT_MANIFEST_FILENAME,
3
3
  IntegrityError,
4
4
  sha256Base64Url,
5
- splitIntegrity,
6
5
  verifyIntegrity,
7
6
  type ManifestCacheCoords,
8
7
  type ManifestSource,
@@ -24,7 +23,13 @@ import {
24
23
  TELO_LAYER_MEDIA_TYPE,
25
24
  type OciManifest,
26
25
  } from "./oci-client.js";
27
- import { OCI_SCHEME, isOciRef, parseOciRef } from "./oci-ref.js";
26
+ import {
27
+ OCI_SCHEME,
28
+ isOciRef,
29
+ parseOciRef,
30
+ parseVersionedRef,
31
+ withRefVersion,
32
+ } from "./oci-ref.js";
28
33
 
29
34
  /** Pull the module blob, returning its extracted entries and the `telo.yaml`
30
35
  * bytes, verified against the ref's inline hash and its own `filesIntegrity`. */
@@ -139,16 +144,14 @@ export class OciTransport implements Transport {
139
144
  refVersion(ref: string): string | null {
140
145
  // The reference (tag or `sha256:` digest) is what `@` separates. An implicit
141
146
  // `latest` (no `@`) is not an upgradeable pin, so return null there; a digest
142
- // reference flows through raw and the caller's SemVer check skips it.
143
- const { base } = splitIntegrity(ref);
144
- if (!base.startsWith(OCI_SCHEME)) return null;
145
- const at = base.lastIndexOf("@");
146
- return at > 0 ? base.slice(at + 1) : null;
147
+ // reference flows through raw and the caller's SemVer check skips it. The
148
+ // split itself is the shared grammar — the editor reads the same pin from a
149
+ // browser, where no transport exists.
150
+ return isOciRef(ref) ? (parseVersionedRef(ref)?.version ?? null) : null;
147
151
  }
148
152
 
149
153
  withVersion(ref: string, version: string): string {
150
- const { host, repo } = parseOciRef(ref);
151
- return `${OCI_SCHEME}${host}/${repo}@${version}`;
154
+ return withRefVersion(ref, version);
152
155
  }
153
156
 
154
157
  async digest(ref: string): Promise<string | null> {
@@ -5,7 +5,9 @@ import {
5
5
  RegistrySource,
6
6
  isRegistryRef,
7
7
  parseModuleRef,
8
+ parseVersionedRef,
8
9
  sha256Base64Url,
10
+ withRefVersion,
9
11
  splitIntegrity,
10
12
  type ManifestCacheCoords,
11
13
  type ManifestSource,
@@ -211,15 +213,11 @@ export class RegistryTransport implements Transport {
211
213
  refVersion(ref: string): string | null {
212
214
  // Only bare `namespace/name@version` refs carry an upgradeable version — a
213
215
  // direct `https://` URL has no version segment to bump.
214
- if (!isRegistryRef(ref)) return null;
215
- const { base } = splitIntegrity(ref);
216
- const at = base.lastIndexOf("@");
217
- return at > 0 ? base.slice(at + 1) : null;
216
+ return isRegistryRef(ref) ? (parseVersionedRef(ref)?.version ?? null) : null;
218
217
  }
219
218
 
220
219
  withVersion(ref: string, version: string): string {
221
- const { modulePath } = parseModuleRef(ref);
222
- return `${modulePath}@${version}`;
220
+ return withRefVersion(ref, version);
223
221
  }
224
222
 
225
223
  /** Mirrors the sources' fetch-URL derivation: a direct URL points at (or