@h-rig/kernel-seed 0.0.6-alpha.133
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/README.md +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +165 -0
- package/dist/src/pluginAbi.d.ts +31 -0
- package/dist/src/pluginAbi.js +8 -0
- package/dist/src/resolveCapability.d.ts +24 -0
- package/dist/src/resolveCapability.js +106 -0
- package/dist/src/seed.d.ts +8 -0
- package/dist/src/seed.js +156 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @h-rig/kernel-seed
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/kernel-seed/src/pluginAbi.ts
|
|
3
|
+
function loadedPluginId(plugin) {
|
|
4
|
+
return plugin.meta.id;
|
|
5
|
+
}
|
|
6
|
+
// packages/kernel-seed/src/resolveCapability.ts
|
|
7
|
+
class AmbiguousCapabilityError extends Error {
|
|
8
|
+
capability;
|
|
9
|
+
providers;
|
|
10
|
+
name = "AmbiguousCapabilityError";
|
|
11
|
+
constructor(capability, providers) {
|
|
12
|
+
super(`Ambiguous capability provider for ${capability}: ${providers.join(", ")}`);
|
|
13
|
+
this.capability = capability;
|
|
14
|
+
this.providers = providers;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class CapabilityProviderMissingError extends Error {
|
|
19
|
+
capability;
|
|
20
|
+
name = "CapabilityProviderMissingError";
|
|
21
|
+
constructor(capability) {
|
|
22
|
+
super(`No provider resolved for capability ${capability}`);
|
|
23
|
+
this.capability = capability;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function capabilityValue(plugin, capability) {
|
|
27
|
+
const direct = plugin.runtime.capabilities?.[capability];
|
|
28
|
+
const value = direct ?? (capability === "kernel" ? plugin.runtime.kernel : undefined) ?? plugin.runtime;
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function replacementTarget(capability, providerId) {
|
|
32
|
+
return `${capability}@${providerId}`;
|
|
33
|
+
}
|
|
34
|
+
function capabilityProviders(plugins, capability) {
|
|
35
|
+
return plugins.filter((plugin) => plugin.provides.has(capability));
|
|
36
|
+
}
|
|
37
|
+
function byPrecedence(providers, precedence) {
|
|
38
|
+
if (!precedence || precedence.length === 0)
|
|
39
|
+
return null;
|
|
40
|
+
const byId = new Map(providers.map((provider) => [loadedPluginId(provider), provider]));
|
|
41
|
+
for (const id of precedence) {
|
|
42
|
+
const provider = byId.get(id);
|
|
43
|
+
if (provider)
|
|
44
|
+
return provider;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function providersAfterReplacement(capability, providers) {
|
|
49
|
+
const providerIds = new Set(providers.map(loadedPluginId));
|
|
50
|
+
const replaced = new Set;
|
|
51
|
+
for (const provider of providers) {
|
|
52
|
+
for (const replacement of provider.replaces ?? []) {
|
|
53
|
+
const separator = replacement.indexOf("@");
|
|
54
|
+
if (separator <= 0)
|
|
55
|
+
continue;
|
|
56
|
+
const replacementCapability = replacement.slice(0, separator);
|
|
57
|
+
const replacementProviderId = replacement.slice(separator + 1);
|
|
58
|
+
if (replacementCapability === capability && providerIds.has(replacementProviderId)) {
|
|
59
|
+
replaced.add(replacementProviderId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
providers: providers.filter((provider) => !replaced.has(loadedPluginId(provider))),
|
|
65
|
+
replaced: [...replaced].sort()
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function resolveCapability(plugins, capability, config = {}) {
|
|
69
|
+
const providers = capabilityProviders(plugins, capability);
|
|
70
|
+
if (providers.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
const precedenceProvider = byPrecedence(providers, config.capabilityPrecedence?.[capability]);
|
|
73
|
+
if (precedenceProvider) {
|
|
74
|
+
return {
|
|
75
|
+
capability,
|
|
76
|
+
plugin: precedenceProvider,
|
|
77
|
+
value: capabilityValue(precedenceProvider, capability),
|
|
78
|
+
selectedBy: "precedence",
|
|
79
|
+
replacedProviders: []
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const afterReplacement = providersAfterReplacement(capability, providers);
|
|
83
|
+
if (afterReplacement.providers.length === 1) {
|
|
84
|
+
const [provider] = afterReplacement.providers;
|
|
85
|
+
if (!provider)
|
|
86
|
+
throw new CapabilityProviderMissingError(capability);
|
|
87
|
+
return {
|
|
88
|
+
capability,
|
|
89
|
+
plugin: provider,
|
|
90
|
+
value: capabilityValue(provider, capability),
|
|
91
|
+
selectedBy: afterReplacement.replaced.length > 0 ? "replaces" : "sole-provider",
|
|
92
|
+
replacedProviders: afterReplacement.replaced
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
throw new AmbiguousCapabilityError(capability, afterReplacement.providers.map(loadedPluginId).sort());
|
|
96
|
+
}
|
|
97
|
+
function declaresReplacement(plugin, capability, providerId) {
|
|
98
|
+
return (plugin.replaces ?? []).includes(replacementTarget(capability, providerId));
|
|
99
|
+
}
|
|
100
|
+
// packages/kernel-seed/src/seed.ts
|
|
101
|
+
class BootIncoherent extends Error {
|
|
102
|
+
name = "BootIncoherent";
|
|
103
|
+
constructor(message) {
|
|
104
|
+
super(message);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function isRecord(value) {
|
|
108
|
+
return value !== null && typeof value === "object";
|
|
109
|
+
}
|
|
110
|
+
function objectAt(value, key) {
|
|
111
|
+
const child = value[key];
|
|
112
|
+
return isRecord(child) ? child : null;
|
|
113
|
+
}
|
|
114
|
+
function hasFunction(value, key) {
|
|
115
|
+
return value !== null && typeof value[key] === "function";
|
|
116
|
+
}
|
|
117
|
+
function assertSatisfiesKernelInterface(value) {
|
|
118
|
+
if (!isRecord(value)) {
|
|
119
|
+
throw new BootIncoherent("kernel provider did not return an object");
|
|
120
|
+
}
|
|
121
|
+
const stageRunner = objectAt(value, "stageRunner");
|
|
122
|
+
if (!hasFunction(stageRunner, "resolve") || !hasFunction(stageRunner, "runPipeline")) {
|
|
123
|
+
throw new BootIncoherent("kernel provider must expose stageRunner.resolve and stageRunner.runPipeline");
|
|
124
|
+
}
|
|
125
|
+
const journal = objectAt(value, "journal");
|
|
126
|
+
if (!hasFunction(journal, "append") || !hasFunction(journal, "recordPipeline") || !hasFunction(journal, "read")) {
|
|
127
|
+
throw new BootIncoherent("kernel provider must expose journal.append, journal.recordPipeline, and journal.read");
|
|
128
|
+
}
|
|
129
|
+
const loaderPolicy = objectAt(value, "loaderPolicy");
|
|
130
|
+
if (!hasFunction(loaderPolicy, "resolveCapability")) {
|
|
131
|
+
throw new BootIncoherent("kernel provider must expose loaderPolicy.resolveCapability");
|
|
132
|
+
}
|
|
133
|
+
const transport = objectAt(value, "transport");
|
|
134
|
+
if (!hasFunction(transport, "dispatch")) {
|
|
135
|
+
throw new BootIncoherent("kernel provider must expose transport.dispatch");
|
|
136
|
+
}
|
|
137
|
+
if (typeof value.start !== "function") {
|
|
138
|
+
throw new BootIncoherent("kernel provider must expose start");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function loadPlugins(config) {
|
|
142
|
+
if (config.loadPlugins) {
|
|
143
|
+
return await config.loadPlugins();
|
|
144
|
+
}
|
|
145
|
+
return config.plugins ?? [];
|
|
146
|
+
}
|
|
147
|
+
async function boot(config) {
|
|
148
|
+
const plugins = await loadPlugins(config);
|
|
149
|
+
const resolution = resolveCapability(plugins, "kernel", config);
|
|
150
|
+
if (!resolution) {
|
|
151
|
+
throw new BootIncoherent("no kernel provider resolved");
|
|
152
|
+
}
|
|
153
|
+
assertSatisfiesKernelInterface(resolution.value);
|
|
154
|
+
return { kernel: resolution.value, plugins };
|
|
155
|
+
}
|
|
156
|
+
export {
|
|
157
|
+
resolveCapability,
|
|
158
|
+
loadedPluginId,
|
|
159
|
+
declaresReplacement,
|
|
160
|
+
boot,
|
|
161
|
+
assertSatisfiesKernelInterface,
|
|
162
|
+
CapabilityProviderMissingError,
|
|
163
|
+
BootIncoherent,
|
|
164
|
+
AmbiguousCapabilityError
|
|
165
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CapabilityTag, KernelCapability } from "@rig/contracts";
|
|
2
|
+
export type PluginMeta = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name?: string;
|
|
5
|
+
readonly version?: string;
|
|
6
|
+
};
|
|
7
|
+
export type CapabilityReplacement = `${CapabilityTag}@${string}`;
|
|
8
|
+
export type PluginContributions = Readonly<Record<string, unknown>>;
|
|
9
|
+
export type PluginRuntimeChannel = {
|
|
10
|
+
readonly capabilities?: Readonly<Record<string, unknown>>;
|
|
11
|
+
readonly kernel?: KernelCapability;
|
|
12
|
+
};
|
|
13
|
+
export type LoadedPlugin = {
|
|
14
|
+
readonly meta: PluginMeta;
|
|
15
|
+
readonly provides: ReadonlySet<CapabilityTag>;
|
|
16
|
+
readonly requires?: readonly CapabilityTag[];
|
|
17
|
+
readonly replaces?: readonly CapabilityReplacement[];
|
|
18
|
+
readonly contributes: PluginContributions;
|
|
19
|
+
readonly runtime: PluginRuntimeChannel;
|
|
20
|
+
};
|
|
21
|
+
export type CapabilityPrecedence = Partial<Record<CapabilityTag, readonly string[]>>;
|
|
22
|
+
export type ResolvedBootConfig = {
|
|
23
|
+
readonly plugins?: readonly LoadedPlugin[];
|
|
24
|
+
readonly loadPlugins?: () => Promise<readonly LoadedPlugin[]> | readonly LoadedPlugin[];
|
|
25
|
+
readonly capabilityPrecedence?: CapabilityPrecedence;
|
|
26
|
+
};
|
|
27
|
+
export type BootResult = {
|
|
28
|
+
readonly kernel: KernelCapability;
|
|
29
|
+
readonly plugins: readonly LoadedPlugin[];
|
|
30
|
+
};
|
|
31
|
+
export declare function loadedPluginId(plugin: LoadedPlugin): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CapabilityTag } from "@rig/contracts";
|
|
2
|
+
import type { CapabilityPrecedence, LoadedPlugin } from "./pluginAbi";
|
|
3
|
+
export type CapabilityResolution<T> = {
|
|
4
|
+
readonly capability: CapabilityTag;
|
|
5
|
+
readonly plugin: LoadedPlugin;
|
|
6
|
+
readonly value: T;
|
|
7
|
+
readonly selectedBy: "sole-provider" | "precedence" | "replaces";
|
|
8
|
+
readonly replacedProviders: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
export declare class AmbiguousCapabilityError extends Error {
|
|
11
|
+
readonly capability: CapabilityTag;
|
|
12
|
+
readonly providers: readonly string[];
|
|
13
|
+
readonly name = "AmbiguousCapabilityError";
|
|
14
|
+
constructor(capability: CapabilityTag, providers: readonly string[]);
|
|
15
|
+
}
|
|
16
|
+
export declare class CapabilityProviderMissingError extends Error {
|
|
17
|
+
readonly capability: CapabilityTag;
|
|
18
|
+
readonly name = "CapabilityProviderMissingError";
|
|
19
|
+
constructor(capability: CapabilityTag);
|
|
20
|
+
}
|
|
21
|
+
export declare function resolveCapability<T>(plugins: readonly LoadedPlugin[], capability: CapabilityTag, config?: {
|
|
22
|
+
readonly capabilityPrecedence?: CapabilityPrecedence;
|
|
23
|
+
}): CapabilityResolution<T> | null;
|
|
24
|
+
export declare function declaresReplacement(plugin: LoadedPlugin, capability: CapabilityTag, providerId: string): boolean;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/kernel-seed/src/pluginAbi.ts
|
|
3
|
+
function loadedPluginId(plugin) {
|
|
4
|
+
return plugin.meta.id;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// packages/kernel-seed/src/resolveCapability.ts
|
|
8
|
+
class AmbiguousCapabilityError extends Error {
|
|
9
|
+
capability;
|
|
10
|
+
providers;
|
|
11
|
+
name = "AmbiguousCapabilityError";
|
|
12
|
+
constructor(capability, providers) {
|
|
13
|
+
super(`Ambiguous capability provider for ${capability}: ${providers.join(", ")}`);
|
|
14
|
+
this.capability = capability;
|
|
15
|
+
this.providers = providers;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class CapabilityProviderMissingError extends Error {
|
|
20
|
+
capability;
|
|
21
|
+
name = "CapabilityProviderMissingError";
|
|
22
|
+
constructor(capability) {
|
|
23
|
+
super(`No provider resolved for capability ${capability}`);
|
|
24
|
+
this.capability = capability;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function capabilityValue(plugin, capability) {
|
|
28
|
+
const direct = plugin.runtime.capabilities?.[capability];
|
|
29
|
+
const value = direct ?? (capability === "kernel" ? plugin.runtime.kernel : undefined) ?? plugin.runtime;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function replacementTarget(capability, providerId) {
|
|
33
|
+
return `${capability}@${providerId}`;
|
|
34
|
+
}
|
|
35
|
+
function capabilityProviders(plugins, capability) {
|
|
36
|
+
return plugins.filter((plugin) => plugin.provides.has(capability));
|
|
37
|
+
}
|
|
38
|
+
function byPrecedence(providers, precedence) {
|
|
39
|
+
if (!precedence || precedence.length === 0)
|
|
40
|
+
return null;
|
|
41
|
+
const byId = new Map(providers.map((provider) => [loadedPluginId(provider), provider]));
|
|
42
|
+
for (const id of precedence) {
|
|
43
|
+
const provider = byId.get(id);
|
|
44
|
+
if (provider)
|
|
45
|
+
return provider;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function providersAfterReplacement(capability, providers) {
|
|
50
|
+
const providerIds = new Set(providers.map(loadedPluginId));
|
|
51
|
+
const replaced = new Set;
|
|
52
|
+
for (const provider of providers) {
|
|
53
|
+
for (const replacement of provider.replaces ?? []) {
|
|
54
|
+
const separator = replacement.indexOf("@");
|
|
55
|
+
if (separator <= 0)
|
|
56
|
+
continue;
|
|
57
|
+
const replacementCapability = replacement.slice(0, separator);
|
|
58
|
+
const replacementProviderId = replacement.slice(separator + 1);
|
|
59
|
+
if (replacementCapability === capability && providerIds.has(replacementProviderId)) {
|
|
60
|
+
replaced.add(replacementProviderId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
providers: providers.filter((provider) => !replaced.has(loadedPluginId(provider))),
|
|
66
|
+
replaced: [...replaced].sort()
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function resolveCapability(plugins, capability, config = {}) {
|
|
70
|
+
const providers = capabilityProviders(plugins, capability);
|
|
71
|
+
if (providers.length === 0)
|
|
72
|
+
return null;
|
|
73
|
+
const precedenceProvider = byPrecedence(providers, config.capabilityPrecedence?.[capability]);
|
|
74
|
+
if (precedenceProvider) {
|
|
75
|
+
return {
|
|
76
|
+
capability,
|
|
77
|
+
plugin: precedenceProvider,
|
|
78
|
+
value: capabilityValue(precedenceProvider, capability),
|
|
79
|
+
selectedBy: "precedence",
|
|
80
|
+
replacedProviders: []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const afterReplacement = providersAfterReplacement(capability, providers);
|
|
84
|
+
if (afterReplacement.providers.length === 1) {
|
|
85
|
+
const [provider] = afterReplacement.providers;
|
|
86
|
+
if (!provider)
|
|
87
|
+
throw new CapabilityProviderMissingError(capability);
|
|
88
|
+
return {
|
|
89
|
+
capability,
|
|
90
|
+
plugin: provider,
|
|
91
|
+
value: capabilityValue(provider, capability),
|
|
92
|
+
selectedBy: afterReplacement.replaced.length > 0 ? "replaces" : "sole-provider",
|
|
93
|
+
replacedProviders: afterReplacement.replaced
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
throw new AmbiguousCapabilityError(capability, afterReplacement.providers.map(loadedPluginId).sort());
|
|
97
|
+
}
|
|
98
|
+
function declaresReplacement(plugin, capability, providerId) {
|
|
99
|
+
return (plugin.replaces ?? []).includes(replacementTarget(capability, providerId));
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
resolveCapability,
|
|
103
|
+
declaresReplacement,
|
|
104
|
+
CapabilityProviderMissingError,
|
|
105
|
+
AmbiguousCapabilityError
|
|
106
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { KernelCapability } from "@rig/contracts";
|
|
2
|
+
import type { BootResult, ResolvedBootConfig } from "./pluginAbi";
|
|
3
|
+
export declare class BootIncoherent extends Error {
|
|
4
|
+
readonly name = "BootIncoherent";
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function assertSatisfiesKernelInterface(value: unknown): asserts value is KernelCapability;
|
|
8
|
+
export declare function boot(config: ResolvedBootConfig): Promise<BootResult>;
|
package/dist/src/seed.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/kernel-seed/src/pluginAbi.ts
|
|
3
|
+
function loadedPluginId(plugin) {
|
|
4
|
+
return plugin.meta.id;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// packages/kernel-seed/src/resolveCapability.ts
|
|
8
|
+
class AmbiguousCapabilityError extends Error {
|
|
9
|
+
capability;
|
|
10
|
+
providers;
|
|
11
|
+
name = "AmbiguousCapabilityError";
|
|
12
|
+
constructor(capability, providers) {
|
|
13
|
+
super(`Ambiguous capability provider for ${capability}: ${providers.join(", ")}`);
|
|
14
|
+
this.capability = capability;
|
|
15
|
+
this.providers = providers;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class CapabilityProviderMissingError extends Error {
|
|
20
|
+
capability;
|
|
21
|
+
name = "CapabilityProviderMissingError";
|
|
22
|
+
constructor(capability) {
|
|
23
|
+
super(`No provider resolved for capability ${capability}`);
|
|
24
|
+
this.capability = capability;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function capabilityValue(plugin, capability) {
|
|
28
|
+
const direct = plugin.runtime.capabilities?.[capability];
|
|
29
|
+
const value = direct ?? (capability === "kernel" ? plugin.runtime.kernel : undefined) ?? plugin.runtime;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function capabilityProviders(plugins, capability) {
|
|
33
|
+
return plugins.filter((plugin) => plugin.provides.has(capability));
|
|
34
|
+
}
|
|
35
|
+
function byPrecedence(providers, precedence) {
|
|
36
|
+
if (!precedence || precedence.length === 0)
|
|
37
|
+
return null;
|
|
38
|
+
const byId = new Map(providers.map((provider) => [loadedPluginId(provider), provider]));
|
|
39
|
+
for (const id of precedence) {
|
|
40
|
+
const provider = byId.get(id);
|
|
41
|
+
if (provider)
|
|
42
|
+
return provider;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function providersAfterReplacement(capability, providers) {
|
|
47
|
+
const providerIds = new Set(providers.map(loadedPluginId));
|
|
48
|
+
const replaced = new Set;
|
|
49
|
+
for (const provider of providers) {
|
|
50
|
+
for (const replacement of provider.replaces ?? []) {
|
|
51
|
+
const separator = replacement.indexOf("@");
|
|
52
|
+
if (separator <= 0)
|
|
53
|
+
continue;
|
|
54
|
+
const replacementCapability = replacement.slice(0, separator);
|
|
55
|
+
const replacementProviderId = replacement.slice(separator + 1);
|
|
56
|
+
if (replacementCapability === capability && providerIds.has(replacementProviderId)) {
|
|
57
|
+
replaced.add(replacementProviderId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
providers: providers.filter((provider) => !replaced.has(loadedPluginId(provider))),
|
|
63
|
+
replaced: [...replaced].sort()
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function resolveCapability(plugins, capability, config = {}) {
|
|
67
|
+
const providers = capabilityProviders(plugins, capability);
|
|
68
|
+
if (providers.length === 0)
|
|
69
|
+
return null;
|
|
70
|
+
const precedenceProvider = byPrecedence(providers, config.capabilityPrecedence?.[capability]);
|
|
71
|
+
if (precedenceProvider) {
|
|
72
|
+
return {
|
|
73
|
+
capability,
|
|
74
|
+
plugin: precedenceProvider,
|
|
75
|
+
value: capabilityValue(precedenceProvider, capability),
|
|
76
|
+
selectedBy: "precedence",
|
|
77
|
+
replacedProviders: []
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const afterReplacement = providersAfterReplacement(capability, providers);
|
|
81
|
+
if (afterReplacement.providers.length === 1) {
|
|
82
|
+
const [provider] = afterReplacement.providers;
|
|
83
|
+
if (!provider)
|
|
84
|
+
throw new CapabilityProviderMissingError(capability);
|
|
85
|
+
return {
|
|
86
|
+
capability,
|
|
87
|
+
plugin: provider,
|
|
88
|
+
value: capabilityValue(provider, capability),
|
|
89
|
+
selectedBy: afterReplacement.replaced.length > 0 ? "replaces" : "sole-provider",
|
|
90
|
+
replacedProviders: afterReplacement.replaced
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
throw new AmbiguousCapabilityError(capability, afterReplacement.providers.map(loadedPluginId).sort());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// packages/kernel-seed/src/seed.ts
|
|
97
|
+
class BootIncoherent extends Error {
|
|
98
|
+
name = "BootIncoherent";
|
|
99
|
+
constructor(message) {
|
|
100
|
+
super(message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function isRecord(value) {
|
|
104
|
+
return value !== null && typeof value === "object";
|
|
105
|
+
}
|
|
106
|
+
function objectAt(value, key) {
|
|
107
|
+
const child = value[key];
|
|
108
|
+
return isRecord(child) ? child : null;
|
|
109
|
+
}
|
|
110
|
+
function hasFunction(value, key) {
|
|
111
|
+
return value !== null && typeof value[key] === "function";
|
|
112
|
+
}
|
|
113
|
+
function assertSatisfiesKernelInterface(value) {
|
|
114
|
+
if (!isRecord(value)) {
|
|
115
|
+
throw new BootIncoherent("kernel provider did not return an object");
|
|
116
|
+
}
|
|
117
|
+
const stageRunner = objectAt(value, "stageRunner");
|
|
118
|
+
if (!hasFunction(stageRunner, "resolve") || !hasFunction(stageRunner, "runPipeline")) {
|
|
119
|
+
throw new BootIncoherent("kernel provider must expose stageRunner.resolve and stageRunner.runPipeline");
|
|
120
|
+
}
|
|
121
|
+
const journal = objectAt(value, "journal");
|
|
122
|
+
if (!hasFunction(journal, "append") || !hasFunction(journal, "recordPipeline") || !hasFunction(journal, "read")) {
|
|
123
|
+
throw new BootIncoherent("kernel provider must expose journal.append, journal.recordPipeline, and journal.read");
|
|
124
|
+
}
|
|
125
|
+
const loaderPolicy = objectAt(value, "loaderPolicy");
|
|
126
|
+
if (!hasFunction(loaderPolicy, "resolveCapability")) {
|
|
127
|
+
throw new BootIncoherent("kernel provider must expose loaderPolicy.resolveCapability");
|
|
128
|
+
}
|
|
129
|
+
const transport = objectAt(value, "transport");
|
|
130
|
+
if (!hasFunction(transport, "dispatch")) {
|
|
131
|
+
throw new BootIncoherent("kernel provider must expose transport.dispatch");
|
|
132
|
+
}
|
|
133
|
+
if (typeof value.start !== "function") {
|
|
134
|
+
throw new BootIncoherent("kernel provider must expose start");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function loadPlugins(config) {
|
|
138
|
+
if (config.loadPlugins) {
|
|
139
|
+
return await config.loadPlugins();
|
|
140
|
+
}
|
|
141
|
+
return config.plugins ?? [];
|
|
142
|
+
}
|
|
143
|
+
async function boot(config) {
|
|
144
|
+
const plugins = await loadPlugins(config);
|
|
145
|
+
const resolution = resolveCapability(plugins, "kernel", config);
|
|
146
|
+
if (!resolution) {
|
|
147
|
+
throw new BootIncoherent("no kernel provider resolved");
|
|
148
|
+
}
|
|
149
|
+
assertSatisfiesKernelInterface(resolution.value);
|
|
150
|
+
return { kernel: resolution.value, plugins };
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
boot,
|
|
154
|
+
assertSatisfiesKernelInterface,
|
|
155
|
+
BootIncoherent
|
|
156
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@h-rig/kernel-seed",
|
|
3
|
+
"version": "0.0.6-alpha.133",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Irreducible Rig bootstrap seed, plugin ABI, and capability resolver.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/src/index.d.ts",
|
|
14
|
+
"import": "./dist/src/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./resolve-capability": {
|
|
17
|
+
"types": "./dist/src/resolveCapability.d.ts",
|
|
18
|
+
"import": "./dist/src/resolveCapability.js"
|
|
19
|
+
},
|
|
20
|
+
"./seed": {
|
|
21
|
+
"types": "./dist/src/seed.d.ts",
|
|
22
|
+
"import": "./dist/src/seed.js"
|
|
23
|
+
},
|
|
24
|
+
"./plugin-abi": {
|
|
25
|
+
"types": "./dist/src/pluginAbi.d.ts",
|
|
26
|
+
"import": "./dist/src/pluginAbi.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"bun": ">=1.3.11"
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/src/index.js",
|
|
33
|
+
"module": "./dist/src/index.js",
|
|
34
|
+
"types": "./dist/src/index.d.ts",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.133"
|
|
37
|
+
}
|
|
38
|
+
}
|