@photostructure/fs-metadata 1.4.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photostructure/fs-metadata",
3
- "version": "1.4.1",
3
+ "version": "2.0.0",
4
4
  "description": "Cross-platform native filesystem metadata retrieval for Node.js",
5
5
  "homepage": "https://photostructure.github.io/fs-metadata/",
6
6
  "types": "./dist/index.d.ts",
@@ -13,7 +13,7 @@
13
13
  "default": "./dist/index.cjs"
14
14
  },
15
15
  "import": {
16
- "types": "./dist/index.d.ts",
16
+ "types": "./dist/index.d.mts",
17
17
  "default": "./dist/index.mjs"
18
18
  }
19
19
  },
@@ -46,6 +46,8 @@
46
46
  "lint:native": "tsx scripts/clang-tidy.ts",
47
47
  "lint:tsc": "tsc --noEmit",
48
48
  "lint:eslint": "eslint",
49
+ "// check:exports": "verify package.json exports + type declarations resolve correctly (arethetypeswrong). Requires `npm run build:dist` first.",
50
+ "check:exports": "attw --pack .",
49
51
  "fmt": "run-p fmt:*",
50
52
  "// fmt:cpp": "on ubuntu: `sudo apt install clang-format`. Note that windows emits `invalid format` with this || true approach, but it works and is better than introducing Yet Another Script Script (like scripts/clang-format.mjs).",
51
53
  "fmt:cpp": "clang-format --style=LLVM -i src/*.cpp src/*/*.cpp src/*/*.h || echo \"problem with clang-format\"",
@@ -68,7 +70,7 @@
68
70
  "access": "public"
69
71
  },
70
72
  "engines": {
71
- "node": ">=20.0.0"
73
+ "node": ">=22"
72
74
  },
73
75
  "os": [
74
76
  "darwin",
@@ -90,32 +92,33 @@
90
92
  "cross-platform"
91
93
  ],
92
94
  "dependencies": {
93
- "node-addon-api": "^8.7.0",
95
+ "node-addon-api": "^8.8.0",
94
96
  "node-gyp-build": "^4.8.4"
95
97
  },
96
98
  "devDependencies": {
99
+ "@arethetypeswrong/cli": "^0.18.3",
97
100
  "@types/jest": "^30.0.0",
98
- "@types/node": "^25.6.0",
101
+ "@types/node": "^25.9.1",
99
102
  "@types/semver": "^7.7.1",
100
103
  "cross-env": "^10.1.0",
101
104
  "del-cli": "^7.0.0",
102
105
  "eslint": "9.39.1",
103
106
  "eslint-plugin-regexp": "^3.1.0",
104
107
  "eslint-plugin-security": "^4.0.0",
105
- "globals": "^17.5.0",
106
- "jest": "^30.3.0",
107
- "jest-environment-node": "^30.3.0",
108
+ "globals": "^17.6.0",
109
+ "jest": "^30.4.2",
110
+ "jest-environment-node": "^30.4.1",
108
111
  "jest-extended": "^7.0.0",
109
112
  "node-gyp": "^12.3.0",
110
- "npm-check-updates": "^22.0.1",
111
- "npm-run-all2": "8.0.4",
113
+ "npm-check-updates": "^22.2.2",
114
+ "npm-run-all2": "9.0.1",
112
115
  "prebuildify": "^6.0.1",
113
116
  "prettier": "^3.8.3",
114
117
  "prettier-plugin-organize-imports": "4.3.0",
115
- "terser": "^5.46.2",
116
- "ts-jest": "^29.4.9",
118
+ "terser": "^5.48.0",
119
+ "ts-jest": "^29.4.11",
117
120
  "tsup": "^8.5.1",
118
- "tsx": "^4.21.0",
121
+ "tsx": "^4.22.4",
119
122
  "typedoc": "^0.28.19",
120
123
  "typescript": "^5.9.3",
121
124
  "typescript-eslint": "^8.57.2"
@@ -7,15 +7,24 @@ import { fileURLToPath } from "node:url";
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
8
  const distDir = join(__dirname, "..", "dist");
9
9
 
10
- // Copy .d.ts to .d.cts for CommonJS type safety
11
- async function createCjsTypes() {
10
+ // Copy .d.ts to .d.cts and .d.mts so each exports condition resolves to a
11
+ // declaration file whose module format matches its JavaScript counterpart.
12
+ //
13
+ // Without a .d.mts file, the `import` condition's types (`index.d.ts`) are
14
+ // interpreted as CommonJS (the package has no `"type": "module"`), while the
15
+ // resolved JavaScript (`index.mjs`) is ESM. That mismatch is the "Masquerading
16
+ // as CJS" / FalseCJS problem reported by arethetypeswrong. A .d.cts pairs with
17
+ // the CJS `.cjs` output; a .d.mts pairs with the ESM `.mjs` output.
18
+ async function createDualTypes() {
12
19
  try {
13
20
  await copyFile(join(distDir, "index.d.ts"), join(distDir, "index.d.cts"));
14
21
  console.log("Created index.d.cts for CommonJS type safety");
22
+ await copyFile(join(distDir, "index.d.ts"), join(distDir, "index.d.mts"));
23
+ console.log("Created index.d.mts for ESM type safety");
15
24
  } catch (error) {
16
- console.error("Error creating .d.cts file:", error);
25
+ console.error("Error creating dual declaration files:", error);
17
26
  process.exit(1);
18
27
  }
19
28
  }
20
29
 
21
- createCjsTypes();
30
+ createDualTypes();
@@ -21,6 +21,11 @@ console.log(`Building for platform: ${currentPlatform}, arch: ${currentArch}`);
21
21
  // Set up environment variables to help node-gyp
22
22
  const env = { ...process.env };
23
23
 
24
+ // Architecture-specific compiler defines for Windows. Tracked in a local so we
25
+ // can log the value we set without reading it back out of the environment
26
+ // (which CodeQL flags as clear-text logging of process environment data).
27
+ let clDefines: string | undefined;
28
+
24
29
  // Set architecture-specific defines for Windows
25
30
  if (currentPlatform === "win32") {
26
31
  // Try various environment variables that might work
@@ -30,9 +35,13 @@ if (currentPlatform === "win32") {
30
35
 
31
36
  // Try setting compiler flags directly
32
37
  if (currentArch === "x64") {
33
- env.CL = "/D_M_X64 /D_WIN64 /D_AMD64_";
38
+ clDefines = "/D_M_X64 /D_WIN64 /D_AMD64_";
34
39
  } else if (currentArch === "arm64") {
35
- env.CL = "/D_M_ARM64 /D_WIN64";
40
+ clDefines = "/D_M_ARM64 /D_WIN64";
41
+ }
42
+
43
+ if (clDefines) {
44
+ env.CL = clDefines;
36
45
  }
37
46
  }
38
47
 
@@ -53,8 +62,8 @@ if (process.argv.length > 2) {
53
62
  }
54
63
 
55
64
  console.log(`Running: prebuildify ${args.join(" ")}`);
56
- if (currentPlatform === "win32" && env.CL) {
57
- console.log(`CL environment variable: ${env.CL}`);
65
+ if (currentPlatform === "win32" && clDefines) {
66
+ console.log(`CL environment variable: ${clDefines}`);
58
67
  }
59
68
 
60
69
  // Spawn prebuildify with the arguments
@@ -36,6 +36,10 @@ run({ cmd: "npm run fmt", desc: "Formatting code" });
36
36
  run({ cmd: "npm run lint", desc: "Running linting checks" });
37
37
  run({ cmd: "npm run docs", desc: "TypeDoc generation" });
38
38
  run({ cmd: "npm run build:dist", desc: "Building distribution files" });
39
+ run({
40
+ cmd: "npm run check:exports",
41
+ desc: "Verifying package exports & type declarations (arethetypeswrong)",
42
+ });
39
43
 
40
44
  // Detect if we're using glibc (vs musl)
41
45
  // Check process.report for musl loader - if not found, assume glibc
@@ -14,7 +14,8 @@ protected:
14
14
 
15
15
  MetadataWorkerBase(const std::string &path,
16
16
  const Napi::Promise::Deferred &deferred)
17
- : SafeAsyncWorker(deferred.Env()), mountPoint(path), deferred_(deferred) {}
17
+ : SafeAsyncWorker(deferred.Env()), mountPoint(path), deferred_(deferred) {
18
+ }
18
19
 
19
20
  void OnError(const Napi::Error &error) override {
20
21
  Napi::HandleScope scope(Env());
@@ -4,9 +4,9 @@
4
4
  // can never escape AsyncWorker callbacks.
5
5
  //
6
6
  // Why this exists: AsyncWorker::OnWorkComplete can run during
7
- // node::FreeEnvironment cleanup. If napi_resolve_deferred / napi_reject_deferred
8
- // fail at that point (env tearing down), node-addon-api throws a C++
9
- // Napi::Error. With NAPI_CPP_EXCEPTIONS the rethrow path inside
7
+ // node::FreeEnvironment cleanup. If napi_resolve_deferred /
8
+ // napi_reject_deferred fail at that point (env tearing down), node-addon-api
9
+ // throws a C++ Napi::Error. With NAPI_CPP_EXCEPTIONS the rethrow path inside
10
10
  // WrapVoidCallback then calls ThrowAsJavaScriptException, which can also fail,
11
11
  // letting the C++ exception escape into a libuv cleanup hook frame that has
12
12
  // no catch - terminate() / SIGABRT.
@@ -150,9 +150,7 @@ protected:
150
150
 
151
151
  void SetError(const std::string &error) { error_ = error; }
152
152
 
153
- bool IsShuttingDown() const {
154
- return FSMeta::IsShuttingDown(shutdownState_);
155
- }
153
+ bool IsShuttingDown() const { return FSMeta::IsShuttingDown(shutdownState_); }
156
154
 
157
155
  private:
158
156
  std::shared_ptr<ShutdownState> shutdownState_;
package/src/fs.ts CHANGED
@@ -10,7 +10,10 @@ import { withTimeout } from "./async";
10
10
  */
11
11
  export async function statAsync(
12
12
  path: PathLike,
13
- options?: StatOptions & { bigint?: false },
13
+ // `throwIfNoEntry?: true` selects the overload that resolves to
14
+ // Promise<Stats> rather than Promise<Stats | undefined>; this wrapper always
15
+ // throws (rather than returning undefined) when the path doesn't exist.
16
+ options?: StatOptions & { bigint?: false; throwIfNoEntry?: true },
14
17
  ): Promise<Stats> {
15
18
  return stat(path, options);
16
19
  }