@wairon/cli 5.1.1-dev.64 → 5.1.1-dev.66
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 +533 -327
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +199 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5819,6 +5819,24 @@ var ImplementationSpecSchema = import_zod8.z.object({
|
|
|
5819
5819
|
* stereotypes should bind tech directly (TECH_ON_LOGIC_COMPONENT).
|
|
5820
5820
|
*/
|
|
5821
5821
|
technologies: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
5822
|
+
/**
|
|
5823
|
+
* The parameter names THIS realization takes BEFORE the ones its contract
|
|
5824
|
+
* declares — a config object, a data root, the transport handles a portal is
|
|
5825
|
+
* handed. Supplied by whatever wires the component up, never by the caller
|
|
5826
|
+
* the contract describes, which is why they belong to the realization and
|
|
5827
|
+
* not to the contract: another realization of the same contract may hold
|
|
5828
|
+
* them as fields instead.
|
|
5829
|
+
*
|
|
5830
|
+
* Declared rather than guessed, because a leading parameter the contract
|
|
5831
|
+
* does not name is otherwise indistinguishable from one it named under a
|
|
5832
|
+
* different name — `seed(config)` realized as `bootstrapInstance(cfg)` reads
|
|
5833
|
+
* as a dropped parameter to anything that infers. A leading parameter this
|
|
5834
|
+
* list does not name is a finding (UNDECLARED_PARAM), and only a LEADING run
|
|
5835
|
+
* is dropped: one of these names appearing after the contract's own
|
|
5836
|
+
* parameters is not wiring, it is an argument in the middle of the caller's
|
|
5837
|
+
* list.
|
|
5838
|
+
*/
|
|
5839
|
+
injectedParams: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
5822
5840
|
methods: import_zod8.z.array(MethodImplementationSchema).default([]).superRefine((methods, ctx) => {
|
|
5823
5841
|
methods.forEach((m, i) => {
|
|
5824
5842
|
if (!m.calls?.length || m.narrative.length === 0) return;
|
|
@@ -11549,7 +11567,7 @@ function defaultTargetConfig(type) {
|
|
|
11549
11567
|
enabled: true
|
|
11550
11568
|
};
|
|
11551
11569
|
}
|
|
11552
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
11570
|
+
var WAIRON_VERSION = "5.1.1-dev.66";
|
|
11553
11571
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
11554
11572
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
11555
11573
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -16881,6 +16899,166 @@ var typeShapeRule = {
|
|
|
16881
16899
|
}
|
|
16882
16900
|
};
|
|
16883
16901
|
|
|
16902
|
+
// src/core/rules/conformance/param-conformance.ts
|
|
16903
|
+
function omittable(optional) {
|
|
16904
|
+
return optional ? "may be left out" : "is required";
|
|
16905
|
+
}
|
|
16906
|
+
function unitOf2(parameter, position) {
|
|
16907
|
+
return parameter.name ?? `#${position + 1}`;
|
|
16908
|
+
}
|
|
16909
|
+
function judge(declared, realized, injected, typeAgrees) {
|
|
16910
|
+
let wiring = 0;
|
|
16911
|
+
while (wiring < realized.length && realized[wiring].name !== void 0 && injected.has(realized[wiring].name)) wiring++;
|
|
16912
|
+
const taken = realized.slice(wiring);
|
|
16913
|
+
const matched = Math.min(declared.length, taken.length);
|
|
16914
|
+
const found = {
|
|
16915
|
+
unrealized: /* @__PURE__ */ new Map(),
|
|
16916
|
+
undeclared: /* @__PURE__ */ new Map(),
|
|
16917
|
+
renamed: /* @__PURE__ */ new Map(),
|
|
16918
|
+
optionality: /* @__PURE__ */ new Map()
|
|
16919
|
+
};
|
|
16920
|
+
for (const param of declared.slice(matched)) {
|
|
16921
|
+
found.unrealized.set(param.name, { unit: param.name, told: `"${param.name}"` });
|
|
16922
|
+
}
|
|
16923
|
+
taken.slice(matched).forEach((param, index) => {
|
|
16924
|
+
const unit = unitOf2(param, wiring + matched + index);
|
|
16925
|
+
found.undeclared.set(unit, { unit, told: `"${unit}"` });
|
|
16926
|
+
});
|
|
16927
|
+
for (let position = 0; position < matched; position++) {
|
|
16928
|
+
const param = declared[position];
|
|
16929
|
+
const at = taken[position];
|
|
16930
|
+
if (at.name !== void 0 && at.name !== param.name && typeAgrees(param, at)) {
|
|
16931
|
+
found.renamed.set(`${param.name}\u2192${at.name}`, {
|
|
16932
|
+
unit: param.name,
|
|
16933
|
+
told: `"${param.name}" (the code calls it "${at.name}")`
|
|
16934
|
+
});
|
|
16935
|
+
}
|
|
16936
|
+
if (!!param.optional !== at.optional) {
|
|
16937
|
+
found.optionality.set(`${param.name}:${at.optional}`, {
|
|
16938
|
+
unit: param.name,
|
|
16939
|
+
told: `"${param.name}" (the contract says it ${omittable(!!param.optional)}, the code says it ${omittable(at.optional)})`
|
|
16940
|
+
});
|
|
16941
|
+
}
|
|
16942
|
+
}
|
|
16943
|
+
return found;
|
|
16944
|
+
}
|
|
16945
|
+
function agreed(judgements, reading) {
|
|
16946
|
+
const [first, ...rest] = judgements;
|
|
16947
|
+
return [...first[reading]].filter(([fact]) => rest.every((other) => other[reading].has(fact))).map(([, finding]) => finding);
|
|
16948
|
+
}
|
|
16949
|
+
var paramConformanceRule = {
|
|
16950
|
+
name: "param-conformance",
|
|
16951
|
+
description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
|
|
16952
|
+
codes: [
|
|
16953
|
+
{
|
|
16954
|
+
code: "UNREALIZED_PARAM",
|
|
16955
|
+
defaultSeverity: "warning",
|
|
16956
|
+
summary: "A contract declares a parameter the function realizing the method does not take \u2014 the contract promises an argument that would go nowhere, and every brief and caller built from it passes one",
|
|
16957
|
+
// Measured code↔spec drift, parameter by parameter, at a site the
|
|
16958
|
+
// finding names — which is why every one of them hands over `parts`.
|
|
16959
|
+
carryable: true
|
|
16960
|
+
},
|
|
16961
|
+
{
|
|
16962
|
+
code: "UNDECLARED_PARAM",
|
|
16963
|
+
defaultSeverity: "warning",
|
|
16964
|
+
summary: "The realizing function takes a parameter no contract parameter names and no declared injection accounts for \u2014 an argument a caller must supply that the design never mentions",
|
|
16965
|
+
carryable: true
|
|
16966
|
+
},
|
|
16967
|
+
{
|
|
16968
|
+
code: "PARAM_NAME_MISMATCH",
|
|
16969
|
+
defaultSeverity: "warning",
|
|
16970
|
+
summary: "A contract parameter and the one realizing it agree on position and type but not on name \u2014 the contract, the ERD and every brief say one word while the code says another",
|
|
16971
|
+
carryable: true
|
|
16972
|
+
},
|
|
16973
|
+
{
|
|
16974
|
+
code: "PARAM_OPTIONALITY",
|
|
16975
|
+
defaultSeverity: "warning",
|
|
16976
|
+
summary: "A contract and its realization disagree about whether a parameter may be left out \u2014 one of them is telling a caller an argument is required when it is not, or the reverse",
|
|
16977
|
+
carryable: true
|
|
16978
|
+
}
|
|
16979
|
+
],
|
|
16980
|
+
check(ctx) {
|
|
16981
|
+
const code = ctx.codeIndex();
|
|
16982
|
+
const codeNameOf = /* @__PURE__ */ new Map();
|
|
16983
|
+
for (const type of ctx.types) {
|
|
16984
|
+
const named = type.symbol ?? type.name;
|
|
16985
|
+
codeNameOf.set(type.id, named);
|
|
16986
|
+
if (type.subsystem) codeNameOf.set(`${type.subsystem}.${type.id}`, named);
|
|
16987
|
+
}
|
|
16988
|
+
const normalize3 = (text) => text.replace(/\s+/g, " ").trim();
|
|
16989
|
+
const typeAgrees = (declared, realized) => {
|
|
16990
|
+
if (!realized.type) return false;
|
|
16991
|
+
const stated = normalize3(declared.type);
|
|
16992
|
+
return stated === realized.type || codeNameOf.get(stated) === realized.type;
|
|
16993
|
+
};
|
|
16994
|
+
for (const { implementation, method, sourceFile, draftContext } of ctx.implementationMethods()) {
|
|
16995
|
+
const contract = ctx.interfaceMap.get(implementation.contract);
|
|
16996
|
+
const declared = contract?.methods.find((m) => m.name === method.name)?.params ?? [];
|
|
16997
|
+
if (declared.length === 0 || !sourceFile) continue;
|
|
16998
|
+
const file = pathKey(sourceFile);
|
|
16999
|
+
const facts = code.factsAt(file);
|
|
17000
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
17001
|
+
const symbol = method.symbol ?? method.name;
|
|
17002
|
+
const signatures = facts.functionParams;
|
|
17003
|
+
if (!signatures || !Object.prototype.hasOwnProperty.call(signatures, symbol)) continue;
|
|
17004
|
+
const candidates = signatures[symbol];
|
|
17005
|
+
if (candidates.length === 0) continue;
|
|
17006
|
+
const injected = new Set(implementation.injectedParams ?? []);
|
|
17007
|
+
const judgements = candidates.map((realized) => judge(declared, realized, injected, typeAgrees));
|
|
17008
|
+
const subject = candidates.length > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
|
|
17009
|
+
const opening = subject.charAt(0).toUpperCase() + subject.slice(1);
|
|
17010
|
+
const unrealized = agreed(judgements, "unrealized");
|
|
17011
|
+
if (unrealized.length > 0) {
|
|
17012
|
+
ctx.addIssue(
|
|
17013
|
+
"warning",
|
|
17014
|
+
"UNREALIZED_PARAM",
|
|
17015
|
+
`Method "${method.name}" of contract "${implementation.contract}" declares ${unrealized.length} parameter(s) ${subject} does not take \u2014 ${unrealized.map((found) => found.told).join(", ")}. The contract is promising an argument that would go nowhere, and nothing breaks at a call site to correct it: every brief and every caller built from the contract passes one. Take the parameter in the code, or drop it from the contract.`,
|
|
17016
|
+
implementation.id,
|
|
17017
|
+
draftContext,
|
|
17018
|
+
void 0,
|
|
17019
|
+
{ at: method.name, covers: unrealized.map((found) => found.unit) }
|
|
17020
|
+
);
|
|
17021
|
+
}
|
|
17022
|
+
const undeclared = agreed(judgements, "undeclared");
|
|
17023
|
+
if (undeclared.length > 0) {
|
|
17024
|
+
ctx.addIssue(
|
|
17025
|
+
"warning",
|
|
17026
|
+
"UNDECLARED_PARAM",
|
|
17027
|
+
`${opening} realizing method "${method.name}" of contract "${implementation.contract}" takes ${undeclared.length} parameter(s) the contract does not declare and no declared injection accounts for \u2014 ${undeclared.map((found) => found.told).join(", ")}. An argument a caller must supply that the design never mentions is how a credential ends up in a signature nobody has read against its contract. Declare it on the contract, name it in the implementation's \`injectedParams\` when whatever wires this component up supplies it (a LEADING run only), or take it out of the signature.`,
|
|
17028
|
+
implementation.id,
|
|
17029
|
+
draftContext,
|
|
17030
|
+
void 0,
|
|
17031
|
+
{ at: method.name, covers: undeclared.map((found) => found.unit) }
|
|
17032
|
+
);
|
|
17033
|
+
}
|
|
17034
|
+
const renamed = agreed(judgements, "renamed");
|
|
17035
|
+
if (renamed.length > 0) {
|
|
17036
|
+
ctx.addIssue(
|
|
17037
|
+
"warning",
|
|
17038
|
+
"PARAM_NAME_MISMATCH",
|
|
17039
|
+
`Method "${method.name}" of contract "${implementation.contract}" and ${subject} agree on position and type but not on name for ${renamed.length} parameter(s) \u2014 ${renamed.map((f) => f.told).join("; ")}. The contract, the ERD and every brief carry one word and the code answers to another, which is a rename nobody recorded rather than a different argument \u2014 the declared type agreeing is what says so. Rename one side to the other.`,
|
|
17040
|
+
implementation.id,
|
|
17041
|
+
draftContext,
|
|
17042
|
+
void 0,
|
|
17043
|
+
{ at: method.name, covers: renamed.map((found) => found.unit) }
|
|
17044
|
+
);
|
|
17045
|
+
}
|
|
17046
|
+
const disagreed = agreed(judgements, "optionality");
|
|
17047
|
+
if (disagreed.length > 0) {
|
|
17048
|
+
ctx.addIssue(
|
|
17049
|
+
"warning",
|
|
17050
|
+
"PARAM_OPTIONALITY",
|
|
17051
|
+
`Method "${method.name}" of contract "${implementation.contract}" and ${subject} disagree about whether ${disagreed.length} parameter(s) may be left out \u2014 ${disagreed.map((f) => f.told).join("; ")}. One of them is telling a caller an argument is required when it is not, or the reverse. Omittable is the code's word for it: a default value and a rest parameter make an argument omittable exactly as a question mark does.`,
|
|
17052
|
+
implementation.id,
|
|
17053
|
+
draftContext,
|
|
17054
|
+
void 0,
|
|
17055
|
+
{ at: method.name, covers: disagreed.map((found) => found.unit) }
|
|
17056
|
+
);
|
|
17057
|
+
}
|
|
17058
|
+
}
|
|
17059
|
+
}
|
|
17060
|
+
};
|
|
17061
|
+
|
|
16884
17062
|
// src/core/rules/conformance/unclaimed-source.ts
|
|
16885
17063
|
var unclaimedSourceRule = {
|
|
16886
17064
|
name: "unclaimed-source",
|
|
@@ -18343,11 +18521,14 @@ var SDD_RULES = [
|
|
|
18343
18521
|
integrationSimWiringRule,
|
|
18344
18522
|
integrationSimCoverageRule,
|
|
18345
18523
|
// The data model's own claim on code — does the file hold the type, and is
|
|
18346
|
-
// it the SHAPE the spec claims —
|
|
18347
|
-
// the
|
|
18348
|
-
//
|
|
18524
|
+
// it the SHAPE the spec claims — then the SIGNATURE, the third reading:
|
|
18525
|
+
// does the code take the arguments the contract promises. And then the
|
|
18526
|
+
// question no spec can ask from the spec side: which files are named by
|
|
18527
|
+
// nothing at all. Last in the family because that one reads what all the
|
|
18528
|
+
// others named.
|
|
18349
18529
|
typeRealizationRule,
|
|
18350
18530
|
typeShapeRule,
|
|
18531
|
+
paramConformanceRule,
|
|
18351
18532
|
unclaimedSourceRule,
|
|
18352
18533
|
exportConformanceRule,
|
|
18353
18534
|
couplingRule,
|
|
@@ -18665,6 +18846,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18665
18846
|
const fieldTypes = /* @__PURE__ */ new Map();
|
|
18666
18847
|
const localTypes = /* @__PURE__ */ new Map();
|
|
18667
18848
|
const typeShapes = /* @__PURE__ */ new Map();
|
|
18849
|
+
const functionParams = /* @__PURE__ */ new Map();
|
|
18668
18850
|
const mutableBindings = /* @__PURE__ */ new Set();
|
|
18669
18851
|
const schemaConstants = /* @__PURE__ */ new Map();
|
|
18670
18852
|
const derivedAliases = [];
|
|
@@ -18794,6 +18976,14 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18794
18976
|
}
|
|
18795
18977
|
return void 0;
|
|
18796
18978
|
};
|
|
18979
|
+
const declaredParameters = (fn) => (fn.parameters ?? []).map((parameter) => {
|
|
18980
|
+
const fact = {
|
|
18981
|
+
optional: !!parameter.questionToken || !!parameter.initializer || !!parameter.dotDotDotToken
|
|
18982
|
+
};
|
|
18983
|
+
if (ts.isIdentifier(parameter.name)) fact.name = parameter.name.text;
|
|
18984
|
+
if (parameter.type) fact.type = parameter.type.getText(sf).replace(/\s+/g, " ").trim();
|
|
18985
|
+
return fact;
|
|
18986
|
+
});
|
|
18797
18987
|
const collectFunctionFacts = (fn) => {
|
|
18798
18988
|
let score = 1;
|
|
18799
18989
|
const callees = /* @__PURE__ */ new Map();
|
|
@@ -18836,6 +19026,9 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18836
19026
|
const sites = calls.get(fnName) ?? /* @__PURE__ */ new Map();
|
|
18837
19027
|
for (const [key, site] of facts.callees) sites.set(key, site);
|
|
18838
19028
|
calls.set(fnName, sites);
|
|
19029
|
+
const signatures = functionParams.get(fnName) ?? [];
|
|
19030
|
+
signatures.push(declaredParameters(node));
|
|
19031
|
+
functionParams.set(fnName, signatures);
|
|
18839
19032
|
}
|
|
18840
19033
|
if (ts.isPropertyDeclaration(node)) recordFieldType(node.name, node.type);
|
|
18841
19034
|
else if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent) && isParameterProperty(node)) {
|
|
@@ -18943,7 +19136,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18943
19136
|
typeShapes.set(alias.name, { origin: "derived", fields: schemaMembers(literal), methods: [] });
|
|
18944
19137
|
}
|
|
18945
19138
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
18946
|
-
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, mutableBindings, reexportOnly };
|
|
19139
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, mutableBindings, reexportOnly };
|
|
18947
19140
|
}
|
|
18948
19141
|
function resolveRelativeModule(fromFile, specifier) {
|
|
18949
19142
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -19126,6 +19319,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
19126
19319
|
fieldTypes: Object.fromEntries([...facts.fieldTypes].map(([k, v]) => [k, [...v]])),
|
|
19127
19320
|
localTypes: Object.fromEntries([...facts.localTypes].map(([k, v]) => [k, [...v]])),
|
|
19128
19321
|
typeShapes: Object.fromEntries(facts.typeShapes),
|
|
19322
|
+
functionParams: Object.fromEntries(facts.functionParams),
|
|
19129
19323
|
topLevelMutableBindings: [...facts.mutableBindings],
|
|
19130
19324
|
reexportOnly: facts.reexportOnly
|
|
19131
19325
|
};
|