candor-ts 0.5.15 → 0.5.16

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scan.mjs +24 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "candor-ts",
3
- "version": "0.5.15",
3
+ "version": "0.5.16",
4
4
  "description": "candor for TypeScript — per-function side effects, transitively, with a policy gate (candor-spec 0.5)",
5
5
  "type": "module",
6
6
  "dependencies": {
package/scan.mjs CHANGED
@@ -791,9 +791,30 @@ function visitCalls(node) {
791
791
  }
792
792
  }
793
793
  if (!edged && !externalClass) {
794
- rec.direct.add("Unknown"); // unresolvable call → Unknown, never silent-pure (SPEC §4)
795
- const callee = (node.expression?.getText?.() ?? "?").replace(/\s+/g, "").slice(0, 60);
796
- rec.why.add(`call:${callee}`); // an `any`-typed/indeterminate callee — named, so triage starts here
794
+ // Blind PACKAGE member call: the receiver is a NAMESPACE import from a bare specifier
795
+ // (`import * as winstonm from "winston"; winstonm.info()`) that didn't resolve — typically the
796
+ // package isn't installed in this tree. The κ table may still MODEL it, so classify by the import
797
+ // SPECIFIER (the syntactic path, mirroring how the Rust scanner classifies a crate path without
798
+ // building). Only fires for κ-modeled packages (winston/pino/pg/…); everything else still → Unknown.
799
+ let kEff = null;
800
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)
801
+ && ts.isIdentifier(node.expression.expression)) {
802
+ const sym = checker.getSymbolAtLocation(node.expression.expression);
803
+ for (const d of sym?.declarations ?? []) {
804
+ if (ts.isNamespaceImport(d)) {
805
+ const spec = d.parent?.parent?.moduleSpecifier;
806
+ if (spec && ts.isStringLiteralLike(spec)) kEff = kappa(spec.text, node.expression.name.text);
807
+ break;
808
+ }
809
+ }
810
+ }
811
+ if (kEff) {
812
+ rec.direct.add(kEff); // κ-modeled package reached via an uninstalled namespace import
813
+ } else {
814
+ rec.direct.add("Unknown"); // unresolvable call → Unknown, never silent-pure (SPEC §4)
815
+ const callee = (node.expression?.getText?.() ?? "?").replace(/\s+/g, "").slice(0, 60);
816
+ rec.why.add(`call:${callee}`); // an `any`-typed/indeterminate callee — named, so triage starts here
817
+ }
797
818
  }
798
819
  } else {
799
820
  const mod = declModule(decl);