@alimaandev/ripple 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ripple 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 ADDED
@@ -0,0 +1,453 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/ripple-logo.png" alt="Ripple" width="120" />
4
+
5
+ # Ripple
6
+
7
+ **Dependency impact analysis for TypeScript and JavaScript.**
8
+
9
+ Change less. Break less. Know the blast radius of any file before you touch it.
10
+
11
+ <p align="center">
12
+ &nbsp;<a href="https://www.npmjs.com/package/@alimaandev/ripple"><img alt="npm version" src="https://img.shields.io/npm/v/@alimaandev/ripple?logo=npm&logoColor=white&label=npm&style=for-the-badge"/></a>&nbsp;
13
+ &nbsp;<a href="https://www.npmjs.com/package/@alimaandev/ripple"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@alimaandev/ripple?label=downloads&style=for-the-badge"/></a>&nbsp;
14
+ &nbsp;<a href="https://github.com/alimaandev/ripple/actions/workflows/ci.yml"><img alt="CI build" src="https://img.shields.io/github/actions/workflow/status/alimaandev/ripple/ci.yml?branch=main&logo=githubactions&logoColor=white&label=CI%20build&style=for-the-badge"/></a>&nbsp;
15
+ &nbsp;<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/github/license/alimaandev/ripple?label=License&style=for-the-badge"/></a>&nbsp;
16
+ &nbsp;<a href="package.json"><img alt="Node.js >= 22" src="https://img.shields.io/badge/Node.js-%3E%3D22-339933?logo=nodedotjs&logoColor=white&style=for-the-badge"/></a>&nbsp;
17
+ </p>
18
+
19
+ <img src="assets/hero.svg" alt="Terminal output of `ripple analyze` — risk, affected files, routes, components, tests, and confidence score" title="ripple analyze" width="100%" />
20
+
21
+ </div>
22
+
23
+ ## Why Ripple
24
+
25
+ ### The question your tooling doesn't answer
26
+
27
+ In any non-trivial codebase, there is a deceptively simple question:
28
+
29
+ > **If I change this file, what else could break?**
30
+
31
+ Most developer tooling stops short.
32
+
33
+ `grep` finds text. Package managers track dependencies, not dependents. Traditional import graphs show what a file uses—not everything that relies on it.
34
+
35
+ So the blast radius of a change becomes a guess.
36
+
37
+ **Ripple makes it explicit.**
38
+
39
+ It builds the project's import graph once, then walks it in reverse to show
40
+ exactly how a change propagates through your codebase:
41
+
42
+ ```text
43
+ $ ripple analyze src/authentication/login.ts
44
+
45
+ Risk MEDIUM
46
+ Affected Files 7 files
47
+ API Routes 4
48
+ Components 2
49
+ Tests 1
50
+ Max Depth 2
51
+ Confidence 100%
52
+ ```
53
+
54
+ No black box.
55
+
56
+ No cloud processing.
57
+
58
+ No telemetry.
59
+
60
+ No network calls.
61
+
62
+ Just deterministic analysis against the code you actually have.
63
+
64
+ **Know the ripple before you commit.**
65
+
66
+ ## Features
67
+
68
+ | Feature | What it does |
69
+ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------- |
70
+ | **Impact analysis** | Reverse dependency traversal, transitively, with depth caps. Know who imports a target file. |
71
+ | **Risk scoring** | A transparent 0–100 score from seven weighted signals, with a per-factor breakdown in `--verbose`. |
72
+ | **Cycle detection** | Strongly-connected components with a concrete cycle path — `a → c → b → a`, not just a list of names. |
73
+ | **Alias & path support** | tsconfig `paths` and custom aliases like `{ "@": "./src" }`, including single-wildcard patterns. |
74
+ | **Real-world resolution** | Extension probing, index files, `.js` → `.ts` rewriting, dynamic `import()`, `require()`, `export … from`. |
75
+ | **Categorized results** | Routes, layouts, components, utilities, and tests detected by path conventions — not a raw string of file names. |
76
+ | **Entry-point intelligence** | `package.json` `main`/`bin` plus conventional root and `src` entry files, flagged in the report. |
77
+ | **Stable JSON contract** | A machine-readable report for CI gates, dashboards, and tooling — additive-only compatibility. |
78
+ | **Error-tolerant parsing** | A broken file lowers confidence instead of aborting the run. No false precision. |
79
+ | **Deterministic & offline** | Same input, same output, every time. No hidden network dependency. |
80
+
81
+ ## Quick start
82
+
83
+ Everything below is real output against a shipped example project in `tests/fixtures/basic`.
84
+
85
+ ### Requirements
86
+
87
+ - Node.js ≥ 22
88
+ - pnpm 11+
89
+
90
+ ### Install & verify
91
+
92
+ ```bash
93
+ npm install -g @alimaandev/ripple
94
+
95
+ ripple version # → 0.1.0
96
+ ```
97
+
98
+ Or install from source:
99
+
100
+ ```bash
101
+ git clone https://github.com/alimaandev/ripple.git
102
+ cd ripple
103
+ pnpm install
104
+ pnpm build # bundles dist/bin.js via tsup
105
+ npm link # exposes the global `ripple` binary
106
+ ```
107
+
108
+ ### Run your first analysis
109
+
110
+ ```bash
111
+ cd tests/fixtures/basic
112
+ ripple analyze src/authentication/login.ts
113
+ ```
114
+
115
+ Expected output:
116
+
117
+ ```text
118
+ 🌊 Ripple Analysis
119
+
120
+ File src/authentication/login.ts
121
+ Risk MEDIUM
122
+ Affected Files 7 files
123
+ Components 2
124
+ API Routes 4
125
+ Tests 1
126
+ Utilities 0
127
+ Max Depth 2
128
+ Confidence 100%
129
+
130
+ Top Impact
131
+ • Dashboard (2)
132
+ • Admin (1)
133
+ • Index TS (1)
134
+
135
+ Circular Dependencies
136
+ ⭕ src/circular/a.ts → src/circular/c.ts → src/circular/b.ts → src/circular/a.ts
137
+
138
+ Affected Files (7)
139
+ ├─ src/admin/page.tsx (depth 1)
140
+ ├─ src/dashboard/page.tsx (depth 1)
141
+ └─ src/main.ts (depth 2)
142
+ ```
143
+
144
+ ### See more of the project
145
+
146
+ `ripple graph` gives a project-wide overview:
147
+
148
+ ```bash
149
+ cd tests/fixtures/basic
150
+ ripple graph
151
+ ```
152
+
153
+ ```text
154
+ 🌊 Dependency Graph
155
+
156
+ Files 25
157
+ Edges 28
158
+ External 4
159
+ Unresolved 0
160
+ Circular groups 1
161
+
162
+ Circular Dependencies
163
+ ⭕ src/circular/a.ts → src/circular/c.ts → src/circular/b.ts → src/circular/a.ts
164
+ ```
165
+
166
+ Point it at a file to see its dependency tree — forwards (what it imports)
167
+ or, with `-r`, backwards (what imports it):
168
+
169
+ ```bash
170
+ ripple graph src/circular/b.ts -r
171
+ ```
172
+
173
+ ```text
174
+ Dependents (reverse)
175
+ ├─ src/circular/c.ts
176
+ └─ src/circular/a.ts
177
+ ```
178
+
179
+ ## CLI reference
180
+
181
+ ```text
182
+ ripple <command> [options]
183
+ ```
184
+
185
+ | Command | Description |
186
+ | ---------------- | ------------------------------------------------- |
187
+ | `analyze <file>` | Impact analysis for a target file |
188
+ | `graph [file]` | Project stats, or a single file's dependency tree |
189
+ | `doctor` | Project and environment health check |
190
+ | `init` | Scaffold a `ripple.config.json` |
191
+ | `version` | Print the current version |
192
+
193
+ ### Options
194
+
195
+ | Command | Flag | Description |
196
+ | --------- | --------------------- | ---------------------------------------------------------------- |
197
+ | `analyze` | `-j, --json` | Emit the JSON report instead of the terminal report |
198
+ | `analyze` | `-v, --verbose` | Include the risk-factor point breakdown |
199
+ | `analyze` | `-d, --depth <n>` | Cap the reverse traversal at `n` levels |
200
+ | `analyze` | `-c, --config <path>` | Use a specific config file |
201
+ | `analyze` | `--no-color` | Disable ANSI colors |
202
+ | `graph` | `-j, --json` | Emit the JSON report |
203
+ | `graph` | `-r, --reverse` | Show dependents (what imports this file) instead of dependencies |
204
+ | `graph` | `-d, --depth <n>` | Cap tree depth |
205
+ | `graph` | `-c, --config <path>` | Use a specific config file |
206
+ | `graph` | `--no-color` | Disable ANSI colors |
207
+ | `doctor` | `-c, --config <path>` | Use a specific config file |
208
+ | `doctor` | `-v, --verbose` | Verbose output |
209
+ | `doctor` | `--no-color` | Disable ANSI colors |
210
+ | `init` | `-f, --force` | Overwrite an existing config |
211
+
212
+ ### `ripple doctor`
213
+
214
+ Independent health checks — config validity, tsconfig presence, source
215
+ discovery, parse rate, import resolution, cycles. A failed check is reported
216
+ but doesn't abort the run; the command exits non-zero if anything fails.
217
+
218
+ > [!TIP]
219
+ > Run `ripple doctor` before your first analysis — it fails fast on config
220
+ > and resolution problems and tells you exactly what to fix.
221
+
222
+ ```text
223
+ $ ripple doctor
224
+ ✓ Node.js runtime v22.11.0
225
+ ✓ package.json package.json
226
+ ✓ ripple config ripple.config.json
227
+ ✓ tsconfig.json tsconfig.json
228
+ ✓ source files 25 discovered
229
+ ✓ path aliases 1 configured
230
+ ✓ parse all files parsed cleanly
231
+ ✓ import resolution all internal imports resolved
232
+ ⚠ circular dependencies 1 cycle group(s)
233
+ ⚠ Diagnoses passed with warnings.
234
+ ```
235
+
236
+ ### `ripple init`
237
+
238
+ Writes `ripple.config.json` with the recommended defaults, ready to edit.
239
+
240
+ ## Configuration
241
+
242
+ Ripple discovers `ripple.config.ts`, `.js`, `.cjs`, `.mjs`, or `.json` in the
243
+ current directory, or from `--config <path>`. Your file is merged over the
244
+ defaults — every field is optional.
245
+
246
+ ```json
247
+ {
248
+ "include": ["**/*.{ts,tsx,js,jsx}"],
249
+ "ignore": ["node_modules", "dist", "build", "coverage", ".next", "out"],
250
+ "aliases": { "@": "./src" },
251
+ "tsconfigPath": "tsconfig.json",
252
+ "risk": {
253
+ "weights": {
254
+ "affectedFiles": 0.3,
255
+ "entryPoint": 0.15,
256
+ "sharedUtility": 0.15,
257
+ "publicExports": 0.1,
258
+ "tests": 0.1,
259
+ "routes": 0.1,
260
+ "cycleMembership": 0.1
261
+ },
262
+ "thresholds": { "medium": 30, "high": 55, "critical": 80 }
263
+ }
264
+ }
265
+ ```
266
+
267
+ | Field | Default | Purpose |
268
+ | ----------------- | ----------------------------------------------------------- | ------------------------------------------------------ |
269
+ | `include` | `**/*.{ts,tsx,js,jsx}` | Source globs to analyze |
270
+ | `ignore` | `node_modules`, `dist`, `build`, `coverage`, `.next`, `out` | Exclusions |
271
+ | `aliases` | `{}` | Path aliases, merged with tsconfig `paths` (user wins) |
272
+ | `tsconfigPath` | `tsconfig.json` | tsconfig to read for `paths` |
273
+ | `risk.weights` | see example | Risk signal weights |
274
+ | `risk.thresholds` | 30 / 55 / 80 | Score thresholds for MEDIUM / HIGH / CRITICAL |
275
+
276
+ `node_modules` is always excluded, regardless of config.
277
+
278
+ ## Risk scoring
279
+
280
+ The score is a weighted sum of seven signals, normalized to 0–1. Affected
281
+ counts are log-scaled so a big graph can never drown out the signal:
282
+
283
+ | Signal | Weight |
284
+ | ------------------------------- | ------ |
285
+ | Affected files | 0.30 |
286
+ | Entry point impacted | 0.15 |
287
+ | Shared utilities impacted | 0.15 |
288
+ | Target public export surface | 0.10 |
289
+ | Tests impacted | 0.10 |
290
+ | API routes impacted | 0.10 |
291
+ | Target in a circular dependency | 0.10 |
292
+
293
+ That yields a score from 0–100 and a level at thresholds 30, 55, 80 — the
294
+ same colors the CLI prints on your terminal:
295
+
296
+ <p align="center">
297
+ <img src="assets/risk-levels.svg" alt="Risk levels: LOW, MEDIUM, HIGH, CRITICAL" title="Risk levels" width="440" />
298
+ </p>
299
+
300
+ Confidence (0–100) is separate — how trustworthy the analysis is. It drops
301
+ when files fail to parse or imports stay unresolved, and takes a penalty if
302
+ the target sits inside a cycle. Use it to gate merges in CI, not just to look
303
+ at pretty numbers.
304
+
305
+ ## How it works
306
+
307
+ Ripple is a small pipeline: discover source files, parse their imports in a
308
+ real TypeScript project, build the dependency graph, then answer questions
309
+ against it. Risk and confidence are derived from that same graph — never from
310
+ heuristics invented on the fly.
311
+
312
+ ```mermaid
313
+ flowchart LR
314
+ A[Scanner] --> B[Parser]
315
+ B --> C[Graph]
316
+ C --> D[Reverse traversal]
317
+ D --> E[Risk + confidence]
318
+ E --> F[Terminal or JSON report]
319
+ ```
320
+
321
+ The pieces map to the source tree: `scanner/` discovers files from your
322
+ `include`/`ignore` globs, `parser/` extracts imports and exports with
323
+ error-tolerant recovery, `graph/` resolves specifiers and detects cycles, and
324
+ `risk/` turns graph signals into the score you see in the report.
325
+
326
+ ## JSON output (for CI)
327
+
328
+ `ripple analyze --json` emits a stable, versioned contract:
329
+
330
+ ```json
331
+ {
332
+ "tool": "ripple",
333
+ "version": "0.1.0",
334
+ "command": "analyze",
335
+ "file": "src/authentication/login.ts",
336
+ "risk": { "score": 34.4, "level": "MEDIUM", "factors": [] },
337
+ "summary": {
338
+ "affectedFiles": 7,
339
+ "routes": 4,
340
+ "components": 2,
341
+ "entries": 1,
342
+ "tests": 1,
343
+ "utilities": 0,
344
+ "maxDepth": 2,
345
+ "confidence": 100,
346
+ "topImpact": [
347
+ { "label": "Dashboard", "count": 2 },
348
+ { "label": "Admin", "count": 1 }
349
+ ]
350
+ },
351
+ "affected": [
352
+ {
353
+ "path": "src/admin/page.tsx",
354
+ "depth": 1,
355
+ "direct": true,
356
+ "categories": ["route"],
357
+ "inCycle": false
358
+ }
359
+ ],
360
+ "cycles": [
361
+ {
362
+ "members": ["src/circular/a.ts", "src/circular/b.ts", "src/circular/c.ts"],
363
+ "path": ["src/circular/a.ts", "src/circular/c.ts", "src/circular/b.ts", "src/circular/a.ts"]
364
+ }
365
+ ],
366
+ "targetInCycle": false,
367
+ "durationMs": 242
368
+ }
369
+ ```
370
+
371
+ > [!IMPORTANT]
372
+ > The contract is additive-only — existing field names and shapes never
373
+ > change without a major version bump. `graph --json` follows the same
374
+ > convention.
375
+
376
+ ### Use it in CI
377
+
378
+ ```bash
379
+ ripple analyze src/authentication/login.ts --json | jq -r .risk.level
380
+ # MEDIUM
381
+ ```
382
+
383
+ ```bash
384
+ #!/usr/bin/env bash
385
+ set -euo pipefail
386
+
387
+ level="$(ripple analyze "$1" --json | jq -r .risk.level)"
388
+ if [ "$level" = "CRITICAL" ]; then
389
+ echo "CRITICAL impact — review before merging" >&2
390
+ exit 1
391
+ fi
392
+ ```
393
+
394
+ ## FAQ
395
+
396
+ **Why is confidence below 100%?**
397
+
398
+ > [!WARNING]
399
+ > Confidence drops when files fail to parse or imports stay unresolved — the
400
+ > two signals that make an analysis less trustworthy. Run `ripple doctor` to
401
+ > see exactly which checks are failing.
402
+
403
+ **Why do `.css` or package imports appear in the report?**
404
+
405
+ > [!NOTE]
406
+ > Non-source imports (styles, assets, npm packages) are classified as
407
+ > external. They appear in the graph (see `External` in `ripple graph`) but
408
+ > are never counted as affected files — you can't "break" a package by
409
+ > changing local code.
410
+
411
+ **Why are `node_modules` files never analyzed?**
412
+
413
+ > [!NOTE]
414
+ > `node_modules` is always excluded, even if your config adds it to
415
+ > `include`. Analyzing installed packages would only add noise to your blast
416
+ > radius.
417
+
418
+ **Does Ripple work on Windows?**
419
+
420
+ > [!TIP]
421
+ > Yes — path handling is platform-aware. Integration tests run on Linux CI,
422
+ > and development happens on Windows; both report the same results from the
423
+ > same fixtures.
424
+
425
+ ## Exit codes
426
+
427
+ | Code | Meaning |
428
+ | ---- | ------------------------- |
429
+ | `0` | Success |
430
+ | `1` | General failure |
431
+ | `2` | Target file not found |
432
+ | `3` | Config missing or invalid |
433
+
434
+ ## Development
435
+
436
+ ```bash
437
+ pnpm install
438
+ pnpm run dev # watch-mode rebuild via tsup
439
+ pnpm run typecheck # tsc --noEmit
440
+ pnpm run lint # eslint
441
+ pnpm run lint:fix # eslint --fix
442
+ pnpm run format # prettier --write
443
+ pnpm run test # vitest run
444
+ pnpm run test:watch # vitest
445
+ pnpm run build # tsup
446
+ ```
447
+
448
+ Architecture and design decisions live in [docs/architecture.md](docs/architecture.md).
449
+ Before contributing, read [CONTRIBUTING.md](CONTRIBUTING.md).
450
+
451
+ ## License
452
+
453
+ MIT — see [LICENSE](LICENSE).