@ankurhalder/codebase-snapshot 0.0.0-stage → 2.0.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.
@@ -0,0 +1,372 @@
1
+ type Mode = "generate" | "validate" | "report";
2
+ type OutputFormat = "text" | "json";
3
+ /**
4
+ * How much of a file made it into the snapshot.
5
+ * `full` – the complete file body was emitted.
6
+ * `preview` – the body was truncated (by lines and/or bytes) before emission.
7
+ */
8
+ type InclusionMode = "full" | "preview";
9
+ interface SourceSummary {
10
+ directives: string[];
11
+ imports: string[];
12
+ exports: string[];
13
+ /** Module specifiers re-exported wholesale via `export * from "..."`. */
14
+ reExports: string[];
15
+ functions: string[];
16
+ classes: string[];
17
+ interfaces: string[];
18
+ types: string[];
19
+ enums: string[];
20
+ variables: string[];
21
+ reactComponents: string[];
22
+ hooks: string[];
23
+ routeHandlers: string[];
24
+ /** Number of parser diagnostics, present only when the file has syntax errors. */
25
+ parseErrors?: number;
26
+ }
27
+ interface JsonSummary {
28
+ shape: string;
29
+ itemCount: number | null;
30
+ keys: string[];
31
+ sampleKeys: string[];
32
+ }
33
+ interface MarkdownSummary {
34
+ headings: string[];
35
+ frontmatterKeys: string[];
36
+ }
37
+ type SkipReason = "asset" | "binary" | "read-error" | "not-file" | "sensitive-file" | "symlink" | "default-ignore" | "excluded";
38
+ interface SkipRecord {
39
+ path: string;
40
+ reason: SkipReason;
41
+ detail?: string;
42
+ }
43
+ /**
44
+ * One scanned file. Field names are canonical and unique by design:
45
+ * earlier releases carried `path`/`relativePath`, `sizeBytes`/`bytes` and
46
+ * `hashSha256`/`sha256` aliases that could only ever agree.
47
+ */
48
+ interface FileEntry {
49
+ relativePath: string;
50
+ sizeBytes: number;
51
+ lines: number;
52
+ hashSha256: string;
53
+ part: string;
54
+ layer: string;
55
+ kind: string;
56
+ language: string;
57
+ inclusionMode: InclusionMode;
58
+ isLarge: boolean;
59
+ isPreviewTruncated: boolean;
60
+ /** Line count of the file on disk. */
61
+ originalLines: number;
62
+ /** Lines actually emitted into the snapshot. */
63
+ includedLines: number;
64
+ /** UTF-8 bytes of file content actually emitted into the snapshot. */
65
+ includedBytes: number;
66
+ summary?: SourceSummary | JsonSummary | MarkdownSummary;
67
+ /** True when the summary alone was too large for the part budget and was dropped. */
68
+ summaryOmitted?: boolean;
69
+ /** Emitted body after redaction/truncation. Never serialized into the manifest. */
70
+ content?: string;
71
+ }
72
+ interface PartFileEntry {
73
+ relativePath: string;
74
+ sourceBytes: number;
75
+ includedBytes: number;
76
+ lines: number;
77
+ layer: string;
78
+ kind: string;
79
+ }
80
+ interface PartMetadata {
81
+ index: number;
82
+ filename: string;
83
+ /** Exact UTF-8 byte length of the emitted part file. */
84
+ byteSize: number;
85
+ sha256: string;
86
+ lines: number;
87
+ fileCount: number;
88
+ layersCovered: string[];
89
+ includedFiles: PartFileEntry[];
90
+ }
91
+ interface SecretRule {
92
+ rule: string;
93
+ pattern: RegExp;
94
+ }
95
+ /** Shape accepted from JSON config files, where `pattern` can only be a string. */
96
+ interface SecretRuleInput {
97
+ rule: string;
98
+ pattern: RegExp | string;
99
+ }
100
+ /**
101
+ * A detected secret. Deliberately carries no excerpt of the matched source:
102
+ * the previous `snippet` field was the vector by which raw credentials were
103
+ * written into `reports/audit.md`.
104
+ */
105
+ interface SecretFinding {
106
+ rule: string;
107
+ line: number;
108
+ path?: string;
109
+ }
110
+ interface ManifestTotals {
111
+ candidateFiles: number;
112
+ includedFiles: number;
113
+ fullFiles: number;
114
+ previewFiles: number;
115
+ skippedFiles: number;
116
+ totalSourceBytes: number;
117
+ totalContentBytes: number;
118
+ partCount: number;
119
+ warningCount: number;
120
+ }
121
+ interface ManifestArtifact {
122
+ byteSize: number;
123
+ sha256: string;
124
+ }
125
+ interface ManifestIndexes {
126
+ routes: Array<{
127
+ path: string;
128
+ methods: string[];
129
+ part: string;
130
+ }>;
131
+ components: Array<{
132
+ path: string;
133
+ symbols: string[];
134
+ part: string;
135
+ layer: string;
136
+ }>;
137
+ handlers: Array<{
138
+ path: string;
139
+ exports: string[];
140
+ part: string;
141
+ }>;
142
+ features: Array<{
143
+ name: string;
144
+ files: number;
145
+ }>;
146
+ imports: {
147
+ byFile: Array<{
148
+ path: string;
149
+ imports: string[];
150
+ exports: string[];
151
+ }>;
152
+ centralFiles: Array<{
153
+ path: string;
154
+ importedByCount: number;
155
+ }>;
156
+ };
157
+ directives: Array<{
158
+ path: string;
159
+ directives: string[];
160
+ }>;
161
+ }
162
+ /** Effective configuration recorded so `validate` can detect config drift. */
163
+ interface ManifestConfigSnapshot {
164
+ maxPartBytes: number;
165
+ largeFileBytes: number;
166
+ previewHeadLines: number;
167
+ previewTailLines: number;
168
+ includeMonolith: boolean;
169
+ includeSensitiveFiles: boolean;
170
+ artifacts: ResolvedArtifacts;
171
+ include: string[];
172
+ layersMode: "merge" | "replace";
173
+ budgets: ResolvedBudgets;
174
+ excludePatterns: string[];
175
+ layers: string[];
176
+ kinds: string[];
177
+ }
178
+ interface Manifest {
179
+ schemaVersion: string;
180
+ generatedAt: string;
181
+ generator: {
182
+ name: string;
183
+ version: string;
184
+ };
185
+ git: {
186
+ branch: string;
187
+ commit: string;
188
+ isDirty: boolean;
189
+ };
190
+ budgets: {
191
+ maxPartBytes: number;
192
+ largeFileBytes: number;
193
+ };
194
+ config: ManifestConfigSnapshot;
195
+ counts: {
196
+ totalFiles: number;
197
+ totalSizeBytes: number;
198
+ totalLines: number;
199
+ partsCount: number;
200
+ skippedCount: number;
201
+ largeFileCount: number;
202
+ truncatedPreviewCount: number;
203
+ };
204
+ totals: ManifestTotals;
205
+ fingerprint: string;
206
+ validation: {
207
+ status: "passed" | "failed";
208
+ errors: string[];
209
+ };
210
+ artifacts: {
211
+ fullJson: ManifestArtifact | null;
212
+ fullMarkdown: ManifestArtifact | null;
213
+ };
214
+ layers: Record<string, number>;
215
+ kinds: Record<string, number>;
216
+ indexes: ManifestIndexes;
217
+ parts: PartMetadata[];
218
+ files: FileEntry[];
219
+ skipped: SkipRecord[];
220
+ warnings: string[];
221
+ }
222
+ interface LayerRule {
223
+ name: string;
224
+ match: string | RegExp | ((filePath: string) => boolean);
225
+ }
226
+ /**
227
+ * Which snapshot artifacts get written.
228
+ *
229
+ * Everything a developer opens is produced by default; each entry exists only so
230
+ * a single artifact can be switched off without disabling the rest. `monolith`
231
+ * is the one exception, because it duplicates the entire payload: it follows
232
+ * `includeMonolith` (off by default) unless set here explicitly.
233
+ */
234
+ interface ArtifactSelection {
235
+ /** `README.md` — the orientation page for humans and models. */
236
+ readme?: boolean;
237
+ /** `manifest.json` — the registry `validate` and `report` read. */
238
+ manifest?: boolean;
239
+ /** `parts/part-XXX.md` — the partitioned code payload. */
240
+ parts?: boolean;
241
+ /** `reports/navigation.md` — file-to-part lookup index. */
242
+ navigation?: boolean;
243
+ /** `reports/project-tree.md` — ASCII repository tree. */
244
+ projectTree?: boolean;
245
+ /** `reports/audit.md` — security, layer, kind and skip findings. */
246
+ audit?: boolean;
247
+ /** `full-codebase.{json,md}` — one file per format, the whole payload again. */
248
+ monolith?: boolean;
249
+ }
250
+ /** Every artifact decision made concrete. */
251
+ type ResolvedArtifacts = Required<ArtifactSelection>;
252
+ interface KindRule {
253
+ kind: string;
254
+ match: string | RegExp | ((filePath: string) => boolean);
255
+ }
256
+ /** A byte budget: either raw bytes or a human-readable size such as `"120k"`. */
257
+ type ByteSize = number | string;
258
+ /** Per-layer part budgets. Unlisted layers fall back to `default`. */
259
+ interface BudgetConfig {
260
+ default?: ByteSize;
261
+ byLayer?: Record<string, ByteSize>;
262
+ }
263
+ /** Every budget resolved to concrete byte counts. */
264
+ interface ResolvedBudgets {
265
+ default: number;
266
+ byLayer: Record<string, number>;
267
+ }
268
+ /** Lifecycle hooks. Not serialized into the manifest (functions are not data). */
269
+ interface HooksConfig {
270
+ /** Runs once after a successful generate, with the manifest that was written. */
271
+ afterGenerate?: (manifest: Manifest) => void;
272
+ }
273
+ interface SnapshotConfig {
274
+ cwd?: string;
275
+ branch?: string;
276
+ outDir?: string;
277
+ tmpDir?: string;
278
+ /** Raw bytes, or a human-readable size (`"120k"`, `"1.5mb"`). */
279
+ maxPartBytes?: ByteSize;
280
+ largeFileBytes?: ByteSize;
281
+ previewHeadLines?: number;
282
+ previewTailLines?: number;
283
+ includeMonolith?: boolean;
284
+ maxMonolithBytes?: ByteSize | null;
285
+ /** Opt in to emitting `.env`/`*.pem`/`.npmrc`/credential files. Default false. */
286
+ includeSensitiveFiles?: boolean;
287
+ /** Allow-list of globs. When non-empty, only matching files are included. */
288
+ include?: string[];
289
+ /** Switch individual snapshot artifacts off. Everything is on by default. */
290
+ artifacts?: ArtifactSelection;
291
+ /**
292
+ * How user `layers`/`kinds` interact with the detected preset (default
293
+ * `"merge"`): user rules are evaluated first, then the preset fills the gaps.
294
+ * `"replace"` discards the preset entirely.
295
+ */
296
+ layersMode?: "merge" | "replace";
297
+ /** Per-layer part budgets. */
298
+ budgets?: BudgetConfig;
299
+ hooks?: HooksConfig;
300
+ layers?: LayerRule[];
301
+ kinds?: KindRule[];
302
+ excludePatterns?: string[];
303
+ secrets?: {
304
+ customPatterns?: SecretRuleInput[];
305
+ disableDefaultPatterns?: boolean;
306
+ };
307
+ }
308
+ interface ResolvedSnapshotConfig {
309
+ cwd: string;
310
+ branch: string;
311
+ outDir: string;
312
+ tmpDir: string;
313
+ maxPartBytes: number;
314
+ largeFileBytes: number;
315
+ previewHeadLines: number;
316
+ previewTailLines: number;
317
+ includeMonolith: boolean;
318
+ maxMonolithBytes: number | null;
319
+ includeSensitiveFiles: boolean;
320
+ /** Empty means "no allow-list"; every discovered file is a candidate. */
321
+ include: string[];
322
+ artifacts: ResolvedArtifacts;
323
+ layersMode: "merge" | "replace";
324
+ budgets: ResolvedBudgets;
325
+ hooks: HooksConfig;
326
+ layers: LayerRule[];
327
+ kinds: KindRule[];
328
+ excludePatterns: string[];
329
+ secretRules: SecretRule[];
330
+ }
331
+ interface CliOptions {
332
+ mode: Mode;
333
+ format: OutputFormat;
334
+ dryRun: boolean;
335
+ cwd?: string;
336
+ outDir?: string;
337
+ includeMonolith?: boolean;
338
+ includeSensitiveFiles?: boolean;
339
+ maxMonolithBytes?: number | null;
340
+ maxPartBytes?: number;
341
+ largeFileBytes?: number;
342
+ /** Artifact names to suppress for this invocation (`--skip-artifacts`). */
343
+ skipArtifacts?: string[];
344
+ }
345
+ interface ValidationDrift {
346
+ missingInSnapshot: string[];
347
+ missingOnDisk: string[];
348
+ hashMismatches: Array<{
349
+ path: string;
350
+ diskHash: string;
351
+ snapshotHash: string;
352
+ }>;
353
+ /** Emitted `parts/*.md` that are missing or no longer match their recorded hash. */
354
+ partIssues: Array<{
355
+ part: string;
356
+ issue: string;
357
+ }>;
358
+ /** `counts`/`totals`/`budgets` that disagree with the rest of the manifest. */
359
+ totalsMismatches: string[];
360
+ /** Effective-configuration differences between the manifest and the current run. */
361
+ configMismatches: string[];
362
+ }
363
+ interface ValidationResult {
364
+ valid: boolean;
365
+ manifestPresent: boolean;
366
+ drift: ValidationDrift;
367
+ errors: string[];
368
+ /** Non-fatal observations (e.g. branch moved on). */
369
+ warnings: string[];
370
+ }
371
+
372
+ export type { ArtifactSelection as A, ByteSize as B, CliOptions as C, FileEntry as F, HooksConfig as H, InclusionMode as I, JsonSummary as J, KindRule as K, LayerRule as L, MarkdownSummary as M, OutputFormat as O, PartMetadata as P, ResolvedSnapshotConfig as R, SourceSummary as S, ValidationResult as V, SnapshotConfig as a, SecretRule as b, SecretFinding as c, Manifest as d, SkipRecord as e, BudgetConfig as f, ManifestArtifact as g, ManifestConfigSnapshot as h, ManifestIndexes as i, ManifestTotals as j, Mode as k, PartFileEntry as l, ResolvedArtifacts as m, ResolvedBudgets as n, SecretRuleInput as o, SkipReason as p, ValidationDrift as q };
@@ -0,0 +1,244 @@
1
+ # Configuration reference
2
+
3
+ Everything `@ankurhalder/codebase-snapshot` can be told to do, in one place.
4
+
5
+ ```bash
6
+ npx @ankurhalder/codebase-snapshot init # writes a starter config
7
+ ```
8
+
9
+ Config files are resolved in this order, first match wins:
10
+
11
+ `snapshot.config.ts` → `.js` → `.mjs` → `.cjs` → `.json` → `.snapshotrc.json` → `.snapshotrc` → `"snapshot"` in `package.json`.
12
+
13
+ > A `.ts` config is only importable natively from Node **22.6+**. `init` writes `.mjs` on older Node, and an unloadable config is reported on stderr rather than ignored.
14
+
15
+ ---
16
+
17
+ ## Scoping: which files get in
18
+
19
+ ```javascript
20
+ export default {
21
+ include: ["src/**", "packages/*/src/**"], // allow-list, empty = everything
22
+ excludePatterns: ["**/*.test.ts", "docs"], // narrows what include let through
23
+ };
24
+ ```
25
+
26
+ `include` runs first, `excludePatterns` narrows the survivors. Both accept globs
27
+ (`**`, `*`, `?`, `{a,b}`, `[abc]`) or plain strings; a plain string matches a
28
+ whole path segment sequence, so `"docs"` matches `docs/a.md` and `src/docs/b.md`
29
+ but not `docsite/c.md`.
30
+
31
+ Every dropped file is recorded in `manifest.skipped` with `reason` and the exact
32
+ pattern responsible (`excluded`, `default-ignore`, `sensitive-file`, `binary`,
33
+ `symlink`, `read-error`, `not-file`).
34
+
35
+ ```javascript
36
+ export default {
37
+ includeSensitiveFiles: false, // .env, *.pem, .npmrc, .ssh/, .tfstate …
38
+ };
39
+ ```
40
+
41
+ ## Classification: layers and kinds
42
+
43
+ ```javascript
44
+ export default {
45
+ layersMode: "merge", // default: your rules first, then the preset's
46
+ layers: [
47
+ { name: "billing", match: "src/features/billing/**" },
48
+ { name: "legacy", match: (p) => p.includes(".legacy.") },
49
+ ],
50
+ kinds: [
51
+ { kind: "route", match: /(^|\/)route\.tsx?$/ },
52
+ { kind: "hook", match: "src/hooks/**" },
53
+ ],
54
+ };
55
+ ```
56
+
57
+ Rules are evaluated in order, **first match wins**, with an `other` fallback.
58
+ `match` may be a string, a `RegExp`, or a predicate function.
59
+
60
+ `layersMode: "merge"` (the default) keeps the detected preset's rules and lets
61
+ yours win by being evaluated first. `"replace"` discards the preset entirely.
62
+
63
+ Presets are auto-detected: **nextjs** → **vite** → **node** → **default**.
64
+
65
+ ## Sizing: byte budgets
66
+
67
+ ```javascript
68
+ export default {
69
+ maxPartBytes: "120k", // hard ceiling per part; default 300 KB, min 4096 B
70
+ largeFileBytes: "200k", // above this a file gets a head/tail preview
71
+ previewHeadLines: 80,
72
+ previewTailLines: 40,
73
+
74
+ budgets: { // optional per-layer overrides
75
+ default: "120k",
76
+ byLayer: { docs: 40_000, core: 300_000 },
77
+ },
78
+ };
79
+ ```
80
+
81
+ Budgets accept raw bytes (`122880`) or human sizes (`"120k"`, `"1.5mb"`, `"2 GB"`).
82
+ Anything unparseable is an error rather than a silent fallback to the default.
83
+
84
+ Files under different budgets are partitioned separately and the resulting parts
85
+ are re-numbered, so filenames and `Previous:`/`Next:` links stay correct.
86
+
87
+ ## Artifacts: what gets written
88
+
89
+ Everything is on by default. Each artifact can be switched off independently —
90
+ `artifacts` in config, or `--skip-artifacts` / `--only-artifacts` on the CLI.
91
+
92
+ | Artifact | Default | File |
93
+ | :--- | :--- | :--- |
94
+ | `readme` | on | `README.md` |
95
+ | `manifest` | on | `manifest.json` — **off breaks `validate` and `report`** |
96
+ | `parts` | on | `parts/part-XXX.md` |
97
+ | `navigation` | on | `reports/navigation.md` — dangles if `parts` is off |
98
+ | `projectTree` | on | `reports/project-tree.md` |
99
+ | `audit` | on | `reports/audit.md` |
100
+ | `monolith` | off | `full-codebase.{json,md}` |
101
+
102
+ ```javascript
103
+ export default {
104
+ artifacts: { parts: false, navigation: false }, // reports-only pass
105
+ includeMonolith: false,
106
+ maxMonolithBytes: null,
107
+ };
108
+ ```
109
+
110
+ Unknown artifact names are rejected, not ignored. The selection is recorded in
111
+ `manifest.config.artifacts`, so changing it later shows up as config drift.
112
+
113
+ The monolith is built from the **pre-partition** contents, so it holds the whole
114
+ file even when a part had to truncate it.
115
+
116
+ ## Secrets: redaction rules
117
+
118
+ 21 built-in rules cover PEM keys, JWT, AWS, GitHub, Slack, Stripe, Google,
119
+ OpenAI/Anthropic, npm, PyPI, SendGrid, Twilio, DigitalOcean, Azure, database
120
+ URIs, basic-auth URLs and `.env`-style assignments.
121
+
122
+ ```javascript
123
+ export default {
124
+ secrets: {
125
+ customPatterns: [
126
+ { rule: "internal-token", pattern: /CORP-[A-Z0-9]{32}/ },
127
+ { rule: "from-json", pattern: "ACME_[a-z0-9]{10}" }, // strings allowed
128
+ ],
129
+ disableDefaultPatterns: false,
130
+ },
131
+ };
132
+ ```
133
+
134
+ Only the matched span is replaced; the rest of the line survives. Multi-line PEM
135
+ blocks are masked as a block, including ones cut off before their `END` marker.
136
+ No excerpt of a matched line is ever written to any artifact.
137
+
138
+ ## Output layout
139
+
140
+ ```javascript
141
+ export default {
142
+ outDir: "snapshot",
143
+ tmpDir: "snapshot.tmp",
144
+ branch: "main", // fallback when git cannot report one
145
+ };
146
+ ```
147
+
148
+ `outDir` may not be the working directory, an ancestor, or the filesystem root,
149
+ and `outDir`/`tmpDir` may not contain each other. Writes are atomic: the previous
150
+ snapshot survives until the new one is fully in place.
151
+
152
+ ## Hooks
153
+
154
+ ```javascript
155
+ export default {
156
+ hooks: {
157
+ afterGenerate: (manifest) => {
158
+ // runs after a successful generate (and after a dry run)
159
+ },
160
+ },
161
+ };
162
+ ```
163
+
164
+ A throwing hook becomes a warning on the manifest — it never loses a snapshot
165
+ that was already written.
166
+
167
+ ---
168
+
169
+ # Commands
170
+
171
+ | Command | Purpose |
172
+ | :--- | :--- |
173
+ | `generate` *(default)* | Write the snapshot |
174
+ | `validate`, `check` | Re-hash sources and parts, compare config, detect drift |
175
+ | `report` | Print metrics, layer distribution and drift status |
176
+ | `explain <path>` | Show how one path would be treated |
177
+ | `diff <a> <b>` | Compare two snapshots |
178
+ | `init` | Write a starter config |
179
+
180
+ ## `explain`
181
+
182
+ ```
183
+ $ snap explain src/app/page.tsx
184
+
185
+ src/app/page.tsx
186
+
187
+ included yes
188
+ layer app (rule (p) => p.startsWith("src/app/") || ...)
189
+ kind component (rule (p) => { const base = p.split("/").pop() ...)
190
+ language tsx
191
+ size 48 B, 1 line(s)
192
+ part part-001.md
193
+ inclusion full
194
+ secrets none
195
+ ```
196
+
197
+ Excluded paths say why:
198
+
199
+ ```
200
+ $ snap explain .env
201
+
202
+ .env
203
+
204
+ included no
205
+ reason sensitive-file
206
+ detail credential-bearing file; enable includeSensitiveFiles to emit it
207
+ ```
208
+
209
+ Exits `0` when the path is included, `1` when it is not. `--format json` emits the
210
+ same data as an object.
211
+
212
+ ## `diff`
213
+
214
+ ```
215
+ $ snap diff old-snapshot new-snapshot
216
+
217
+ before 2026-11-03T10:00:00.000Z 812 file(s), 4 part(s)
218
+ after 2026-11-03T11:30:00.000Z 820 file(s), 4 part(s)
219
+
220
+ + 9 added
221
+ - 1 removed
222
+ ~ 3 changed
223
+ = 808 unchanged
224
+ ```
225
+
226
+ Both arguments may be snapshot directories or paths to a `manifest.json`.
227
+
228
+ ## `--watch`
229
+
230
+ ```bash
231
+ npx @ankurhalder/codebase-snapshot generate --watch
232
+ ```
233
+
234
+ Re-generates on every change, ignoring writes into the output and temporary
235
+ directories (including the atomic-swap backup) so it cannot trigger itself.
236
+
237
+ ---
238
+
239
+ # Drift
240
+
241
+ `validate` compares five things: source hashes on disk, the emitted parts (size
242
+ and SHA-256), the manifest's own bookkeeping, the recorded configuration
243
+ (budgets, includes, excludes, artifacts, layers mode), and git state. Changing a
244
+ config value without re-generating is reported as drift, not silently accepted.
package/package.json CHANGED
@@ -1,6 +1,95 @@
1
1
  {
2
2
  "name": "@ankurhalder/codebase-snapshot",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.0",
4
+ "description": "Production-grade LLM codebase snapshot generator, auditor, and AST partitioner",
5
+ "keywords": [
6
+ "codebase",
7
+ "snapshot",
8
+ "llm",
9
+ "ai",
10
+ "context",
11
+ "ast",
12
+ "audit",
13
+ "codebase-snapshot",
14
+ "code-analysis"
15
+ ],
16
+ "author": "Ankur Halder <ankur.halder12345@gmail.com>",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/ankurhalder/codebase-snapshot.git"
21
+ },
22
+ "homepage": "https://github.com/ankurhalder/codebase-snapshot#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/ankurhalder/codebase-snapshot/issues"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "type": "module",
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "sideEffects": false,
34
+ "bin": {
35
+ "codebase-snapshot": "./dist/bin.js",
36
+ "snap": "./dist/bin.js"
37
+ },
38
+ "exports": {
39
+ ".": {
40
+ "import": {
41
+ "types": "./dist/index.d.ts",
42
+ "default": "./dist/index.js"
43
+ },
44
+ "require": {
45
+ "types": "./dist/index.d.cts",
46
+ "default": "./dist/index.cjs"
47
+ }
48
+ },
49
+ "./cli": {
50
+ "import": {
51
+ "types": "./dist/cli.d.ts",
52
+ "default": "./dist/cli.js"
53
+ },
54
+ "require": {
55
+ "types": "./dist/cli.d.cts",
56
+ "default": "./dist/cli.cjs"
57
+ }
58
+ },
59
+ "./package.json": "./package.json"
60
+ },
61
+ "files": [
62
+ "dist/*.js",
63
+ "dist/*.cjs",
64
+ "dist/*.d.ts",
65
+ "dist/*.d.cts",
66
+ "docs/*.md",
67
+ "README.md",
68
+ "LICENSE"
69
+ ],
70
+ "scripts": {
71
+ "build": "tsup",
72
+ "dev": "tsup --watch",
73
+ "typecheck": "tsc --noEmit",
74
+ "lint": "node scripts/lint.mjs",
75
+ "check": "npm run lint && npm run typecheck && npm test",
76
+ "pretest": "npm run build",
77
+ "test": "node --test tests/*.test.js",
78
+ "test:coverage": "node --test --experimental-test-coverage tests/*.test.js",
79
+ "prepare": "npm run build",
80
+ "prepublishOnly": "npm run build"
81
+ },
82
+ "dependencies": {
83
+ "typescript": "^5.0.0"
84
+ },
85
+ "overrides": {
86
+ "esbuild": "^0.28.1"
87
+ },
88
+ "devDependencies": {
89
+ "@types/node": "^22.0.0",
90
+ "tsup": "^8.3.0"
91
+ },
92
+ "engines": {
93
+ "node": ">=18.0.0"
94
+ }
95
+ }