candor-ts 0.20.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/Cases.ts +4 -0
- package/README.md +2 -2
- package/package.json +13 -4
- package/query-core.mjs +1 -1
- package/query.mjs +1 -1
- package/scan-core.mjs +12 -1
- package/scan.mjs +698 -55
- package/sensitivity.mjs +166 -0
- package/verify-core.mjs +226 -0
- package/verify-emit.mjs +174 -0
- package/verify-loader.mjs +62 -0
- package/verify-preload.mjs +114 -0
- package/verify-syscall.mjs +76 -0
- package/verify.mjs +175 -0
package/AGENTS.md
CHANGED
|
@@ -12,7 +12,7 @@ chains by hand.
|
|
|
12
12
|
> **Already installed? Report the version and ask before upgrading — before you scan.** If this
|
|
13
13
|
> project already has candor (a `.candor/` report directory, or `candor-ts` reachable via `npx`/a
|
|
14
14
|
> global install), do this *first*: run `npx -y candor-ts --version` (offline) and **tell the user
|
|
15
|
-
> plainly which version they're on** — e.g. *"This project is on candor-ts `<version>` (spec 0.
|
|
15
|
+
> plainly which version they're on** — e.g. *"This project is on candor-ts `<version>` (spec 0.23)."*
|
|
16
16
|
> On a build too old for the flag, read `candor.version` / `candor.spec` from an existing
|
|
17
17
|
> `.candor/report*.json`, or `npm ls -g candor-ts`.
|
|
18
18
|
>
|
package/Cases.ts
CHANGED
|
@@ -32,6 +32,10 @@ class Holder { cb: () => void = () => {}; }
|
|
|
32
32
|
const h = new Holder();
|
|
33
33
|
export function unknown_dyn(): void { const cb = h.cb; cb(); }
|
|
34
34
|
|
|
35
|
+
// An OPAQUE callback handed to a SYNCHRONOUS invoker (forEach) is an unresolvable call -> Unknown,
|
|
36
|
+
// never silently pure (four-way sync-callback-invoker rung).
|
|
37
|
+
export function sync_callback_opaque(xs: number[], cb: (x: number) => void): void { xs.forEach(cb); }
|
|
38
|
+
|
|
35
39
|
// --- multi-effect union in one body ---
|
|
36
40
|
export function combined(): void { try { fsm.readFileSync("/tmp/x"); netm.connect(1, "h"); } catch {} }
|
|
37
41
|
|
package/README.md
CHANGED
|
@@ -184,7 +184,7 @@ pure-vs-Unknown ruling (PART 16) — the engines must answer identically, on eve
|
|
|
184
184
|
| A call resolving to a *type* (function-typed field/param) → `Unknown`, never silent-pure | SPEC §4 |
|
|
185
185
|
| Unmatched external calls contribute nothing (curated-classifier caveat) | SEMANTICS §8 C1 |
|
|
186
186
|
| The literal surfaces `hosts`/`cmds`/`paths`/`tables`, literal-read only | SPEC §2 |
|
|
187
|
-
| `{ candor: { version, toolchain, spec: "0.
|
|
187
|
+
| `{ candor: { version, toolchain, spec: "0.23" }, functions }` envelope; pure fns omitted | SPEC §2/§2.1 |
|
|
188
188
|
| Call-graph sidecar with **every** analyzed function a key | SPEC §2.2 |
|
|
189
189
|
| The gate: AS-EFF-006 / 008 / 009, loud on an unreadable policy | SPEC §6.2 |
|
|
190
190
|
|
|
@@ -202,7 +202,7 @@ read the Rust source".
|
|
|
202
202
|
|
|
203
203
|
## Status
|
|
204
204
|
|
|
205
|
-
0.19.x, speaking candor-spec 0.
|
|
205
|
+
0.19.x, speaking candor-spec 0.23: the analysis core, the gate (`--policy` / `--gate-json` /
|
|
206
206
|
`.candor/config`), the full §3.1 query surface (including `containment`, `blindspots`, the
|
|
207
207
|
`--include-unknown` dispatch frontier), the MCP server, the LSP server, and the watch loop are
|
|
208
208
|
real, behaviorally tested (`npm test` — the behavioral suite across six harnesses), **soundness-fuzzed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "candor-ts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "candor for TypeScript — per-function side effects, transitively, with a policy gate (candor-spec 0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
|
+
"description": "candor for TypeScript — per-function side effects, transitively, with a policy gate (candor-spec 0.23)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@types/node": "^25.9.2",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"candor-ts-mcp": "./mcp.mjs",
|
|
14
14
|
"candor-ts-watch": "./watch.mjs",
|
|
15
15
|
"candor-mcp": "./mcp.mjs",
|
|
16
|
-
"candor-lsp": "./lsp.mjs"
|
|
16
|
+
"candor-lsp": "./lsp.mjs",
|
|
17
|
+
"candor-ts-verify": "./verify.mjs",
|
|
18
|
+
"candor-ts-sensitivity": "./sensitivity.mjs"
|
|
17
19
|
},
|
|
18
20
|
"scripts": {
|
|
19
21
|
"lint": "eslint *.mjs",
|
|
@@ -55,7 +57,14 @@
|
|
|
55
57
|
"surface.mjs",
|
|
56
58
|
"mcp.mjs",
|
|
57
59
|
"watch.mjs",
|
|
58
|
-
"lsp.mjs"
|
|
60
|
+
"lsp.mjs",
|
|
61
|
+
"verify.mjs",
|
|
62
|
+
"verify-core.mjs",
|
|
63
|
+
"verify-preload.mjs",
|
|
64
|
+
"verify-emit.mjs",
|
|
65
|
+
"verify-loader.mjs",
|
|
66
|
+
"verify-syscall.mjs",
|
|
67
|
+
"sensitivity.mjs"
|
|
59
68
|
],
|
|
60
69
|
"devDependencies": {
|
|
61
70
|
"@eslint/js": "^9.39.4",
|
package/query-core.mjs
CHANGED
|
@@ -29,7 +29,7 @@ function siblings(prefix, predicate) {
|
|
|
29
29
|
// sibling is `.encountered-*`/`.calibrated.json` passes the existence check but loads ZERO functions →
|
|
30
30
|
// an authoritative-empty result (silent under-report; review find). `.gate.json` has no functions array,
|
|
31
31
|
// so merging it "disclosed a malformed report" on every query over the recommended CI layout — noisy, excluded.
|
|
32
|
-
export const isReport = (f) => !f.endsWith(".callgraph.json") && !f.endsWith(".hierarchy.json") && !f.includes(".encountered-") && !f.endsWith(".calibrated.json") && !f.endsWith(".gate.json");
|
|
32
|
+
export const isReport = (f) => !f.endsWith(".callgraph.json") && !f.endsWith(".hierarchy.json") && !f.endsWith(".locs.json") && !f.includes(".encountered-") && !f.endsWith(".calibrated.json") && !f.endsWith(".gate.json");
|
|
33
33
|
|
|
34
34
|
// A report exists at the prefix if there's an exact `<prefix>.json` (candor-ts) OR a sibling
|
|
35
35
|
// `<prefix>.<crate>.scan.json` (the candor-scan/Rust multi-report form) — the loaders read both, so a
|
package/query.mjs
CHANGED
|
@@ -202,7 +202,7 @@ function renderPathHuman(fns, cg, fnQ, eff) {
|
|
|
202
202
|
// package.json; SPEC_VERSION is the spec contract this build speaks. Reused, never re-littered.
|
|
203
203
|
const QUERY_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
204
204
|
const PKG_VERSION = JSON.parse(fs.readFileSync(path.join(QUERY_DIR, "package.json"), "utf8")).version;
|
|
205
|
-
const SPEC_VERSION = "0.
|
|
205
|
+
const SPEC_VERSION = "0.23";
|
|
206
206
|
|
|
207
207
|
// ---- the §3.3.1 canonical query grammar (⟨0.10⟩, additive over 0.9) --------------------------------
|
|
208
208
|
// One shape for every verb: `<verb> <verb-args…> [--report <locator>] [--policy <file>] [--json]
|
package/scan-core.mjs
CHANGED
|
@@ -109,6 +109,11 @@ export const KAPPA_RULES = [
|
|
|
109
109
|
/^(capture|captureImmediate|identify|identifyImmediate|alias|groupIdentify|flush|shutdown|isFeatureEnabled|getFeatureFlag|getFeatureFlagPayload|getAllFlags|getAllFlagsAndPayloads|getRemoteConfigPayload|reloadFeatureFlags)$/,
|
|
110
110
|
"Net"],
|
|
111
111
|
[/^(pg|mysql2?|mongodb|ioredis|redis|sqlite3|better-sqlite3|knex)$/, null, "Db"],
|
|
112
|
+
// Template-literal SQL clients that EXECUTE the query through a `sql`…`` tag: postgres.js (porsager),
|
|
113
|
+
// @vercel/postgres, slonik. Whole-module Db (the client factory + the tag both reach the connection, like
|
|
114
|
+
// pg's `new Pool()`). NOT `sql-template-tag` — that only BUILDS a query object (executed via pg), so it is
|
|
115
|
+
// a pure builder and stays uncurated. The tag call is classified through the TaggedTemplateExpression arm.
|
|
116
|
+
[/^(postgres|@vercel\/postgres|slonik)$/, null, "Db"],
|
|
112
117
|
// bull/bullmq are Redis-backed job queues — the queue/worker/job ops issue Redis commands (Db). Their
|
|
113
118
|
// surface is almost entirely I/O, but be VERB-precise (the I/O ops) so inert event-wiring
|
|
114
119
|
// (`queue.on(...)`) and `new Queue()`/`new Worker()` construction (which only opens a lazy connection)
|
|
@@ -163,8 +168,14 @@ export const KAPPA_RULES = [
|
|
|
163
168
|
// builders; `createQueryBuilder` is pure, its `getMany`/`execute` is the I/O). Found on the
|
|
164
169
|
// first framework-APP scan: a TypeORM/Nest application — Db-heavy by construction — read zero
|
|
165
170
|
// Db because the ORM resolved into an unlisted package (the JVM's Spring-Data lesson, replayed).
|
|
171
|
+
// The verb set is the QUERY surface PLUS the DataSource lifecycle that performs connection/DDL I/O:
|
|
172
|
+
// `initialize`/`connect` OPEN the pool (a real round-trip to the server), and
|
|
173
|
+
// `synchronize`/`runMigrations`/`undoLastMigration`/`dropDatabase` execute DDL/migration SQL — all Db.
|
|
174
|
+
// (Found dogfooding ukri-tfs: `buildPostgresDataSource` did `new DataSource(o).initialize()` and read
|
|
175
|
+
// PURE — a false all-clear on a fn that opens a database connection.) Still module-gated to typeorm, so
|
|
176
|
+
// these generic-looking verbs only fire on a typeorm-typed receiver; the pure builder heads stay pure.
|
|
166
177
|
[/^(typeorm|@nestjs\/typeorm)$/,
|
|
167
|
-
/^(find|save|remove|softRemove|recover|insert|update|upsert|delete|restore|count|exist|sum|average|minimum|maximum|query|clear|increment|decrement|getMany|getOne|getOneOrFail|getRawMany|getRawOne|getCount|getExists|execute|stream|transaction)/,
|
|
178
|
+
/^(find|save|remove|softRemove|recover|insert|update|upsert|delete|restore|count|exist|sum|average|minimum|maximum|query|clear|increment|decrement|getMany|getOne|getOneOrFail|getRawMany|getRawOne|getCount|getExists|execute|stream|transaction|initialize|connect|synchronize|runMigrations|undoLastMigration|dropDatabase)/,
|
|
168
179
|
"Db"],
|
|
169
180
|
[/^(@prisma\/client|\.prisma|\.prisma\/client)$/,
|
|
170
181
|
/^(\$?(queryRaw|executeRaw|transaction)|find(Many|Unique|First)|create|createMany|update|updateMany|upsert|delete|deleteMany|aggregate|count|groupBy)/,
|