@wairon/cli 5.1.1-dev.63 → 5.1.1-dev.65
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 +567 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +538 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5767,6 +5767,23 @@ var MethodImplementationSchema = import_zod8.z.object({
|
|
|
5767
5767
|
* name — e.g. a store's `put` realized by `saveSnapshot`.
|
|
5768
5768
|
*/
|
|
5769
5769
|
symbol: import_zod8.z.string().optional(),
|
|
5770
|
+
/**
|
|
5771
|
+
* The exported binding a consumer imports to REACH this method, when the
|
|
5772
|
+
* realization is published as a value that COMPOSES it rather than exported
|
|
5773
|
+
* in its own right — a rule module publishes `callConformanceRule`, and the
|
|
5774
|
+
* method is the `check` inside it.
|
|
5775
|
+
*
|
|
5776
|
+
* `symbol` cannot say this: it names the code-level name REALIZING the
|
|
5777
|
+
* contract method — the function itself — so a file whose whole published
|
|
5778
|
+
* surface is the composing value publishes a name no contract mentions, and
|
|
5779
|
+
* the surface check reads an unpromised export where the design has a
|
|
5780
|
+
* promised one. Naming the handle here is what closes that gap.
|
|
5781
|
+
*
|
|
5782
|
+
* A declaration, never a waiver: a handle the method's own source file
|
|
5783
|
+
* does not actually export is reported (UNREALIZED_EXPORT_HANDLE) and
|
|
5784
|
+
* allows nothing, so the field can never become a free-text suppression.
|
|
5785
|
+
*/
|
|
5786
|
+
exportedVia: import_zod8.z.string().optional(),
|
|
5770
5787
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
5771
5788
|
ext: ExtDataSchema.optional()
|
|
5772
5789
|
});
|
|
@@ -5802,6 +5819,24 @@ var ImplementationSpecSchema = import_zod8.z.object({
|
|
|
5802
5819
|
* stereotypes should bind tech directly (TECH_ON_LOGIC_COMPONENT).
|
|
5803
5820
|
*/
|
|
5804
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(),
|
|
5805
5840
|
methods: import_zod8.z.array(MethodImplementationSchema).default([]).superRefine((methods, ctx) => {
|
|
5806
5841
|
methods.forEach((m, i) => {
|
|
5807
5842
|
if (!m.calls?.length || m.narrative.length === 0) return;
|
|
@@ -11532,7 +11567,7 @@ function defaultTargetConfig(type) {
|
|
|
11532
11567
|
enabled: true
|
|
11533
11568
|
};
|
|
11534
11569
|
}
|
|
11535
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
11570
|
+
var WAIRON_VERSION = "5.1.1-dev.65";
|
|
11536
11571
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
11537
11572
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
11538
11573
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -12219,7 +12254,7 @@ function buildCodeIndex(model) {
|
|
|
12219
12254
|
const f = facts.get(path28);
|
|
12220
12255
|
return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path28, facts: f } : void 0;
|
|
12221
12256
|
};
|
|
12222
|
-
const
|
|
12257
|
+
const originOf2 = (site, from) => {
|
|
12223
12258
|
const resolved = scopeOf(site, from);
|
|
12224
12259
|
if (!resolved) return NO_ORIGIN;
|
|
12225
12260
|
const { path: scope, facts: f } = resolved;
|
|
@@ -12240,7 +12275,7 @@ function buildCodeIndex(model) {
|
|
|
12240
12275
|
return NO_ORIGIN;
|
|
12241
12276
|
};
|
|
12242
12277
|
const possibleOriginsOf = (site, from) => {
|
|
12243
|
-
const proven =
|
|
12278
|
+
const proven = originOf2(site, from);
|
|
12244
12279
|
if (!site.field && !site.constructed && !site.via) return proven;
|
|
12245
12280
|
const resolved = scopeOf(site, from);
|
|
12246
12281
|
if (!resolved) return proven;
|
|
@@ -12264,7 +12299,7 @@ function buildCodeIndex(model) {
|
|
|
12264
12299
|
declarationsAt,
|
|
12265
12300
|
anchorsAt: (path28) => namesAt(anchors, pathKey(path28), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
|
|
12266
12301
|
findingAnchorsAt: (path28) => namesAt(findingAnchors, pathKey(path28), (f) => new Set(f.anchoredNames)),
|
|
12267
|
-
originOf,
|
|
12302
|
+
originOf: originOf2,
|
|
12268
12303
|
possibleOriginsOf
|
|
12269
12304
|
};
|
|
12270
12305
|
}
|
|
@@ -16770,6 +16805,260 @@ var typeRealizationRule = {
|
|
|
16770
16805
|
}
|
|
16771
16806
|
};
|
|
16772
16807
|
|
|
16808
|
+
// src/core/rules/conformance/type-shape.ts
|
|
16809
|
+
function originOf(shape) {
|
|
16810
|
+
if (shape.origin === "derived") return "derived shape";
|
|
16811
|
+
return shape.inherited ? "declared shape (which extends another)" : "declared shape";
|
|
16812
|
+
}
|
|
16813
|
+
function absence(optional) {
|
|
16814
|
+
return optional ? "may be absent" : "is always there";
|
|
16815
|
+
}
|
|
16816
|
+
var typeShapeRule = {
|
|
16817
|
+
name: "type-shape",
|
|
16818
|
+
description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count.",
|
|
16819
|
+
codes: [
|
|
16820
|
+
{
|
|
16821
|
+
code: "UNREALIZED_TYPE_FIELD",
|
|
16822
|
+
defaultSeverity: "warning",
|
|
16823
|
+
summary: "A type spec declares a field the shape at its sourcePath does not carry \u2014 the spec describes data the code does not have, and every reader of the ERD and the briefs has been told it exists",
|
|
16824
|
+
// Measured code↔spec drift, field by field, at a site the finding names
|
|
16825
|
+
// — which is why every one of them hands over `parts`.
|
|
16826
|
+
carryable: true
|
|
16827
|
+
},
|
|
16828
|
+
{
|
|
16829
|
+
code: "UNDECLARED_TYPE_FIELD",
|
|
16830
|
+
defaultSeverity: "warning",
|
|
16831
|
+
summary: "The shape at a type's sourcePath carries a field the type spec does not declare \u2014 data the design never described, so no diagram draws it and no brief hands it to an implementer",
|
|
16832
|
+
carryable: true
|
|
16833
|
+
},
|
|
16834
|
+
{
|
|
16835
|
+
code: "TYPE_FIELD_OPTIONALITY",
|
|
16836
|
+
defaultSeverity: "warning",
|
|
16837
|
+
summary: "A type spec and the shape at its sourcePath disagree about whether a field may be absent \u2014 one of them is telling a reader the data is guaranteed when it is not, or the reverse",
|
|
16838
|
+
carryable: true
|
|
16839
|
+
}
|
|
16840
|
+
],
|
|
16841
|
+
check(ctx) {
|
|
16842
|
+
const code = ctx.codeIndex();
|
|
16843
|
+
for (const type of ctx.types) {
|
|
16844
|
+
if (!type.sourcePath) continue;
|
|
16845
|
+
if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
|
|
16846
|
+
const file = pathKey(type.sourcePath);
|
|
16847
|
+
const facts = code.factsAt(file);
|
|
16848
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
16849
|
+
const symbol = type.symbol ?? type.name;
|
|
16850
|
+
const shapes = facts.typeShapes;
|
|
16851
|
+
if (!shapes || !Object.prototype.hasOwnProperty.call(shapes, symbol)) continue;
|
|
16852
|
+
const shape = shapes[symbol];
|
|
16853
|
+
const specFields = type.fields ?? [];
|
|
16854
|
+
const specFieldNames = new Set(specFields.map((field) => field.name));
|
|
16855
|
+
const specMethodNames = new Set((type.methods ?? []).map((method) => method.name));
|
|
16856
|
+
const codeFields = new Map(shape.fields.map((field) => [field.name, field.optional]));
|
|
16857
|
+
const codeMethodNames = new Set(shape.methods);
|
|
16858
|
+
const unrealized = shape.inherited ? [] : specFields.filter((field) => !codeFields.has(field.name) && !codeMethodNames.has(field.name)).map((field) => field.name);
|
|
16859
|
+
if (unrealized.length > 0) {
|
|
16860
|
+
ctx.addIssue(
|
|
16861
|
+
"warning",
|
|
16862
|
+
"UNREALIZED_TYPE_FIELD",
|
|
16863
|
+
`Type "${type.id}" declares ${unrealized.length} field(s) the ${originOf(shape)} "${symbol}" in "${file}" does not carry \u2014 ${unrealized.map((name) => `"${name}"`).join(", ")}. The spec is describing data the code does not have, and nothing breaks at a call site to correct it: the ERD draws those fields and every brief hands them to an implementer as if they were there. Add them to the code, or drop them from the type.`,
|
|
16864
|
+
type.id,
|
|
16865
|
+
void 0,
|
|
16866
|
+
void 0,
|
|
16867
|
+
{ at: symbol, covers: unrealized }
|
|
16868
|
+
);
|
|
16869
|
+
}
|
|
16870
|
+
const undeclared = shape.inherited ? [] : shape.fields.filter((field) => !specFieldNames.has(field.name) && !specMethodNames.has(field.name)).map((field) => field.name);
|
|
16871
|
+
if (undeclared.length > 0) {
|
|
16872
|
+
ctx.addIssue(
|
|
16873
|
+
"warning",
|
|
16874
|
+
"UNDECLARED_TYPE_FIELD",
|
|
16875
|
+
`The ${originOf(shape)} "${symbol}" in "${file}" carries ${undeclared.length} field(s) type "${type.id}" does not declare \u2014 ${undeclared.map((name) => `"${name}"`).join(", ")}. Data the design never described is data no diagram draws and no brief hands to an implementer, which is how a record grows past the model of it without anyone deciding to. Model the field, or take it out of the shape.`,
|
|
16876
|
+
type.id,
|
|
16877
|
+
void 0,
|
|
16878
|
+
void 0,
|
|
16879
|
+
{ at: symbol, covers: undeclared }
|
|
16880
|
+
);
|
|
16881
|
+
}
|
|
16882
|
+
const disagreed = specFields.filter((field) => {
|
|
16883
|
+
const absentable = codeFields.get(field.name);
|
|
16884
|
+
return absentable !== void 0 && absentable !== !!field.optional;
|
|
16885
|
+
});
|
|
16886
|
+
if (disagreed.length > 0) {
|
|
16887
|
+
const told = disagreed.map((field) => `"${field.name}" (the spec says it ${absence(!!field.optional)}, the code says it ${absence(!!codeFields.get(field.name))})`).join("; ");
|
|
16888
|
+
ctx.addIssue(
|
|
16889
|
+
"warning",
|
|
16890
|
+
"TYPE_FIELD_OPTIONALITY",
|
|
16891
|
+
`Type "${type.id}" and the ${originOf(shape)} "${symbol}" in "${file}" disagree about whether ${disagreed.length} field(s) may be absent \u2014 ${told}. One of them is telling a reader the data is guaranteed when it is not, or the reverse. Absent-able is the code's word for it \u2014 a wrapper that lets the key be missing \u2014 and a wrapper that FILLS a missing key leaves the field required in the value the type finally holds, which is the shape the spec describes.`,
|
|
16892
|
+
type.id,
|
|
16893
|
+
void 0,
|
|
16894
|
+
void 0,
|
|
16895
|
+
{ at: symbol, covers: disagreed.map((field) => field.name) }
|
|
16896
|
+
);
|
|
16897
|
+
}
|
|
16898
|
+
}
|
|
16899
|
+
}
|
|
16900
|
+
};
|
|
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
|
+
|
|
16773
17062
|
// src/core/rules/conformance/unclaimed-source.ts
|
|
16774
17063
|
var unclaimedSourceRule = {
|
|
16775
17064
|
name: "unclaimed-source",
|
|
@@ -16816,6 +17105,126 @@ var unclaimedSourceRule = {
|
|
|
16816
17105
|
}
|
|
16817
17106
|
};
|
|
16818
17107
|
|
|
17108
|
+
// src/core/rules/conformance/export-conformance.ts
|
|
17109
|
+
var NOTHING_PROMISED = /* @__PURE__ */ new Set();
|
|
17110
|
+
var exportConformanceRule = {
|
|
17111
|
+
name: "export-conformance",
|
|
17112
|
+
description: "Code-to-contract for the SURFACE: does this file publish anything the components it realizes never promised, that another component then takes? The rest of the conformance set reads contract-to-code \u2014 it asks whether the code holds what a spec claims \u2014 so a file could export whatever it liked under a component's name and nothing looked. That is one half of the same hole as a contract promising a parameter it never passes. A method's `exportedVia` names the export a consumer imports to REACH it \u2014 the value that composes it, which `symbol` cannot name because `symbol` names the function inside \u2014 and a declared handle the file does not actually export is itself a finding, so naming one can never become a free-text suppression. Both questions are asked only of a file read at exact grade, and a taker counts only when its own import specifier resolves to the file it is accused of taking from: below that nothing separates an export from a mention, or a shared word from a shared module.",
|
|
17113
|
+
codes: [
|
|
17114
|
+
{
|
|
17115
|
+
code: "UNDECLARED_EXPORT",
|
|
17116
|
+
defaultSeverity: "warning",
|
|
17117
|
+
summary: "A file realizing a component exports a name no contract of that component declares, and a file realizing a DIFFERENT component imports it \u2014 a surface reached across a boundary that the design never promised",
|
|
17118
|
+
// Measured code-to-spec drift at a site the finding names, so the
|
|
17119
|
+
// debt register may carry it — which is why it hands over `parts`.
|
|
17120
|
+
carryable: true
|
|
17121
|
+
},
|
|
17122
|
+
{
|
|
17123
|
+
code: "UNREALIZED_EXPORT_HANDLE",
|
|
17124
|
+
defaultSeverity: "warning",
|
|
17125
|
+
summary: "An implementation method declares an `exportedVia` handle its own source file does not export \u2014 the spec names a published route to the method that nobody can import"
|
|
17126
|
+
// Deliberately NOT carryable, and so it hands over no `parts`: a wrong
|
|
17127
|
+
// handle is one string to correct, and a parking space for it could
|
|
17128
|
+
// only ever be a parking space for a lie.
|
|
17129
|
+
}
|
|
17130
|
+
],
|
|
17131
|
+
check(ctx) {
|
|
17132
|
+
const code = ctx.codeIndex();
|
|
17133
|
+
const realization = ctx.realizationIndex();
|
|
17134
|
+
const promised = /* @__PURE__ */ new Map();
|
|
17135
|
+
const promise = (file, name) => {
|
|
17136
|
+
if (!name) return;
|
|
17137
|
+
const key = pathKey(file);
|
|
17138
|
+
let names = promised.get(key);
|
|
17139
|
+
if (!names) promised.set(key, names = /* @__PURE__ */ new Set());
|
|
17140
|
+
names.add(name);
|
|
17141
|
+
};
|
|
17142
|
+
for (const file of realization.paths) {
|
|
17143
|
+
for (const component of realization.componentsAt(file)) {
|
|
17144
|
+
for (const method of ctx.interfaceMethodsOf(component.id)) promise(file, method.name);
|
|
17145
|
+
for (const impl of realization.implementationsOf(component.id)) {
|
|
17146
|
+
for (const method of impl.methods ?? []) {
|
|
17147
|
+
promise(file, method.name);
|
|
17148
|
+
promise(file, method.symbol);
|
|
17149
|
+
}
|
|
17150
|
+
}
|
|
17151
|
+
}
|
|
17152
|
+
}
|
|
17153
|
+
for (const { implementation, method, sourceFile, draftContext } of ctx.implementationMethods()) {
|
|
17154
|
+
const handle = method.exportedVia;
|
|
17155
|
+
if (!handle || !sourceFile) continue;
|
|
17156
|
+
const file = pathKey(sourceFile);
|
|
17157
|
+
const facts = code.factsAt(file);
|
|
17158
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
17159
|
+
if (facts.exportedNames.includes(handle)) {
|
|
17160
|
+
promise(file, handle);
|
|
17161
|
+
continue;
|
|
17162
|
+
}
|
|
17163
|
+
ctx.addIssue(
|
|
17164
|
+
"warning",
|
|
17165
|
+
"UNREALIZED_EXPORT_HANDLE",
|
|
17166
|
+
`Method "${method.name}" in implementation "${implementation.id}" declares exportedVia "${handle}", but "${file}" exports no such name. A handle names the export a consumer imports to REACH the method \u2014 the value that composes it, which \`symbol\` cannot name because \`symbol\` names the function inside \u2014 so one the file does not publish promises a route nobody can import. It allows nothing either: every export of this file is still read against the contracts. Name the binding the file actually exports, export it, or drop the handle.`,
|
|
17167
|
+
implementation.id,
|
|
17168
|
+
draftContext
|
|
17169
|
+
);
|
|
17170
|
+
}
|
|
17171
|
+
for (const type of ctx.types) {
|
|
17172
|
+
if (!type.sourcePath) continue;
|
|
17173
|
+
promise(type.sourcePath, type.symbol ?? type.name);
|
|
17174
|
+
for (const method of type.methods ?? []) {
|
|
17175
|
+
promise(method.sourcePath ?? type.sourcePath, method.symbol ?? method.name);
|
|
17176
|
+
}
|
|
17177
|
+
}
|
|
17178
|
+
const takenFrom = /* @__PURE__ */ new Map();
|
|
17179
|
+
for (const consumer of realization.paths) {
|
|
17180
|
+
if (!code.exactPaths.has(consumer)) continue;
|
|
17181
|
+
for (const [local, binding] of Object.entries(code.factsAt(consumer)?.importBindings ?? {})) {
|
|
17182
|
+
if (binding.namespace) continue;
|
|
17183
|
+
const target = resolveImport(consumer, binding.from, code.paths);
|
|
17184
|
+
if (!target) continue;
|
|
17185
|
+
let names = takenFrom.get(target);
|
|
17186
|
+
if (!names) takenFrom.set(target, names = /* @__PURE__ */ new Map());
|
|
17187
|
+
for (const name of [local, binding.imported]) {
|
|
17188
|
+
if (!name) continue;
|
|
17189
|
+
let takers = names.get(name);
|
|
17190
|
+
if (!takers) names.set(name, takers = /* @__PURE__ */ new Set());
|
|
17191
|
+
takers.add(consumer);
|
|
17192
|
+
}
|
|
17193
|
+
}
|
|
17194
|
+
}
|
|
17195
|
+
for (const file of realization.paths) {
|
|
17196
|
+
const facts = code.factsAt(file);
|
|
17197
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
17198
|
+
const declared = promised.get(file) ?? NOTHING_PROMISED;
|
|
17199
|
+
const taken = takenFrom.get(file);
|
|
17200
|
+
const owners = new Set(realization.componentsAt(file).map((c) => c.id));
|
|
17201
|
+
const crossed = [];
|
|
17202
|
+
for (const name of [...new Set(facts.exportedNames)].sort()) {
|
|
17203
|
+
if (declared.has(name)) continue;
|
|
17204
|
+
const takers = taken?.get(name);
|
|
17205
|
+
if (!takers) continue;
|
|
17206
|
+
const crossing = [...takers].some(
|
|
17207
|
+
(taker) => taker !== file && !realization.componentsAt(taker).some((c) => owners.has(c.id))
|
|
17208
|
+
);
|
|
17209
|
+
if (crossing) crossed.push(name);
|
|
17210
|
+
}
|
|
17211
|
+
if (crossed.length === 0) continue;
|
|
17212
|
+
const [owner] = realization.implementationsAt(file).map((impl) => impl.id).sort();
|
|
17213
|
+
ctx.addIssue(
|
|
17214
|
+
"warning",
|
|
17215
|
+
"UNDECLARED_EXPORT",
|
|
17216
|
+
`Source file "${file}" publishes ${crossed.length} name(s) that no contract of the component(s) it realizes (${[...owners].sort().map((id) => `"${id}"`).join(", ")}) declares, and a file realizing NONE of those components imports each one FROM HERE \u2014 ${crossed.map((n) => `"${n}"`).join(", ")}. An export only this file's own components use is the module's private factoring and no concern of the design; one another component reaches for is a surface the design never promised. Declare it as a contract method, stop exporting it, or move it to the component that owns the name.`,
|
|
17217
|
+
owner,
|
|
17218
|
+
void 0,
|
|
17219
|
+
void 0,
|
|
17220
|
+
// The crossings themselves, named the way the message names them, so
|
|
17221
|
+
// a register entry written for today's set cannot carry tomorrow's.
|
|
17222
|
+
{ at: file, covers: crossed }
|
|
17223
|
+
);
|
|
17224
|
+
}
|
|
17225
|
+
}
|
|
17226
|
+
};
|
|
17227
|
+
|
|
16819
17228
|
// src/core/rules/conformance/carried-debt.ts
|
|
16820
17229
|
var carriedDebtRule = {
|
|
16821
17230
|
name: "carried-debt",
|
|
@@ -18111,11 +18520,17 @@ var SDD_RULES = [
|
|
|
18111
18520
|
integrationSimFileRule,
|
|
18112
18521
|
integrationSimWiringRule,
|
|
18113
18522
|
integrationSimCoverageRule,
|
|
18114
|
-
// The data model's own claim on code
|
|
18115
|
-
//
|
|
18116
|
-
// the
|
|
18523
|
+
// The data model's own claim on code — does the file hold the type, and is
|
|
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.
|
|
18117
18529
|
typeRealizationRule,
|
|
18530
|
+
typeShapeRule,
|
|
18531
|
+
paramConformanceRule,
|
|
18118
18532
|
unclaimedSourceRule,
|
|
18533
|
+
exportConformanceRule,
|
|
18119
18534
|
couplingRule,
|
|
18120
18535
|
// Target-language fit in two questions: what a CONTRACT may name, and what
|
|
18121
18536
|
// a NARRATIVE may describe.
|
|
@@ -18430,7 +18845,11 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18430
18845
|
const typeOnlyBindings = /* @__PURE__ */ new Map();
|
|
18431
18846
|
const fieldTypes = /* @__PURE__ */ new Map();
|
|
18432
18847
|
const localTypes = /* @__PURE__ */ new Map();
|
|
18848
|
+
const typeShapes = /* @__PURE__ */ new Map();
|
|
18849
|
+
const functionParams = /* @__PURE__ */ new Map();
|
|
18433
18850
|
const mutableBindings = /* @__PURE__ */ new Set();
|
|
18851
|
+
const schemaConstants = /* @__PURE__ */ new Map();
|
|
18852
|
+
const derivedAliases = [];
|
|
18434
18853
|
const receiverKey = (site) => site.via ?? (site.field ? `this.${site.field}` : site.constructed ? `new ${site.constructed}` : "");
|
|
18435
18854
|
const siteKey = (site) => `${site.member ? "m" : "b"}:${receiverKey(site)}:${site.name}`;
|
|
18436
18855
|
const addBindingNames = (name) => {
|
|
@@ -18464,6 +18883,79 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18464
18883
|
named.add(typeName);
|
|
18465
18884
|
localTypes.set(name.text, named);
|
|
18466
18885
|
};
|
|
18886
|
+
const recordDeclaredShape = (name, members, inherited) => {
|
|
18887
|
+
const fields = [];
|
|
18888
|
+
const methods = [];
|
|
18889
|
+
for (const member of members) {
|
|
18890
|
+
const memberName = member.name ? propertyNameText(member.name) : void 0;
|
|
18891
|
+
if (!memberName) continue;
|
|
18892
|
+
if (ts.isMethodSignature(member) || ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member)) {
|
|
18893
|
+
methods.push(memberName);
|
|
18894
|
+
} else if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
|
|
18895
|
+
fields.push({ name: memberName, optional: !!member.questionToken });
|
|
18896
|
+
}
|
|
18897
|
+
}
|
|
18898
|
+
const shape = { origin: "declared", fields, methods };
|
|
18899
|
+
if (inherited) shape.inherited = true;
|
|
18900
|
+
typeShapes.set(name, shape);
|
|
18901
|
+
};
|
|
18902
|
+
const inferredSchemaConstant = (type) => {
|
|
18903
|
+
if (!ts.isTypeReferenceNode(type)) return void 0;
|
|
18904
|
+
const reference = ts.isIdentifier(type.typeName) ? type.typeName.text : ts.isQualifiedName(type.typeName) ? type.typeName.right.text : void 0;
|
|
18905
|
+
if (reference !== "infer") return void 0;
|
|
18906
|
+
const [argument] = type.typeArguments ?? [];
|
|
18907
|
+
if (!argument || !ts.isTypeQueryNode(argument) || !ts.isIdentifier(argument.exprName)) return void 0;
|
|
18908
|
+
return argument.exprName.text;
|
|
18909
|
+
};
|
|
18910
|
+
const baseObjectLiteral = (expression) => {
|
|
18911
|
+
let node = expression;
|
|
18912
|
+
const walked = /* @__PURE__ */ new Set();
|
|
18913
|
+
while (node && !walked.has(node)) {
|
|
18914
|
+
walked.add(node);
|
|
18915
|
+
if (ts.isCallExpression(node)) {
|
|
18916
|
+
const callee = node.expression;
|
|
18917
|
+
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "object") {
|
|
18918
|
+
const literal = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
18919
|
+
if (literal) return literal;
|
|
18920
|
+
}
|
|
18921
|
+
node = callee;
|
|
18922
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
18923
|
+
node = node.expression;
|
|
18924
|
+
} else break;
|
|
18925
|
+
}
|
|
18926
|
+
return void 0;
|
|
18927
|
+
};
|
|
18928
|
+
const chainedCombinators = (expression) => {
|
|
18929
|
+
const applied = /* @__PURE__ */ new Set();
|
|
18930
|
+
let node = expression;
|
|
18931
|
+
const walked = /* @__PURE__ */ new Set();
|
|
18932
|
+
while (node && !walked.has(node)) {
|
|
18933
|
+
walked.add(node);
|
|
18934
|
+
if (ts.isCallExpression(node)) {
|
|
18935
|
+
const callee = node.expression;
|
|
18936
|
+
if (ts.isPropertyAccessExpression(callee)) applied.add(callee.name.text);
|
|
18937
|
+
node = callee;
|
|
18938
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
18939
|
+
node = node.expression;
|
|
18940
|
+
} else break;
|
|
18941
|
+
}
|
|
18942
|
+
return applied;
|
|
18943
|
+
};
|
|
18944
|
+
const schemaMembers = (literal) => {
|
|
18945
|
+
const fields = [];
|
|
18946
|
+
for (const property of literal.properties) {
|
|
18947
|
+
const memberName = property.name ? propertyNameText(property.name) : void 0;
|
|
18948
|
+
if (!memberName) continue;
|
|
18949
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
18950
|
+
fields.push({ name: memberName, optional: false });
|
|
18951
|
+
} else if (ts.isPropertyAssignment(property)) {
|
|
18952
|
+
const applied = chainedCombinators(property.initializer);
|
|
18953
|
+
const optional = (applied.has("optional") || applied.has("nullish")) && !applied.has("default");
|
|
18954
|
+
fields.push({ name: memberName, optional });
|
|
18955
|
+
}
|
|
18956
|
+
}
|
|
18957
|
+
return fields;
|
|
18958
|
+
};
|
|
18467
18959
|
const hasExportModifier = (node) => {
|
|
18468
18960
|
const mods = node.modifiers;
|
|
18469
18961
|
return !!mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
@@ -18484,6 +18976,14 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18484
18976
|
}
|
|
18485
18977
|
return void 0;
|
|
18486
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
|
+
});
|
|
18487
18987
|
const collectFunctionFacts = (fn) => {
|
|
18488
18988
|
let score = 1;
|
|
18489
18989
|
const callees = /* @__PURE__ */ new Map();
|
|
@@ -18526,12 +19026,32 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18526
19026
|
const sites = calls.get(fnName) ?? /* @__PURE__ */ new Map();
|
|
18527
19027
|
for (const [key, site] of facts.callees) sites.set(key, site);
|
|
18528
19028
|
calls.set(fnName, sites);
|
|
19029
|
+
const signatures = functionParams.get(fnName) ?? [];
|
|
19030
|
+
signatures.push(declaredParameters(node));
|
|
19031
|
+
functionParams.set(fnName, signatures);
|
|
18529
19032
|
}
|
|
18530
19033
|
if (ts.isPropertyDeclaration(node)) recordFieldType(node.name, node.type);
|
|
18531
19034
|
else if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent) && isParameterProperty(node)) {
|
|
18532
19035
|
recordFieldType(node.name, node.type);
|
|
18533
19036
|
}
|
|
18534
19037
|
if (ts.isParameter(node) || ts.isVariableDeclaration(node)) recordLocalType(node.name, node.type);
|
|
19038
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
19039
|
+
recordDeclaredShape(node.name.text, node.members, (node.heritageClauses?.length ?? 0) > 0);
|
|
19040
|
+
} else if (ts.isClassDeclaration(node) && node.name) {
|
|
19041
|
+
const extendsOther = (node.heritageClauses ?? []).some(
|
|
19042
|
+
(clause) => clause.token === ts.SyntaxKind.ExtendsKeyword
|
|
19043
|
+
);
|
|
19044
|
+
recordDeclaredShape(node.name.text, node.members, extendsOther);
|
|
19045
|
+
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
19046
|
+
if (ts.isTypeLiteralNode(node.type)) recordDeclaredShape(node.name.text, node.type.members, false);
|
|
19047
|
+
else {
|
|
19048
|
+
const constant = inferredSchemaConstant(node.type);
|
|
19049
|
+
if (constant) derivedAliases.push({ name: node.name.text, constant });
|
|
19050
|
+
}
|
|
19051
|
+
}
|
|
19052
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && !schemaConstants.has(node.name.text)) {
|
|
19053
|
+
schemaConstants.set(node.name.text, node.initializer);
|
|
19054
|
+
}
|
|
18535
19055
|
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
|
|
18536
19056
|
const name = node.name && ts.isIdentifier(node.name) ? node.name.text : void 0;
|
|
18537
19057
|
if (name) {
|
|
@@ -18607,8 +19127,16 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
18607
19127
|
ts.forEachChild(node, visit);
|
|
18608
19128
|
};
|
|
18609
19129
|
visit(sf);
|
|
19130
|
+
for (const alias of derivedAliases) {
|
|
19131
|
+
if (typeShapes.has(alias.name)) continue;
|
|
19132
|
+
const initializer = schemaConstants.get(alias.constant);
|
|
19133
|
+
if (!initializer) continue;
|
|
19134
|
+
const literal = baseObjectLiteral(initializer);
|
|
19135
|
+
if (!literal) continue;
|
|
19136
|
+
typeShapes.set(alias.name, { origin: "derived", fields: schemaMembers(literal), methods: [] });
|
|
19137
|
+
}
|
|
18610
19138
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
18611
|
-
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, mutableBindings, reexportOnly };
|
|
19139
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, mutableBindings, reexportOnly };
|
|
18612
19140
|
}
|
|
18613
19141
|
function resolveRelativeModule(fromFile, specifier) {
|
|
18614
19142
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -18790,6 +19318,8 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
18790
19318
|
typeOnlyBindings: Object.fromEntries(facts.typeOnlyBindings),
|
|
18791
19319
|
fieldTypes: Object.fromEntries([...facts.fieldTypes].map(([k, v]) => [k, [...v]])),
|
|
18792
19320
|
localTypes: Object.fromEntries([...facts.localTypes].map(([k, v]) => [k, [...v]])),
|
|
19321
|
+
typeShapes: Object.fromEntries(facts.typeShapes),
|
|
19322
|
+
functionParams: Object.fromEntries(facts.functionParams),
|
|
18793
19323
|
topLevelMutableBindings: [...facts.mutableBindings],
|
|
18794
19324
|
reexportOnly: facts.reexportOnly
|
|
18795
19325
|
};
|