@sdd-method/author-cli 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/NOTICE +17 -0
- package/README.md +6 -2
- package/dist/author-bin.js +8 -0
- package/dist/author-program.js +99 -0
- package/dist/lib/adr-origin.js +59 -0
- package/dist/lib/agent-harness.js +37 -0
- package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +18 -0
- package/dist/lib/api-surface/analyzers/library.js +95 -0
- package/dist/lib/api-surface/analyzers/serverless.js +336 -0
- package/dist/lib/api-surface/analyzers/spring.js +237 -0
- package/dist/lib/api-surface/analyzers/terraform.js +109 -0
- package/dist/lib/api-surface/detect.js +25 -0
- package/dist/lib/api-surface/emit.js +109 -0
- package/dist/lib/api-surface/file-set.js +43 -0
- package/dist/lib/api-surface/index.js +134 -0
- package/dist/lib/api-surface/types.js +17 -0
- package/dist/lib/briefing/index.js +13 -0
- package/dist/lib/briefing/render.js +132 -0
- package/dist/lib/briefing/variables.js +92 -0
- package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
- package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
- package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
- package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
- package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
- package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
- package/dist/lib/catalogue/archetype/resolver.js +103 -0
- package/dist/lib/catalogue/archetype/wireframe.js +418 -0
- package/dist/lib/catalogue/baseline-check.js +151 -0
- package/dist/lib/catalogue/build.js +1502 -0
- package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
- package/dist/lib/catalogue/builders/adrs.js +222 -0
- package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
- package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
- package/dist/lib/catalogue/builders/c4-components.js +229 -0
- package/dist/lib/catalogue/builders/c4-containers.js +240 -0
- package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
- package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
- package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
- package/dist/lib/catalogue/builders/c4-systems.js +98 -0
- package/dist/lib/catalogue/builders/capabilities.js +488 -0
- package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
- package/dist/lib/catalogue/builders/capability-edges.js +256 -0
- package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
- package/dist/lib/catalogue/builders/capability-spec.js +348 -0
- package/dist/lib/catalogue/builders/container-dependency.js +183 -0
- package/dist/lib/catalogue/builders/contracts.js +402 -0
- package/dist/lib/catalogue/builders/data-items.js +344 -0
- package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
- package/dist/lib/catalogue/builders/domain-models.js +175 -0
- package/dist/lib/catalogue/builders/domains.js +119 -0
- package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
- package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
- package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
- package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
- package/dist/lib/catalogue/builders/entity-slug.js +47 -0
- package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
- package/dist/lib/catalogue/builders/event-edges.js +327 -0
- package/dist/lib/catalogue/builders/events.js +168 -0
- package/dist/lib/catalogue/builders/evidence-class.js +43 -0
- package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
- package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
- package/dist/lib/catalogue/builders/feature-contract.js +77 -0
- package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
- package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
- package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
- package/dist/lib/catalogue/builders/feature-groups.js +63 -0
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
- package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
- package/dist/lib/catalogue/builders/features.js +300 -0
- package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
- package/dist/lib/catalogue/builders/glossary.js +204 -0
- package/dist/lib/catalogue/builders/implementations.js +214 -0
- package/dist/lib/catalogue/builders/index.js +64 -0
- package/dist/lib/catalogue/builders/integration-components.js +344 -0
- package/dist/lib/catalogue/builders/integrations.js +181 -0
- package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
- package/dist/lib/catalogue/builders/persona-container.js +133 -0
- package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
- package/dist/lib/catalogue/builders/personas.js +152 -0
- package/dist/lib/catalogue/builders/product-edges.js +106 -0
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
- package/dist/lib/catalogue/builders/products.js +162 -0
- package/dist/lib/catalogue/builders/quality-scores.js +635 -0
- package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
- package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
- package/dist/lib/catalogue/builders/repo-edges.js +340 -0
- package/dist/lib/catalogue/builders/repositories.js +458 -0
- package/dist/lib/catalogue/builders/scaffolds.js +197 -0
- package/dist/lib/catalogue/builders/screen-tree.js +222 -0
- package/dist/lib/catalogue/builders/screens.js +396 -0
- package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
- package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
- package/dist/lib/catalogue/builders/service-contract.js +74 -0
- package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
- package/dist/lib/catalogue/builders/services.js +232 -0
- package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
- package/dist/lib/catalogue/builders/user-journeys.js +253 -0
- package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
- package/dist/lib/catalogue/cli-version.js +10 -0
- package/dist/lib/catalogue/contract-id.js +49 -0
- package/dist/lib/catalogue/coverage-predicates.js +603 -0
- package/dist/lib/catalogue/coverage-render.js +552 -0
- package/dist/lib/catalogue/coverage-run.js +115 -0
- package/dist/lib/catalogue/csv-reader.js +97 -0
- package/dist/lib/catalogue/csv-writer.js +96 -0
- package/dist/lib/catalogue/data-plane-coherence.js +77 -0
- package/dist/lib/catalogue/ddl-generator.js +233 -0
- package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
- package/dist/lib/catalogue/engine-validator.js +239 -0
- package/dist/lib/catalogue/ext-id.js +76 -0
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
- package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
- package/dist/lib/catalogue/finding-identity.js +183 -0
- package/dist/lib/catalogue/findings.js +46 -0
- package/dist/lib/catalogue/fk-validator.js +136 -0
- package/dist/lib/catalogue/freshness.js +70 -0
- package/dist/lib/catalogue/gate-checks.js +338 -0
- package/dist/lib/catalogue/graph-projection.js +346 -0
- package/dist/lib/catalogue/impact-run.js +228 -0
- package/dist/lib/catalogue/manifest-loader.js +435 -0
- package/dist/lib/catalogue/platform-scope.js +7 -0
- package/dist/lib/catalogue/population-checks.js +87 -0
- package/dist/lib/catalogue/relationship-semantics.js +66 -0
- package/dist/lib/catalogue/reverse-product-quality.js +99 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
- package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
- package/dist/lib/catalogue/scaffold-delta.js +261 -0
- package/dist/lib/catalogue/schema-compat.js +52 -0
- package/dist/lib/catalogue/schema-loader.js +87 -0
- package/dist/lib/catalogue/scores-coverage.js +573 -0
- package/dist/lib/catalogue/screen-validation-loader.js +89 -0
- package/dist/lib/catalogue/service-architecture-parser.js +193 -0
- package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
- package/dist/lib/catalogue/staleness-verdicts.js +345 -0
- package/dist/lib/catalogue/traceability-loader.js +350 -0
- package/dist/lib/catalogue/validate.js +200 -0
- package/dist/lib/catalogue/version.js +18 -0
- package/dist/lib/conformance/binding.js +70 -0
- package/dist/lib/conformance/finding.js +69 -0
- package/dist/lib/conformance/index.js +578 -0
- package/dist/lib/context-pull/index.js +191 -0
- package/dist/lib/context-pull/resolve.js +70 -0
- package/dist/lib/context-pull/retired-mcp-setup.js +7 -0
- package/dist/lib/contracts-availability/index.js +267 -0
- package/dist/lib/generate-briefing/index.js +77 -0
- package/dist/lib/list/discover.js +100 -0
- package/dist/lib/list/format.js +84 -0
- package/dist/lib/list/index.js +16 -0
- package/dist/lib/lock-file.js +437 -0
- package/dist/lib/pack/log.js +18 -0
- package/dist/lib/pack/schema.js +121 -0
- package/dist/lib/pack/seed.js +182 -0
- package/dist/lib/pack/validate.js +49 -0
- package/dist/lib/package-version.js +92 -0
- package/dist/lib/plan/index.js +69 -0
- package/dist/lib/plan/read-sdd.js +102 -0
- package/dist/lib/plan/render-template.js +84 -0
- package/dist/lib/repo-kind.js +129 -0
- package/dist/lib/scaffold/domain.js +234 -0
- package/dist/lib/scaffold/registry.js +136 -0
- package/dist/lib/scripts.js +7 -0
- package/dist/lib/sdd-index/repo-config.js +245 -0
- package/dist/lib/sdd-reader.js +91 -0
- package/dist/lib/snapshot/advance.js +454 -0
- package/dist/lib/snapshot/diff.js +190 -0
- package/dist/lib/snapshot/fold.js +208 -0
- package/dist/lib/snapshot/land.js +178 -0
- package/dist/lib/snapshot/manifest.js +206 -0
- package/dist/lib/snapshot/targets.js +169 -0
- package/dist/lib/snapshot/verify.js +91 -0
- package/dist/lib/sync/briefing-merge.js +106 -0
- package/dist/lib/sync/bundle-cache.js +109 -0
- package/dist/lib/sync/yaml-block-merge.js +120 -0
- package/dist/lib/validate/aggregate.js +51 -0
- package/dist/lib/validate/bundle-coherence.js +74 -0
- package/dist/lib/validate/dispatch.js +243 -0
- package/dist/lib/validate/index.js +110 -0
- package/dist/lib/validate/plan.js +54 -0
- package/dist/lib/validate/runner.js +85 -0
- package/dist/lib/validate/yaml-parse.js +112 -0
- package/dist/maintain-version.json +4 -0
- package/dist/verbs/api-surface.js +71 -0
- package/dist/verbs/catalogue-check.js +28 -0
- package/dist/verbs/catalogue-verify.js +114 -0
- package/dist/verbs/conformance.js +34 -0
- package/dist/verbs/context.js +39 -0
- package/dist/verbs/contracts.js +34 -0
- package/dist/verbs/generate-briefing.js +29 -0
- package/dist/verbs/list.js +45 -0
- package/dist/verbs/pack-validate.js +17 -0
- package/dist/verbs/path-option.js +25 -0
- package/dist/verbs/plan.js +42 -0
- package/dist/verbs/render.js +87 -0
- package/dist/verbs/scaffold.js +83 -0
- package/dist/verbs/snapshot.js +134 -0
- package/dist/verbs/validate.js +48 -0
- package/dist/verify-program.js +122 -0
- package/dist/verify-version.json +4 -0
- package/package.json +32 -4
- package/scripts/validate-app-dependencies.sh +664 -0
- package/scripts/validate-app-docs.sh +199 -0
- package/scripts/validate-integration-docs.sh +505 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse orchestration/repo-config/**\/*.yaml into ServiceNode records.
|
|
3
|
+
*
|
|
4
|
+
* Accepts both the pre-ADR-0110 shape (contracts as lists of path
|
|
5
|
+
* strings) and the extended shape (`{path, package?, version?, stability?}`).
|
|
6
|
+
* Unknown top-level fields are preserved in `ServiceNode.extra` so that
|
|
7
|
+
* adopter-specific schema additions don't get lost.
|
|
8
|
+
*/
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { parse as parseYaml } from "yaml";
|
|
11
|
+
import { walkFiles } from "../sdd-reader.js";
|
|
12
|
+
export const REPO_CONFIG_DIR = "orchestration/repo-config";
|
|
13
|
+
const CONTRACT_KINDS = [
|
|
14
|
+
"openapi",
|
|
15
|
+
"proto",
|
|
16
|
+
"avro",
|
|
17
|
+
"spicedb",
|
|
18
|
+
];
|
|
19
|
+
export function readAllRepoConfigs(reader) {
|
|
20
|
+
const services = [];
|
|
21
|
+
const warnings = [];
|
|
22
|
+
const files = walkFiles(reader, REPO_CONFIG_DIR, (p) => p.endsWith(".yaml") || p.endsWith(".yml"));
|
|
23
|
+
for (const relPath of files) {
|
|
24
|
+
const parsed = parseRepoConfigFile(reader, relPath, warnings);
|
|
25
|
+
if (parsed)
|
|
26
|
+
services.push(...expandDeclaredServices(parsed, relPath, warnings));
|
|
27
|
+
}
|
|
28
|
+
return { services, warnings };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* M-ADR 0197 §4.6 — expand one parsed repo-config node into N declared
|
|
32
|
+
* service nodes when the file's `services:` block (preserved unread in
|
|
33
|
+
* `base.extra.services` by `parseRepoConfigFile`) is present and valid.
|
|
34
|
+
*
|
|
35
|
+
* Reuses §4.6's exact primary-selection rule rather than inventing a
|
|
36
|
+
* second one for this index: the declared entry matching the
|
|
37
|
+
* repository's own id is primary; failing that, exactly one entry
|
|
38
|
+
* marked `primary: true`; failing that, attribution is refused with a
|
|
39
|
+
* warning rather than guessed. Only the primary entry inherits the
|
|
40
|
+
* file's top-level `dependencies` / `contracts` / `aggregates` — this
|
|
41
|
+
* model has no fact type below "service", so there is no separate
|
|
42
|
+
* repo-level node to keep alongside the declared ones. A non-primary
|
|
43
|
+
* declared entry's own dependencies/contracts stay empty until an
|
|
44
|
+
* authoring surface for them exists (tracked as a residual gap, not
|
|
45
|
+
* fixed here — a per-entry-attributed events:/contracts: authoring
|
|
46
|
+
* mechanism is §4.6's still-unimplemented other half).
|
|
47
|
+
*
|
|
48
|
+
* Absent or invalid-shaped `services:`: returns `[base]` unchanged —
|
|
49
|
+
* byte-exact with today's one-node-per-file behaviour.
|
|
50
|
+
*/
|
|
51
|
+
function expandDeclaredServices(base, relPath, warnings) {
|
|
52
|
+
const raw = base.extra?.["services"];
|
|
53
|
+
if (raw === undefined)
|
|
54
|
+
return [base];
|
|
55
|
+
if (!Array.isArray(raw)) {
|
|
56
|
+
warnings.push(`repo-config: ${relPath}: services: must be a list — block dropped, default single-service node kept`);
|
|
57
|
+
return [base];
|
|
58
|
+
}
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
const entries = [];
|
|
61
|
+
for (let i = 0; i < raw.length; i++) {
|
|
62
|
+
const entry = raw[i];
|
|
63
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
|
|
64
|
+
warnings.push(`repo-config: ${relPath}: services[${i}] is not an object — entry dropped`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const o = entry;
|
|
68
|
+
const id = typeof o["id"] === "string" ? o["id"].trim() : "";
|
|
69
|
+
if (id === "") {
|
|
70
|
+
warnings.push(`repo-config: ${relPath}: services[${i}] missing required "id" — entry dropped`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (seen.has(id)) {
|
|
74
|
+
warnings.push(`repo-config: ${relPath}: services: declares "${id}" more than once — first occurrence kept`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
seen.add(id);
|
|
78
|
+
entries.push({
|
|
79
|
+
id,
|
|
80
|
+
...(typeof o["domain"] === "string" ? { domain: o["domain"] } : {}),
|
|
81
|
+
...(typeof o["service_type"] === "string" ? { serviceType: o["service_type"] } : {}),
|
|
82
|
+
...(typeof o["description"] === "string" ? { description: o["description"] } : {}),
|
|
83
|
+
primary: o["primary"] === true,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Present-but-empty (or all-entries-invalid) is authoritative and distinct
|
|
87
|
+
// from absent, per M-ADR 0197 §4.1 — zero declared services, not the default.
|
|
88
|
+
if (entries.length === 0)
|
|
89
|
+
return [];
|
|
90
|
+
const ownIdEntry = entries.find((e) => e.id === base.id);
|
|
91
|
+
let primaryId = null;
|
|
92
|
+
if (ownIdEntry !== undefined) {
|
|
93
|
+
primaryId = ownIdEntry.id;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const explicitPrimaries = entries.filter((e) => e.primary);
|
|
97
|
+
if (explicitPrimaries.length === 1) {
|
|
98
|
+
primaryId = explicitPrimaries[0].id;
|
|
99
|
+
}
|
|
100
|
+
else if (explicitPrimaries.length > 1) {
|
|
101
|
+
warnings.push(`repo-config: ${relPath}: services: more than one entry marked primary: true — attribution refused, declare exactly one`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
warnings.push(`repo-config: ${relPath}: services: no entry matches the repository's own id ("${base.id}") and none marked primary: true — dependencies/contracts/aggregates cannot be attributed to any declared service`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return entries.map((e) => ({
|
|
108
|
+
id: e.id,
|
|
109
|
+
domain: e.domain ?? base.domain,
|
|
110
|
+
...(e.serviceType ?? base.serviceType ? { serviceType: e.serviceType ?? base.serviceType } : {}),
|
|
111
|
+
...(e.description ?? base.description ? { description: e.description ?? base.description } : {}),
|
|
112
|
+
aggregates: e.id === primaryId ? base.aggregates : [],
|
|
113
|
+
internalDependencies: e.id === primaryId ? base.internalDependencies : [],
|
|
114
|
+
contracts: e.id === primaryId ? base.contracts : [],
|
|
115
|
+
sourcePath: base.sourcePath,
|
|
116
|
+
// M-ADR 0197 §4.7's fourth failure mode (vacuous): a non-primary
|
|
117
|
+
// declared entry has no per-entry authoring surface yet, so its empty
|
|
118
|
+
// fields above are not evidence it has none — flagged here so a
|
|
119
|
+
// consumer (find_dependencies) can say so rather than presenting an
|
|
120
|
+
// honest gap as a complete answer.
|
|
121
|
+
...(e.id !== primaryId ? { extra: { declaredNonPrimary: true } } : {}),
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse a single repo-config YAML file. `warnings` is appended to as a
|
|
126
|
+
* side channel — callers interested in parse quality should pass in a
|
|
127
|
+
* fresh array.
|
|
128
|
+
*/
|
|
129
|
+
export function parseRepoConfigFile(reader, relPath, warnings) {
|
|
130
|
+
const raw = reader.readFile(relPath);
|
|
131
|
+
if (raw === null)
|
|
132
|
+
return null;
|
|
133
|
+
let doc;
|
|
134
|
+
try {
|
|
135
|
+
doc = parseYaml(raw);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
warnings.push(`repo-config: ${relPath} failed to parse as YAML (${err.message})`);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
142
|
+
warnings.push(`repo-config: ${relPath} is not a YAML mapping`);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const record = doc;
|
|
146
|
+
// Derive identity from file path: orchestration/repo-config/{domain}/{service}.yaml
|
|
147
|
+
const pathParts = relPath.split("/");
|
|
148
|
+
const fileName = pathParts[pathParts.length - 1] ?? "";
|
|
149
|
+
const derivedDomain = pathParts[pathParts.length - 2];
|
|
150
|
+
const derivedId = fileName.replace(/\.ya?ml$/, "");
|
|
151
|
+
const id = stringField(record, "id") ?? stringField(record, "repo") ?? derivedId;
|
|
152
|
+
const domain = stringField(record, "domain") ?? derivedDomain ?? "";
|
|
153
|
+
if (!domain) {
|
|
154
|
+
warnings.push(`repo-config: ${relPath} missing domain (no 'domain' field and no parent directory)`);
|
|
155
|
+
}
|
|
156
|
+
const serviceType = stringField(record, "service_type");
|
|
157
|
+
const description = stringField(record, "description");
|
|
158
|
+
const aggregates = stringArray(record["aggregates"]);
|
|
159
|
+
const deps = record["dependencies"];
|
|
160
|
+
const internalDependencies = deps && typeof deps === "object" && !Array.isArray(deps)
|
|
161
|
+
? stringArray(deps["internal"])
|
|
162
|
+
: [];
|
|
163
|
+
const contractsRecord = record["contracts"] &&
|
|
164
|
+
typeof record["contracts"] === "object" &&
|
|
165
|
+
!Array.isArray(record["contracts"])
|
|
166
|
+
? record["contracts"]
|
|
167
|
+
: {};
|
|
168
|
+
const contracts = [];
|
|
169
|
+
for (const kind of CONTRACT_KINDS) {
|
|
170
|
+
const entries = contractsRecord[kind];
|
|
171
|
+
if (!Array.isArray(entries))
|
|
172
|
+
continue;
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
const ref = parseContractEntry(entry, kind, relPath, warnings);
|
|
175
|
+
if (ref)
|
|
176
|
+
contracts.push(ref);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const reservedKeys = new Set([
|
|
180
|
+
"id",
|
|
181
|
+
"repo",
|
|
182
|
+
"domain",
|
|
183
|
+
"service_type",
|
|
184
|
+
"description",
|
|
185
|
+
"aggregates",
|
|
186
|
+
"dependencies",
|
|
187
|
+
"contracts",
|
|
188
|
+
]);
|
|
189
|
+
const extra = {};
|
|
190
|
+
for (const [k, v] of Object.entries(record)) {
|
|
191
|
+
if (!reservedKeys.has(k))
|
|
192
|
+
extra[k] = v;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
id,
|
|
196
|
+
domain,
|
|
197
|
+
...(serviceType !== undefined ? { serviceType } : {}),
|
|
198
|
+
...(description !== undefined ? { description } : {}),
|
|
199
|
+
aggregates,
|
|
200
|
+
internalDependencies,
|
|
201
|
+
contracts,
|
|
202
|
+
sourcePath: join(reader.sddRoot, relPath),
|
|
203
|
+
...(Object.keys(extra).length > 0 ? { extra } : {}),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function parseContractEntry(entry, kind, sourceRel, warnings) {
|
|
207
|
+
if (typeof entry === "string") {
|
|
208
|
+
return { path: entry, kind };
|
|
209
|
+
}
|
|
210
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
211
|
+
warnings.push(`repo-config: ${sourceRel} contains a ${kind} contract entry that is neither string nor mapping`);
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
const obj = entry;
|
|
215
|
+
const path = stringField(obj, "path");
|
|
216
|
+
if (!path) {
|
|
217
|
+
warnings.push(`repo-config: ${sourceRel} has a ${kind} contract entry with no 'path'`);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const pkg = stringField(obj, "package");
|
|
221
|
+
const version = stringField(obj, "version");
|
|
222
|
+
const stability = parseStability(stringField(obj, "stability"));
|
|
223
|
+
return {
|
|
224
|
+
path,
|
|
225
|
+
kind,
|
|
226
|
+
...(pkg !== undefined ? { package: pkg } : {}),
|
|
227
|
+
...(version !== undefined ? { version } : {}),
|
|
228
|
+
...(stability !== undefined ? { stability } : {}),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function parseStability(v) {
|
|
232
|
+
if (v === "provisional" || v === "locked")
|
|
233
|
+
return v;
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
function stringField(record, key) {
|
|
237
|
+
const v = record[key];
|
|
238
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
239
|
+
}
|
|
240
|
+
function stringArray(v) {
|
|
241
|
+
if (!Array.isArray(v))
|
|
242
|
+
return [];
|
|
243
|
+
return v.filter((x) => typeof x === "string");
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=repo-config.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDD content access abstraction: callers read an SDD through an `SddReader`
|
|
3
|
+
* rooted at the repo, so the reading code does not care where the bytes live.
|
|
4
|
+
*
|
|
5
|
+
* Live reads: wrap node:fs.
|
|
6
|
+
*
|
|
7
|
+
* Relative paths are rooted at the SDD repo root; absolute paths are
|
|
8
|
+
* rejected so that callers cannot accidentally escape the SDD.
|
|
9
|
+
*/
|
|
10
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
11
|
+
import { isAbsolute, join } from "node:path";
|
|
12
|
+
export function liveReader(sddRoot, pin = "HEAD") {
|
|
13
|
+
const root = sddRoot;
|
|
14
|
+
const check = (relPath) => {
|
|
15
|
+
if (isAbsolute(relPath)) {
|
|
16
|
+
throw new Error(`SddReader paths must be relative to the SDD root; got ${relPath}`);
|
|
17
|
+
}
|
|
18
|
+
return join(root, relPath);
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
sddRoot: root,
|
|
22
|
+
pin,
|
|
23
|
+
readFile(relPath) {
|
|
24
|
+
const full = check(relPath);
|
|
25
|
+
try {
|
|
26
|
+
return readFileSync(full, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
listDir(relPath) {
|
|
33
|
+
const full = check(relPath);
|
|
34
|
+
try {
|
|
35
|
+
return readdirSync(full);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
isFile(relPath) {
|
|
42
|
+
const full = check(relPath);
|
|
43
|
+
try {
|
|
44
|
+
return statSync(full).isFile();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
isDir(relPath) {
|
|
51
|
+
const full = check(relPath);
|
|
52
|
+
try {
|
|
53
|
+
return statSync(full).isDirectory();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Recursive directory walk. Returns relative paths (from SDD root) of
|
|
63
|
+
* every regular file under `relRoot` that matches the optional filter.
|
|
64
|
+
* Depth-first, lexicographically sorted at each level for deterministic
|
|
65
|
+
* index contents.
|
|
66
|
+
*/
|
|
67
|
+
export function walkFiles(reader, relRoot, filter) {
|
|
68
|
+
const out = [];
|
|
69
|
+
if (!reader.isDir(relRoot))
|
|
70
|
+
return out;
|
|
71
|
+
const stack = [relRoot];
|
|
72
|
+
while (stack.length > 0) {
|
|
73
|
+
const dir = stack.pop();
|
|
74
|
+
if (dir === undefined)
|
|
75
|
+
break;
|
|
76
|
+
const entries = reader.listDir(dir).sort();
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const child = dir === "" ? entry : `${dir}/${entry}`;
|
|
79
|
+
if (reader.isDir(child)) {
|
|
80
|
+
stack.push(child);
|
|
81
|
+
}
|
|
82
|
+
else if (reader.isFile(child)) {
|
|
83
|
+
if (!filter || filter(child))
|
|
84
|
+
out.push(child);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
out.sort();
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=sdd-reader.js.map
|