@h-rig/kernel-seed 0.0.6-alpha.157 → 0.0.6-alpha.159
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/bootDefault.d.ts +39 -0
- package/dist/src/bootDefault.js +851 -0
- package/dist/src/defaultKernel.d.ts +31 -0
- package/dist/src/defaultKernel.js +687 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +642 -0
- package/dist/src/loaderPolicy.d.ts +22 -0
- package/dist/src/loaderPolicy.js +147 -0
- package/dist/src/resolver.d.ts +9 -0
- package/dist/src/resolver.js +425 -0
- package/dist/src/stageResolve.d.ts +77 -0
- package/dist/src/stageResolve.js +361 -0
- package/package.json +18 -2
package/dist/src/index.js
CHANGED
|
@@ -32,6 +32,7 @@ async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
|
|
|
32
32
|
function loadedPluginId(plugin) {
|
|
33
33
|
return plugin.name;
|
|
34
34
|
}
|
|
35
|
+
|
|
35
36
|
// packages/kernel-seed/src/resolveCapability.ts
|
|
36
37
|
function replacementMatches(spec, capability, providerId) {
|
|
37
38
|
if (typeof spec === "string")
|
|
@@ -124,6 +125,55 @@ function resolveCapability(plugins, capability, config = {}) {
|
|
|
124
125
|
function declaresReplacement(plugin, capability, providerId) {
|
|
125
126
|
return (plugin.replaces ?? []).some((spec) => replacementMatches(spec, capability, providerId));
|
|
126
127
|
}
|
|
128
|
+
|
|
129
|
+
// packages/kernel-seed/src/loaderPolicy.ts
|
|
130
|
+
function createLoaderPolicyCapability() {
|
|
131
|
+
return {
|
|
132
|
+
resolveCapability(plugins, capability, grants) {
|
|
133
|
+
const candidatePluginIds = plugins.filter((plugin) => (plugin.provides ?? []).includes(capability)).map((plugin) => plugin.name).sort();
|
|
134
|
+
const providers = plugins.map((plugin) => ({
|
|
135
|
+
name: plugin.name,
|
|
136
|
+
...plugin.provides ? { provides: plugin.provides } : {},
|
|
137
|
+
...plugin.replaces ? { replaces: plugin.replaces } : {}
|
|
138
|
+
}));
|
|
139
|
+
try {
|
|
140
|
+
const resolution = resolveCapability(providers, capability, {
|
|
141
|
+
...grants?.capabilityPrecedence ? { capabilityPrecedence: grants.capabilityPrecedence } : {}
|
|
142
|
+
});
|
|
143
|
+
if (!resolution) {
|
|
144
|
+
return {
|
|
145
|
+
capability,
|
|
146
|
+
status: "missing",
|
|
147
|
+
selectedPluginId: null,
|
|
148
|
+
candidatePluginIds,
|
|
149
|
+
precedenceUsed: false,
|
|
150
|
+
error: `No provider for ${capability}`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
capability,
|
|
155
|
+
status: "resolved",
|
|
156
|
+
selectedPluginId: resolution.plugin.name,
|
|
157
|
+
candidatePluginIds,
|
|
158
|
+
precedenceUsed: resolution.selectedBy === "precedence",
|
|
159
|
+
...resolution.replacedProviders.length > 0 ? { error: `Replaced providers: ${resolution.replacedProviders.join(", ")}` } : {}
|
|
160
|
+
};
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error instanceof AmbiguousCapabilityError) {
|
|
163
|
+
return {
|
|
164
|
+
capability,
|
|
165
|
+
status: "ambiguous",
|
|
166
|
+
selectedPluginId: null,
|
|
167
|
+
candidatePluginIds,
|
|
168
|
+
precedenceUsed: false,
|
|
169
|
+
error: `Ambiguous providers for ${capability}`
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
127
177
|
// packages/kernel-seed/src/seed.ts
|
|
128
178
|
class BootIncoherent extends Error {
|
|
129
179
|
name = "BootIncoherent";
|
|
@@ -245,14 +295,606 @@ async function boot(config) {
|
|
|
245
295
|
}
|
|
246
296
|
};
|
|
247
297
|
}
|
|
298
|
+
// packages/kernel-seed/src/stageResolve.ts
|
|
299
|
+
class PipelineUnresolvableError extends Error {
|
|
300
|
+
cycles;
|
|
301
|
+
contributors;
|
|
302
|
+
constructor(message, cycles, contributors) {
|
|
303
|
+
super(message);
|
|
304
|
+
this.name = "PipelineUnresolvableError";
|
|
305
|
+
this.cycles = cycles;
|
|
306
|
+
this.contributors = contributors;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function uniqueSorted(values) {
|
|
310
|
+
return Array.from(new Set(values)).sort((left, right) => left.localeCompare(right));
|
|
311
|
+
}
|
|
312
|
+
function wrapperForMutation(mutation) {
|
|
313
|
+
return "wrapper" in mutation ? mutation.wrapper : mutation.around;
|
|
314
|
+
}
|
|
315
|
+
function contributorOf(mutation) {
|
|
316
|
+
if (mutation.contributedBy?.trim())
|
|
317
|
+
return mutation.contributedBy;
|
|
318
|
+
if (mutation.op === "wrap") {
|
|
319
|
+
const wrapper = wrapperForMutation(mutation);
|
|
320
|
+
if (wrapper.id?.trim())
|
|
321
|
+
return wrapper.id;
|
|
322
|
+
}
|
|
323
|
+
return "anonymous";
|
|
324
|
+
}
|
|
325
|
+
function stableMutationCompare(left, right) {
|
|
326
|
+
const leftTarget = left.op === "insert" ? left.stage.id : left.id;
|
|
327
|
+
const rightTarget = right.op === "insert" ? right.stage.id : right.id;
|
|
328
|
+
const targetDelta = leftTarget.localeCompare(rightTarget);
|
|
329
|
+
if (targetDelta !== 0)
|
|
330
|
+
return targetDelta;
|
|
331
|
+
return contributorOf(left).localeCompare(contributorOf(right));
|
|
332
|
+
}
|
|
333
|
+
function ensureUniqueStageIds(stages) {
|
|
334
|
+
const seen = new Set;
|
|
335
|
+
for (const stage of stages) {
|
|
336
|
+
if (seen.has(stage.id)) {
|
|
337
|
+
throw new PipelineUnresolvableError(`Duplicate stage id: ${stage.id}`, [], []);
|
|
338
|
+
}
|
|
339
|
+
seen.add(stage.id);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function assertNoDuplicateMutationTargets(mutations, op) {
|
|
343
|
+
const seen = new Map;
|
|
344
|
+
for (const mutation of mutations.filter((entry) => entry.op === op)) {
|
|
345
|
+
const id = mutation.op === "insert" ? mutation.stage.id : mutation.id;
|
|
346
|
+
const previous = seen.get(id);
|
|
347
|
+
if (previous) {
|
|
348
|
+
throw new PipelineUnresolvableError(`Duplicate ${op} mutation for stage ${id}: ${previous}, ${contributorOf(mutation)}`, [], uniqueSorted([previous, contributorOf(mutation)]));
|
|
349
|
+
}
|
|
350
|
+
seen.set(id, contributorOf(mutation));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function mergeAnchors(current, incoming) {
|
|
354
|
+
return uniqueSorted([...current, ...incoming ?? []]);
|
|
355
|
+
}
|
|
356
|
+
function stagePriority(stage) {
|
|
357
|
+
return typeof stage.priority === "number" && Number.isFinite(stage.priority) ? stage.priority : 0;
|
|
358
|
+
}
|
|
359
|
+
function readyCompare(states) {
|
|
360
|
+
return (left, right) => {
|
|
361
|
+
const leftState = states.get(left);
|
|
362
|
+
const rightState = states.get(right);
|
|
363
|
+
const priorityDelta = stagePriority(rightState?.stage ?? { id: right }) - stagePriority(leftState?.stage ?? { id: left });
|
|
364
|
+
if (priorityDelta !== 0)
|
|
365
|
+
return priorityDelta;
|
|
366
|
+
if (leftState?.baseIndex !== null && rightState?.baseIndex !== null && leftState?.baseIndex !== rightState?.baseIndex) {
|
|
367
|
+
return (leftState?.baseIndex ?? 0) - (rightState?.baseIndex ?? 0);
|
|
368
|
+
}
|
|
369
|
+
if (leftState?.baseIndex !== null && rightState?.baseIndex === null)
|
|
370
|
+
return -1;
|
|
371
|
+
if (leftState?.baseIndex === null && rightState?.baseIndex !== null)
|
|
372
|
+
return 1;
|
|
373
|
+
return left.localeCompare(right);
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function findCycles(nodes, edges) {
|
|
377
|
+
const adjacency = new Map;
|
|
378
|
+
for (const node of nodes)
|
|
379
|
+
adjacency.set(node, []);
|
|
380
|
+
for (const edge of edges)
|
|
381
|
+
adjacency.get(edge.from)?.push(edge.to);
|
|
382
|
+
for (const targets of adjacency.values())
|
|
383
|
+
targets.sort((left, right) => left.localeCompare(right));
|
|
384
|
+
let nextIndex = 0;
|
|
385
|
+
const indexByNode = new Map;
|
|
386
|
+
const lowByNode = new Map;
|
|
387
|
+
const stack = [];
|
|
388
|
+
const onStack = new Set;
|
|
389
|
+
const cycles = [];
|
|
390
|
+
const visit = (node) => {
|
|
391
|
+
indexByNode.set(node, nextIndex);
|
|
392
|
+
lowByNode.set(node, nextIndex);
|
|
393
|
+
nextIndex += 1;
|
|
394
|
+
stack.push(node);
|
|
395
|
+
onStack.add(node);
|
|
396
|
+
for (const target of adjacency.get(node) ?? []) {
|
|
397
|
+
if (!indexByNode.has(target)) {
|
|
398
|
+
visit(target);
|
|
399
|
+
lowByNode.set(node, Math.min(lowByNode.get(node) ?? 0, lowByNode.get(target) ?? 0));
|
|
400
|
+
} else if (onStack.has(target)) {
|
|
401
|
+
lowByNode.set(node, Math.min(lowByNode.get(node) ?? 0, indexByNode.get(target) ?? 0));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (lowByNode.get(node) !== indexByNode.get(node))
|
|
405
|
+
return;
|
|
406
|
+
const component = [];
|
|
407
|
+
let current;
|
|
408
|
+
do {
|
|
409
|
+
current = stack.pop();
|
|
410
|
+
if (current) {
|
|
411
|
+
onStack.delete(current);
|
|
412
|
+
component.push(current);
|
|
413
|
+
}
|
|
414
|
+
} while (current && current !== node);
|
|
415
|
+
const hasSelfLoop = edges.some((edge) => edge.from === node && edge.to === node);
|
|
416
|
+
if (component.length > 1 || hasSelfLoop) {
|
|
417
|
+
cycles.push(component.sort((left, right) => left.localeCompare(right)));
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
for (const node of [...nodes].sort((left, right) => left.localeCompare(right))) {
|
|
421
|
+
if (!indexByNode.has(node))
|
|
422
|
+
visit(node);
|
|
423
|
+
}
|
|
424
|
+
return cycles.sort((left, right) => left.join("\x00").localeCompare(right.join("\x00")));
|
|
425
|
+
}
|
|
426
|
+
function topologicalOrder(states, edges) {
|
|
427
|
+
const nodes = [...states.keys()];
|
|
428
|
+
const outgoing = new Map;
|
|
429
|
+
const indegree = new Map;
|
|
430
|
+
for (const node of nodes) {
|
|
431
|
+
outgoing.set(node, []);
|
|
432
|
+
indegree.set(node, 0);
|
|
433
|
+
}
|
|
434
|
+
for (const edge of edges) {
|
|
435
|
+
outgoing.get(edge.from)?.push(edge.to);
|
|
436
|
+
indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);
|
|
437
|
+
}
|
|
438
|
+
for (const targets of outgoing.values())
|
|
439
|
+
targets.sort((left, right) => left.localeCompare(right));
|
|
440
|
+
const compare = readyCompare(states);
|
|
441
|
+
const ready = nodes.filter((node) => (indegree.get(node) ?? 0) === 0).sort(compare);
|
|
442
|
+
const order = [];
|
|
443
|
+
while (ready.length > 0) {
|
|
444
|
+
const node = ready.shift();
|
|
445
|
+
if (!node)
|
|
446
|
+
break;
|
|
447
|
+
order.push(node);
|
|
448
|
+
for (const target of outgoing.get(node) ?? []) {
|
|
449
|
+
const next = (indegree.get(target) ?? 0) - 1;
|
|
450
|
+
indegree.set(target, next);
|
|
451
|
+
if (next === 0) {
|
|
452
|
+
ready.push(target);
|
|
453
|
+
ready.sort(compare);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (order.length === nodes.length)
|
|
458
|
+
return order;
|
|
459
|
+
const cycles = findCycles(nodes, edges);
|
|
460
|
+
const cycleMembers = new Set(cycles.flat());
|
|
461
|
+
const contributors = uniqueSorted(edges.filter((edge) => cycleMembers.has(edge.from) && cycleMembers.has(edge.to)).flatMap((edge) => edge.contributors));
|
|
462
|
+
throw new PipelineUnresolvableError(`Stage pipeline has unresolved cycle: ${cycles.map((cycle) => cycle.join(" -> ")).join("; ")}`, cycles, contributors);
|
|
463
|
+
}
|
|
464
|
+
function composeStageWrappers(state) {
|
|
465
|
+
const wrappers = state.wrappers.toSorted((left, right) => {
|
|
466
|
+
const priorityDelta = (right.wrapper.priority ?? 0) - (left.wrapper.priority ?? 0);
|
|
467
|
+
if (priorityDelta !== 0)
|
|
468
|
+
return priorityDelta;
|
|
469
|
+
return left.contributedBy.localeCompare(right.contributedBy);
|
|
470
|
+
});
|
|
471
|
+
let stage = state.stage;
|
|
472
|
+
for (const wrapper of wrappers.toReversed()) {
|
|
473
|
+
if (wrapper.wrapper.apply)
|
|
474
|
+
stage = wrapper.wrapper.apply(stage);
|
|
475
|
+
}
|
|
476
|
+
return stage;
|
|
477
|
+
}
|
|
478
|
+
function resolveStagePipeline(input) {
|
|
479
|
+
ensureUniqueStageIds(input.defaultStages);
|
|
480
|
+
const mutations = [...input.mutations ?? []];
|
|
481
|
+
assertNoDuplicateMutationTargets(mutations, "insert");
|
|
482
|
+
assertNoDuplicateMutationTargets(mutations, "replace");
|
|
483
|
+
const states = new Map;
|
|
484
|
+
const removedStates = new Map;
|
|
485
|
+
for (const [index, stage] of input.defaultStages.entries()) {
|
|
486
|
+
states.set(stage.id, {
|
|
487
|
+
stage: { ...stage, protected: false },
|
|
488
|
+
before: [...stage.before ?? []],
|
|
489
|
+
after: [...stage.after ?? []],
|
|
490
|
+
baseIndex: index,
|
|
491
|
+
contributedBy: "default",
|
|
492
|
+
wrappers: [],
|
|
493
|
+
droppedAnchors: [],
|
|
494
|
+
isProtected: false
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
for (const mutation of mutations.filter((entry) => entry.op === "remove").sort(stableMutationCompare)) {
|
|
498
|
+
const state = states.get(mutation.id);
|
|
499
|
+
const contributor = contributorOf(mutation);
|
|
500
|
+
if (!state)
|
|
501
|
+
continue;
|
|
502
|
+
const removedState = {
|
|
503
|
+
...state,
|
|
504
|
+
removedBy: contributor
|
|
505
|
+
};
|
|
506
|
+
removedStates.set(mutation.id, removedState);
|
|
507
|
+
states.delete(mutation.id);
|
|
508
|
+
}
|
|
509
|
+
for (const mutation of mutations.filter((entry) => entry.op === "replace").sort(stableMutationCompare)) {
|
|
510
|
+
const state = states.get(mutation.id);
|
|
511
|
+
const contributor = contributorOf(mutation);
|
|
512
|
+
if (!state)
|
|
513
|
+
continue;
|
|
514
|
+
const replacement = { ...mutation.stage, id: mutation.id, protected: false };
|
|
515
|
+
states.set(mutation.id, {
|
|
516
|
+
...state,
|
|
517
|
+
stage: replacement,
|
|
518
|
+
before: mutation.stage.before ? [...mutation.stage.before] : state.before,
|
|
519
|
+
after: mutation.stage.after ? [...mutation.stage.after] : state.after,
|
|
520
|
+
replacedBy: contributor,
|
|
521
|
+
contributedBy: state.contributedBy,
|
|
522
|
+
isProtected: false
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
for (const mutation of mutations.filter((entry) => entry.op === "insert").sort(stableMutationCompare)) {
|
|
526
|
+
const contributor = contributorOf(mutation);
|
|
527
|
+
if (states.has(mutation.stage.id)) {
|
|
528
|
+
throw new PipelineUnresolvableError(`Inserted stage ${mutation.stage.id} conflicts with an existing stage`, [], [contributor]);
|
|
529
|
+
}
|
|
530
|
+
states.set(mutation.stage.id, {
|
|
531
|
+
stage: { ...mutation.stage, protected: false },
|
|
532
|
+
before: [...mutation.stage.before ?? []],
|
|
533
|
+
after: [...mutation.stage.after ?? []],
|
|
534
|
+
baseIndex: null,
|
|
535
|
+
contributedBy: contributor,
|
|
536
|
+
wrappers: [],
|
|
537
|
+
droppedAnchors: [],
|
|
538
|
+
isProtected: false
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
for (const mutation of mutations.filter((entry) => entry.op === "reorder").sort(stableMutationCompare)) {
|
|
542
|
+
const state = states.get(mutation.id);
|
|
543
|
+
if (!state)
|
|
544
|
+
continue;
|
|
545
|
+
states.set(mutation.id, {
|
|
546
|
+
...state,
|
|
547
|
+
before: mergeAnchors(state.before, mutation.before),
|
|
548
|
+
after: mergeAnchors(state.after, mutation.after)
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
for (const mutation of mutations.filter((entry) => entry.op === "wrap").sort(stableMutationCompare)) {
|
|
552
|
+
const state = states.get(mutation.id);
|
|
553
|
+
const contributor = contributorOf(mutation);
|
|
554
|
+
const wrapper = wrapperForMutation(mutation);
|
|
555
|
+
if (!state)
|
|
556
|
+
continue;
|
|
557
|
+
states.set(mutation.id, {
|
|
558
|
+
...state,
|
|
559
|
+
wrappers: [...state.wrappers, { contributedBy: contributor, wrapper }]
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
const contributorsForAnchor = (stageId, direction, anchor, state) => {
|
|
563
|
+
const contributors = new Set;
|
|
564
|
+
if (state.replacedBy)
|
|
565
|
+
contributors.add(state.replacedBy);
|
|
566
|
+
for (const mutation of mutations) {
|
|
567
|
+
if (mutation.op === "reorder" && mutation.id === stageId && (direction === "before" ? mutation.before : mutation.after)?.includes(anchor)) {
|
|
568
|
+
contributors.add(contributorOf(mutation));
|
|
569
|
+
}
|
|
570
|
+
if (mutation.op === "insert" && mutation.stage.id === stageId && (direction === "before" ? mutation.stage.before : mutation.stage.after)?.includes(anchor)) {
|
|
571
|
+
contributors.add(contributorOf(mutation));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (contributors.size === 0)
|
|
575
|
+
contributors.add(state.contributedBy);
|
|
576
|
+
return uniqueSorted(contributors);
|
|
577
|
+
};
|
|
578
|
+
const edges = [];
|
|
579
|
+
for (const [stageId, state] of states) {
|
|
580
|
+
const before = [];
|
|
581
|
+
const after = [];
|
|
582
|
+
for (const anchor of state.before) {
|
|
583
|
+
if (states.has(anchor))
|
|
584
|
+
before.push(anchor);
|
|
585
|
+
else {
|
|
586
|
+
const removed = removedStates.get(anchor);
|
|
587
|
+
state.droppedAnchors.push({
|
|
588
|
+
stageId,
|
|
589
|
+
anchor,
|
|
590
|
+
direction: "before",
|
|
591
|
+
reason: removed ? "removed" : "missing",
|
|
592
|
+
...removed?.removedBy ? { removedBy: removed.removedBy } : {}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
for (const anchor of state.after) {
|
|
597
|
+
if (states.has(anchor))
|
|
598
|
+
after.push(anchor);
|
|
599
|
+
else {
|
|
600
|
+
const removed = removedStates.get(anchor);
|
|
601
|
+
state.droppedAnchors.push({
|
|
602
|
+
stageId,
|
|
603
|
+
anchor,
|
|
604
|
+
direction: "after",
|
|
605
|
+
reason: removed ? "removed" : "missing",
|
|
606
|
+
...removed?.removedBy ? { removedBy: removed.removedBy } : {}
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
state.before = uniqueSorted(before);
|
|
611
|
+
state.after = uniqueSorted(after);
|
|
612
|
+
for (const target of state.before)
|
|
613
|
+
edges.push({ from: stageId, to: target, contributors: contributorsForAnchor(stageId, "before", target, state) });
|
|
614
|
+
for (const source of state.after)
|
|
615
|
+
edges.push({ from: source, to: stageId, contributors: contributorsForAnchor(stageId, "after", source, state) });
|
|
616
|
+
}
|
|
617
|
+
const order = topologicalOrder(states, edges);
|
|
618
|
+
const stages = [];
|
|
619
|
+
const record = [];
|
|
620
|
+
for (const id of order) {
|
|
621
|
+
const state = states.get(id);
|
|
622
|
+
if (!state)
|
|
623
|
+
continue;
|
|
624
|
+
stages.push(composeStageWrappers(state));
|
|
625
|
+
const wrappedBy = state.wrappers.toSorted((left, right) => {
|
|
626
|
+
const priorityDelta = (right.wrapper.priority ?? 0) - (left.wrapper.priority ?? 0);
|
|
627
|
+
if (priorityDelta !== 0)
|
|
628
|
+
return priorityDelta;
|
|
629
|
+
return left.contributedBy.localeCompare(right.contributedBy);
|
|
630
|
+
}).map((wrapper) => wrapper.contributedBy);
|
|
631
|
+
record.push({
|
|
632
|
+
stageId: id,
|
|
633
|
+
contributedBy: state.contributedBy,
|
|
634
|
+
...state.replacedBy ? { replacedBy: state.replacedBy } : {},
|
|
635
|
+
...wrappedBy.length > 0 ? { wrappedBy } : {},
|
|
636
|
+
...state.droppedAnchors.length > 0 ? { droppedAnchors: state.droppedAnchors.toSorted((left, right) => left.anchor.localeCompare(right.anchor)) } : {},
|
|
637
|
+
isProtected: state.isProtected
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
record.push(...[...removedStates.entries()].toSorted((left, right) => {
|
|
641
|
+
const leftIndex = left[1].baseIndex ?? Number.MAX_SAFE_INTEGER;
|
|
642
|
+
const rightIndex = right[1].baseIndex ?? Number.MAX_SAFE_INTEGER;
|
|
643
|
+
if (leftIndex !== rightIndex)
|
|
644
|
+
return leftIndex - rightIndex;
|
|
645
|
+
return left[0].localeCompare(right[0]);
|
|
646
|
+
}).map(([stageId, state]) => ({
|
|
647
|
+
stageId,
|
|
648
|
+
contributedBy: state.contributedBy,
|
|
649
|
+
...state.removedBy ? { removedBy: state.removedBy } : {},
|
|
650
|
+
isProtected: state.isProtected
|
|
651
|
+
})));
|
|
652
|
+
return { stages, order, record, cycles: [] };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// packages/kernel-seed/src/resolver.ts
|
|
656
|
+
function toCoreStage(stage) {
|
|
657
|
+
return {
|
|
658
|
+
id: String(stage.id),
|
|
659
|
+
kind: stage.kind,
|
|
660
|
+
priority: stage.priority,
|
|
661
|
+
protected: false,
|
|
662
|
+
...stage.before !== undefined ? { before: stage.before.map(String) } : {},
|
|
663
|
+
...stage.after !== undefined ? { after: stage.after.map(String) } : {}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function coreMutation(mutation) {
|
|
667
|
+
const contributedBy = typeof mutation.contributedBy === "string" && mutation.contributedBy.length > 0 ? mutation.contributedBy : "anonymous";
|
|
668
|
+
switch (mutation.op) {
|
|
669
|
+
case "insert":
|
|
670
|
+
return { op: "insert", stage: toCoreStage(mutation.stage), contributedBy };
|
|
671
|
+
case "remove":
|
|
672
|
+
return { op: "remove", id: String(mutation.id), contributedBy };
|
|
673
|
+
case "replace":
|
|
674
|
+
return { op: "replace", id: String(mutation.id), stage: toCoreStage(mutation.stage), contributedBy };
|
|
675
|
+
case "wrap":
|
|
676
|
+
return { op: "wrap", id: String(mutation.id), wrapper: { priority: mutation.around.priority }, contributedBy };
|
|
677
|
+
case "reorder":
|
|
678
|
+
return {
|
|
679
|
+
op: "reorder",
|
|
680
|
+
id: String(mutation.id),
|
|
681
|
+
contributedBy,
|
|
682
|
+
...mutation.before !== undefined ? { before: mutation.before.map(String) } : {},
|
|
683
|
+
...mutation.after !== undefined ? { after: mutation.after.map(String) } : {}
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function resolutionRecordEntry(entry) {
|
|
688
|
+
return {
|
|
689
|
+
stageId: entry.stageId,
|
|
690
|
+
contributedBy: entry.contributedBy,
|
|
691
|
+
...entry.removedBy !== undefined ? { removedBy: entry.removedBy } : {},
|
|
692
|
+
...entry.replacedBy !== undefined ? { replacedBy: entry.replacedBy } : {},
|
|
693
|
+
...entry.wrappedBy !== undefined ? { wrappedBy: [...entry.wrappedBy] } : {},
|
|
694
|
+
...entry.droppedAnchors !== undefined ? { droppedAnchors: entry.droppedAnchors.map((anchor) => anchor.anchor) } : {},
|
|
695
|
+
isProtected: entry.isProtected
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function resolvedPipeline(input) {
|
|
699
|
+
const mutations = input.mutations ?? [];
|
|
700
|
+
const resolved = resolveStagePipeline({
|
|
701
|
+
defaultStages: input.defaultStages.map(toCoreStage),
|
|
702
|
+
mutations: mutations.map(coreMutation)
|
|
703
|
+
});
|
|
704
|
+
return {
|
|
705
|
+
order: resolved.order.map((stageId) => stageId),
|
|
706
|
+
record: resolved.record.map(resolutionRecordEntry),
|
|
707
|
+
cycles: resolved.cycles.map((cycle) => cycle.map((stageId) => stageId)),
|
|
708
|
+
grantUses: [],
|
|
709
|
+
resolvedAt: new Date().toISOString()
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function resolveKernelStages(defaultStages, mutations = [], grants = {}) {
|
|
713
|
+
return resolvedPipeline({ defaultStages, mutations, grants });
|
|
714
|
+
}
|
|
715
|
+
function resolveKernelStagePipeline(input) {
|
|
716
|
+
return resolvedPipeline(input);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// packages/kernel-seed/src/defaultKernel.ts
|
|
720
|
+
var DEFAULT_KERNEL_PLUGIN_ID = "@rig/kernel-seed/default";
|
|
721
|
+
function createInMemoryJournalProvider(runId) {
|
|
722
|
+
const records = [];
|
|
723
|
+
const eventRunId = (event) => {
|
|
724
|
+
if (event !== null && typeof event === "object") {
|
|
725
|
+
const candidate = event.runId;
|
|
726
|
+
if (typeof candidate === "string" && candidate.length > 0)
|
|
727
|
+
return candidate;
|
|
728
|
+
}
|
|
729
|
+
return runId;
|
|
730
|
+
};
|
|
731
|
+
return {
|
|
732
|
+
async append(event) {
|
|
733
|
+
records.push({ runId: eventRunId(event), event });
|
|
734
|
+
},
|
|
735
|
+
async recordPipeline(targetRunId, pipeline) {
|
|
736
|
+
records.push({ runId: targetRunId, event: { type: "pipeline-resolved", pipeline } });
|
|
737
|
+
},
|
|
738
|
+
async recordStageOutcome(targetRunId, outcome) {
|
|
739
|
+
records.push({ runId: targetRunId, event: { type: "stage-outcome", outcome } });
|
|
740
|
+
},
|
|
741
|
+
async read(targetRunId) {
|
|
742
|
+
return records.filter((record) => record.runId === targetRunId).map((record) => record.event);
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function createDefaultTransport() {
|
|
747
|
+
return {
|
|
748
|
+
async dispatch() {
|
|
749
|
+
throw new Error("Default kernel transport is not bound to a live OMP/collab dispatcher yet.");
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function createDefaultKernel(options = {}) {
|
|
754
|
+
const journal = options.journal ?? createInMemoryJournalProvider("default-kernel-bootstrap");
|
|
755
|
+
const stageRunner = {
|
|
756
|
+
resolve(defaultStages, mutations, grants) {
|
|
757
|
+
return resolveKernelStages(defaultStages, mutations, grants ?? {});
|
|
758
|
+
},
|
|
759
|
+
async runPipeline(runId, resolved, ctx) {
|
|
760
|
+
await journal.recordPipeline(runId, resolved);
|
|
761
|
+
const outcomes = [];
|
|
762
|
+
let currentCtx = ctx;
|
|
763
|
+
for (const stageId of resolved.order) {
|
|
764
|
+
const startedAt = new Date().toISOString();
|
|
765
|
+
const executor = options.stageExecutors?.[stageId];
|
|
766
|
+
const result = executor ? await executor(currentCtx) : { kind: "continue", ctx: currentCtx };
|
|
767
|
+
const stageOutcome = { stageId, result, startedAt, finishedAt: new Date().toISOString() };
|
|
768
|
+
outcomes.push(stageOutcome);
|
|
769
|
+
await journal.recordStageOutcome?.(runId, stageOutcome);
|
|
770
|
+
if (result.kind === "continue") {
|
|
771
|
+
currentCtx = result.ctx;
|
|
772
|
+
} else if (result.kind === "block") {
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return outcomes;
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
const boundTransport = options.transport ?? createDefaultTransport();
|
|
780
|
+
const transport = {
|
|
781
|
+
dispatch: (task, opts) => boundTransport.dispatch(task, opts),
|
|
782
|
+
attach: (runId) => boundTransport.attach ? boundTransport.attach(runId) : Promise.reject(new Error("transport.attach not supported")),
|
|
783
|
+
serve: () => boundTransport.serve ? boundTransport.serve() : Promise.resolve()
|
|
784
|
+
};
|
|
785
|
+
return {
|
|
786
|
+
id: DEFAULT_KERNEL_PLUGIN_ID,
|
|
787
|
+
stageRunner,
|
|
788
|
+
journal,
|
|
789
|
+
transport,
|
|
790
|
+
loaderPolicy: createLoaderPolicyCapability(),
|
|
791
|
+
async start(_plugins) {}
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
var LOCAL_TRANSPORT_PLUGIN_ID = "@rig/kernel-seed/local-transport";
|
|
795
|
+
var PLACEMENT_TRANSPORT_PLUGIN_ID = "@rig/kernel-seed/placement-transport";
|
|
796
|
+
function createTransportProviderPlugin(input) {
|
|
797
|
+
return {
|
|
798
|
+
name: input.id,
|
|
799
|
+
version: "0.0.0-alpha.1",
|
|
800
|
+
provides: ["transport"],
|
|
801
|
+
replaces: input.replaces,
|
|
802
|
+
capabilityProviders: { transport: input.transport }
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function createLocalTransportPlugin(transport) {
|
|
806
|
+
return createTransportProviderPlugin({
|
|
807
|
+
id: LOCAL_TRANSPORT_PLUGIN_ID,
|
|
808
|
+
transport,
|
|
809
|
+
replaces: [`transport@${DEFAULT_KERNEL_PLUGIN_ID}`]
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
function createPlacementTransportPlugin(transport, _options = {}) {
|
|
813
|
+
return createTransportProviderPlugin({
|
|
814
|
+
id: PLACEMENT_TRANSPORT_PLUGIN_ID,
|
|
815
|
+
transport,
|
|
816
|
+
replaces: [`transport@${DEFAULT_KERNEL_PLUGIN_ID}`, `transport@${LOCAL_TRANSPORT_PLUGIN_ID}`]
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
function createDefaultKernelPlugin(options = {}) {
|
|
820
|
+
const kernel = createDefaultKernel(options);
|
|
821
|
+
return {
|
|
822
|
+
name: DEFAULT_KERNEL_PLUGIN_ID,
|
|
823
|
+
version: "0.0.0-alpha.1",
|
|
824
|
+
provides: ["kernel", "stage-runner", "journal", "transport", "loader-policy"],
|
|
825
|
+
capabilityProviders: {
|
|
826
|
+
kernel,
|
|
827
|
+
"stage-runner": kernel.stageRunner,
|
|
828
|
+
journal: kernel.journal,
|
|
829
|
+
transport: kernel.transport,
|
|
830
|
+
"loader-policy": kernel.loaderPolicy
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// packages/kernel-seed/src/bootDefault.ts
|
|
836
|
+
var RIG_KERNEL_BOOT_MARKER = "__RIG_DEFAULT_KERNEL_BOOT__";
|
|
837
|
+
async function bootDefaultKernel(input = {}) {
|
|
838
|
+
const defaultPlugin = createDefaultKernelPlugin(input);
|
|
839
|
+
return await boot({
|
|
840
|
+
...input.config ?? {},
|
|
841
|
+
plugins: [defaultPlugin, ...input.extraPlugins ?? []]
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
async function bootDefaultKernelIntoProcess(input = {}) {
|
|
845
|
+
const existing = globalThis[RIG_KERNEL_BOOT_MARKER];
|
|
846
|
+
if (existing) {
|
|
847
|
+
return existing.record;
|
|
848
|
+
}
|
|
849
|
+
const booted = await bootDefaultKernel(input);
|
|
850
|
+
const record = {
|
|
851
|
+
providerId: booted.capabilityProviderIds.kernel,
|
|
852
|
+
entrypoint: input.entrypoint ?? "unknown",
|
|
853
|
+
bootedAt: new Date().toISOString(),
|
|
854
|
+
pluginIds: booted.plugins.map((plugin) => plugin.name),
|
|
855
|
+
capabilityProviderIds: booted.capabilityProviderIds
|
|
856
|
+
};
|
|
857
|
+
await booted.kernel.start(booted.plugins);
|
|
858
|
+
globalThis[RIG_KERNEL_BOOT_MARKER] = { record, kernel: booted.kernel, plugins: booted.plugins };
|
|
859
|
+
process.env.RIG_DEFAULT_KERNEL_PROVIDER_ID = record.providerId;
|
|
860
|
+
process.env.RIG_DEFAULT_KERNEL_BOOT_ENTRYPOINT = record.entrypoint;
|
|
861
|
+
return record;
|
|
862
|
+
}
|
|
863
|
+
function readDefaultKernelBootRecord() {
|
|
864
|
+
return globalThis[RIG_KERNEL_BOOT_MARKER]?.record ?? null;
|
|
865
|
+
}
|
|
866
|
+
function getProcessKernel() {
|
|
867
|
+
return globalThis[RIG_KERNEL_BOOT_MARKER]?.kernel ?? null;
|
|
868
|
+
}
|
|
869
|
+
function readDefaultKernelBootState() {
|
|
870
|
+
return globalThis[RIG_KERNEL_BOOT_MARKER] ?? null;
|
|
871
|
+
}
|
|
248
872
|
export {
|
|
873
|
+
resolveStagePipeline,
|
|
874
|
+
resolveKernelStages,
|
|
875
|
+
resolveKernelStagePipeline,
|
|
249
876
|
resolveCapability,
|
|
877
|
+
readDefaultKernelBootState,
|
|
878
|
+
readDefaultKernelBootRecord,
|
|
250
879
|
loadedPluginId,
|
|
880
|
+
getProcessKernel,
|
|
251
881
|
declaresReplacement,
|
|
882
|
+
createPlacementTransportPlugin,
|
|
883
|
+
createLocalTransportPlugin,
|
|
884
|
+
createLoaderPolicyCapability,
|
|
885
|
+
createDefaultKernelPlugin,
|
|
886
|
+
createDefaultKernel,
|
|
887
|
+
bootDefaultKernelIntoProcess,
|
|
888
|
+
bootDefaultKernel,
|
|
252
889
|
boot,
|
|
253
890
|
assertSatisfiesKernelInterface,
|
|
254
891
|
assertJournalConformance,
|
|
892
|
+
RIG_KERNEL_BOOT_MARKER,
|
|
893
|
+
PipelineUnresolvableError,
|
|
894
|
+
PLACEMENT_TRANSPORT_PLUGIN_ID,
|
|
895
|
+
LOCAL_TRANSPORT_PLUGIN_ID,
|
|
255
896
|
JournalConformanceError,
|
|
897
|
+
DEFAULT_KERNEL_PLUGIN_ID,
|
|
256
898
|
CapabilityProviderMissingError,
|
|
257
899
|
BootIncoherent,
|
|
258
900
|
AmbiguousCapabilityError
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LoaderPolicyCapability } from "@rig/contracts";
|
|
2
|
+
/**
|
|
3
|
+
* THE canonical `loader-policy` capability factory (doc §2).
|
|
4
|
+
*
|
|
5
|
+
* It owns NO resolution logic of its own: the precedence / `replaces` /
|
|
6
|
+
* sole-provider / ambiguity decision is made exclusively by the single
|
|
7
|
+
* {@link resolveCapability} resolver. This factory is a thin shape-adapter that
|
|
8
|
+
* translates that resolver's outcome (resolution | null | thrown
|
|
9
|
+
* `AmbiguousCapabilityError`) into the non-throwing `CapabilityResolutionRecord`
|
|
10
|
+
* report shape the `LoaderPolicyCapability` contract expects.
|
|
11
|
+
*
|
|
12
|
+
* A `RigPlugin` is structurally a `CapabilityProviderPlugin`, so the manifest
|
|
13
|
+
* `plugins` are normalized (dropping the `| undefined` that `Schema.optional`
|
|
14
|
+
* emits, which `exactOptionalPropertyTypes` rejects) and passed straight
|
|
15
|
+
* through. The loader-policy report only needs provider IDENTITY/`provides`/
|
|
16
|
+
* `replaces`; capability VALUE resolution is N/A on this manifest path.
|
|
17
|
+
*
|
|
18
|
+
* `candidatePluginIds` reports ALL providers that declare the capability
|
|
19
|
+
* (sorted) — a diagnostic on the record, not part of selection. The SELECTION
|
|
20
|
+
* is whatever `resolveCapability` returns.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createLoaderPolicyCapability(): LoaderPolicyCapability;
|