archctx 0.9.4 → 0.10.0
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/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +599 -481
- package/package.json +1 -1
package/bin/archctx.mjs
CHANGED
|
@@ -1961,6 +1961,450 @@ var init_projection = __esm(() => {
|
|
|
1961
1961
|
ISO_INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
|
|
1962
1962
|
});
|
|
1963
1963
|
|
|
1964
|
+
// packages/contracts/src/projection-manifest.ts
|
|
1965
|
+
function isRuntimeTopologyTargetType(type) {
|
|
1966
|
+
return typeof type === "string" && RUNTIME_TOPOLOGY_TARGET_TYPES.includes(type);
|
|
1967
|
+
}
|
|
1968
|
+
function architectureDocsProjectionManifestIssues(value) {
|
|
1969
|
+
const issues = [];
|
|
1970
|
+
const manifest = record2(value);
|
|
1971
|
+
if (!manifest)
|
|
1972
|
+
return ["manifest must be an object"];
|
|
1973
|
+
issues.push(...exactKeys(manifest, ["schemaVersion", "rendererVersion", "profile", "layoutVersion", "generatedFrom", "semanticBaseline", "targets"], ["minimumRendererVersion", "minimumProtocolVersion"], "manifest"));
|
|
1974
|
+
if (manifest.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION)
|
|
1975
|
+
issues.push("manifest.schemaVersion is unsupported");
|
|
1976
|
+
if (!isNonEmptyString(manifest.rendererVersion))
|
|
1977
|
+
issues.push("manifest.rendererVersion must be a non-empty string");
|
|
1978
|
+
for (const [key, family] of [["minimumRendererVersion", "archcontext.docs-renderer/v"], ["minimumProtocolVersion", "archcontext.projection-request/v"]]) {
|
|
1979
|
+
if (Object.hasOwn(manifest, key) && versionNumber(manifest[key], family) === undefined)
|
|
1980
|
+
issues.push(`manifest.${key} must name a supported version family`);
|
|
1981
|
+
}
|
|
1982
|
+
if (!ARCHITECTURE_DOCS_PROJECTION_PROFILES.includes(manifest.profile))
|
|
1983
|
+
issues.push("manifest.profile is unsupported");
|
|
1984
|
+
if (manifest.layoutVersion !== ARCHITECTURE_DOCS_LAYOUT_VERSION_V1)
|
|
1985
|
+
issues.push("manifest.layoutVersion is unsupported");
|
|
1986
|
+
issues.push(...generatedFromIssues(manifest.generatedFrom, "manifest.generatedFrom"));
|
|
1987
|
+
const baseline = record2(manifest.semanticBaseline);
|
|
1988
|
+
if (!baseline)
|
|
1989
|
+
issues.push("manifest.semanticBaseline must be an object");
|
|
1990
|
+
else {
|
|
1991
|
+
issues.push(...exactKeys(baseline, ["modelDigest", "capabilities"], [], "manifest.semanticBaseline"));
|
|
1992
|
+
if (!isDigest(baseline.modelDigest))
|
|
1993
|
+
issues.push("manifest.semanticBaseline.modelDigest must be a SHA-256 digest");
|
|
1994
|
+
issues.push(...capabilityEntriesIssues(baseline.capabilities, "manifest.semanticBaseline.capabilities", true));
|
|
1995
|
+
}
|
|
1996
|
+
const targets = record2(manifest.targets);
|
|
1997
|
+
if (!targets) {
|
|
1998
|
+
issues.push("manifest.targets must be an object keyed by targetId");
|
|
1999
|
+
return issues;
|
|
2000
|
+
}
|
|
2001
|
+
const paths = new Set;
|
|
2002
|
+
const stampOwners = new Map;
|
|
2003
|
+
let topologyStamps;
|
|
2004
|
+
for (const [targetId, row] of Object.entries(targets)) {
|
|
2005
|
+
const prefix = `manifest.targets[${JSON.stringify(targetId)}]`;
|
|
2006
|
+
if (!isNonEmptyString(targetId))
|
|
2007
|
+
issues.push("manifest.targets keys must be non-empty targetIds");
|
|
2008
|
+
issues.push(...manifestTargetIssues(row, prefix));
|
|
2009
|
+
const path = record2(row)?.path;
|
|
2010
|
+
if (typeof path === "string") {
|
|
2011
|
+
if (paths.has(path))
|
|
2012
|
+
issues.push(`${prefix}.path is recorded by another target`);
|
|
2013
|
+
paths.add(path);
|
|
2014
|
+
}
|
|
2015
|
+
const stamps = record2(record2(row)?.sourceStamps) ?? {};
|
|
2016
|
+
if (isRuntimeTopologyTargetType(record2(row)?.type)) {
|
|
2017
|
+
const serialized = JSON.stringify(Object.keys(stamps).sort(compareStrings).map((nodeId) => [nodeId, stamps[nodeId]]));
|
|
2018
|
+
if (topologyStamps === undefined)
|
|
2019
|
+
topologyStamps = { targetId, stamps: serialized };
|
|
2020
|
+
else if (topologyStamps.stamps !== serialized)
|
|
2021
|
+
issues.push(`${prefix}.sourceStamps must equal the sourceStamps of ${topologyStamps.targetId}`);
|
|
2022
|
+
for (const nodeId of Object.keys(stamps)) {
|
|
2023
|
+
const owner = stampOwners.get(nodeId);
|
|
2024
|
+
if (owner !== undefined && !isRuntimeTopologyTargetType(record2(targets[owner])?.type))
|
|
2025
|
+
issues.push(`${prefix}.sourceStamps.${nodeId} is already stamped on ${owner}`);
|
|
2026
|
+
else if (owner === undefined)
|
|
2027
|
+
stampOwners.set(nodeId, targetId);
|
|
2028
|
+
}
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
for (const nodeId of Object.keys(stamps)) {
|
|
2032
|
+
const owner = stampOwners.get(nodeId);
|
|
2033
|
+
if (owner !== undefined)
|
|
2034
|
+
issues.push(`${prefix}.sourceStamps.${nodeId} is already stamped on ${owner}`);
|
|
2035
|
+
else
|
|
2036
|
+
stampOwners.set(nodeId, targetId);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return issues;
|
|
2040
|
+
}
|
|
2041
|
+
function architectureDocsProjectionManifestSchemaVersion(body) {
|
|
2042
|
+
let parsed;
|
|
2043
|
+
try {
|
|
2044
|
+
parsed = JSON.parse(body);
|
|
2045
|
+
} catch {
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
const schemaVersion = record2(parsed)?.schemaVersion;
|
|
2049
|
+
return typeof schemaVersion === "string" ? schemaVersion : undefined;
|
|
2050
|
+
}
|
|
2051
|
+
function isSupersededArchitectureDocsProjectionManifestSchemaVersion(schemaVersion) {
|
|
2052
|
+
return SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS.includes(schemaVersion);
|
|
2053
|
+
}
|
|
2054
|
+
function serializeArchitectureDocsProjectionManifest(manifest) {
|
|
2055
|
+
const capabilities = [...manifest.semanticBaseline.capabilities].sort((left, right) => compareStrings(left.capabilityId, right.capabilityId)).map(canonicalCapability);
|
|
2056
|
+
const capabilityBlock = JSON.stringify(capabilities, null, 2).split(`
|
|
2057
|
+
`).map((line, index) => index === 0 ? line : ` ${line}`).join(`
|
|
2058
|
+
`);
|
|
2059
|
+
const targetIds = Object.keys(manifest.targets).sort(compareStrings);
|
|
2060
|
+
const targetLines = targetIds.length === 0 ? [' "targets": {}'] : [
|
|
2061
|
+
' "targets": {',
|
|
2062
|
+
...targetIds.flatMap((targetId, index) => [
|
|
2063
|
+
` ${JSON.stringify(targetId)}:`,
|
|
2064
|
+
` ${JSON.stringify(canonicalTargetRow(manifest.targets[targetId]))}${index === targetIds.length - 1 ? "" : ","}`
|
|
2065
|
+
]),
|
|
2066
|
+
" }"
|
|
2067
|
+
];
|
|
2068
|
+
return [
|
|
2069
|
+
"{",
|
|
2070
|
+
` "schemaVersion": ${JSON.stringify(manifest.schemaVersion)},`,
|
|
2071
|
+
` "rendererVersion": ${JSON.stringify(manifest.rendererVersion)},`,
|
|
2072
|
+
...manifest.minimumRendererVersion === undefined ? [] : [` "minimumRendererVersion": ${JSON.stringify(manifest.minimumRendererVersion)},`],
|
|
2073
|
+
...manifest.minimumProtocolVersion === undefined ? [] : [` "minimumProtocolVersion": ${JSON.stringify(manifest.minimumProtocolVersion)},`],
|
|
2074
|
+
` "profile": ${JSON.stringify(manifest.profile)},`,
|
|
2075
|
+
` "layoutVersion": ${JSON.stringify(manifest.layoutVersion)},`,
|
|
2076
|
+
` "generatedFrom": ${JSON.stringify({ codeGraphPackage: manifest.generatedFrom.codeGraphPackage, codeGraphVersion: manifest.generatedFrom.codeGraphVersion })},`,
|
|
2077
|
+
' "semanticBaseline": {',
|
|
2078
|
+
` "modelDigest": ${JSON.stringify(manifest.semanticBaseline.modelDigest)},`,
|
|
2079
|
+
` "capabilities": ${capabilityBlock}`,
|
|
2080
|
+
" },",
|
|
2081
|
+
...targetLines,
|
|
2082
|
+
"}",
|
|
2083
|
+
""
|
|
2084
|
+
].join(`
|
|
2085
|
+
`);
|
|
2086
|
+
}
|
|
2087
|
+
function architectureDocsProjectionManifestSemanticState(manifest) {
|
|
2088
|
+
return architectureSemanticStateFromCapabilities(manifest.semanticBaseline.capabilities.map(({ selectorEvidenceDigest: _selectorEvidenceDigest, ...capability }) => capability));
|
|
2089
|
+
}
|
|
2090
|
+
function architectureSemanticStateFromCapabilities(capabilities) {
|
|
2091
|
+
const sorted = [...capabilities].sort((left, right) => compareStrings(left.capabilityId, right.capabilityId));
|
|
2092
|
+
return {
|
|
2093
|
+
schemaVersion: ARCHITECTURE_SEMANTIC_STATE_SCHEMA_VERSION,
|
|
2094
|
+
capabilities: sorted,
|
|
2095
|
+
semanticFingerprint: digestJson(sorted.map(({ capabilityId, semanticFingerprint }) => ({ capabilityId, semanticFingerprint }))),
|
|
2096
|
+
flowProofFingerprint: digestJson(sorted.map(({ capabilityId, flowProofFingerprint }) => ({ capabilityId, flowProofFingerprint })))
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
function architectureDocsProjectionManifestSourceStamps(manifest) {
|
|
2100
|
+
const stamps = {};
|
|
2101
|
+
for (const row of Object.values(manifest.targets))
|
|
2102
|
+
Object.assign(stamps, row.sourceStamps ?? {});
|
|
2103
|
+
return stamps;
|
|
2104
|
+
}
|
|
2105
|
+
function architectureDocsProjectionManifestProofEvidence(manifest) {
|
|
2106
|
+
return {
|
|
2107
|
+
rendererVersion: manifest.rendererVersion,
|
|
2108
|
+
selectorEvidenceDigests: Object.fromEntries(manifest.semanticBaseline.capabilities.map((capability) => [capability.capabilityId, capability.selectorEvidenceDigest])),
|
|
2109
|
+
sourceStamps: architectureDocsProjectionManifestSourceStamps(manifest)
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
function architectureSourceStampsDigest(stamps) {
|
|
2113
|
+
return digestJson({
|
|
2114
|
+
schemaVersion: "archcontext.architecture-source-stamps/v1",
|
|
2115
|
+
stamps: Object.keys(stamps).sort(compareStrings).map((nodeId) => ({ nodeId, digest: stamps[nodeId] }))
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
function architectureProjectionOutputsDigest(input) {
|
|
2119
|
+
return digestJson({
|
|
2120
|
+
schemaVersion: "archcontext.architecture-projection-outputs/v1",
|
|
2121
|
+
rendererVersion: input.rendererVersion,
|
|
2122
|
+
profile: input.profile,
|
|
2123
|
+
outputs: Object.keys(input.outputDigests).sort(compareStrings).map((targetId) => ({ targetId, outputDigest: input.outputDigests[targetId] }))
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
function architectureDocsProjectionManifestDigests(manifest) {
|
|
2127
|
+
return {
|
|
2128
|
+
modelDigest: manifest.semanticBaseline.modelDigest,
|
|
2129
|
+
sourceTreeDigest: architectureSourceStampsDigest(architectureDocsProjectionManifestSourceStamps(manifest)),
|
|
2130
|
+
flowProofDigest: architectureDocsProjectionManifestSemanticState(manifest).flowProofFingerprint,
|
|
2131
|
+
projectionDigest: architectureProjectionOutputsDigest({
|
|
2132
|
+
rendererVersion: manifest.rendererVersion,
|
|
2133
|
+
profile: manifest.profile,
|
|
2134
|
+
outputDigests: Object.fromEntries(Object.entries(manifest.targets).map(([targetId, row]) => [targetId, row.outputDigest]))
|
|
2135
|
+
})
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
function architectureDocsProjectionProvenanceIssues(value, prefix = "provenance") {
|
|
2139
|
+
const provenance = record2(value);
|
|
2140
|
+
if (!provenance)
|
|
2141
|
+
return [`${prefix} must be an object`];
|
|
2142
|
+
const issues = exactKeys(provenance, ["schemaVersion", "sourceTreeDigest", "modelDigest", "projectionInputDigest", "rendererVersion", "layoutVersion", "generatedFrom"], [], prefix);
|
|
2143
|
+
if (provenance.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_PROVENANCE_SCHEMA_VERSION)
|
|
2144
|
+
issues.push(`${prefix}.schemaVersion is unsupported`);
|
|
2145
|
+
for (const field of ["sourceTreeDigest", "modelDigest", "projectionInputDigest"]) {
|
|
2146
|
+
if (!isDigest(provenance[field]))
|
|
2147
|
+
issues.push(`${prefix}.${field} must be a SHA-256 digest`);
|
|
2148
|
+
}
|
|
2149
|
+
if (provenance.rendererVersion !== ARCHITECTURE_DOCS_RENDERER_VERSION)
|
|
2150
|
+
issues.push(`${prefix}.rendererVersion must be ${ARCHITECTURE_DOCS_RENDERER_VERSION}`);
|
|
2151
|
+
if (provenance.layoutVersion !== ARCHITECTURE_DOCS_LAYOUT_VERSION_V1)
|
|
2152
|
+
issues.push(`${prefix}.layoutVersion is unsupported`);
|
|
2153
|
+
issues.push(...generatedFromIssues(provenance.generatedFrom, `${prefix}.generatedFrom`));
|
|
2154
|
+
return issues;
|
|
2155
|
+
}
|
|
2156
|
+
function capabilityEntriesIssues(value, prefix, withSelectorEvidence) {
|
|
2157
|
+
if (!Array.isArray(value))
|
|
2158
|
+
return [`${prefix} must be an array`];
|
|
2159
|
+
const issues = [];
|
|
2160
|
+
const ids = value.map((entry) => record2(entry)?.capabilityId);
|
|
2161
|
+
if (!isSortedUniqueStrings(ids))
|
|
2162
|
+
issues.push(`${prefix} must be sorted and unique by capabilityId`);
|
|
2163
|
+
value.forEach((entry, index) => {
|
|
2164
|
+
const entryPrefix = `${prefix}[${index}]`;
|
|
2165
|
+
const capability = record2(entry);
|
|
2166
|
+
if (!capability) {
|
|
2167
|
+
issues.push(`${entryPrefix} must be an object`);
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
const required = ["capabilityId", "memberNodeIds", "semanticFingerprint", "flowProofFingerprint", "proofStatus", "facets", ...withSelectorEvidence ? ["selectorEvidenceDigest"] : []];
|
|
2171
|
+
issues.push(...exactKeys(capability, required, [], entryPrefix));
|
|
2172
|
+
if (!isNonEmptyString(capability.capabilityId))
|
|
2173
|
+
issues.push(`${entryPrefix}.capabilityId must be a non-empty string`);
|
|
2174
|
+
if (!Array.isArray(capability.memberNodeIds) || !isSortedUniqueStrings(capability.memberNodeIds))
|
|
2175
|
+
issues.push(`${entryPrefix}.memberNodeIds must be sorted and unique`);
|
|
2176
|
+
if (!isDigest(capability.semanticFingerprint))
|
|
2177
|
+
issues.push(`${entryPrefix}.semanticFingerprint must be a SHA-256 digest`);
|
|
2178
|
+
if (!isDigest(capability.flowProofFingerprint))
|
|
2179
|
+
issues.push(`${entryPrefix}.flowProofFingerprint must be a SHA-256 digest`);
|
|
2180
|
+
if (withSelectorEvidence && !isDigest(capability.selectorEvidenceDigest))
|
|
2181
|
+
issues.push(`${entryPrefix}.selectorEvidenceDigest must be a SHA-256 digest`);
|
|
2182
|
+
if (!isArchitectureCapabilityProofStatus(capability.proofStatus))
|
|
2183
|
+
issues.push(`${entryPrefix}.proofStatus must be a P1/P2 proof status`);
|
|
2184
|
+
const facets = record2(capability.facets);
|
|
2185
|
+
if (!facets)
|
|
2186
|
+
issues.push(`${entryPrefix}.facets must be an object`);
|
|
2187
|
+
else {
|
|
2188
|
+
issues.push(...exactKeys(facets, [...ARCHITECTURE_SEMANTIC_FACETS], [], `${entryPrefix}.facets`));
|
|
2189
|
+
for (const facet of ARCHITECTURE_SEMANTIC_FACETS) {
|
|
2190
|
+
if (!isDigest(facets[facet]))
|
|
2191
|
+
issues.push(`${entryPrefix}.facets.${facet} must be a SHA-256 digest`);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
});
|
|
2195
|
+
return issues;
|
|
2196
|
+
}
|
|
2197
|
+
function generatedFromIssues(value, prefix) {
|
|
2198
|
+
const generatedFrom = record2(value);
|
|
2199
|
+
if (!generatedFrom)
|
|
2200
|
+
return [`${prefix} must be an object`];
|
|
2201
|
+
const issues = exactKeys(generatedFrom, ["codeGraphPackage", "codeGraphVersion"], [], prefix);
|
|
2202
|
+
for (const field of ["codeGraphPackage", "codeGraphVersion"]) {
|
|
2203
|
+
if (!isNonEmptyString(generatedFrom[field]))
|
|
2204
|
+
issues.push(`${prefix}.${field} must be a non-empty string`);
|
|
2205
|
+
}
|
|
2206
|
+
return issues;
|
|
2207
|
+
}
|
|
2208
|
+
function canonicalCapability(capability) {
|
|
2209
|
+
return {
|
|
2210
|
+
capabilityId: capability.capabilityId,
|
|
2211
|
+
memberNodeIds: [...capability.memberNodeIds],
|
|
2212
|
+
semanticFingerprint: capability.semanticFingerprint,
|
|
2213
|
+
flowProofFingerprint: capability.flowProofFingerprint,
|
|
2214
|
+
proofStatus: { p1: capability.proofStatus.p1, p2: capability.proofStatus.p2 },
|
|
2215
|
+
facets: Object.fromEntries(Object.keys(capability.facets).sort(compareStrings).map((facet) => [facet, capability.facets[facet]])),
|
|
2216
|
+
selectorEvidenceDigest: capability.selectorEvidenceDigest
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
function canonicalTargetRow(row) {
|
|
2220
|
+
return {
|
|
2221
|
+
type: row.type,
|
|
2222
|
+
scope: {
|
|
2223
|
+
kind: row.scope.kind,
|
|
2224
|
+
...row.scope.id !== undefined ? { id: row.scope.id } : {},
|
|
2225
|
+
...row.scope.entityKind !== undefined ? { entityKind: row.scope.entityKind } : {}
|
|
2226
|
+
},
|
|
2227
|
+
path: row.path,
|
|
2228
|
+
ownership: row.ownership,
|
|
2229
|
+
format: row.format,
|
|
2230
|
+
outputDigest: row.outputDigest,
|
|
2231
|
+
...row.sourceStamps !== undefined ? { sourceStamps: Object.fromEntries(Object.keys(row.sourceStamps).sort(compareStrings).map((nodeId) => [nodeId, row.sourceStamps[nodeId]])) } : {},
|
|
2232
|
+
...row.scale !== undefined ? {
|
|
2233
|
+
scale: {
|
|
2234
|
+
fileCountBucket: { lower: row.scale.fileCountBucket.lower, upper: row.scale.fileCountBucket.upper },
|
|
2235
|
+
lineCountBucket: { lower: row.scale.lineCountBucket.lower, upper: row.scale.lineCountBucket.upper }
|
|
2236
|
+
}
|
|
2237
|
+
} : {}
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
function isArchitectureScaleBucket(value) {
|
|
2241
|
+
const bucket = record2(value);
|
|
2242
|
+
if (!bucket || Object.keys(bucket).length !== 2)
|
|
2243
|
+
return false;
|
|
2244
|
+
const { lower, upper } = bucket;
|
|
2245
|
+
if (typeof lower !== "number" || typeof upper !== "number" || !Number.isSafeInteger(lower) || !Number.isSafeInteger(upper))
|
|
2246
|
+
return false;
|
|
2247
|
+
if (lower === 0)
|
|
2248
|
+
return upper === 1;
|
|
2249
|
+
let magnitude = 1;
|
|
2250
|
+
while (magnitude * 10 <= lower)
|
|
2251
|
+
magnitude *= 10;
|
|
2252
|
+
const mantissa = lower / magnitude;
|
|
2253
|
+
if (mantissa === 1)
|
|
2254
|
+
return upper === 2 * magnitude;
|
|
2255
|
+
if (mantissa === 2)
|
|
2256
|
+
return upper === 5 * magnitude;
|
|
2257
|
+
if (mantissa === 5)
|
|
2258
|
+
return upper === 10 * magnitude;
|
|
2259
|
+
return false;
|
|
2260
|
+
}
|
|
2261
|
+
function manifestTargetIssues(value, prefix) {
|
|
2262
|
+
const target = record2(value);
|
|
2263
|
+
if (!target)
|
|
2264
|
+
return [`${prefix} must be an object`];
|
|
2265
|
+
const issues = exactKeys(target, ["type", "scope", "path", "ownership", "format", "outputDigest"], ["sourceStamps", "scale"], prefix);
|
|
2266
|
+
if (typeof target.type !== "string" || !Object.hasOwn(TARGET_TYPES, target.type))
|
|
2267
|
+
issues.push(`${prefix}.type is unsupported`);
|
|
2268
|
+
const scope = record2(target.scope);
|
|
2269
|
+
if (!scope || typeof scope.kind !== "string" || !Object.hasOwn(SCOPE_KINDS, scope.kind))
|
|
2270
|
+
issues.push(`${prefix}.scope.kind is unsupported`);
|
|
2271
|
+
else {
|
|
2272
|
+
issues.push(...exactKeys(scope, ["kind"], ["id", "entityKind"], `${prefix}.scope`));
|
|
2273
|
+
for (const field of ["id", "entityKind"]) {
|
|
2274
|
+
if (scope[field] !== undefined && typeof scope[field] !== "string")
|
|
2275
|
+
issues.push(`${prefix}.scope.${field} must be a string`);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
if (typeof target.path !== "string" || !isRepoRelativePosixPath(target.path))
|
|
2279
|
+
issues.push(`${prefix}.path must be a repository-relative POSIX path`);
|
|
2280
|
+
if (typeof target.ownership !== "string" || !Object.hasOwn(OWNERSHIPS, target.ownership))
|
|
2281
|
+
issues.push(`${prefix}.ownership is unsupported`);
|
|
2282
|
+
if (typeof target.format !== "string" || !Object.hasOwn(FORMATS, target.format))
|
|
2283
|
+
issues.push(`${prefix}.format is unsupported`);
|
|
2284
|
+
if (!isDigest(target.outputDigest))
|
|
2285
|
+
issues.push(`${prefix}.outputDigest must be a SHA-256 digest`);
|
|
2286
|
+
if (target.sourceStamps !== undefined && target.type !== "entity-summary" && !isRuntimeTopologyTargetType(target.type)) {
|
|
2287
|
+
issues.push(`${prefix}.sourceStamps are only allowed on entity-summary and runtime topology targets`);
|
|
2288
|
+
}
|
|
2289
|
+
if (target.scale !== undefined && target.type !== "entity-summary")
|
|
2290
|
+
issues.push(`${prefix}.scale is only allowed on entity-summary targets`);
|
|
2291
|
+
if (target.sourceStamps !== undefined) {
|
|
2292
|
+
const stamps = record2(target.sourceStamps);
|
|
2293
|
+
if (!stamps || Object.keys(stamps).length === 0)
|
|
2294
|
+
issues.push(`${prefix}.sourceStamps must be a non-empty object keyed by node id`);
|
|
2295
|
+
else {
|
|
2296
|
+
for (const [nodeId, digest] of Object.entries(stamps)) {
|
|
2297
|
+
if (!isNonEmptyString(nodeId))
|
|
2298
|
+
issues.push(`${prefix}.sourceStamps keys must be non-empty node ids`);
|
|
2299
|
+
if (!isDigest(digest))
|
|
2300
|
+
issues.push(`${prefix}.sourceStamps.${nodeId} must be a SHA-256 digest`);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
const ownNodeId = scope?.id;
|
|
2305
|
+
const stampsOwnNode = typeof ownNodeId === "string" && record2(target.sourceStamps) !== undefined && Object.hasOwn(record2(target.sourceStamps), ownNodeId);
|
|
2306
|
+
if (stampsOwnNode !== (target.scale !== undefined))
|
|
2307
|
+
issues.push(`${prefix}.scale must be present exactly when sourceStamps stamps the target's own node`);
|
|
2308
|
+
if (target.scale !== undefined) {
|
|
2309
|
+
const scale = record2(target.scale);
|
|
2310
|
+
if (!scale)
|
|
2311
|
+
issues.push(`${prefix}.scale must be an object`);
|
|
2312
|
+
else {
|
|
2313
|
+
issues.push(...exactKeys(scale, ["fileCountBucket", "lineCountBucket"], [], `${prefix}.scale`));
|
|
2314
|
+
for (const field of ["fileCountBucket", "lineCountBucket"]) {
|
|
2315
|
+
if (!isArchitectureScaleBucket(scale[field]))
|
|
2316
|
+
issues.push(`${prefix}.scale.${field} must be a 1-2-5 magnitude bucket`);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
return issues;
|
|
2321
|
+
}
|
|
2322
|
+
function record2(value) {
|
|
2323
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
2324
|
+
}
|
|
2325
|
+
function exactKeys(value, required, optional, prefix) {
|
|
2326
|
+
const issues = required.filter((key) => !(key in value)).map((key) => `${prefix}.${key} is required`);
|
|
2327
|
+
const allowed = new Set([...required, ...optional]);
|
|
2328
|
+
for (const key of Object.keys(value))
|
|
2329
|
+
if (!allowed.has(key))
|
|
2330
|
+
issues.push(`${prefix}.${key} is not allowed`);
|
|
2331
|
+
return issues;
|
|
2332
|
+
}
|
|
2333
|
+
function isDigest(value) {
|
|
2334
|
+
return typeof value === "string" && SHA256.test(value);
|
|
2335
|
+
}
|
|
2336
|
+
function isNonEmptyString(value) {
|
|
2337
|
+
return typeof value === "string" && value.trim() !== "";
|
|
2338
|
+
}
|
|
2339
|
+
function isSortedUniqueStrings(values) {
|
|
2340
|
+
return values.every((value, index) => typeof value === "string" && (index === 0 || values[index - 1] < value));
|
|
2341
|
+
}
|
|
2342
|
+
function compareStrings(left, right) {
|
|
2343
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
2344
|
+
}
|
|
2345
|
+
function versionNumber(value, family) {
|
|
2346
|
+
if (typeof value !== "string" || !value.startsWith(family))
|
|
2347
|
+
return;
|
|
2348
|
+
const suffix = value.slice(family.length);
|
|
2349
|
+
if (!/^[1-9][0-9]*$/.test(suffix))
|
|
2350
|
+
return;
|
|
2351
|
+
const version = Number(suffix);
|
|
2352
|
+
return Number.isSafeInteger(version) ? version : undefined;
|
|
2353
|
+
}
|
|
2354
|
+
function architectureDocsProjectionCompatibilityIssues(value, engine = {
|
|
2355
|
+
rendererVersion: ARCHITECTURE_DOCS_RENDERER_VERSION,
|
|
2356
|
+
protocolVersion: PROJECTION_REQUEST_SCHEMA_VERSION
|
|
2357
|
+
}) {
|
|
2358
|
+
const manifest = record2(value);
|
|
2359
|
+
if (!manifest)
|
|
2360
|
+
return [];
|
|
2361
|
+
const issues = [];
|
|
2362
|
+
if (["minimumRendererVersion", "minimumProtocolVersion"].some((key) => Object.hasOwn(manifest, key)) && manifest.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION)
|
|
2363
|
+
issues.push("projection manifest floors require the current schema");
|
|
2364
|
+
if (typeof manifest.schemaVersion === "string" && manifest.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION && !isSupersededArchitectureDocsProjectionManifestSchemaVersion(manifest.schemaVersion))
|
|
2365
|
+
issues.push("projection manifest schema is unsupported by this engine");
|
|
2366
|
+
for (const [key, family, current] of [
|
|
2367
|
+
["minimumRendererVersion", "archcontext.docs-renderer/v", engine.rendererVersion],
|
|
2368
|
+
["minimumProtocolVersion", "archcontext.projection-request/v", engine.protocolVersion]
|
|
2369
|
+
]) {
|
|
2370
|
+
if (!Object.hasOwn(manifest, key))
|
|
2371
|
+
continue;
|
|
2372
|
+
const required = versionNumber(manifest[key], family);
|
|
2373
|
+
const supported = versionNumber(current, family);
|
|
2374
|
+
if (required === undefined || supported === undefined || supported < required)
|
|
2375
|
+
issues.push(`projection manifest ${key} is unsupported by this engine`);
|
|
2376
|
+
}
|
|
2377
|
+
return issues;
|
|
2378
|
+
}
|
|
2379
|
+
var ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION = "archcontext.architecture-docs-projection-manifest/v3", SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS, ARCHITECTURE_DOCS_PROJECTION_MANIFEST_UPGRADE_COMMAND = "archctx docs apply --approved --profile <default|repo-harness/v1>", ARCHITECTURE_DOCS_PROJECTION_MANIFEST_MIGRATION_GUIDE = "docs/runbooks/architecture-documentation-projections.md", PROJECTION_MANIFEST_UPGRADE_REQUIRED = "projection-manifest-upgrade-required", PROJECTION_MANIFEST_INVALID = "projection-manifest-invalid", PROJECTION_FOOTPRINT_UNCOVERED = "projection-footprint-uncovered", ARCHITECTURE_DOCS_PROJECTION_PROVENANCE_SCHEMA_VERSION = "archcontext.architecture-docs-projection-provenance/v3", ARCHITECTURE_DOCS_LAYOUT_VERSION_V1 = "archcontext.docs-layout/v1", ARCHITECTURE_SEMANTIC_STATE_SCHEMA_VERSION = "archcontext.architecture-semantic-state/v1", ARCHITECTURE_DOCS_PROJECTION_PROFILES, SHA256, TARGET_TYPES, RUNTIME_TOPOLOGY_TARGET_TYPES, SCOPE_KINDS, OWNERSHIPS, FORMATS;
|
|
2380
|
+
var init_projection_manifest = __esm(() => {
|
|
2381
|
+
init_projection();
|
|
2382
|
+
init_schema();
|
|
2383
|
+
SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS = [
|
|
2384
|
+
"archcontext.architecture-docs-projection-manifest/v1",
|
|
2385
|
+
"archcontext.architecture-docs-projection-manifest/v2"
|
|
2386
|
+
];
|
|
2387
|
+
ARCHITECTURE_DOCS_PROJECTION_PROFILES = ["default", "repo-harness/v1"];
|
|
2388
|
+
SHA256 = /^sha256:[a-f0-9]{64}$/;
|
|
2389
|
+
TARGET_TYPES = {
|
|
2390
|
+
"architecture-index": true,
|
|
2391
|
+
"entity-summary": true,
|
|
2392
|
+
"relation-summary": true,
|
|
2393
|
+
"decision-index": true,
|
|
2394
|
+
"architecture-changelog": true,
|
|
2395
|
+
"diagram-mermaid": true,
|
|
2396
|
+
"diagram-structurizr": true,
|
|
2397
|
+
"diagram-likec4": true,
|
|
2398
|
+
"diagram-runtime-topology": true,
|
|
2399
|
+
"runtime-topology-summary": true,
|
|
2400
|
+
"agent-context": true
|
|
2401
|
+
};
|
|
2402
|
+
RUNTIME_TOPOLOGY_TARGET_TYPES = ["diagram-runtime-topology", "runtime-topology-summary"];
|
|
2403
|
+
SCOPE_KINDS = { repository: true, entity: true, relation: true, decision: true, diagram: true, changelog: true };
|
|
2404
|
+
OWNERSHIPS = { generated: true, mixed: true };
|
|
2405
|
+
FORMATS = { markdown: true, mermaid: true, "structurizr-json": true, likec4: true };
|
|
2406
|
+
});
|
|
2407
|
+
|
|
1964
2408
|
// packages/contracts/src/projection-recovery.ts
|
|
1965
2409
|
function jsonDataCopy(input, maximum) {
|
|
1966
2410
|
let measured = 0;
|
|
@@ -2085,7 +2529,7 @@ var init_projection_recovery = __esm(() => {
|
|
|
2085
2529
|
// packages/contracts/src/failure.ts
|
|
2086
2530
|
import { isNativeError } from "node:util/types";
|
|
2087
2531
|
function ownData(input, key) {
|
|
2088
|
-
if (!
|
|
2532
|
+
if (!record3(input))
|
|
2089
2533
|
throw new Error("Failure metadata must be an object");
|
|
2090
2534
|
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
2091
2535
|
if (!descriptor) {
|
|
@@ -2133,7 +2577,7 @@ function detailsFor(policy, input) {
|
|
|
2133
2577
|
if (policy.facts)
|
|
2134
2578
|
return localFactsFor(policy.facts, input);
|
|
2135
2579
|
const result = {};
|
|
2136
|
-
if (!
|
|
2580
|
+
if (!record3(input))
|
|
2137
2581
|
throw new Error("Failure details must be an object");
|
|
2138
2582
|
let omitted = 0;
|
|
2139
2583
|
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(input))) {
|
|
@@ -2159,7 +2603,7 @@ function detailsFor(policy, input) {
|
|
|
2159
2603
|
return result;
|
|
2160
2604
|
}
|
|
2161
2605
|
function dataRecord(value) {
|
|
2162
|
-
if (!
|
|
2606
|
+
if (!record3(value))
|
|
2163
2607
|
throw new Error("Facts must be a plain record");
|
|
2164
2608
|
const proto = Object.getPrototypeOf(value), keys = Reflect.ownKeys(value);
|
|
2165
2609
|
if (proto !== Object.prototype && proto !== null || keys.length > 16 || Object.keys(value).length !== keys.length || keys.some((key) => typeof key !== "string"))
|
|
@@ -2284,7 +2728,7 @@ function nativeFactsFor(policy, input) {
|
|
|
2284
2728
|
let omitted = 0, characters = 0;
|
|
2285
2729
|
const omit = (value) => {
|
|
2286
2730
|
omitted++;
|
|
2287
|
-
const strings = (v) => typeof v === "string" ? v.length : Array.isArray(v) ? v.reduce((n, item) => n + strings(item), 0) :
|
|
2731
|
+
const strings = (v) => typeof v === "string" ? v.length : Array.isArray(v) ? v.reduce((n, item) => n + strings(item), 0) : record3(v) ? Object.values(v).reduce((n, item) => n + strings(item), 0) : 0;
|
|
2288
2732
|
characters += strings(value);
|
|
2289
2733
|
};
|
|
2290
2734
|
const text = (value) => {
|
|
@@ -2423,7 +2867,7 @@ function budgetIssues(value, depth = 0, maxStringBytes = 256) {
|
|
|
2423
2867
|
return Number.isFinite(value) ? [] : ["detail number is not finite"];
|
|
2424
2868
|
if (Array.isArray(value))
|
|
2425
2869
|
return value.length <= 32 ? value.flatMap((item) => budgetIssues(item, depth + 1, maxStringBytes)) : ["detail list exceeds 32"];
|
|
2426
|
-
if (
|
|
2870
|
+
if (record3(value))
|
|
2427
2871
|
return Object.keys(value).length <= 16 ? Object.values(value).flatMap((item) => budgetIssues(item, depth + 1, maxStringBytes)) : ["detail object exceeds 16 fields"];
|
|
2428
2872
|
return value === null || typeof value === "boolean" ? [] : ["detail value is not JSON"];
|
|
2429
2873
|
}
|
|
@@ -2498,7 +2942,7 @@ function toEnvelope(requestId, error, stage) {
|
|
|
2498
2942
|
}
|
|
2499
2943
|
}
|
|
2500
2944
|
function normalizeFailureResult(result, stage, requestId = "failure") {
|
|
2501
|
-
if (!
|
|
2945
|
+
if (!record3(result))
|
|
2502
2946
|
return result;
|
|
2503
2947
|
try {
|
|
2504
2948
|
if (ownData(result, "ok") !== false)
|
|
@@ -2528,7 +2972,7 @@ function jsonDataSnapshot(value, depth = 0, maxStringBytes = 256) {
|
|
|
2528
2972
|
return jsonDataSnapshot(descriptor.value, depth + 1, maxStringBytes);
|
|
2529
2973
|
});
|
|
2530
2974
|
}
|
|
2531
|
-
if (
|
|
2975
|
+
if (record3(value)) {
|
|
2532
2976
|
const prototype = Object.getPrototypeOf(value);
|
|
2533
2977
|
const keys = Reflect.ownKeys(value);
|
|
2534
2978
|
if (prototype !== Object.prototype && prototype !== null || keys.length > 16 || Object.keys(value).length !== keys.length || keys.some((key) => typeof key !== "string"))
|
|
@@ -2539,7 +2983,7 @@ function jsonDataSnapshot(value, depth = 0, maxStringBytes = 256) {
|
|
|
2539
2983
|
}
|
|
2540
2984
|
function failureIssues(value) {
|
|
2541
2985
|
try {
|
|
2542
|
-
if (!
|
|
2986
|
+
if (!record3(value))
|
|
2543
2987
|
return ["failure must be an object"];
|
|
2544
2988
|
const fields = ["code", "message", "severity", "retryable", "action", "reasonCode", "details"];
|
|
2545
2989
|
const keys = Reflect.ownKeys(value);
|
|
@@ -2558,10 +3002,12 @@ function failureIssues(value) {
|
|
|
2558
3002
|
return ["failure violates its registered contract or budget"];
|
|
2559
3003
|
}
|
|
2560
3004
|
}
|
|
2561
|
-
var FAILURE_STAGES, ERROR_CLASSES, CODE_FACTS_ISSUE_MESSAGES, policies, FAILURE_POLICIES, catalogDefaults, actions, bytes = (value) => new TextEncoder().encode(value).byteLength,
|
|
3005
|
+
var FAILURE_STAGES, ERROR_CLASSES, CODE_FACTS_ISSUE_MESSAGES, policies, FAILURE_POLICIES, catalogDefaults, actions, bytes = (value) => new TextEncoder().encode(value).byteLength, record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), finiteNonnegative = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0, criticalKey = (key) => /id$|digest|receipt|binding|readback|lookupkey/i.test(key), localEnums, nativeEnums, nativeDiagnosticKinds, FailureError;
|
|
2562
3006
|
var init_failure = __esm(() => {
|
|
2563
3007
|
init_changeset_recovery();
|
|
2564
3008
|
init_ports();
|
|
3009
|
+
init_projection();
|
|
3010
|
+
init_projection_manifest();
|
|
2565
3011
|
init_projection_recovery();
|
|
2566
3012
|
init_schema();
|
|
2567
3013
|
FAILURE_STAGES = Object.freeze(["debt", "complete", "practice-checkpoint", "cli", "mcp", "hook-checkpoint", "failure-metadata", "public-operation", "native-export", "docs-anchors", "flows-check"]);
|
|
@@ -2589,7 +3035,7 @@ var init_failure = __esm(() => {
|
|
|
2589
3035
|
"model-missing": { code: "AC_MODEL_ADOPTION_REQUIRED", message: "Architecture debt requires an adopted model.", fields: ["checks"] },
|
|
2590
3036
|
"changed-path-invalid": { code: "AC_SCHEMA_INVALID", message: "Changed paths must be an array of repository-relative POSIX paths.", fields: ["checks"] },
|
|
2591
3037
|
"debt-report-invalid": { code: "AC_SCHEMA_INVALID", message: "Architecture debt evidence violates its contract.", fields: ["checks"] },
|
|
2592
|
-
"projection-manifest-upgrade-required": { code: "AC_PRECONDITION_FAILED", message:
|
|
3038
|
+
"projection-manifest-upgrade-required": { code: "AC_PRECONDITION_FAILED", message: `The committed projection manifest requires supported re-baselining. First restore the original model if missing; the manifest digest cannot recover it. With the reviewed model available, follow ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_MIGRATION_GUIDE} before running ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_UPGRADE_COMMAND}.`, facts: { manifestPath: "optional:projectionManifestPath", foundSchemaVersion: "optional:projectionManifestSchemaVersion", supportedSchemaVersion: "optional:projectionManifestSchemaVersion", upgradeCommand: "optional:projectionManifestUpgradeCommand", migrationGuide: "optional:projectionManifestMigrationGuide" } },
|
|
2593
3039
|
"projection-manifest-invalid": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest violates its contract." },
|
|
2594
3040
|
"architecture-model-ignored": { code: "AC_PRECONDITION_FAILED", action: "commit-architecture-model", message: "Git ignore rules exclude architecture model paths, so the model cannot be committed and a manifest digest cannot rebuild it. Remove or narrow the rule, commit the model, then retry adoption. The diagnostic command prints each deciding rule.", facts: { pathCount: "uint", paths: "optional:diagnosticPaths", commands: "optional:diagnosticCommands", omittedPathCount: "uint" } },
|
|
2595
3041
|
"projection-footprint-uncovered": { code: "AC_PRECONDITION_FAILED", message: "Declared source footprints lack a projection target." },
|
|
@@ -2850,6 +3296,10 @@ var init_failure = __esm(() => {
|
|
|
2850
3296
|
upgradeAction: ["upgrade-archctx-runtime"],
|
|
2851
3297
|
upgradeCommand: ["archctx daemon upgrade"],
|
|
2852
3298
|
ownershipAction: ["remove-whole-file-outputs-and-reapply"],
|
|
3299
|
+
projectionManifestPath: [ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH],
|
|
3300
|
+
projectionManifestSchemaVersion: [...SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS, ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION],
|
|
3301
|
+
projectionManifestUpgradeCommand: [ARCHITECTURE_DOCS_PROJECTION_MANIFEST_UPGRADE_COMMAND],
|
|
3302
|
+
projectionManifestMigrationGuide: [ARCHITECTURE_DOCS_PROJECTION_MANIFEST_MIGRATION_GUIDE],
|
|
2853
3303
|
ledgerModeField: ["rolloutMode", "readMode", "writeMode"],
|
|
2854
3304
|
depsField: ["from", "into", "importNodeLimit", "edgeLimit"],
|
|
2855
3305
|
depsIssue: ["unknown-node", "invalid-limit", "missing-value"],
|
|
@@ -3566,450 +4016,6 @@ var PRACTICE_SCHEMA_VERSION = "archcontext.practice/v1", PRACTICE_PROFILE_SCHEMA
|
|
|
3566
4016
|
var PROJECTION_PUBLISH_RUN_SCHEMA_VERSION = "archcontext.projection-publish-run/v1", PROJECTION_PUBLISH_STATUS_SCHEMA_VERSION = "archcontext.projection-publish-status/v1", PROJECTION_PR_VERIFICATION_SCHEMA_VERSION = "archcontext.projection-pr-verification/v1", PROJECTION_WRITER_BRANCH_PREFIX = "archctx/", PROJECTION_WRITER_DEFAULT_BRANCH = "archctx/projection";
|
|
3567
4017
|
var init_projection_writer = () => {};
|
|
3568
4018
|
|
|
3569
|
-
// packages/contracts/src/projection-manifest.ts
|
|
3570
|
-
function isRuntimeTopologyTargetType(type) {
|
|
3571
|
-
return typeof type === "string" && RUNTIME_TOPOLOGY_TARGET_TYPES.includes(type);
|
|
3572
|
-
}
|
|
3573
|
-
function architectureDocsProjectionManifestIssues(value) {
|
|
3574
|
-
const issues = [];
|
|
3575
|
-
const manifest = record3(value);
|
|
3576
|
-
if (!manifest)
|
|
3577
|
-
return ["manifest must be an object"];
|
|
3578
|
-
issues.push(...exactKeys(manifest, ["schemaVersion", "rendererVersion", "profile", "layoutVersion", "generatedFrom", "semanticBaseline", "targets"], ["minimumRendererVersion", "minimumProtocolVersion"], "manifest"));
|
|
3579
|
-
if (manifest.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION)
|
|
3580
|
-
issues.push("manifest.schemaVersion is unsupported");
|
|
3581
|
-
if (!isNonEmptyString(manifest.rendererVersion))
|
|
3582
|
-
issues.push("manifest.rendererVersion must be a non-empty string");
|
|
3583
|
-
for (const [key, family] of [["minimumRendererVersion", "archcontext.docs-renderer/v"], ["minimumProtocolVersion", "archcontext.projection-request/v"]]) {
|
|
3584
|
-
if (Object.hasOwn(manifest, key) && versionNumber(manifest[key], family) === undefined)
|
|
3585
|
-
issues.push(`manifest.${key} must name a supported version family`);
|
|
3586
|
-
}
|
|
3587
|
-
if (!ARCHITECTURE_DOCS_PROJECTION_PROFILES.includes(manifest.profile))
|
|
3588
|
-
issues.push("manifest.profile is unsupported");
|
|
3589
|
-
if (manifest.layoutVersion !== ARCHITECTURE_DOCS_LAYOUT_VERSION_V1)
|
|
3590
|
-
issues.push("manifest.layoutVersion is unsupported");
|
|
3591
|
-
issues.push(...generatedFromIssues(manifest.generatedFrom, "manifest.generatedFrom"));
|
|
3592
|
-
const baseline = record3(manifest.semanticBaseline);
|
|
3593
|
-
if (!baseline)
|
|
3594
|
-
issues.push("manifest.semanticBaseline must be an object");
|
|
3595
|
-
else {
|
|
3596
|
-
issues.push(...exactKeys(baseline, ["modelDigest", "capabilities"], [], "manifest.semanticBaseline"));
|
|
3597
|
-
if (!isDigest(baseline.modelDigest))
|
|
3598
|
-
issues.push("manifest.semanticBaseline.modelDigest must be a SHA-256 digest");
|
|
3599
|
-
issues.push(...capabilityEntriesIssues(baseline.capabilities, "manifest.semanticBaseline.capabilities", true));
|
|
3600
|
-
}
|
|
3601
|
-
const targets = record3(manifest.targets);
|
|
3602
|
-
if (!targets) {
|
|
3603
|
-
issues.push("manifest.targets must be an object keyed by targetId");
|
|
3604
|
-
return issues;
|
|
3605
|
-
}
|
|
3606
|
-
const paths = new Set;
|
|
3607
|
-
const stampOwners = new Map;
|
|
3608
|
-
let topologyStamps;
|
|
3609
|
-
for (const [targetId, row] of Object.entries(targets)) {
|
|
3610
|
-
const prefix = `manifest.targets[${JSON.stringify(targetId)}]`;
|
|
3611
|
-
if (!isNonEmptyString(targetId))
|
|
3612
|
-
issues.push("manifest.targets keys must be non-empty targetIds");
|
|
3613
|
-
issues.push(...manifestTargetIssues(row, prefix));
|
|
3614
|
-
const path = record3(row)?.path;
|
|
3615
|
-
if (typeof path === "string") {
|
|
3616
|
-
if (paths.has(path))
|
|
3617
|
-
issues.push(`${prefix}.path is recorded by another target`);
|
|
3618
|
-
paths.add(path);
|
|
3619
|
-
}
|
|
3620
|
-
const stamps = record3(record3(row)?.sourceStamps) ?? {};
|
|
3621
|
-
if (isRuntimeTopologyTargetType(record3(row)?.type)) {
|
|
3622
|
-
const serialized = JSON.stringify(Object.keys(stamps).sort(compareStrings).map((nodeId) => [nodeId, stamps[nodeId]]));
|
|
3623
|
-
if (topologyStamps === undefined)
|
|
3624
|
-
topologyStamps = { targetId, stamps: serialized };
|
|
3625
|
-
else if (topologyStamps.stamps !== serialized)
|
|
3626
|
-
issues.push(`${prefix}.sourceStamps must equal the sourceStamps of ${topologyStamps.targetId}`);
|
|
3627
|
-
for (const nodeId of Object.keys(stamps)) {
|
|
3628
|
-
const owner = stampOwners.get(nodeId);
|
|
3629
|
-
if (owner !== undefined && !isRuntimeTopologyTargetType(record3(targets[owner])?.type))
|
|
3630
|
-
issues.push(`${prefix}.sourceStamps.${nodeId} is already stamped on ${owner}`);
|
|
3631
|
-
else if (owner === undefined)
|
|
3632
|
-
stampOwners.set(nodeId, targetId);
|
|
3633
|
-
}
|
|
3634
|
-
continue;
|
|
3635
|
-
}
|
|
3636
|
-
for (const nodeId of Object.keys(stamps)) {
|
|
3637
|
-
const owner = stampOwners.get(nodeId);
|
|
3638
|
-
if (owner !== undefined)
|
|
3639
|
-
issues.push(`${prefix}.sourceStamps.${nodeId} is already stamped on ${owner}`);
|
|
3640
|
-
else
|
|
3641
|
-
stampOwners.set(nodeId, targetId);
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
return issues;
|
|
3645
|
-
}
|
|
3646
|
-
function architectureDocsProjectionManifestSchemaVersion(body) {
|
|
3647
|
-
let parsed;
|
|
3648
|
-
try {
|
|
3649
|
-
parsed = JSON.parse(body);
|
|
3650
|
-
} catch {
|
|
3651
|
-
return;
|
|
3652
|
-
}
|
|
3653
|
-
const schemaVersion = record3(parsed)?.schemaVersion;
|
|
3654
|
-
return typeof schemaVersion === "string" ? schemaVersion : undefined;
|
|
3655
|
-
}
|
|
3656
|
-
function isSupersededArchitectureDocsProjectionManifestSchemaVersion(schemaVersion) {
|
|
3657
|
-
return SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS.includes(schemaVersion);
|
|
3658
|
-
}
|
|
3659
|
-
function serializeArchitectureDocsProjectionManifest(manifest) {
|
|
3660
|
-
const capabilities = [...manifest.semanticBaseline.capabilities].sort((left, right) => compareStrings(left.capabilityId, right.capabilityId)).map(canonicalCapability);
|
|
3661
|
-
const capabilityBlock = JSON.stringify(capabilities, null, 2).split(`
|
|
3662
|
-
`).map((line, index) => index === 0 ? line : ` ${line}`).join(`
|
|
3663
|
-
`);
|
|
3664
|
-
const targetIds = Object.keys(manifest.targets).sort(compareStrings);
|
|
3665
|
-
const targetLines = targetIds.length === 0 ? [' "targets": {}'] : [
|
|
3666
|
-
' "targets": {',
|
|
3667
|
-
...targetIds.flatMap((targetId, index) => [
|
|
3668
|
-
` ${JSON.stringify(targetId)}:`,
|
|
3669
|
-
` ${JSON.stringify(canonicalTargetRow(manifest.targets[targetId]))}${index === targetIds.length - 1 ? "" : ","}`
|
|
3670
|
-
]),
|
|
3671
|
-
" }"
|
|
3672
|
-
];
|
|
3673
|
-
return [
|
|
3674
|
-
"{",
|
|
3675
|
-
` "schemaVersion": ${JSON.stringify(manifest.schemaVersion)},`,
|
|
3676
|
-
` "rendererVersion": ${JSON.stringify(manifest.rendererVersion)},`,
|
|
3677
|
-
...manifest.minimumRendererVersion === undefined ? [] : [` "minimumRendererVersion": ${JSON.stringify(manifest.minimumRendererVersion)},`],
|
|
3678
|
-
...manifest.minimumProtocolVersion === undefined ? [] : [` "minimumProtocolVersion": ${JSON.stringify(manifest.minimumProtocolVersion)},`],
|
|
3679
|
-
` "profile": ${JSON.stringify(manifest.profile)},`,
|
|
3680
|
-
` "layoutVersion": ${JSON.stringify(manifest.layoutVersion)},`,
|
|
3681
|
-
` "generatedFrom": ${JSON.stringify({ codeGraphPackage: manifest.generatedFrom.codeGraphPackage, codeGraphVersion: manifest.generatedFrom.codeGraphVersion })},`,
|
|
3682
|
-
' "semanticBaseline": {',
|
|
3683
|
-
` "modelDigest": ${JSON.stringify(manifest.semanticBaseline.modelDigest)},`,
|
|
3684
|
-
` "capabilities": ${capabilityBlock}`,
|
|
3685
|
-
" },",
|
|
3686
|
-
...targetLines,
|
|
3687
|
-
"}",
|
|
3688
|
-
""
|
|
3689
|
-
].join(`
|
|
3690
|
-
`);
|
|
3691
|
-
}
|
|
3692
|
-
function architectureDocsProjectionManifestSemanticState(manifest) {
|
|
3693
|
-
return architectureSemanticStateFromCapabilities(manifest.semanticBaseline.capabilities.map(({ selectorEvidenceDigest: _selectorEvidenceDigest, ...capability }) => capability));
|
|
3694
|
-
}
|
|
3695
|
-
function architectureSemanticStateFromCapabilities(capabilities) {
|
|
3696
|
-
const sorted = [...capabilities].sort((left, right) => compareStrings(left.capabilityId, right.capabilityId));
|
|
3697
|
-
return {
|
|
3698
|
-
schemaVersion: ARCHITECTURE_SEMANTIC_STATE_SCHEMA_VERSION,
|
|
3699
|
-
capabilities: sorted,
|
|
3700
|
-
semanticFingerprint: digestJson(sorted.map(({ capabilityId, semanticFingerprint }) => ({ capabilityId, semanticFingerprint }))),
|
|
3701
|
-
flowProofFingerprint: digestJson(sorted.map(({ capabilityId, flowProofFingerprint }) => ({ capabilityId, flowProofFingerprint })))
|
|
3702
|
-
};
|
|
3703
|
-
}
|
|
3704
|
-
function architectureDocsProjectionManifestSourceStamps(manifest) {
|
|
3705
|
-
const stamps = {};
|
|
3706
|
-
for (const row of Object.values(manifest.targets))
|
|
3707
|
-
Object.assign(stamps, row.sourceStamps ?? {});
|
|
3708
|
-
return stamps;
|
|
3709
|
-
}
|
|
3710
|
-
function architectureDocsProjectionManifestProofEvidence(manifest) {
|
|
3711
|
-
return {
|
|
3712
|
-
rendererVersion: manifest.rendererVersion,
|
|
3713
|
-
selectorEvidenceDigests: Object.fromEntries(manifest.semanticBaseline.capabilities.map((capability) => [capability.capabilityId, capability.selectorEvidenceDigest])),
|
|
3714
|
-
sourceStamps: architectureDocsProjectionManifestSourceStamps(manifest)
|
|
3715
|
-
};
|
|
3716
|
-
}
|
|
3717
|
-
function architectureSourceStampsDigest(stamps) {
|
|
3718
|
-
return digestJson({
|
|
3719
|
-
schemaVersion: "archcontext.architecture-source-stamps/v1",
|
|
3720
|
-
stamps: Object.keys(stamps).sort(compareStrings).map((nodeId) => ({ nodeId, digest: stamps[nodeId] }))
|
|
3721
|
-
});
|
|
3722
|
-
}
|
|
3723
|
-
function architectureProjectionOutputsDigest(input) {
|
|
3724
|
-
return digestJson({
|
|
3725
|
-
schemaVersion: "archcontext.architecture-projection-outputs/v1",
|
|
3726
|
-
rendererVersion: input.rendererVersion,
|
|
3727
|
-
profile: input.profile,
|
|
3728
|
-
outputs: Object.keys(input.outputDigests).sort(compareStrings).map((targetId) => ({ targetId, outputDigest: input.outputDigests[targetId] }))
|
|
3729
|
-
});
|
|
3730
|
-
}
|
|
3731
|
-
function architectureDocsProjectionManifestDigests(manifest) {
|
|
3732
|
-
return {
|
|
3733
|
-
modelDigest: manifest.semanticBaseline.modelDigest,
|
|
3734
|
-
sourceTreeDigest: architectureSourceStampsDigest(architectureDocsProjectionManifestSourceStamps(manifest)),
|
|
3735
|
-
flowProofDigest: architectureDocsProjectionManifestSemanticState(manifest).flowProofFingerprint,
|
|
3736
|
-
projectionDigest: architectureProjectionOutputsDigest({
|
|
3737
|
-
rendererVersion: manifest.rendererVersion,
|
|
3738
|
-
profile: manifest.profile,
|
|
3739
|
-
outputDigests: Object.fromEntries(Object.entries(manifest.targets).map(([targetId, row]) => [targetId, row.outputDigest]))
|
|
3740
|
-
})
|
|
3741
|
-
};
|
|
3742
|
-
}
|
|
3743
|
-
function architectureDocsProjectionProvenanceIssues(value, prefix = "provenance") {
|
|
3744
|
-
const provenance = record3(value);
|
|
3745
|
-
if (!provenance)
|
|
3746
|
-
return [`${prefix} must be an object`];
|
|
3747
|
-
const issues = exactKeys(provenance, ["schemaVersion", "sourceTreeDigest", "modelDigest", "projectionInputDigest", "rendererVersion", "layoutVersion", "generatedFrom"], [], prefix);
|
|
3748
|
-
if (provenance.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_PROVENANCE_SCHEMA_VERSION)
|
|
3749
|
-
issues.push(`${prefix}.schemaVersion is unsupported`);
|
|
3750
|
-
for (const field of ["sourceTreeDigest", "modelDigest", "projectionInputDigest"]) {
|
|
3751
|
-
if (!isDigest(provenance[field]))
|
|
3752
|
-
issues.push(`${prefix}.${field} must be a SHA-256 digest`);
|
|
3753
|
-
}
|
|
3754
|
-
if (provenance.rendererVersion !== ARCHITECTURE_DOCS_RENDERER_VERSION)
|
|
3755
|
-
issues.push(`${prefix}.rendererVersion must be ${ARCHITECTURE_DOCS_RENDERER_VERSION}`);
|
|
3756
|
-
if (provenance.layoutVersion !== ARCHITECTURE_DOCS_LAYOUT_VERSION_V1)
|
|
3757
|
-
issues.push(`${prefix}.layoutVersion is unsupported`);
|
|
3758
|
-
issues.push(...generatedFromIssues(provenance.generatedFrom, `${prefix}.generatedFrom`));
|
|
3759
|
-
return issues;
|
|
3760
|
-
}
|
|
3761
|
-
function capabilityEntriesIssues(value, prefix, withSelectorEvidence) {
|
|
3762
|
-
if (!Array.isArray(value))
|
|
3763
|
-
return [`${prefix} must be an array`];
|
|
3764
|
-
const issues = [];
|
|
3765
|
-
const ids = value.map((entry) => record3(entry)?.capabilityId);
|
|
3766
|
-
if (!isSortedUniqueStrings(ids))
|
|
3767
|
-
issues.push(`${prefix} must be sorted and unique by capabilityId`);
|
|
3768
|
-
value.forEach((entry, index) => {
|
|
3769
|
-
const entryPrefix = `${prefix}[${index}]`;
|
|
3770
|
-
const capability = record3(entry);
|
|
3771
|
-
if (!capability) {
|
|
3772
|
-
issues.push(`${entryPrefix} must be an object`);
|
|
3773
|
-
return;
|
|
3774
|
-
}
|
|
3775
|
-
const required = ["capabilityId", "memberNodeIds", "semanticFingerprint", "flowProofFingerprint", "proofStatus", "facets", ...withSelectorEvidence ? ["selectorEvidenceDigest"] : []];
|
|
3776
|
-
issues.push(...exactKeys(capability, required, [], entryPrefix));
|
|
3777
|
-
if (!isNonEmptyString(capability.capabilityId))
|
|
3778
|
-
issues.push(`${entryPrefix}.capabilityId must be a non-empty string`);
|
|
3779
|
-
if (!Array.isArray(capability.memberNodeIds) || !isSortedUniqueStrings(capability.memberNodeIds))
|
|
3780
|
-
issues.push(`${entryPrefix}.memberNodeIds must be sorted and unique`);
|
|
3781
|
-
if (!isDigest(capability.semanticFingerprint))
|
|
3782
|
-
issues.push(`${entryPrefix}.semanticFingerprint must be a SHA-256 digest`);
|
|
3783
|
-
if (!isDigest(capability.flowProofFingerprint))
|
|
3784
|
-
issues.push(`${entryPrefix}.flowProofFingerprint must be a SHA-256 digest`);
|
|
3785
|
-
if (withSelectorEvidence && !isDigest(capability.selectorEvidenceDigest))
|
|
3786
|
-
issues.push(`${entryPrefix}.selectorEvidenceDigest must be a SHA-256 digest`);
|
|
3787
|
-
if (!isArchitectureCapabilityProofStatus(capability.proofStatus))
|
|
3788
|
-
issues.push(`${entryPrefix}.proofStatus must be a P1/P2 proof status`);
|
|
3789
|
-
const facets = record3(capability.facets);
|
|
3790
|
-
if (!facets)
|
|
3791
|
-
issues.push(`${entryPrefix}.facets must be an object`);
|
|
3792
|
-
else {
|
|
3793
|
-
issues.push(...exactKeys(facets, [...ARCHITECTURE_SEMANTIC_FACETS], [], `${entryPrefix}.facets`));
|
|
3794
|
-
for (const facet of ARCHITECTURE_SEMANTIC_FACETS) {
|
|
3795
|
-
if (!isDigest(facets[facet]))
|
|
3796
|
-
issues.push(`${entryPrefix}.facets.${facet} must be a SHA-256 digest`);
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
});
|
|
3800
|
-
return issues;
|
|
3801
|
-
}
|
|
3802
|
-
function generatedFromIssues(value, prefix) {
|
|
3803
|
-
const generatedFrom = record3(value);
|
|
3804
|
-
if (!generatedFrom)
|
|
3805
|
-
return [`${prefix} must be an object`];
|
|
3806
|
-
const issues = exactKeys(generatedFrom, ["codeGraphPackage", "codeGraphVersion"], [], prefix);
|
|
3807
|
-
for (const field of ["codeGraphPackage", "codeGraphVersion"]) {
|
|
3808
|
-
if (!isNonEmptyString(generatedFrom[field]))
|
|
3809
|
-
issues.push(`${prefix}.${field} must be a non-empty string`);
|
|
3810
|
-
}
|
|
3811
|
-
return issues;
|
|
3812
|
-
}
|
|
3813
|
-
function canonicalCapability(capability) {
|
|
3814
|
-
return {
|
|
3815
|
-
capabilityId: capability.capabilityId,
|
|
3816
|
-
memberNodeIds: [...capability.memberNodeIds],
|
|
3817
|
-
semanticFingerprint: capability.semanticFingerprint,
|
|
3818
|
-
flowProofFingerprint: capability.flowProofFingerprint,
|
|
3819
|
-
proofStatus: { p1: capability.proofStatus.p1, p2: capability.proofStatus.p2 },
|
|
3820
|
-
facets: Object.fromEntries(Object.keys(capability.facets).sort(compareStrings).map((facet) => [facet, capability.facets[facet]])),
|
|
3821
|
-
selectorEvidenceDigest: capability.selectorEvidenceDigest
|
|
3822
|
-
};
|
|
3823
|
-
}
|
|
3824
|
-
function canonicalTargetRow(row) {
|
|
3825
|
-
return {
|
|
3826
|
-
type: row.type,
|
|
3827
|
-
scope: {
|
|
3828
|
-
kind: row.scope.kind,
|
|
3829
|
-
...row.scope.id !== undefined ? { id: row.scope.id } : {},
|
|
3830
|
-
...row.scope.entityKind !== undefined ? { entityKind: row.scope.entityKind } : {}
|
|
3831
|
-
},
|
|
3832
|
-
path: row.path,
|
|
3833
|
-
ownership: row.ownership,
|
|
3834
|
-
format: row.format,
|
|
3835
|
-
outputDigest: row.outputDigest,
|
|
3836
|
-
...row.sourceStamps !== undefined ? { sourceStamps: Object.fromEntries(Object.keys(row.sourceStamps).sort(compareStrings).map((nodeId) => [nodeId, row.sourceStamps[nodeId]])) } : {},
|
|
3837
|
-
...row.scale !== undefined ? {
|
|
3838
|
-
scale: {
|
|
3839
|
-
fileCountBucket: { lower: row.scale.fileCountBucket.lower, upper: row.scale.fileCountBucket.upper },
|
|
3840
|
-
lineCountBucket: { lower: row.scale.lineCountBucket.lower, upper: row.scale.lineCountBucket.upper }
|
|
3841
|
-
}
|
|
3842
|
-
} : {}
|
|
3843
|
-
};
|
|
3844
|
-
}
|
|
3845
|
-
function isArchitectureScaleBucket(value) {
|
|
3846
|
-
const bucket = record3(value);
|
|
3847
|
-
if (!bucket || Object.keys(bucket).length !== 2)
|
|
3848
|
-
return false;
|
|
3849
|
-
const { lower, upper } = bucket;
|
|
3850
|
-
if (typeof lower !== "number" || typeof upper !== "number" || !Number.isSafeInteger(lower) || !Number.isSafeInteger(upper))
|
|
3851
|
-
return false;
|
|
3852
|
-
if (lower === 0)
|
|
3853
|
-
return upper === 1;
|
|
3854
|
-
let magnitude = 1;
|
|
3855
|
-
while (magnitude * 10 <= lower)
|
|
3856
|
-
magnitude *= 10;
|
|
3857
|
-
const mantissa = lower / magnitude;
|
|
3858
|
-
if (mantissa === 1)
|
|
3859
|
-
return upper === 2 * magnitude;
|
|
3860
|
-
if (mantissa === 2)
|
|
3861
|
-
return upper === 5 * magnitude;
|
|
3862
|
-
if (mantissa === 5)
|
|
3863
|
-
return upper === 10 * magnitude;
|
|
3864
|
-
return false;
|
|
3865
|
-
}
|
|
3866
|
-
function manifestTargetIssues(value, prefix) {
|
|
3867
|
-
const target = record3(value);
|
|
3868
|
-
if (!target)
|
|
3869
|
-
return [`${prefix} must be an object`];
|
|
3870
|
-
const issues = exactKeys(target, ["type", "scope", "path", "ownership", "format", "outputDigest"], ["sourceStamps", "scale"], prefix);
|
|
3871
|
-
if (typeof target.type !== "string" || !Object.hasOwn(TARGET_TYPES, target.type))
|
|
3872
|
-
issues.push(`${prefix}.type is unsupported`);
|
|
3873
|
-
const scope = record3(target.scope);
|
|
3874
|
-
if (!scope || typeof scope.kind !== "string" || !Object.hasOwn(SCOPE_KINDS, scope.kind))
|
|
3875
|
-
issues.push(`${prefix}.scope.kind is unsupported`);
|
|
3876
|
-
else {
|
|
3877
|
-
issues.push(...exactKeys(scope, ["kind"], ["id", "entityKind"], `${prefix}.scope`));
|
|
3878
|
-
for (const field of ["id", "entityKind"]) {
|
|
3879
|
-
if (scope[field] !== undefined && typeof scope[field] !== "string")
|
|
3880
|
-
issues.push(`${prefix}.scope.${field} must be a string`);
|
|
3881
|
-
}
|
|
3882
|
-
}
|
|
3883
|
-
if (typeof target.path !== "string" || !isRepoRelativePosixPath(target.path))
|
|
3884
|
-
issues.push(`${prefix}.path must be a repository-relative POSIX path`);
|
|
3885
|
-
if (typeof target.ownership !== "string" || !Object.hasOwn(OWNERSHIPS, target.ownership))
|
|
3886
|
-
issues.push(`${prefix}.ownership is unsupported`);
|
|
3887
|
-
if (typeof target.format !== "string" || !Object.hasOwn(FORMATS, target.format))
|
|
3888
|
-
issues.push(`${prefix}.format is unsupported`);
|
|
3889
|
-
if (!isDigest(target.outputDigest))
|
|
3890
|
-
issues.push(`${prefix}.outputDigest must be a SHA-256 digest`);
|
|
3891
|
-
if (target.sourceStamps !== undefined && target.type !== "entity-summary" && !isRuntimeTopologyTargetType(target.type)) {
|
|
3892
|
-
issues.push(`${prefix}.sourceStamps are only allowed on entity-summary and runtime topology targets`);
|
|
3893
|
-
}
|
|
3894
|
-
if (target.scale !== undefined && target.type !== "entity-summary")
|
|
3895
|
-
issues.push(`${prefix}.scale is only allowed on entity-summary targets`);
|
|
3896
|
-
if (target.sourceStamps !== undefined) {
|
|
3897
|
-
const stamps = record3(target.sourceStamps);
|
|
3898
|
-
if (!stamps || Object.keys(stamps).length === 0)
|
|
3899
|
-
issues.push(`${prefix}.sourceStamps must be a non-empty object keyed by node id`);
|
|
3900
|
-
else {
|
|
3901
|
-
for (const [nodeId, digest] of Object.entries(stamps)) {
|
|
3902
|
-
if (!isNonEmptyString(nodeId))
|
|
3903
|
-
issues.push(`${prefix}.sourceStamps keys must be non-empty node ids`);
|
|
3904
|
-
if (!isDigest(digest))
|
|
3905
|
-
issues.push(`${prefix}.sourceStamps.${nodeId} must be a SHA-256 digest`);
|
|
3906
|
-
}
|
|
3907
|
-
}
|
|
3908
|
-
}
|
|
3909
|
-
const ownNodeId = scope?.id;
|
|
3910
|
-
const stampsOwnNode = typeof ownNodeId === "string" && record3(target.sourceStamps) !== undefined && Object.hasOwn(record3(target.sourceStamps), ownNodeId);
|
|
3911
|
-
if (stampsOwnNode !== (target.scale !== undefined))
|
|
3912
|
-
issues.push(`${prefix}.scale must be present exactly when sourceStamps stamps the target's own node`);
|
|
3913
|
-
if (target.scale !== undefined) {
|
|
3914
|
-
const scale = record3(target.scale);
|
|
3915
|
-
if (!scale)
|
|
3916
|
-
issues.push(`${prefix}.scale must be an object`);
|
|
3917
|
-
else {
|
|
3918
|
-
issues.push(...exactKeys(scale, ["fileCountBucket", "lineCountBucket"], [], `${prefix}.scale`));
|
|
3919
|
-
for (const field of ["fileCountBucket", "lineCountBucket"]) {
|
|
3920
|
-
if (!isArchitectureScaleBucket(scale[field]))
|
|
3921
|
-
issues.push(`${prefix}.scale.${field} must be a 1-2-5 magnitude bucket`);
|
|
3922
|
-
}
|
|
3923
|
-
}
|
|
3924
|
-
}
|
|
3925
|
-
return issues;
|
|
3926
|
-
}
|
|
3927
|
-
function record3(value) {
|
|
3928
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
3929
|
-
}
|
|
3930
|
-
function exactKeys(value, required, optional, prefix) {
|
|
3931
|
-
const issues = required.filter((key) => !(key in value)).map((key) => `${prefix}.${key} is required`);
|
|
3932
|
-
const allowed = new Set([...required, ...optional]);
|
|
3933
|
-
for (const key of Object.keys(value))
|
|
3934
|
-
if (!allowed.has(key))
|
|
3935
|
-
issues.push(`${prefix}.${key} is not allowed`);
|
|
3936
|
-
return issues;
|
|
3937
|
-
}
|
|
3938
|
-
function isDigest(value) {
|
|
3939
|
-
return typeof value === "string" && SHA256.test(value);
|
|
3940
|
-
}
|
|
3941
|
-
function isNonEmptyString(value) {
|
|
3942
|
-
return typeof value === "string" && value.trim() !== "";
|
|
3943
|
-
}
|
|
3944
|
-
function isSortedUniqueStrings(values) {
|
|
3945
|
-
return values.every((value, index) => typeof value === "string" && (index === 0 || values[index - 1] < value));
|
|
3946
|
-
}
|
|
3947
|
-
function compareStrings(left, right) {
|
|
3948
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
3949
|
-
}
|
|
3950
|
-
function versionNumber(value, family) {
|
|
3951
|
-
if (typeof value !== "string" || !value.startsWith(family))
|
|
3952
|
-
return;
|
|
3953
|
-
const suffix = value.slice(family.length);
|
|
3954
|
-
if (!/^[1-9][0-9]*$/.test(suffix))
|
|
3955
|
-
return;
|
|
3956
|
-
const version = Number(suffix);
|
|
3957
|
-
return Number.isSafeInteger(version) ? version : undefined;
|
|
3958
|
-
}
|
|
3959
|
-
function architectureDocsProjectionCompatibilityIssues(value, engine = {
|
|
3960
|
-
rendererVersion: ARCHITECTURE_DOCS_RENDERER_VERSION,
|
|
3961
|
-
protocolVersion: PROJECTION_REQUEST_SCHEMA_VERSION
|
|
3962
|
-
}) {
|
|
3963
|
-
const manifest = record3(value);
|
|
3964
|
-
if (!manifest)
|
|
3965
|
-
return [];
|
|
3966
|
-
const issues = [];
|
|
3967
|
-
if (["minimumRendererVersion", "minimumProtocolVersion"].some((key) => Object.hasOwn(manifest, key)) && manifest.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION)
|
|
3968
|
-
issues.push("projection manifest floors require the current schema");
|
|
3969
|
-
if (typeof manifest.schemaVersion === "string" && manifest.schemaVersion !== ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION && !isSupersededArchitectureDocsProjectionManifestSchemaVersion(manifest.schemaVersion))
|
|
3970
|
-
issues.push("projection manifest schema is unsupported by this engine");
|
|
3971
|
-
for (const [key, family, current] of [
|
|
3972
|
-
["minimumRendererVersion", "archcontext.docs-renderer/v", engine.rendererVersion],
|
|
3973
|
-
["minimumProtocolVersion", "archcontext.projection-request/v", engine.protocolVersion]
|
|
3974
|
-
]) {
|
|
3975
|
-
if (!Object.hasOwn(manifest, key))
|
|
3976
|
-
continue;
|
|
3977
|
-
const required = versionNumber(manifest[key], family);
|
|
3978
|
-
const supported = versionNumber(current, family);
|
|
3979
|
-
if (required === undefined || supported === undefined || supported < required)
|
|
3980
|
-
issues.push(`projection manifest ${key} is unsupported by this engine`);
|
|
3981
|
-
}
|
|
3982
|
-
return issues;
|
|
3983
|
-
}
|
|
3984
|
-
var ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION = "archcontext.architecture-docs-projection-manifest/v3", SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS, PROJECTION_MANIFEST_UPGRADE_REQUIRED = "projection-manifest-upgrade-required", PROJECTION_MANIFEST_INVALID = "projection-manifest-invalid", PROJECTION_FOOTPRINT_UNCOVERED = "projection-footprint-uncovered", ARCHITECTURE_DOCS_PROJECTION_PROVENANCE_SCHEMA_VERSION = "archcontext.architecture-docs-projection-provenance/v3", ARCHITECTURE_DOCS_LAYOUT_VERSION_V1 = "archcontext.docs-layout/v1", ARCHITECTURE_SEMANTIC_STATE_SCHEMA_VERSION = "archcontext.architecture-semantic-state/v1", ARCHITECTURE_DOCS_PROJECTION_PROFILES, SHA256, TARGET_TYPES, RUNTIME_TOPOLOGY_TARGET_TYPES, SCOPE_KINDS, OWNERSHIPS, FORMATS;
|
|
3985
|
-
var init_projection_manifest = __esm(() => {
|
|
3986
|
-
init_projection();
|
|
3987
|
-
init_schema();
|
|
3988
|
-
SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS = [
|
|
3989
|
-
"archcontext.architecture-docs-projection-manifest/v1",
|
|
3990
|
-
"archcontext.architecture-docs-projection-manifest/v2"
|
|
3991
|
-
];
|
|
3992
|
-
ARCHITECTURE_DOCS_PROJECTION_PROFILES = ["default", "repo-harness/v1"];
|
|
3993
|
-
SHA256 = /^sha256:[a-f0-9]{64}$/;
|
|
3994
|
-
TARGET_TYPES = {
|
|
3995
|
-
"architecture-index": true,
|
|
3996
|
-
"entity-summary": true,
|
|
3997
|
-
"relation-summary": true,
|
|
3998
|
-
"decision-index": true,
|
|
3999
|
-
"architecture-changelog": true,
|
|
4000
|
-
"diagram-mermaid": true,
|
|
4001
|
-
"diagram-structurizr": true,
|
|
4002
|
-
"diagram-likec4": true,
|
|
4003
|
-
"diagram-runtime-topology": true,
|
|
4004
|
-
"runtime-topology-summary": true,
|
|
4005
|
-
"agent-context": true
|
|
4006
|
-
};
|
|
4007
|
-
RUNTIME_TOPOLOGY_TARGET_TYPES = ["diagram-runtime-topology", "runtime-topology-summary"];
|
|
4008
|
-
SCOPE_KINDS = { repository: true, entity: true, relation: true, decision: true, diagram: true, changelog: true };
|
|
4009
|
-
OWNERSHIPS = { generated: true, mixed: true };
|
|
4010
|
-
FORMATS = { markdown: true, mermaid: true, "structurizr-json": true, likec4: true };
|
|
4011
|
-
});
|
|
4012
|
-
|
|
4013
4019
|
// packages/contracts/src/product-version.ts
|
|
4014
4020
|
function productVersionManifest() {
|
|
4015
4021
|
return {
|
|
@@ -4095,7 +4101,7 @@ function compareVersionTriples(left, right) {
|
|
|
4095
4101
|
}
|
|
4096
4102
|
return 0;
|
|
4097
4103
|
}
|
|
4098
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.
|
|
4104
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.10.0", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
|
|
4099
4105
|
|
|
4100
4106
|
// packages/contracts/src/refactor.ts
|
|
4101
4107
|
function refactorNamespaceIssues(value, label = "product.id") {
|
|
@@ -18071,12 +18077,13 @@ function resolveArchitectureDocumentationLayout(input) {
|
|
|
18071
18077
|
});
|
|
18072
18078
|
const relationTargets = (profile === REPO_HARNESS_PROJECTION_PROFILE ? [] : input.relations).map((relation) => layoutTarget(`projection_target.relation.${stableId(relation.id)}`, "relation-summary", { kind: "relation", id: relation.id }, `docs/architecture/relations/${pathSegment(relation.id)}.md`, "mixed", "markdown"));
|
|
18073
18079
|
const topologyTargets = hasRuntimeTopologyNodes(input.nodes) ? RUNTIME_TOPOLOGY_TARGETS : [];
|
|
18074
|
-
const targets = [FIXED_TARGETS[0], ...entityTargets, ...relationTargets, ...FIXED_TARGETS.slice(1), ...topologyTargets].map((target) => ({ ...target, path: requiredPath(target.path, target.targetId) }));
|
|
18080
|
+
const targets = (profile === REPO_HARNESS_PROJECTION_PROFILE ? [...entityTargets, ...topologyTargets] : [FIXED_TARGETS[0], ...entityTargets, ...relationTargets, ...FIXED_TARGETS.slice(1), ...topologyTargets]).map((target) => ({ ...target, path: requiredPath(target.path, target.targetId) }));
|
|
18075
18081
|
assertUniqueTargets(targets);
|
|
18076
18082
|
return {
|
|
18077
18083
|
profile,
|
|
18078
18084
|
targets,
|
|
18079
18085
|
expectedPaths: [...new Set([...targets.map((target) => target.path), "docs/architecture/.projection-manifest.json"])].sort(),
|
|
18086
|
+
retiredTargetPaths: profile === REPO_HARNESS_PROJECTION_PROFILE ? FIXED_TARGETS.map((target) => target.path) : [],
|
|
18080
18087
|
orphanRoots: ["docs/architecture/modules", "docs/architecture/relations"],
|
|
18081
18088
|
entityPathByNodeId: new Map(entityTargets.map((target, index) => [targetNodes[index].id, target.path]))
|
|
18082
18089
|
};
|
|
@@ -18124,7 +18131,10 @@ function loadArchitectureFilesForLayout(root, layout) {
|
|
|
18124
18131
|
}
|
|
18125
18132
|
if (paths.has(ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH)) {
|
|
18126
18133
|
const manifest = readProjectionOwnedFile(resolve5(root, ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH), ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH);
|
|
18127
|
-
|
|
18134
|
+
const retired = new Set(layout.retiredTargetPaths);
|
|
18135
|
+
for (const output of committedProjectionOutputs(currentFormatManifest(manifest.body))) {
|
|
18136
|
+
if (output.ownership !== "generated" && !retired.has(output.path))
|
|
18137
|
+
continue;
|
|
18128
18138
|
if (resolveSafeRegularPath(root, output.path, false))
|
|
18129
18139
|
paths.add(output.path);
|
|
18130
18140
|
}
|
|
@@ -18132,17 +18142,20 @@ function loadArchitectureFilesForLayout(root, layout) {
|
|
|
18132
18142
|
return [...paths].sort().map((path) => readProjectionOwnedFile(resolve5(root, path), path));
|
|
18133
18143
|
}
|
|
18134
18144
|
var OUTPUT_DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
18135
|
-
function
|
|
18145
|
+
function committedProjectionOutputs(manifest) {
|
|
18136
18146
|
if (manifest === undefined)
|
|
18137
18147
|
return [];
|
|
18138
18148
|
const outputs = [];
|
|
18139
18149
|
for (const [targetId, row] of Object.entries(manifest.targets)) {
|
|
18140
|
-
if (
|
|
18150
|
+
if (!OUTPUT_DIGEST_PATTERN.test(row.outputDigest) || !isRepoRelativePosixPath(row.path) || !row.path.startsWith("docs/architecture/") || row.path === ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH)
|
|
18141
18151
|
continue;
|
|
18142
|
-
outputs.push({ targetId, path: row.path, outputDigest: row.outputDigest });
|
|
18152
|
+
outputs.push({ targetId, path: row.path, ownership: row.ownership, outputDigest: row.outputDigest });
|
|
18143
18153
|
}
|
|
18144
18154
|
return outputs;
|
|
18145
18155
|
}
|
|
18156
|
+
function committedGeneratedOutputs(manifest) {
|
|
18157
|
+
return committedProjectionOutputs(manifest).filter((output) => output.ownership === "generated").map(({ targetId, path, outputDigest }) => ({ targetId, path, outputDigest }));
|
|
18158
|
+
}
|
|
18146
18159
|
function currentFormatManifest(body) {
|
|
18147
18160
|
let parsed;
|
|
18148
18161
|
try {
|
|
@@ -20034,9 +20047,17 @@ class ProjectionPreconditionError extends Error {
|
|
|
20034
20047
|
class ProjectionManifestUpgradeRequiredError extends ProjectionPreconditionError {
|
|
20035
20048
|
schemaVersion;
|
|
20036
20049
|
reasonCode = PROJECTION_MANIFEST_UPGRADE_REQUIRED;
|
|
20050
|
+
details;
|
|
20037
20051
|
constructor(schemaVersion) {
|
|
20038
|
-
super(`${PROJECTION_MANIFEST_UPGRADE_REQUIRED}: ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH} is ${schemaVersion}, and this archctx reads only ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION}; reach a clean projection on the earlier release first, then re-baseline once with
|
|
20052
|
+
super(`${PROJECTION_MANIFEST_UPGRADE_REQUIRED}: ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH} is ${schemaVersion}, and this archctx reads only ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION}; reach a clean projection on the earlier release first, then re-baseline once with ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_UPGRADE_COMMAND}; if that apply names whole-file outputs the earlier release wrote, remove them with git rm and run it again; ${ARCHITECTURE_DOCS_PROJECTION_MANIFEST_MIGRATION_GUIDE} lists the model and release preconditions`);
|
|
20039
20053
|
this.schemaVersion = schemaVersion;
|
|
20054
|
+
this.details = isSupersededArchitectureDocsProjectionManifestSchemaVersion(schemaVersion) ? {
|
|
20055
|
+
manifestPath: ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH,
|
|
20056
|
+
foundSchemaVersion: schemaVersion,
|
|
20057
|
+
supportedSchemaVersion: ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION,
|
|
20058
|
+
upgradeCommand: ARCHITECTURE_DOCS_PROJECTION_MANIFEST_UPGRADE_COMMAND,
|
|
20059
|
+
migrationGuide: ARCHITECTURE_DOCS_PROJECTION_MANIFEST_MIGRATION_GUIDE
|
|
20060
|
+
} : {};
|
|
20040
20061
|
}
|
|
20041
20062
|
}
|
|
20042
20063
|
|
|
@@ -25459,6 +25480,8 @@ function architectureDeltaTask(git) {
|
|
|
25459
25480
|
var CODEGRAPH_IMPORT_NODE_READ_LIMIT = 50000;
|
|
25460
25481
|
var CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES = 4 * 1024;
|
|
25461
25482
|
function prepareArchitectureDocumentationProjectionSnapshot(root, model, options = {}) {
|
|
25483
|
+
if (codeGraphIndexAvailable(root))
|
|
25484
|
+
assertRepositoryCodeFactsReady(root, options.binary ? { binary: options.binary } : {});
|
|
25462
25485
|
const baseHeadSha = readProjectionHeadSha(root);
|
|
25463
25486
|
const sourceFiles = listGitVisibleFiles(root);
|
|
25464
25487
|
const worktreeDigest = architectureDocumentationProjectionWorktreeDigest(root, model, sourceFiles);
|
|
@@ -25603,7 +25626,33 @@ function prepareProjectionCodeFacts(root, model, options) {
|
|
|
25603
25626
|
}
|
|
25604
25627
|
};
|
|
25605
25628
|
}
|
|
25629
|
+
|
|
25630
|
+
class CodeGraphIndexBuildInProgressError extends Error {
|
|
25631
|
+
constructor() {
|
|
25632
|
+
super("CodeGraph index build is in progress");
|
|
25633
|
+
this.name = "CodeGraphIndexBuildInProgressError";
|
|
25634
|
+
}
|
|
25635
|
+
}
|
|
25636
|
+
function codeGraphIndexBuildInProgress(workspaceRoot) {
|
|
25637
|
+
let raw;
|
|
25638
|
+
try {
|
|
25639
|
+
raw = readFileSync7(join3(workspaceRoot, ".codegraph", "codegraph.lock"), "utf8").trim();
|
|
25640
|
+
} catch {
|
|
25641
|
+
return false;
|
|
25642
|
+
}
|
|
25643
|
+
const pid = Number.parseInt(raw, 10);
|
|
25644
|
+
if (!Number.isSafeInteger(pid) || pid <= 0 || String(pid) !== raw)
|
|
25645
|
+
return false;
|
|
25646
|
+
try {
|
|
25647
|
+
process.kill(pid, 0);
|
|
25648
|
+
return true;
|
|
25649
|
+
} catch (error) {
|
|
25650
|
+
return error.code === "EPERM";
|
|
25651
|
+
}
|
|
25652
|
+
}
|
|
25606
25653
|
function readProjectionCodeGraphStatus(invocation, root, timeout, envOverrides) {
|
|
25654
|
+
if (codeGraphIndexBuildInProgress(root))
|
|
25655
|
+
throw new CodeGraphIndexBuildInProgressError;
|
|
25607
25656
|
const output = runProjectionCodeGraph(invocation, root, ["status", "-j", root], timeout, envOverrides);
|
|
25608
25657
|
try {
|
|
25609
25658
|
return JSON.parse(output);
|
|
@@ -25922,8 +25971,9 @@ function mirrorCodeGraphInputs(root, gitDir, commonDir, destination) {
|
|
|
25922
25971
|
var CODEGRAPH_NO_DOWNLOAD_ENV = { CODEGRAPH_NO_DOWNLOAD: "1" };
|
|
25923
25972
|
function inspectRepositoryCodeFacts(root, options = {}) {
|
|
25924
25973
|
const schemaVersion = "archcontext.repository-code-facts/v1";
|
|
25925
|
-
let codeGraphVersion;
|
|
25974
|
+
let codeGraphVersion = "";
|
|
25926
25975
|
let status;
|
|
25976
|
+
let indexBuildInProgress = false;
|
|
25927
25977
|
const indexPresent = codeGraphIndexAvailable(root);
|
|
25928
25978
|
try {
|
|
25929
25979
|
const invocation = options.binary ? codeGraphCliInvocation(options.binary, root, "") : packagedCodeGraphCliInvocation();
|
|
@@ -25931,18 +25981,37 @@ function inspectRepositoryCodeFacts(root, options = {}) {
|
|
|
25931
25981
|
if (indexPresent)
|
|
25932
25982
|
status = readProjectionCodeGraphStatus(invocation, root, options.timeouts?.statusMs ?? 1e4, CODEGRAPH_NO_DOWNLOAD_ENV);
|
|
25933
25983
|
} catch (error) {
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
25940
|
-
|
|
25984
|
+
if (!(error instanceof CodeGraphIndexBuildInProgressError)) {
|
|
25985
|
+
return {
|
|
25986
|
+
schemaVersion,
|
|
25987
|
+
state: "unavailable",
|
|
25988
|
+
requiredVersion: REQUIRED_CODEGRAPH_VERSION,
|
|
25989
|
+
reasonCode: "codegraph-unavailable",
|
|
25990
|
+
message: (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 400)
|
|
25991
|
+
};
|
|
25992
|
+
}
|
|
25993
|
+
indexBuildInProgress = true;
|
|
25941
25994
|
}
|
|
25942
25995
|
const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION ? [] : [{ code: "version-mismatch", message: CODE_FACTS_ISSUE_MESSAGES["version-mismatch"] }];
|
|
25943
25996
|
if (!indexPresent) {
|
|
25944
25997
|
return { schemaVersion, state: "index-missing", indexPresent: false, requiredVersion: REQUIRED_CODEGRAPH_VERSION, codeGraphVersion, issues: versionIssues };
|
|
25945
25998
|
}
|
|
25999
|
+
if (indexBuildInProgress) {
|
|
26000
|
+
return {
|
|
26001
|
+
schemaVersion,
|
|
26002
|
+
state: "not-ready",
|
|
26003
|
+
indexPresent: true,
|
|
26004
|
+
requiredVersion: REQUIRED_CODEGRAPH_VERSION,
|
|
26005
|
+
codeGraphVersion,
|
|
26006
|
+
indexBuiltWithVersion: null,
|
|
26007
|
+
extractionVersion: { builtWith: null, current: null },
|
|
26008
|
+
indexState: null,
|
|
26009
|
+
reindexRecommended: null,
|
|
26010
|
+
pendingRefs: null,
|
|
26011
|
+
pendingChanges: null,
|
|
26012
|
+
issues: [...versionIssues, { code: "index-incomplete", message: CODE_FACTS_ISSUE_MESSAGES["index-incomplete"] }]
|
|
26013
|
+
};
|
|
26014
|
+
}
|
|
25946
26015
|
if (typeof status !== "object" || status === null || Array.isArray(status)) {
|
|
25947
26016
|
return {
|
|
25948
26017
|
schemaVersion,
|
|
@@ -26329,6 +26398,8 @@ function selectedProducerFailure(error, stage) {
|
|
|
26329
26398
|
const details = stage === "docs-anchors" || stage === "flows-check" ? {} : { checks: ["ownership", "anchors"] };
|
|
26330
26399
|
return new FailureError(selectedProducerData(error, "code"), selectedProducerData(error, "reasonCode"), details);
|
|
26331
26400
|
}
|
|
26401
|
+
if (error instanceof ProjectionManifestUpgradeRequiredError)
|
|
26402
|
+
return new FailureError("AC_PRECONDITION_FAILED", selectedProducerData(error, "reasonCode"), selectedProducerData(error, "details"));
|
|
26332
26403
|
if (error instanceof ProjectionPreconditionError)
|
|
26333
26404
|
return new FailureError("AC_PRECONDITION_FAILED", selectedProducerData(error, "reasonCode"));
|
|
26334
26405
|
if (error instanceof ProjectionCodeFactsUnavailableError)
|
|
@@ -34030,6 +34101,8 @@ function publicOperationFailure(error) {
|
|
|
34030
34101
|
failure = toEnvelope("public.operation", undefined, "public-operation").error;
|
|
34031
34102
|
} else if (error instanceof GitVisibleFilesUnavailableError)
|
|
34032
34103
|
failure = knownFailure(own2(error, "code"), own2(error, "reasonCode"));
|
|
34104
|
+
else if (error instanceof ProjectionManifestUpgradeRequiredError)
|
|
34105
|
+
failure = knownFailure("AC_PRECONDITION_FAILED", own2(error, "reasonCode"), own2(error, "details"));
|
|
34033
34106
|
else if (error instanceof ProjectionPreconditionError)
|
|
34034
34107
|
failure = knownFailure("AC_PRECONDITION_FAILED", own2(error, "reasonCode"));
|
|
34035
34108
|
else if (error instanceof ProjectionCodeFactsUnavailableError)
|
|
@@ -41603,7 +41676,7 @@ function rpcRequiredBoolean(params, index, context, label) {
|
|
|
41603
41676
|
function rpcOptionalString(params, index, context, label) {
|
|
41604
41677
|
const value = params[index];
|
|
41605
41678
|
if (value === undefined || value === null)
|
|
41606
|
-
return
|
|
41679
|
+
return;
|
|
41607
41680
|
if (typeof value !== "string")
|
|
41608
41681
|
throw rpcInputInvalid(context, `${label} must be a string`);
|
|
41609
41682
|
return value;
|
|
@@ -41611,7 +41684,7 @@ function rpcOptionalString(params, index, context, label) {
|
|
|
41611
41684
|
function rpcOptionalNumber(params, index, context, label) {
|
|
41612
41685
|
const value = params[index];
|
|
41613
41686
|
if (value === undefined || value === null)
|
|
41614
|
-
return
|
|
41687
|
+
return;
|
|
41615
41688
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
41616
41689
|
throw rpcInputInvalid(context, `${label} must be a number`);
|
|
41617
41690
|
return value;
|
|
@@ -41619,7 +41692,7 @@ function rpcOptionalNumber(params, index, context, label) {
|
|
|
41619
41692
|
function rpcOptionalStringArray(params, index, context, label) {
|
|
41620
41693
|
const value = params[index];
|
|
41621
41694
|
if (value === undefined || value === null)
|
|
41622
|
-
return
|
|
41695
|
+
return;
|
|
41623
41696
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string"))
|
|
41624
41697
|
throw rpcInputInvalid(context, `${label} must be an array of strings`);
|
|
41625
41698
|
return value;
|
|
@@ -41636,7 +41709,7 @@ function rpcRequiredObject(params, index, context, label) {
|
|
|
41636
41709
|
function rpcOptionalObject(params, index, context, label) {
|
|
41637
41710
|
const value = params[index];
|
|
41638
41711
|
if (value === undefined || value === null)
|
|
41639
|
-
return
|
|
41712
|
+
return;
|
|
41640
41713
|
if (!isPlainRpcObject(value))
|
|
41641
41714
|
throw rpcInputInvalid(context, `${label} must be an object`);
|
|
41642
41715
|
return value;
|
|
@@ -61940,7 +62013,32 @@ function readFilePrefix2(path, length) {
|
|
|
61940
62013
|
}
|
|
61941
62014
|
}
|
|
61942
62015
|
var CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES2 = 4 * 1024;
|
|
62016
|
+
class CodeGraphIndexBuildInProgressError2 extends Error {
|
|
62017
|
+
constructor() {
|
|
62018
|
+
super("CodeGraph index build is in progress");
|
|
62019
|
+
this.name = "CodeGraphIndexBuildInProgressError";
|
|
62020
|
+
}
|
|
62021
|
+
}
|
|
62022
|
+
function codeGraphIndexBuildInProgress2(workspaceRoot) {
|
|
62023
|
+
let raw;
|
|
62024
|
+
try {
|
|
62025
|
+
raw = readFileSync32(join26(workspaceRoot, ".codegraph", "codegraph.lock"), "utf8").trim();
|
|
62026
|
+
} catch {
|
|
62027
|
+
return false;
|
|
62028
|
+
}
|
|
62029
|
+
const pid = Number.parseInt(raw, 10);
|
|
62030
|
+
if (!Number.isSafeInteger(pid) || pid <= 0 || String(pid) !== raw)
|
|
62031
|
+
return false;
|
|
62032
|
+
try {
|
|
62033
|
+
process.kill(pid, 0);
|
|
62034
|
+
return true;
|
|
62035
|
+
} catch (error) {
|
|
62036
|
+
return error.code === "EPERM";
|
|
62037
|
+
}
|
|
62038
|
+
}
|
|
61943
62039
|
function readProjectionCodeGraphStatus2(invocation, root, timeout, envOverrides) {
|
|
62040
|
+
if (codeGraphIndexBuildInProgress2(root))
|
|
62041
|
+
throw new CodeGraphIndexBuildInProgressError2;
|
|
61944
62042
|
const output = runProjectionCodeGraph2(invocation, root, ["status", "-j", root], timeout, envOverrides);
|
|
61945
62043
|
try {
|
|
61946
62044
|
return JSON.parse(output);
|
|
@@ -62011,8 +62109,9 @@ var CODEGRAPH_SNAPSHOT_MAX_BYTES2 = 512 * 1024 * 1024;
|
|
|
62011
62109
|
var CODEGRAPH_NO_DOWNLOAD_ENV2 = { CODEGRAPH_NO_DOWNLOAD: "1" };
|
|
62012
62110
|
function inspectRepositoryCodeFacts2(root, options = {}) {
|
|
62013
62111
|
const schemaVersion = "archcontext.repository-code-facts/v1";
|
|
62014
|
-
let codeGraphVersion;
|
|
62112
|
+
let codeGraphVersion = "";
|
|
62015
62113
|
let status;
|
|
62114
|
+
let indexBuildInProgress = false;
|
|
62016
62115
|
const indexPresent = codeGraphIndexAvailable2(root);
|
|
62017
62116
|
try {
|
|
62018
62117
|
const invocation = options.binary ? codeGraphCliInvocation2(options.binary, root, "") : packagedCodeGraphCliInvocation2();
|
|
@@ -62020,18 +62119,37 @@ function inspectRepositoryCodeFacts2(root, options = {}) {
|
|
|
62020
62119
|
if (indexPresent)
|
|
62021
62120
|
status = readProjectionCodeGraphStatus2(invocation, root, options.timeouts?.statusMs ?? 1e4, CODEGRAPH_NO_DOWNLOAD_ENV2);
|
|
62022
62121
|
} catch (error) {
|
|
62023
|
-
|
|
62024
|
-
|
|
62025
|
-
|
|
62026
|
-
|
|
62027
|
-
|
|
62028
|
-
|
|
62029
|
-
|
|
62122
|
+
if (!(error instanceof CodeGraphIndexBuildInProgressError2)) {
|
|
62123
|
+
return {
|
|
62124
|
+
schemaVersion,
|
|
62125
|
+
state: "unavailable",
|
|
62126
|
+
requiredVersion: REQUIRED_CODEGRAPH_VERSION2,
|
|
62127
|
+
reasonCode: "codegraph-unavailable",
|
|
62128
|
+
message: (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 400)
|
|
62129
|
+
};
|
|
62130
|
+
}
|
|
62131
|
+
indexBuildInProgress = true;
|
|
62030
62132
|
}
|
|
62031
62133
|
const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION2 ? [] : [{ code: "version-mismatch", message: CODE_FACTS_ISSUE_MESSAGES["version-mismatch"] }];
|
|
62032
62134
|
if (!indexPresent) {
|
|
62033
62135
|
return { schemaVersion, state: "index-missing", indexPresent: false, requiredVersion: REQUIRED_CODEGRAPH_VERSION2, codeGraphVersion, issues: versionIssues };
|
|
62034
62136
|
}
|
|
62137
|
+
if (indexBuildInProgress) {
|
|
62138
|
+
return {
|
|
62139
|
+
schemaVersion,
|
|
62140
|
+
state: "not-ready",
|
|
62141
|
+
indexPresent: true,
|
|
62142
|
+
requiredVersion: REQUIRED_CODEGRAPH_VERSION2,
|
|
62143
|
+
codeGraphVersion,
|
|
62144
|
+
indexBuiltWithVersion: null,
|
|
62145
|
+
extractionVersion: { builtWith: null, current: null },
|
|
62146
|
+
indexState: null,
|
|
62147
|
+
reindexRecommended: null,
|
|
62148
|
+
pendingRefs: null,
|
|
62149
|
+
pendingChanges: null,
|
|
62150
|
+
issues: [...versionIssues, { code: "index-incomplete", message: CODE_FACTS_ISSUE_MESSAGES["index-incomplete"] }]
|
|
62151
|
+
};
|
|
62152
|
+
}
|
|
62035
62153
|
if (typeof status !== "object" || status === null || Array.isArray(status)) {
|
|
62036
62154
|
return {
|
|
62037
62155
|
schemaVersion,
|