@photostructure/fs-metadata 1.4.0 → 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.
package/AGENTS.md ADDED
@@ -0,0 +1,213 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ @photostructure/fs-metadata - Cross-platform native Node.js module for filesystem metadata retrieval.
8
+
9
+ ### Directory Structure
10
+
11
+ - `src/` - Source code (TypeScript and C++)
12
+ - `dist/` - Compiled JavaScript output (gitignored)
13
+ - `doc/` - Static documentation (manually written, checked into git)
14
+ - `build/` - All build artifacts (gitignored)
15
+ - `build/docs/` - Generated API documentation from TypeDoc (deployed to GitHub Pages)
16
+ - `scripts/` - Build and utility scripts
17
+ - `prebuilds/` - Prebuilt native binaries for different platforms
18
+
19
+ ### Script Preferences
20
+
21
+ **Always** use TypeScript (`.ts`) scripts executed with `tsx` instead of:
22
+
23
+ - `.js` scripts (require compilation or older Node.js syntax)
24
+ - `.mjs` scripts (ESM-only, compatibility issues)
25
+ - `.cjs` scripts (CommonJS-only, less type safety)
26
+
27
+ TypeScript with tsx provides type safety, modern syntax, and seamless execution.
28
+
29
+ ## Critical Knowledge
30
+
31
+ ### Testing File System Metadata
32
+
33
+ **Never** expect exact equality for dynamic values (`available`, `used`) between calls. Only verify:
34
+
35
+ - Value exists and has correct type: `typeof result.available === 'number'`
36
+ - Test static properties (`size`, `mountFrom`, `fstype`) for exact equality
37
+ - Avoid range assertions (`available > 0`) - file changes can be dramatic
38
+
39
+ ### Cross-Module Compatibility
40
+
41
+ Use `_dirname()` from `./dirname` instead of `__dirname` - works in both CommonJS and ESM contexts.
42
+
43
+ ### Node.js Version Compatibility
44
+
45
+ Jest 30 doesn't support Node.js 23. Use Node.js 20, 22, or 24.
46
+
47
+ ## System Volume Detection
48
+
49
+ **IMPORTANT: Read `doc/system-volume-detection.md` before modifying any system volume detection logic.** It documents the full detection strategy across all platforms, including flag matrices and rationale for each approach.
50
+
51
+ Summary:
52
+
53
+ - The root `/` is a sealed, read-only APFS snapshot whose **UUID changes on every OS update** — never use it for persistent identification.
54
+ - **Primary detection** combines mount flags with APFS volume roles: `MNT_SNAPSHOT || (MNT_DONTBROWSE && hasApfsRole && role != "Data")`. See `ClassifyMacVolume()` in `src/darwin/system_volume.h`.
55
+ - The APFS role string is exposed as `volumeRole` on `MountPoint` and `VolumeMetadata`.
56
+ - **Fallback** uses `MNT_SNAPSHOT` only from `statfs` `f_flags` if DA session creation fails.
57
+ - `MNT_DONTBROWSE` is safe to use **only when combined with a non-Data APFS role**. The Data volume (`/System/Volumes/Data`) has `MNT_DONTBROWSE` but role `"Data"`, so it is correctly excluded.
58
+ - Pseudo-filesystems like `devfs` (no IOMedia, no APFS role) are caught by TypeScript fstype/path heuristics.
59
+
60
+ ## Windows-Specific Issues
61
+
62
+ ### Windows CI Jest Worker Failures
63
+
64
+ **Problem**: Jest worker processes fail on Windows CI environments (both x64 and ARM64) with "Jest worker encountered 4 child process exceptions".
65
+
66
+ **Solution for Memory Tests**:
67
+
68
+ Memory tests now use a standalone TypeScript runner (`src/test-utils/memory-test-runner.ts`) that bypasses Jest entirely on all platforms. This provides more accurate memory measurements without Jest overhead and avoids worker process issues.
69
+
70
+ - Run full memory check suite (includes native tools): `npm run check:memory`
71
+ - Memory test logic is in `src/test-utils/memory-test-core.ts`
72
+
73
+ **Workaround for Other Tests**:
74
+
75
+ 1. Jest is configured to use single worker mode (`maxWorkers: 1`) for all Windows CI environments
76
+ 2. Tests that stress worker threads or concurrency are skipped on Windows CI using `describeSkipWindowsCI` or `describePlatformStable`:
77
+
78
+ - `worker_threads.test.ts` - Worker thread integration tests
79
+ - `thread_safety.test.ts` - Concurrent operations stress tests
80
+ - `windows-memory-check.test.ts` - Memory leak detection (Windows only)
81
+ - `windows-resource-security.test.ts` - Resource handle leak tests (Windows only)
82
+
83
+ **Note**: These tests pass locally but fail in CI. The native module loads correctly, but Jest's worker process management has fundamental incompatibilities with these specific tests on GitHub Actions Windows runners.
84
+
85
+ ### Build Architecture Issue
86
+
87
+ **Problem**: "No Target Architecture" error from Windows SDK headers when building with node-gyp/prebuildify.
88
+
89
+ **Solution**: Use `scripts/prebuildify-wrapper.ts` which sets the `CL` environment variable with architecture defines:
90
+
91
+ - For x64: `CL=/D_M_X64 /D_WIN64 /D_AMD64_`
92
+ - For ARM64: `CL=/D_M_ARM64 /D_WIN64`
93
+
94
+ **Why This is Necessary**:
95
+
96
+ - Prebuildify doesn't properly pass architecture defines from binding.gyp conditions
97
+ - The Windows SDK requires these macros before including `<windows.h>`
98
+ - Projects like node-sqlite avoid this by not using Windows headers directly
99
+
100
+ **Why Other Approaches Failed**:
101
+
102
+ - **Source file defines**: Would hardcode x64 defines, breaking ARM64 builds
103
+ - **windows_compat.h wrapper**: Can't distinguish x64 from ARM64 at compile time
104
+ - **binding.gyp conditions**: Not evaluated properly by prebuildify
105
+ - **msvs_settings defines**: Not passed through to the compiler
106
+
107
+ ### Memory Testing Limitations
108
+
109
+ Traditional Windows tools **do not work** with Node.js native modules:
110
+
111
+ - **Dr. Memory**: Fails with "Unable to load client library: ucrtbase.dll"
112
+ - **Debug CRT builds**: Cannot be loaded by Node.js (missing debug runtime + UNC path issues)
113
+ - **Visual Leak Detector**: Requires debug builds which don't work
114
+ - **Application Verifier**: Cannot hook into Node.js memory management
115
+
116
+ Use JavaScript-based memory testing (`src/windows-memory-check.test.ts`) instead.
117
+
118
+ ### Static Analysis (clang-tidy) Limitations
119
+
120
+ **clang-tidy on Windows** has limited effectiveness due to MSVC header incompatibility:
121
+
122
+ - Generates many false errors about missing std namespace members
123
+ - Still provides valuable warnings about your code
124
+ - See `doc/windows-clang-tidy.md` for details
125
+ - Consider using Visual Studio Code Analysis as an alternative
126
+
127
+ ### WSL Development
128
+
129
+ Run Windows commands from WSL:
130
+
131
+ ```bash
132
+ cmd.exe /c "cd C:\\Users\\matth\\src\\fs-metadata && npm test"
133
+ # Or create helper: echo 'cmd.exe /c "cd C:\\Users\\matth\\src\\fs-metadata && $@"' > ~/bin/win-run
134
+ ```
135
+
136
+ ## Memory Leak Detection
137
+
138
+ Run `npm run check:memory` for comprehensive platform-specific testing:
139
+
140
+ - **All platforms**: JavaScript memory tests with GC triggers
141
+ - **Windows**: Handle count monitoring via `process.report`
142
+ - **Linux**: Valgrind + AddressSanitizer/LeakSanitizer
143
+ - **macOS**: AddressSanitizer (may fail due to SIP - expected)
144
+
145
+ ## CI/CD Test Reliability
146
+
147
+ ### Critical Anti-Patterns
148
+
149
+ **Never** use these to "fix" async issues:
150
+
151
+ ```javascript
152
+ // BAD: Arbitrary timeouts
153
+ await new Promise((resolve) => setTimeout(resolve, 100));
154
+ // BAD: Forcing GC
155
+ if (global.gc) global.gc();
156
+ // BAD: setImmediate in afterAll
157
+ afterAll(async () => {
158
+ await new Promise((resolve) => setImmediate(resolve));
159
+ });
160
+ ```
161
+
162
+ ### Windows Directory Cleanup
163
+
164
+ Always use retry logic:
165
+
166
+ ```typescript
167
+ await fsp.rm(tempDir, {
168
+ recursive: true,
169
+ force: true,
170
+ maxRetries: process.platform === "win32" ? 3 : 1,
171
+ retryDelay: process.platform === "win32" ? 100 : 0,
172
+ });
173
+ ```
174
+
175
+ ### Platform Performance Multipliers
176
+
177
+ - Alpine Linux (musl): 2x slower
178
+ - ARM64 emulation: 5x slower
179
+ - Windows processes: 4x slower
180
+ - macOS VMs: 4x slower
181
+
182
+ ### Multi-Process Synchronization
183
+
184
+ Use explicit signals:
185
+
186
+ ```javascript
187
+ console.log("READY"); // Signal readiness
188
+ console.log("RESULT:" + outcome); // Signal result
189
+ ```
190
+
191
+ ## Release Process
192
+
193
+ Requires repository secrets:
194
+
195
+ - `NPM_TOKEN`: npm authentication
196
+ - `GPG_PRIVATE_KEY`: ASCII-armored GPG key
197
+ - `GPG_PASSPHRASE`: GPG passphrase
198
+
199
+ Automated via GitHub Actions workflow dispatch or manual:
200
+
201
+ ```bash
202
+ npm run prepare-release
203
+ git config commit.gpgsign true
204
+ npm version patch|minor|major
205
+ npm publish
206
+ git push origin main --follow-tags
207
+ ```
208
+
209
+ ## General guidance
210
+
211
+ Never do inline imports like `const { mkdirSync } = await import("node:fs");` -- just use standard imports.
212
+
213
+ **NEVER** add "Generated with Codex" or "Co-Authored-By: Codex" lines to git commit messages. Keep commits clean and professional.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,52 @@ Fixed for any bug fixes.
14
14
  Security in case of vulnerabilities.
15
15
  -->
16
16
 
17
+ ## 2.0.0 - 2026-06-03
18
+
19
+ ### Changed
20
+
21
+ - **BREAKING: Minimum supported Node.js raised to v22.** `engines.node` is now
22
+ `>=22` (previously `>=20.0.0`). Node.js 20 and 21 are no longer supported. The
23
+ supported matrix is now Node.js 22, 24, and 26. This drop is what makes the
24
+ release a major version; there are no breaking changes to the public runtime
25
+ API.
26
+
27
+ - **Dual type declarations for ESM and CommonJS.** The build now emits separate
28
+ `index.d.cts` and `index.d.mts` declaration files, and the package `exports`
29
+ map points each module system at its matching types. This fixes type
30
+ resolution under `"moduleResolution": "node16"`/`"nodenext"` consumers. The
31
+ build is now verified with [`@arethetypeswrong/cli`](https://arethetypeswrong.github.io/),
32
+ and dual-declaration generation plus export checks (`check:exports`) run as
33
+ part of the precommit/lint pipeline.
34
+
35
+ ### Fixed
36
+
37
+ - **`statAsync` now passes `throwIfNoEntry`** to comply with the updated Node.js
38
+ `fs.stat` signature, avoiding a deprecation/typing mismatch on newer runtimes.
39
+
40
+ ## 1.4.1 - 2026-04-27
41
+
42
+ ### Fixed
43
+
44
+ - **SIGABRT during Node.js environment teardown.** In-flight async workers
45
+ (DiskArbitration/IOKit calls on macOS, and the equivalent paths on Linux
46
+ and Windows) could complete after `node::FreeEnvironment` had begun
47
+ teardown. The default `node-addon-api` completion path then threw a C++
48
+ `Napi::Error` out of a libuv cleanup-hook frame with no catch, causing
49
+ `terminate()` / abort.
50
+
51
+ All async workers now derive from a new `SafeAsyncWorker` base that
52
+ tracks per-env shutdown state via napi instance data plus
53
+ `napi_add_env_cleanup_hook`. During teardown, completion callbacks
54
+ short-circuit and deferred resolve/reject calls are wrapped to swallow
55
+ teardown-time napi failures (the JS-side promise is unobservable at
56
+ that point anyway). Long-running uncancellable native calls
57
+ (`IOServiceGetMatchingService`, drive-status probes) also bail out as
58
+ soon as the shutdown flag flips, so process exit isn't dragged out by
59
+ in-flight work.
60
+
61
+ No public API changes.
62
+
17
63
  ## 1.4.0 - 2026-04-20
18
64
 
19
65
  ### Removed
package/CLAUDE.md CHANGED
@@ -42,7 +42,7 @@ Use `_dirname()` from `./dirname` instead of `__dirname` - works in both CommonJ
42
42
 
43
43
  ### Node.js Version Compatibility
44
44
 
45
- Jest 30 doesn't support Node.js 23. Use Node.js 20, 22, or 24.
45
+ Jest 30 doesn't support Node.js 23. Use Node.js 22, 24, or 26.
46
46
 
47
47
  ## System Volume Detection
48
48