@wairon/cli 5.1.1-dev.91 → 5.1.1-dev.93
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/cli/index.js +935 -99
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +469 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3621,6 +3621,7 @@ __export(src_exports, {
|
|
|
3621
3621
|
getRequestParentReach: () => getRequestParentReach,
|
|
3622
3622
|
getRequestProjectRoot: () => getRequestProjectRoot,
|
|
3623
3623
|
getStatusReport: () => getStatusReport,
|
|
3624
|
+
getWriteReach: () => getWriteReach,
|
|
3624
3625
|
globalGuideFilePath: () => globalGuideFilePath,
|
|
3625
3626
|
globalPacksDir: () => globalPacksDir,
|
|
3626
3627
|
globalPacksEnabled: () => globalPacksEnabled,
|
|
@@ -3725,6 +3726,7 @@ __export(src_exports, {
|
|
|
3725
3726
|
registerExporter: () => registerExporter,
|
|
3726
3727
|
registerPackRef: () => registerPackRef,
|
|
3727
3728
|
reinjectLocalGuides: () => reinjectLocalGuides,
|
|
3729
|
+
relationHealth: () => relationHealth,
|
|
3728
3730
|
removeDomain: () => removeDomain,
|
|
3729
3731
|
removeExternal: () => removeExternal,
|
|
3730
3732
|
removeMember: () => removeMember,
|
|
@@ -3791,6 +3793,8 @@ __export(src_exports, {
|
|
|
3791
3793
|
writeFileIfChanged: () => writeFileIfChanged,
|
|
3792
3794
|
writeJsonFile: () => writeJsonFile,
|
|
3793
3795
|
writeLockRecord: () => writeLockRecord,
|
|
3796
|
+
writeReachDenials: () => writeReachDenials,
|
|
3797
|
+
writeReachPermits: () => writeReachPermits,
|
|
3794
3798
|
writeRootGuideDelegator: () => writeRootGuideDelegator,
|
|
3795
3799
|
writeYamlFile: () => writeYamlFile
|
|
3796
3800
|
});
|
|
@@ -3860,6 +3864,18 @@ function listFilesRecursive(dirPath, ext) {
|
|
|
3860
3864
|
return files;
|
|
3861
3865
|
}
|
|
3862
3866
|
var projectRootOverride = null;
|
|
3867
|
+
var sameRoot = (a, b) => path.resolve(a) === path.resolve(b);
|
|
3868
|
+
function writeReachPermits(reach2, root, subsystemId) {
|
|
3869
|
+
const own = subsystemId === null ? void 0 : reach2.subsystems.find((s) => s.subsystemId === subsystemId && sameRoot(s.root, root));
|
|
3870
|
+
if (own) return own.writable;
|
|
3871
|
+
return !reach2.unwritableRoots.some((r) => sameRoot(r, root));
|
|
3872
|
+
}
|
|
3873
|
+
function writeReachDenials(reach2, root) {
|
|
3874
|
+
return reach2.subsystems.filter((s) => !s.writable && sameRoot(s.root, root));
|
|
3875
|
+
}
|
|
3876
|
+
function getWriteReach() {
|
|
3877
|
+
return requestRootStore.getStore()?.writeReach ?? null;
|
|
3878
|
+
}
|
|
3863
3879
|
var requestRootStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
3864
3880
|
function currentRootBinding() {
|
|
3865
3881
|
return requestRootStore.getStore();
|
|
@@ -3875,7 +3891,8 @@ function runWithProjectBinding(dir, reach2, fn) {
|
|
|
3875
3891
|
parentReach: reach2.parentReach,
|
|
3876
3892
|
...reach2.narrowed ? { narrowed: true } : {},
|
|
3877
3893
|
...reach2.unwritableRoots ? { unwritableRoots: reach2.unwritableRoots.map((r) => path.resolve(r)) } : {},
|
|
3878
|
-
...reach2.hostedLookup ? { hostedLookup: reach2.hostedLookup } : {}
|
|
3894
|
+
...reach2.hostedLookup ? { hostedLookup: reach2.hostedLookup } : {},
|
|
3895
|
+
...reach2.writeReach ? { writeReach: reach2.writeReach } : {}
|
|
3879
3896
|
},
|
|
3880
3897
|
fn
|
|
3881
3898
|
);
|
|
@@ -7727,6 +7744,12 @@ var ParentExcerptSchema = import_zod8.z.object({
|
|
|
7727
7744
|
/** sha256:… over the canonical excerpt without its commit. */
|
|
7728
7745
|
digest: import_zod8.z.string()
|
|
7729
7746
|
});
|
|
7747
|
+
function relationHealth(status) {
|
|
7748
|
+
if (status.uses.some((u) => u.state === "changed" || u.state === "removed")) return "incompatible";
|
|
7749
|
+
if (!status.reachable || status.outOfReach || status.uses.some((u) => u.state === "unavailable" || u.state === "unlocked")) return "unavailable";
|
|
7750
|
+
if (status.drifted) return "drifted";
|
|
7751
|
+
return "ok";
|
|
7752
|
+
}
|
|
7730
7753
|
var NamedOpenApiSpecSchema = import_zod8.z.object({
|
|
7731
7754
|
portalId: import_zod8.z.string(),
|
|
7732
7755
|
name: import_zod8.z.string(),
|
|
@@ -9048,12 +9071,16 @@ function memberProjectNodes() {
|
|
|
9048
9071
|
trustedLinks: [],
|
|
9049
9072
|
project: true
|
|
9050
9073
|
}));
|
|
9051
|
-
const
|
|
9052
|
-
|
|
9074
|
+
const rootExternals = family.nodes.find((n) => n.namespace === "")?.externals ?? [];
|
|
9075
|
+
const referenced = rootExternals.filter((e) => e.role === "member").map((e) => ({ id: e.alias, name: e.alias, description: "", trustedLinks: [], project: true, storage: storageBadge(e.sourceKind === "family" ? "contained" : e.sourceKind, e.directory, e.commit) }));
|
|
9076
|
+
const taken = new Set([...contained, ...referenced].map((n) => n.id));
|
|
9077
|
+
const external = rootExternals.filter((e) => e.role !== "member" && e.sourceKind !== "family" && !taken.has(e.alias)).map((e) => ({ id: e.alias, name: e.alias, description: e.project === e.alias ? "" : `project ${e.project}`, trustedLinks: [], project: true, external: true, storage: storageBadge(e.sourceKind, e.directory, e.commit) }));
|
|
9078
|
+
return [...contained, ...referenced, ...external];
|
|
9053
9079
|
}
|
|
9054
9080
|
function storageBadge(storage, directory, commit2) {
|
|
9055
9081
|
if (storage === "git") return `git ${commit2 ? commit2.slice(0, 7) : ""}`.trim();
|
|
9056
9082
|
if (storage === "hosted") return "hosted";
|
|
9083
|
+
if (storage === "unresolved") return "unresolved";
|
|
9057
9084
|
if (storage === "path") return directory ? `../${directory.split(/[\\/]/).pop()}` : "sibling";
|
|
9058
9085
|
return "contained";
|
|
9059
9086
|
}
|
|
@@ -9065,7 +9092,60 @@ function partBadges() {
|
|
|
9065
9092
|
}
|
|
9066
9093
|
return out;
|
|
9067
9094
|
}
|
|
9068
|
-
function
|
|
9095
|
+
function useLabel(use) {
|
|
9096
|
+
if (use.publicName === void 0) return use.member ?? "(the external)";
|
|
9097
|
+
return use.member === void 0 || use.member === "type" ? use.publicName : `${use.publicName}.${use.member}`;
|
|
9098
|
+
}
|
|
9099
|
+
function relationFrom(id, consumer, producer, status, comparedAt) {
|
|
9100
|
+
const changed = status.uses.filter((u) => u.state !== "unchanged");
|
|
9101
|
+
const health = relationHealth(status);
|
|
9102
|
+
return {
|
|
9103
|
+
id,
|
|
9104
|
+
consumer,
|
|
9105
|
+
producer,
|
|
9106
|
+
health,
|
|
9107
|
+
uses: changed.map((u) => ({ name: useLabel(u), state: u.state, ...u.detail !== void 0 ? { detail: u.detail } : {} })),
|
|
9108
|
+
unchanged: status.uses.length - changed.length,
|
|
9109
|
+
...status.pinnedCommit !== void 0 ? { pinnedCommit: status.pinnedCommit } : {},
|
|
9110
|
+
...status.pinnedDigest !== void 0 ? { pinnedDigest: status.pinnedDigest } : {},
|
|
9111
|
+
comparedAt,
|
|
9112
|
+
...health === "unavailable" && status.detail !== void 0 ? { detail: status.detail } : {}
|
|
9113
|
+
};
|
|
9114
|
+
}
|
|
9115
|
+
function statusFor(entry, consumer, producer) {
|
|
9116
|
+
const { alias, node } = producer;
|
|
9117
|
+
return entry.externals.find((s) => alias !== void 0 && s.alias === alias || node !== void 0 && (node.parent === consumer && s.alias === node.mountAlias || s.sourceKind === "family" && node.id !== void 0 && s.project === node.id));
|
|
9118
|
+
}
|
|
9119
|
+
function nameRelations(edges, relations, outsideIds, ownerOf4) {
|
|
9120
|
+
const family = graph();
|
|
9121
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9122
|
+
const out = /* @__PURE__ */ new Map();
|
|
9123
|
+
for (const edge of edges) {
|
|
9124
|
+
if (!edge.consumption) continue;
|
|
9125
|
+
const consumer = ownerOf4(edge.from);
|
|
9126
|
+
const node = outsideIds.has(edge.to) ? void 0 : familyNode(family, ownerOf4(edge.to)) ?? void 0;
|
|
9127
|
+
const alias = outsideIds.has(edge.to) ? edge.to : void 0;
|
|
9128
|
+
const entry = relations.find((r) => r.project === consumer);
|
|
9129
|
+
const status = entry && !entry.detail ? statusFor(entry, consumer, { alias, node }) : void 0;
|
|
9130
|
+
const producer = status?.alias ?? alias ?? (node?.parent === consumer ? node.mountAlias : void 0) ?? node?.id ?? ownerOf4(edge.to);
|
|
9131
|
+
const id = `${consumer}\u2192${producer}`;
|
|
9132
|
+
if (!out.has(id)) {
|
|
9133
|
+
out.set(id, status ? relationFrom(id, consumer, producer, status, entry.comparedAt) : {
|
|
9134
|
+
id,
|
|
9135
|
+
consumer,
|
|
9136
|
+
producer,
|
|
9137
|
+
health: "unavailable",
|
|
9138
|
+
uses: [],
|
|
9139
|
+
unchanged: 0,
|
|
9140
|
+
comparedAt: entry?.comparedAt ?? now,
|
|
9141
|
+
detail: entry?.detail ?? (entry ? `the consumer's statuses name no producer "${producer}"` : "the consumer's statuses were not read")
|
|
9142
|
+
});
|
|
9143
|
+
}
|
|
9144
|
+
edge.relation = id;
|
|
9145
|
+
}
|
|
9146
|
+
return [...out.values()];
|
|
9147
|
+
}
|
|
9148
|
+
function buildCanvasModel(issues = [], relations) {
|
|
9069
9149
|
const system = loadSystemSpec();
|
|
9070
9150
|
const subsystems = loadSubsystemSpecs();
|
|
9071
9151
|
const components = loadComponentSpecs();
|
|
@@ -9173,22 +9253,26 @@ function buildCanvasModel(issues = []) {
|
|
|
9173
9253
|
const badges = partBadges();
|
|
9174
9254
|
const componentSub = new Map(components.map((c) => [c.id, c.subsystem]));
|
|
9175
9255
|
const edges = [];
|
|
9176
|
-
const
|
|
9256
|
+
const outsideIds = new Set(projectNodes.filter((p) => p.storage !== void 0).map((p) => p.id));
|
|
9257
|
+
const owners = graph().owners;
|
|
9258
|
+
const ownerOfSpec = (id) => owners.get(id) ?? "";
|
|
9177
9259
|
for (const comp of components) {
|
|
9178
9260
|
for (const depId of comp.dependsOn) {
|
|
9179
9261
|
const alias = depId.includes("::") ? depId.split("::")[0] : "";
|
|
9180
|
-
if (
|
|
9181
|
-
edges.push({ from: comp.id, to: alias, cross: true });
|
|
9262
|
+
if (outsideIds.has(alias) && !componentIds.has(depId)) {
|
|
9263
|
+
edges.push({ from: comp.id, to: alias, cross: true, consumption: true });
|
|
9182
9264
|
continue;
|
|
9183
9265
|
}
|
|
9184
9266
|
if (!componentIds.has(depId)) continue;
|
|
9185
9267
|
edges.push({
|
|
9186
9268
|
from: comp.id,
|
|
9187
9269
|
to: depId,
|
|
9188
|
-
cross: componentSub.get(depId) !== comp.subsystem
|
|
9270
|
+
cross: componentSub.get(depId) !== comp.subsystem,
|
|
9271
|
+
...ownerOfSpec(depId) !== ownerOfSpec(comp.id) ? { consumption: true } : {}
|
|
9189
9272
|
});
|
|
9190
9273
|
}
|
|
9191
9274
|
}
|
|
9275
|
+
const canvasRelations = relations === void 0 ? void 0 : nameRelations(edges, relations, outsideIds, ownerOfSpec);
|
|
9192
9276
|
const typeSpecs = loadTypeSpecs();
|
|
9193
9277
|
const usedByFor = (t) => {
|
|
9194
9278
|
const qualified = t.subsystem && !t.id.startsWith(`${t.subsystem}::`) ? `${t.subsystem}::${t.id}` : t.id;
|
|
@@ -9310,6 +9394,7 @@ function buildCanvasModel(issues = []) {
|
|
|
9310
9394
|
],
|
|
9311
9395
|
components: modelComponents,
|
|
9312
9396
|
edges,
|
|
9397
|
+
...canvasRelations !== void 0 ? { relations: canvasRelations } : {},
|
|
9313
9398
|
types: modelTypes,
|
|
9314
9399
|
typeEdges,
|
|
9315
9400
|
dataEdges,
|
|
@@ -9483,6 +9568,7 @@ body[data-theme="light"] .selectControl { color-scheme:light; }
|
|
|
9483
9568
|
#cy { position:absolute; inset:0; }
|
|
9484
9569
|
.legend { position:absolute; left:12px; bottom:12px; background:var(--chrome); border:1px solid var(--chrome-border); border-radius:10px; padding:8px 12px; font-size:11px; color:var(--dim); z-index:5; pointer-events:none; }
|
|
9485
9570
|
.legend .sw { display:inline-block; width:10px; height:10px; border-radius:3px; margin-right:4px; vertical-align:-1px; border:1.5px solid; }
|
|
9571
|
+
.reltip { position:absolute; display:none; background:var(--chrome); border:1px solid var(--chrome-border); border-radius:8px; padding:6px 10px; font-size:11px; color:var(--dim); white-space:pre; z-index:6; pointer-events:none; max-width:520px; overflow:hidden; }
|
|
9486
9572
|
/* Stage overlays clear the floating header (52px + 10px top + 10px gap). The
|
|
9487
9573
|
header hides in presentation mode, where they return to the top edge. */
|
|
9488
9574
|
.viewhint { position:absolute; top:72px; left:12px; color:var(--dim); font-size:11px; background:var(--chrome); border:1px solid var(--chrome-border); border-radius:9px; padding:5px 10px; z-index:5; pointer-events:none; }
|
|
@@ -9668,6 +9754,7 @@ body.presentation #exitPresent, body.presentation #presentDetails { display:bloc
|
|
|
9668
9754
|
<div class="viewhint" id="viewHint"></div>
|
|
9669
9755
|
<div id="typesWarn"></div>
|
|
9670
9756
|
<div class="legend" id="legend"></div>
|
|
9757
|
+
<div class="reltip" id="relTip"></div>
|
|
9671
9758
|
</div>
|
|
9672
9759
|
<div id="panelResizer" title="Drag to resize details sidebar"></div>
|
|
9673
9760
|
<div id="panel"></div>
|
|
@@ -9995,6 +10082,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
9995
10082
|
retired: { fill: '#ebe8e4', stroke: '#857b73', text: '#35302b' },
|
|
9996
10083
|
},
|
|
9997
10084
|
issue: '#b3261e', selGlow: '#3465b4', bgLabel: '#f2f5f8', png: '#f2f5f8',
|
|
10085
|
+
relOk: '#2e7d4f', relDrift: '#8a6116', relBad: '#b3261e', relUnknown: '#5f6b78',
|
|
9998
10086
|
},
|
|
9999
10087
|
syw: {
|
|
10000
10088
|
pageEdge: '#7c8ca3', edgeText: '#aebdd2', cross: '#ff6b81', ink: '#eef2f8',
|
|
@@ -10016,6 +10104,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
10016
10104
|
retired: { fill: '#24211f', stroke: '#a8a29e', text: '#e7e5e4' },
|
|
10017
10105
|
},
|
|
10018
10106
|
issue: '#ff6b81', selGlow: '#22ddff', bgLabel: '#0a0a0f', png: '#0a0a0f',
|
|
10107
|
+
relOk: '#34d399', relDrift: '#f59e0b', relBad: '#ff6b81', relUnknown: '#93a1b8',
|
|
10019
10108
|
},
|
|
10020
10109
|
};
|
|
10021
10110
|
|
|
@@ -10078,6 +10167,15 @@ var MODEL = __MODEL_JSON__;
|
|
|
10078
10167
|
{ selector: 'edge.bundle', style: { width: 4.5, opacity: 0.7 } },
|
|
10079
10168
|
{ selector: 'edge.toghost', style: { 'line-style': 'dashed', opacity: 0.75 } },
|
|
10080
10169
|
{ selector: 'edge.inneredge', style: { width: 1.1, 'arrow-scale': 0.6, opacity: 0.8 } },
|
|
10170
|
+
// Relation health on consumption edges: the health colour replaces the
|
|
10171
|
+
// boundary-hop colour, and unavailable is dotted as well as grey, so the
|
|
10172
|
+
// state never rests on colour alone (the glyph is the edge's label).
|
|
10173
|
+
{ selector: 'edge.rel-unchecked', style: { 'line-color': t.pageEdge, 'target-arrow-color': t.pageEdge } },
|
|
10174
|
+
{ selector: 'edge.rel-ok', style: { 'line-color': t.relOk, 'target-arrow-color': t.relOk } },
|
|
10175
|
+
{ selector: 'edge.rel-drifted', style: { 'line-color': t.relDrift, 'target-arrow-color': t.relDrift } },
|
|
10176
|
+
{ selector: 'edge.rel-incompatible', style: { 'line-color': t.relBad, 'target-arrow-color': t.relBad } },
|
|
10177
|
+
{ selector: 'edge.rel-unavailable', style: { 'line-color': t.relUnknown, 'target-arrow-color': t.relUnknown, 'line-style': 'dotted' } },
|
|
10178
|
+
{ selector: '.extProject', style: { 'border-style': 'dashed' } },
|
|
10081
10179
|
{ selector: 'edge.stubHover', style: { 'line-color': t.selGlow, 'target-arrow-color': t.selGlow, width: 2.6, opacity: 1, 'z-compound-depth': 'top' } },
|
|
10082
10180
|
{ selector: '.dimmed', style: { opacity: 0.13 } },
|
|
10083
10181
|
{ selector: '.hasIssue', style: { 'border-color': t.issue, 'border-style': 'dashed', 'border-width': 3 } },
|
|
@@ -10114,6 +10212,83 @@ var MODEL = __MODEL_JSON__;
|
|
|
10114
10212
|
];
|
|
10115
10213
|
}
|
|
10116
10214
|
|
|
10215
|
+
// ---- relation health (consumption edges) ----------------------------------
|
|
10216
|
+
// The model carries relations only when health was computed; without them a
|
|
10217
|
+
// consumption edge is drawn neutral and the legend says it was not checked \u2014
|
|
10218
|
+
// never green for something nobody compared.
|
|
10219
|
+
var HAS_RELATIONS = Array.isArray(MODEL.relations);
|
|
10220
|
+
var REL_BY_ID = {};
|
|
10221
|
+
(MODEL.relations || []).forEach(function (r) { REL_BY_ID[r.id] = r; });
|
|
10222
|
+
var HEALTH_RANK = { ok: 1, drifted: 2, unavailable: 3, incompatible: 4 };
|
|
10223
|
+
var HEALTH_GLYPH = { ok: '\\u2713', drifted: '\\u0394', incompatible: '\\u2715', unavailable: '?' };
|
|
10224
|
+
var HAS_CONSUMPTION = MODEL.edges.some(function (e) { return !!e.consumption; });
|
|
10225
|
+
function edgeHealth(edge) {
|
|
10226
|
+
var r = edge.relation ? REL_BY_ID[edge.relation] : null;
|
|
10227
|
+
return r ? r.health : (HAS_RELATIONS ? 'unavailable' : null);
|
|
10228
|
+
}
|
|
10229
|
+
// Where edges fold into one, the worst health wins.
|
|
10230
|
+
function worseHealth(a, b) {
|
|
10231
|
+
if (!a) return b;
|
|
10232
|
+
if (!b) return a;
|
|
10233
|
+
return HEALTH_RANK[b] > HEALTH_RANK[a] ? b : a;
|
|
10234
|
+
}
|
|
10235
|
+
function relationsComparedAt() {
|
|
10236
|
+
var at = null;
|
|
10237
|
+
(MODEL.relations || []).forEach(function (r) { if (!at || r.comparedAt < at) at = r.comparedAt; });
|
|
10238
|
+
return at;
|
|
10239
|
+
}
|
|
10240
|
+
function shortPin(r) {
|
|
10241
|
+
if (r.pinnedCommit) return r.pinnedCommit.slice(0, 7);
|
|
10242
|
+
return r.pinnedDigest ? r.pinnedDigest.replace(/^[a-z0-9-]+:/, '').slice(0, 12) : '';
|
|
10243
|
+
}
|
|
10244
|
+
function relationName(r) { return (r.consumer || 'this project') + ' \\u2192 ' + r.producer; }
|
|
10245
|
+
function relationText(r) {
|
|
10246
|
+
var lines = [relationName(r) + ': ' + HEALTH_GLYPH[r.health] + ' ' + r.health];
|
|
10247
|
+
r.uses.forEach(function (u) { lines.push(' ' + u.name + ' \\u2014 ' + u.state + (u.detail ? ': ' + u.detail : '')); });
|
|
10248
|
+
if (r.unchanged) lines.push(' ' + r.unchanged + ' use' + (r.unchanged === 1 ? '' : 's') + ' unchanged');
|
|
10249
|
+
if (r.detail) lines.push(' ' + r.detail);
|
|
10250
|
+
var pin = shortPin(r);
|
|
10251
|
+
lines.push((pin ? 'pinned ' + pin + ' \\u00B7 ' : '') + 'compared ' + r.comparedAt);
|
|
10252
|
+
return lines.join('\\n');
|
|
10253
|
+
}
|
|
10254
|
+
function relationHtml(r) {
|
|
10255
|
+
var pin = shortPin(r);
|
|
10256
|
+
return '<div class="method"><div class="mname">' + esc(relationName(r)) + '<span class="grow"></span><span class="chip">' + HEALTH_GLYPH[r.health] + ' ' + esc(r.health) + '</span></div>'
|
|
10257
|
+
+ r.uses.map(function (u) { return '<div class="mdesc">' + esc(u.name) + ' \\u2014 ' + esc(u.state) + (u.detail ? ': ' + esc(u.detail) : '') + '</div>'; }).join('')
|
|
10258
|
+
+ (r.unchanged ? '<div class="mdesc">' + r.unchanged + ' use' + (r.unchanged === 1 ? '' : 's') + ' unchanged</div>' : '')
|
|
10259
|
+
+ (r.detail ? '<div class="mdesc">' + esc(r.detail) + '</div>' : '')
|
|
10260
|
+
+ '<div class="mdesc">' + (pin ? 'pinned ' + esc(pin) + ' \\u00B7 ' : '') + 'compared ' + esc(r.comparedAt) + '</div></div>';
|
|
10261
|
+
}
|
|
10262
|
+
// The relations whose consumption edges start or land inside a node.
|
|
10263
|
+
function relationsTouching(sid) {
|
|
10264
|
+
var seen = {}, out = [];
|
|
10265
|
+
var inside = function (x) {
|
|
10266
|
+
var c = compById[x];
|
|
10267
|
+
return x === sid || x.indexOf(sid + '::') === 0 || !!(c && (c.subsystem === sid || c.subsystem.indexOf(sid + '::') === 0));
|
|
10268
|
+
};
|
|
10269
|
+
MODEL.edges.forEach(function (e) {
|
|
10270
|
+
if (!e.relation || seen[e.relation] || !(inside(e.to) || inside(e.from))) return;
|
|
10271
|
+
seen[e.relation] = true;
|
|
10272
|
+
if (REL_BY_ID[e.relation]) out.push(REL_BY_ID[e.relation]);
|
|
10273
|
+
});
|
|
10274
|
+
return out;
|
|
10275
|
+
}
|
|
10276
|
+
// The one legend row, only when the model has a consumption edge.
|
|
10277
|
+
function relationLegend(t) {
|
|
10278
|
+
if (!HAS_CONSUMPTION) return '';
|
|
10279
|
+
if (!HAS_RELATIONS) return '<br>relations: <span style="color:' + t.pageEdge + '">health not checked</span>';
|
|
10280
|
+
var at = relationsComparedAt();
|
|
10281
|
+
return '<br>relations: <span style="color:' + t.relOk + '">\\u2713 ok</span> <span style="color:' + t.relDrift + '">\\u0394 drifted</span> '
|
|
10282
|
+
+ '<span style="color:' + t.relBad + '">\\u2715 incompatible</span> <span style="color:' + t.relUnknown + '">? unavailable (dotted)</span>'
|
|
10283
|
+
+ (at ? ' \\u00B7 as of ' + esc(at) : '');
|
|
10284
|
+
}
|
|
10285
|
+
// A project's own page, only through the host's open-project hook (the web
|
|
10286
|
+
// app): never in a standalone file or on a shared page, which have none.
|
|
10287
|
+
function openProjectButton(s) {
|
|
10288
|
+
if (!s.recordId || typeof opts === 'undefined' || !opts || typeof opts.onOpenProject !== 'function') return '';
|
|
10289
|
+
return '<div class="openbtn"><button class="tbtn" data-openproject-id="' + esc(s.recordId) + '">Open project \\u2197</button></div>';
|
|
10290
|
+
}
|
|
10291
|
+
|
|
10117
10292
|
function renderLegend() {
|
|
10118
10293
|
var t = THEMES[state.theme];
|
|
10119
10294
|
var sw = function (c) { return '<span class="sw" style="background:' + c.fill + ';border-color:' + c.stroke + '"></span>'; };
|
|
@@ -10127,7 +10302,8 @@ var MODEL = __MODEL_JSON__;
|
|
|
10127
10302
|
sw(t.proxyIn) + '\\u21E0 in-port ' + sw(t.proxyOut) + '\\u21E2 out-port \u2014 bold border = published \xB7 ' +
|
|
10128
10303
|
'<span style="color:' + t.cross + '">red</span> = boundary hop \xB7 double-click = open<br>' +
|
|
10129
10304
|
'on select: <span style="color:' + t.selGlow + '">\\u2192 depends on</span> <span style="color:' + t.warn + '">\\u2190 used by</span>' +
|
|
10130
|
-
(state.dataCoupling ? ' \xB7 <span style="color:' + t.typeV.stroke + '">- - \\u25B8 uses models</span>' : '')
|
|
10305
|
+
(state.dataCoupling ? ' \xB7 <span style="color:' + t.typeV.stroke + '">- - \\u25B8 uses models</span>' : '') +
|
|
10306
|
+
relationLegend(t);
|
|
10131
10307
|
}
|
|
10132
10308
|
|
|
10133
10309
|
// ---- view layout ---------------------------------------------------------------
|
|
@@ -10594,6 +10770,14 @@ var MODEL = __MODEL_JSON__;
|
|
|
10594
10770
|
if (!agg[key]) agg[key] = { src: src, tgt: tgt, n: 0, cross: false, ghost: ghost };
|
|
10595
10771
|
agg[key].n++;
|
|
10596
10772
|
if (edge.cross) agg[key].cross = true;
|
|
10773
|
+
if (edge.consumption) {
|
|
10774
|
+
agg[key].consumption = true;
|
|
10775
|
+
agg[key].health = worseHealth(agg[key].health, edgeHealth(edge));
|
|
10776
|
+
if (edge.relation) {
|
|
10777
|
+
var rels = agg[key].rels || (agg[key].rels = []);
|
|
10778
|
+
if (rels.indexOf(edge.relation) < 0) rels.push(edge.relation);
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10597
10781
|
});
|
|
10598
10782
|
return { agg: agg, ghosts: ghosts };
|
|
10599
10783
|
}
|
|
@@ -11154,7 +11338,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
11154
11338
|
var classes, label;
|
|
11155
11339
|
var isPub = e.kind === 'component' && compById[e.id] && compById[e.id].public;
|
|
11156
11340
|
if (e.kind === 'subsystem') {
|
|
11157
|
-
classes = 'subsysBox';
|
|
11341
|
+
classes = 'subsysBox' + (subById[e.id] && subById[e.id].external ? ' extProject' : '');
|
|
11158
11342
|
label = nameOf(e) + (e.hasKids && !inner ? '\\n\\u25B8 open' : '');
|
|
11159
11343
|
} else {
|
|
11160
11344
|
var c = compById[e.id];
|
|
@@ -11362,9 +11546,13 @@ var MODEL = __MODEL_JSON__;
|
|
|
11362
11546
|
var bundle = e.n > 1;
|
|
11363
11547
|
var dim = state.query && (dimmedAnchors[e.src] || dimmedAnchors[e.tgt]);
|
|
11364
11548
|
var route = routeData(e.src, e.tgt, key);
|
|
11549
|
+
// A consumption edge carries its relation's health: the glyph at its middle,
|
|
11550
|
+
// its colour (and line, dotted when unavailable) by class.
|
|
11551
|
+
var glyph = e.health ? HEALTH_GLYPH[e.health] : '';
|
|
11552
|
+
var relCls = e.consumption ? 'rel rel-' + (e.health || 'unchecked') + ' ' : '';
|
|
11365
11553
|
eles.push({
|
|
11366
|
-
data: { id: 'e' + (i++), source: e.src, target: e.tgt, lbl: bundle ? e.n + ' links' : '', cpDist: route.cpDist, cpWeight: route.cpWeight, taxiTurn: route.taxiTurn },
|
|
11367
|
-
classes: 'routed ' + (e.cross ? 'cross ' : '') + (bundle ? 'bundle ' : '') + (e.ghost ? 'toghost ' : '') + (dim ? 'dimmed' : ''),
|
|
11554
|
+
data: { id: 'e' + (i++), source: e.src, target: e.tgt, lbl: (bundle ? e.n + ' links' : '') + (glyph ? (bundle ? ' ' : '') + glyph : ''), rels: (e.rels || []).join('|'), cpDist: route.cpDist, cpWeight: route.cpWeight, taxiTurn: route.taxiTurn },
|
|
11555
|
+
classes: 'routed ' + (e.cross ? 'cross ' : '') + (bundle ? 'bundle ' : '') + (e.ghost ? 'toghost ' : '') + relCls + (dim ? 'dimmed' : ''),
|
|
11368
11556
|
});
|
|
11369
11557
|
});
|
|
11370
11558
|
|
|
@@ -11864,6 +12052,27 @@ var MODEL = __MODEL_JSON__;
|
|
|
11864
12052
|
cy.on('mouseover', 'node', function (ev) { setHover(ev.target); });
|
|
11865
12053
|
cy.on('mouseout', 'node', function (ev) { if (hoveredNode && hoveredNode.id() === ev.target.id()) setHover(null); });
|
|
11866
12054
|
|
|
12055
|
+
// Relation health: hovering a consumption edge shows its relation(s) \u2014 which
|
|
12056
|
+
// uses changed or were removed, why one could not be compared, the pin and
|
|
12057
|
+
// when it was compared; tapping it shows the same in the details panel.
|
|
12058
|
+
function relationsOfEdge(edge) {
|
|
12059
|
+
return (edge.data('rels') || '').split('|').filter(Boolean).map(function (rid) { return REL_BY_ID[rid]; }).filter(Boolean);
|
|
12060
|
+
}
|
|
12061
|
+
cy.on('mouseover', 'edge.rel', function (ev) {
|
|
12062
|
+
var tip = document.getElementById('relTip');
|
|
12063
|
+
var rels = relationsOfEdge(ev.target);
|
|
12064
|
+
tip.textContent = rels.length ? rels.map(relationText).join('\\n\\n') : 'relation health not checked';
|
|
12065
|
+
var at = ev.renderedPosition || { x: 0, y: 0 };
|
|
12066
|
+
tip.style.left = (at.x + 12) + 'px';
|
|
12067
|
+
tip.style.top = (at.y + 12) + 'px';
|
|
12068
|
+
tip.style.display = 'block';
|
|
12069
|
+
});
|
|
12070
|
+
cy.on('mouseout', 'edge.rel', function () { document.getElementById('relTip').style.display = 'none'; });
|
|
12071
|
+
cy.on('tap', 'edge.rel', function (ev) {
|
|
12072
|
+
var ids = ev.target.data('rels') || '';
|
|
12073
|
+
if (ids) select('relation', ids, false);
|
|
12074
|
+
});
|
|
12075
|
+
|
|
11867
12076
|
cy.on('tap', 'node', function (ev) {
|
|
11868
12077
|
var t = idOf(ev.target);
|
|
11869
12078
|
if (t.group) return;
|
|
@@ -11921,7 +12130,15 @@ var MODEL = __MODEL_JSON__;
|
|
|
11921
12130
|
var hasKids = t.kind === 'subsystem'
|
|
11922
12131
|
? (childSubsOf(t.id).length + childCompsOf(t.id).length) > 0
|
|
11923
12132
|
: memberCompsOf(t.id).length > 0;
|
|
11924
|
-
if (hasKids) navigateTo(t.kind, t.id);
|
|
12133
|
+
if (hasKids) { navigateTo(t.kind, t.id); return; }
|
|
12134
|
+
// A project node with nothing to drill into (a referenced or external one):
|
|
12135
|
+
// its own page through the host's open-project hook when it carries a
|
|
12136
|
+
// record id, else its details panel \u2014 its storage and its relations.
|
|
12137
|
+
var ps = t.kind === 'subsystem' ? subById[t.id] : null;
|
|
12138
|
+
if (ps && ps.project) {
|
|
12139
|
+
if (ps.recordId && typeof opts !== 'undefined' && opts && typeof opts.onOpenProject === 'function') { opts.onOpenProject(ps.recordId); return; }
|
|
12140
|
+
select('subsystem', t.id, false);
|
|
12141
|
+
}
|
|
11925
12142
|
});
|
|
11926
12143
|
cy.on('dragfree', 'node', function () { harvestPositions(); });
|
|
11927
12144
|
|
|
@@ -13158,7 +13375,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
13158
13375
|
cy.nodes().removeClass('sel');
|
|
13159
13376
|
cy.edges().removeClass('fieldhl');
|
|
13160
13377
|
if (id) {
|
|
13161
|
-
var node = nodeForRef(kind, id);
|
|
13378
|
+
var node = kind === 'relation' ? cy.collection() : nodeForRef(kind, id);
|
|
13162
13379
|
if (node.length) {
|
|
13163
13380
|
node.addClass('sel');
|
|
13164
13381
|
applyFocus(node);
|
|
@@ -13328,6 +13545,10 @@ var MODEL = __MODEL_JSON__;
|
|
|
13328
13545
|
via.map(function (v) { return chip(v.label, v.kind, v.id); }).join(''), true);
|
|
13329
13546
|
}
|
|
13330
13547
|
}
|
|
13548
|
+
} else if (focusKind === 'relation') {
|
|
13549
|
+
var rl = String(focusId).split('|').map(function (rid) { return REL_BY_ID[rid]; }).filter(Boolean);
|
|
13550
|
+
head = '<h2>' + esc(rl.length === 1 ? relationName(rl[0]) : rl.length + ' relations') + '</h2>' + staticChip('relation');
|
|
13551
|
+
body += rl.map(relationHtml).join('');
|
|
13331
13552
|
} else if (focusKind === 'type') {
|
|
13332
13553
|
var ty = null;
|
|
13333
13554
|
MODEL.types.forEach(function (t2) { if (t2.id === focusId) ty = t2; });
|
|
@@ -13411,8 +13632,14 @@ var MODEL = __MODEL_JSON__;
|
|
|
13411
13632
|
+ (scopeFocus ? staticChip('current view') : '')
|
|
13412
13633
|
+ (scopeFocus ? '' : openViewButton('subsystem', s.id, subKids > 0))
|
|
13413
13634
|
+ openApiButton(subsystemExposesApi(s.id), '')
|
|
13414
|
-
+ openSpecButton('subsystem', s.id)
|
|
13635
|
+
+ openSpecButton('subsystem', s.id)
|
|
13636
|
+
+ (s.project ? staticChip(s.external ? 'external project' : 'project') : '')
|
|
13637
|
+
+ (s.storage ? staticChip(s.storage) : '')
|
|
13638
|
+
+ openProjectButton(s);
|
|
13415
13639
|
body += '<p class="desc">' + esc(s.description) + '</p>';
|
|
13640
|
+
var nodeRels = relationsTouching(s.id);
|
|
13641
|
+
if (nodeRels.length) body += section('Relations', nodeRels.length, nodeRels.map(relationHtml).join(''), true);
|
|
13642
|
+
else if (s.project && HAS_CONSUMPTION && !HAS_RELATIONS) body += section('Relations', null, '<span class="desc">relation health was not checked</span>', true);
|
|
13416
13643
|
if (s.trustedLinks.length) {
|
|
13417
13644
|
body += section('Trusted links (fast lanes)', s.trustedLinks.length, s.trustedLinks.map(function (t2) {
|
|
13418
13645
|
return '<div class="method">' + chip(t2.subsystem, 'subsystem', t2.subsystem) + '<div class="mdesc">' + esc(t2.reason) + '</div></div>';
|
|
@@ -13479,6 +13706,14 @@ var MODEL = __MODEL_JSON__;
|
|
|
13479
13706
|
});
|
|
13480
13707
|
})(ospecs[si]);
|
|
13481
13708
|
}
|
|
13709
|
+
var oprojs = panel.querySelectorAll('[data-openproject-id]');
|
|
13710
|
+
for (var opi = 0; opi < oprojs.length; opi++) {
|
|
13711
|
+
(function (b) {
|
|
13712
|
+
b.addEventListener('click', function () {
|
|
13713
|
+
if (typeof opts !== 'undefined' && opts && typeof opts.onOpenProject === 'function') opts.onOpenProject(b.getAttribute('data-openproject-id'));
|
|
13714
|
+
});
|
|
13715
|
+
})(oprojs[opi]);
|
|
13716
|
+
}
|
|
13482
13717
|
var flows = panel.querySelectorAll('[data-flow-comp]');
|
|
13483
13718
|
for (var j = 0; j < flows.length; j++) {
|
|
13484
13719
|
(function (b) {
|
|
@@ -14074,7 +14309,14 @@ function externalStatus(binding, lock) {
|
|
|
14074
14309
|
const { external } = binding;
|
|
14075
14310
|
const entry = lock?.externals[external.alias];
|
|
14076
14311
|
const pinned = entry !== void 0 && externalsRepository.readSnapshot(external.alias) !== null;
|
|
14077
|
-
const base = {
|
|
14312
|
+
const base = {
|
|
14313
|
+
alias: external.alias,
|
|
14314
|
+
project: external.project,
|
|
14315
|
+
sourceKind: external.sourceKind,
|
|
14316
|
+
pinned,
|
|
14317
|
+
...entry !== void 0 ? { pinnedDigest: entry.digest } : {},
|
|
14318
|
+
...entry?.commit !== void 0 ? { pinnedCommit: entry.commit } : {}
|
|
14319
|
+
};
|
|
14078
14320
|
const unreadable = external.sourceKind === "unresolved" ? hostedOnly(external) ? external.problem : `the external does not resolve: ${external.problem}` : producerUnreadable(external);
|
|
14079
14321
|
let live = null;
|
|
14080
14322
|
let reason = unreadable;
|
|
@@ -14314,7 +14556,8 @@ function reachCeiling(root, family) {
|
|
|
14314
14556
|
}
|
|
14315
14557
|
function within2(dir, ceiling, fn) {
|
|
14316
14558
|
if (ceiling === null) return runWithProjectRoot(dir, fn);
|
|
14317
|
-
|
|
14559
|
+
const lookup2 = getHostedLookup();
|
|
14560
|
+
return runWithProjectBinding(dir, { topRoot: ceiling.topRoot, parentReach: true, narrowed: ceiling.narrowed, ...lookup2 ? { hostedLookup: lookup2 } : {} }, fn);
|
|
14318
14561
|
}
|
|
14319
14562
|
function depthOf(family, node) {
|
|
14320
14563
|
let depth = 0;
|
|
@@ -14365,7 +14608,8 @@ function referencedOf(nodes) {
|
|
|
14365
14608
|
return out;
|
|
14366
14609
|
}
|
|
14367
14610
|
function atReferenced(ref, fn) {
|
|
14368
|
-
|
|
14611
|
+
const lookup2 = getHostedLookup();
|
|
14612
|
+
return runWithProjectBinding(ref.directory, { topRoot: ref.directory, parentReach: true, narrowed: true, ...lookup2 ? { hostedLookup: lookup2 } : {} }, fn);
|
|
14369
14613
|
}
|
|
14370
14614
|
function narrowed(family, selected) {
|
|
14371
14615
|
const keys = /* @__PURE__ */ new Set(["", ...selected.map((s) => s.node.namespace)]);
|
|
@@ -14853,7 +15097,7 @@ function defaultTargetConfig(type) {
|
|
|
14853
15097
|
enabled: true
|
|
14854
15098
|
};
|
|
14855
15099
|
}
|
|
14856
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
15100
|
+
var WAIRON_VERSION = "5.1.1-dev.93";
|
|
14857
15101
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
14858
15102
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
14859
15103
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -25349,10 +25593,11 @@ function familyApprovals(depth) {
|
|
|
25349
25593
|
}
|
|
25350
25594
|
|
|
25351
25595
|
// src/core/diagram.ts
|
|
25352
|
-
function renderDiagram(format) {
|
|
25596
|
+
function renderDiagram(format, relations) {
|
|
25353
25597
|
switch (format) {
|
|
25598
|
+
// Only the canvas carries relation health; the other formats ignore it.
|
|
25354
25599
|
case "canvas":
|
|
25355
|
-
return renderCanvasHtml(buildCanvasModel(diagramIssues()));
|
|
25600
|
+
return renderCanvasHtml(buildCanvasModel(diagramIssues(), relations));
|
|
25356
25601
|
case "mermaid":
|
|
25357
25602
|
return generateComponentDiagram();
|
|
25358
25603
|
case "drawio":
|
|
@@ -25363,8 +25608,8 @@ function renderDiagram(format) {
|
|
|
25363
25608
|
throw new Error(`Unsupported diagram format "${format}" (canvas | mermaid | drawio | excalidraw).`);
|
|
25364
25609
|
}
|
|
25365
25610
|
}
|
|
25366
|
-
function buildCanvasDataModel() {
|
|
25367
|
-
return buildCanvasModel(diagramIssues());
|
|
25611
|
+
function buildCanvasDataModel(relations) {
|
|
25612
|
+
return buildCanvasModel(diagramIssues(), relations);
|
|
25368
25613
|
}
|
|
25369
25614
|
function diagramIssues() {
|
|
25370
25615
|
try {
|
|
@@ -26617,6 +26862,34 @@ var PartReadOnly = class extends WaironError {
|
|
|
26617
26862
|
this.name = "PartReadOnly";
|
|
26618
26863
|
}
|
|
26619
26864
|
};
|
|
26865
|
+
var SubsystemWriteDenied = class extends WaironError {
|
|
26866
|
+
constructor(message) {
|
|
26867
|
+
super(message);
|
|
26868
|
+
this.name = "SubsystemWriteDenied";
|
|
26869
|
+
}
|
|
26870
|
+
};
|
|
26871
|
+
function deniedOwners(reach2, owners) {
|
|
26872
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26873
|
+
const denied = [];
|
|
26874
|
+
for (const o of owners) {
|
|
26875
|
+
const key = `${path21.resolve(o.root)}|${o.subsystemId ?? ""}`;
|
|
26876
|
+
if (seen.has(key)) continue;
|
|
26877
|
+
seen.add(key);
|
|
26878
|
+
if (writeReachPermits(reach2, o.root, o.subsystemId)) continue;
|
|
26879
|
+
const rule = o.subsystemId === null ? void 0 : reach2.subsystems.find((s) => s.subsystemId === o.subsystemId && path21.resolve(s.root) === path21.resolve(o.root));
|
|
26880
|
+
denied.push(o.subsystemId === null ? `project "${o.project}" itself (a spec of no subsystem, decided at the project rung)` : `subsystem "${o.subsystemId}" of project "${o.project}" (decided by ${rule?.decidedBy ?? "the project rung"})`);
|
|
26881
|
+
}
|
|
26882
|
+
return denied;
|
|
26883
|
+
}
|
|
26884
|
+
function assertSpecWritesPermitted(owners, what) {
|
|
26885
|
+
const reach2 = getWriteReach();
|
|
26886
|
+
if (!reach2) return;
|
|
26887
|
+
const denied = deniedOwners(reach2, owners);
|
|
26888
|
+
if (denied.length === 0) return;
|
|
26889
|
+
throw new SubsystemWriteDenied(
|
|
26890
|
+
`SubsystemWriteDenied: ${what} would change ${denied.length === 1 ? "a spec" : "specs"} this request may not change \u2014 ${denied.join("; ")}. Nothing was written.`
|
|
26891
|
+
);
|
|
26892
|
+
}
|
|
26620
26893
|
function unknownKeysIn(raw, parsed, at, out) {
|
|
26621
26894
|
if (Array.isArray(raw)) {
|
|
26622
26895
|
if (!Array.isArray(parsed) || parsed.length !== raw.length) return;
|
|
@@ -28627,6 +28900,92 @@ var SpecWorkspace = class {
|
|
|
28627
28900
|
`The spec file ${file} belongs to the part "${held.part.alias}", fetched from git (${held.source?.git ?? "its repository"} at ${held.part.commit ?? "its pinned commit"}): a git part is read-only here. Edit it in its own repository, commit it there, and take it in with \`wairon member update ${held.part.alias}\`. Nothing was written.`
|
|
28628
28901
|
);
|
|
28629
28902
|
}
|
|
28903
|
+
/**
|
|
28904
|
+
* spec_registry.save step 5 / delete step 2: inside a hosted request, may the
|
|
28905
|
+
* request change this spec? Judged against the request's write reach with the
|
|
28906
|
+
* home's project root and the spec's owning subsystem — the document written
|
|
28907
|
+
* and, when a stored document is replaced or removed, the one stored (a
|
|
28908
|
+
* component moved out of a subsystem changes that subsystem too). Outside a
|
|
28909
|
+
* hosted request nothing is judged.
|
|
28910
|
+
*/
|
|
28911
|
+
assertHomeInReach(file, kind, document) {
|
|
28912
|
+
if (!getWriteReach()) return;
|
|
28913
|
+
const raw = this.homeRootOf(file);
|
|
28914
|
+
const owners = [];
|
|
28915
|
+
if (document !== null) owners.push(this.documentOwner(raw, kind, document));
|
|
28916
|
+
if (pathExists(file)) {
|
|
28917
|
+
try {
|
|
28918
|
+
owners.push(this.documentOwner(raw, kind, readSpecFile(file)));
|
|
28919
|
+
} catch {
|
|
28920
|
+
}
|
|
28921
|
+
}
|
|
28922
|
+
assertSpecWritesPermitted(owners, `writing ${kind} spec ${path21.basename(path21.dirname(file))}/${path21.basename(file)}`);
|
|
28923
|
+
}
|
|
28924
|
+
/** The project root a spec file is homed under: the root whose specs folder, or one of whose parts' folders, holds it (deepest wins). */
|
|
28925
|
+
homeRootOf(file) {
|
|
28926
|
+
this.scanAll();
|
|
28927
|
+
let best;
|
|
28928
|
+
let depth = -1;
|
|
28929
|
+
for (const raw of this.cachedRawRoots) {
|
|
28930
|
+
const homes = [aiPathsAt(raw.dir).specsDir(), ...raw.record.parts.map((p) => p.directory).filter((d) => d !== void 0)];
|
|
28931
|
+
for (const home of homes) {
|
|
28932
|
+
if (isWithin(home, file) && path21.resolve(home).length > depth) {
|
|
28933
|
+
best = raw;
|
|
28934
|
+
depth = path21.resolve(home).length;
|
|
28935
|
+
}
|
|
28936
|
+
}
|
|
28937
|
+
}
|
|
28938
|
+
if (!best) return { dir: this.rootDir, namespace: "", project: path21.basename(this.rootDir) };
|
|
28939
|
+
return { dir: best.dir, namespace: best.record.namespace, project: idOf(best) ?? (best.record.namespace || path21.basename(best.dir)) };
|
|
28940
|
+
}
|
|
28941
|
+
/**
|
|
28942
|
+
* The owner of one spec DOCUMENT as stored in its root (its references local
|
|
28943
|
+
* to that root): a subsystem is its own, a component or type names its
|
|
28944
|
+
* subsystem, an interface or implementation follows its component; the L0, a
|
|
28945
|
+
* system-level type and a type group belong to no subsystem.
|
|
28946
|
+
*/
|
|
28947
|
+
documentOwner(root, kind, document) {
|
|
28948
|
+
const doc = document ?? {};
|
|
28949
|
+
const local = (v) => typeof v === "string" && v ? splitNamespace(v).localId : null;
|
|
28950
|
+
const key = (v) => typeof v === "string" && v ? keyIn(root.namespace, splitNamespace(v).localId) : null;
|
|
28951
|
+
let subsystemId = null;
|
|
28952
|
+
if (kind === "subsystem") subsystemId = local(doc.id);
|
|
28953
|
+
else if (kind === "component" || kind === "type") subsystemId = local(doc.subsystem);
|
|
28954
|
+
else if (kind === "interface") subsystemId = this.componentSubsystem(key(doc.component));
|
|
28955
|
+
else if (kind === "implementation") {
|
|
28956
|
+
const contract = key(doc.contract);
|
|
28957
|
+
subsystemId = this.componentSubsystem(contract ? this.loadInterfaceSpec(contract)?.component ?? null : null);
|
|
28958
|
+
}
|
|
28959
|
+
return { root: root.dir, project: root.project, subsystemId };
|
|
28960
|
+
}
|
|
28961
|
+
/** The local id of a component's subsystem, by the component's key; null when no such component is stored. */
|
|
28962
|
+
componentSubsystem(componentKey) {
|
|
28963
|
+
if (!componentKey) return null;
|
|
28964
|
+
const subsystem = this.loadComponentSpec(componentKey)?.subsystem;
|
|
28965
|
+
return subsystem ? splitNamespace(subsystem).localId : null;
|
|
28966
|
+
}
|
|
28967
|
+
/**
|
|
28968
|
+
* The owner of one IN-MEMORY spec (its references keys, as the loader binds
|
|
28969
|
+
* them) — what a multi-spec write (a rename, a method move) judges before its
|
|
28970
|
+
* first write. `staged` answers a spec the write itself creates.
|
|
28971
|
+
*/
|
|
28972
|
+
specOwner(kind, spec, staged) {
|
|
28973
|
+
this.scanAll();
|
|
28974
|
+
const id = kind === "system" ? "" : String(spec?.id ?? "");
|
|
28975
|
+
const namespace = (kind === "system" ? "" : this.ownerKeyOf(id)) ?? "";
|
|
28976
|
+
const raw = this.cachedRawRoots.find((r) => r.record.namespace === namespace);
|
|
28977
|
+
const root = raw ? { dir: raw.dir, project: idOf(raw) ?? (namespace || path21.basename(raw.dir)) } : { dir: this.rootDir, project: path21.basename(this.rootDir) };
|
|
28978
|
+
const localOfKey = (v) => v ? splitNamespace(v).localId : null;
|
|
28979
|
+
let subsystemId = null;
|
|
28980
|
+
if (kind === "subsystem") subsystemId = localOfKey(id);
|
|
28981
|
+
else if (kind === "component" || kind === "type") subsystemId = localOfKey(spec?.subsystem);
|
|
28982
|
+
else if (kind === "interface") subsystemId = this.componentSubsystem(spec?.component ?? null);
|
|
28983
|
+
else if (kind === "implementation" && spec?.contract) {
|
|
28984
|
+
const component = this.loadInterfaceSpec(spec.contract)?.component ?? staged?.("interface", spec.contract)?.component ?? null;
|
|
28985
|
+
subsystemId = this.componentSubsystem(component);
|
|
28986
|
+
}
|
|
28987
|
+
return { root: root.dir, project: root.project, subsystemId };
|
|
28988
|
+
}
|
|
28630
28989
|
/**
|
|
28631
28990
|
* The key one spec file is approved under (stage 8): its path from the bound
|
|
28632
28991
|
* root, POSIX — or, for a part's file, `members/<alias>/<path inside the
|
|
@@ -28637,13 +28996,15 @@ var SpecWorkspace = class {
|
|
|
28637
28996
|
return approvalKeyIn(file, this.rootDir, this.cachedRoots[0]?.parts ?? []);
|
|
28638
28997
|
}
|
|
28639
28998
|
/** The file store's write, at a home the writer resolved — a part's folder included, a git part's refused. */
|
|
28640
|
-
writeHome(file, document) {
|
|
28999
|
+
writeHome(file, document, kind) {
|
|
28641
29000
|
this.assertHomeWritable(file);
|
|
29001
|
+
this.assertHomeInReach(file, kind, document);
|
|
28642
29002
|
writeSpecFile(file, document);
|
|
28643
29003
|
}
|
|
28644
29004
|
/** The file store's delete, pruning up to the specs folder the file lives in (its part's, for a part's file). */
|
|
28645
|
-
removeHome(file) {
|
|
29005
|
+
removeHome(file, kind) {
|
|
28646
29006
|
this.assertHomeWritable(file);
|
|
29007
|
+
this.assertHomeInReach(file, kind, null);
|
|
28647
29008
|
const part = this.partHolding(file)?.part;
|
|
28648
29009
|
return removeSpecFile(file, part?.directory ? aiPathsAt(part.directory).specsDir() : this.paths.specsDir());
|
|
28649
29010
|
}
|
|
@@ -28994,6 +29355,7 @@ var SpecWorkspace = class {
|
|
|
28994
29355
|
}
|
|
28995
29356
|
saveSystemSpec(spec) {
|
|
28996
29357
|
const p = this.paths.specsSystem();
|
|
29358
|
+
this.assertHomeInReach(p, "system", spec);
|
|
28997
29359
|
ensureDir(path21.dirname(p));
|
|
28998
29360
|
writeSpecFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
|
|
28999
29361
|
invalidateSpecCache();
|
|
@@ -29144,12 +29506,12 @@ var SpecWorkspace = class {
|
|
|
29144
29506
|
}
|
|
29145
29507
|
}
|
|
29146
29508
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
29147
|
-
this.writeHome(p, parseOrThrow(SubsystemSpecSchema, specToWrite, "subsystem", spec.id));
|
|
29509
|
+
this.writeHome(p, parseOrThrow(SubsystemSpecSchema, specToWrite, "subsystem", spec.id), "subsystem");
|
|
29148
29510
|
invalidateSpecCache();
|
|
29149
29511
|
}
|
|
29150
29512
|
deleteSubsystemSpec(id) {
|
|
29151
29513
|
const p = this.getSubsystemPath(id);
|
|
29152
|
-
if (!this.removeHome(p)) return false;
|
|
29514
|
+
if (!this.removeHome(p, "subsystem")) return false;
|
|
29153
29515
|
invalidateSpecCache();
|
|
29154
29516
|
return true;
|
|
29155
29517
|
}
|
|
@@ -29204,14 +29566,14 @@ var SpecWorkspace = class {
|
|
|
29204
29566
|
);
|
|
29205
29567
|
}
|
|
29206
29568
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
29207
|
-
this.writeHome(p, parseOrThrow(ComponentSpecSchema, specToWrite, "component", spec.id));
|
|
29569
|
+
this.writeHome(p, parseOrThrow(ComponentSpecSchema, specToWrite, "component", spec.id), "component");
|
|
29208
29570
|
invalidateSpecCache();
|
|
29209
29571
|
this.normalizeComponentLayout();
|
|
29210
29572
|
return notices;
|
|
29211
29573
|
}
|
|
29212
29574
|
deleteComponentSpec(id) {
|
|
29213
29575
|
const p = this.getComponentPath(id);
|
|
29214
|
-
if (!this.removeHome(p)) return false;
|
|
29576
|
+
if (!this.removeHome(p, "component")) return false;
|
|
29215
29577
|
invalidateSpecCache();
|
|
29216
29578
|
return true;
|
|
29217
29579
|
}
|
|
@@ -29333,13 +29695,13 @@ var SpecWorkspace = class {
|
|
|
29333
29695
|
}
|
|
29334
29696
|
}
|
|
29335
29697
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
29336
|
-
this.writeHome(p, parseOrThrow(InterfaceSpecSchema, specToWrite, "interface", spec.id));
|
|
29698
|
+
this.writeHome(p, parseOrThrow(InterfaceSpecSchema, specToWrite, "interface", spec.id), "interface");
|
|
29337
29699
|
invalidateSpecCache();
|
|
29338
29700
|
return notices;
|
|
29339
29701
|
}
|
|
29340
29702
|
deleteInterfaceSpec(id) {
|
|
29341
29703
|
const p = this.getInterfacePath(id);
|
|
29342
|
-
if (!this.removeHome(p)) return false;
|
|
29704
|
+
if (!this.removeHome(p, "interface")) return false;
|
|
29343
29705
|
invalidateSpecCache();
|
|
29344
29706
|
return true;
|
|
29345
29707
|
}
|
|
@@ -29389,13 +29751,13 @@ var SpecWorkspace = class {
|
|
|
29389
29751
|
}
|
|
29390
29752
|
}
|
|
29391
29753
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
29392
|
-
this.writeHome(p, parseOrThrow(ImplementationSpecSchema, specToWrite, "implementation", spec.id));
|
|
29754
|
+
this.writeHome(p, parseOrThrow(ImplementationSpecSchema, specToWrite, "implementation", spec.id), "implementation");
|
|
29393
29755
|
invalidateSpecCache();
|
|
29394
29756
|
return notices;
|
|
29395
29757
|
}
|
|
29396
29758
|
deleteImplementationSpec(id) {
|
|
29397
29759
|
const p = this.getImplementationPath(id);
|
|
29398
|
-
if (!this.removeHome(p)) return false;
|
|
29760
|
+
if (!this.removeHome(p, "implementation")) return false;
|
|
29399
29761
|
invalidateSpecCache();
|
|
29400
29762
|
return true;
|
|
29401
29763
|
}
|
|
@@ -29441,14 +29803,14 @@ var SpecWorkspace = class {
|
|
|
29441
29803
|
}
|
|
29442
29804
|
}
|
|
29443
29805
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
29444
|
-
this.writeHome(p, parseOrThrow(TypeSpecSchema, specToWrite, "type", spec.id));
|
|
29806
|
+
this.writeHome(p, parseOrThrow(TypeSpecSchema, specToWrite, "type", spec.id), "type");
|
|
29445
29807
|
invalidateSpecCache();
|
|
29446
29808
|
return notices;
|
|
29447
29809
|
}
|
|
29448
29810
|
deleteTypeSpec(id) {
|
|
29449
29811
|
const spec = this.loadTypeSpec(id);
|
|
29450
29812
|
const p = this.getTypePath(id, spec?.subsystem, spec?.group);
|
|
29451
|
-
if (!this.removeHome(p)) return false;
|
|
29813
|
+
if (!this.removeHome(p, "type")) return false;
|
|
29452
29814
|
invalidateSpecCache();
|
|
29453
29815
|
return true;
|
|
29454
29816
|
}
|
|
@@ -29471,12 +29833,12 @@ var SpecWorkspace = class {
|
|
|
29471
29833
|
specToWrite.createdAt = existing.createdAt;
|
|
29472
29834
|
}
|
|
29473
29835
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
29474
|
-
this.writeHome(p, parseOrThrow(GroupSpecSchema, specToWrite, "group", spec.id));
|
|
29836
|
+
this.writeHome(p, parseOrThrow(GroupSpecSchema, specToWrite, "group", spec.id), "group");
|
|
29475
29837
|
invalidateSpecCache();
|
|
29476
29838
|
}
|
|
29477
29839
|
deleteGroupSpec(id) {
|
|
29478
29840
|
const p = this.getGroupPath(id);
|
|
29479
|
-
if (!this.removeHome(p)) return false;
|
|
29841
|
+
if (!this.removeHome(p, "group")) return false;
|
|
29480
29842
|
invalidateSpecCache();
|
|
29481
29843
|
return true;
|
|
29482
29844
|
}
|
|
@@ -29674,7 +30036,7 @@ var SpecWorkspace = class {
|
|
|
29674
30036
|
);
|
|
29675
30037
|
}
|
|
29676
30038
|
if (JSON.stringify(document) === JSON.stringify(stored)) return false;
|
|
29677
|
-
this.writeHome(file, document);
|
|
30039
|
+
this.writeHome(file, document, kind);
|
|
29678
30040
|
invalidateSpecCache();
|
|
29679
30041
|
return true;
|
|
29680
30042
|
}
|
|
@@ -30588,6 +30950,7 @@ var SpecWorkspace = class {
|
|
|
30588
30950
|
}
|
|
30589
30951
|
}
|
|
30590
30952
|
const carried = projectConfigRepository.rekeyCarried(rename, true);
|
|
30953
|
+
this.assertMoveInReach(from, to, plan6, carried.length > 0);
|
|
30591
30954
|
if (dryRun) {
|
|
30592
30955
|
const edits2 = moveChangeReports([...plan6.edits.values()], true);
|
|
30593
30956
|
return {
|
|
@@ -30627,6 +30990,14 @@ var SpecWorkspace = class {
|
|
|
30627
30990
|
carried
|
|
30628
30991
|
};
|
|
30629
30992
|
}
|
|
30993
|
+
/** Refuse a method move, naming every subsystem it may not change, before anything is written. */
|
|
30994
|
+
assertMoveInReach(from, to, plan6, rekeysRegister) {
|
|
30995
|
+
if (!getWriteReach()) return;
|
|
30996
|
+
const staged = (kind, id) => plan6.edits.get(`${kind}:${id}`)?.after;
|
|
30997
|
+
const owners = [...plan6.edits.values()].flatMap((e) => [e.before, e.after].filter((s) => s !== null && s !== void 0).map((s) => this.specOwner(e.kind, s, staged)));
|
|
30998
|
+
if (rekeysRegister) owners.push(this.specOwner("system", null));
|
|
30999
|
+
assertSpecWritesPermitted(owners, `moving methods from "${from}" to "${to}"`);
|
|
31000
|
+
}
|
|
30630
31001
|
/**
|
|
30631
31002
|
* Rekey every lint allow keyed on a moved method, staging each spec it
|
|
30632
31003
|
* changes like any other reference the move follows. An allow anchored at a
|
|
@@ -31357,6 +31728,14 @@ function normalizeReferences(kind, id) {
|
|
|
31357
31728
|
function rewriteReferences(kind, id, edits) {
|
|
31358
31729
|
return current().respellReferences(kind, id, edits);
|
|
31359
31730
|
}
|
|
31731
|
+
function assertSpecsInReach(targets, what, projectRung = false) {
|
|
31732
|
+
if (!getWriteReach()) return;
|
|
31733
|
+
const ws = current();
|
|
31734
|
+
const writable = ["system", "subsystem", "component", "interface", "implementation", "type"];
|
|
31735
|
+
const owners = targets.map((t) => ws.specOwner(t.kind, t.kind === "system" ? null : (writable.includes(t.kind) ? ws.load(t.kind, t.id) : null) ?? { id: t.id }));
|
|
31736
|
+
if (projectRung) owners.push(ws.specOwner("system", null));
|
|
31737
|
+
assertSpecWritesPermitted(owners, what);
|
|
31738
|
+
}
|
|
31360
31739
|
function moveMethods(from, to, methods, hooks, dryRun) {
|
|
31361
31740
|
return current().moveMethods(from, to, methods, hooks, dryRun);
|
|
31362
31741
|
}
|
|
@@ -32922,7 +33301,7 @@ function restateReExports(from, to) {
|
|
|
32922
33301
|
publicInterfaces: entries.map((e) => e.from === from ? { ...e, from: to } : e)
|
|
32923
33302
|
});
|
|
32924
33303
|
}
|
|
32925
|
-
function rewriteRefFields(specsDir, remap, excludeDir) {
|
|
33304
|
+
function rewriteRefFields(specsDir, remap, excludeDir, dryRun = false) {
|
|
32926
33305
|
const rewritten = [];
|
|
32927
33306
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
32928
33307
|
if (excludeDir && isWithinDir2(excludeDir, file)) continue;
|
|
@@ -32935,12 +33314,12 @@ function rewriteRefFields(specsDir, remap, excludeDir) {
|
|
|
32935
33314
|
if (!raw || typeof raw !== "object") continue;
|
|
32936
33315
|
const kind = specKind(raw);
|
|
32937
33316
|
if (!rewriteSpecRefs(raw, remap) || !kind) continue;
|
|
32938
|
-
writeYamlFile(file, raw);
|
|
33317
|
+
if (!dryRun) writeYamlFile(file, raw);
|
|
32939
33318
|
rewritten.push({ kind, id: kind === "system" ? "system" : String(raw.id) });
|
|
32940
33319
|
}
|
|
32941
33320
|
return rewritten;
|
|
32942
33321
|
}
|
|
32943
|
-
function rekeyLintAllows(specsDir, rename) {
|
|
33322
|
+
function rekeyLintAllows(specsDir, rename, dryRun = false) {
|
|
32944
33323
|
const rewritten = [];
|
|
32945
33324
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
32946
33325
|
let raw;
|
|
@@ -32971,7 +33350,7 @@ function rekeyLintAllows(specsDir, rename) {
|
|
|
32971
33350
|
}
|
|
32972
33351
|
}
|
|
32973
33352
|
if (!changed) continue;
|
|
32974
|
-
writeYamlFile(file, raw);
|
|
33353
|
+
if (!dryRun) writeYamlFile(file, raw);
|
|
32975
33354
|
rewritten.push({ kind, id: specId });
|
|
32976
33355
|
}
|
|
32977
33356
|
return rewritten;
|
|
@@ -33049,15 +33428,22 @@ function renameComponent(componentId, newId) {
|
|
|
33049
33428
|
specs: renamed.map(({ from, to }) => ({ from, to })),
|
|
33050
33429
|
components: [{ from: componentId, to: newId }]
|
|
33051
33430
|
};
|
|
33052
|
-
projectConfigRepository.rekeyCarried(rename, true);
|
|
33053
|
-
const
|
|
33431
|
+
const registerEdits = projectConfigRepository.rekeyCarried(rename, true);
|
|
33432
|
+
const remap = (ref, position) => {
|
|
33054
33433
|
if (position === "component" || position === "entity-class" || position === "auth-source") {
|
|
33055
33434
|
return ref === componentId ? newId : ref;
|
|
33056
33435
|
}
|
|
33057
33436
|
if (position === "interface" && ref === movingInterface?.id) return interfaceId;
|
|
33058
33437
|
return ref;
|
|
33059
|
-
}
|
|
33060
|
-
const
|
|
33438
|
+
};
|
|
33439
|
+
const specsDir = aiPathsAt(getProjectRoot()).specsDir();
|
|
33440
|
+
assertSpecsInReach(
|
|
33441
|
+
[...renamed.map(({ kind, from }) => ({ kind, id: from })), ...rewriteRefFields(specsDir, remap, void 0, true), ...rekeyLintAllows(specsDir, rename, true)],
|
|
33442
|
+
`renaming component "${componentId}"`,
|
|
33443
|
+
Array.isArray(registerEdits) && registerEdits.length > 0
|
|
33444
|
+
);
|
|
33445
|
+
const rewrittenRefs = rewriteRefFields(specsDir, remap);
|
|
33446
|
+
const allowsRekeyed = rekeyLintAllows(specsDir, rename);
|
|
33061
33447
|
const rewritten = [...new Set([...rewrittenRefs, ...allowsRekeyed].filter((spec) => !renamed.some((moved) => moved.kind === spec.kind && moved.from === spec.id)).map((spec) => spec.id))];
|
|
33062
33448
|
invalidateSpecCache();
|
|
33063
33449
|
moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation);
|
|
@@ -33147,7 +33533,18 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
33147
33533
|
]
|
|
33148
33534
|
}]
|
|
33149
33535
|
};
|
|
33150
|
-
projectConfigRepository.rekeyCarried(rename, true);
|
|
33536
|
+
const registerEdits = projectConfigRepository.rekeyCarried(rename, true);
|
|
33537
|
+
const methodRemap = (ref, position, owner) => position === "method" && owner === componentId && ref === methodName ? newName : ref;
|
|
33538
|
+
const reachDir = aiPathsAt(getProjectRoot()).specsDir();
|
|
33539
|
+
assertSpecsInReach(
|
|
33540
|
+
[
|
|
33541
|
+
...(rename.methods?.[0]?.specs ?? []).map((id) => ({ kind: moving.some((i) => i.id === id) ? "interface" : "implementation", id })),
|
|
33542
|
+
...rewriteRefFields(reachDir, methodRemap, void 0, true),
|
|
33543
|
+
...rekeyLintAllows(reachDir, rename, true)
|
|
33544
|
+
],
|
|
33545
|
+
`renaming method "${componentId}.${methodName}"`,
|
|
33546
|
+
Array.isArray(registerEdits) && registerEdits.length > 0
|
|
33547
|
+
);
|
|
33151
33548
|
for (const contract of moving) {
|
|
33152
33549
|
saveInterfaceSpec({
|
|
33153
33550
|
...contract,
|
|
@@ -33170,7 +33567,7 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
33170
33567
|
}
|
|
33171
33568
|
invalidateSpecCache();
|
|
33172
33569
|
const specsDir = aiPathsAt(getProjectRoot()).specsDir();
|
|
33173
|
-
const retargeted = rewriteRefFields(specsDir,
|
|
33570
|
+
const retargeted = rewriteRefFields(specsDir, methodRemap);
|
|
33174
33571
|
const allowsRekeyed = rekeyLintAllows(specsDir, rename);
|
|
33175
33572
|
const rewritten = [...new Set([...retargeted, ...allowsRekeyed].map((spec) => spec.id))];
|
|
33176
33573
|
const mentions = collectMentions(specsDir, methodName, moving);
|
|
@@ -39209,10 +39606,13 @@ function rehearse2(plan6) {
|
|
|
39209
39606
|
return { ...plan6, refusals: [...plan6.refusals, { code: `${plan6.request.verb}-refused`, project: "", detail }] };
|
|
39210
39607
|
}
|
|
39211
39608
|
const changes = diff(rehearsal);
|
|
39212
|
-
const
|
|
39213
|
-
|
|
39609
|
+
const refused2 = [
|
|
39610
|
+
...unwritableOwners(changes).map((o) => ({ code: "family-partial", project: o, detail: `the plan must write ${o}, which this request may not write` })),
|
|
39611
|
+
...deniedSubsystems(changes)
|
|
39612
|
+
];
|
|
39613
|
+
if (refused2.length > 0) {
|
|
39214
39614
|
discard(rehearsal);
|
|
39215
|
-
return { ...plan6, refusals: [...plan6.refusals, ...
|
|
39615
|
+
return { ...plan6, refusals: [...plan6.refusals, ...refused2] };
|
|
39216
39616
|
}
|
|
39217
39617
|
return { ...plan6, rehearsal, changes, relock: relockOf(plan6, changes) };
|
|
39218
39618
|
}
|
|
@@ -39256,6 +39656,16 @@ function unwritableOwners(changes) {
|
|
|
39256
39656
|
const owners = [...new Set(changes.map((c) => path47.resolve(c.project)))];
|
|
39257
39657
|
return owners.filter((o) => denied.includes(o));
|
|
39258
39658
|
}
|
|
39659
|
+
function deniedSubsystems(changes) {
|
|
39660
|
+
const reach2 = getWriteReach();
|
|
39661
|
+
if (!reach2) return [];
|
|
39662
|
+
const owners = [...new Set(changes.map((c) => path47.resolve(c.project)))];
|
|
39663
|
+
return owners.flatMap((o) => writeReachDenials(reach2, o).map((s) => ({
|
|
39664
|
+
code: "subsystem-denied",
|
|
39665
|
+
project: o,
|
|
39666
|
+
detail: `the plan must change ${o}, which holds subsystem "${s.subsystemId}" this request may not change (decided by ${s.decidedBy}); a family migration reshapes its projects whole, so it needs write on every subsystem of every project it changes`
|
|
39667
|
+
})));
|
|
39668
|
+
}
|
|
39259
39669
|
function pathProducers(plan6, family) {
|
|
39260
39670
|
const inside = (dir) => {
|
|
39261
39671
|
const rel2 = path47.relative(plan6.familyRoot, dir);
|
|
@@ -39525,7 +39935,10 @@ var externalStatusesOutput = {
|
|
|
39525
39935
|
stale: import_zod11.z.boolean(),
|
|
39526
39936
|
drifted: import_zod11.z.boolean().optional(),
|
|
39527
39937
|
uses: import_zod11.z.array(import_zod11.z.record(import_zod11.z.unknown())),
|
|
39528
|
-
detail: import_zod11.z.string().optional()
|
|
39938
|
+
detail: import_zod11.z.string().optional(),
|
|
39939
|
+
outOfReach: import_zod11.z.boolean().optional(),
|
|
39940
|
+
pinnedDigest: import_zod11.z.string().optional().describe("The content digest the lock recorded; provenance, never compared here."),
|
|
39941
|
+
pinnedCommit: import_zod11.z.string().optional().describe("The producer commit the lock recorded, for one in another repository; provenance only.")
|
|
39529
39942
|
})).describe("One status per declared external, in declaration order; EXTERNAL_CHECK_UNAVAILABLE marks what could not be compared."),
|
|
39530
39943
|
...staleServerOutput
|
|
39531
39944
|
};
|
|
@@ -41613,6 +42026,7 @@ function loadProjectConfig3() {
|
|
|
41613
42026
|
getRequestParentReach,
|
|
41614
42027
|
getRequestProjectRoot,
|
|
41615
42028
|
getStatusReport,
|
|
42029
|
+
getWriteReach,
|
|
41616
42030
|
globalGuideFilePath,
|
|
41617
42031
|
globalPacksDir,
|
|
41618
42032
|
globalPacksEnabled,
|
|
@@ -41717,6 +42131,7 @@ function loadProjectConfig3() {
|
|
|
41717
42131
|
registerExporter,
|
|
41718
42132
|
registerPackRef,
|
|
41719
42133
|
reinjectLocalGuides,
|
|
42134
|
+
relationHealth,
|
|
41720
42135
|
removeDomain,
|
|
41721
42136
|
removeExternal,
|
|
41722
42137
|
removeMember,
|
|
@@ -41783,6 +42198,8 @@ function loadProjectConfig3() {
|
|
|
41783
42198
|
writeFileIfChanged,
|
|
41784
42199
|
writeJsonFile,
|
|
41785
42200
|
writeLockRecord,
|
|
42201
|
+
writeReachDenials,
|
|
42202
|
+
writeReachPermits,
|
|
41786
42203
|
writeRootGuideDelegator,
|
|
41787
42204
|
writeYamlFile
|
|
41788
42205
|
});
|