@h-rig/kernel-seed 0.0.6-alpha.135 → 0.0.6-alpha.137
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/src/index.d.ts +1 -0
- package/dist/src/index.js +101 -5
- package/dist/src/journalConformance.d.ts +22 -0
- package/dist/src/journalConformance.js +34 -0
- package/dist/src/pluginAbi.d.ts +11 -0
- package/dist/src/seed.js +100 -5
- package/package.json +2 -2
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/kernel-seed/src/journalConformance.ts
|
|
3
|
+
class JournalConformanceError extends Error {
|
|
4
|
+
name = "JournalConformanceError";
|
|
5
|
+
}
|
|
6
|
+
var CONFORMANCE_RUN_ID = "rig.run.__journal-conformance__";
|
|
7
|
+
function sampleResolvedPipeline() {
|
|
8
|
+
return {
|
|
9
|
+
order: ["validate", "merge-gate"],
|
|
10
|
+
record: [
|
|
11
|
+
{ stageId: "validate", contributedBy: "default", isProtected: false },
|
|
12
|
+
{ stageId: "merge-gate", contributedBy: "default", isProtected: false }
|
|
13
|
+
],
|
|
14
|
+
cycles: [],
|
|
15
|
+
grantUses: [],
|
|
16
|
+
resolvedAt: "1970-01-01T00:00:00.000Z"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
|
|
20
|
+
const pipeline = sampleResolvedPipeline();
|
|
21
|
+
await journal.recordPipeline(runId, pipeline);
|
|
22
|
+
const events = await journal.read(runId);
|
|
23
|
+
if (!Array.isArray(events) || events.length === 0) {
|
|
24
|
+
throw new JournalConformanceError("journal.read returned no records after recordPipeline \u2014 provider does not durably record (append-and-record contract violated)");
|
|
25
|
+
}
|
|
26
|
+
const serialized = JSON.stringify(events);
|
|
27
|
+
if (!serialized.includes("merge-gate") || !serialized.includes("validate")) {
|
|
28
|
+
throw new JournalConformanceError("journal.read did not surface the recorded resolved-pipeline \u2014 provider does not expose the append-and-record contract");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
2
31
|
// packages/kernel-seed/src/pluginAbi.ts
|
|
3
32
|
function loadedPluginId(plugin) {
|
|
4
33
|
return plugin.meta.id;
|
|
@@ -114,6 +143,42 @@ function objectAt(value, key) {
|
|
|
114
143
|
function hasFunction(value, key) {
|
|
115
144
|
return value !== null && typeof value[key] === "function";
|
|
116
145
|
}
|
|
146
|
+
function assertSatisfiesStageRunnerInterface(value) {
|
|
147
|
+
if (!isRecord(value) || typeof value.resolve !== "function" || typeof value.runPipeline !== "function") {
|
|
148
|
+
throw new BootIncoherent("stage-runner provider must expose resolve and runPipeline");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function assertSatisfiesJournalInterface(value) {
|
|
152
|
+
if (!isRecord(value) || typeof value.append !== "function" || typeof value.recordPipeline !== "function" || typeof value.read !== "function") {
|
|
153
|
+
throw new BootIncoherent("journal provider must expose append, recordPipeline, and read");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function assertSatisfiesLoaderPolicyInterface(value) {
|
|
157
|
+
if (!isRecord(value) || typeof value.resolveCapability !== "function") {
|
|
158
|
+
throw new BootIncoherent("loader-policy provider must expose resolveCapability");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function assertSatisfiesTransportInterface(value) {
|
|
162
|
+
if (!isRecord(value) || typeof value.dispatch !== "function") {
|
|
163
|
+
throw new BootIncoherent("transport provider must expose dispatch");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function resolveRequiredCapability(plugins, capability, config, assertInterface) {
|
|
167
|
+
const resolution = resolveCapability(plugins, capability, config);
|
|
168
|
+
if (!resolution) {
|
|
169
|
+
throw new BootIncoherent(`no ${capability} provider resolved`);
|
|
170
|
+
}
|
|
171
|
+
assertInterface(resolution.value);
|
|
172
|
+
return resolution;
|
|
173
|
+
}
|
|
174
|
+
function assertSatisfiesKernelStartProvider(value) {
|
|
175
|
+
if (!isRecord(value)) {
|
|
176
|
+
throw new BootIncoherent("kernel provider did not return an object");
|
|
177
|
+
}
|
|
178
|
+
if (typeof value.start !== "function") {
|
|
179
|
+
throw new BootIncoherent("kernel provider must expose start");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
117
182
|
function assertSatisfiesKernelInterface(value) {
|
|
118
183
|
if (!isRecord(value)) {
|
|
119
184
|
throw new BootIncoherent("kernel provider did not return an object");
|
|
@@ -146,12 +211,41 @@ async function loadPlugins(config) {
|
|
|
146
211
|
}
|
|
147
212
|
async function boot(config) {
|
|
148
213
|
const plugins = await loadPlugins(config);
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
214
|
+
const kernelResolution = resolveRequiredCapability(plugins, "kernel", config, assertSatisfiesKernelStartProvider);
|
|
215
|
+
const stageRunnerResolution = resolveRequiredCapability(plugins, "stage-runner", config, assertSatisfiesStageRunnerInterface);
|
|
216
|
+
const journalResolution = resolveRequiredCapability(plugins, "journal", config, assertSatisfiesJournalInterface);
|
|
217
|
+
const loaderPolicyResolution = resolveRequiredCapability(plugins, "loader-policy", config, assertSatisfiesLoaderPolicyInterface);
|
|
218
|
+
const transportResolution = resolveRequiredCapability(plugins, "transport", config, assertSatisfiesTransportInterface);
|
|
219
|
+
const kernel = kernelResolution.value;
|
|
220
|
+
const stageRunner = stageRunnerResolution.value;
|
|
221
|
+
const journal = journalResolution.value;
|
|
222
|
+
const loaderPolicy = loaderPolicyResolution.value;
|
|
223
|
+
const transport = transportResolution.value;
|
|
224
|
+
const composed = {
|
|
225
|
+
...kernel,
|
|
226
|
+
stageRunner,
|
|
227
|
+
journal,
|
|
228
|
+
loaderPolicy,
|
|
229
|
+
transport,
|
|
230
|
+
start(loadedPlugins) {
|
|
231
|
+
return kernel.start.call(composed, loadedPlugins);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
assertSatisfiesKernelInterface(composed);
|
|
235
|
+
if (config.verifyJournalConformance === true) {
|
|
236
|
+
await assertJournalConformance(composed.journal);
|
|
152
237
|
}
|
|
153
|
-
|
|
154
|
-
|
|
238
|
+
return {
|
|
239
|
+
kernel: composed,
|
|
240
|
+
plugins,
|
|
241
|
+
capabilityProviderIds: {
|
|
242
|
+
kernel: kernelResolution.plugin.meta.id,
|
|
243
|
+
"stage-runner": stageRunnerResolution.plugin.meta.id,
|
|
244
|
+
journal: journalResolution.plugin.meta.id,
|
|
245
|
+
"loader-policy": loaderPolicyResolution.plugin.meta.id,
|
|
246
|
+
transport: transportResolution.plugin.meta.id
|
|
247
|
+
}
|
|
248
|
+
};
|
|
155
249
|
}
|
|
156
250
|
export {
|
|
157
251
|
resolveCapability,
|
|
@@ -159,6 +253,8 @@ export {
|
|
|
159
253
|
declaresReplacement,
|
|
160
254
|
boot,
|
|
161
255
|
assertSatisfiesKernelInterface,
|
|
256
|
+
assertJournalConformance,
|
|
257
|
+
JournalConformanceError,
|
|
162
258
|
CapabilityProviderMissingError,
|
|
163
259
|
BootIncoherent,
|
|
164
260
|
AmbiguousCapabilityError
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { JournalCapability } from "@rig/contracts";
|
|
2
|
+
/**
|
|
3
|
+
* Raised when a `journal` capability provider passes the *structural* check
|
|
4
|
+
* (append/recordPipeline/read present) but does not actually durably record —
|
|
5
|
+
* e.g. a no-op stub that would silently erase the audit trail / safety backstop.
|
|
6
|
+
*/
|
|
7
|
+
export declare class JournalConformanceError extends Error {
|
|
8
|
+
readonly name = "JournalConformanceError";
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Behavioral journal-append conformance — the fixed contract every `journal`
|
|
12
|
+
* provider must satisfy (see the Journal-Append Invariant). The storage backend
|
|
13
|
+
* is swappable; the guarantee that the resolved-pipeline record is durably
|
|
14
|
+
* appended and exposed for projection is NOT. A structurally-complete no-op
|
|
15
|
+
* stub (all three methods present, recording nothing) fails this, which is the
|
|
16
|
+
* whole point: observability is the safety backstop, so a journal that does not
|
|
17
|
+
* record must be rejected — not merely accepted because it has the right shape.
|
|
18
|
+
*
|
|
19
|
+
* Uses a clearly-namespaced probe runId so the record is identifiable and
|
|
20
|
+
* harmless to real-run projections (which filter by the actual runId).
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertJournalConformance(journal: JournalCapability, runId?: string): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/kernel-seed/src/journalConformance.ts
|
|
3
|
+
class JournalConformanceError extends Error {
|
|
4
|
+
name = "JournalConformanceError";
|
|
5
|
+
}
|
|
6
|
+
var CONFORMANCE_RUN_ID = "rig.run.__journal-conformance__";
|
|
7
|
+
function sampleResolvedPipeline() {
|
|
8
|
+
return {
|
|
9
|
+
order: ["validate", "merge-gate"],
|
|
10
|
+
record: [
|
|
11
|
+
{ stageId: "validate", contributedBy: "default", isProtected: false },
|
|
12
|
+
{ stageId: "merge-gate", contributedBy: "default", isProtected: false }
|
|
13
|
+
],
|
|
14
|
+
cycles: [],
|
|
15
|
+
grantUses: [],
|
|
16
|
+
resolvedAt: "1970-01-01T00:00:00.000Z"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
|
|
20
|
+
const pipeline = sampleResolvedPipeline();
|
|
21
|
+
await journal.recordPipeline(runId, pipeline);
|
|
22
|
+
const events = await journal.read(runId);
|
|
23
|
+
if (!Array.isArray(events) || events.length === 0) {
|
|
24
|
+
throw new JournalConformanceError("journal.read returned no records after recordPipeline \u2014 provider does not durably record (append-and-record contract violated)");
|
|
25
|
+
}
|
|
26
|
+
const serialized = JSON.stringify(events);
|
|
27
|
+
if (!serialized.includes("merge-gate") || !serialized.includes("validate")) {
|
|
28
|
+
throw new JournalConformanceError("journal.read did not surface the recorded resolved-pipeline \u2014 provider does not expose the append-and-record contract");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
assertJournalConformance,
|
|
33
|
+
JournalConformanceError
|
|
34
|
+
};
|
package/dist/src/pluginAbi.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type PluginContributions = Readonly<Record<string, unknown>>;
|
|
|
9
9
|
export type PluginRuntimeChannel = {
|
|
10
10
|
readonly capabilities?: Readonly<Record<string, unknown>>;
|
|
11
11
|
readonly kernel?: KernelCapability;
|
|
12
|
+
readonly [key: string]: unknown;
|
|
12
13
|
};
|
|
13
14
|
export type LoadedPlugin = {
|
|
14
15
|
readonly meta: PluginMeta;
|
|
@@ -19,13 +20,23 @@ export type LoadedPlugin = {
|
|
|
19
20
|
readonly runtime: PluginRuntimeChannel;
|
|
20
21
|
};
|
|
21
22
|
export type CapabilityPrecedence = Partial<Record<CapabilityTag, readonly string[]>>;
|
|
23
|
+
export type CapabilityProviderIds = Readonly<Record<CapabilityTag, string>>;
|
|
22
24
|
export type ResolvedBootConfig = {
|
|
23
25
|
readonly plugins?: readonly LoadedPlugin[];
|
|
24
26
|
readonly loadPlugins?: () => Promise<readonly LoadedPlugin[]> | readonly LoadedPlugin[];
|
|
25
27
|
readonly capabilityPrecedence?: CapabilityPrecedence;
|
|
28
|
+
/**
|
|
29
|
+
* When true, boot runs the behavioral journal-append conformance probe
|
|
30
|
+
* against the resolved kernel's journal (rejecting a structurally-complete
|
|
31
|
+
* no-op provider). Off by default to avoid writing a probe record into a live
|
|
32
|
+
* run journal on every boot; opt-in for managed/multi-tenant postures and
|
|
33
|
+
* exercised directly by the conformance test.
|
|
34
|
+
*/
|
|
35
|
+
readonly verifyJournalConformance?: boolean;
|
|
26
36
|
};
|
|
27
37
|
export type BootResult = {
|
|
28
38
|
readonly kernel: KernelCapability;
|
|
29
39
|
readonly plugins: readonly LoadedPlugin[];
|
|
40
|
+
readonly capabilityProviderIds: CapabilityProviderIds;
|
|
30
41
|
};
|
|
31
42
|
export declare function loadedPluginId(plugin: LoadedPlugin): string;
|
package/dist/src/seed.js
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/kernel-seed/src/journalConformance.ts
|
|
3
|
+
class JournalConformanceError extends Error {
|
|
4
|
+
name = "JournalConformanceError";
|
|
5
|
+
}
|
|
6
|
+
var CONFORMANCE_RUN_ID = "rig.run.__journal-conformance__";
|
|
7
|
+
function sampleResolvedPipeline() {
|
|
8
|
+
return {
|
|
9
|
+
order: ["validate", "merge-gate"],
|
|
10
|
+
record: [
|
|
11
|
+
{ stageId: "validate", contributedBy: "default", isProtected: false },
|
|
12
|
+
{ stageId: "merge-gate", contributedBy: "default", isProtected: false }
|
|
13
|
+
],
|
|
14
|
+
cycles: [],
|
|
15
|
+
grantUses: [],
|
|
16
|
+
resolvedAt: "1970-01-01T00:00:00.000Z"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
|
|
20
|
+
const pipeline = sampleResolvedPipeline();
|
|
21
|
+
await journal.recordPipeline(runId, pipeline);
|
|
22
|
+
const events = await journal.read(runId);
|
|
23
|
+
if (!Array.isArray(events) || events.length === 0) {
|
|
24
|
+
throw new JournalConformanceError("journal.read returned no records after recordPipeline \u2014 provider does not durably record (append-and-record contract violated)");
|
|
25
|
+
}
|
|
26
|
+
const serialized = JSON.stringify(events);
|
|
27
|
+
if (!serialized.includes("merge-gate") || !serialized.includes("validate")) {
|
|
28
|
+
throw new JournalConformanceError("journal.read did not surface the recorded resolved-pipeline \u2014 provider does not expose the append-and-record contract");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
2
32
|
// packages/kernel-seed/src/pluginAbi.ts
|
|
3
33
|
function loadedPluginId(plugin) {
|
|
4
34
|
return plugin.meta.id;
|
|
@@ -110,6 +140,42 @@ function objectAt(value, key) {
|
|
|
110
140
|
function hasFunction(value, key) {
|
|
111
141
|
return value !== null && typeof value[key] === "function";
|
|
112
142
|
}
|
|
143
|
+
function assertSatisfiesStageRunnerInterface(value) {
|
|
144
|
+
if (!isRecord(value) || typeof value.resolve !== "function" || typeof value.runPipeline !== "function") {
|
|
145
|
+
throw new BootIncoherent("stage-runner provider must expose resolve and runPipeline");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function assertSatisfiesJournalInterface(value) {
|
|
149
|
+
if (!isRecord(value) || typeof value.append !== "function" || typeof value.recordPipeline !== "function" || typeof value.read !== "function") {
|
|
150
|
+
throw new BootIncoherent("journal provider must expose append, recordPipeline, and read");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function assertSatisfiesLoaderPolicyInterface(value) {
|
|
154
|
+
if (!isRecord(value) || typeof value.resolveCapability !== "function") {
|
|
155
|
+
throw new BootIncoherent("loader-policy provider must expose resolveCapability");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function assertSatisfiesTransportInterface(value) {
|
|
159
|
+
if (!isRecord(value) || typeof value.dispatch !== "function") {
|
|
160
|
+
throw new BootIncoherent("transport provider must expose dispatch");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function resolveRequiredCapability(plugins, capability, config, assertInterface) {
|
|
164
|
+
const resolution = resolveCapability(plugins, capability, config);
|
|
165
|
+
if (!resolution) {
|
|
166
|
+
throw new BootIncoherent(`no ${capability} provider resolved`);
|
|
167
|
+
}
|
|
168
|
+
assertInterface(resolution.value);
|
|
169
|
+
return resolution;
|
|
170
|
+
}
|
|
171
|
+
function assertSatisfiesKernelStartProvider(value) {
|
|
172
|
+
if (!isRecord(value)) {
|
|
173
|
+
throw new BootIncoherent("kernel provider did not return an object");
|
|
174
|
+
}
|
|
175
|
+
if (typeof value.start !== "function") {
|
|
176
|
+
throw new BootIncoherent("kernel provider must expose start");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
113
179
|
function assertSatisfiesKernelInterface(value) {
|
|
114
180
|
if (!isRecord(value)) {
|
|
115
181
|
throw new BootIncoherent("kernel provider did not return an object");
|
|
@@ -142,12 +208,41 @@ async function loadPlugins(config) {
|
|
|
142
208
|
}
|
|
143
209
|
async function boot(config) {
|
|
144
210
|
const plugins = await loadPlugins(config);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
211
|
+
const kernelResolution = resolveRequiredCapability(plugins, "kernel", config, assertSatisfiesKernelStartProvider);
|
|
212
|
+
const stageRunnerResolution = resolveRequiredCapability(plugins, "stage-runner", config, assertSatisfiesStageRunnerInterface);
|
|
213
|
+
const journalResolution = resolveRequiredCapability(plugins, "journal", config, assertSatisfiesJournalInterface);
|
|
214
|
+
const loaderPolicyResolution = resolveRequiredCapability(plugins, "loader-policy", config, assertSatisfiesLoaderPolicyInterface);
|
|
215
|
+
const transportResolution = resolveRequiredCapability(plugins, "transport", config, assertSatisfiesTransportInterface);
|
|
216
|
+
const kernel = kernelResolution.value;
|
|
217
|
+
const stageRunner = stageRunnerResolution.value;
|
|
218
|
+
const journal = journalResolution.value;
|
|
219
|
+
const loaderPolicy = loaderPolicyResolution.value;
|
|
220
|
+
const transport = transportResolution.value;
|
|
221
|
+
const composed = {
|
|
222
|
+
...kernel,
|
|
223
|
+
stageRunner,
|
|
224
|
+
journal,
|
|
225
|
+
loaderPolicy,
|
|
226
|
+
transport,
|
|
227
|
+
start(loadedPlugins) {
|
|
228
|
+
return kernel.start.call(composed, loadedPlugins);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
assertSatisfiesKernelInterface(composed);
|
|
232
|
+
if (config.verifyJournalConformance === true) {
|
|
233
|
+
await assertJournalConformance(composed.journal);
|
|
148
234
|
}
|
|
149
|
-
|
|
150
|
-
|
|
235
|
+
return {
|
|
236
|
+
kernel: composed,
|
|
237
|
+
plugins,
|
|
238
|
+
capabilityProviderIds: {
|
|
239
|
+
kernel: kernelResolution.plugin.meta.id,
|
|
240
|
+
"stage-runner": stageRunnerResolution.plugin.meta.id,
|
|
241
|
+
journal: journalResolution.plugin.meta.id,
|
|
242
|
+
"loader-policy": loaderPolicyResolution.plugin.meta.id,
|
|
243
|
+
transport: transportResolution.plugin.meta.id
|
|
244
|
+
}
|
|
245
|
+
};
|
|
151
246
|
}
|
|
152
247
|
export {
|
|
153
248
|
boot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/kernel-seed",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.137",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Irreducible Rig bootstrap seed, plugin ABI, and capability resolver.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -33,6 +33,6 @@
|
|
|
33
33
|
"module": "./dist/src/index.js",
|
|
34
34
|
"types": "./dist/src/index.d.ts",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
36
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.137"
|
|
37
37
|
}
|
|
38
38
|
}
|