@wairon/cli 5.1.1-dev.37 → 5.1.1-dev.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +536 -103
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +313 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -436,7 +436,7 @@ function activeTargetTypes(config) {
|
|
|
436
436
|
const targets = config.targets;
|
|
437
437
|
return targets.filter((t) => typeof t === "string" || t.enabled !== false).map((t) => typeof t === "string" ? t : t.type);
|
|
438
438
|
}
|
|
439
|
-
var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
|
|
439
|
+
var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, ConformanceRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
|
|
440
440
|
var init_project = __esm({
|
|
441
441
|
"src/models/project.ts"() {
|
|
442
442
|
"use strict";
|
|
@@ -533,6 +533,30 @@ var init_project = __esm({
|
|
|
533
533
|
*/
|
|
534
534
|
maxCognitiveLevel: import_zod3.z.string().optional()
|
|
535
535
|
});
|
|
536
|
+
ConformanceRuleConfigSchema = import_zod3.z.object({
|
|
537
|
+
/**
|
|
538
|
+
* Project-relative paths holding this project's own source code; a
|
|
539
|
+
* directory is walked recursively, a file names itself. Absolute or
|
|
540
|
+
* parent-escaping entries are refused by containment, exactly as a
|
|
541
|
+
* sourcePath is.
|
|
542
|
+
*/
|
|
543
|
+
sourceRoots: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
544
|
+
/**
|
|
545
|
+
* Paths inside a source root that are not this project's code to claim —
|
|
546
|
+
* vendored libraries, generated output, a chained subproject's own
|
|
547
|
+
* directory (the child claims its files in its own run). A file at, or
|
|
548
|
+
* under, one of these is never walked, so it is neither reported nor
|
|
549
|
+
* carried as debt.
|
|
550
|
+
*/
|
|
551
|
+
exclude: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
552
|
+
/**
|
|
553
|
+
* The files under the source roots that no spec names yet, frozen. A listed
|
|
554
|
+
* file is not reported; an unlisted one is UNCLAIMED_SOURCE_FILE; an entry
|
|
555
|
+
* that is now claimed, proven a barrel, or no longer found is
|
|
556
|
+
* STALE_UNCLAIMED_ENTRY.
|
|
557
|
+
*/
|
|
558
|
+
unclaimed: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
559
|
+
});
|
|
536
560
|
DesignDepthSchema = import_zod3.z.enum(["components", "interfaces", "implementations", "narratives"]);
|
|
537
561
|
RulesConfigSchema = import_zod3.z.object({
|
|
538
562
|
/**
|
|
@@ -583,6 +607,8 @@ var init_project = __esm({
|
|
|
583
607
|
documentation: DocumentationRuleConfigSchema.optional(),
|
|
584
608
|
/** Dynamic structural complexity caps (method limit, step limit, dependency limit) */
|
|
585
609
|
complexity: ComplexityRuleConfigSchema.optional(),
|
|
610
|
+
/** Where this project's own source lives and what of it no spec claims yet (see ConformanceRuleConfigSchema) */
|
|
611
|
+
conformance: ConformanceRuleConfigSchema.optional(),
|
|
586
612
|
/** Project-default design depth (see DesignDepthSchema); subsystems may override. */
|
|
587
613
|
designDepth: DesignDepthSchema.optional()
|
|
588
614
|
});
|
|
@@ -1113,7 +1139,7 @@ var init_defaults = __esm({
|
|
|
1113
1139
|
copilot: ".github/prompts",
|
|
1114
1140
|
codex: ".codex/agents"
|
|
1115
1141
|
};
|
|
1116
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
1142
|
+
WAIRON_VERSION = "5.1.1-dev.39";
|
|
1117
1143
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
1118
1144
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
1119
1145
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -1515,6 +1541,15 @@ function implementationSourceFiles(impl) {
|
|
|
1515
1541
|
for (const method of impl.methods ?? []) add(method.sourcePath);
|
|
1516
1542
|
return files;
|
|
1517
1543
|
}
|
|
1544
|
+
function typeSourceFiles(type) {
|
|
1545
|
+
const files = [];
|
|
1546
|
+
const add = (file) => {
|
|
1547
|
+
if (file && !files.includes(file)) files.push(file);
|
|
1548
|
+
};
|
|
1549
|
+
add(type.sourcePath);
|
|
1550
|
+
for (const method of type.methods ?? []) add(method.sourcePath);
|
|
1551
|
+
return files;
|
|
1552
|
+
}
|
|
1518
1553
|
var import_zod8, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, InvariantSchema, TypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
|
|
1519
1554
|
var init_specs = __esm({
|
|
1520
1555
|
"src/models/specs.ts"() {
|
|
@@ -1823,7 +1858,13 @@ var init_specs = __esm({
|
|
|
1823
1858
|
GuaranteeSchema = import_zod8.z.string().min(1);
|
|
1824
1859
|
MethodParamSchema = import_zod8.z.object({
|
|
1825
1860
|
name: import_zod8.z.string(),
|
|
1826
|
-
/**
|
|
1861
|
+
/**
|
|
1862
|
+
* A primitive/builtin or a defined type id (qualified across subsystems, e.g.
|
|
1863
|
+
* "billing.Invoice"), on its own or inside a generic, an array or a UNION:
|
|
1864
|
+
* `Invoice | null`, `Promise<Invoice | null>`, `Invoice[] | null`. Every
|
|
1865
|
+
* identifier the string names has to resolve — see the grammar on
|
|
1866
|
+
* src/models/type-references.ts.
|
|
1867
|
+
*/
|
|
1827
1868
|
type: import_zod8.z.string(),
|
|
1828
1869
|
description: import_zod8.z.string().optional(),
|
|
1829
1870
|
optional: import_zod8.z.boolean().optional()
|
|
@@ -1841,8 +1882,9 @@ var init_specs = __esm({
|
|
|
1841
1882
|
description: import_zod8.z.string(),
|
|
1842
1883
|
signature: import_zod8.z.string(),
|
|
1843
1884
|
// e.g. "save(key: string, data: Buffer): Promise<void>"
|
|
1885
|
+
// e.g. "Promise<void>", or a union: "Invoice | null" — the commonest shape in
|
|
1886
|
+
// any real tree. See the grammar on src/models/type-references.ts.
|
|
1844
1887
|
returns: import_zod8.z.string(),
|
|
1845
|
-
// e.g. "Promise<void>"
|
|
1846
1888
|
/** Structured parameters (authoritative for type checking when present). */
|
|
1847
1889
|
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
1848
1890
|
/** Concrete wire binding for this method when its component is a Portal (set via sdd_set_endpoints). */
|
|
@@ -2111,8 +2153,10 @@ var init_specs = __esm({
|
|
|
2111
2153
|
TypeKindSchema = import_zod8.z.enum(["entity", "value-object"]);
|
|
2112
2154
|
TypeFieldSchema = import_zod8.z.object({
|
|
2113
2155
|
name: import_zod8.z.string(),
|
|
2156
|
+
// A primitive, or another type id (qualified across subsystems, e.g.
|
|
2157
|
+
// "billing.Invoice") — on its own or inside a generic, an array or a union
|
|
2158
|
+
// ("Invoice[] | null"). See the grammar on src/models/type-references.ts.
|
|
2114
2159
|
type: import_zod8.z.string(),
|
|
2115
|
-
// a primitive, or another type id (qualified across subsystems, e.g. "billing.Invoice")
|
|
2116
2160
|
description: import_zod8.z.string().optional(),
|
|
2117
2161
|
optional: import_zod8.z.boolean().default(false),
|
|
2118
2162
|
/**
|
|
@@ -2132,7 +2176,22 @@ var init_specs = __esm({
|
|
|
2132
2176
|
name: import_zod8.z.string(),
|
|
2133
2177
|
signature: import_zod8.z.string(),
|
|
2134
2178
|
returns: import_zod8.z.string(),
|
|
2135
|
-
description: import_zod8.z.string().optional()
|
|
2179
|
+
description: import_zod8.z.string().optional(),
|
|
2180
|
+
/**
|
|
2181
|
+
* Source file realizing this method when it is not the type's own
|
|
2182
|
+
* sourcePath. A type's pure methods routinely live apart from its
|
|
2183
|
+
* declaration: the declaration is a struct or an interface, the methods are
|
|
2184
|
+
* free functions, and a language without methods-on-data has nowhere else
|
|
2185
|
+
* to put them.
|
|
2186
|
+
*/
|
|
2187
|
+
sourcePath: import_zod8.z.string().optional(),
|
|
2188
|
+
/**
|
|
2189
|
+
* The code-level name realizing this method, when it legitimately differs
|
|
2190
|
+
* from the method name — e.g. `narrative_step.foreignFields` realized by
|
|
2191
|
+
* `narrativeStepForeignFields`, the free-function form a pure type method
|
|
2192
|
+
* takes in a language whose data carries no methods.
|
|
2193
|
+
*/
|
|
2194
|
+
symbol: import_zod8.z.string().optional()
|
|
2136
2195
|
});
|
|
2137
2196
|
InvariantSchema = import_zod8.z.object({
|
|
2138
2197
|
/** Stable invariant id, unique within the entity (referenced as "<type-id>.<invariant-id>"). */
|
|
@@ -2177,6 +2236,23 @@ var init_specs = __esm({
|
|
|
2177
2236
|
* logical system entity type it maps to.
|
|
2178
2237
|
*/
|
|
2179
2238
|
linkedEntity: import_zod8.z.string().optional(),
|
|
2239
|
+
/**
|
|
2240
|
+
* The source file holding this type's declaration, and the default for
|
|
2241
|
+
* every method that names no sourcePath of its own. Relative to the root of
|
|
2242
|
+
* the project that holds the spec.
|
|
2243
|
+
*
|
|
2244
|
+
* Naming one turns the type into a CLAIM on code, judged exactly as an
|
|
2245
|
+
* implementation's sourcePath is: the file must resolve, and the
|
|
2246
|
+
* declaration must be anchored in it (UNREALIZED_TYPE). A type that names
|
|
2247
|
+
* none claims nothing and is never reported.
|
|
2248
|
+
*/
|
|
2249
|
+
sourcePath: import_zod8.z.string().optional(),
|
|
2250
|
+
/**
|
|
2251
|
+
* The code-level name realizing this type's declaration, when it
|
|
2252
|
+
* legitimately differs from `name` — a type named "Invoice Line" declared
|
|
2253
|
+
* as `InvoiceLine`.
|
|
2254
|
+
*/
|
|
2255
|
+
symbol: import_zod8.z.string().optional(),
|
|
2180
2256
|
/** Per-spec lint suppressions (see LintConfigSchema). */
|
|
2181
2257
|
lint: LintConfigSchema.optional(),
|
|
2182
2258
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
@@ -3626,11 +3702,11 @@ function buildCanvasModel(issues = []) {
|
|
|
3626
3702
|
if (pi.component && pi.id) apiTagOf.set(pi.component, pi.id);
|
|
3627
3703
|
}
|
|
3628
3704
|
const patternTypes = /* @__PURE__ */ new Set(["Repository", "FeatureComponent", "RouterComponent"]);
|
|
3629
|
-
const
|
|
3705
|
+
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
3630
3706
|
for (const comp of components) {
|
|
3631
3707
|
if (!patternTypes.has(comp.componentType)) continue;
|
|
3632
3708
|
for (const memberId of comp.owns) {
|
|
3633
|
-
if (componentIds.has(memberId))
|
|
3709
|
+
if (componentIds.has(memberId)) ownerOf2.set(memberId, comp.id);
|
|
3634
3710
|
}
|
|
3635
3711
|
}
|
|
3636
3712
|
const modelComponents = components.map((comp) => {
|
|
@@ -3686,7 +3762,7 @@ function buildCanvasModel(issues = []) {
|
|
|
3686
3762
|
...comp.status ? { status: comp.status } : {},
|
|
3687
3763
|
public: publicComponents.has(comp.id),
|
|
3688
3764
|
...apiTagOf.has(comp.id) ? { apiTag: apiTagOf.get(comp.id) } : {},
|
|
3689
|
-
...
|
|
3765
|
+
...ownerOf2.has(comp.id) ? { owner: ownerOf2.get(comp.id) } : {},
|
|
3690
3766
|
owns: comp.owns.filter((o) => componentIds.has(o)),
|
|
3691
3767
|
dependsOn: comp.dependsOn,
|
|
3692
3768
|
...comp.externalLinks && comp.externalLinks.length ? { externalLinks: comp.externalLinks } : {},
|
|
@@ -13279,9 +13355,9 @@ var init_dependency_conformance = __esm({
|
|
|
13279
13355
|
};
|
|
13280
13356
|
const mappedImplsOf = (compId) => realization.implementationsOf(compId).filter((impl) => implementationSourceFiles(impl).some((f) => isExact(pathKey(f))));
|
|
13281
13357
|
const dependsOrOwns = (from, toId) => from.dependsOn.includes(toId) || (from.owns ?? []).includes(toId);
|
|
13282
|
-
const
|
|
13358
|
+
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
13283
13359
|
for (const c of ctx.components) {
|
|
13284
|
-
for (const member of c.owns ?? [])
|
|
13360
|
+
for (const member of c.owns ?? []) ownerOf2.set(member, c);
|
|
13285
13361
|
}
|
|
13286
13362
|
const declaresSurfaceEdge = (from, subsystemId) => {
|
|
13287
13363
|
const published = ctx.publicSet.get(subsystemId);
|
|
@@ -13294,9 +13370,9 @@ var init_dependency_conformance = __esm({
|
|
|
13294
13370
|
if (cf.id === cg.id) return true;
|
|
13295
13371
|
if (dependsOrOwns(cf, cg.id)) return true;
|
|
13296
13372
|
if (cf.subsystem === cg.subsystem && dependsOrOwns(cg, cf.id) && (cg.componentType === "Portal" || cg.componentType === "Observer")) return true;
|
|
13297
|
-
const ownerG =
|
|
13373
|
+
const ownerG = ownerOf2.get(cg.id);
|
|
13298
13374
|
if (ownerG && (dependsOrOwns(cf, ownerG.id) || cf.id === ownerG.id)) return true;
|
|
13299
|
-
const ownerF =
|
|
13375
|
+
const ownerF = ownerOf2.get(cf.id);
|
|
13300
13376
|
if (ownerF && (dependsOrOwns(ownerF, cg.id) || ownerF.id === cg.id)) return true;
|
|
13301
13377
|
if (ownerF && ownerG && ownerF.id === ownerG.id) return true;
|
|
13302
13378
|
if (cf.subsystem !== cg.subsystem && declaresSurfaceEdge(cf, cg.subsystem)) return true;
|
|
@@ -13550,6 +13626,150 @@ var init_integration_sim_coverage = __esm({
|
|
|
13550
13626
|
}
|
|
13551
13627
|
});
|
|
13552
13628
|
|
|
13629
|
+
// src/core/rules/conformance/type-realization.ts
|
|
13630
|
+
function ownerOf(type, file) {
|
|
13631
|
+
if (type.sourcePath && pathKey(type.sourcePath) === pathKey(file)) return `Type "${type.id}"`;
|
|
13632
|
+
const users = (type.methods ?? []).filter((m) => m.sourcePath && pathKey(m.sourcePath) === pathKey(file)).map((m) => m.name);
|
|
13633
|
+
if (users.length === 0) return `Type "${type.id}"`;
|
|
13634
|
+
return `Type "${type.id}" method${users.length === 1 ? "" : "s"} ${users.map((n) => `"${n}"`).join(", ")}`;
|
|
13635
|
+
}
|
|
13636
|
+
var FILE_PROBLEMS, typeRealizationRule;
|
|
13637
|
+
var init_type_realization = __esm({
|
|
13638
|
+
"src/core/rules/conformance/type-realization.ts"() {
|
|
13639
|
+
"use strict";
|
|
13640
|
+
init_models();
|
|
13641
|
+
FILE_PROBLEMS = {
|
|
13642
|
+
escaped: {
|
|
13643
|
+
code: "SOURCE_PATH_ESCAPES_ROOT",
|
|
13644
|
+
severity: "error",
|
|
13645
|
+
detail: "is absolute or escapes the project root \u2014 sourcePaths must stay inside the project."
|
|
13646
|
+
},
|
|
13647
|
+
missing: {
|
|
13648
|
+
code: "MISSING_SOURCE_FILE",
|
|
13649
|
+
severity: "error",
|
|
13650
|
+
detail: "does not resolve to a file \u2014 the spec names code that does not exist."
|
|
13651
|
+
},
|
|
13652
|
+
unreadable: {
|
|
13653
|
+
code: "CONFORMANCE_ANALYSIS_SKIPPED",
|
|
13654
|
+
severity: "warning",
|
|
13655
|
+
detail: "could not be analyzed (binary or unreadable) \u2014 what it would have realized was not checked."
|
|
13656
|
+
}
|
|
13657
|
+
};
|
|
13658
|
+
typeRealizationRule = {
|
|
13659
|
+
name: "type-realization",
|
|
13660
|
+
description: "Code\u2194spec Level 1 for the data model: a type that names a sourcePath is claiming code, so every file it names \u2014 its own and each method's \u2014 must resolve to a real, readable file inside the project root, its file must PUBLISH its declaration (an exported name at exact grade, the declaration tier below it, under its `symbol` when the code-level name differs from the type's name \u2014 and a pure re-export barrel publishes nothing of its own, so a claim on one is never realized), and each of its pure methods must appear in its own file (the method's sourcePath, else the type's) at the declaration tier, under the method's `symbol`, else the method's name. A type that names no sourcePath claims nothing and is never reported. Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible, a file that escapes the root, is missing or could not be analyzed is reported once and blocks only what it would have realized, and types under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
13661
|
+
codes: [
|
|
13662
|
+
{ code: "UNREALIZED_TYPE", defaultSeverity: "warning", summary: "A type names a sourcePath but its declaration is nowhere in that file \u2014 the claim points at code that does not hold it" },
|
|
13663
|
+
{ code: "UNREALIZED_TYPE_METHOD", defaultSeverity: "warning", summary: "A pure method of a claimed type is nowhere in its own source file (the method's sourcePath, else the type's)" },
|
|
13664
|
+
{ code: "MISSING_SOURCE_FILE", defaultSeverity: "error", summary: "A source file a type or one of its methods names does not resolve to a file on disk" },
|
|
13665
|
+
{ code: "SOURCE_PATH_ESCAPES_ROOT", defaultSeverity: "error", summary: "A source file a type or one of its methods names is absolute or escapes the project root (containment refusal)" },
|
|
13666
|
+
{ code: "CONFORMANCE_ANALYSIS_SKIPPED", defaultSeverity: "warning", summary: "A source file a type or one of its methods names could not be analyzed (binary/unreadable) \u2014 what it would have realized was not checked" }
|
|
13667
|
+
],
|
|
13668
|
+
check(ctx) {
|
|
13669
|
+
const code = ctx.codeIndex();
|
|
13670
|
+
for (const type of ctx.types) {
|
|
13671
|
+
if (!type.sourcePath) continue;
|
|
13672
|
+
if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
|
|
13673
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
13674
|
+
for (const file of typeSourceFiles(type)) {
|
|
13675
|
+
const key = pathKey(file);
|
|
13676
|
+
const facts = code.factsAt(key);
|
|
13677
|
+
if (!facts || facts.status === "analyzed") continue;
|
|
13678
|
+
blocked.add(key);
|
|
13679
|
+
const problem = FILE_PROBLEMS[facts.status];
|
|
13680
|
+
if (!problem) continue;
|
|
13681
|
+
ctx.addIssue(problem.severity, problem.code, `${ownerOf(type, file)} sourcePath "${file}" ${problem.detail}`, type.id);
|
|
13682
|
+
}
|
|
13683
|
+
const typeKey = pathKey(type.sourcePath);
|
|
13684
|
+
const typeFacts = code.factsAt(typeKey);
|
|
13685
|
+
if (typeFacts && !blocked.has(typeKey)) {
|
|
13686
|
+
const symbol = type.symbol ?? type.name;
|
|
13687
|
+
const published = typeFacts.analysisGrade === "exact" ? new Set(typeFacts.reexportOnly ? [] : typeFacts.exportedNames) : code.declarationsAt(typeKey);
|
|
13688
|
+
if (!published.has(symbol)) {
|
|
13689
|
+
const label = type.symbol ? `"${type.name}" (symbol "${symbol}")` : `"${type.name}"`;
|
|
13690
|
+
ctx.addIssue(
|
|
13691
|
+
"warning",
|
|
13692
|
+
"UNREALIZED_TYPE",
|
|
13693
|
+
`Type ${label} is not published by "${type.sourcePath}" (analysis grade: ${typeFacts.analysisGrade})${typeFacts.reexportOnly ? ", which is a pure re-export barrel and declares nothing of its own" : ""} \u2014 name the file the declaration lives in, or bind the code-level name with \`symbol\`.`,
|
|
13694
|
+
type.id
|
|
13695
|
+
);
|
|
13696
|
+
}
|
|
13697
|
+
}
|
|
13698
|
+
for (const method of type.methods ?? []) {
|
|
13699
|
+
const file = method.sourcePath ?? type.sourcePath;
|
|
13700
|
+
const key = pathKey(file);
|
|
13701
|
+
if (blocked.has(key)) continue;
|
|
13702
|
+
const facts = code.factsAt(key);
|
|
13703
|
+
if (!facts || facts.status !== "analyzed") continue;
|
|
13704
|
+
const symbol = method.symbol ?? method.name;
|
|
13705
|
+
if (code.declarationsAt(key).has(symbol)) continue;
|
|
13706
|
+
const label = method.symbol ? `"${method.name}" (symbol "${symbol}")` : `"${method.name}"`;
|
|
13707
|
+
ctx.addIssue(
|
|
13708
|
+
"warning",
|
|
13709
|
+
"UNREALIZED_TYPE_METHOD",
|
|
13710
|
+
`Method ${label} of type "${type.id}" is not declared in "${file}" (analysis grade: ${facts.analysisGrade}) \u2014 a pure type method is often a free function named after its type; bind that name with \`symbol\`, or name the file it lives in with the method's own \`sourcePath\`.`,
|
|
13711
|
+
type.id
|
|
13712
|
+
);
|
|
13713
|
+
}
|
|
13714
|
+
}
|
|
13715
|
+
}
|
|
13716
|
+
};
|
|
13717
|
+
}
|
|
13718
|
+
});
|
|
13719
|
+
|
|
13720
|
+
// src/core/rules/conformance/unclaimed-source.ts
|
|
13721
|
+
var unclaimedSourceRule;
|
|
13722
|
+
var init_unclaimed_source = __esm({
|
|
13723
|
+
"src/core/rules/conformance/unclaimed-source.ts"() {
|
|
13724
|
+
"use strict";
|
|
13725
|
+
init_models();
|
|
13726
|
+
unclaimedSourceRule = {
|
|
13727
|
+
name: "unclaimed-source",
|
|
13728
|
+
description: "Code\u2194spec Level 1, asked the other way round: every source file under the project's declared source roots must be named by some spec \u2014 an implementation's sourcePath, a method's, a simPath, or a type's \u2014 or be carried in the frozen `rules.conformance.unclaimed` list. A pure re-export barrel declares nothing of its own and only republishes other modules, so it has no code to claim and is exempt (exact grade only: a weaker grade cannot tell a barrel from a file it failed to parse). Declaring no source roots leaves the check silent, which is what makes it opt-in; the unclaimed list is a one-way debt register, so an entry the walk no longer finds unclaimed must be deleted and the list can only shrink.",
|
|
13729
|
+
codes: [
|
|
13730
|
+
{ code: "UNCLAIMED_SOURCE_FILE", defaultSeverity: "warning", summary: "A source file under a declared source root that no spec names and the frozen unclaimed list does not carry \u2014 code nobody designed" },
|
|
13731
|
+
{ code: "STALE_UNCLAIMED_ENTRY", defaultSeverity: "warning", summary: "An entry of the frozen unclaimed list names a file that is now claimed, or that the source-root walk no longer finds \u2014 delete it, the list only shrinks" }
|
|
13732
|
+
],
|
|
13733
|
+
check(ctx) {
|
|
13734
|
+
const walked = ctx.codeModel.rootFiles;
|
|
13735
|
+
const listed = (ctx.rules?.conformance?.unclaimed ?? []).map(pathKey);
|
|
13736
|
+
if (walked.length === 0) return;
|
|
13737
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
13738
|
+
for (const impl of ctx.implementations) {
|
|
13739
|
+
for (const file of implementationSourceFiles(impl)) claimed.add(pathKey(file));
|
|
13740
|
+
if (impl.simPath) claimed.add(pathKey(impl.simPath));
|
|
13741
|
+
}
|
|
13742
|
+
for (const type of ctx.types) {
|
|
13743
|
+
for (const file of typeSourceFiles(type)) claimed.add(pathKey(file));
|
|
13744
|
+
}
|
|
13745
|
+
const code = ctx.codeIndex();
|
|
13746
|
+
const isBarrel = (file) => code.factsAt(file)?.reexportOnly === true;
|
|
13747
|
+
const isLiveDebt = (file) => !claimed.has(file) && !isBarrel(file);
|
|
13748
|
+
const listedSet = new Set(listed);
|
|
13749
|
+
for (const file of walked) {
|
|
13750
|
+
if (!isLiveDebt(file)) continue;
|
|
13751
|
+
if (listedSet.has(file)) continue;
|
|
13752
|
+
ctx.addIssue(
|
|
13753
|
+
"warning",
|
|
13754
|
+
"UNCLAIMED_SOURCE_FILE",
|
|
13755
|
+
`Source file "${file}" is under a declared source root but no spec names it \u2014 give it to a spec as a sourcePath (an implementation's, a method's, or a type's), or carry it in \`rules.conformance.unclaimed\` until it is designed.`
|
|
13756
|
+
);
|
|
13757
|
+
}
|
|
13758
|
+
const walkedSet = new Set(walked);
|
|
13759
|
+
for (const entry of listed) {
|
|
13760
|
+
if (walkedSet.has(entry) && isLiveDebt(entry)) continue;
|
|
13761
|
+
const why = !walkedSet.has(entry) ? "the source-root walk no longer finds it (deleted, renamed, excluded, or outside the declared roots)" : isBarrel(entry) ? "it is a pure re-export barrel, which has nothing to claim" : "a spec now names it";
|
|
13762
|
+
ctx.addIssue(
|
|
13763
|
+
"warning",
|
|
13764
|
+
"STALE_UNCLAIMED_ENTRY",
|
|
13765
|
+
`"${entry}" is carried in \`rules.conformance.unclaimed\` but ${why} \u2014 delete the entry; the list only shrinks.`
|
|
13766
|
+
);
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
};
|
|
13770
|
+
}
|
|
13771
|
+
});
|
|
13772
|
+
|
|
13553
13773
|
// src/core/rules/heuristic/coupling-health.ts
|
|
13554
13774
|
var DEFAULT_GOD_COMPONENT_THRESHOLD, couplingRule;
|
|
13555
13775
|
var init_coupling_health = __esm({
|
|
@@ -13893,14 +14113,14 @@ var init_technology_boundaries = __esm({
|
|
|
13893
14113
|
{ code: "VENDOR_NAME_IN_CONTRACT", defaultSeverity: "warning", summary: "Technology name in L3 contract identifiers" }
|
|
13894
14114
|
],
|
|
13895
14115
|
check(ctx) {
|
|
13896
|
-
const
|
|
13897
|
-
for (const c of ctx.components) for (const m of c.owns)
|
|
14116
|
+
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
14117
|
+
for (const c of ctx.components) for (const m of c.owns) ownerOf2.set(m, c.id);
|
|
13898
14118
|
const ownershipRoot = (id) => {
|
|
13899
14119
|
const seen = /* @__PURE__ */ new Set();
|
|
13900
14120
|
let cur = id;
|
|
13901
|
-
while (
|
|
14121
|
+
while (ownerOf2.has(cur) && !seen.has(cur)) {
|
|
13902
14122
|
seen.add(cur);
|
|
13903
|
-
cur =
|
|
14123
|
+
cur = ownerOf2.get(cur);
|
|
13904
14124
|
}
|
|
13905
14125
|
return cur;
|
|
13906
14126
|
};
|
|
@@ -14793,6 +15013,8 @@ var init_repository = __esm({
|
|
|
14793
15013
|
init_integration_sim_file();
|
|
14794
15014
|
init_integration_sim_wiring();
|
|
14795
15015
|
init_integration_sim_coverage();
|
|
15016
|
+
init_type_realization();
|
|
15017
|
+
init_unclaimed_source();
|
|
14796
15018
|
init_coupling_health();
|
|
14797
15019
|
init_signature_language_builtins();
|
|
14798
15020
|
init_narrative_language_constructs();
|
|
@@ -14957,6 +15179,11 @@ var init_repository = __esm({
|
|
|
14957
15179
|
integrationSimFileRule,
|
|
14958
15180
|
integrationSimWiringRule,
|
|
14959
15181
|
integrationSimCoverageRule,
|
|
15182
|
+
// The data model's own claim on code, and the question no spec can ask
|
|
15183
|
+
// from the spec side: which files are named by nothing at all. Last in
|
|
15184
|
+
// the family because the second one reads what all the others named.
|
|
15185
|
+
typeRealizationRule,
|
|
15186
|
+
unclaimedSourceRule,
|
|
14960
15187
|
couplingRule,
|
|
14961
15188
|
// Target-language fit in two questions: what a CONTRACT may name, and what
|
|
14962
15189
|
// a NARRATIVE may describe.
|
|
@@ -14989,7 +15216,7 @@ var init_repository = __esm({
|
|
|
14989
15216
|
|
|
14990
15217
|
// src/core/source-analysis.ts
|
|
14991
15218
|
function emptyCodeModel() {
|
|
14992
|
-
return { files: [], projectRoot: "" };
|
|
15219
|
+
return { files: [], projectRoot: "", rootFiles: [] };
|
|
14993
15220
|
}
|
|
14994
15221
|
function stripComments(text, patterns) {
|
|
14995
15222
|
let out = text;
|
|
@@ -15229,7 +15456,8 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15229
15456
|
ts.forEachChild(node, visit);
|
|
15230
15457
|
};
|
|
15231
15458
|
visit(sf);
|
|
15232
|
-
|
|
15459
|
+
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
15460
|
+
return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings, reexportOnly };
|
|
15233
15461
|
}
|
|
15234
15462
|
function resolveRelativeModule(fromFile, specifier) {
|
|
15235
15463
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -15287,15 +15515,65 @@ function looksBinary(buffer) {
|
|
|
15287
15515
|
const probe = buffer.subarray(0, Math.min(buffer.length, 4096));
|
|
15288
15516
|
return probe.includes(0);
|
|
15289
15517
|
}
|
|
15290
|
-
function
|
|
15518
|
+
function walkSourceRoots(sourceRoots, exclude, projectRoot) {
|
|
15519
|
+
const found = [];
|
|
15520
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15521
|
+
const excluded = exclude.map(pathKey);
|
|
15522
|
+
const isExcluded = (key) => excluded.some((e) => key === e || key.startsWith(`${e}/`));
|
|
15523
|
+
const record = (absolute) => {
|
|
15524
|
+
const key = pathKey(path11.relative(projectRoot, absolute));
|
|
15525
|
+
if (seen.has(key) || isExcluded(key)) return;
|
|
15526
|
+
if (!EXTENSION_LANGUAGE[path11.extname(key).toLowerCase()]) return;
|
|
15527
|
+
seen.add(key);
|
|
15528
|
+
found.push(key);
|
|
15529
|
+
};
|
|
15530
|
+
const descend = (dir) => {
|
|
15531
|
+
let entries;
|
|
15532
|
+
try {
|
|
15533
|
+
entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
15534
|
+
} catch {
|
|
15535
|
+
return;
|
|
15536
|
+
}
|
|
15537
|
+
for (const entry of entries.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
15538
|
+
const child = path11.join(dir, entry.name);
|
|
15539
|
+
if (entry.isDirectory()) {
|
|
15540
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
15541
|
+
if (isExcluded(pathKey(path11.relative(projectRoot, child)))) continue;
|
|
15542
|
+
descend(child);
|
|
15543
|
+
} else if (entry.isFile()) {
|
|
15544
|
+
record(child);
|
|
15545
|
+
}
|
|
15546
|
+
}
|
|
15547
|
+
};
|
|
15548
|
+
for (const root of sourceRoots) {
|
|
15549
|
+
const key = pathKey(root);
|
|
15550
|
+
if (path11.isAbsolute(key) || path11.normalize(key).split(path11.sep)[0] === "..") continue;
|
|
15551
|
+
const absolute = path11.resolve(projectRoot, key);
|
|
15552
|
+
if (path11.relative(projectRoot, absolute).startsWith("..")) continue;
|
|
15553
|
+
if (isExcluded(key)) continue;
|
|
15554
|
+
let stat;
|
|
15555
|
+
try {
|
|
15556
|
+
stat = fs8.statSync(absolute);
|
|
15557
|
+
} catch {
|
|
15558
|
+
continue;
|
|
15559
|
+
}
|
|
15560
|
+
if (stat.isDirectory()) descend(absolute);
|
|
15561
|
+
else if (stat.isFile()) record(absolute);
|
|
15562
|
+
}
|
|
15563
|
+
return found;
|
|
15564
|
+
}
|
|
15565
|
+
function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], exclude = []) {
|
|
15291
15566
|
const files = [];
|
|
15292
15567
|
const seen = /* @__PURE__ */ new Set();
|
|
15293
15568
|
const exactCache = /* @__PURE__ */ new Map();
|
|
15569
|
+
const rootFiles = walkSourceRoots(sourceRoots, exclude, projectRoot);
|
|
15294
15570
|
const declaredPaths = [];
|
|
15295
15571
|
for (const impl of implementations) {
|
|
15296
15572
|
declaredPaths.push(...implementationSourceFiles(impl));
|
|
15297
15573
|
if (impl.simPath) declaredPaths.push(impl.simPath);
|
|
15298
15574
|
}
|
|
15575
|
+
for (const type of types) declaredPaths.push(...typeSourceFiles(type));
|
|
15576
|
+
declaredPaths.push(...rootFiles);
|
|
15299
15577
|
for (const declared of declaredPaths) {
|
|
15300
15578
|
const sourcePath = pathKey(declared);
|
|
15301
15579
|
if (seen.has(sourcePath)) continue;
|
|
@@ -15344,7 +15622,8 @@ function buildCodeModel(implementations, projectRoot) {
|
|
|
15344
15622
|
reexports: [...facts.reexports],
|
|
15345
15623
|
functionComplexity: Object.fromEntries(facts.complexity),
|
|
15346
15624
|
functionCalls: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v]])),
|
|
15347
|
-
topLevelMutableBindings: [...facts.mutableBindings]
|
|
15625
|
+
topLevelMutableBindings: [...facts.mutableBindings],
|
|
15626
|
+
reexportOnly: facts.reexportOnly
|
|
15348
15627
|
};
|
|
15349
15628
|
} catch {
|
|
15350
15629
|
analyzed = analyzeGeneric(text);
|
|
@@ -15359,7 +15638,7 @@ function buildCodeModel(implementations, projectRoot) {
|
|
|
15359
15638
|
}
|
|
15360
15639
|
files.push({ path: sourcePath, status: "analyzed", language, ...analyzed });
|
|
15361
15640
|
}
|
|
15362
|
-
return { files, projectRoot };
|
|
15641
|
+
return { files, projectRoot, rootFiles };
|
|
15363
15642
|
}
|
|
15364
15643
|
var fs8, path11, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
|
|
15365
15644
|
var init_source_analysis = __esm({
|
|
@@ -17212,7 +17491,14 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
17212
17491
|
const implementations = loadImplementationSpecs();
|
|
17213
17492
|
const types = loadTypeSpecs();
|
|
17214
17493
|
const surfaceSnapshots = loadSurfaceSnapshots();
|
|
17215
|
-
const
|
|
17494
|
+
const conformance = rules?.conformance;
|
|
17495
|
+
const codeModel = buildCodeModel(
|
|
17496
|
+
implementations,
|
|
17497
|
+
types,
|
|
17498
|
+
getProjectRoot(),
|
|
17499
|
+
conformance?.sourceRoots ?? [],
|
|
17500
|
+
conformance?.exclude ?? []
|
|
17501
|
+
);
|
|
17216
17502
|
const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
|
|
17217
17503
|
const statusSnapshot = statusBearing.map((s) => s.status);
|
|
17218
17504
|
for (const s of statusBearing) s.status = "complete";
|
|
@@ -26203,6 +26489,7 @@ __export(src_exports, {
|
|
|
26203
26489
|
ComponentSpecSchema: () => ComponentSpecSchema,
|
|
26204
26490
|
ComponentTypeSchema: () => ComponentTypeSchema,
|
|
26205
26491
|
ConfigValidationError: () => ConfigValidationError,
|
|
26492
|
+
ConformanceRuleConfigSchema: () => ConformanceRuleConfigSchema,
|
|
26206
26493
|
ConformanceTierSchema: () => ConformanceTierSchema,
|
|
26207
26494
|
CustomExporter: () => CustomExporter,
|
|
26208
26495
|
CustomTargetConfigSchema: () => CustomTargetConfigSchema,
|
|
@@ -26584,6 +26871,7 @@ __export(src_exports, {
|
|
|
26584
26871
|
toOpenApiSet: () => toOpenApiSet,
|
|
26585
26872
|
typeGenericParameters: () => typeGenericParameters,
|
|
26586
26873
|
typeMatchesRef: () => typeMatchesRef,
|
|
26874
|
+
typeSourceFiles: () => typeSourceFiles,
|
|
26587
26875
|
uninstallPack: () => uninstallPack,
|
|
26588
26876
|
updateSpec: () => updateSpec,
|
|
26589
26877
|
upsertPackSelection: () => upsertPackSelection,
|
|
@@ -26931,6 +27219,7 @@ function loadProjectConfig4() {
|
|
|
26931
27219
|
ComponentSpecSchema,
|
|
26932
27220
|
ComponentTypeSchema,
|
|
26933
27221
|
ConfigValidationError,
|
|
27222
|
+
ConformanceRuleConfigSchema,
|
|
26934
27223
|
ConformanceTierSchema,
|
|
26935
27224
|
CustomExporter,
|
|
26936
27225
|
CustomTargetConfigSchema,
|
|
@@ -27312,6 +27601,7 @@ function loadProjectConfig4() {
|
|
|
27312
27601
|
toOpenApiSet,
|
|
27313
27602
|
typeGenericParameters,
|
|
27314
27603
|
typeMatchesRef,
|
|
27604
|
+
typeSourceFiles,
|
|
27315
27605
|
uninstallPack,
|
|
27316
27606
|
updateSpec,
|
|
27317
27607
|
upsertPackSelection,
|