@nimiplatform/nimi-coding 0.3.1 → 0.5.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/CHANGELOG.md +32 -0
- package/CONTRIBUTING.md +3 -3
- package/README.md +297 -41
- package/README.zh-CN.md +306 -31
- package/bin/nimicoding.mjs +1 -1
- package/cli/commands/authority.mjs +804 -0
- package/cli/commands/clear.mjs +27 -7
- package/cli/commands/doctor.mjs +10 -1
- package/cli/commands/start.mjs +23 -27
- package/cli/commands/sync.mjs +21 -101
- package/cli/constants.mjs +7 -31
- package/cli/dev/check-authority-surface.mjs +182 -0
- package/cli/dev/check-product-conformance.mjs +889 -0
- package/cli/help.mjs +10 -17
- package/cli/index.mjs +6 -47
- package/cli/lib/authority/anchors.mjs +491 -0
- package/cli/lib/authority/audit.mjs +567 -0
- package/cli/lib/authority/change-candidates.mjs +807 -0
- package/cli/lib/authority/closed-sets.mjs +645 -0
- package/cli/lib/authority/compile.mjs +92 -0
- package/cli/lib/authority/compiled-snapshot.mjs +143 -0
- package/cli/lib/authority/consumers.mjs +520 -0
- package/cli/lib/authority/diagnostics.mjs +93 -0
- package/cli/lib/authority/diff.mjs +310 -0
- package/cli/lib/authority/discover.mjs +447 -0
- package/cli/lib/authority/format.mjs +169 -0
- package/cli/lib/authority/git-snapshot.mjs +720 -0
- package/cli/lib/authority/graph.mjs +351 -0
- package/cli/lib/authority/impact.mjs +125 -0
- package/cli/lib/authority/lexicon.mjs +55 -0
- package/cli/lib/authority/query.mjs +162 -0
- package/cli/lib/authority/repository-corpus.mjs +37 -0
- package/cli/lib/authority/repository-inventory.mjs +34 -0
- package/cli/lib/authority/review.mjs +212 -0
- package/cli/lib/authority/sarif.mjs +174 -0
- package/cli/lib/authority/scope-bindings.mjs +225 -0
- package/cli/lib/authority/source-map.mjs +105 -0
- package/cli/lib/authority/source-markdown.mjs +167 -0
- package/cli/lib/authority/source-yaml.mjs +308 -0
- package/cli/lib/authority/surface.mjs +97 -0
- package/cli/lib/authority/terms.mjs +422 -0
- package/cli/lib/authority/tracked-text.mjs +70 -0
- package/cli/lib/authority/validate.mjs +300 -0
- package/cli/lib/bootstrap.mjs +55 -71
- package/cli/lib/doctor.mjs +40 -123
- package/cli/lib/entrypoints.mjs +104 -105
- package/cli/lib/fs-helpers.mjs +139 -15
- package/cli/lib/internal/governance/config.mjs +0 -36
- package/cli/lib/shared.mjs +8 -21
- package/cli/lib/sync.mjs +81 -92
- package/cli/lib/value-helpers.mjs +0 -26
- package/cli/lib/yaml-helpers.mjs +1 -124
- package/cli/seeds/bootstrap.mjs +31 -87
- package/cli/seeds/seed-policy.yaml +9 -63
- package/contracts/authority-scope-bindings.schema.yaml +15 -0
- package/contracts/authority-source.schema.yaml +58 -0
- package/contracts/authority-verifier-bindings.schema.yaml +31 -0
- package/methodology/authority-authoring.yaml +127 -0
- package/methodology/core.yaml +34 -27
- package/package.json +10 -6
- package/spec/authority-anchors.yaml +99 -0
- package/spec/authority-audit.yaml +89 -0
- package/spec/authority-authoring.yaml +137 -0
- package/spec/authority-change-candidates.yaml +146 -0
- package/spec/authority-closed-sets.yaml +65 -0
- package/spec/authority-consumers.yaml +77 -0
- package/spec/authority-context.yaml +50 -0
- package/spec/authority-discovery.yaml +119 -0
- package/spec/authority-graph.yaml +59 -0
- package/spec/authority-impact.yaml +61 -0
- package/spec/authority-review.yaml +71 -0
- package/spec/authority-terms.yaml +63 -0
- package/spec/product-scope.yaml +37 -23
- package/cli/commands/blueprint-audit.mjs +0 -91
- package/cli/commands/classify-spec-tree.mjs +0 -5
- package/cli/commands/generate-spec-derived-docs.mjs +0 -151
- package/cli/commands/generate-spec-migration-plan.mjs +0 -30
- package/cli/commands/surface-validator-command.mjs +0 -49
- package/cli/commands/validate-domain-admission.mjs +0 -5
- package/cli/commands/validate-guidance-bodies.mjs +0 -5
- package/cli/commands/validate-placement.mjs +0 -5
- package/cli/commands/validate-projection-edges.mjs +0 -5
- package/cli/commands/validate-spec-audit.mjs +0 -27
- package/cli/commands/validate-spec-governance.mjs +0 -144
- package/cli/commands/validate-spec-tree.mjs +0 -27
- package/cli/commands/validate-table-family.mjs +0 -5
- package/cli/commands/validate-tracked-output-admission.mjs +0 -5
- package/cli/lib/blueprint-audit.mjs +0 -363
- package/cli/lib/contracts.mjs +0 -21
- package/cli/lib/internal/contracts-loaders.mjs +0 -47
- package/cli/lib/internal/contracts-parse.mjs +0 -192
- package/cli/lib/internal/governance/runner.mjs +0 -35
- package/cli/lib/internal/surface-taxonomy-validators.mjs +0 -1147
- package/cli/lib/internal/validators-shared.mjs +0 -12
- package/cli/lib/internal/validators-spec-helpers.mjs +0 -52
- package/cli/lib/internal/validators-spec.mjs +0 -380
- package/cli/lib/validators.mjs +0 -30
- package/config/bootstrap.yaml +0 -6
- package/config/spec-generation-inputs.yaml +0 -41
- package/contracts/domain-admission.schema.yaml +0 -56
- package/contracts/migration-inventory.schema.yaml +0 -15
- package/contracts/negative-fixtures.yaml +0 -26
- package/contracts/placement-contract.schema.yaml +0 -127
- package/contracts/projection-edge.schema.yaml +0 -46
- package/contracts/shared-enums.yaml +0 -63
- package/contracts/spec-generation-audit.schema.yaml +0 -46
- package/contracts/spec-generation-inputs.schema.yaml +0 -43
- package/contracts/spec-layout.schema.yaml +0 -32
- package/contracts/surface-taxonomy.schema.yaml +0 -139
- package/contracts/table-family.schema.yaml +0 -133
- package/contracts/tracked-output-admission.schema.yaml +0 -17
- package/methodology/four-closure-policy.yaml +0 -20
- package/methodology/role-separation-policy.yaml +0 -22
- package/methodology/spec-reconstruction.yaml +0 -69
- package/spec/_meta/spec-tree-model.yaml +0 -44
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { compareText } from "./diagnostics.mjs";
|
|
2
|
+
import { formattingDiagnostics, parseAuthorityPath } from "./format.mjs";
|
|
3
|
+
import { validateAuthorityScopeBindings } from "./scope-bindings.mjs";
|
|
4
|
+
import { buildSourceMap } from "./source-map.mjs";
|
|
5
|
+
import { validateAuthoritySources } from "./validate.mjs";
|
|
6
|
+
|
|
7
|
+
function lowerUnit(source) {
|
|
8
|
+
const data = source.data;
|
|
9
|
+
const unit = {
|
|
10
|
+
id: data.id,
|
|
11
|
+
kind: data.kind,
|
|
12
|
+
owner: data.owner,
|
|
13
|
+
lifecycle: data.lifecycle,
|
|
14
|
+
metadata: {
|
|
15
|
+
title: data.title,
|
|
16
|
+
...(data.reason === undefined ? {} : { reason: data.reason }),
|
|
17
|
+
},
|
|
18
|
+
relations: [...data.relations]
|
|
19
|
+
.map((relation) => ({ type: relation.type, target: relation.target }))
|
|
20
|
+
.sort((left, right) => compareText(left.type, right.type) || compareText(left.target, right.target)),
|
|
21
|
+
};
|
|
22
|
+
if (data.lifecycle === "active" && data.kind === "definition") unit.semantic = { meaning: data.meaning };
|
|
23
|
+
if (data.lifecycle === "active" && data.kind === "rule") unit.semantic = {
|
|
24
|
+
modality: data.modality,
|
|
25
|
+
scope: [...data.scope].sort(compareText),
|
|
26
|
+
statement: data.statement,
|
|
27
|
+
condition: data.condition,
|
|
28
|
+
failure: data.failure,
|
|
29
|
+
};
|
|
30
|
+
return unit;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function admittedSources(inputPath) {
|
|
34
|
+
const parsed = await parseAuthorityPath(inputPath);
|
|
35
|
+
const formatDiagnostics = formattingDiagnostics(parsed.documents, parsed.contract);
|
|
36
|
+
if (formatDiagnostics.length > 0) return { ok: false, diagnostics: formatDiagnostics, parsed };
|
|
37
|
+
const validation = validateAuthoritySources(parsed.sources, parsed.contract);
|
|
38
|
+
if (!validation.ok) return { ok: false, diagnostics: validation.diagnostics, parsed };
|
|
39
|
+
return { ok: true, diagnostics: [], parsed };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function checkAuthorityPath(inputPath, { scopeBindings = null } = {}) {
|
|
43
|
+
const result = await admittedSources(inputPath);
|
|
44
|
+
if (!result.ok || scopeBindings === null) return {
|
|
45
|
+
ok: result.ok,
|
|
46
|
+
diagnostics: result.diagnostics,
|
|
47
|
+
fileCount: result.parsed.files.length,
|
|
48
|
+
unitCount: result.ok ? result.parsed.sources.length : 0,
|
|
49
|
+
};
|
|
50
|
+
const scopeValidation = await validateAuthorityScopeBindings(result.parsed.sources, scopeBindings);
|
|
51
|
+
return {
|
|
52
|
+
ok: scopeValidation.ok,
|
|
53
|
+
diagnostics: scopeValidation.diagnostics,
|
|
54
|
+
fileCount: result.parsed.files.length,
|
|
55
|
+
unitCount: scopeValidation.ok ? result.parsed.sources.length : 0,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function compileAuthorityPath(inputPath) {
|
|
60
|
+
const admitted = await admittedSources(inputPath);
|
|
61
|
+
const inputBytes = admitted.parsed.documents.reduce((total, document) => total + document.inputBytes, 0);
|
|
62
|
+
if (!admitted.ok) return {
|
|
63
|
+
ok: false,
|
|
64
|
+
diagnostics: admitted.diagnostics,
|
|
65
|
+
fileCount: admitted.parsed.files.length,
|
|
66
|
+
unitCount: 0,
|
|
67
|
+
inputBytes,
|
|
68
|
+
ir: null,
|
|
69
|
+
sourceMap: null,
|
|
70
|
+
};
|
|
71
|
+
const sources = [...admitted.parsed.sources].sort((left, right) => compareText(left.data.id, right.data.id));
|
|
72
|
+
const ir = { units: sources.map(lowerUnit) };
|
|
73
|
+
const mapped = buildSourceMap(sources, ir);
|
|
74
|
+
if (!mapped.ok) return {
|
|
75
|
+
ok: false,
|
|
76
|
+
diagnostics: mapped.diagnostics,
|
|
77
|
+
fileCount: admitted.parsed.files.length,
|
|
78
|
+
unitCount: 0,
|
|
79
|
+
inputBytes,
|
|
80
|
+
ir: null,
|
|
81
|
+
sourceMap: null,
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
ok: true,
|
|
85
|
+
diagnostics: [],
|
|
86
|
+
fileCount: admitted.parsed.files.length,
|
|
87
|
+
unitCount: ir.units.length,
|
|
88
|
+
inputBytes,
|
|
89
|
+
ir,
|
|
90
|
+
sourceMap: mapped.sourceMap,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { compareText, portablePath } from "./diagnostics.mjs";
|
|
4
|
+
|
|
5
|
+
class ReadonlyIndex {
|
|
6
|
+
#values;
|
|
7
|
+
|
|
8
|
+
constructor(entries) {
|
|
9
|
+
this.#values = new Map(entries);
|
|
10
|
+
Object.freeze(this);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get size() {
|
|
14
|
+
return this.#values.size;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get(key) {
|
|
18
|
+
return this.#values.get(key);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
has(key) {
|
|
22
|
+
return this.#values.has(key);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
*entries() {
|
|
26
|
+
for (const [key, value] of this.#values) yield Object.freeze([key, value]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
keys() {
|
|
30
|
+
return this.#values.keys();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
values() {
|
|
34
|
+
return this.#values.values();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
forEach(callback, thisArg = undefined) {
|
|
38
|
+
for (const [key, value] of this.#values) callback.call(thisArg, value, key, this);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[Symbol.iterator]() {
|
|
42
|
+
return this.entries();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get [Symbol.toStringTag]() {
|
|
46
|
+
return "ReadonlyIndex";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function freezeTree(value, seen = new Set()) {
|
|
51
|
+
if (value === null || typeof value !== "object" || Object.isFrozen(value) || seen.has(value)) return value;
|
|
52
|
+
seen.add(value);
|
|
53
|
+
for (const key of Reflect.ownKeys(value)) freezeTree(value[key], seen);
|
|
54
|
+
return Object.freeze(value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readonlyIndex(entries) {
|
|
58
|
+
return new ReadonlyIndex([...entries].sort(([left], [right]) => compareText(left, right)));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function groupedIndex(entries, keys) {
|
|
62
|
+
const groups = new Map();
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
for (const key of keys(entry)) {
|
|
65
|
+
const values = groups.get(key) ?? [];
|
|
66
|
+
values.push(entry);
|
|
67
|
+
groups.set(key, values);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return readonlyIndex([...groups.entries()].map(([key, values]) => [
|
|
71
|
+
key,
|
|
72
|
+
Object.freeze(values.sort((left, right) => compareText(left.unit.id, right.unit.id))),
|
|
73
|
+
]));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function compareEdge(left, right) {
|
|
77
|
+
return compareText(left.source, right.source)
|
|
78
|
+
|| compareText(left.type, right.type)
|
|
79
|
+
|| compareText(left.target, right.target);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function adjacencyIndexes(entries) {
|
|
83
|
+
const outgoing = new Map(entries.map((entry) => [entry.unit.id, []]));
|
|
84
|
+
const incoming = new Map(entries.map((entry) => [entry.unit.id, []]));
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
entry.unit.relations.forEach((relation, relationIndex) => {
|
|
87
|
+
const edge = Object.freeze({
|
|
88
|
+
source: entry.unit.id,
|
|
89
|
+
type: relation.type,
|
|
90
|
+
target: relation.target,
|
|
91
|
+
sourceUnitIndex: entry.unitIndex,
|
|
92
|
+
relationIndex,
|
|
93
|
+
});
|
|
94
|
+
outgoing.get(edge.source).push(edge);
|
|
95
|
+
incoming.get(edge.target).push(edge);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const finish = (index) => readonlyIndex([...index.entries()].map(([id, edges]) => [
|
|
99
|
+
id,
|
|
100
|
+
Object.freeze(edges.sort(compareEdge)),
|
|
101
|
+
]));
|
|
102
|
+
return { outgoing: finish(outgoing), incoming: finish(incoming) };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function createCompiledAuthoritySnapshot(compiled, inputRoot) {
|
|
106
|
+
if (
|
|
107
|
+
compiled?.ok !== true
|
|
108
|
+
|| !Array.isArray(compiled?.ir?.units)
|
|
109
|
+
|| compiled?.sourceMap?.fields === null
|
|
110
|
+
|| typeof compiled?.sourceMap?.fields !== "object"
|
|
111
|
+
) {
|
|
112
|
+
throw new TypeError("compiled authority snapshot requires one successful compiler result");
|
|
113
|
+
}
|
|
114
|
+
if (typeof inputRoot !== "string" || inputRoot.length === 0) {
|
|
115
|
+
throw new TypeError("compiled authority snapshot requires one non-empty input root");
|
|
116
|
+
}
|
|
117
|
+
const resolvedRoot = path.resolve(inputRoot);
|
|
118
|
+
const entries = compiled.ir.units.map((unit, unitIndex) => {
|
|
119
|
+
const mapped = compiled.sourceMap.fields[`/units/${unitIndex}/id`];
|
|
120
|
+
if (!mapped) throw new TypeError(`compiled authority snapshot cannot locate unit identity: ${unit.id}`);
|
|
121
|
+
return Object.freeze({
|
|
122
|
+
unit,
|
|
123
|
+
unitIndex,
|
|
124
|
+
container: portablePath(mapped.file, resolvedRoot),
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const byId = readonlyIndex(entries.map((entry) => [entry.unit.id, entry]));
|
|
128
|
+
const byOwner = groupedIndex(entries, (entry) => [entry.unit.owner]);
|
|
129
|
+
const byScope = groupedIndex(entries, (entry) => entry.unit.semantic?.scope ?? []);
|
|
130
|
+
const byContainer = groupedIndex(entries, (entry) => [entry.container]);
|
|
131
|
+
const { incoming, outgoing } = adjacencyIndexes(entries);
|
|
132
|
+
freezeTree(compiled);
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
inputRoot: resolvedRoot,
|
|
135
|
+
compiled,
|
|
136
|
+
byId,
|
|
137
|
+
byOwner,
|
|
138
|
+
byScope,
|
|
139
|
+
byContainer,
|
|
140
|
+
incoming,
|
|
141
|
+
outgoing,
|
|
142
|
+
});
|
|
143
|
+
}
|