@h-rig/kernel-seed 0.0.6-alpha.157 → 0.0.6-alpha.158

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.
@@ -0,0 +1,147 @@
1
+ // @bun
2
+ // packages/kernel-seed/src/pluginAbi.ts
3
+ function loadedPluginId(plugin) {
4
+ return plugin.name;
5
+ }
6
+
7
+ // packages/kernel-seed/src/resolveCapability.ts
8
+ function replacementMatches(spec, capability, providerId) {
9
+ if (typeof spec === "string")
10
+ return spec === `${capability}@${providerId}`;
11
+ return spec.capability === capability && spec.providerPluginId === providerId;
12
+ }
13
+
14
+ class AmbiguousCapabilityError extends Error {
15
+ capability;
16
+ providers;
17
+ name = "AmbiguousCapabilityError";
18
+ constructor(capability, providers) {
19
+ super(`Ambiguous capability provider for ${capability}: ${providers.join(", ")}`);
20
+ this.capability = capability;
21
+ this.providers = providers;
22
+ }
23
+ }
24
+
25
+ class CapabilityProviderMissingError extends Error {
26
+ capability;
27
+ name = "CapabilityProviderMissingError";
28
+ constructor(capability) {
29
+ super(`No provider resolved for capability ${capability}`);
30
+ this.capability = capability;
31
+ }
32
+ }
33
+ function capabilityValue(plugin, capability) {
34
+ return plugin.capabilityProviders?.[capability];
35
+ }
36
+ function capabilityProviders(plugins, capability) {
37
+ return plugins.filter((plugin) => (plugin.provides ?? []).includes(capability));
38
+ }
39
+ function byPrecedence(providers, precedence) {
40
+ if (!precedence || precedence.length === 0)
41
+ return null;
42
+ const byId = new Map(providers.map((provider) => [loadedPluginId(provider), provider]));
43
+ for (const id of precedence) {
44
+ const provider = byId.get(id);
45
+ if (provider)
46
+ return provider;
47
+ }
48
+ return null;
49
+ }
50
+ function providersAfterReplacement(capability, providers) {
51
+ const providerIds = new Set(providers.map(loadedPluginId));
52
+ const replaced = new Set;
53
+ for (const provider of providers) {
54
+ for (const replacement of provider.replaces ?? []) {
55
+ for (const candidateId of providerIds) {
56
+ if (replacementMatches(replacement, capability, candidateId)) {
57
+ replaced.add(candidateId);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ return {
63
+ providers: providers.filter((provider) => !replaced.has(loadedPluginId(provider))),
64
+ replaced: [...replaced].sort()
65
+ };
66
+ }
67
+ function resolveCapability(plugins, capability, config = {}) {
68
+ const providers = capabilityProviders(plugins, capability);
69
+ if (providers.length === 0)
70
+ return null;
71
+ const precedenceProvider = byPrecedence(providers, config.capabilityPrecedence?.[capability]);
72
+ if (precedenceProvider) {
73
+ return {
74
+ capability,
75
+ plugin: precedenceProvider,
76
+ value: capabilityValue(precedenceProvider, capability),
77
+ selectedBy: "precedence",
78
+ replacedProviders: []
79
+ };
80
+ }
81
+ const afterReplacement = providersAfterReplacement(capability, providers);
82
+ if (afterReplacement.providers.length === 1) {
83
+ const [provider] = afterReplacement.providers;
84
+ if (!provider)
85
+ throw new CapabilityProviderMissingError(capability);
86
+ return {
87
+ capability,
88
+ plugin: provider,
89
+ value: capabilityValue(provider, capability),
90
+ selectedBy: afterReplacement.replaced.length > 0 ? "replaces" : "sole-provider",
91
+ replacedProviders: afterReplacement.replaced
92
+ };
93
+ }
94
+ throw new AmbiguousCapabilityError(capability, afterReplacement.providers.map(loadedPluginId).sort());
95
+ }
96
+
97
+ // packages/kernel-seed/src/loaderPolicy.ts
98
+ function createLoaderPolicyCapability() {
99
+ return {
100
+ resolveCapability(plugins, capability, grants) {
101
+ const candidatePluginIds = plugins.filter((plugin) => (plugin.provides ?? []).includes(capability)).map((plugin) => plugin.name).sort();
102
+ const providers = plugins.map((plugin) => ({
103
+ name: plugin.name,
104
+ ...plugin.provides ? { provides: plugin.provides } : {},
105
+ ...plugin.replaces ? { replaces: plugin.replaces } : {}
106
+ }));
107
+ try {
108
+ const resolution = resolveCapability(providers, capability, {
109
+ ...grants?.capabilityPrecedence ? { capabilityPrecedence: grants.capabilityPrecedence } : {}
110
+ });
111
+ if (!resolution) {
112
+ return {
113
+ capability,
114
+ status: "missing",
115
+ selectedPluginId: null,
116
+ candidatePluginIds,
117
+ precedenceUsed: false,
118
+ error: `No provider for ${capability}`
119
+ };
120
+ }
121
+ return {
122
+ capability,
123
+ status: "resolved",
124
+ selectedPluginId: resolution.plugin.name,
125
+ candidatePluginIds,
126
+ precedenceUsed: resolution.selectedBy === "precedence",
127
+ ...resolution.replacedProviders.length > 0 ? { error: `Replaced providers: ${resolution.replacedProviders.join(", ")}` } : {}
128
+ };
129
+ } catch (error) {
130
+ if (error instanceof AmbiguousCapabilityError) {
131
+ return {
132
+ capability,
133
+ status: "ambiguous",
134
+ selectedPluginId: null,
135
+ candidatePluginIds,
136
+ precedenceUsed: false,
137
+ error: `Ambiguous providers for ${capability}`
138
+ };
139
+ }
140
+ throw error;
141
+ }
142
+ }
143
+ };
144
+ }
145
+ export {
146
+ createLoaderPolicyCapability
147
+ };
@@ -0,0 +1,9 @@
1
+ import { type CapabilityGrants, type ResolvedPipeline, type Stage, type StageMutation } from "@rig/contracts";
2
+ export type KernelStageResolverInput = {
3
+ readonly defaultStages: readonly Stage[];
4
+ readonly mutations?: readonly StageMutation[];
5
+ readonly grants?: CapabilityGrants;
6
+ };
7
+ export type KernelStageResolverResult = ResolvedPipeline;
8
+ export declare function resolveKernelStages(defaultStages: readonly Stage[], mutations?: readonly StageMutation[], grants?: CapabilityGrants): KernelStageResolverResult;
9
+ export declare function resolveKernelStagePipeline(input: KernelStageResolverInput): KernelStageResolverResult;
@@ -0,0 +1,425 @@
1
+ // @bun
2
+ // packages/kernel-seed/src/stageResolve.ts
3
+ class PipelineUnresolvableError extends Error {
4
+ cycles;
5
+ contributors;
6
+ constructor(message, cycles, contributors) {
7
+ super(message);
8
+ this.name = "PipelineUnresolvableError";
9
+ this.cycles = cycles;
10
+ this.contributors = contributors;
11
+ }
12
+ }
13
+ function uniqueSorted(values) {
14
+ return Array.from(new Set(values)).sort((left, right) => left.localeCompare(right));
15
+ }
16
+ function wrapperForMutation(mutation) {
17
+ return "wrapper" in mutation ? mutation.wrapper : mutation.around;
18
+ }
19
+ function contributorOf(mutation) {
20
+ if (mutation.contributedBy?.trim())
21
+ return mutation.contributedBy;
22
+ if (mutation.op === "wrap") {
23
+ const wrapper = wrapperForMutation(mutation);
24
+ if (wrapper.id?.trim())
25
+ return wrapper.id;
26
+ }
27
+ return "anonymous";
28
+ }
29
+ function stableMutationCompare(left, right) {
30
+ const leftTarget = left.op === "insert" ? left.stage.id : left.id;
31
+ const rightTarget = right.op === "insert" ? right.stage.id : right.id;
32
+ const targetDelta = leftTarget.localeCompare(rightTarget);
33
+ if (targetDelta !== 0)
34
+ return targetDelta;
35
+ return contributorOf(left).localeCompare(contributorOf(right));
36
+ }
37
+ function ensureUniqueStageIds(stages) {
38
+ const seen = new Set;
39
+ for (const stage of stages) {
40
+ if (seen.has(stage.id)) {
41
+ throw new PipelineUnresolvableError(`Duplicate stage id: ${stage.id}`, [], []);
42
+ }
43
+ seen.add(stage.id);
44
+ }
45
+ }
46
+ function assertNoDuplicateMutationTargets(mutations, op) {
47
+ const seen = new Map;
48
+ for (const mutation of mutations.filter((entry) => entry.op === op)) {
49
+ const id = mutation.op === "insert" ? mutation.stage.id : mutation.id;
50
+ const previous = seen.get(id);
51
+ if (previous) {
52
+ throw new PipelineUnresolvableError(`Duplicate ${op} mutation for stage ${id}: ${previous}, ${contributorOf(mutation)}`, [], uniqueSorted([previous, contributorOf(mutation)]));
53
+ }
54
+ seen.set(id, contributorOf(mutation));
55
+ }
56
+ }
57
+ function mergeAnchors(current, incoming) {
58
+ return uniqueSorted([...current, ...incoming ?? []]);
59
+ }
60
+ function stagePriority(stage) {
61
+ return typeof stage.priority === "number" && Number.isFinite(stage.priority) ? stage.priority : 0;
62
+ }
63
+ function readyCompare(states) {
64
+ return (left, right) => {
65
+ const leftState = states.get(left);
66
+ const rightState = states.get(right);
67
+ const priorityDelta = stagePriority(rightState?.stage ?? { id: right }) - stagePriority(leftState?.stage ?? { id: left });
68
+ if (priorityDelta !== 0)
69
+ return priorityDelta;
70
+ if (leftState?.baseIndex !== null && rightState?.baseIndex !== null && leftState?.baseIndex !== rightState?.baseIndex) {
71
+ return (leftState?.baseIndex ?? 0) - (rightState?.baseIndex ?? 0);
72
+ }
73
+ if (leftState?.baseIndex !== null && rightState?.baseIndex === null)
74
+ return -1;
75
+ if (leftState?.baseIndex === null && rightState?.baseIndex !== null)
76
+ return 1;
77
+ return left.localeCompare(right);
78
+ };
79
+ }
80
+ function findCycles(nodes, edges) {
81
+ const adjacency = new Map;
82
+ for (const node of nodes)
83
+ adjacency.set(node, []);
84
+ for (const edge of edges)
85
+ adjacency.get(edge.from)?.push(edge.to);
86
+ for (const targets of adjacency.values())
87
+ targets.sort((left, right) => left.localeCompare(right));
88
+ let nextIndex = 0;
89
+ const indexByNode = new Map;
90
+ const lowByNode = new Map;
91
+ const stack = [];
92
+ const onStack = new Set;
93
+ const cycles = [];
94
+ const visit = (node) => {
95
+ indexByNode.set(node, nextIndex);
96
+ lowByNode.set(node, nextIndex);
97
+ nextIndex += 1;
98
+ stack.push(node);
99
+ onStack.add(node);
100
+ for (const target of adjacency.get(node) ?? []) {
101
+ if (!indexByNode.has(target)) {
102
+ visit(target);
103
+ lowByNode.set(node, Math.min(lowByNode.get(node) ?? 0, lowByNode.get(target) ?? 0));
104
+ } else if (onStack.has(target)) {
105
+ lowByNode.set(node, Math.min(lowByNode.get(node) ?? 0, indexByNode.get(target) ?? 0));
106
+ }
107
+ }
108
+ if (lowByNode.get(node) !== indexByNode.get(node))
109
+ return;
110
+ const component = [];
111
+ let current;
112
+ do {
113
+ current = stack.pop();
114
+ if (current) {
115
+ onStack.delete(current);
116
+ component.push(current);
117
+ }
118
+ } while (current && current !== node);
119
+ const hasSelfLoop = edges.some((edge) => edge.from === node && edge.to === node);
120
+ if (component.length > 1 || hasSelfLoop) {
121
+ cycles.push(component.sort((left, right) => left.localeCompare(right)));
122
+ }
123
+ };
124
+ for (const node of [...nodes].sort((left, right) => left.localeCompare(right))) {
125
+ if (!indexByNode.has(node))
126
+ visit(node);
127
+ }
128
+ return cycles.sort((left, right) => left.join("\x00").localeCompare(right.join("\x00")));
129
+ }
130
+ function topologicalOrder(states, edges) {
131
+ const nodes = [...states.keys()];
132
+ const outgoing = new Map;
133
+ const indegree = new Map;
134
+ for (const node of nodes) {
135
+ outgoing.set(node, []);
136
+ indegree.set(node, 0);
137
+ }
138
+ for (const edge of edges) {
139
+ outgoing.get(edge.from)?.push(edge.to);
140
+ indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);
141
+ }
142
+ for (const targets of outgoing.values())
143
+ targets.sort((left, right) => left.localeCompare(right));
144
+ const compare = readyCompare(states);
145
+ const ready = nodes.filter((node) => (indegree.get(node) ?? 0) === 0).sort(compare);
146
+ const order = [];
147
+ while (ready.length > 0) {
148
+ const node = ready.shift();
149
+ if (!node)
150
+ break;
151
+ order.push(node);
152
+ for (const target of outgoing.get(node) ?? []) {
153
+ const next = (indegree.get(target) ?? 0) - 1;
154
+ indegree.set(target, next);
155
+ if (next === 0) {
156
+ ready.push(target);
157
+ ready.sort(compare);
158
+ }
159
+ }
160
+ }
161
+ if (order.length === nodes.length)
162
+ return order;
163
+ const cycles = findCycles(nodes, edges);
164
+ const cycleMembers = new Set(cycles.flat());
165
+ const contributors = uniqueSorted(edges.filter((edge) => cycleMembers.has(edge.from) && cycleMembers.has(edge.to)).flatMap((edge) => edge.contributors));
166
+ throw new PipelineUnresolvableError(`Stage pipeline has unresolved cycle: ${cycles.map((cycle) => cycle.join(" -> ")).join("; ")}`, cycles, contributors);
167
+ }
168
+ function composeStageWrappers(state) {
169
+ const wrappers = state.wrappers.toSorted((left, right) => {
170
+ const priorityDelta = (right.wrapper.priority ?? 0) - (left.wrapper.priority ?? 0);
171
+ if (priorityDelta !== 0)
172
+ return priorityDelta;
173
+ return left.contributedBy.localeCompare(right.contributedBy);
174
+ });
175
+ let stage = state.stage;
176
+ for (const wrapper of wrappers.toReversed()) {
177
+ if (wrapper.wrapper.apply)
178
+ stage = wrapper.wrapper.apply(stage);
179
+ }
180
+ return stage;
181
+ }
182
+ function resolveStagePipeline(input) {
183
+ ensureUniqueStageIds(input.defaultStages);
184
+ const mutations = [...input.mutations ?? []];
185
+ assertNoDuplicateMutationTargets(mutations, "insert");
186
+ assertNoDuplicateMutationTargets(mutations, "replace");
187
+ const states = new Map;
188
+ const removedStates = new Map;
189
+ for (const [index, stage] of input.defaultStages.entries()) {
190
+ states.set(stage.id, {
191
+ stage: { ...stage, protected: false },
192
+ before: [...stage.before ?? []],
193
+ after: [...stage.after ?? []],
194
+ baseIndex: index,
195
+ contributedBy: "default",
196
+ wrappers: [],
197
+ droppedAnchors: [],
198
+ isProtected: false
199
+ });
200
+ }
201
+ for (const mutation of mutations.filter((entry) => entry.op === "remove").sort(stableMutationCompare)) {
202
+ const state = states.get(mutation.id);
203
+ const contributor = contributorOf(mutation);
204
+ if (!state)
205
+ continue;
206
+ const removedState = {
207
+ ...state,
208
+ removedBy: contributor
209
+ };
210
+ removedStates.set(mutation.id, removedState);
211
+ states.delete(mutation.id);
212
+ }
213
+ for (const mutation of mutations.filter((entry) => entry.op === "replace").sort(stableMutationCompare)) {
214
+ const state = states.get(mutation.id);
215
+ const contributor = contributorOf(mutation);
216
+ if (!state)
217
+ continue;
218
+ const replacement = { ...mutation.stage, id: mutation.id, protected: false };
219
+ states.set(mutation.id, {
220
+ ...state,
221
+ stage: replacement,
222
+ before: mutation.stage.before ? [...mutation.stage.before] : state.before,
223
+ after: mutation.stage.after ? [...mutation.stage.after] : state.after,
224
+ replacedBy: contributor,
225
+ contributedBy: state.contributedBy,
226
+ isProtected: false
227
+ });
228
+ }
229
+ for (const mutation of mutations.filter((entry) => entry.op === "insert").sort(stableMutationCompare)) {
230
+ const contributor = contributorOf(mutation);
231
+ if (states.has(mutation.stage.id)) {
232
+ throw new PipelineUnresolvableError(`Inserted stage ${mutation.stage.id} conflicts with an existing stage`, [], [contributor]);
233
+ }
234
+ states.set(mutation.stage.id, {
235
+ stage: { ...mutation.stage, protected: false },
236
+ before: [...mutation.stage.before ?? []],
237
+ after: [...mutation.stage.after ?? []],
238
+ baseIndex: null,
239
+ contributedBy: contributor,
240
+ wrappers: [],
241
+ droppedAnchors: [],
242
+ isProtected: false
243
+ });
244
+ }
245
+ for (const mutation of mutations.filter((entry) => entry.op === "reorder").sort(stableMutationCompare)) {
246
+ const state = states.get(mutation.id);
247
+ if (!state)
248
+ continue;
249
+ states.set(mutation.id, {
250
+ ...state,
251
+ before: mergeAnchors(state.before, mutation.before),
252
+ after: mergeAnchors(state.after, mutation.after)
253
+ });
254
+ }
255
+ for (const mutation of mutations.filter((entry) => entry.op === "wrap").sort(stableMutationCompare)) {
256
+ const state = states.get(mutation.id);
257
+ const contributor = contributorOf(mutation);
258
+ const wrapper = wrapperForMutation(mutation);
259
+ if (!state)
260
+ continue;
261
+ states.set(mutation.id, {
262
+ ...state,
263
+ wrappers: [...state.wrappers, { contributedBy: contributor, wrapper }]
264
+ });
265
+ }
266
+ const contributorsForAnchor = (stageId, direction, anchor, state) => {
267
+ const contributors = new Set;
268
+ if (state.replacedBy)
269
+ contributors.add(state.replacedBy);
270
+ for (const mutation of mutations) {
271
+ if (mutation.op === "reorder" && mutation.id === stageId && (direction === "before" ? mutation.before : mutation.after)?.includes(anchor)) {
272
+ contributors.add(contributorOf(mutation));
273
+ }
274
+ if (mutation.op === "insert" && mutation.stage.id === stageId && (direction === "before" ? mutation.stage.before : mutation.stage.after)?.includes(anchor)) {
275
+ contributors.add(contributorOf(mutation));
276
+ }
277
+ }
278
+ if (contributors.size === 0)
279
+ contributors.add(state.contributedBy);
280
+ return uniqueSorted(contributors);
281
+ };
282
+ const edges = [];
283
+ for (const [stageId, state] of states) {
284
+ const before = [];
285
+ const after = [];
286
+ for (const anchor of state.before) {
287
+ if (states.has(anchor))
288
+ before.push(anchor);
289
+ else {
290
+ const removed = removedStates.get(anchor);
291
+ state.droppedAnchors.push({
292
+ stageId,
293
+ anchor,
294
+ direction: "before",
295
+ reason: removed ? "removed" : "missing",
296
+ ...removed?.removedBy ? { removedBy: removed.removedBy } : {}
297
+ });
298
+ }
299
+ }
300
+ for (const anchor of state.after) {
301
+ if (states.has(anchor))
302
+ after.push(anchor);
303
+ else {
304
+ const removed = removedStates.get(anchor);
305
+ state.droppedAnchors.push({
306
+ stageId,
307
+ anchor,
308
+ direction: "after",
309
+ reason: removed ? "removed" : "missing",
310
+ ...removed?.removedBy ? { removedBy: removed.removedBy } : {}
311
+ });
312
+ }
313
+ }
314
+ state.before = uniqueSorted(before);
315
+ state.after = uniqueSorted(after);
316
+ for (const target of state.before)
317
+ edges.push({ from: stageId, to: target, contributors: contributorsForAnchor(stageId, "before", target, state) });
318
+ for (const source of state.after)
319
+ edges.push({ from: source, to: stageId, contributors: contributorsForAnchor(stageId, "after", source, state) });
320
+ }
321
+ const order = topologicalOrder(states, edges);
322
+ const stages = [];
323
+ const record = [];
324
+ for (const id of order) {
325
+ const state = states.get(id);
326
+ if (!state)
327
+ continue;
328
+ stages.push(composeStageWrappers(state));
329
+ const wrappedBy = state.wrappers.toSorted((left, right) => {
330
+ const priorityDelta = (right.wrapper.priority ?? 0) - (left.wrapper.priority ?? 0);
331
+ if (priorityDelta !== 0)
332
+ return priorityDelta;
333
+ return left.contributedBy.localeCompare(right.contributedBy);
334
+ }).map((wrapper) => wrapper.contributedBy);
335
+ record.push({
336
+ stageId: id,
337
+ contributedBy: state.contributedBy,
338
+ ...state.replacedBy ? { replacedBy: state.replacedBy } : {},
339
+ ...wrappedBy.length > 0 ? { wrappedBy } : {},
340
+ ...state.droppedAnchors.length > 0 ? { droppedAnchors: state.droppedAnchors.toSorted((left, right) => left.anchor.localeCompare(right.anchor)) } : {},
341
+ isProtected: state.isProtected
342
+ });
343
+ }
344
+ record.push(...[...removedStates.entries()].toSorted((left, right) => {
345
+ const leftIndex = left[1].baseIndex ?? Number.MAX_SAFE_INTEGER;
346
+ const rightIndex = right[1].baseIndex ?? Number.MAX_SAFE_INTEGER;
347
+ if (leftIndex !== rightIndex)
348
+ return leftIndex - rightIndex;
349
+ return left[0].localeCompare(right[0]);
350
+ }).map(([stageId, state]) => ({
351
+ stageId,
352
+ contributedBy: state.contributedBy,
353
+ ...state.removedBy ? { removedBy: state.removedBy } : {},
354
+ isProtected: state.isProtected
355
+ })));
356
+ return { stages, order, record, cycles: [] };
357
+ }
358
+
359
+ // packages/kernel-seed/src/resolver.ts
360
+ function toCoreStage(stage) {
361
+ return {
362
+ id: String(stage.id),
363
+ kind: stage.kind,
364
+ priority: stage.priority,
365
+ protected: false,
366
+ ...stage.before !== undefined ? { before: stage.before.map(String) } : {},
367
+ ...stage.after !== undefined ? { after: stage.after.map(String) } : {}
368
+ };
369
+ }
370
+ function coreMutation(mutation) {
371
+ const contributedBy = typeof mutation.contributedBy === "string" && mutation.contributedBy.length > 0 ? mutation.contributedBy : "anonymous";
372
+ switch (mutation.op) {
373
+ case "insert":
374
+ return { op: "insert", stage: toCoreStage(mutation.stage), contributedBy };
375
+ case "remove":
376
+ return { op: "remove", id: String(mutation.id), contributedBy };
377
+ case "replace":
378
+ return { op: "replace", id: String(mutation.id), stage: toCoreStage(mutation.stage), contributedBy };
379
+ case "wrap":
380
+ return { op: "wrap", id: String(mutation.id), wrapper: { priority: mutation.around.priority }, contributedBy };
381
+ case "reorder":
382
+ return {
383
+ op: "reorder",
384
+ id: String(mutation.id),
385
+ contributedBy,
386
+ ...mutation.before !== undefined ? { before: mutation.before.map(String) } : {},
387
+ ...mutation.after !== undefined ? { after: mutation.after.map(String) } : {}
388
+ };
389
+ }
390
+ }
391
+ function resolutionRecordEntry(entry) {
392
+ return {
393
+ stageId: entry.stageId,
394
+ contributedBy: entry.contributedBy,
395
+ ...entry.removedBy !== undefined ? { removedBy: entry.removedBy } : {},
396
+ ...entry.replacedBy !== undefined ? { replacedBy: entry.replacedBy } : {},
397
+ ...entry.wrappedBy !== undefined ? { wrappedBy: [...entry.wrappedBy] } : {},
398
+ ...entry.droppedAnchors !== undefined ? { droppedAnchors: entry.droppedAnchors.map((anchor) => anchor.anchor) } : {},
399
+ isProtected: entry.isProtected
400
+ };
401
+ }
402
+ function resolvedPipeline(input) {
403
+ const mutations = input.mutations ?? [];
404
+ const resolved = resolveStagePipeline({
405
+ defaultStages: input.defaultStages.map(toCoreStage),
406
+ mutations: mutations.map(coreMutation)
407
+ });
408
+ return {
409
+ order: resolved.order.map((stageId) => stageId),
410
+ record: resolved.record.map(resolutionRecordEntry),
411
+ cycles: resolved.cycles.map((cycle) => cycle.map((stageId) => stageId)),
412
+ grantUses: [],
413
+ resolvedAt: new Date().toISOString()
414
+ };
415
+ }
416
+ function resolveKernelStages(defaultStages, mutations = [], grants = {}) {
417
+ return resolvedPipeline({ defaultStages, mutations, grants });
418
+ }
419
+ function resolveKernelStagePipeline(input) {
420
+ return resolvedPipeline(input);
421
+ }
422
+ export {
423
+ resolveKernelStages,
424
+ resolveKernelStagePipeline
425
+ };
@@ -0,0 +1,77 @@
1
+ export type StageKind = "transform" | "gate" | "observe";
2
+ export type StageMutationOp = "insert" | "remove" | "replace" | "wrap" | "reorder";
3
+ export interface ResolvableStage {
4
+ readonly id: string;
5
+ readonly kind?: StageKind | string;
6
+ readonly before?: readonly string[];
7
+ readonly after?: readonly string[];
8
+ readonly priority?: number;
9
+ readonly protected?: boolean;
10
+ }
11
+ export interface StageWrapper<TStage extends ResolvableStage = ResolvableStage> {
12
+ readonly id?: string;
13
+ readonly priority?: number;
14
+ readonly apply?: (stage: TStage) => TStage;
15
+ }
16
+ export type StageMutation<TStage extends ResolvableStage = ResolvableStage> = Readonly<{
17
+ op: "insert";
18
+ stage: TStage;
19
+ contributedBy?: string;
20
+ }> | Readonly<{
21
+ op: "remove";
22
+ id: string;
23
+ contributedBy?: string;
24
+ }> | Readonly<{
25
+ op: "replace";
26
+ id: string;
27
+ stage: TStage;
28
+ contributedBy?: string;
29
+ }> | Readonly<{
30
+ op: "wrap";
31
+ id: string;
32
+ wrapper: StageWrapper<TStage>;
33
+ contributedBy?: string;
34
+ }> | Readonly<{
35
+ op: "wrap";
36
+ id: string;
37
+ around: StageWrapper<TStage>;
38
+ contributedBy?: string;
39
+ }> | Readonly<{
40
+ op: "reorder";
41
+ id: string;
42
+ before?: readonly string[];
43
+ after?: readonly string[];
44
+ contributedBy?: string;
45
+ }>;
46
+ export interface ResolveStagePipelineInput<TStage extends ResolvableStage = ResolvableStage> {
47
+ readonly defaultStages: readonly TStage[];
48
+ readonly mutations?: readonly StageMutation<TStage>[];
49
+ }
50
+ export interface DroppedStageAnchor {
51
+ readonly stageId: string;
52
+ readonly anchor: string;
53
+ readonly direction: "before" | "after";
54
+ readonly reason: "removed" | "missing";
55
+ readonly removedBy?: string;
56
+ }
57
+ export interface StageResolutionRecordEntry {
58
+ readonly stageId: string;
59
+ readonly contributedBy: string;
60
+ readonly removedBy?: string;
61
+ readonly replacedBy?: string;
62
+ readonly wrappedBy?: readonly string[];
63
+ readonly droppedAnchors?: readonly DroppedStageAnchor[];
64
+ readonly isProtected: boolean;
65
+ }
66
+ export interface ResolvedStagePipeline<TStage extends ResolvableStage = ResolvableStage> {
67
+ readonly stages: readonly TStage[];
68
+ readonly order: readonly string[];
69
+ readonly record: readonly StageResolutionRecordEntry[];
70
+ readonly cycles: readonly (readonly string[])[];
71
+ }
72
+ export declare class PipelineUnresolvableError extends Error {
73
+ readonly cycles: readonly (readonly string[])[];
74
+ readonly contributors: readonly string[];
75
+ constructor(message: string, cycles: readonly (readonly string[])[], contributors: readonly string[]);
76
+ }
77
+ export declare function resolveStagePipeline<TStage extends ResolvableStage>(input: ResolveStagePipelineInput<TStage>): ResolvedStagePipeline<TStage>;