@helpers4/node 3.1.1 → 3.1.3
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/README.md +4 -0
- package/lib/index.js +34 -27
- package/lib/index.js.map +1 -1
- package/llms.txt +1 -1
- package/meta/api.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ A set of helpers for working with URLs.
|
|
|
30
30
|
- [function](../function)
|
|
31
31
|
- [guard](../guard)
|
|
32
32
|
- [id](../id)
|
|
33
|
+
- [json](../json)
|
|
34
|
+
- [license](../license)
|
|
33
35
|
- [map](../map)
|
|
34
36
|
- [markdown](../markdown)
|
|
35
37
|
- [number](../number)
|
|
@@ -57,6 +59,8 @@ A set of helpers for working with URLs.
|
|
|
57
59
|
| function | [@helpers4/function](https://www.npmjs.com/package/@helpers4/function) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/function) | Function utilities and type guards |
|
|
58
60
|
| guard | [@helpers4/guard](https://www.npmjs.com/package/@helpers4/guard) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/guard) | Runtime type guard utilities |
|
|
59
61
|
| id | [@helpers4/id](https://www.npmjs.com/package/@helpers4/id) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/id) | Unique identifier generation utilities |
|
|
62
|
+
| json | [@helpers4/json](https://www.npmjs.com/package/@helpers4/json) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/json) | JSON parsing and serialization, with its common variants |
|
|
63
|
+
| license | [@helpers4/license](https://www.npmjs.com/package/@helpers4/license) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/license) | License string classification and comparison |
|
|
60
64
|
| map | [@helpers4/map](https://www.npmjs.com/package/@helpers4/map) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/map) | Native Map manipulation utilities |
|
|
61
65
|
| markdown | [@helpers4/markdown](https://www.npmjs.com/package/@helpers4/markdown) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/markdown) | Markdown formatting and escaping utilities |
|
|
62
66
|
| node | [@helpers4/node](https://www.npmjs.com/package/@helpers4/node) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/node) | Node.js-specific utilities |
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
4
5
|
//#region helpers/node/isBuffer.ts
|
|
5
6
|
/**
|
|
6
7
|
* This file is part of helpers4.
|
|
@@ -80,27 +81,27 @@ function isSharedArrayBuffer(value) {
|
|
|
80
81
|
return typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer;
|
|
81
82
|
}
|
|
82
83
|
//#endregion
|
|
83
|
-
//#region helpers/
|
|
84
|
-
var import___vite_browser_external = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
85
|
-
module.exports = {};
|
|
86
|
-
})))();
|
|
84
|
+
//#region helpers/_shared/_stripJsonComments.ts
|
|
87
85
|
/**
|
|
88
86
|
* This file is part of helpers4.
|
|
89
87
|
* Copyright (C) 2025 baxyz
|
|
90
88
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
91
89
|
*
|
|
92
|
-
* Internal helper used by safeReadJsonFile
|
|
93
|
-
* line and block comments, plus trailing commas) — the dialect used by
|
|
94
|
-
*
|
|
95
|
-
*
|
|
90
|
+
* Internal helper used by `node/safeReadJsonFile` and `json/parse` to support JSONC (JSON with
|
|
91
|
+
* line and block comments, plus trailing commas) — the dialect used by tsconfig.json, VS Code's
|
|
92
|
+
* settings.json, etc. Not exported from any package barrel — tests live in
|
|
93
|
+
* _stripJsonComments.test.ts.
|
|
96
94
|
*/
|
|
97
95
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
96
|
+
* Blanks out line comments (starting with two slashes), block comments (slash-star ... star-slash),
|
|
97
|
+
* and trailing commas (before `}` or `]`) in a JSONC string, so the result can be passed to
|
|
98
|
+
* `JSON.parse`. String literals are tracked so a comment-like or comma character occurring
|
|
99
|
+
* *inside* a JSON string (e.g. a URL) is left untouched — this is a string-boundary-aware pass,
|
|
100
|
+
* not a naive regex strip.
|
|
101
|
+
*
|
|
102
|
+
* Everything removed is replaced by spaces (line breaks inside a block comment are kept), so the
|
|
103
|
+
* output has exactly the same length and line layout as the input: a position `JSON.parse`
|
|
104
|
+
* reports in an error on the output points at the same place in the original JSONC text.
|
|
104
105
|
* @ignore
|
|
105
106
|
*/
|
|
106
107
|
function stripJsonComments(input) {
|
|
@@ -108,10 +109,12 @@ function stripJsonComments(input) {
|
|
|
108
109
|
let inString = false;
|
|
109
110
|
let pendingComma = "";
|
|
110
111
|
const flushPendingComma = () => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
output += pendingComma;
|
|
113
|
+
pendingComma = "";
|
|
114
|
+
};
|
|
115
|
+
const emitFiller = (text) => {
|
|
116
|
+
if (pendingComma) pendingComma += text;
|
|
117
|
+
else output += text;
|
|
115
118
|
};
|
|
116
119
|
let i = 0;
|
|
117
120
|
while (i < input.length) {
|
|
@@ -133,14 +136,18 @@ function stripJsonComments(input) {
|
|
|
133
136
|
continue;
|
|
134
137
|
}
|
|
135
138
|
if (char === "/" && input[i + 1] === "/") {
|
|
139
|
+
const start = i;
|
|
136
140
|
i += 2;
|
|
137
141
|
while (i < input.length && input[i] !== "\n" && input[i] !== "\r") i++;
|
|
142
|
+
emitFiller(" ".repeat(i - start));
|
|
138
143
|
continue;
|
|
139
144
|
}
|
|
140
145
|
if (char === "/" && input[i + 1] === "*") {
|
|
146
|
+
const start = i;
|
|
141
147
|
i += 2;
|
|
142
148
|
while (i < input.length && !(input[i] === "*" && input[i + 1] === "/")) i++;
|
|
143
|
-
i
|
|
149
|
+
i = Math.min(i + 2, input.length);
|
|
150
|
+
emitFiller(input.slice(start, i).replace(/[^\r\n]/g, " "));
|
|
144
151
|
continue;
|
|
145
152
|
}
|
|
146
153
|
if (char === ",") {
|
|
@@ -150,14 +157,14 @@ function stripJsonComments(input) {
|
|
|
150
157
|
continue;
|
|
151
158
|
}
|
|
152
159
|
if (char === "}" || char === "]") {
|
|
160
|
+
output += pendingComma.replace(",", " ");
|
|
153
161
|
pendingComma = "";
|
|
154
162
|
output += char;
|
|
155
163
|
i++;
|
|
156
164
|
continue;
|
|
157
165
|
}
|
|
158
166
|
if (char === " " || char === " " || char === "\n" || char === "\r") {
|
|
159
|
-
|
|
160
|
-
else output += char;
|
|
167
|
+
emitFiller(char);
|
|
161
168
|
i++;
|
|
162
169
|
continue;
|
|
163
170
|
}
|
|
@@ -177,7 +184,7 @@ function stripJsonComments(input) {
|
|
|
177
184
|
*/
|
|
178
185
|
function safeReadJsonFile(filePath, fallback) {
|
|
179
186
|
try {
|
|
180
|
-
const content =
|
|
187
|
+
const content = readFileSync(filePath, "utf-8");
|
|
181
188
|
try {
|
|
182
189
|
return JSON.parse(content);
|
|
183
190
|
} catch {
|
|
@@ -210,11 +217,11 @@ function safeReadJsonFile(filePath, fallback) {
|
|
|
210
217
|
* @since 3.1.1
|
|
211
218
|
*/
|
|
212
219
|
async function withTempDir(prefix, fn) {
|
|
213
|
-
const dir = await
|
|
220
|
+
const dir = await mkdtemp(join(tmpdir(), `${prefix}-`));
|
|
214
221
|
try {
|
|
215
222
|
return await fn(dir);
|
|
216
223
|
} finally {
|
|
217
|
-
await
|
|
224
|
+
await rm(dir, {
|
|
218
225
|
recursive: true,
|
|
219
226
|
force: true
|
|
220
227
|
});
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../helpers/node/isBuffer.ts","../../../helpers/node/isNodeStream.ts","../../../helpers/node/isSharedArrayBuffer.ts","../../../__vite-browser-external","../../../helpers/node/_stripJsonComments.ts","../../../helpers/node/safeReadJsonFile.ts","../../../helpers/node/withTempDir.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a Node.js Buffer instance.\n *\n * `Buffer` extends `Uint8Array` and is specific to Node.js, Bun, and Deno.\n * In browser-only environments where `Buffer` is not defined, this function\n * always returns `false`.\n *\n * Useful for filtering or type-narrowing in a functional pipeline:\n * `values.filter(isBuffer)`\n *\n * @param value - The value to check\n * @returns True if value is a Buffer\n * @example\n * isBuffer(Buffer.from('hello')) // => true\n * isBuffer(new Uint8Array(8)) // => false\n * isBuffer('hello') // => false\n * @since 2.0.0\n */\nexport function isBuffer(value: unknown): value is Buffer {\n return typeof Buffer !== 'undefined' && value instanceof Buffer;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a Node.js stream (has a `.pipe()` method).\n *\n * Uses duck-typing: any object with a `pipe` function qualifies, covering\n * `Readable`, `Writable`, `Duplex`, `Transform`, and custom stream-compatible\n * objects without importing from `node:stream`.\n *\n * @param value - The value to check\n * @returns `true` if value is a Node.js stream\n * @example\n * import { Readable } from 'node:stream';\n * isNodeStream(new Readable()) // => true\n * isNodeStream({}) // => false\n * isNodeStream(null) // => false\n * @since 2.0.3\n */\nexport function isNodeStream(value: unknown): value is { pipe: (...args: unknown[]) => unknown } {\n return (\n // Intentionally `object`-only, unlike `isPromiseLike` (see type/isPromiseLike.ts):\n // a callable function exposing `.pipe()` would be an unusual, contrived stream shape.\n value !== null &&\n typeof value === 'object' &&\n typeof (value as Record<string, unknown>)['pipe'] === 'function'\n );\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a `SharedArrayBuffer` instance.\n *\n * `SharedArrayBuffer` enables shared memory between the main thread and worker\n * threads. In browsers without COOP/COEP headers, `SharedArrayBuffer` may be\n * unavailable; this function returns `false` in that case.\n *\n * @param value - The value to check\n * @returns `true` if value is a SharedArrayBuffer\n * @example\n * isSharedArrayBuffer(new SharedArrayBuffer(8)) // => true\n * isSharedArrayBuffer(new ArrayBuffer(8)) // => false\n * isSharedArrayBuffer(null) // => false\n * @since 2.0.3\n */\nexport function isSharedArrayBuffer(value: unknown): value is SharedArrayBuffer {\n return typeof SharedArrayBuffer !== 'undefined' && value instanceof SharedArrayBuffer;\n}\n","module.exports = {}","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n *\n * Internal helper used by safeReadJsonFile.ts to support JSONC (JSON with\n * line and block comments, plus trailing commas) — the dialect used by\n * tsconfig.json, VS Code's settings.json, etc. Not exported from the package\n * barrel — tests live in _stripJsonComments.test.ts.\n */\n\n/**\n * Strips line comments (starting with two slashes), block comments (slash-star\n * ... star-slash), and trailing commas (before `}` or `]`) from a JSONC\n * string, so the result can be safely passed to `JSON.parse`. String literals\n * are tracked so a comment-like or comma character occurring *inside* a JSON\n * string (e.g. a URL) is left untouched — this is a string-boundary-aware\n * pass, not a naive regex strip.\n * @ignore\n */\nexport function stripJsonComments(input: string): string {\n let output = '';\n let inString = false;\n // A comma seen outside a string is held back instead of emitted immediately:\n // if the next significant token turns out to be `}`/`]`, it was a trailing\n // comma and gets dropped; otherwise it (and any whitespace/comments held\n // alongside it) is flushed once a real token confirms it wasn't trailing.\n let pendingComma = '';\n const flushPendingComma = () => {\n if (pendingComma) {\n output += pendingComma;\n pendingComma = '';\n }\n };\n\n let i = 0;\n while (i < input.length) {\n const char = input[i];\n\n if (inString) {\n output += char;\n if (char === '\\\\') {\n // Copy the escaped character as-is (whatever it is) so an escaped\n // quote (`\\\"`) doesn't get mistaken for the string's end.\n i++;\n if (i < input.length) output += input[i];\n } else if (char === '\"') {\n inString = false;\n }\n i++;\n continue;\n }\n\n if (char === '\"') {\n flushPendingComma();\n inString = true;\n output += char;\n i++;\n continue;\n }\n\n if (char === '/' && input[i + 1] === '/') {\n i += 2;\n // Stop at any line-ending character, not just \\n — a bare \\r (classic\n // Mac line endings, or a lone \\r before a later \\n) also ends the line,\n // otherwise everything up to the next \\n (or EOF) gets silently eaten.\n while (i < input.length && input[i] !== '\\n' && input[i] !== '\\r') i++;\n continue;\n }\n\n if (char === '/' && input[i + 1] === '*') {\n i += 2;\n while (i < input.length && !(input[i] === '*' && input[i + 1] === '/')) i++;\n i += 2;\n continue;\n }\n\n if (char === ',') {\n flushPendingComma();\n pendingComma = char;\n i++;\n continue;\n }\n\n if (char === '}' || char === ']') {\n pendingComma = ''; // drop — it was trailing\n output += char;\n i++;\n continue;\n }\n\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') {\n // Whitespace doesn't resolve a pending comma either way — buffer it\n // alongside so it's dropped too if the comma turns out to be trailing.\n if (pendingComma) {\n pendingComma += char;\n } else {\n output += char;\n }\n i++;\n continue;\n }\n\n flushPendingComma();\n output += char;\n i++;\n }\n\n flushPendingComma();\n return output;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { readFileSync } from 'node:fs';\nimport { stripJsonComments } from './_stripJsonComments';\n\n/**\n * Reads a file and parses its contents as JSON, returning `null` (or a\n * fallback) on any failure — a missing/unreadable file and invalid content\n * are both treated the same way.\n *\n * Also tolerates JSONC (line/block comments and trailing commas — the\n * dialect used by `tsconfig.json`, VS Code's `settings.json`, etc.): strict\n * `JSON.parse` is tried first, and only on failure is the content re-parsed\n * with comments/trailing commas stripped, so plain JSON pays no extra cost.\n *\n * Unlike `readFileSync` + `JSON.parse`, this never throws.\n *\n * @param filePath - Path to the JSON (or JSONC) file to read.\n * @param fallback - Value returned on failure. Defaults to `null` when omitted.\n * @returns The parsed value typed as `T`, or `fallback` on failure.\n * @example\n * safeReadJsonFile<{ name: string }>('./package.json') // => { name: 'helpers4', ... }\n * safeReadJsonFile('./missing.json') // => null\n * safeReadJsonFile('./missing.json', {}) // => {}\n * @example\n * // tsconfig.json-style JSONC (comments + trailing comma) also parses:\n * safeReadJsonFile('./tsconfig.json') // => { compilerOptions: { strict: true }, ... }\n * @since 3.0.6\n */\nexport function safeReadJsonFile<T>(filePath: string): T | null;\nexport function safeReadJsonFile<T>(filePath: string, fallback: T): T;\nexport function safeReadJsonFile<T>(filePath: string, fallback?: T): T | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n try {\n return JSON.parse(content) as T;\n } catch {\n // Not strict JSON — retry tolerating JSONC comments/trailing commas\n // before giving up.\n return JSON.parse(stripJsonComments(content)) as T;\n }\n } catch {\n // Distinguish \"no fallback provided\" (→ null) from \"fallback is undefined\"\n // (→ undefined as T) to keep runtime behaviour aligned with the overloads.\n return arguments.length < 2 ? null : (fallback as T);\n }\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Creates a fresh temporary directory under the OS temp root, runs `fn` with its path, and\n * always removes it (recursively) afterward — including when `fn` throws.\n * @param prefix - Prepended to the generated directory name, to make it identifiable\n * @param fn - Receives the temp directory's absolute path; its (possibly synchronous) return\n * value is returned\n * @returns Whatever `fn` returns or resolves to\n * @example\n * await withTempDir('my-tool', async (dir) => {\n * await writeFile(`${dir}/output.txt`, 'data');\n * return readFile(`${dir}/output.txt`, 'utf-8');\n * });\n * // => 'data' (the directory no longer exists once this resolves)\n * @since 3.1.1\n */\nexport async function withTempDir<T>(prefix: string, fn: (dir: string) => T | Promise<T>): Promise<T> {\n const dir = await mkdtemp(join(tmpdir(), `${prefix}-`));\n try {\n return await fn(dir);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,SAAS,OAAiC;CACxD,OAAO,OAAO,WAAW,eAAe,iBAAiB;AAC3D;;;;;;;;;;;;;;;;;;;;;;;;ACJA,SAAgB,aAAa,OAAoE;CAC/F,OAGE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAAkC,YAAY;AAE1D;;;;;;;;;;;;;;;;;;;;;;;ACTA,SAAgB,oBAAoB,OAA4C;CAC9E,OAAO,OAAO,sBAAsB,eAAe,iBAAiB;AACtE;;;;CCvBA,OAAO,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;ACoBlB,SAAgB,kBAAkB,OAAuB;CACvD,IAAI,SAAS;CACb,IAAI,WAAW;CAKf,IAAI,eAAe;CACnB,MAAM,0BAA0B;EAC9B,IAAI,cAAc;GAChB,UAAU;GACV,eAAe;EACjB;CACF;CAEA,IAAI,IAAI;CACR,OAAO,IAAI,MAAM,QAAQ;EACvB,MAAM,OAAO,MAAM;EAEnB,IAAI,UAAU;GACZ,UAAU;GACV,IAAI,SAAS,MAAM;IAGjB;IACA,IAAI,IAAI,MAAM,QAAQ,UAAU,MAAM;GACxC,OAAO,IAAI,SAAS,MAClB,WAAW;GAEb;GACA;EACF;EAEA,IAAI,SAAS,MAAK;GAChB,kBAAkB;GAClB,WAAW;GACX,UAAU;GACV;GACA;EACF;EAEA,IAAI,SAAS,OAAO,MAAM,IAAI,OAAO,KAAK;GACxC,KAAK;GAIL,OAAO,IAAI,MAAM,UAAU,MAAM,OAAO,QAAQ,MAAM,OAAO,MAAM;GACnE;EACF;EAEA,IAAI,SAAS,OAAO,MAAM,IAAI,OAAO,KAAK;GACxC,KAAK;GACL,OAAO,IAAI,MAAM,UAAU,EAAE,MAAM,OAAO,OAAO,MAAM,IAAI,OAAO,MAAM;GACxE,KAAK;GACL;EACF;EAEA,IAAI,SAAS,KAAK;GAChB,kBAAkB;GAClB,eAAe;GACf;GACA;EACF;EAEA,IAAI,SAAS,OAAO,SAAS,KAAK;GAChC,eAAe;GACf,UAAU;GACV;GACA;EACF;EAEA,IAAI,SAAS,OAAO,SAAS,OAAQ,SAAS,QAAQ,SAAS,MAAM;GAGnE,IAAI,cACF,gBAAgB;QAEhB,UAAU;GAEZ;GACA;EACF;EAEA,kBAAkB;EAClB,UAAU;EACV;CACF;CAEA,kBAAkB;CAClB,OAAO;AACT;;;;;;;;AC3EA,SAAgB,iBAAoB,UAAkB,UAAwB;CAC5E,IAAI;EACF,MAAM,WAAA,GAAU,+BAAA,aAAA,CAAa,UAAU,OAAO;EAC9C,IAAI;GACF,OAAO,KAAK,MAAM,OAAO;EAC3B,QAAQ;GAGN,OAAO,KAAK,MAAM,kBAAkB,OAAO,CAAC;EAC9C;CACF,QAAQ;EAGN,OAAO,UAAU,SAAS,IAAI,OAAQ;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;;;ACzBA,eAAsB,YAAe,QAAgB,IAAiD;CACpG,MAAM,MAAM,OAAA,GAAM,+BAAA,QAAA,EAAA,GAAQ,+BAAA,KAAA,EAAA,GAAK,+BAAA,OAAA,CAAO,GAAG,GAAG,OAAO,EAAE,CAAC;CACtD,IAAI;EACF,OAAO,MAAM,GAAG,GAAG;CACrB,UAAU;EACR,OAAA,GAAM,+BAAA,GAAA,CAAG,KAAK;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CAChD;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../helpers/node/isBuffer.ts","../../../helpers/node/isNodeStream.ts","../../../helpers/node/isSharedArrayBuffer.ts","../../../helpers/_shared/_stripJsonComments.ts","../../../helpers/node/safeReadJsonFile.ts","../../../helpers/node/withTempDir.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a Node.js Buffer instance.\n *\n * `Buffer` extends `Uint8Array` and is specific to Node.js, Bun, and Deno.\n * In browser-only environments where `Buffer` is not defined, this function\n * always returns `false`.\n *\n * Useful for filtering or type-narrowing in a functional pipeline:\n * `values.filter(isBuffer)`\n *\n * @param value - The value to check\n * @returns True if value is a Buffer\n * @example\n * isBuffer(Buffer.from('hello')) // => true\n * isBuffer(new Uint8Array(8)) // => false\n * isBuffer('hello') // => false\n * @since 2.0.0\n */\nexport function isBuffer(value: unknown): value is Buffer {\n return typeof Buffer !== 'undefined' && value instanceof Buffer;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a Node.js stream (has a `.pipe()` method).\n *\n * Uses duck-typing: any object with a `pipe` function qualifies, covering\n * `Readable`, `Writable`, `Duplex`, `Transform`, and custom stream-compatible\n * objects without importing from `node:stream`.\n *\n * @param value - The value to check\n * @returns `true` if value is a Node.js stream\n * @example\n * import { Readable } from 'node:stream';\n * isNodeStream(new Readable()) // => true\n * isNodeStream({}) // => false\n * isNodeStream(null) // => false\n * @since 2.0.3\n */\nexport function isNodeStream(value: unknown): value is { pipe: (...args: unknown[]) => unknown } {\n return (\n // Intentionally `object`-only, unlike `isPromiseLike` (see type/isPromiseLike.ts):\n // a callable function exposing `.pipe()` would be an unusual, contrived stream shape.\n value !== null &&\n typeof value === 'object' &&\n typeof (value as Record<string, unknown>)['pipe'] === 'function'\n );\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a `SharedArrayBuffer` instance.\n *\n * `SharedArrayBuffer` enables shared memory between the main thread and worker\n * threads. In browsers without COOP/COEP headers, `SharedArrayBuffer` may be\n * unavailable; this function returns `false` in that case.\n *\n * @param value - The value to check\n * @returns `true` if value is a SharedArrayBuffer\n * @example\n * isSharedArrayBuffer(new SharedArrayBuffer(8)) // => true\n * isSharedArrayBuffer(new ArrayBuffer(8)) // => false\n * isSharedArrayBuffer(null) // => false\n * @since 2.0.3\n */\nexport function isSharedArrayBuffer(value: unknown): value is SharedArrayBuffer {\n return typeof SharedArrayBuffer !== 'undefined' && value instanceof SharedArrayBuffer;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n *\n * Internal helper used by `node/safeReadJsonFile` and `json/parse` to support JSONC (JSON with\n * line and block comments, plus trailing commas) — the dialect used by tsconfig.json, VS Code's\n * settings.json, etc. Not exported from any package barrel — tests live in\n * _stripJsonComments.test.ts.\n */\n\n/**\n * Blanks out line comments (starting with two slashes), block comments (slash-star ... star-slash),\n * and trailing commas (before `}` or `]`) in a JSONC string, so the result can be passed to\n * `JSON.parse`. String literals are tracked so a comment-like or comma character occurring\n * *inside* a JSON string (e.g. a URL) is left untouched — this is a string-boundary-aware pass,\n * not a naive regex strip.\n *\n * Everything removed is replaced by spaces (line breaks inside a block comment are kept), so the\n * output has exactly the same length and line layout as the input: a position `JSON.parse`\n * reports in an error on the output points at the same place in the original JSONC text.\n * @ignore\n */\nexport function stripJsonComments(input: string): string {\n let output = '';\n let inString = false;\n // A comma seen outside a string is held back instead of emitted immediately:\n // if the next significant token turns out to be `}`/`]`, it was a trailing\n // comma and gets blanked; otherwise it (and any whitespace/comments held\n // alongside it) is flushed once a real token confirms it wasn't trailing.\n let pendingComma = '';\n const flushPendingComma = () => {\n output += pendingComma;\n pendingComma = '';\n };\n // Whitespace and blanked-out comments don't resolve a pending comma either way, so they're\n // buffered alongside it to keep the output in the original order.\n const emitFiller = (text: string) => {\n if (pendingComma) {\n pendingComma += text;\n } else {\n output += text;\n }\n };\n\n let i = 0;\n while (i < input.length) {\n const char = input[i];\n\n if (inString) {\n output += char;\n if (char === '\\\\') {\n // Copy the escaped character as-is (whatever it is) so an escaped\n // quote (`\\\"`) doesn't get mistaken for the string's end.\n i++;\n if (i < input.length) output += input[i];\n } else if (char === '\"') {\n inString = false;\n }\n i++;\n continue;\n }\n\n if (char === '\"') {\n flushPendingComma();\n inString = true;\n output += char;\n i++;\n continue;\n }\n\n if (char === '/' && input[i + 1] === '/') {\n const start = i;\n i += 2;\n // Stop at any line-ending character, not just \\n — a bare \\r (classic\n // Mac line endings, or a lone \\r before a later \\n) also ends the line,\n // otherwise everything up to the next \\n (or EOF) gets silently eaten.\n while (i < input.length && input[i] !== '\\n' && input[i] !== '\\r') i++;\n emitFiller(' '.repeat(i - start));\n continue;\n }\n\n if (char === '/' && input[i + 1] === '*') {\n const start = i;\n i += 2;\n while (i < input.length && !(input[i] === '*' && input[i + 1] === '/')) i++;\n // An unterminated block comment runs to the end of the input.\n i = Math.min(i + 2, input.length);\n emitFiller(input.slice(start, i).replace(/[^\\r\\n]/g, ' '));\n continue;\n }\n\n if (char === ',') {\n flushPendingComma();\n pendingComma = char;\n i++;\n continue;\n }\n\n if (char === '}' || char === ']') {\n // Blank the comma — it was trailing — but keep what was buffered after it.\n output += pendingComma.replace(',', ' ');\n pendingComma = '';\n output += char;\n i++;\n continue;\n }\n\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') {\n emitFiller(char);\n i++;\n continue;\n }\n\n flushPendingComma();\n output += char;\n i++;\n }\n\n flushPendingComma();\n return output;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { readFileSync } from 'node:fs';\nimport { stripJsonComments } from '../_shared/_stripJsonComments';\n\n/**\n * Reads a file and parses its contents as JSON, returning `null` (or a\n * fallback) on any failure — a missing/unreadable file and invalid content\n * are both treated the same way.\n *\n * Also tolerates JSONC (line/block comments and trailing commas — the\n * dialect used by `tsconfig.json`, VS Code's `settings.json`, etc.): strict\n * `JSON.parse` is tried first, and only on failure is the content re-parsed\n * with comments/trailing commas stripped, so plain JSON pays no extra cost.\n *\n * Unlike `readFileSync` + `JSON.parse`, this never throws.\n *\n * @param filePath - Path to the JSON (or JSONC) file to read.\n * @param fallback - Value returned on failure. Defaults to `null` when omitted.\n * @returns The parsed value typed as `T`, or `fallback` on failure.\n * @example\n * safeReadJsonFile<{ name: string }>('./package.json') // => { name: 'helpers4', ... }\n * safeReadJsonFile('./missing.json') // => null\n * safeReadJsonFile('./missing.json', {}) // => {}\n * @example\n * // tsconfig.json-style JSONC (comments + trailing comma) also parses:\n * safeReadJsonFile('./tsconfig.json') // => { compilerOptions: { strict: true }, ... }\n * @since 3.0.6\n */\nexport function safeReadJsonFile<T>(filePath: string): T | null;\nexport function safeReadJsonFile<T>(filePath: string, fallback: T): T;\nexport function safeReadJsonFile<T>(filePath: string, fallback?: T): T | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n try {\n return JSON.parse(content) as T;\n } catch {\n // Not strict JSON — retry tolerating JSONC comments/trailing commas\n // before giving up.\n return JSON.parse(stripJsonComments(content)) as T;\n }\n } catch {\n // Distinguish \"no fallback provided\" (→ null) from \"fallback is undefined\"\n // (→ undefined as T) to keep runtime behaviour aligned with the overloads.\n return arguments.length < 2 ? null : (fallback as T);\n }\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Creates a fresh temporary directory under the OS temp root, runs `fn` with its path, and\n * always removes it (recursively) afterward — including when `fn` throws.\n * @param prefix - Prepended to the generated directory name, to make it identifiable\n * @param fn - Receives the temp directory's absolute path; its (possibly synchronous) return\n * value is returned\n * @returns Whatever `fn` returns or resolves to\n * @example\n * await withTempDir('my-tool', async (dir) => {\n * await writeFile(`${dir}/output.txt`, 'data');\n * return readFile(`${dir}/output.txt`, 'utf-8');\n * });\n * // => 'data' (the directory no longer exists once this resolves)\n * @since 3.1.1\n */\nexport async function withTempDir<T>(prefix: string, fn: (dir: string) => T | Promise<T>): Promise<T> {\n const dir = await mkdtemp(join(tmpdir(), `${prefix}-`));\n try {\n return await fn(dir);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,SAAS,OAAiC;CACxD,OAAO,OAAO,WAAW,eAAe,iBAAiB;AAC3D;;;;;;;;;;;;;;;;;;;;;;;;ACJA,SAAgB,aAAa,OAAoE;CAC/F,OAGE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAAkC,YAAY;AAE1D;;;;;;;;;;;;;;;;;;;;;;;ACTA,SAAgB,oBAAoB,OAA4C;CAC9E,OAAO,OAAO,sBAAsB,eAAe,iBAAiB;AACtE;;;;;;;;;;;;;;;;;;;;;;;;;ACAA,SAAgB,kBAAkB,OAAuB;CACvD,IAAI,SAAS;CACb,IAAI,WAAW;CAKf,IAAI,eAAe;CACnB,MAAM,0BAA0B;EAC9B,UAAU;EACV,eAAe;CACjB;CAGA,MAAM,cAAc,SAAiB;EACnC,IAAI,cACF,gBAAgB;OAEhB,UAAU;CAEd;CAEA,IAAI,IAAI;CACR,OAAO,IAAI,MAAM,QAAQ;EACvB,MAAM,OAAO,MAAM;EAEnB,IAAI,UAAU;GACZ,UAAU;GACV,IAAI,SAAS,MAAM;IAGjB;IACA,IAAI,IAAI,MAAM,QAAQ,UAAU,MAAM;GACxC,OAAO,IAAI,SAAS,MAClB,WAAW;GAEb;GACA;EACF;EAEA,IAAI,SAAS,MAAK;GAChB,kBAAkB;GAClB,WAAW;GACX,UAAU;GACV;GACA;EACF;EAEA,IAAI,SAAS,OAAO,MAAM,IAAI,OAAO,KAAK;GACxC,MAAM,QAAQ;GACd,KAAK;GAIL,OAAO,IAAI,MAAM,UAAU,MAAM,OAAO,QAAQ,MAAM,OAAO,MAAM;GACnE,WAAW,IAAI,OAAO,IAAI,KAAK,CAAC;GAChC;EACF;EAEA,IAAI,SAAS,OAAO,MAAM,IAAI,OAAO,KAAK;GACxC,MAAM,QAAQ;GACd,KAAK;GACL,OAAO,IAAI,MAAM,UAAU,EAAE,MAAM,OAAO,OAAO,MAAM,IAAI,OAAO,MAAM;GAExE,IAAI,KAAK,IAAI,IAAI,GAAG,MAAM,MAAM;GAChC,WAAW,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,QAAQ,YAAY,GAAG,CAAC;GACzD;EACF;EAEA,IAAI,SAAS,KAAK;GAChB,kBAAkB;GAClB,eAAe;GACf;GACA;EACF;EAEA,IAAI,SAAS,OAAO,SAAS,KAAK;GAEhC,UAAU,aAAa,QAAQ,KAAK,GAAG;GACvC,eAAe;GACf,UAAU;GACV;GACA;EACF;EAEA,IAAI,SAAS,OAAO,SAAS,OAAQ,SAAS,QAAQ,SAAS,MAAM;GACnE,WAAW,IAAI;GACf;GACA;EACF;EAEA,kBAAkB;EAClB,UAAU;EACV;CACF;CAEA,kBAAkB;CAClB,OAAO;AACT;;;;;;;;ACtFA,SAAgB,iBAAoB,UAAkB,UAAwB;CAC5E,IAAI;EACF,MAAM,UAAU,aAAa,UAAU,OAAO;EAC9C,IAAI;GACF,OAAO,KAAK,MAAM,OAAO;EAC3B,QAAQ;GAGN,OAAO,KAAK,MAAM,kBAAkB,OAAO,CAAC;EAC9C;CACF,QAAQ;EAGN,OAAO,UAAU,SAAS,IAAI,OAAQ;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;;;ACzBA,eAAsB,YAAe,QAAgB,IAAiD;CACpG,MAAM,MAAM,MAAM,QAAQ,KAAK,OAAO,GAAG,GAAG,OAAO,EAAE,CAAC;CACtD,IAAI;EACF,OAAO,MAAM,GAAG,GAAG;CACrB,UAAU;EACR,MAAM,GAAG,KAAK;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CAChD;AACF"}
|
package/llms.txt
CHANGED
package/meta/api.json
CHANGED