@photostructure/fs-metadata 0.6.1 → 0.7.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/CLAUDE.md +141 -315
  3. package/CODE_OF_CONDUCT.md +11 -11
  4. package/CONTRIBUTING.md +1 -1
  5. package/README.md +34 -103
  6. package/binding.gyp +97 -22
  7. package/claude.sh +23 -0
  8. package/dist/index.cjs +51 -21
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +5 -0
  11. package/dist/index.d.mts +5 -0
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.mjs +51 -21
  14. package/dist/index.mjs.map +1 -1
  15. package/doc/C++_REVIEW_TODO.md +97 -25
  16. package/doc/GPG_RELEASE_HOWTO.md +44 -13
  17. package/doc/MACOS_API_REFERENCE.md +469 -0
  18. package/doc/SECURITY_AUDIT_2025.md +809 -0
  19. package/doc/SSH_RELEASE_HOWTO.md +28 -24
  20. package/doc/WINDOWS_API_REFERENCE.md +422 -0
  21. package/doc/WINDOWS_ARM64_SECURITY.md +161 -0
  22. package/doc/WINDOWS_DEBUG_GUIDE.md +9 -2
  23. package/doc/examples.md +267 -0
  24. package/doc/gotchas.md +297 -0
  25. package/doc/logo.png +0 -0
  26. package/doc/logo.svg +85 -0
  27. package/doc/macos-asan-sip-issue.md +71 -0
  28. package/doc/social.png +0 -0
  29. package/doc/social.svg +125 -0
  30. package/doc/windows-build.md +226 -0
  31. package/doc/windows-clang-tidy.md +72 -0
  32. package/doc/windows-memory-testing.md +108 -0
  33. package/doc/windows-prebuildify-arm64.md +232 -0
  34. package/jest.config.cjs +23 -0
  35. package/package.json +61 -36
  36. package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  37. package/prebuilds/darwin-x64/@photostructure+fs-metadata.glibc.node +0 -0
  38. package/prebuilds/linux-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  39. package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
  40. package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
  41. package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
  42. package/prebuilds/win32-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  43. package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
  44. package/scripts/check-memory.ts +186 -0
  45. package/scripts/clang-tidy.ts +690 -99
  46. package/scripts/install.cjs +42 -0
  47. package/scripts/is-platform.mjs +1 -1
  48. package/scripts/macos-asan.sh +155 -0
  49. package/scripts/post-build.mjs +3 -3
  50. package/scripts/prebuild-linux-glibc.sh +12 -1
  51. package/scripts/prebuildify-wrapper.ts +77 -0
  52. package/scripts/precommit.ts +45 -20
  53. package/scripts/sanitizers-test.sh +1 -1
  54. package/src/common/volume_metadata.h +6 -0
  55. package/src/darwin/hidden.cpp +73 -25
  56. package/src/darwin/path_security.h +149 -0
  57. package/src/darwin/raii_utils.h +104 -4
  58. package/src/darwin/volume_metadata.cpp +132 -58
  59. package/src/darwin/volume_mount_points.cpp +80 -47
  60. package/src/hidden.ts +36 -13
  61. package/src/linux/gio_mount_points.cpp +17 -18
  62. package/src/linux/gio_utils.cpp +92 -37
  63. package/src/linux/gio_utils.h +11 -5
  64. package/src/linux/gio_volume_metadata.cpp +111 -48
  65. package/src/linux/volume_metadata.cpp +67 -4
  66. package/src/object.ts +1 -0
  67. package/src/options.ts +6 -0
  68. package/src/path.ts +11 -0
  69. package/src/remote_info.ts +5 -3
  70. package/src/stack_path.ts +8 -6
  71. package/src/string_enum.ts +1 -0
  72. package/src/test-utils/memory-test-core.ts +336 -0
  73. package/src/test-utils/memory-test-runner.ts +108 -0
  74. package/src/test-utils/platform.ts +46 -1
  75. package/src/test-utils/worker-thread-helper.cjs +154 -27
  76. package/src/types/native_bindings.ts +1 -1
  77. package/src/types/options.ts +6 -0
  78. package/src/windows/drive_status.h +133 -163
  79. package/src/windows/error_utils.h +54 -3
  80. package/src/windows/fs_meta.h +1 -1
  81. package/src/windows/hidden.cpp +60 -43
  82. package/src/windows/security_utils.h +250 -0
  83. package/src/windows/string.h +68 -11
  84. package/src/windows/system_volume.h +1 -1
  85. package/src/windows/thread_pool.h +206 -0
  86. package/src/windows/volume_metadata.cpp +11 -6
  87. package/src/windows/volume_mount_points.cpp +8 -7
  88. package/src/windows/windows_arch.h +39 -0
  89. package/scripts/check-memory.mjs +0 -123
package/doc/gotchas.md ADDED
@@ -0,0 +1,297 @@
1
+ # Gotchas and Platform-Specific Issues
2
+
3
+ This guide covers common issues, platform quirks, and important considerations when using `@photostructure/fs-metadata`.
4
+
5
+ ## Timeout Issues
6
+
7
+ ### Network Volumes Can Hang
8
+
9
+ **Problem**: Linux, macOS, and Windows can block system calls indefinitely when network filesystems are unhealthy.
10
+
11
+ **Solution**: Always use timeouts for network volumes:
12
+
13
+ ```typescript
14
+ // Default timeout may be too short for network drives
15
+ const metadata = await getVolumeMetadata("\\\\nas\\share", {
16
+ timeoutMs: 30000, // 30 seconds
17
+ });
18
+ ```
19
+
20
+ **Why it happens**:
21
+
22
+ - Windows: SMB shares can block when the remote host is down
23
+ - Linux: NFS mounts without `soft` option will retry forever
24
+ - macOS: AFP/SMB shares may hang during network interruptions
25
+
26
+ ### Optical Drives
27
+
28
+ Optical drives (CD/DVD) can take 30+ seconds to spin up:
29
+
30
+ ```typescript
31
+ const metadata = await getVolumeMetadata("D:\\", {
32
+ timeoutMs: 45000, // 45 seconds for optical drives
33
+ });
34
+ ```
35
+
36
+ ## Platform-Specific Gotchas
37
+
38
+ ### Windows
39
+
40
+ #### UNC Paths and Mapped Drives
41
+
42
+ Mapped network drives may not appear in volume listings:
43
+
44
+ ```typescript
45
+ // This might not show mapped drives
46
+ const volumes = await getVolumeMountPoints();
47
+
48
+ // Use UNC path directly instead
49
+ const metadata = await getVolumeMetadata("\\\\server\\share");
50
+ ```
51
+
52
+ #### System Volume Detection
53
+
54
+ `C:\` is both a system volume and user storage. The library returns it in all queries:
55
+
56
+ ```typescript
57
+ const volumes = await getAllVolumeMetadata({ includeSystemVolumes: false });
58
+ // C:\ will still be included on Windows
59
+ ```
60
+
61
+ #### Long Path Support
62
+
63
+ Windows 10+ supports paths longer than 260 characters (MAX_PATH) when long path support is enabled:
64
+
65
+ ```typescript
66
+ // Paths up to 32,768 characters are now supported
67
+ const longPath = "C:\\" + "verylongdirectoryname\\".repeat(20) + "file.txt";
68
+ const metadata = await getVolumeMetadata(longPath);
69
+ ```
70
+
71
+ **Requirements**:
72
+
73
+ - Windows 10 version 1607 or later
74
+ - Long path support enabled in registry or manifest
75
+ - Path must not exceed PATHCCH_MAX_CCH (32,768 wide characters)
76
+
77
+ **Enabling long paths** (administrator required):
78
+
79
+ ```powershell
80
+ # Set registry key
81
+ New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
82
+ -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
83
+ ```
84
+
85
+ **Note**: Even without long path support enabled system-wide, the library handles paths up to 32,768 characters internally and fails gracefully on older systems.
86
+
87
+ #### Build Issues
88
+
89
+ If you see "No Target Architecture" errors when building from source, ensure Visual Studio build tools are properly installed. See [Windows Build Guide](./windows-build.md).
90
+
91
+ ### Linux
92
+
93
+ #### Docker Containers
94
+
95
+ The `node:20` Docker image is **not supported** due to GLIBC version requirements:
96
+
97
+ ```dockerfile
98
+ # ❌ Won't work
99
+ FROM node:20
100
+
101
+ # ✅ Use this instead
102
+ FROM node:20-bullseye
103
+ # or
104
+ FROM debian:bullseye
105
+ RUN apt-get update && apt-get install -y nodejs npm
106
+ ```
107
+
108
+ #### System Volume Filtering
109
+
110
+ Many mount points on Linux are system-only:
111
+
112
+ ```typescript
113
+ // This filters out /proc, /sys, /dev, snap mounts, etc.
114
+ const userVolumes = await getAllVolumeMetadata({ includeSystemVolumes: false });
115
+
116
+ // To see everything:
117
+ const allVolumes = await getAllVolumeMetadata({ includeSystemVolumes: true });
118
+ ```
119
+
120
+ #### GVfs/FUSE Mounts
121
+
122
+ User-mounted volumes (like Google Drive, SMB shares via Nautilus) appear under `/run/user/*/gvfs`:
123
+
124
+ ```typescript
125
+ const volumes = await getVolumeMountPoints();
126
+ // May include entries like:
127
+ // /run/user/1000/gvfs/smb-share:server=nas,share=documents
128
+ ```
129
+
130
+ ### macOS
131
+
132
+ #### APFS Containers
133
+
134
+ APFS volumes in the same container share space:
135
+
136
+ ```typescript
137
+ const volumes = await getAllVolumeMetadata();
138
+ // Multiple volumes might report identical 'available' space
139
+ // because they share the same APFS container
140
+ ```
141
+
142
+ #### System Integrity Protection (SIP)
143
+
144
+ Memory debugging tools like AddressSanitizer may fail due to SIP:
145
+
146
+ ```bash
147
+ # This might not work on macOS with SIP enabled
148
+ ASAN_OPTIONS=detect_leaks=1 npm test
149
+ ```
150
+
151
+ ## Hidden Files Gotchas
152
+
153
+ ### POSIX vs Native Behavior
154
+
155
+ Hidden file operations behave differently per platform:
156
+
157
+ ```typescript
158
+ // On Windows: Sets hidden attribute
159
+ await setHidden("C:\\file.txt", true);
160
+ // File remains at: C:\file.txt (hidden)
161
+
162
+ // On Linux/macOS: Renames file
163
+ await setHidden("/home/user/file.txt", true);
164
+ // File moved to: /home/user/.file.txt
165
+ ```
166
+
167
+ ### Already Hidden Files
168
+
169
+ Setting an already-hidden file to hidden is a no-op:
170
+
171
+ ```typescript
172
+ // No error, no change
173
+ await setHidden("/path/to/.hidden", true);
174
+ ```
175
+
176
+ ### Invalid Paths
177
+
178
+ Dot-prefixing can create invalid paths:
179
+
180
+ ```typescript
181
+ // This will fail - can't hide root directory
182
+ await setHidden("/", true); // Error!
183
+
184
+ // This will fail - parent directory in path
185
+ await setHidden("/path/../file", true); // Error!
186
+ ```
187
+
188
+ ## Memory and Resource Management
189
+
190
+ ### Handle Leaks on Windows
191
+
192
+ Each volume check uses a separate thread on Windows. With many volumes:
193
+
194
+ ```typescript
195
+ // This creates one thread per volume
196
+ const volumes = await getVolumeMountPoints();
197
+
198
+ // For systems with many volumes, use smaller batches
199
+ const BATCH_SIZE = 10;
200
+ for (let i = 0; i < mountPoints.length; i += BATCH_SIZE) {
201
+ const batch = mountPoints.slice(i, i + BATCH_SIZE);
202
+ await Promise.all(batch.map((mp) => getVolumeMetadata(mp.mountPoint)));
203
+ }
204
+ ```
205
+
206
+ ## Testing Gotchas
207
+
208
+ ### File System State
209
+
210
+ Tests can fail due to file system state changes:
211
+
212
+ ```typescript
213
+ // ❌ Bad: Assumes exact values
214
+ expect(metadata.available).toBe(1000000);
215
+
216
+ // ✅ Good: Checks types and ranges
217
+ expect(typeof metadata.available).toBe("number");
218
+ expect(metadata.available).toBeGreaterThanOrEqual(0);
219
+ ```
220
+
221
+ ### Timing Issues
222
+
223
+ File operations may not be immediately visible:
224
+
225
+ ```typescript
226
+ // After creating a file
227
+ await fs.writeFile(path, data);
228
+ // May need a small delay on some systems
229
+ await new Promise((resolve) => setTimeout(resolve, 10));
230
+ const hidden = await isHidden(path);
231
+ ```
232
+
233
+ ### Cross-Platform Testing
234
+
235
+ What works on one platform may fail on another:
236
+
237
+ ```typescript
238
+ // Windows: This is valid
239
+ const metadata = await getVolumeMetadata("C:"); // No trailing slash
240
+
241
+ // Linux/macOS: Must have trailing slash
242
+ const metadata2 = await getVolumeMetadata("/"); // Trailing slash required
243
+ ```
244
+
245
+ ## Error Messages
246
+
247
+ ### Common Errors and Solutions
248
+
249
+ | Error | Cause | Solution |
250
+ | ----------------------------- | ---------------------- | -------------------------------- |
251
+ | `ENOENT` | Path doesn't exist | Check path exists before calling |
252
+ | `EACCES` | Permission denied | Run with appropriate permissions |
253
+ | `ETIMEDOUT` | Operation timed out | Increase `timeoutMs` option |
254
+ | `Invalid mountPoint` | Empty or invalid path | Validate input before calling |
255
+ | `statvfs failed` | Linux filesystem issue | Check mount is accessible |
256
+ | `GetVolumeInformation failed` | Windows API error | Verify drive letter is correct |
257
+
258
+ ### Platform-Specific Error Patterns
259
+
260
+ ```typescript
261
+ try {
262
+ await getVolumeMetadata(mountPoint);
263
+ } catch (error) {
264
+ if (
265
+ process.platform === "win32" &&
266
+ error.message.includes("cannot find the path")
267
+ ) {
268
+ // Windows-specific path error
269
+ } else if (
270
+ process.platform === "linux" &&
271
+ error.message.includes("statvfs")
272
+ ) {
273
+ // Linux-specific filesystem error
274
+ } else if (error.code === "ETIMEDOUT") {
275
+ // Cross-platform timeout
276
+ }
277
+ }
278
+ ```
279
+
280
+ ## Debug Mode
281
+
282
+ Enable debug logging to troubleshoot issues:
283
+
284
+ ```bash
285
+ # Linux/macOS
286
+ NODE_DEBUG=fs-meta npm test
287
+
288
+ # Windows
289
+ set NODE_DEBUG=fs-meta && npm test
290
+ ```
291
+
292
+ Debug output includes:
293
+
294
+ - Native API calls and their results
295
+ - Timeout occurrences
296
+ - Thread creation/destruction (Windows)
297
+ - Memory allocation tracking (debug builds)
package/doc/logo.png ADDED
Binary file
package/doc/logo.svg ADDED
@@ -0,0 +1,85 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="150"
6
+ height="165"
7
+ viewBox="0 0 37.5 41.25"
8
+ version="1.1"
9
+ id="svg1"
10
+ inkscape:version="1.4.2 (7335b4f457, 2025-06-11)"
11
+ sodipodi:docname="logo.svg"
12
+ xml:space="preserve"
13
+ inkscape:export-filename="logo.png"
14
+ inkscape:export-xdpi="734.07092"
15
+ inkscape:export-ydpi="734.07092"
16
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
17
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
18
+ xmlns:xlink="http://www.w3.org/1999/xlink"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
21
+ id="namedview1"
22
+ pagecolor="#505050"
23
+ bordercolor="#ffffff"
24
+ borderopacity="1"
25
+ inkscape:showpageshadow="0"
26
+ inkscape:pageopacity="0"
27
+ inkscape:pagecheckerboard="1"
28
+ inkscape:deskcolor="#505050"
29
+ inkscape:document-units="mm"
30
+ inkscape:zoom="7.6368504"
31
+ inkscape:cx="39.61057"
32
+ inkscape:cy="62.65672"
33
+ inkscape:window-width="3774"
34
+ inkscape:window-height="2075"
35
+ inkscape:window-x="66"
36
+ inkscape:window-y="46"
37
+ inkscape:window-maximized="1"
38
+ inkscape:current-layer="g1"
39
+ showgrid="false" /><defs
40
+ id="defs1"><inkscape:path-effect
41
+ effect="offset"
42
+ id="path-effect14"
43
+ is_visible="true"
44
+ lpeversion="1.3"
45
+ linejoin_type="bevel"
46
+ unit="mm"
47
+ offset="0.65"
48
+ miter_limit="4"
49
+ attempt_force_join="false"
50
+ update_on_knot_move="true" /><linearGradient
51
+ inkscape:collect="always"
52
+ xlink:href="#a"
53
+ id="linearGradient503"
54
+ gradientUnits="userSpaceOnUse"
55
+ gradientTransform="matrix(0.15514043,0.00118797,-6.1719265e-4,0.08060071,59.744983,-84.81034)"
56
+ x1="352.15787"
57
+ y1="1902.979"
58
+ x2="562.99939"
59
+ y2="2219.3118" /><linearGradient
60
+ id="a"><stop
61
+ offset="0"
62
+ stop-color="#4ed8ff"
63
+ id="stop869" /><stop
64
+ offset="1"
65
+ stop-color="#ff14b3"
66
+ id="stop871" /></linearGradient></defs><g
67
+ inkscape:label="Layer 1"
68
+ inkscape:groupmode="layer"
69
+ id="layer1"
70
+ transform="translate(-110.28523,-61.796263)"><g
71
+ id="g1"><path
72
+ style="fill:#ffffff;stroke-width:0.0735"
73
+ d="m 129.89285,100.11556 c 0.16568,-0.0848 3.50065,-2.033848 13.46043,-7.866705 1.2326,-0.721858 1.38946,-0.828757 1.54506,-1.052961 0.25688,-0.370117 0.24282,0.101444 0.2593,-8.69347 l 0.0152,-8.10339 -0.11102,-0.243072 c -0.22445,-0.491384 -0.35893,-0.585758 -3.20734,-2.250908 -0.74956,-0.438185 -2.10682,-1.233241 -3.01614,-1.766785 -9.39657,-5.513485 -9.08628,-5.341062 -9.55645,-5.310353 -0.39795,0.02599 -0.1182,-0.122569 -3.56274,1.89189 -1.58711,0.928187 -4.38553,2.563026 -6.2187,3.632972 -1.83318,1.069955 -3.79609,2.21582 -4.36204,2.546364 -1.10806,0.647167 -1.38937,0.870732 -1.54253,1.225892 l -0.0933,0.216288 -0.007,8.134267 c -0.008,9.14498 -0.0359,8.398632 0.3324,8.808711 0.12048,0.134159 0.37587,0.319901 0.69551,0.505863 0.27782,0.161619 1.06814,0.623956 1.75627,1.027413 9.67576,5.673002 12.46814,7.292594 12.71234,7.373184 0.17446,0.0576 0.7321,0.011 0.90056,-0.0752 z"
74
+ id="path507" /><g
75
+ id="g3"
76
+ transform="translate(-0.18434968,0.2733591)"><g
77
+ id="g2" /></g><path
78
+ style="font-variation-settings:normal;baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:url(#linearGradient503);fill-opacity:1;stroke:none;stroke-width:0.40255;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;paint-order:markers fill stroke;enable-background:accumulate;stop-color:#000000"
79
+ d="m 129.33172,62.474478 c -0.71044,1.1e-5 -1.42066,0.183282 -2.05579,0.549994 l -13.79801,7.966458 c -1.27025,0.7334 -2.05589,2.094602 -2.05587,3.561363 l 2.2e-4,15.932551 c 3e-5,1.466762 0.78586,2.827676 2.05613,3.561036 l 13.79792,7.9661 c 1.27027,0.73336 2.84204,0.73339 4.11228,-10e-6 l 13.79771,-7.966467 c 1.27024,-0.733401 2.05621,-2.094599 2.05619,-3.561361 l -2.2e-4,-15.932551 c -3e-5,-1.466762 -0.78586,-2.827677 -2.05613,-3.561037 L 131.3879,63.024459 c -0.63513,-0.36668 -1.34573,-0.549992 -2.05618,-0.549981 z m 1.5e-4,2.901126 c 0.20823,-1.7e-5 0.41672,0.05417 0.60481,0.162766 l 13.79793,7.966096 c 0.37616,0.217181 0.60465,0.612905 0.60465,1.047267 l 5.2e-4,15.932556 c 3e-5,0.434362 -0.22845,0.829959 -0.60461,1.047145 l -13.79803,7.966771 c -0.37618,0.217183 -0.8331,0.217053 -1.20927,-1.31e-4 L 114.92962,91.53198 c -0.37618,-0.217181 -0.60463,-0.612908 -0.60463,-1.04727 l -2.2e-4,-15.932553 c -3e-5,-0.434362 0.22845,-0.829956 0.60461,-1.047145 l 13.79803,-7.966771 c 0.18804,-0.108591 0.39619,-0.162635 0.60446,-0.162637 z"
80
+ id="path491" /><g
81
+ id="g55"
82
+ transform="translate(379.31824,236.94698)" /><path
83
+ style="fill:#4d4d4d;stroke-width:0.999997"
84
+ d="m 120.85241,90.551072 c -0.45299,-0.09328 -0.79404,-0.422443 -0.89813,-0.866853 -0.0621,-0.26508 -0.0763,-13.953019 -0.0148,-14.285543 0.091,-0.492226 0.41156,-0.824672 0.88082,-0.913448 0.29206,-0.05525 10.78459,-0.03219 10.94812,0.02407 0.63278,0.217685 0.76542,0.561655 0.76542,1.984801 v 1.000462 l -0.29725,0.07246 c -0.51586,0.125741 -1.09108,0.373025 -1.53774,0.661066 l -0.21213,0.136798 -0.19868,-0.281488 c -2.19097,-3.104235 -6.71525,-2.835562 -8.38363,0.49786 -1.00594,2.009853 -0.41679,4.554218 1.38799,5.994355 l 0.1737,0.138609 -0.24672,0.179248 c -1.20959,0.878785 -1.53259,1.219731 -1.64021,1.731391 -0.284,1.350272 1.16088,2.501333 2.37043,1.888405 0.36477,-0.184843 0.53472,-0.388294 1.6257,-1.946225 l 0.73137,-1.044418 0.30973,-0.02175 c 0.77593,-0.05448 1.61755,-0.342932 2.26247,-0.775413 l 0.2174,-0.145789 0.0521,0.101912 c 0.56284,1.100421 1.93935,2.12349 3.18749,2.369046 l 0.19796,0.03895 v 1.226304 c 0,1.693212 -0.0716,1.905324 -0.73912,2.190342 -0.15532,0.06631 -10.62955,0.109256 -10.94229,0.04486 z m 17.99613,-1.323393 c -0.053,-0.01853 -0.14485,-0.06985 -0.20406,-0.114034 -0.0817,-0.06096 -1.98866,-2.182614 -2.75837,-3.068928 l -0.10232,-0.117834 -0.22667,0.124593 c -4.29848,2.362779 -8.65328,-3.174125 -5.28242,-6.71632 3.68598,-3.873346 9.83783,1.322125 6.59037,5.565821 l -0.0899,0.117491 1.22951,1.184015 c 2.02565,1.950702 1.92194,1.831883 1.94623,2.229731 0.0357,0.584343 -0.5328,0.994551 -1.10236,0.795465 z m -4.6792,-3.880733 c 1.65687,-0.408997 2.73353,-2.187831 2.30537,-3.808918 -0.74639,-2.826001 -4.57042,-3.239705 -5.88053,-0.63619 -1.18315,2.351227 1.01018,5.078269 3.57516,4.445108 z m -11.30125,2.827817 c -0.56259,-0.202446 -0.85879,-0.618408 -0.85879,-1.206033 0,-0.606531 0.0238,-0.632744 1.77898,-1.95568 2.55695,-1.92731 2.60031,-1.955752 2.87046,-1.883007 0.4561,0.122816 0.456,0.446978 -3.1e-4,1.102836 -0.17382,0.24981 -0.45815,0.663886 -0.63185,0.92017 -0.98722,1.456598 -1.72916,2.4843 -1.96674,2.724232 -0.33413,0.337426 -0.77492,0.447463 -1.19171,0.297482 z m 2.9654,-5.638293 c -1.49942,-0.347288 -1.98448,-2.183253 -0.84845,-3.211308 1.18818,-1.075228 3.12684,-0.211478 3.12684,1.393126 0,1.168493 -1.14352,2.081037 -2.27839,1.818182 z m 0.68416,-1.236936 c 0.44447,-0.229159 0.52309,-0.764738 0.15917,-1.084261 -0.42752,-0.375368 -1.09629,-0.07738 -1.09123,0.48623 0.005,0.486031 0.51449,0.81333 0.93206,0.598031 z"
85
+ id="path74" /></g></g></svg>
@@ -0,0 +1,71 @@
1
+ # macOS AddressSanitizer and System Integrity Protection (SIP) Issue
2
+
3
+ ## Problem
4
+
5
+ When running tests with AddressSanitizer (ASAN) on macOS, Jest worker processes fail with:
6
+
7
+ ```
8
+ ERROR: Interceptors are not working. This may be because AddressSanitizer is loaded too late (e.g. via dlopen).
9
+ A jest worker process was terminated by another process: signal=SIGABRT, exitCode=null.
10
+ ```
11
+
12
+ ## Root Cause
13
+
14
+ macOS System Integrity Protection (SIP) strips `DYLD_*` environment variables (including `DYLD_INSERT_LIBRARIES`) from child processes for security reasons. Since Jest spawns worker processes to run tests in parallel, these workers don't inherit the ASAN library injection, causing them to abort.
15
+
16
+ ## Verification
17
+
18
+ The native module works correctly with ASAN when called directly:
19
+
20
+ ```bash
21
+ export DYLD_INSERT_LIBRARIES="/Library/Developer/CommandLineTools/usr/lib/clang/17/lib/darwin/libclang_rt.asan_osx_dynamic.dylib"
22
+ node -e "require('./build/Release/fs_metadata.node').getVolumeMountPoints().then(console.log)"
23
+ ```
24
+
25
+ ## Solutions
26
+
27
+ ### 1. Run Tests in Single Process Mode (Recommended)
28
+
29
+ The easiest workaround is to run Jest in single-process mode:
30
+
31
+ ```bash
32
+ npm test -- --runInBand --maxWorkers=1
33
+ ```
34
+
35
+ This has been implemented in `scripts/macos-asan.sh`.
36
+
37
+ ### 2. Static ASAN Linking (Alternative)
38
+
39
+ Instead of relying on dynamic library injection, link ASAN statically. However, this requires careful coordination with Node.js's build configuration.
40
+
41
+ ### 3. Disable SIP (Not Recommended)
42
+
43
+ Temporarily disabling SIP allows DYLD_INSERT_LIBRARIES to work but compromises system security.
44
+
45
+ ### 4. Use Linux for ASAN Testing
46
+
47
+ The Linux ASAN tests in CI don't have this limitation and can catch most memory issues.
48
+
49
+ ## Current Implementation
50
+
51
+ The `scripts/macos-asan.sh` script now:
52
+
53
+ 1. Builds with ASAN flags
54
+ 2. Runs tests with `--runInBand` to avoid worker processes
55
+ 3. Detects the "interceptors not installed" error and treats it as expected behavior
56
+ 4. Falls back to the macOS `leaks` tool for additional memory checking
57
+ 5. Provides clear messaging about the SIP limitation
58
+
59
+ The `scripts/check-memory.mjs` script:
60
+
61
+ 1. Ensures a clean build before running JavaScript memory tests to avoid ASAN contamination
62
+ 2. Clears ASAN environment variables when running tests
63
+ 3. Treats macOS ASAN failures due to SIP as warnings, not errors
64
+ 4. Still runs the `leaks` tool for native memory checking
65
+
66
+ ## CI Considerations
67
+
68
+ - The GitHub Actions workflow runs ASAN tests on both Linux and macOS
69
+ - Linux ASAN tests are more reliable due to no SIP restrictions
70
+ - macOS ASAN failures due to SIP are treated as warnings, not CI failures
71
+ - macOS tests still provide value through the `leaks` tool and JavaScript memory tests
package/doc/social.png ADDED
Binary file
package/doc/social.svg ADDED
@@ -0,0 +1,125 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="1280"
6
+ height="640"
7
+ viewBox="0 0 338.66666 169.33333"
8
+ version="1.1"
9
+ id="svg1"
10
+ inkscape:version="1.4.2 (7335b4f457, 2025-06-11)"
11
+ sodipodi:docname="social.svg"
12
+ inkscape:export-filename="social.png"
13
+ inkscape:export-xdpi="96"
14
+ inkscape:export-ydpi="96"
15
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
16
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
17
+ xmlns:xlink="http://www.w3.org/1999/xlink"
18
+ xmlns="http://www.w3.org/2000/svg"
19
+ xmlns:svg="http://www.w3.org/2000/svg">
20
+ <sodipodi:namedview
21
+ id="namedview1"
22
+ pagecolor="#505050"
23
+ bordercolor="#ffffff"
24
+ borderopacity="1"
25
+ inkscape:showpageshadow="0"
26
+ inkscape:pageopacity="0"
27
+ inkscape:pagecheckerboard="1"
28
+ inkscape:deskcolor="#505050"
29
+ inkscape:document-units="mm"
30
+ inkscape:zoom="1.72549"
31
+ inkscape:cx="497.53983"
32
+ inkscape:cy="397.858"
33
+ inkscape:window-width="3774"
34
+ inkscape:window-height="2075"
35
+ inkscape:window-x="66"
36
+ inkscape:window-y="46"
37
+ inkscape:window-maximized="1"
38
+ inkscape:current-layer="layer1" />
39
+ <defs
40
+ id="defs1">
41
+ <linearGradient
42
+ id="a">
43
+ <stop
44
+ offset="0"
45
+ stop-color="#4ed8ff"
46
+ id="stop869" />
47
+ <stop
48
+ offset="1"
49
+ stop-color="#ff14b3"
50
+ id="stop871" />
51
+ </linearGradient>
52
+ <linearGradient
53
+ inkscape:collect="always"
54
+ xlink:href="#a"
55
+ id="linearGradient503"
56
+ gradientUnits="userSpaceOnUse"
57
+ gradientTransform="matrix(0.39403452,0.00301727,-0.00156758,0.20471429,-6.7202983,-353.74855)"
58
+ x1="352.15787"
59
+ y1="1902.979"
60
+ x2="562.99939"
61
+ y2="2219.3118" />
62
+ </defs>
63
+ <g
64
+ inkscape:label="Layer 1"
65
+ inkscape:groupmode="layer"
66
+ id="layer1">
67
+ <rect
68
+ style="fill:#b3b3b3;stroke-width:0.25;paint-order:markers fill stroke;stroke-dasharray:none"
69
+ id="rect3"
70
+ width="338.73895"
71
+ height="169.20047"
72
+ x="-0.49628502"
73
+ y="0.040662654"
74
+ inkscape:export-filename="social.png"
75
+ inkscape:export-xdpi="251.23711"
76
+ inkscape:export-ydpi="251.23711" />
77
+ <g
78
+ id="g3"
79
+ transform="matrix(2.5048234,0,0,2.5048234,-159.04805,-139.45947)"
80
+ style="stroke-width:0.186306">
81
+ <g
82
+ id="g2"
83
+ style="stroke-width:0.186306" />
84
+ </g>
85
+ <g
86
+ id="g55"
87
+ transform="matrix(2.5048234,0,0,2.5048234,791.53892,453.36615)"
88
+ style="stroke-width:0.186306" />
89
+ <text
90
+ xml:space="preserve"
91
+ style="font-size:14.2942px;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#4d4d4d;stroke-width:0.183461;-inkscape-stroke:none;paint-order:markers fill stroke"
92
+ x="163.786"
93
+ y="141.75613"
94
+ id="text3"><tspan
95
+ sodipodi:role="line"
96
+ id="tspan3"
97
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Roboto Mono';-inkscape-font-specification:'Roboto Mono Bold';text-align:center;text-anchor:middle;fill:#000000;stroke-width:0.183461"
98
+ x="163.786"
99
+ y="141.75613">@photostructure/fs-metadata</tspan></text>
100
+ <path
101
+ style="fill:#ffffff;stroke-width:0.0342997"
102
+ d="m 171.44524,115.93678 c 0.42071,-0.21553 8.89118,-5.16568 34.18759,-19.980303 3.13064,-1.833392 3.52904,-2.104923 3.92422,-2.674382 0.65246,-0.940045 0.6167,0.257435 0.65856,-22.080167 l 0.0381,-20.58144 -0.28193,-0.617353 c -0.57006,-1.248041 -0.91164,-1.487733 -8.1462,-5.717009 -1.90377,-1.112902 -5.351,-3.132213 -7.66056,-4.487364 -23.86594,-14.003467 -23.07785,-13.565556 -24.27201,-13.487563 -1.01074,0.06586 -0.30043,-0.311317 -9.04886,4.805157 -4.031,2.357459 -11.13859,6.509722 -15.79462,9.227209 -4.65598,2.717542 -9.6415,5.62786 -11.07894,6.467434 -2.81432,1.643718 -3.52882,2.211544 -3.9178,3.113599 l -0.23675,0.549321 -0.0163,20.659868 c -0.0218,23.226921 -0.0914,21.33132 0.84425,22.372869 0.30588,0.340707 0.95469,0.812525 1.76651,1.284834 0.70563,0.410371 2.71291,1.58472 4.46069,2.609451 24.57505,14.408609 31.6673,18.522179 32.28753,18.726819 0.44303,0.1464 1.85941,0.0272 2.28725,-0.19104 z"
103
+ id="path507" />
104
+ <g
105
+ id="g3-0"
106
+ transform="matrix(2.5398569,0,0,2.5398569,-158.93223,-137.64812)"
107
+ style="stroke-width:0.183736">
108
+ <g
109
+ id="g2-6"
110
+ style="stroke-width:0.183736" />
111
+ </g>
112
+ <path
113
+ style="font-variation-settings:normal;baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:url(#linearGradient503);fill-opacity:1;stroke:none;stroke-width:0.187855;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;paint-order:markers fill stroke;enable-background:accumulate;stop-color:#000000"
114
+ d="m 170.02004,20.333816 c -1.80438,5.4e-5 -3.60828,0.465341 -5.22141,1.396896 l -35.04497,20.233712 c -3.22626,1.862728 -5.22167,5.319972 -5.22162,9.045319 l 6e-4,40.466391 c 5e-5,3.725401 1.99596,7.181883 5.22228,9.044556 l 35.04469,20.23273 c 3.22632,1.86262 7.21841,1.86273 10.44461,0 l 35.04421,-20.23371 c 3.22626,-1.862727 5.22249,-5.319972 5.22244,-9.045318 l -5.4e-4,-40.466445 c -1.1e-4,-3.725347 -1.99597,-7.181884 -5.22228,-9.044502 L 175.24248,21.730712 c -1.61318,-0.931337 -3.418,-1.396896 -5.22244,-1.396896 z m 3.8e-4,7.368455 c 0.52902,-5.4e-5 1.05842,0.137698 1.53617,0.413637 l 35.04475,20.232733 c 0.9554,0.551607 1.53574,1.556691 1.53574,2.659904 l 0.001,40.466391 c 5e-5,1.103214 -0.58024,2.108026 -1.53563,2.659632 l -35.04502,20.234472 c -0.95545,0.55161 -2.11598,0.55128 -3.07137,-3.8e-4 L 133.4408,94.135929 c -0.95544,-0.551607 -1.53568,-1.55669 -1.53568,-2.659904 l -5.4e-4,-40.466391 c -1.1e-4,-1.103214 0.58018,-2.107971 1.53557,-2.659632 l 35.04502,-20.23442 c 0.47786,-0.27594 1.00629,-0.413093 1.53525,-0.413093 z"
115
+ id="path491" />
116
+ <g
117
+ id="g55-3"
118
+ transform="matrix(2.5398569,0,0,2.5398569,804.95006,463.469)"
119
+ style="stroke-width:0.183736" />
120
+ <path
121
+ style="fill:#4d4d4d;stroke-width:0.466662"
122
+ d="m 148.48381,91.644364 c -1.15051,-0.236753 -2.01676,-1.072953 -2.2811,-2.201692 -0.15784,-0.673249 -0.19376,-35.438686 -0.0381,-36.283214 0.23131,-1.250218 1.0453,-2.094582 2.23718,-2.320068 0.74177,-0.140419 27.39132,-0.08164 27.80664,0.06096 1.6072,0.552858 1.94404,1.426503 1.94404,5.041093 v 2.541038 l -0.75494,0.18396 c -1.3102,0.31948 -2.77121,0.947446 -3.90566,1.67904 l -0.53882,0.347237 -0.50453,-0.714939 c -5.56472,-7.884304 -17.05578,-7.201912 -21.29322,1.264478 -2.55491,5.104717 -1.05858,11.567035 3.52533,15.224785 l 0.4414,0.352136 -0.62666,0.455 c -3.07218,2.231954 -3.89254,3.097925 -4.16587,4.397453 -0.72136,3.429487 2.94847,6.35303 6.02054,4.796285 0.92644,-0.469695 1.35809,-0.986198 4.12902,-4.943126 l 1.85756,-2.652666 0.78667,-0.05497 c 1.97077,-0.138242 4.10835,-0.871033 5.7464,-1.969457 l 0.55215,-0.370097 0.13226,0.259068 c 1.42955,2.794881 4.92566,5.393338 8.09575,6.017005 l 0.50289,0.09906 v 3.114633 c 0,4.30052 -0.18178,4.839228 -1.8772,5.563147 -0.39459,0.168176 -26.99755,0.277572 -27.7919,0.11375 z m 45.7076,-3.361237 c -0.13443,-0.04898 -0.36792,-0.177428 -0.51813,-0.289546 -0.20736,-0.155114 -5.05089,-5.543553 -7.00587,-7.794665 l -0.25961,-0.299342 -0.57572,0.316214 c -10.91752,6.001167 -21.97812,-8.061787 -13.41659,-17.058449 9.36186,-9.83776 24.98667,3.357972 16.73859,14.136375 l -0.22859,0.298255 3.12274,3.007196 c 5.14488,4.954502 4.88146,4.652764 4.94318,5.663236 0.0909,1.484141 -1.35325,2.526017 -2.79983,2.020345 z m -11.8845,-9.856482 c 4.20822,-1.038828 6.94279,-5.55678 5.85531,-9.674102 -1.89571,-7.177638 -11.60818,-8.228439 -14.93568,-1.615851 -3.00507,5.971777 2.5657,12.898076 9.08037,11.289953 z m -28.70353,7.182209 c -1.4289,-0.514325 -2.18123,-1.570623 -2.18123,-3.063146 0,-1.540471 0.0604,-1.607089 4.51839,-4.967128 6.49427,-4.895069 6.60437,-4.967347 7.29052,-4.782571 1.15845,0.311861 1.15818,1.135271 -7.6e-4,2.801031 -0.4414,0.634498 -1.16363,1.68617 -1.60481,2.337104 -2.5074,3.699549 -4.39184,6.309762 -4.99526,6.91917 -0.84861,0.85699 -1.96816,1.136468 -3.02674,0.75554 z m 7.53168,-14.320443 c -3.80829,-0.882081 -5.04028,-5.545132 -2.15494,-8.156271 3.01781,-2.73093 7.94172,-0.537184 7.94172,3.538339 0,2.967847 -2.90438,5.285575 -5.78678,4.617932 z m 1.73766,-3.141628 c 1.1289,-0.582031 1.32859,-1.942353 0.40438,-2.753898 -1.08585,-0.953379 -2.78443,-0.196478 -2.77158,1.234979 0.0109,1.234434 1.30676,2.065736 2.36731,1.518919 z"
123
+ id="path74" />
124
+ </g>
125
+ </svg>