@telorun/kernel 0.75.0 → 0.76.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 (62) hide show
  1. package/dist/bundle/module-artifact.d.ts +21 -4
  2. package/dist/bundle/module-artifact.d.ts.map +1 -1
  3. package/dist/bundle/module-artifact.js +42 -17
  4. package/dist/bundle/module-artifact.js.map +1 -1
  5. package/dist/bundle/module-manifest.d.ts +5 -1
  6. package/dist/bundle/module-manifest.d.ts.map +1 -1
  7. package/dist/bundle/module-manifest.js +5 -1
  8. package/dist/bundle/module-manifest.js.map +1 -1
  9. package/dist/controller-loader.d.ts +3 -2
  10. package/dist/controller-loader.d.ts.map +1 -1
  11. package/dist/controller-loader.js +10 -9
  12. package/dist/controller-loader.js.map +1 -1
  13. package/dist/controller-loaders/bundle-loader.d.ts +51 -4
  14. package/dist/controller-loaders/bundle-loader.d.ts.map +1 -1
  15. package/dist/controller-loaders/bundle-loader.js +260 -11
  16. package/dist/controller-loaders/bundle-loader.js.map +1 -1
  17. package/dist/controller-loaders/napi-loader.d.ts.map +1 -1
  18. package/dist/controller-loaders/napi-loader.js +8 -1
  19. package/dist/controller-loaders/napi-loader.js.map +1 -1
  20. package/dist/controller-loaders/npm-loader.d.ts.map +1 -1
  21. package/dist/controller-loaders/npm-loader.js +65 -3
  22. package/dist/controller-loaders/npm-loader.js.map +1 -1
  23. package/dist/controller-loaders/sibling-libraries.d.ts +93 -0
  24. package/dist/controller-loaders/sibling-libraries.d.ts.map +1 -0
  25. package/dist/controller-loaders/sibling-libraries.js +111 -0
  26. package/dist/controller-loaders/sibling-libraries.js.map +1 -0
  27. package/dist/controller-loaders/source-bundle-builder.d.ts +25 -2
  28. package/dist/controller-loaders/source-bundle-builder.d.ts.map +1 -1
  29. package/dist/controller-loaders/source-bundle-builder.js +232 -26
  30. package/dist/controller-loaders/source-bundle-builder.js.map +1 -1
  31. package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
  32. package/dist/controllers/resource-definition/resource-definition-controller.js +5 -1
  33. package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/kernel.d.ts +11 -1
  38. package/dist/kernel.d.ts.map +1 -1
  39. package/dist/kernel.js +37 -4
  40. package/dist/kernel.js.map +1 -1
  41. package/dist/manifest-sources/local-manifest-cache-source.d.ts +13 -2
  42. package/dist/manifest-sources/local-manifest-cache-source.d.ts.map +1 -1
  43. package/dist/manifest-sources/local-manifest-cache-source.js +19 -3
  44. package/dist/manifest-sources/local-manifest-cache-source.js.map +1 -1
  45. package/dist/resource-context.d.ts +5 -0
  46. package/dist/resource-context.d.ts.map +1 -1
  47. package/dist/resource-context.js +6 -0
  48. package/dist/resource-context.js.map +1 -1
  49. package/package.json +2 -2
  50. package/src/bundle/module-artifact.ts +50 -20
  51. package/src/bundle/module-manifest.ts +14 -1
  52. package/src/controller-loader.ts +25 -7
  53. package/src/controller-loaders/bundle-loader.ts +326 -10
  54. package/src/controller-loaders/napi-loader.ts +8 -1
  55. package/src/controller-loaders/npm-loader.ts +73 -3
  56. package/src/controller-loaders/sibling-libraries.ts +171 -0
  57. package/src/controller-loaders/source-bundle-builder.ts +281 -24
  58. package/src/controllers/resource-definition/resource-definition-controller.ts +7 -1
  59. package/src/index.ts +1 -0
  60. package/src/kernel.ts +50 -11
  61. package/src/manifest-sources/local-manifest-cache-source.ts +20 -3
  62. package/src/resource-context.ts +8 -0
@@ -15,6 +15,7 @@ import {
15
15
  type DefResolver,
16
16
  } from "@telorun/analyzer";
17
17
  import type { ModuleArtifact } from "../../bundle/module-artifact.js";
18
+ import type { SiblingLibraryMap } from "../../controller-loaders/sibling-libraries.js";
18
19
  import { ControllerLoader } from "../../controller-loader.js";
19
20
  import { formatAjvErrors, validateResourceDefinition } from "../../manifest-schemas.js";
20
21
  import { createTemplateController } from "./resource-template-controller.js";
@@ -166,6 +167,10 @@ class ResourceDefinition implements ResourceInstance {
166
167
  // ref and the verified layer index, so the loader picks a candidate and asks
167
168
  // it for that selector's directory rather than fetching anything itself.
168
169
  const artifact = host.getModuleArtifact?.(this.resource.metadata.source);
170
+ // The libraries of the module that declared this kind, for the same reason:
171
+ // a bundle's bare `@telorun/kv-store` means whatever THAT module's imports
172
+ // say it means, never the consumer's.
173
+ const libraries = host.getSiblingLibraries?.(this.resource.metadata.source);
169
174
  ctx.registerDefinition(this.resource);
170
175
 
171
176
  const moduleName = this.resource.metadata.module;
@@ -187,7 +192,7 @@ class ResourceDefinition implements ResourceInstance {
187
192
  kindName,
188
193
  async () => {
189
194
  const resolved = await loader
190
- .resolve(controllers, source, policy, artifact)
195
+ .resolve(controllers, source, policy, artifact, libraries)
191
196
  .catch((err) => {
192
197
  if (err instanceof RuntimeError) {
193
198
  throw new RuntimeError(err.code, `kind '${moduleName}.${kindName}': ${err.message}`);
@@ -229,6 +234,7 @@ class ResourceDefinition implements ResourceInstance {
229
234
  */
230
235
  interface KernelResourceContext {
231
236
  getModuleArtifact?(source: string | undefined): ModuleArtifact | undefined;
237
+ getSiblingLibraries?(source: string | undefined): SiblingLibraryMap | undefined;
232
238
  getCacheRoot?(): string | undefined;
233
239
  registerLazyController(
234
240
  moduleName: string,
package/src/index.ts CHANGED
@@ -39,6 +39,7 @@ export {
39
39
  buildControllerBundle,
40
40
  canBuildFromSource,
41
41
  lastBuildInputs,
42
+ type SiblingLibrary,
42
43
  } from "./controller-loaders/source-bundle-builder.js";
43
44
  export type {
44
45
  PayloadLayer,
package/src/kernel.ts CHANGED
@@ -64,12 +64,17 @@ import {
64
64
  resolveCacheRoot,
65
65
  resolveEntryDir,
66
66
  } from "./manifest-sources/local-manifest-cache-source.js";
67
- import { readOwnerManifest } from "./bundle/module-manifest.js";
67
+ import { readOwnerManifest, type OwnerManifest } from "./bundle/module-manifest.js";
68
68
  import {
69
69
  moduleArtifactFor,
70
70
  moduleDirectoryFor,
71
71
  type ModuleArtifact,
72
72
  } from "./bundle/module-artifact.js";
73
+ import {
74
+ NO_SIBLING_LIBRARIES,
75
+ buildSiblingLibraries,
76
+ type SiblingLibraryMap,
77
+ } from "./controller-loaders/sibling-libraries.js";
73
78
  import { defaultTransportRegistry } from "./transports/transport-registry.js";
74
79
  import {
75
80
  collectDeclaredEnvKeys,
@@ -159,6 +164,10 @@ export class Kernel implements IKernel {
159
164
  /** Per-module artifact handles, keyed by canonical manifest source. Rebuilt on
160
165
  * every `load()`; empty for a graph of purely local / manifest-only modules. */
161
166
  private readonly moduleArtifacts = new Map<string, ModuleArtifact>();
167
+ /** Per-module sibling-library resolution, keyed by the same canonical manifest
168
+ * source: which bare specifiers this module's controller bundles import, and
169
+ * which module's library layer each resolves to. Rebuilt on every `load()`. */
170
+ private readonly siblingLibraries = new Map<string, SiblingLibraryMap>();
162
171
  private _loadedGraph?: LoadedGraph;
163
172
  // Lifecycle state — guards boot/runTargets/teardown/invoke transitions.
164
173
  // teardown() is the only idempotent method; everything else throws on misuse.
@@ -1014,7 +1023,9 @@ export class Kernel implements IKernel {
1014
1023
  }
1015
1024
 
1016
1025
  /**
1017
- * Build one {@link ModuleArtifact} per loaded module that ships a payload.
1026
+ * Read every loaded module's owner manifest once, and build from it the two
1027
+ * things a controller resolution needs: the module's artifact, and the sibling
1028
+ * libraries its bundles import by bare specifier.
1018
1029
  *
1019
1030
  * Here, and not inside a controller loader, because this is the only point
1020
1031
  * where both halves are in hand: the **pinned** ref the importer wrote
@@ -1038,26 +1049,48 @@ export class Kernel implements IKernel {
1038
1049
  */
1039
1050
  private buildModuleArtifacts(graph: LoadedGraph, manifestsDir: string | undefined): void {
1040
1051
  this.moduleArtifacts.clear();
1052
+ this.siblingLibraries.clear();
1041
1053
  const transports = defaultTransportRegistry(this.registryUrl);
1042
1054
  const entryDir = this._entryUrl ? resolveEntryDir(this._entryUrl) ?? "" : "";
1055
+ // Parsed once per module and shared with the sibling-library join below: a
1056
+ // `telo.yaml` is a large multi-document file, and re-reading each target's
1057
+ // once per import edge put dozens of redundant full-YAML parses on the boot
1058
+ // path of every standard-library app.
1059
+ const owners = new Map<string, OwnerManifest>();
1060
+ const directories = new Map<string, string | undefined>();
1043
1061
  for (const [, module] of graph.modules) {
1044
1062
  const file = module.owner;
1045
- if (this.moduleArtifacts.has(file.source)) continue;
1063
+ if (owners.has(file.source)) continue;
1064
+ const owner = readOwnerManifest(file.text);
1065
+ owners.set(file.source, owner);
1066
+ const moduleDir = moduleDirectoryFor(
1067
+ file.requestedUrl,
1068
+ file.source,
1069
+ entryDir,
1070
+ this.registryUrl,
1071
+ manifestsDir,
1072
+ );
1073
+ directories.set(file.source, moduleDir ?? undefined);
1046
1074
  const artifact = moduleArtifactFor({
1047
1075
  pinnedRef: file.requestedUrl,
1048
- layers: readOwnerManifest(file.text).layers,
1049
- moduleDir: moduleDirectoryFor(
1050
- file.requestedUrl,
1051
- file.source,
1052
- entryDir,
1053
- this.registryUrl,
1054
- manifestsDir,
1055
- ),
1076
+ layers: owner.layers,
1077
+ moduleDir,
1056
1078
  transports,
1057
1079
  log: this.logging.kernelLogger(),
1058
1080
  });
1059
1081
  if (artifact) this.moduleArtifacts.set(file.source, artifact);
1060
1082
  }
1083
+
1084
+ // The sibling-library join is a pure function of the graph and these three
1085
+ // lookups; it lives beside the model it produces (`sibling-libraries.ts`).
1086
+ for (const [source, map] of buildSiblingLibraries(graph, {
1087
+ ownerManifests: owners,
1088
+ artifactFor: (source) => this.moduleArtifacts.get(source),
1089
+ directoryFor: (source) => directories.get(source),
1090
+ log: this.logging.kernelLogger(),
1091
+ })) {
1092
+ this.siblingLibraries.set(source, map);
1093
+ }
1061
1094
  }
1062
1095
 
1063
1096
  /** The artifact of the module whose manifest resolved from `source`, or
@@ -1067,6 +1100,12 @@ export class Kernel implements IKernel {
1067
1100
  return source ? this.moduleArtifacts.get(source) : undefined;
1068
1101
  }
1069
1102
 
1103
+ /** The module-owned libraries the module at `source` imports by bare
1104
+ * specifier. Empty for a module that imports none. */
1105
+ getSiblingLibraries(source: string | undefined): SiblingLibraryMap {
1106
+ return (source ? this.siblingLibraries.get(source) : undefined) ?? NO_SIBLING_LIBRARIES;
1107
+ }
1108
+
1070
1109
  /** Authored `kind` of a declared resource by name, from the static manifest
1071
1110
  * set. Init-order-independent (unlike `resourceInstances`), so a controller
1072
1111
  * resolving a `!ref <name>` sentinel before the target initializes can still
@@ -179,13 +179,29 @@ export async function writeManifestCache(
179
179
  return written;
180
180
  }
181
181
 
182
+ /** Matches a URL scheme, as distinct from a Windows drive letter. `D:\src`
183
+ * satisfies RFC 3986's scheme grammar, so the second character is required:
184
+ * no registered scheme is one letter, and every drive is. */
185
+ const URL_SCHEME = /^[a-zA-Z][a-zA-Z0-9+.-]+:/;
186
+
182
187
  /** Resolve the entry-anchor directory for the manifest cache.
183
188
  *
184
189
  * For a file path or `file://` URL: returns the containing directory.
185
190
  * For a directory path: returns the directory itself.
186
- * For an HTTP(S) URL: returns `null` (no local anchor; cache writes skipped). */
191
+ * For ANY other scheme: returns `null` (no local anchor; cache writes skipped).
192
+ *
193
+ * The rule is the scheme, not a list of known ones. Testing only for http(s)
194
+ * left every other scheme falling through to `path.resolve`, which reads it as
195
+ * a relative path: a `memory://app/telo.yaml` entry anchored its cache at
196
+ * `<cwd>/memory:/app/.telo`. On POSIX that is a legal directory name, so the
197
+ * kernel silently created one inside whatever directory it was run from and the
198
+ * cache appeared to work; on Windows `:` is illegal in a filename, so the same
199
+ * load failed at `mkdir`. A transient source has no local anchor by
200
+ * construction — that is what the whole-scheme test says, and it now covers
201
+ * `oci://` and any scheme added later without another edit here. */
187
202
  export function resolveEntryDir(entryPath: string): string | null {
188
- if (entryPath.startsWith("http://") || entryPath.startsWith("https://")) {
203
+ const scheme = URL_SCHEME.exec(entryPath)?.[0];
204
+ if (scheme !== undefined && scheme !== "file:") {
189
205
  return null;
190
206
  }
191
207
  let absolute: string;
@@ -208,7 +224,8 @@ export function resolveEntryDir(entryPath: string): string | null {
208
224
  *
209
225
  * `TELO_CACHE_DIR` (the relocated root a prebuilt image bakes its deps into)
210
226
  * wins; otherwise the root sits beside the entry at `<entry-dir>/.telo`.
211
- * Returns `null` for http(s) entries with no local anchor (disk cache skipped).
227
+ * Returns `null` for an entry with no local anchor an http(s), `memory://`
228
+ * or any other non-`file:` scheme — in which case the disk cache is skipped.
212
229
  * Consumers append the conventional subdirs: `manifests/`, `manifests/__validators/`,
213
230
  * `npm/`. */
214
231
  export function resolveCacheRoot(entryPath: string): string | null {
@@ -35,6 +35,7 @@ import * as path from "path";
35
35
  import { pathToFileURL } from "url";
36
36
  import { withBigIntsAsNumbers } from "./bigint-schema-view.js";
37
37
  import type { ModuleArtifact } from "./bundle/module-artifact.js";
38
+ import type { SiblingLibraryMap } from "./controller-loaders/sibling-libraries.js";
38
39
  import { resolveModuleFileUri } from "./module-file-resolution.js";
39
40
  import { hostEnv } from "./host-env.js";
40
41
  import type { LoggingHost } from "./logging/logging-host.js";
@@ -781,6 +782,13 @@ export class ResourceContextImpl implements ResourceContext {
781
782
  return this.kernel.getModuleArtifact(source);
782
783
  }
783
784
 
785
+ /** The module-owned libraries the module at `source` imports by bare specifier
786
+ * — how a controller bundle's `@telorun/kv-store` finds the module that owns
787
+ * it. Kernel-only, on the same seam as {@link getModuleArtifact}. */
788
+ getSiblingLibraries(source: string | undefined): SiblingLibraryMap {
789
+ return this.kernel.getSiblingLibraries(source);
790
+ }
791
+
784
792
  /**
785
793
  * Resolve a module-relative reference against the declaring module's own
786
794
  * directory, materializing the layers that could carry it on first use.