querydig 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +173 -2
- package/dist/analyzer.d.ts +6 -0
- package/dist/baseline.d.ts +3 -0
- package/dist/chunk-VAYEO4RI.js +1208 -0
- package/dist/chunk-WTZLJ5ZV.js +474 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +166 -0
- package/dist/config.d.ts +14 -0
- package/dist/fileAnalyzer.d.ts +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +26 -0
- package/dist/languages.d.ts +19 -0
- package/dist/parser.d.ts +33 -0
- package/dist/report/formats.d.ts +4 -0
- package/dist/report/html.d.ts +2 -0
- package/dist/report/terminal.d.ts +5 -0
- package/dist/rules.d.ts +17 -0
- package/dist/scan.d.ts +16 -0
- package/dist/types.d.ts +42 -0
- package/dist/version.d.ts +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +10 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +71 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 querydig contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,174 @@
|
|
|
1
|
-
#
|
|
1
|
+
# querydig
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Find slow, N+1 and unoptimized database queries before they reach production.**
|
|
4
|
+
|
|
5
|
+
querydig is a zero-config CLI that statically scans your code for MongoDB and SQL query anti-patterns: N+1 queries, nested queries over results (N²), writes in loops, unbounded reads, unanchored regexes, `skip()`/`OFFSET` pagination, non-sargable SQL and more. Each finding gets a severity (**critical / high / medium / low**), the exact location, why it's slow and how to fix it.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx querydig # scan the current directory
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- **Runs locally.** No account, no server, your code never leaves the machine.
|
|
12
|
+
- **Fast.** Tree-sitter (WASM) parsing in a worker pool: about 2,000 files in under 1 second.
|
|
13
|
+
- **Multi-language.** JavaScript, TypeScript, Python and Go.
|
|
14
|
+
- **Multi-database.** MongoDB (Mongoose, native driver, PyMongo/Motor, mongo-go-driver) and SQL (Prisma, TypeORM, Sequelize, pg/mysql2/knex raw, Django ORM, SQLAlchemy, DB-API cursors, GORM/xorm, `database/sql`).
|
|
15
|
+
- **CI-ready.** `--fail-on`, baselines, `--diff`, SARIF for GitHub code scanning, Markdown for PR comments, HTML report.
|
|
16
|
+
|
|
17
|
+
> Requires Node.js 22.12+.
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
CRITICAL QD002 Nested query over another query's results (N² risk) [mongodb]
|
|
23
|
+
src/orders.service.ts:7:20
|
|
24
|
+
│ o.user = await User.findById(o.userId);
|
|
25
|
+
`User.findById()` is executed for every row of `orders`, which itself came from the database (loop at line 6).
|
|
26
|
+
fix: Collect the _id values first and run a single `User.find({ _id: { $in: values } })`, then match results in memory with a Map.
|
|
27
|
+
|
|
28
|
+
MEDIUM MQ005 sort() without limit() [mongodb]
|
|
29
|
+
src/search.ts:25:10
|
|
30
|
+
│ return User.find({ status }).sort({ createdAt: -1 }).lean();
|
|
31
|
+
fix: Add .limit(n) and an index matching filter + sort (ESR order), e.g. createIndex({ status: 1, createdAt: -1 }).
|
|
32
|
+
|
|
33
|
+
Summary
|
|
34
|
+
CRITICAL 6 HIGH 16 MEDIUM 7 LOW 12
|
|
35
|
+
Query health: F (0/100)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx querydig [path] # terminal report
|
|
42
|
+
npx querydig --format html # writes querydig-report.html (filterable, self-contained)
|
|
43
|
+
npx querydig --format json -o report.json
|
|
44
|
+
npx querydig --format sarif -o querydig.sarif
|
|
45
|
+
npx querydig --format md > querydig.md # for PR comments
|
|
46
|
+
|
|
47
|
+
npx querydig --fail-on high # exit 1 if any high/critical issue (CI gate)
|
|
48
|
+
npx querydig --min-severity medium # hide low findings
|
|
49
|
+
npx querydig --diff origin/main # only files changed vs a branch
|
|
50
|
+
npx querydig --update-baseline # record existing issues in .querydig-baseline.json
|
|
51
|
+
npx querydig --baseline .querydig-baseline.json --fail-on high # only NEW issues fail
|
|
52
|
+
|
|
53
|
+
npx querydig rules # list rules
|
|
54
|
+
npx querydig explain QD001 # why + before/after example
|
|
55
|
+
npx querydig init # create querydig.config.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Add it to a project:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm i -D querydig
|
|
62
|
+
# package.json → "scripts": { "querydig": "querydig --fail-on high" }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
|
|
67
|
+
| ID | Severity | DB | Detects |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| QD001 | high (critical when nested) | any | Query inside a loop / `.map` / comprehension (N+1) |
|
|
70
|
+
| QD002 | critical | any | Query per row of another query's results (N²) |
|
|
71
|
+
| QD003 | high | any | insert/update/delete inside a loop. Use bulk APIs |
|
|
72
|
+
| MQ001 | high | MongoDB | `find({})` with no limit (full collection scan) |
|
|
73
|
+
| MQ002 | high | MongoDB | Unanchored or case-insensitive `$regex` |
|
|
74
|
+
| MQ003 | high | MongoDB | `$where`, `$function`, `mapReduce` |
|
|
75
|
+
| MQ004 | medium | MongoDB | `skip()` pagination |
|
|
76
|
+
| MQ005 | medium | MongoDB | `sort()` without `limit()` (suggests an ESR index) |
|
|
77
|
+
| MQ006 | medium | MongoDB | `$lookup`/`$unwind`/`$group` before `$match` |
|
|
78
|
+
| MQ007 | medium | MongoDB | Nested or many `populate()` calls |
|
|
79
|
+
| MQ008 | low | MongoDB | `countDocuments()` used only as an existence check |
|
|
80
|
+
| MQ009 | low | MongoDB | `$ne` / `$nin` / `$not` filters |
|
|
81
|
+
| MQ010 | low | MongoDB | `find()` without projection |
|
|
82
|
+
| MQ011 | low | MongoDB | Mongoose read without `.lean()` |
|
|
83
|
+
| SQ001 | high | SQL | `findMany()` / `findAll()` / `objects.all()` / `db.Find()` with no where/limit |
|
|
84
|
+
| SQ002 | low | SQL | `SELECT *` |
|
|
85
|
+
| SQ003 | high | SQL | `LIKE '%term'` |
|
|
86
|
+
| SQ004 | high/medium | SQL | `UPDATE`/`DELETE` without `WHERE`, `SELECT` without `WHERE`/`LIMIT` |
|
|
87
|
+
| SQ005 | medium | SQL | `OFFSET` pagination |
|
|
88
|
+
| SQ006 | medium | SQL | `WHERE LOWER(col) = …` (non-sargable) |
|
|
89
|
+
| SQ007 | high | SQL | `ORDER BY RAND()` |
|
|
90
|
+
| SQ008 | medium | SQL | `NOT IN (SELECT …)` |
|
|
91
|
+
|
|
92
|
+
Built-in noise control: `while`/`for {}` polling loops, batching loops (`skip`/`offset` in the loop), loops over collections or model classes, and array helpers like `arr.find(x => …)` or Python `str.find()` are not flagged.
|
|
93
|
+
|
|
94
|
+
## Suppressing findings
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
await User.findById(id); // querydig-ignore QD001 -- bounded to 3 items
|
|
98
|
+
// querydig-ignore ← suppresses every rule on the next line
|
|
99
|
+
// querydig-ignore-file ← in the first lines of a file, skips the whole file
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
`querydig.config.json` (or `.querydigrc.json`) in the project root:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"ignore": ["scripts/**"],
|
|
109
|
+
"includeTests": false,
|
|
110
|
+
"failOn": "high",
|
|
111
|
+
"minSeverity": "low",
|
|
112
|
+
"rules": { "MQ010": "off", "SQ002": "off", "QD003": "critical" }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
By default querydig skips `node_modules`, build output, vendored code, `.gitignore`d files, tests, specs, fixtures, benchmarks, migrations and seeds. Pass `--include-tests` to scan them too.
|
|
117
|
+
|
|
118
|
+
## CI
|
|
119
|
+
|
|
120
|
+
**GitHub Actions** (with code-scanning annotations):
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
name: querydig
|
|
124
|
+
on: [pull_request]
|
|
125
|
+
jobs:
|
|
126
|
+
querydig:
|
|
127
|
+
runs-on: ubuntu-latest
|
|
128
|
+
permissions: { contents: read, security-events: write }
|
|
129
|
+
steps:
|
|
130
|
+
- uses: actions/checkout@v7
|
|
131
|
+
with: { fetch-depth: 0 }
|
|
132
|
+
- uses: actions/setup-node@v7
|
|
133
|
+
with: { node-version: 22 }
|
|
134
|
+
- run: npx -y querydig --format sarif -o querydig.sarif || true
|
|
135
|
+
- uses: github/codeql-action/upload-sarif@v4
|
|
136
|
+
with: { sarif_file: querydig.sarif }
|
|
137
|
+
- run: npx -y querydig --diff origin/${{ github.base_ref }} --fail-on high
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**GitLab CI**
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
querydig:
|
|
144
|
+
image: node:22
|
|
145
|
+
script:
|
|
146
|
+
- npx -y querydig --fail-on high --format md > querydig.md
|
|
147
|
+
artifacts: { when: always, paths: [querydig.md] }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Any CI / pre-commit**
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npx -y querydig --baseline .querydig-baseline.json --fail-on high
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
More templates in [`examples/ci`](examples/ci).
|
|
157
|
+
|
|
158
|
+
## Programmatic API
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { scan, renderHtml } from 'querydig';
|
|
162
|
+
const summary = await scan({ root: '.', minSeverity: 'medium' });
|
|
163
|
+
console.log(summary.bySeverity, summary.grade);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Roadmap
|
|
167
|
+
|
|
168
|
+
- **Live mode** (`--uri`, read-only): MongoDB profiler + `explain()` (COLLSCAN, docs-examined ratio, in-memory SORT), missing/unused indexes; Postgres `pg_stat_statements` + `EXPLAIN`; MySQL `performance_schema`. Live evidence will be merged with static findings to promote severity.
|
|
169
|
+
- Java/Kotlin (Hibernate/Spring Data), Ruby (ActiveRecord), PHP (Eloquent).
|
|
170
|
+
- An official GitHub Action and incremental cache.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT
|