@telorun/kernel 0.28.0 → 0.29.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.
- package/dist/controller-loader.d.ts +27 -0
- package/dist/controller-loader.d.ts.map +1 -1
- package/dist/controller-loader.js +45 -3
- package/dist/controller-loader.js.map +1 -1
- package/dist/controller-loaders/bundle-loader.d.ts +10 -0
- package/dist/controller-loaders/bundle-loader.d.ts.map +1 -1
- package/dist/controller-loaders/bundle-loader.js +29 -14
- package/dist/controller-loaders/bundle-loader.js.map +1 -1
- package/dist/controller-loaders/napi-loader.d.ts +11 -0
- package/dist/controller-loaders/napi-loader.d.ts.map +1 -1
- package/dist/controller-loaders/napi-loader.js +49 -30
- package/dist/controller-loaders/napi-loader.js.map +1 -1
- package/dist/controller-loaders/npm-loader.d.ts +10 -0
- package/dist/controller-loaders/npm-loader.d.ts.map +1 -1
- package/dist/controller-loaders/npm-loader.js +15 -2
- package/dist/controller-loaders/npm-loader.js.map +1 -1
- package/dist/controller-registry.d.ts +17 -10
- package/dist/controller-registry.d.ts.map +1 -1
- package/dist/controller-registry.js +44 -10
- package/dist/controller-registry.js.map +1 -1
- package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
- package/dist/controllers/resource-definition/resource-definition-controller.js +27 -8
- package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
- package/dist/kernel.d.ts +7 -0
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +19 -1
- package/dist/kernel.js.map +1 -1
- package/dist/resource-context.d.ts +7 -0
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-context.js +10 -0
- package/dist/resource-context.js.map +1 -1
- package/package.json +1 -1
- package/src/controller-loader.ts +72 -3
- package/src/controller-loaders/bundle-loader.ts +39 -20
- package/src/controller-loaders/napi-loader.ts +51 -30
- package/src/controller-loaders/npm-loader.ts +19 -2
- package/src/controller-registry.ts +63 -0
- package/src/controllers/resource-definition/resource-definition-controller.ts +44 -11
- package/src/kernel.ts +25 -1
- package/src/resource-context.ts +15 -0
|
@@ -141,6 +141,20 @@ export class BundleControllerLoader {
|
|
|
141
141
|
purl: string,
|
|
142
142
|
baseUri: string,
|
|
143
143
|
): Promise<{ instance: ControllerInstance; source: ControllerResolveSource }> {
|
|
144
|
+
const { source, importInstance } = await this.resolve(purl, baseUri);
|
|
145
|
+
return { instance: await importInstance(), source };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Resolve without importing: parse + validate the PURL, confirm the bundle
|
|
150
|
+
* file exists and its format is hostable, and ensure the realm symlinks — all
|
|
151
|
+
* cheap, fail-fast checks — but defer the bundle `import()` (the eval cost)
|
|
152
|
+
* into the returned `importInstance` thunk. Used by lazy controller loading.
|
|
153
|
+
*/
|
|
154
|
+
async resolve(
|
|
155
|
+
purl: string,
|
|
156
|
+
baseUri: string,
|
|
157
|
+
): Promise<{ source: ControllerResolveSource; importInstance: () => Promise<ControllerInstance> }> {
|
|
144
158
|
let parsed: PackageURL;
|
|
145
159
|
try {
|
|
146
160
|
parsed = PackageURL.fromString(purl);
|
|
@@ -196,26 +210,31 @@ export class BundleControllerLoader {
|
|
|
196
210
|
// importing the bundle, so authors write normal imports.
|
|
197
211
|
await ensureRealmSymlinks(path.dirname(absFile));
|
|
198
212
|
|
|
199
|
-
// A broken bundle (syntax / failed import) is a real user-code failure —
|
|
200
|
-
// let it propagate rather than masking it as env-missing.
|
|
201
|
-
const mod = (await import(pathToFileURL(absFile).href)) as Record<string, ControllerInstance>;
|
|
202
213
|
const fragment = parsed.subpath;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
return {
|
|
215
|
+
source: "bundle",
|
|
216
|
+
importInstance: async () => {
|
|
217
|
+
// A broken bundle (syntax / failed import) is a real user-code failure —
|
|
218
|
+
// let it propagate rather than masking it as env-missing.
|
|
219
|
+
const mod = (await import(pathToFileURL(absFile).href)) as Record<string, ControllerInstance>;
|
|
220
|
+
// Distinguish "no such export" from "export isn't a controller" so the
|
|
221
|
+
// error points at the actual problem (mirrors the napi loader's project()).
|
|
222
|
+
if (fragment && !(fragment in mod)) {
|
|
223
|
+
throw new RuntimeError(
|
|
224
|
+
"ERR_CONTROLLER_INVALID",
|
|
225
|
+
`Bundled controller "${purl}": module "${absFile}" has no export named "${fragment}"`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const instance = fragment ? mod[fragment] : (mod as unknown as ControllerInstance);
|
|
229
|
+
if (!instance || (!instance.create && !instance.register)) {
|
|
230
|
+
throw new RuntimeError(
|
|
231
|
+
"ERR_CONTROLLER_INVALID",
|
|
232
|
+
`Bundled controller "${purl}" exports neither create() nor register()` +
|
|
233
|
+
(fragment ? ` at fragment "#${fragment}"` : ""),
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return instance;
|
|
237
|
+
},
|
|
238
|
+
};
|
|
220
239
|
}
|
|
221
240
|
}
|
|
@@ -123,6 +123,21 @@ export class NapiControllerLoader {
|
|
|
123
123
|
* though all exports come from one linked dylib.
|
|
124
124
|
*/
|
|
125
125
|
async load(purl: string, baseUri: string): Promise<NapiLoadResult> {
|
|
126
|
+
const { source, importInstance } = await this.resolve(purl, baseUri);
|
|
127
|
+
return { instance: await importInstance(), source };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Resolve without building: validate the PURL and confirm the crate's
|
|
132
|
+
* `local_path` exists (the fail-fast checks), then defer the cargo build +
|
|
133
|
+
* dylib load — the expensive part — into the returned `importInstance` thunk.
|
|
134
|
+
* A cache hit resolves instantly. Used by lazy controller loading so the build
|
|
135
|
+
* is paid on the kind's first instantiation, not at boot.
|
|
136
|
+
*/
|
|
137
|
+
async resolve(
|
|
138
|
+
purl: string,
|
|
139
|
+
baseUri: string,
|
|
140
|
+
): Promise<{ source: NapiResolveSource; importInstance: () => Promise<ControllerInstance> }> {
|
|
126
141
|
const [, , name, , qualifiers, entry] = PackageURL.parseString(purl);
|
|
127
142
|
const localPath = (qualifiers as any)?.get("local_path");
|
|
128
143
|
|
|
@@ -149,38 +164,44 @@ export class NapiControllerLoader {
|
|
|
149
164
|
// can leave napi finalize callbacks racing and crash Node.
|
|
150
165
|
const canonicalCratePath = await fs.realpath(cratePath);
|
|
151
166
|
const cacheKey = canonicalCratePath;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
167
|
+
if (_napiModuleCache.has(cacheKey)) {
|
|
168
|
+
return {
|
|
169
|
+
source: "cache",
|
|
170
|
+
importInstance: async () => project(_napiModuleCache.get(cacheKey), entry, cratePath),
|
|
171
|
+
};
|
|
155
172
|
}
|
|
156
173
|
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
// the
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
174
|
+
// `local` rather than `cargo-build` because the only mode currently wired up
|
|
175
|
+
// is `local_path` dev-mode — see the original note below. The thunk re-checks
|
|
176
|
+
// the module cache and the in-flight gate because resolve() and the deferred
|
|
177
|
+
// import can be separated in time (another instantiation may have built the
|
|
178
|
+
// crate in between).
|
|
179
|
+
const crateName = name ?? "";
|
|
180
|
+
const build = this.buildAndLoad.bind(this);
|
|
181
|
+
return {
|
|
182
|
+
source: "local",
|
|
183
|
+
importInstance: async () => {
|
|
184
|
+
const cached = _napiModuleCache.get(cacheKey);
|
|
185
|
+
if (cached) return project(cached, entry, cratePath);
|
|
186
|
+
// Concurrent callers for the same crate await one shared build, sharing
|
|
187
|
+
// one cargo invocation rather than each running their own.
|
|
188
|
+
const existingInFlight = _napiInFlight.get(cacheKey);
|
|
189
|
+
if (existingInFlight) {
|
|
190
|
+
const { rawModule } = await existingInFlight;
|
|
191
|
+
return project(rawModule, entry, cratePath);
|
|
192
|
+
}
|
|
193
|
+
const buildPromise = build(cratePath, crateName, cacheKey);
|
|
194
|
+
_napiInFlight.set(cacheKey, buildPromise);
|
|
195
|
+
let rawModule: any;
|
|
196
|
+
let nodePath: string;
|
|
197
|
+
try {
|
|
198
|
+
({ rawModule, nodePath } = await buildPromise);
|
|
199
|
+
} finally {
|
|
200
|
+
_napiInFlight.delete(cacheKey);
|
|
201
|
+
}
|
|
202
|
+
return project(rawModule, entry, nodePath);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
184
205
|
}
|
|
185
206
|
|
|
186
207
|
private async buildAndLoad(
|
|
@@ -178,6 +178,20 @@ export class NpmControllerLoader {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
async load(purl: string, baseUri: string): Promise<NpmLoadResult> {
|
|
181
|
+
const { source, importInstance } = await this.resolve(purl, baseUri);
|
|
182
|
+
return { instance: await importInstance(), source };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Resolve without importing: materialize the install root and install/verify
|
|
187
|
+
* the package (the cheap, cache-hit-fast part that fails fast if the package
|
|
188
|
+
* is absent), but defer `loadFromInstall` — the actual `import()`/eval, which
|
|
189
|
+
* is the expensive cold-start cost — into the returned `importInstance` thunk.
|
|
190
|
+
*/
|
|
191
|
+
async resolve(
|
|
192
|
+
purl: string,
|
|
193
|
+
baseUri: string,
|
|
194
|
+
): Promise<{ source: NpmResolveSource; importInstance: () => Promise<ControllerInstance> }> {
|
|
181
195
|
const parsed = PackageURL.fromString(purl);
|
|
182
196
|
if (!parsed.name) {
|
|
183
197
|
throw new Error(`Invalid PURL '${purl}': missing package name`);
|
|
@@ -197,8 +211,11 @@ export class NpmControllerLoader {
|
|
|
197
211
|
resolved.kind,
|
|
198
212
|
version,
|
|
199
213
|
);
|
|
200
|
-
const
|
|
201
|
-
return {
|
|
214
|
+
const subpath = parsed.subpath ?? null;
|
|
215
|
+
return {
|
|
216
|
+
source,
|
|
217
|
+
importInstance: () => loadFromInstall(installRoot, alias, subpath, purl),
|
|
218
|
+
};
|
|
202
219
|
}
|
|
203
220
|
|
|
204
221
|
/**
|
|
@@ -12,9 +12,21 @@ const DEFAULT_FINGERPRINT = "default";
|
|
|
12
12
|
* lock out the second. Definitions remain kind-only; only the loaded
|
|
13
13
|
* controller instance is policy-scoped.
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* A controller whose definition has been resolved but whose module is not yet
|
|
17
|
+
* imported. `load()` performs the import + `registerController` (firing the
|
|
18
|
+
* controller's `register()` hook); `loading` single-flights concurrent
|
|
19
|
+
* instantiations of the same kind through one import.
|
|
20
|
+
*/
|
|
21
|
+
interface LazyControllerEntry {
|
|
22
|
+
load: () => Promise<void>;
|
|
23
|
+
loading?: Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
export class ControllerRegistry {
|
|
16
27
|
private controllersByKind: Map<string, Map<string, ControllerInstance>> = new Map();
|
|
17
28
|
private definitionsByKind: Map<string, ResourceDefinition> = new Map();
|
|
29
|
+
private lazyByKind: Map<string, Map<string, LazyControllerEntry>> = new Map();
|
|
18
30
|
|
|
19
31
|
/**
|
|
20
32
|
* Register a controller definition
|
|
@@ -143,4 +155,55 @@ export class ControllerRegistry {
|
|
|
143
155
|
}
|
|
144
156
|
byFp.set(fingerprint, wrappedController);
|
|
145
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Register a deferred controller for a (kind, fingerprint). The definition's
|
|
161
|
+
* metadata is already registered (so analysis/refs work); the controller
|
|
162
|
+
* module itself is imported lazily by {@link takeLazyController} on the kind's
|
|
163
|
+
* first instantiation. Mirrors `registerController`'s fingerprint keying.
|
|
164
|
+
*/
|
|
165
|
+
registerLazyController(
|
|
166
|
+
kind: string,
|
|
167
|
+
fingerprint: string,
|
|
168
|
+
load: () => Promise<void>,
|
|
169
|
+
): void {
|
|
170
|
+
if (!this.definitionsByKind.has(kind)) {
|
|
171
|
+
throw new Error(`Cannot register lazy controller for kind ${kind} without definition`);
|
|
172
|
+
}
|
|
173
|
+
let byFp = this.lazyByKind.get(kind);
|
|
174
|
+
if (!byFp) {
|
|
175
|
+
byFp = new Map();
|
|
176
|
+
this.lazyByKind.set(kind, byFp);
|
|
177
|
+
}
|
|
178
|
+
byFp.set(fingerprint, { load });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Drive the deferred import for a (kind, fingerprint) if one is registered,
|
|
183
|
+
* single-flighting concurrent callers through the same import. Returns true
|
|
184
|
+
* when a lazy entry existed (after its `load()` has completed and the
|
|
185
|
+
* controller is registered), false when there is none — letting the caller
|
|
186
|
+
* fall through to its existing "no controller" handling. Same fingerprint
|
|
187
|
+
* fallback order as `getControllerOrUndefined`.
|
|
188
|
+
*/
|
|
189
|
+
async takeLazyController(
|
|
190
|
+
kind: string,
|
|
191
|
+
fingerprint: string = DEFAULT_FINGERPRINT,
|
|
192
|
+
): Promise<boolean> {
|
|
193
|
+
const byFp = this.lazyByKind.get(kind);
|
|
194
|
+
if (!byFp) return false;
|
|
195
|
+
const entry =
|
|
196
|
+
byFp.get(fingerprint) ?? byFp.get(DEFAULT_FINGERPRINT) ?? byFp.values().next().value;
|
|
197
|
+
if (!entry) return false;
|
|
198
|
+
if (!entry.loading) {
|
|
199
|
+
// Reset on failure so a later instantiation re-attempts and re-surfaces
|
|
200
|
+
// the error rather than awaiting a cached rejection forever.
|
|
201
|
+
entry.loading = entry.load().catch((err) => {
|
|
202
|
+
entry.loading = undefined;
|
|
203
|
+
throw err;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
await entry.loading;
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
146
209
|
}
|
|
@@ -46,30 +46,63 @@ class ResourceDefinition implements ResourceInstance {
|
|
|
46
46
|
);
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
// The loader owns ControllerLoading / ControllerLoaded / ControllerLoadFailed
|
|
50
|
-
// emission so it can fire one event per attempted candidate (env-missing
|
|
51
|
-
// fallback chains), and so the payload can include the actually-picked PURL,
|
|
52
|
-
// which branch resolved it (`source`), and timing — none of which are known
|
|
53
|
-
// here at the call site.
|
|
54
49
|
const loader = new ControllerLoader({
|
|
55
|
-
emit: (e) => ctx.emit(e.name, e.payload),
|
|
56
50
|
entryUrl: ctx.getEntryUrl(),
|
|
57
51
|
installRoot: ctx.getInstallRoot(),
|
|
58
52
|
});
|
|
59
|
-
|
|
53
|
+
// Eager resolve — verify the controller is hostable now (so a broken
|
|
54
|
+
// `controllers:` candidate fails fast at boot), but defer the expensive
|
|
55
|
+
// import/eval and the controller's `register()` to the kind's first
|
|
56
|
+
// instantiation. Definitions whose kind is never instantiated never import.
|
|
57
|
+
const resolved = await loader.resolve(
|
|
60
58
|
this.resource.controllers,
|
|
61
59
|
this.resource.metadata.source,
|
|
62
60
|
ctx.getControllerPolicy(),
|
|
63
61
|
);
|
|
64
62
|
ctx.registerDefinition(this.resource);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
|
|
64
|
+
const moduleName = this.resource.metadata.module;
|
|
65
|
+
const kindName = this.resource.metadata.name;
|
|
66
|
+
// Emitted here (not in the loader) so ControllerLoading / ControllerLoaded /
|
|
67
|
+
// ControllerLoadFailed — and the import duration — surface when the load
|
|
68
|
+
// actually happens (first instantiation), with the resolved PURL + source.
|
|
69
|
+
(ctx as unknown as LazyControllerHost).registerLazyController(
|
|
70
|
+
moduleName,
|
|
71
|
+
kindName,
|
|
72
|
+
async () => {
|
|
73
|
+
await ctx.emit("ControllerLoading", { purl: resolved.purl });
|
|
74
|
+
const startedAt = Date.now();
|
|
75
|
+
const instance = await resolved.importInstance().catch(async (err) => {
|
|
76
|
+
await ctx.emit("ControllerLoadFailed", {
|
|
77
|
+
purl: resolved.purl,
|
|
78
|
+
error: err instanceof Error ? err.message : String(err),
|
|
79
|
+
});
|
|
80
|
+
throw err;
|
|
81
|
+
});
|
|
82
|
+
await ctx.registerController(moduleName, kindName, instance);
|
|
83
|
+
await ctx.emit("ControllerLoaded", {
|
|
84
|
+
purl: resolved.purl,
|
|
85
|
+
source: resolved.source,
|
|
86
|
+
durationMs: Date.now() - startedAt,
|
|
87
|
+
});
|
|
88
|
+
},
|
|
69
89
|
);
|
|
70
90
|
}
|
|
71
91
|
}
|
|
72
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Kernel-internal hook the concrete `ResourceContextImpl` exposes for lazy
|
|
95
|
+
* controller loading — deliberately off the public SDK `ResourceContext`
|
|
96
|
+
* surface, since only this controller uses it.
|
|
97
|
+
*/
|
|
98
|
+
interface LazyControllerHost {
|
|
99
|
+
registerLazyController(
|
|
100
|
+
moduleName: string,
|
|
101
|
+
kindName: string,
|
|
102
|
+
load: () => Promise<void>,
|
|
103
|
+
): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
73
106
|
export function register(ctx: ControllerContext): void {
|
|
74
107
|
// ResourceDefinition is a passive resource - no registration needed
|
|
75
108
|
}
|
package/src/kernel.ts
CHANGED
|
@@ -165,6 +165,21 @@ export class Kernel implements IKernel {
|
|
|
165
165
|
await controllerInstance.register?.(this.createControllerContext(`${moduleName}.${kindName}`));
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Register a deferred controller. The definition is already registered; the
|
|
170
|
+
* controller module is imported (and `register()` fired, via the `load` thunk
|
|
171
|
+
* calling back into `registerController`) only on the kind's first
|
|
172
|
+
* instantiation — see `_createInstance` / `ControllerRegistry.takeLazyController`.
|
|
173
|
+
*/
|
|
174
|
+
registerLazyController(
|
|
175
|
+
moduleName: string,
|
|
176
|
+
kindName: string,
|
|
177
|
+
fingerprint: string,
|
|
178
|
+
load: () => Promise<void>,
|
|
179
|
+
): void {
|
|
180
|
+
this.controllers.registerLazyController(`${moduleName}.${kindName}`, fingerprint, load);
|
|
181
|
+
}
|
|
182
|
+
|
|
168
183
|
/**
|
|
169
184
|
* Register a resource definition with the controller registry
|
|
170
185
|
*/
|
|
@@ -902,7 +917,16 @@ export class Kernel implements IKernel {
|
|
|
902
917
|
const resolvedKind = (findEnclosingModule(evalContext) ?? this.rootContext).resolveKind(kind);
|
|
903
918
|
|
|
904
919
|
const fingerprint = policyFingerprint(findEnclosingPolicy(evalContext));
|
|
905
|
-
|
|
920
|
+
let controller = this.controllers.getControllerOrUndefined(resolvedKind, fingerprint);
|
|
921
|
+
if (!controller) {
|
|
922
|
+
// Lazy controller loading: the kind's Telo.Definition registered a deferred
|
|
923
|
+
// controller (resolved but not imported). Import + register it now, on this
|
|
924
|
+
// first instantiation, then re-resolve. A kind with no lazy entry (abstract,
|
|
925
|
+
// or genuinely unknown) falls through to the error below unchanged.
|
|
926
|
+
if (await this.controllers.takeLazyController(resolvedKind, fingerprint)) {
|
|
927
|
+
controller = this.controllers.getControllerOrUndefined(resolvedKind, fingerprint);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
906
930
|
if (!controller) {
|
|
907
931
|
const kindInfo =
|
|
908
932
|
resolvedKind !== kind ? `'${kind}' (resolved to '${resolvedKind}')` : `'${kind}'`;
|
package/src/resource-context.ts
CHANGED
|
@@ -332,6 +332,21 @@ export class ResourceContextImpl implements ResourceContext {
|
|
|
332
332
|
await this.kernel.registerController(moduleName, kindName, controllerInstance, fingerprint);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Register a deferred controller under the declaring module's policy
|
|
337
|
+
* fingerprint (same keying as `registerController`). Internal to the kernel's
|
|
338
|
+
* lazy controller loading — not part of the public SDK `ResourceContext`
|
|
339
|
+
* surface; the only caller is the `Telo.Definition` controller.
|
|
340
|
+
*/
|
|
341
|
+
registerLazyController(
|
|
342
|
+
moduleName: string,
|
|
343
|
+
kindName: string,
|
|
344
|
+
load: () => Promise<void>,
|
|
345
|
+
): void {
|
|
346
|
+
const fingerprint = policyFingerprint(this.moduleContext.getControllerPolicy());
|
|
347
|
+
this.kernel.registerLazyController(moduleName, kindName, fingerprint, load);
|
|
348
|
+
}
|
|
349
|
+
|
|
335
350
|
registerDefinition(def: any) {
|
|
336
351
|
this.kernel.registerResourceDefinition(def);
|
|
337
352
|
}
|