Skip to content

Commit 54d31af

Browse files
committed
feat(errors): add isError, isErrnoException, errorMessage, errorStack
- `isError(value)` — spec-compliant ES2025 `Error.isError` with an `@@toStringTag`-based shim for older engines. Cross-realm-safe (recognizes Errors from worker threads, vm contexts, iframes). - `isErrorBuiltin` — reference to the native `Error.isError` when available, `undefined` otherwise. Exposed so callers/tests can detect the fast-path. - `isErrorShim` — the fallback. Exported so test suites on engines that ship native can still exercise the shim branch directly. - `isErrnoException(value)` — narrows to `NodeJS.ErrnoException` (an Error with a non-empty uppercase-prefixed `.code` matching libuv's `UV_E*` / Node's `ERR_*` conventions). Cross-realm safe. - `errorMessage(value)` — readable message from any caught value with cause-chain walking via `messageWithCauses`. Falls back to the shared `UNKNOWN_ERROR` sentinel for nullish / empty / opaque values. - `errorStack(value)` — cause-aware stack for Errors, `undefined` otherwise. Safe for `logger.error(msg, { stack: errorStack(e) })`. - Re-export `UNKNOWN_ERROR` from `constants/core` so callers don't need a separate import. Patch pony-cause@2.1.11 to use `isError` internally in `messageWithCauses` / `stackWithCauses` / `findCauseByReference` / `getErrorCause`. Cross-realm Errors previously returned `''` from those helpers because of same-realm `instanceof Error` gates. Sweep socket-lib src/ to use the new helpers where appropriate (performance.ts, github.ts, process-lock.ts, releases/github.ts, dlx/manifest.ts, dlx/package.ts, json/edit.ts, packages/isolation.ts).
1 parent 74a2940 commit 54d31af

13 files changed

Lines changed: 670 additions & 67 deletions

File tree

patches/pony-cause@2.1.11.patch

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
diff --git a/lib/helpers.js b/lib/helpers.js
2+
index 3733564ae016c6c7a37dda78c56b5997cae567cc..249815036733d66425a7c7dc4481bfe193b04eac 100644
3+
--- a/lib/helpers.js
4+
+++ b/lib/helpers.js
5+
@@ -1,5 +1,9 @@
6+
'use strict';
7+
8+
+const isError = typeof Error.isError === 'function'
9+
+ ? Error.isError
10+
+ : (v) => v !== null && typeof v === 'object' && Object.prototype.toString.call(v) === '[object Error]';
11+
+
12+
/**
13+
* @template {Error} T
14+
* @param {unknown} err
15+
@@ -8,7 +12,7 @@
16+
*/
17+
const findCauseByReference = (err, reference) => { // linemod-prefix-with: export
18+
if (!err || !reference) return;
19+
- if (!(err instanceof Error)) return;
20+
+ if (!isError(err)) return;
21+
if (
22+
!(reference.prototype instanceof Error) &&
23+
// @ts-ignore
24+
@@ -49,11 +53,11 @@ const getErrorCause = (err) => { // linemod-prefix-with: export
25+
if (typeof err.cause === 'function') {
26+
const causeResult = err.cause();
27+
28+
- return causeResult instanceof Error
29+
+ return isError(causeResult)
30+
? causeResult
31+
: undefined;
32+
} else {
33+
- return err.cause instanceof Error
34+
+ return isError(err.cause)
35+
? err.cause
36+
: undefined;
37+
}
38+
@@ -68,7 +72,7 @@ const getErrorCause = (err) => { // linemod-prefix-with: export
39+
* @returns {string}
40+
*/
41+
const _stackWithCauses = (err, seen) => {
42+
- if (!(err instanceof Error)) return '';
43+
+ if (!isError(err)) return '';
44+
45+
const stack = err.stack || '';
46+
47+
@@ -105,7 +109,7 @@ const stackWithCauses = (err) => _stackWithCauses(err, new Set()); // linemod-pr
48+
* @returns {string}
49+
*/
50+
const _messageWithCauses = (err, seen, skip) => {
51+
- if (!(err instanceof Error)) return '';
52+
+ if (!isError(err)) return '';
53+
54+
const message = skip ? '' : (err.message || '');
55+
56+
diff --git a/lib/helpers.mjs b/lib/helpers.mjs
57+
index 71cf29eb3054be9885a75ffa625c59209f211504..92be97b04cbf8541c4426bd33ee26cfd9af52c82 100644
58+
--- a/lib/helpers.mjs
59+
+++ b/lib/helpers.mjs
60+
@@ -1,5 +1,9 @@
61+
'use strict';
62+
63+
+const isError = typeof Error.isError === 'function'
64+
+ ? Error.isError
65+
+ : (v) => v !== null && typeof v === 'object' && Object.prototype.toString.call(v) === '[object Error]';
66+
+
67+
/**
68+
* @template {Error} T
69+
* @param {unknown} err
70+
@@ -8,7 +12,7 @@
71+
*/
72+
export const findCauseByReference = (err, reference) => {
73+
if (!err || !reference) return;
74+
- if (!(err instanceof Error)) return;
75+
+ if (!isError(err)) return;
76+
if (
77+
!(reference.prototype instanceof Error) &&
78+
// @ts-ignore
79+
@@ -49,11 +53,11 @@ export const getErrorCause = (err) => {
80+
if (typeof err.cause === 'function') {
81+
const causeResult = err.cause();
82+
83+
- return causeResult instanceof Error
84+
+ return isError(causeResult)
85+
? causeResult
86+
: undefined;
87+
} else {
88+
- return err.cause instanceof Error
89+
+ return isError(err.cause)
90+
? err.cause
91+
: undefined;
92+
}
93+
@@ -68,7 +72,7 @@ export const getErrorCause = (err) => {
94+
* @returns {string}
95+
*/
96+
const _stackWithCauses = (err, seen) => {
97+
- if (!(err instanceof Error)) return '';
98+
+ if (!isError(err)) return '';
99+
100+
const stack = err.stack || '';
101+
102+
@@ -105,7 +109,7 @@ export const stackWithCauses = (err) => _stackWithCauses(err, new Set());
103+
* @returns {string}
104+
*/
105+
const _messageWithCauses = (err, seen, skip) => {
106+
- if (!(err instanceof Error)) return '';
107+
+ if (!isError(err)) return '';
108+
109+
const message = skip ? '' : (err.message || '');
110+

pnpm-lock.yaml

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
trustPolicy: no-downgrade
2-
trustPolicyExclude:
3-
- '@yarnpkg/core@4.5.0'
4-
- '@yarnpkg/libzip@3.2.2'
5-
61
# Register .claude/hooks/* as workspace packages so taze (run via
72
# `pnpm run update`) sees and bumps their package.json manifests
83
# alongside the root. Keeps hook deps in lockstep with the main tree.
@@ -12,11 +7,13 @@ packages:
127
allowBuilds:
138
esbuild: true
149

15-
# Refuse to run if the pnpm version on PATH differs from the packageManager
16-
# field in package.json. Our setup action pins pnpm via external-tools.json;
17-
# any drift should fail fast, not silently auto-download via @pnpm/exe
18-
# (which in rc.5 leaves a placeholder launcher that errors at runtime).
19-
pmOnFail: error
10+
# Wait 7 days (10080 minutes) before installing newly published packages.
11+
minimumReleaseAge: 10080
12+
minimumReleaseAgeExclude:
13+
- '@socketaddon/*'
14+
- '@socketbin/*'
15+
- '@socketregistry/*'
16+
- '@socketsecurity/*'
2017

2118
overrides:
2219
'@inquirer/ansi': 2.0.5
@@ -38,8 +35,8 @@ overrides:
3835
execa: 5.1.1
3936
has-flag: 5.0.1
4037
hosted-git-info: 8.1.0
41-
json-parse-even-better-errors: 5.0.0
4238
isexe: 3.1.1
39+
json-parse-even-better-errors: 5.0.0
4340
lru-cache: 11.2.2
4441
make-fetch-happen: 15.0.5
4542
minimatch: 9.0.6
@@ -53,8 +50,8 @@ overrides:
5350
npm-normalize-package-bin: 5.0.0
5451
npm-package-arg: 12.0.2
5552
npm-pick-manifest: 10.0.0
56-
pacote: 21.5.0
5753
p-map: 7.0.4
54+
pacote: 21.5.0
5855
picomatch: 4.0.4
5956
proc-log: 6.1.0
6057
semver: 7.7.2
@@ -75,13 +72,16 @@ patchedDependencies:
7572
execa@5.1.1: patches/execa@5.1.1.patch
7673
minipass-flush@1.0.7: patches/minipass-flush@1.0.7.patch
7774
minipass-pipeline@1.2.4: patches/minipass-pipeline@1.2.4.patch
78-
node-gyp@11.5.0: patches/node-gyp@11.5.0.patch
7975
minipass-sized@1.0.3: patches/minipass-sized@1.0.3.patch
76+
node-gyp@11.5.0: patches/node-gyp@11.5.0.patch
77+
pony-cause@2.1.11: patches/pony-cause@2.1.11.patch
8078

81-
# Wait 7 days (10080 minutes) before installing newly published packages.
82-
minimumReleaseAge: 10080
83-
minimumReleaseAgeExclude:
84-
- '@socketaddon/*'
85-
- '@socketbin/*'
86-
- '@socketregistry/*'
87-
- '@socketsecurity/*'
79+
# Refuse to run if the pnpm version on PATH differs from the packageManager
80+
# field in package.json. Our setup action pins pnpm via external-tools.json;
81+
# any drift should fail fast, not silently auto-download via @pnpm/exe
82+
# (which in rc.5 leaves a placeholder launcher that errors at runtime).
83+
pmOnFail: error
84+
trustPolicy: no-downgrade
85+
trustPolicyExclude:
86+
- '@yarnpkg/core@4.5.0'
87+
- '@yarnpkg/libzip@3.2.2'

src/dlx/manifest.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function getPath() {
5555
}
5656
return _path as typeof import('node:path')
5757
}
58+
import { errorMessage } from '../errors'
5859
import { readFileUtf8Sync, safeMkdirSync } from '../fs'
5960
import { getDefaultLogger } from '../logger'
6061
import { getSocketDlxDir } from '../paths/socket'
@@ -205,9 +206,7 @@ export class DlxManifest {
205206

206207
return JSON.parse(content) as Record<string, ManifestEntry | StoreRecord>
207208
} catch (error) {
208-
logger.warn(
209-
`Failed to read manifest: ${error instanceof Error ? error.message : String(error)}`,
210-
)
209+
logger.warn(`Failed to read manifest: ${errorMessage(error)}`)
211210
return { __proto__: null } as unknown as Record<
212211
string,
213212
ManifestEntry | StoreRecord
@@ -227,9 +226,7 @@ export class DlxManifest {
227226
try {
228227
safeMkdirSync(manifestDir, { recursive: true })
229228
} catch (error) {
230-
logger.warn(
231-
`Failed to create manifest directory: ${error instanceof Error ? error.message : String(error)}`,
232-
)
229+
logger.warn(`Failed to create manifest directory: ${errorMessage(error)}`)
233230
}
234231

235232
// Write atomically.
@@ -275,9 +272,7 @@ export class DlxManifest {
275272

276273
await this.writeManifest(data)
277274
} catch (error) {
278-
logger.warn(
279-
`Failed to clear cache for ${name}: ${error instanceof Error ? error.message : String(error)}`,
280-
)
275+
logger.warn(`Failed to clear cache for ${name}: ${errorMessage(error)}`)
281276
}
282277
})
283278
}
@@ -292,9 +287,7 @@ export class DlxManifest {
292287
fs.unlinkSync(this.manifestPath)
293288
}
294289
} catch (error) {
295-
logger.warn(
296-
`Failed to clear all cache: ${error instanceof Error ? error.message : String(error)}`,
297-
)
290+
logger.warn(`Failed to clear all cache: ${errorMessage(error)}`)
298291
}
299292
})
300293
}
@@ -337,9 +330,7 @@ export class DlxManifest {
337330
const data = JSON.parse(content) as Record<string, StoreRecord>
338331
return Object.keys(data)
339332
} catch (error) {
340-
logger.warn(
341-
`Failed to get package list: ${error instanceof Error ? error.message : String(error)}`,
342-
)
333+
logger.warn(`Failed to get package list: ${errorMessage(error)}`)
343334
return []
344335
}
345336
}
@@ -390,9 +381,7 @@ export class DlxManifest {
390381
}
391382
}
392383
} catch (error) {
393-
logger.warn(
394-
`Failed to read existing manifest: ${error instanceof Error ? error.message : String(error)}`,
395-
)
384+
logger.warn(`Failed to read existing manifest: ${errorMessage(error)}`)
396385
}
397386

398387
// Update record.
@@ -404,7 +393,7 @@ export class DlxManifest {
404393
safeMkdirSync(manifestDir, { recursive: true })
405394
} catch (error) {
406395
logger.warn(
407-
`Failed to create manifest directory: ${error instanceof Error ? error.message : String(error)}`,
396+
`Failed to create manifest directory: ${errorMessage(error)}`,
408397
)
409398
}
410399

src/dlx/package.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import { WIN32 } from '../constants/platform'
3434
import { SOCKET_LIB_USER_AGENT } from '../constants/socket'
35+
import { isError } from '../errors'
3536
import Arborist from '../external/@npmcli/arborist'
3637
import libnpmexec from '../external/libnpmexec'
3738
import npmPackageArg from '../external/npm-package-arg'
@@ -553,10 +554,7 @@ export async function ensurePackageInstalled(
553554
await arb.reify({ save: true })
554555
} catch (e) {
555556
// Rethrow firewall block errors without wrapping.
556-
if (
557-
e instanceof Error &&
558-
e.message.startsWith('Socket Firewall blocked')
559-
) {
557+
if (isError(e) && e.message.startsWith('Socket Firewall blocked')) {
560558
throw e
561559
}
562560
const code = (e as { code?: string } | null)?.code

0 commit comments

Comments
 (0)