@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,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application-archetype vocabulary resolution (M-ADR 0191 §4.2).
|
|
3
|
+
*
|
|
4
|
+
* The `application_archetype` manifest trait is a *classification* over a
|
|
5
|
+
* tiered vocabulary. This module resolves a declared value against the union
|
|
6
|
+
* of:
|
|
7
|
+
* - the **method tier** — `unclassified` only, built into sdd-cli (mirrors
|
|
8
|
+
* how the ADR 0173/0176 method-tier scaffold archetypes are built in);
|
|
9
|
+
* - the **reference-architecture / adopter bundle tiers** — seedless
|
|
10
|
+
* vocabulary files (`application-archetypes.yaml`) shipped in consumed
|
|
11
|
+
* bundles and extracted to the local bundle cache
|
|
12
|
+
* (`.sdd/bundle-cache/<bundle>@<version>/`);
|
|
13
|
+
* - an optional **adopter-local** extension file at
|
|
14
|
+
* `.sdd/application-archetypes.yaml` in the SDD repo.
|
|
15
|
+
*
|
|
16
|
+
* This deliberately does NOT reuse the ADR 0176 scaffold registry resolver
|
|
17
|
+
* (`scaffold/registry.ts`): that resolver is seed-oriented and drops any
|
|
18
|
+
* entry with no `seeds[]`, whereas a classification archetype is seedless by
|
|
19
|
+
* design. It DOES share the same bundle-cache distribution substrate — this
|
|
20
|
+
* is a distinct data type over a shared substrate, not a second distribution
|
|
21
|
+
* mechanism (M-ADR 0191 §6).
|
|
22
|
+
*/
|
|
23
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { parse as parseYaml } from "yaml";
|
|
26
|
+
import { BUNDLE_CACHE_REL, VOCAB_FILE } from "../../sync/bundle-cache.js";
|
|
27
|
+
/** The sentinel that lives in the method tier — the back-compat default. */
|
|
28
|
+
export const UNCLASSIFIED = "unclassified";
|
|
29
|
+
export { VOCAB_FILE };
|
|
30
|
+
/** Adopter-local extension file, relative to the SDD repo root. */
|
|
31
|
+
export const ADOPTER_VOCAB_REL = join(".sdd", VOCAB_FILE);
|
|
32
|
+
const VALID_PRESENCE = new Set([
|
|
33
|
+
"required",
|
|
34
|
+
"typical",
|
|
35
|
+
"optional",
|
|
36
|
+
"absent-by-shape",
|
|
37
|
+
]);
|
|
38
|
+
/** The built-in method tier (M-ADR 0191 §4.2 — `unclassified` only). */
|
|
39
|
+
const METHOD_TIER = [
|
|
40
|
+
{
|
|
41
|
+
id: UNCLASSIFIED,
|
|
42
|
+
tier: "method",
|
|
43
|
+
shape: "the back-compat default; a product that has not declared a shape",
|
|
44
|
+
source: "method",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
function parseVocabFile(raw, source) {
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = parseYaml(raw);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return []; // malformed vocab — skip rather than break the build
|
|
54
|
+
}
|
|
55
|
+
if (parsed === null || typeof parsed !== "object")
|
|
56
|
+
return [];
|
|
57
|
+
if (!Array.isArray(parsed.archetypes))
|
|
58
|
+
return [];
|
|
59
|
+
const tier = typeof parsed.tier === "string" ? parsed.tier : "unknown";
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const a of parsed.archetypes) {
|
|
62
|
+
if (a === null || typeof a !== "object")
|
|
63
|
+
continue;
|
|
64
|
+
const rec = a;
|
|
65
|
+
const id = typeof rec.id === "string" ? rec.id : undefined;
|
|
66
|
+
if (!id)
|
|
67
|
+
continue;
|
|
68
|
+
const expectedBindings = parseExpectedBindings(rec.expected_bindings);
|
|
69
|
+
const conformanceExpectations = parseStringList(rec.conformance);
|
|
70
|
+
out.push({
|
|
71
|
+
id,
|
|
72
|
+
tier,
|
|
73
|
+
source,
|
|
74
|
+
...(typeof rec.layer === "string" && { layer: rec.layer }),
|
|
75
|
+
...(typeof rec.shape === "string" && { shape: rec.shape }),
|
|
76
|
+
...(expectedBindings.length > 0 && { expectedBindings }),
|
|
77
|
+
...(conformanceExpectations.length > 0 && { conformanceExpectations }),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** Parse an archetype's `expected_bindings:` list, dropping malformed entries
|
|
83
|
+
* (an unknown presence or a missing pattern is skipped, not fatal). */
|
|
84
|
+
function parseExpectedBindings(raw) {
|
|
85
|
+
if (!Array.isArray(raw))
|
|
86
|
+
return [];
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const e of raw) {
|
|
89
|
+
if (e === null || typeof e !== "object")
|
|
90
|
+
continue;
|
|
91
|
+
const rec = e;
|
|
92
|
+
const pattern = typeof rec.pattern === "string" ? rec.pattern : undefined;
|
|
93
|
+
const presence = typeof rec.presence === "string" ? rec.presence : undefined;
|
|
94
|
+
if (!pattern || !presence || !VALID_PRESENCE.has(presence))
|
|
95
|
+
continue;
|
|
96
|
+
out.push({ pattern, presence: presence });
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/** Parse a `conformance:` list of opaque string tags, dropping non-strings. */
|
|
101
|
+
function parseStringList(raw) {
|
|
102
|
+
if (!Array.isArray(raw))
|
|
103
|
+
return [];
|
|
104
|
+
return raw.filter((v) => typeof v === "string" && v.trim() !== "");
|
|
105
|
+
}
|
|
106
|
+
/** readdir(withFileTypes) that yields [] on any error, sorted by name. */
|
|
107
|
+
async function readDirSafe(dir) {
|
|
108
|
+
try {
|
|
109
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
110
|
+
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Find every application-archetypes.yaml within a cached bundle (root + nested). */
|
|
117
|
+
async function findVocabFiles(dir, depth = 0) {
|
|
118
|
+
const found = [];
|
|
119
|
+
for (const e of await readDirSafe(dir)) {
|
|
120
|
+
if (e.isFile() && e.name === VOCAB_FILE) {
|
|
121
|
+
found.push(join(dir, e.name));
|
|
122
|
+
}
|
|
123
|
+
else if (e.isDirectory() && depth < 4) {
|
|
124
|
+
found.push(...(await findVocabFiles(join(dir, e.name), depth + 1)));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return found;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Discover the active vocabulary for an SDD repo: method tier ∪ every bundle
|
|
131
|
+
* vocab file in the bundle cache ∪ the adopter-local extension file.
|
|
132
|
+
*/
|
|
133
|
+
export async function discoverVocabulary(targetRepo) {
|
|
134
|
+
const terms = [...METHOD_TIER];
|
|
135
|
+
// Bundle tiers (RA + adopter bundles both land in the cache).
|
|
136
|
+
const cacheRoot = join(targetRepo, BUNDLE_CACHE_REL);
|
|
137
|
+
for (const e of await readDirSafe(cacheRoot)) {
|
|
138
|
+
if (!e.isDirectory())
|
|
139
|
+
continue;
|
|
140
|
+
const bundleCacheDir = join(cacheRoot, e.name);
|
|
141
|
+
const at = e.name.lastIndexOf("@");
|
|
142
|
+
const bundleName = at > 0 ? e.name.slice(0, at) : e.name;
|
|
143
|
+
for (const vocabPath of await findVocabFiles(bundleCacheDir)) {
|
|
144
|
+
let raw;
|
|
145
|
+
try {
|
|
146
|
+
raw = await readFile(vocabPath, "utf8");
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
terms.push(...parseVocabFile(raw, bundleName));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Adopter-local extension.
|
|
155
|
+
try {
|
|
156
|
+
const raw = await readFile(join(targetRepo, ADOPTER_VOCAB_REL), "utf8");
|
|
157
|
+
terms.push(...parseVocabFile(raw, "adopter-local"));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// absent — fine
|
|
161
|
+
}
|
|
162
|
+
return { terms, ids: new Set(terms.map((t) => t.id)) };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resolve a declared archetype value against a vocabulary. `undefined`
|
|
166
|
+
* defaults to `unclassified` (always resolved). A declared value that is not
|
|
167
|
+
* in the vocabulary is unresolved (the caller emits an error finding).
|
|
168
|
+
*/
|
|
169
|
+
export function resolveArchetype(declared, vocab) {
|
|
170
|
+
const value = declared ?? UNCLASSIFIED;
|
|
171
|
+
return {
|
|
172
|
+
value,
|
|
173
|
+
resolved: vocab.ids.has(value),
|
|
174
|
+
unclassified: value === UNCLASSIFIED,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=vocabulary.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Default archetype map — android (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Android View + Material toolkit. These names are industry-standard (Google/AndroidX/Material)
|
|
4
|
+
# and identical across every Android app — never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: android` frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: android
|
|
10
|
+
|
|
11
|
+
# Material/AndroidX vendor prefixes, stripped before token lookup.
|
|
12
|
+
strip_prefixes: [Material, Appcompat, AppCompat, Androidx]
|
|
13
|
+
|
|
14
|
+
by_token:
|
|
15
|
+
ConstraintLayout: stack
|
|
16
|
+
FrameLayout: stack
|
|
17
|
+
LinearLayout: stack
|
|
18
|
+
RelativeLayout: stack
|
|
19
|
+
CoordinatorLayout: scaffold
|
|
20
|
+
NestedScrollView: scroll
|
|
21
|
+
ScrollView: scroll
|
|
22
|
+
RecyclerView: list
|
|
23
|
+
ListView: list
|
|
24
|
+
GridLayout: grid
|
|
25
|
+
GridView: grid
|
|
26
|
+
ViewPager: paged-panes
|
|
27
|
+
ViewPager2: paged-panes
|
|
28
|
+
TabLayout: tab-strip
|
|
29
|
+
BottomNavigationView: tab-bar
|
|
30
|
+
NavigationView: sidebar
|
|
31
|
+
NavigationRailView: sidebar
|
|
32
|
+
Toolbar: app-bar
|
|
33
|
+
AppBarLayout: app-bar
|
|
34
|
+
ActionBar: app-bar
|
|
35
|
+
CollapsingToolbarLayout: app-bar
|
|
36
|
+
Button: button
|
|
37
|
+
MaterialButton: button
|
|
38
|
+
ImageButton: button
|
|
39
|
+
FloatingActionButton: fab
|
|
40
|
+
ExtendedFloatingActionButton: fab
|
|
41
|
+
Chip: tab
|
|
42
|
+
ChipGroup: button-group
|
|
43
|
+
ToggleButton: segmented-control
|
|
44
|
+
TextView: text
|
|
45
|
+
EditText: text
|
|
46
|
+
MaterialTextView: text
|
|
47
|
+
ImageView: image
|
|
48
|
+
ImageFilterView: image
|
|
49
|
+
CardView: card
|
|
50
|
+
MaterialCardView: card
|
|
51
|
+
Snackbar: banner
|
|
52
|
+
BottomSheetDialog: sheet
|
|
53
|
+
AlertDialog: modal
|
|
54
|
+
DialogFragment: modal
|
|
55
|
+
|
|
56
|
+
by_suffix_heuristic:
|
|
57
|
+
Layout: stack
|
|
58
|
+
View: panel
|
|
59
|
+
Button: button
|
|
60
|
+
Bar: app-bar
|
|
61
|
+
Dialog: modal
|
|
62
|
+
Card: card
|
|
63
|
+
Text: text
|
|
64
|
+
Image: image
|
|
65
|
+
|
|
66
|
+
default: panel
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Default archetype map — swiftui (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Apple SwiftUI toolkit. Industry-standard Apple names, identical across every SwiftUI app —
|
|
4
|
+
# never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: swiftui` frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: swiftui
|
|
10
|
+
|
|
11
|
+
strip_prefixes: []
|
|
12
|
+
|
|
13
|
+
by_token:
|
|
14
|
+
VStack: stack
|
|
15
|
+
HStack: row
|
|
16
|
+
ZStack: stack
|
|
17
|
+
LazyVStack: stack
|
|
18
|
+
LazyHStack: row
|
|
19
|
+
Grid: grid
|
|
20
|
+
LazyVGrid: grid
|
|
21
|
+
LazyHGrid: grid
|
|
22
|
+
ScrollView: scroll
|
|
23
|
+
List: list
|
|
24
|
+
Section: panel
|
|
25
|
+
ForEach: list-item # repeated child marker; rendered as a list-item row
|
|
26
|
+
Form: list
|
|
27
|
+
NavigationView: nav-bar
|
|
28
|
+
NavigationStack: nav-bar
|
|
29
|
+
NavigationSplitView: sidebar
|
|
30
|
+
NavigationLink: button
|
|
31
|
+
TabView: tab-bar
|
|
32
|
+
Button: button
|
|
33
|
+
Menu: list
|
|
34
|
+
Toolbar: app-bar
|
|
35
|
+
ToolbarItem: button
|
|
36
|
+
Text: text
|
|
37
|
+
Label: text
|
|
38
|
+
TextField: text
|
|
39
|
+
SecureField: text
|
|
40
|
+
Image: image
|
|
41
|
+
AsyncImage: image
|
|
42
|
+
Spacer: text # decorative; flagged by screen-styling-primitive-as-structure
|
|
43
|
+
Divider: text # decorative; flagged by screen-styling-primitive-as-structure
|
|
44
|
+
GroupBox: card
|
|
45
|
+
Sheet: sheet
|
|
46
|
+
Alert: modal
|
|
47
|
+
Picker: segmented-control
|
|
48
|
+
|
|
49
|
+
by_suffix_heuristic:
|
|
50
|
+
Stack: stack
|
|
51
|
+
View: panel
|
|
52
|
+
Button: button
|
|
53
|
+
Link: button
|
|
54
|
+
Field: text
|
|
55
|
+
Image: image
|
|
56
|
+
Text: text
|
|
57
|
+
|
|
58
|
+
default: panel
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Default archetype map — uikit (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Apple UIKit toolkit. Industry-standard Apple names (UI- prefixed), identical across every
|
|
4
|
+
# UIKit app — never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: uikit` frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: uikit
|
|
10
|
+
|
|
11
|
+
# The `UI` vendor prefix is stripped, so the `by_token` keys are unprefixed.
|
|
12
|
+
strip_prefixes: [UI, NS]
|
|
13
|
+
|
|
14
|
+
by_token:
|
|
15
|
+
View: panel
|
|
16
|
+
StackView: stack
|
|
17
|
+
ScrollView: scroll
|
|
18
|
+
TableView: list
|
|
19
|
+
TableViewCell: list-item
|
|
20
|
+
CollectionView: grid
|
|
21
|
+
CollectionViewCell: card
|
|
22
|
+
NavigationBar: nav-bar
|
|
23
|
+
NavigationController: nav-bar
|
|
24
|
+
TabBar: tab-bar
|
|
25
|
+
TabBarController: tab-bar
|
|
26
|
+
Toolbar: app-bar
|
|
27
|
+
BarButtonItem: button
|
|
28
|
+
Button: button
|
|
29
|
+
Label: text
|
|
30
|
+
TextField: text
|
|
31
|
+
TextView: text
|
|
32
|
+
ImageView: image
|
|
33
|
+
PageViewController: paged-panes
|
|
34
|
+
SegmentedControl: segmented-control
|
|
35
|
+
ActivityIndicatorView: text
|
|
36
|
+
AlertController: modal
|
|
37
|
+
Stack: stack
|
|
38
|
+
|
|
39
|
+
by_suffix_heuristic:
|
|
40
|
+
View: panel
|
|
41
|
+
Controller: panel
|
|
42
|
+
Button: button
|
|
43
|
+
Bar: app-bar
|
|
44
|
+
Label: text
|
|
45
|
+
Field: text
|
|
46
|
+
Image: image
|
|
47
|
+
|
|
48
|
+
default: panel
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Default archetype map — web-generic (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Keys off component NAMES (which the builder already parses), not design-system source.
|
|
4
|
+
# Vendor-standard / common-DS tokens ONLY — never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: web-generic` (or web) frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: web-generic
|
|
10
|
+
|
|
11
|
+
# Stripped (case-insensitive) before token lookup, so `MuiButton`/`ChButton` -> `Button`.
|
|
12
|
+
strip_prefixes: [Mui, Ch, Ds, Ui, App, Base, Core, V]
|
|
13
|
+
|
|
14
|
+
by_token:
|
|
15
|
+
Container: grid
|
|
16
|
+
Grid: grid
|
|
17
|
+
Row: row
|
|
18
|
+
Col: col
|
|
19
|
+
Column: col
|
|
20
|
+
Stack: stack
|
|
21
|
+
Flex: row
|
|
22
|
+
Scroll: scroll
|
|
23
|
+
ScrollArea: scroll
|
|
24
|
+
Tabs: tab-strip
|
|
25
|
+
Tab: tab
|
|
26
|
+
TabPanel: pane
|
|
27
|
+
Table: table
|
|
28
|
+
DataGrid: table
|
|
29
|
+
List: list
|
|
30
|
+
ListItem: list-item
|
|
31
|
+
Menu: list
|
|
32
|
+
Card: card
|
|
33
|
+
Panel: panel
|
|
34
|
+
Dialog: modal
|
|
35
|
+
Modal: modal
|
|
36
|
+
Popup: modal
|
|
37
|
+
Popover: modal
|
|
38
|
+
Drawer: sidebar
|
|
39
|
+
SidePanel: sidebar
|
|
40
|
+
Sidebar: sidebar
|
|
41
|
+
Nav: nav-bar
|
|
42
|
+
Navbar: nav-bar
|
|
43
|
+
AppBar: app-bar
|
|
44
|
+
Header: app-bar
|
|
45
|
+
Toolbar: app-bar
|
|
46
|
+
Alert: banner
|
|
47
|
+
Banner: banner
|
|
48
|
+
Toast: banner
|
|
49
|
+
Snackbar: banner
|
|
50
|
+
Sheet: sheet
|
|
51
|
+
BottomSheet: sheet
|
|
52
|
+
Btn: button
|
|
53
|
+
Button: button
|
|
54
|
+
IconButton: button
|
|
55
|
+
Fab: fab
|
|
56
|
+
ButtonGroup: button-group
|
|
57
|
+
SegmentedControl: segmented-control
|
|
58
|
+
Stat: text
|
|
59
|
+
StatCard: card
|
|
60
|
+
KpiBar: kpi-bar
|
|
61
|
+
Text: text
|
|
62
|
+
Typography: text
|
|
63
|
+
Label: text
|
|
64
|
+
Heading: text
|
|
65
|
+
Image: image
|
|
66
|
+
Img: image
|
|
67
|
+
Avatar: image
|
|
68
|
+
Icon: image
|
|
69
|
+
|
|
70
|
+
# Applied when no token matches. First matching suffix wins.
|
|
71
|
+
by_suffix_heuristic:
|
|
72
|
+
Button: button
|
|
73
|
+
Bar: app-bar
|
|
74
|
+
List: list
|
|
75
|
+
Grid: grid
|
|
76
|
+
Card: card
|
|
77
|
+
Panel: panel
|
|
78
|
+
Dialog: modal
|
|
79
|
+
Modal: modal
|
|
80
|
+
Layout: stack
|
|
81
|
+
Container: grid
|
|
82
|
+
Text: text
|
|
83
|
+
Image: image
|
|
84
|
+
Icon: image
|
|
85
|
+
|
|
86
|
+
default: panel
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# OpenUI Archetype Vocabulary — method-owned (ADR 0169 §4.1)
|
|
2
|
+
#
|
|
3
|
+
# A closed, platform-tagged superset of visual archetypes. An archetype assigns a
|
|
4
|
+
# component a VISUAL SHAPE + slots/orientation — never geometry or styling (stays inside
|
|
5
|
+
# the compose-only boundary, ADR 0166 §3). Default archetype maps
|
|
6
|
+
# (archetype-maps/*.yaml) resolve adopter component NAMES to entries here; the
|
|
7
|
+
# render-screen-wireframe skill lowers a resolved tree to SVG using `render`.
|
|
8
|
+
#
|
|
9
|
+
# `is_container` feeds ADR 0168 §4.1 isContainer when a resolved map is present.
|
|
10
|
+
|
|
11
|
+
vocabulary_version: "1.0.0"
|
|
12
|
+
|
|
13
|
+
archetypes:
|
|
14
|
+
|
|
15
|
+
# --- structural containers -------------------------------------------------
|
|
16
|
+
scaffold: { is_container: true, render: { slots: [header, main, footer], orientation: vertical } }
|
|
17
|
+
app-bar: { is_container: true, render: { shape: bar, position: top } }
|
|
18
|
+
nav-bar: { is_container: true, render: { shape: bar, position: top } }
|
|
19
|
+
tab-bar: { is_container: true, render: { shape: bar, position: bottom } }
|
|
20
|
+
sidebar: { is_container: true, render: { shape: rail, position: left } }
|
|
21
|
+
stack: { is_container: true, render: { orientation: vertical } }
|
|
22
|
+
row: { is_container: true, render: { orientation: horizontal } }
|
|
23
|
+
col: { is_container: true, render: { orientation: vertical } }
|
|
24
|
+
grid: { is_container: true, render: { shape: grid } }
|
|
25
|
+
scroll: { is_container: true, render: { orientation: vertical, scroll: true } }
|
|
26
|
+
list: { is_container: true, render: { orientation: vertical, repeat: true } }
|
|
27
|
+
table: { is_container: true, render: { shape: grid, header_row: true } }
|
|
28
|
+
card: { is_container: true, render: { shape: card } }
|
|
29
|
+
panel: { is_container: true, render: { shape: panel } } # opaque fallback
|
|
30
|
+
paged-panes: { is_container: true, render: { shape: paged, active_first: true } }
|
|
31
|
+
pane: { is_container: true, render: { shape: panel } }
|
|
32
|
+
tab-strip: { is_container: true, render: { orientation: horizontal, shape: chips } }
|
|
33
|
+
button-group: { is_container: true, render: { orientation: horizontal } }
|
|
34
|
+
segmented-control: { is_container: true, render: { orientation: horizontal, shape: chips } }
|
|
35
|
+
kpi-bar: { is_container: true, render: { orientation: horizontal, shape: chips } }
|
|
36
|
+
modal: { is_container: true, render: { shape: floating, overlay: true } }
|
|
37
|
+
sheet: { is_container: true, render: { shape: floating, position: bottom } }
|
|
38
|
+
banner: { is_container: true, render: { shape: bar } }
|
|
39
|
+
|
|
40
|
+
# --- leaves ----------------------------------------------------------------
|
|
41
|
+
tab: { is_container: false, render: { shape: chip } }
|
|
42
|
+
list-item: { is_container: false, render: { shape: row } }
|
|
43
|
+
button: { is_container: false, render: { shape: button } }
|
|
44
|
+
fab: { is_container: false, render: { shape: circle, position: bottom-right } }
|
|
45
|
+
text: { is_container: false, render: { shape: text } }
|
|
46
|
+
image: { is_container: false, render: { shape: image } }
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// OpenUI archetype resolver (per ADR 0169).
|
|
2
|
+
//
|
|
3
|
+
// Binds an adopter component NAME to a visual ARCHETYPE via a method-shipped
|
|
4
|
+
// platform default map plus an optional adopter overlay. The binding is what
|
|
5
|
+
// makes a shape-blind openui-lang block renderable as a wireframe, and it
|
|
6
|
+
// supplies the container/leaf classification the ADR 0168 `screen-root-is-leaf`
|
|
7
|
+
// check consumes (isContainerArchetype).
|
|
8
|
+
//
|
|
9
|
+
// Resolution order (ADR 0169 §4.3): adopter override -> default by_token (after
|
|
10
|
+
// stripping a vendor prefix) -> by_suffix_heuristic -> default (panel).
|
|
11
|
+
//
|
|
12
|
+
// The vocabulary + the four default maps are method-owned reference data,
|
|
13
|
+
// VENDORED from sdd-method (docs/method/methods/guides/openui-archetype-*.yaml /
|
|
14
|
+
// archetype-maps/) — the same vendoring relationship as canonical-schema.yaml.
|
|
15
|
+
// They use vendor-standard names only; never an adopter's bespoke vocabulary.
|
|
16
|
+
// copy-yaml-assets.mjs copies them next to the compiled JS at build time.
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
import { parse as parseYaml } from "yaml";
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const PLATFORMS = new Set(["web-generic", "android", "swiftui", "uikit"]);
|
|
23
|
+
const FALLBACK_ARCHETYPE = "panel";
|
|
24
|
+
// Loaded once, cached. Reference data is immutable at runtime.
|
|
25
|
+
let vocabularyCache;
|
|
26
|
+
const mapCache = new Map();
|
|
27
|
+
function loadVocabulary() {
|
|
28
|
+
if (vocabularyCache === undefined) {
|
|
29
|
+
const raw = readFileSync(join(__dirname, "openui-archetype-vocabulary.yaml"), "utf-8");
|
|
30
|
+
const parsed = parseYaml(raw);
|
|
31
|
+
vocabularyCache = { archetypes: parsed?.archetypes ?? {} };
|
|
32
|
+
}
|
|
33
|
+
return vocabularyCache;
|
|
34
|
+
}
|
|
35
|
+
function loadMap(platform) {
|
|
36
|
+
let m = mapCache.get(platform);
|
|
37
|
+
if (m === undefined) {
|
|
38
|
+
const raw = readFileSync(join(__dirname, "maps", `${platform}.yaml`), "utf-8");
|
|
39
|
+
m = parseYaml(raw) ?? {};
|
|
40
|
+
mapCache.set(platform, m);
|
|
41
|
+
}
|
|
42
|
+
return m;
|
|
43
|
+
}
|
|
44
|
+
/** True when `platform` has a method-shipped default map. */
|
|
45
|
+
export function isKnownPlatform(platform) {
|
|
46
|
+
return platform !== undefined && PLATFORMS.has(platform);
|
|
47
|
+
}
|
|
48
|
+
function stripPrefix(name, prefixes) {
|
|
49
|
+
for (const p of prefixes ?? []) {
|
|
50
|
+
if (name.length > p.length && name.startsWith(p))
|
|
51
|
+
return name.slice(p.length);
|
|
52
|
+
}
|
|
53
|
+
return name;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a component name to a visual archetype for a platform, honouring an
|
|
57
|
+
* optional adopter overlay. Always resolves (panel fallback) — never throws.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveArchetype(name, platform, overlay) {
|
|
60
|
+
const override = overlay?.overrides?.[name];
|
|
61
|
+
if (override !== undefined)
|
|
62
|
+
return { archetype: override, provenance: "override" };
|
|
63
|
+
const map = loadMap(platform);
|
|
64
|
+
const token = stripPrefix(name, map.strip_prefixes);
|
|
65
|
+
const byToken = map.by_token?.[token] ?? map.by_token?.[name];
|
|
66
|
+
if (byToken !== undefined)
|
|
67
|
+
return { archetype: byToken, provenance: "default" };
|
|
68
|
+
// First suffix the name ends with wins.
|
|
69
|
+
for (const [suffix, archetype] of Object.entries(map.by_suffix_heuristic ?? {})) {
|
|
70
|
+
if (name.endsWith(suffix))
|
|
71
|
+
return { archetype, provenance: "heuristic" };
|
|
72
|
+
}
|
|
73
|
+
return { archetype: map.default ?? FALLBACK_ARCHETYPE, provenance: "fallback" };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Whether a component classifies as a container archetype for a platform.
|
|
77
|
+
* Used by ADR 0168 §4.1 isContainer to refine the appears-with-children
|
|
78
|
+
* heuristic. Returns undefined when the platform has no map (caller falls back
|
|
79
|
+
* to the heuristic) so an absent map never mis-classifies a known container.
|
|
80
|
+
*/
|
|
81
|
+
export function isContainerArchetype(name, platform, overlay) {
|
|
82
|
+
if (!isKnownPlatform(platform))
|
|
83
|
+
return undefined;
|
|
84
|
+
const { archetype } = resolveArchetype(name, platform, overlay);
|
|
85
|
+
return loadVocabulary().archetypes[archetype]?.is_container === true;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The vendor name prefixes the platform's default map strips (e.g. `UI`/`NS` for
|
|
89
|
+
* uikit, `Mui`/`Ch`/… for web-generic). The renderer combines these with the
|
|
90
|
+
* adopter overlay's `strip_prefixes` to humanise component names into labels.
|
|
91
|
+
* Returns `[]` for an unknown platform.
|
|
92
|
+
*/
|
|
93
|
+
export function mapStripPrefixes(platform) {
|
|
94
|
+
if (!isKnownPlatform(platform))
|
|
95
|
+
return [];
|
|
96
|
+
return loadMap(platform).strip_prefixes ?? [];
|
|
97
|
+
}
|
|
98
|
+
/** Vocabulary metadata for an archetype: container flag + render hints. */
|
|
99
|
+
export function archetypeInfo(archetype) {
|
|
100
|
+
const entry = loadVocabulary().archetypes[archetype];
|
|
101
|
+
return { isContainer: entry?.is_container === true, render: entry?.render ?? {} };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=resolver.js.map
|