Skip to content

Commit 333fee7

Browse files
authored
chore(nextjs): Improve type safety of #safe-node-apis (#6597)
1 parent eb49928 commit 333fee7

File tree

4 files changed

+47
-20
lines changed

4 files changed

+47
-20
lines changed

.changeset/sharp-suits-lay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/nextjs': patch
3+
---
4+
5+
Add types to safe-node-apis modules.

packages/nextjs/src/server/fs/utils.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,27 @@
22
* Attention: Only import this module when the node runtime is used.
33
* We are using conditional imports to mitigate bundling issues with Next.js server actions on version prior to 14.1.0.
44
*/
5-
// @ts-ignore
65
import nodeRuntime from '#safe-node-apis';
76

8-
const throwMissingFsModule = (module: string) => {
9-
throw new Error(`Clerk: ${module} is missing. This is an internal error. Please contact Clerk's support.`);
10-
};
11-
12-
const nodeFsOrThrow = () => {
13-
if (!nodeRuntime.fs) {
14-
throwMissingFsModule('fs');
7+
// Generic assertion function that acts as a proper type guard
8+
function assertNotNullable<T>(value: T, moduleName: string): asserts value is NonNullable<T> {
9+
if (!value) {
10+
throw new Error(`Clerk: ${moduleName} is missing. This is an internal error. Please contact Clerk's support.`);
1511
}
12+
}
13+
14+
const nodeFsOrThrow = (): NonNullable<typeof nodeRuntime.fs> => {
15+
assertNotNullable(nodeRuntime.fs, 'fs');
1616
return nodeRuntime.fs;
1717
};
1818

19-
const nodePathOrThrow = () => {
20-
if (!nodeRuntime.path) {
21-
throwMissingFsModule('path');
22-
}
19+
const nodePathOrThrow = (): NonNullable<typeof nodeRuntime.path> => {
20+
assertNotNullable(nodeRuntime.path, 'path');
2321
return nodeRuntime.path;
2422
};
2523

26-
const nodeCwdOrThrow = () => {
27-
if (!nodeRuntime.cwd) {
28-
throwMissingFsModule('cwd');
29-
}
24+
const nodeCwdOrThrow = (): NonNullable<typeof nodeRuntime.cwd> => {
25+
assertNotNullable(nodeRuntime.cwd, 'cwd');
3026
return nodeRuntime.cwd;
3127
};
3228

packages/nextjs/src/server/keyless-telemetry.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ function getTelemetryFlagFilePath(): string {
4141
* the event should be fired), false if the file already exists (meaning the event was
4242
* already fired) or if there was an error creating the file
4343
*/
44-
async function tryMarkTelemetryEventAsFired(): Promise<boolean> {
44+
function tryMarkTelemetryEventAsFired(): boolean {
4545
try {
4646
if (canUseKeyless) {
4747
const { mkdirSync, writeFileSync } = nodeFsOrThrow();
4848
const flagFilePath = getTelemetryFlagFilePath();
4949
const flagDirectory = dirname(flagFilePath);
5050

5151
// Ensure the directory exists before attempting to write the file
52-
await mkdirSync(flagDirectory, { recursive: true });
52+
mkdirSync(flagDirectory, { recursive: true });
5353

5454
const flagData = {
5555
firedAt: new Date().toISOString(),
5656
event: EVENT_KEYLESS_ENV_DRIFT_DETECTED,
5757
};
58-
await writeFileSync(flagFilePath, JSON.stringify(flagData, null, 2), { flag: 'wx' });
58+
writeFileSync(flagFilePath, JSON.stringify(flagData, null, 2), { flag: 'wx' });
5959
return true;
6060
} else {
6161
return false;
@@ -177,7 +177,7 @@ export async function detectKeylessEnvDrift(): Promise<void> {
177177
},
178178
});
179179

180-
const shouldFireEvent = await tryMarkTelemetryEventAsFired();
180+
const shouldFireEvent = tryMarkTelemetryEventAsFired();
181181

182182
if (shouldFireEvent) {
183183
// Fire drift detected event only if we successfully created the flag
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Global type declarations for #safe-node-apis conditional import
3+
*/
4+
5+
declare module '#safe-node-apis' {
6+
import type { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
7+
import type * as nodePath from 'node:path';
8+
9+
interface FileSystem {
10+
existsSync: typeof existsSync;
11+
writeFileSync: typeof writeFileSync;
12+
readFileSync: typeof readFileSync;
13+
appendFileSync: typeof appendFileSync;
14+
mkdirSync: typeof mkdirSync;
15+
rmSync: typeof rmSync;
16+
}
17+
18+
interface SafeNodeApis {
19+
fs: FileSystem | undefined;
20+
path: typeof nodePath | undefined;
21+
cwd: (() => string) | undefined;
22+
}
23+
24+
const safeNodeApis: SafeNodeApis;
25+
export = safeNodeApis;
26+
}

0 commit comments

Comments
 (0)