@socketsecurity/lib 4.4.0 → 5.0.1
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/CHANGELOG.md +78 -0
- package/README.md +1 -1
- package/dist/constants/node.js +1 -1
- package/dist/{package-default-node-range.js → constants/package-default-node-range.js} +1 -1
- package/dist/constants/packages.js +3 -3
- package/dist/{dlx-binary.d.ts → dlx/binary.d.ts} +2 -2
- package/dist/{dlx-binary.js → dlx/binary.js} +17 -17
- package/dist/dlx/cache.d.ts +25 -0
- package/dist/dlx/cache.js +32 -0
- package/dist/dlx/dir.d.ts +24 -0
- package/dist/dlx/dir.js +79 -0
- package/dist/{dlx-manifest.js → dlx/manifest.js} +7 -7
- package/dist/{dlx-package.d.ts → dlx/package.d.ts} +2 -2
- package/dist/{dlx-package.js → dlx/package.js} +16 -16
- package/dist/dlx/packages.d.ts +24 -0
- package/dist/dlx/packages.js +125 -0
- package/dist/dlx/paths.d.ts +31 -0
- package/dist/dlx/paths.js +75 -0
- package/dist/fs.d.ts +34 -22
- package/dist/fs.js +3 -3
- package/dist/http-request.d.ts +46 -0
- package/dist/http-request.js +18 -1
- package/dist/json/edit.d.ts +16 -0
- package/dist/json/edit.js +248 -0
- package/dist/json/format.d.ts +140 -0
- package/dist/json/format.js +121 -0
- package/dist/json/parse.d.ts +76 -0
- package/dist/{json.js → json/parse.js} +4 -4
- package/dist/json/types.d.ts +229 -0
- package/dist/json/types.js +17 -0
- package/dist/packages/{editable.js → edit.js} +18 -32
- package/dist/packages/operations.js +3 -3
- package/dist/packages.d.ts +2 -2
- package/dist/packages.js +5 -5
- package/package.json +62 -37
- package/dist/dlx.d.ts +0 -104
- package/dist/dlx.js +0 -220
- package/dist/json.d.ts +0 -196
- /package/dist/{lifecycle-script-names.d.ts → constants/lifecycle-script-names.d.ts} +0 -0
- /package/dist/{lifecycle-script-names.js → constants/lifecycle-script-names.js} +0 -0
- /package/dist/{maintained-node-versions.d.ts → constants/maintained-node-versions.d.ts} +0 -0
- /package/dist/{maintained-node-versions.js → constants/maintained-node-versions.js} +0 -0
- /package/dist/{package-default-node-range.d.ts → constants/package-default-node-range.d.ts} +0 -0
- /package/dist/{package-default-socket-categories.d.ts → constants/package-default-socket-categories.d.ts} +0 -0
- /package/dist/{package-default-socket-categories.js → constants/package-default-socket-categories.js} +0 -0
- /package/dist/{dlx-manifest.d.ts → dlx/manifest.d.ts} +0 -0
- /package/dist/packages/{editable.d.ts → edit.d.ts} +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Socket Lib - Built with esbuild */
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var paths_exports = {};
|
|
21
|
+
__export(paths_exports, {
|
|
22
|
+
getDlxInstalledPackageDir: () => getDlxInstalledPackageDir,
|
|
23
|
+
getDlxPackageDir: () => getDlxPackageDir,
|
|
24
|
+
getDlxPackageJsonPath: () => getDlxPackageJsonPath,
|
|
25
|
+
getDlxPackageNodeModulesDir: () => getDlxPackageNodeModulesDir,
|
|
26
|
+
isInSocketDlx: () => isInSocketDlx
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(paths_exports);
|
|
29
|
+
var import_normalize = require("../paths/normalize");
|
|
30
|
+
var import_socket = require("../paths/socket");
|
|
31
|
+
let _path;
|
|
32
|
+
// @__NO_SIDE_EFFECTS__
|
|
33
|
+
function getPath() {
|
|
34
|
+
if (_path === void 0) {
|
|
35
|
+
_path = require("node:path");
|
|
36
|
+
}
|
|
37
|
+
return _path;
|
|
38
|
+
}
|
|
39
|
+
function getDlxInstalledPackageDir(packageName) {
|
|
40
|
+
const path = /* @__PURE__ */ getPath();
|
|
41
|
+
return (0, import_normalize.normalizePath)(
|
|
42
|
+
path.join(getDlxPackageNodeModulesDir(packageName), packageName)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function getDlxPackageDir(packageName) {
|
|
46
|
+
const path = /* @__PURE__ */ getPath();
|
|
47
|
+
return (0, import_normalize.normalizePath)(path.join((0, import_socket.getSocketDlxDir)(), packageName));
|
|
48
|
+
}
|
|
49
|
+
function getDlxPackageJsonPath(packageName) {
|
|
50
|
+
const path = /* @__PURE__ */ getPath();
|
|
51
|
+
return (0, import_normalize.normalizePath)(
|
|
52
|
+
path.join(getDlxInstalledPackageDir(packageName), "package.json")
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
function getDlxPackageNodeModulesDir(packageName) {
|
|
56
|
+
const path = /* @__PURE__ */ getPath();
|
|
57
|
+
return (0, import_normalize.normalizePath)(path.join(getDlxPackageDir(packageName), "node_modules"));
|
|
58
|
+
}
|
|
59
|
+
function isInSocketDlx(filePath) {
|
|
60
|
+
if (!filePath) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const path = /* @__PURE__ */ getPath();
|
|
64
|
+
const dlxDir = (0, import_socket.getSocketDlxDir)();
|
|
65
|
+
const absolutePath = (0, import_normalize.normalizePath)(path.resolve(filePath));
|
|
66
|
+
return absolutePath.startsWith(`${dlxDir}/`);
|
|
67
|
+
}
|
|
68
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
69
|
+
0 && (module.exports = {
|
|
70
|
+
getDlxInstalledPackageDir,
|
|
71
|
+
getDlxPackageDir,
|
|
72
|
+
getDlxPackageJsonPath,
|
|
73
|
+
getDlxPackageNodeModulesDir,
|
|
74
|
+
isInSocketDlx
|
|
75
|
+
});
|
package/dist/fs.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Abortable } from 'events';
|
|
6
6
|
import type { MakeDirectoryOptions, ObjectEncodingOptions, OpenMode, PathLike } from 'fs';
|
|
7
|
-
import type { JsonReviver } from './json';
|
|
7
|
+
import type { JsonReviver } from './json/types';
|
|
8
8
|
import { type Remap } from './objects';
|
|
9
9
|
/**
|
|
10
10
|
* Supported text encodings for Node.js Buffers.
|
|
@@ -545,7 +545,7 @@ export declare function readFileUtf8Sync(filepath: PathLike, options?: ReadFileO
|
|
|
545
545
|
* ```
|
|
546
546
|
*/
|
|
547
547
|
/*@__NO_SIDE_EFFECTS__*/
|
|
548
|
-
export declare function readJson(filepath: PathLike, options?: ReadJsonOptions | string | undefined): Promise<import("./json").JsonValue>;
|
|
548
|
+
export declare function readJson(filepath: PathLike, options?: ReadJsonOptions | string | undefined): Promise<import("./json/types").JsonValue>;
|
|
549
549
|
/**
|
|
550
550
|
* Read and parse a JSON file synchronously.
|
|
551
551
|
* Reads the file as UTF-8 text and parses it as JSON.
|
|
@@ -575,54 +575,66 @@ export declare function readJson(filepath: PathLike, options?: ReadJsonOptions |
|
|
|
575
575
|
* ```
|
|
576
576
|
*/
|
|
577
577
|
/*@__NO_SIDE_EFFECTS__*/
|
|
578
|
-
export declare function readJsonSync(filepath: PathLike, options?: ReadJsonOptions | string | undefined): import("./json").JsonValue;
|
|
578
|
+
export declare function readJsonSync(filepath: PathLike, options?: ReadJsonOptions | string | undefined): import("./json/types").JsonValue;
|
|
579
579
|
/**
|
|
580
580
|
* Safely delete a file or directory asynchronously with built-in protections.
|
|
581
|
-
*
|
|
582
|
-
*
|
|
581
|
+
*
|
|
582
|
+
* Uses [`del`](https://socket.dev/npm/package/del/overview/8.0.1) for safer deletion with these safety features:
|
|
583
|
+
* - By default, prevents deleting the current working directory (cwd) and above
|
|
584
|
+
* - Allows deleting within cwd (descendant paths) without force option
|
|
585
|
+
* - Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories
|
|
586
|
+
* - Protects against accidental deletion of parent directories via `../` paths
|
|
583
587
|
*
|
|
584
588
|
* @param filepath - Path or array of paths to delete (supports glob patterns)
|
|
585
589
|
* @param options - Deletion options including force, retries, and recursion
|
|
590
|
+
* @param options.force - Set to true to allow deleting cwd and above (use with caution)
|
|
586
591
|
* @throws {Error} When attempting to delete protected paths without force option
|
|
587
592
|
*
|
|
588
593
|
* @example
|
|
589
594
|
* ```ts
|
|
590
|
-
* // Delete
|
|
591
|
-
* await safeDelete('./
|
|
592
|
-
*
|
|
593
|
-
* // Delete a directory recursively
|
|
594
|
-
* await safeDelete('./build', { recursive: true })
|
|
595
|
+
* // Delete files within cwd (safe by default)
|
|
596
|
+
* await safeDelete('./build')
|
|
597
|
+
* await safeDelete('./dist')
|
|
595
598
|
*
|
|
596
|
-
* // Delete
|
|
597
|
-
* await safeDelete(['./
|
|
599
|
+
* // Delete with glob patterns
|
|
600
|
+
* await safeDelete(['./temp/**', '!./temp/keep.txt'])
|
|
598
601
|
*
|
|
599
602
|
* // Delete with custom retry settings
|
|
600
603
|
* await safeDelete('./flaky-dir', { maxRetries: 5, retryDelay: 500 })
|
|
604
|
+
*
|
|
605
|
+
* // Force delete cwd or above (requires explicit force: true)
|
|
606
|
+
* await safeDelete('../parent-dir', { force: true })
|
|
601
607
|
* ```
|
|
602
608
|
*/
|
|
603
609
|
export declare function safeDelete(filepath: PathLike | PathLike[], options?: RemoveOptions | undefined): Promise<void>;
|
|
604
610
|
/**
|
|
605
611
|
* Safely delete a file or directory synchronously with built-in protections.
|
|
606
|
-
*
|
|
607
|
-
*
|
|
612
|
+
*
|
|
613
|
+
* Uses [`del`](https://socket.dev/npm/package/del/overview/8.0.1) for safer deletion with these safety features:
|
|
614
|
+
* - By default, prevents deleting the current working directory (cwd) and above
|
|
615
|
+
* - Allows deleting within cwd (descendant paths) without force option
|
|
616
|
+
* - Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories
|
|
617
|
+
* - Protects against accidental deletion of parent directories via `../` paths
|
|
608
618
|
*
|
|
609
619
|
* @param filepath - Path or array of paths to delete (supports glob patterns)
|
|
610
620
|
* @param options - Deletion options including force, retries, and recursion
|
|
621
|
+
* @param options.force - Set to true to allow deleting cwd and above (use with caution)
|
|
611
622
|
* @throws {Error} When attempting to delete protected paths without force option
|
|
612
623
|
*
|
|
613
624
|
* @example
|
|
614
625
|
* ```ts
|
|
615
|
-
* // Delete
|
|
616
|
-
* safeDeleteSync('./
|
|
626
|
+
* // Delete files within cwd (safe by default)
|
|
627
|
+
* safeDeleteSync('./build')
|
|
628
|
+
* safeDeleteSync('./dist')
|
|
617
629
|
*
|
|
618
|
-
* // Delete
|
|
619
|
-
* safeDeleteSync('./
|
|
630
|
+
* // Delete with glob patterns
|
|
631
|
+
* safeDeleteSync(['./temp/**', '!./temp/keep.txt'])
|
|
620
632
|
*
|
|
621
|
-
* // Delete multiple paths
|
|
622
|
-
* safeDeleteSync(['./
|
|
633
|
+
* // Delete multiple paths
|
|
634
|
+
* safeDeleteSync(['./coverage', './reports'])
|
|
623
635
|
*
|
|
624
|
-
* // Force delete
|
|
625
|
-
* safeDeleteSync('
|
|
636
|
+
* // Force delete cwd or above (requires explicit force: true)
|
|
637
|
+
* safeDeleteSync('../parent-dir', { force: true })
|
|
626
638
|
* ```
|
|
627
639
|
*/
|
|
628
640
|
export declare function safeDeleteSync(filepath: PathLike | PathLike[], options?: RemoveOptions | undefined): void;
|
package/dist/fs.js
CHANGED
|
@@ -54,7 +54,7 @@ var import_process = require("./constants/process");
|
|
|
54
54
|
var import_arrays = require("./arrays");
|
|
55
55
|
var import_del = require("./external/del");
|
|
56
56
|
var import_globs = require("./globs");
|
|
57
|
-
var
|
|
57
|
+
var import_parse = require("./json/parse");
|
|
58
58
|
var import_objects = require("./objects");
|
|
59
59
|
var import_normalize = require("./paths/normalize");
|
|
60
60
|
var import_rewire = require("./paths/rewire");
|
|
@@ -455,7 +455,7 @@ Check file permissions or run with appropriate access.`,
|
|
|
455
455
|
}
|
|
456
456
|
return void 0;
|
|
457
457
|
}
|
|
458
|
-
return (0,
|
|
458
|
+
return (0, import_parse.jsonParse)(content, {
|
|
459
459
|
filepath: String(filepath),
|
|
460
460
|
reviver,
|
|
461
461
|
throws: shouldThrow
|
|
@@ -498,7 +498,7 @@ Check file permissions or run with appropriate access.`,
|
|
|
498
498
|
}
|
|
499
499
|
return void 0;
|
|
500
500
|
}
|
|
501
|
-
return (0,
|
|
501
|
+
return (0, import_parse.jsonParse)(content, {
|
|
502
502
|
filepath: String(filepath),
|
|
503
503
|
reviver,
|
|
504
504
|
throws: shouldThrow
|
package/dist/http-request.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from './logger.js';
|
|
1
2
|
/**
|
|
2
3
|
* Configuration options for HTTP/HTTPS requests.
|
|
3
4
|
*/
|
|
@@ -270,9 +271,31 @@ export interface HttpDownloadOptions {
|
|
|
270
271
|
* ```
|
|
271
272
|
*/
|
|
272
273
|
headers?: Record<string, string> | undefined;
|
|
274
|
+
/**
|
|
275
|
+
* Logger instance for automatic progress logging.
|
|
276
|
+
* When provided with `progressInterval`, will automatically log download progress.
|
|
277
|
+
* If both `onProgress` and `logger` are provided, `onProgress` takes precedence.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts
|
|
281
|
+
* import { getDefaultLogger } from '@socketsecurity/lib/logger'
|
|
282
|
+
*
|
|
283
|
+
* const logger = getDefaultLogger()
|
|
284
|
+
* await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
|
|
285
|
+
* logger,
|
|
286
|
+
* progressInterval: 10 // Log every 10%
|
|
287
|
+
* })
|
|
288
|
+
* // Output:
|
|
289
|
+
* // Progress: 10% (5.2 MB / 52.0 MB)
|
|
290
|
+
* // Progress: 20% (10.4 MB / 52.0 MB)
|
|
291
|
+
* // ...
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
logger?: Logger | undefined;
|
|
273
295
|
/**
|
|
274
296
|
* Callback for tracking download progress.
|
|
275
297
|
* Called periodically as data is received.
|
|
298
|
+
* Takes precedence over `logger` if both are provided.
|
|
276
299
|
*
|
|
277
300
|
* @param downloaded - Number of bytes downloaded so far
|
|
278
301
|
* @param total - Total file size in bytes (from Content-Length header)
|
|
@@ -288,6 +311,29 @@ export interface HttpDownloadOptions {
|
|
|
288
311
|
* ```
|
|
289
312
|
*/
|
|
290
313
|
onProgress?: ((downloaded: number, total: number) => void) | undefined;
|
|
314
|
+
/**
|
|
315
|
+
* Progress reporting interval as a percentage (0-100).
|
|
316
|
+
* Only used when `logger` is provided.
|
|
317
|
+
* Progress will be logged each time the download advances by this percentage.
|
|
318
|
+
*
|
|
319
|
+
* @default 10
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```ts
|
|
323
|
+
* // Log every 10%
|
|
324
|
+
* await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
|
|
325
|
+
* logger: getDefaultLogger(),
|
|
326
|
+
* progressInterval: 10
|
|
327
|
+
* })
|
|
328
|
+
*
|
|
329
|
+
* // Log every 25%
|
|
330
|
+
* await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
|
|
331
|
+
* logger: getDefaultLogger(),
|
|
332
|
+
* progressInterval: 25
|
|
333
|
+
* })
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
progressInterval?: number | undefined;
|
|
291
337
|
/**
|
|
292
338
|
* Number of retry attempts for failed downloads.
|
|
293
339
|
* Uses exponential backoff: delay = `retryDelay` * 2^attempt.
|
package/dist/http-request.js
CHANGED
|
@@ -184,17 +184,34 @@ async function httpRequestAttempt(url, options) {
|
|
|
184
184
|
async function httpDownload(url, destPath, options) {
|
|
185
185
|
const {
|
|
186
186
|
headers = {},
|
|
187
|
+
logger,
|
|
187
188
|
onProgress,
|
|
189
|
+
progressInterval = 10,
|
|
188
190
|
retries = 0,
|
|
189
191
|
retryDelay = 1e3,
|
|
190
192
|
timeout = 12e4
|
|
191
193
|
} = { __proto__: null, ...options };
|
|
194
|
+
let progressCallback;
|
|
195
|
+
if (onProgress) {
|
|
196
|
+
progressCallback = onProgress;
|
|
197
|
+
} else if (logger) {
|
|
198
|
+
let lastPercent = 0;
|
|
199
|
+
progressCallback = (downloaded, total) => {
|
|
200
|
+
const percent = Math.floor(downloaded / total * 100);
|
|
201
|
+
if (percent >= lastPercent + progressInterval) {
|
|
202
|
+
logger.log(
|
|
203
|
+
` Progress: ${percent}% (${(downloaded / 1024 / 1024).toFixed(1)} MB / ${(total / 1024 / 1024).toFixed(1)} MB)`
|
|
204
|
+
);
|
|
205
|
+
lastPercent = percent;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
192
209
|
let lastError;
|
|
193
210
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
194
211
|
try {
|
|
195
212
|
return await httpDownloadAttempt(url, destPath, {
|
|
196
213
|
headers,
|
|
197
|
-
onProgress,
|
|
214
|
+
onProgress: progressCallback,
|
|
198
215
|
timeout
|
|
199
216
|
});
|
|
200
217
|
} catch (e) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EditableJsonConstructor } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Get the EditableJson class for JSON file manipulation.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { getEditableJsonClass } from '@socketsecurity/lib/json'
|
|
8
|
+
*
|
|
9
|
+
* const EditableJson = getEditableJsonClass<MyConfigType>()
|
|
10
|
+
* const config = await EditableJson.load('./config.json')
|
|
11
|
+
* config.update({ someField: 'newValue' })
|
|
12
|
+
* await config.save({ sort: true })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
16
|
+
export declare function getEditableJsonClass<T = Record<string, unknown>>(): EditableJsonConstructor<T>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Socket Lib - Built with esbuild */
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var edit_exports = {};
|
|
21
|
+
__export(edit_exports, {
|
|
22
|
+
getEditableJsonClass: () => getEditableJsonClass
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(edit_exports);
|
|
25
|
+
var import_promises = require("node:timers/promises");
|
|
26
|
+
var import_format = require("./format");
|
|
27
|
+
const identSymbol = import_format.INDENT_SYMBOL;
|
|
28
|
+
const newlineSymbol = import_format.NEWLINE_SYMBOL;
|
|
29
|
+
const JSONParse = JSON.parse;
|
|
30
|
+
let _EditableJsonClass;
|
|
31
|
+
let _fs;
|
|
32
|
+
// @__NO_SIDE_EFFECTS__
|
|
33
|
+
function getFs() {
|
|
34
|
+
if (_fs === void 0) {
|
|
35
|
+
_fs = require("node:fs");
|
|
36
|
+
}
|
|
37
|
+
return _fs;
|
|
38
|
+
}
|
|
39
|
+
async function retryWrite(filepath, content, retries = 3, baseDelay = 10) {
|
|
40
|
+
const { promises: fsPromises } = /* @__PURE__ */ getFs();
|
|
41
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
42
|
+
try {
|
|
43
|
+
await fsPromises.writeFile(filepath, content);
|
|
44
|
+
if (process.platform === "win32") {
|
|
45
|
+
await (0, import_promises.setTimeout)(50);
|
|
46
|
+
let accessRetries = 0;
|
|
47
|
+
const maxAccessRetries = 5;
|
|
48
|
+
while (accessRetries < maxAccessRetries) {
|
|
49
|
+
try {
|
|
50
|
+
await fsPromises.access(filepath);
|
|
51
|
+
await (0, import_promises.setTimeout)(10);
|
|
52
|
+
break;
|
|
53
|
+
} catch {
|
|
54
|
+
const delay = 20 * (accessRetries + 1);
|
|
55
|
+
await (0, import_promises.setTimeout)(delay);
|
|
56
|
+
accessRetries++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const isLastAttempt = attempt === retries;
|
|
63
|
+
const isRetriableError = err instanceof Error && "code" in err && (err.code === "EPERM" || err.code === "EBUSY" || err.code === "ENOENT");
|
|
64
|
+
if (!isRetriableError || isLastAttempt) {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
const delay = baseDelay * 2 ** attempt;
|
|
68
|
+
await (0, import_promises.setTimeout)(delay);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function parseJson(content) {
|
|
73
|
+
return JSONParse(content);
|
|
74
|
+
}
|
|
75
|
+
async function readFile(filepath) {
|
|
76
|
+
const { promises: fsPromises } = /* @__PURE__ */ getFs();
|
|
77
|
+
const maxRetries = process.platform === "win32" ? 5 : 1;
|
|
78
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
79
|
+
try {
|
|
80
|
+
return await fsPromises.readFile(filepath, "utf8");
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const isLastAttempt = attempt === maxRetries;
|
|
83
|
+
const isEnoent = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
84
|
+
if (!isEnoent || isLastAttempt) {
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
const delay = process.platform === "win32" ? 50 * (attempt + 1) : 20;
|
|
88
|
+
await (0, import_promises.setTimeout)(delay);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw new Error("Unreachable code");
|
|
92
|
+
}
|
|
93
|
+
// @__NO_SIDE_EFFECTS__
|
|
94
|
+
function getEditableJsonClass() {
|
|
95
|
+
if (_EditableJsonClass === void 0) {
|
|
96
|
+
_EditableJsonClass = class EditableJson {
|
|
97
|
+
_canSave = true;
|
|
98
|
+
_content = {};
|
|
99
|
+
_path = void 0;
|
|
100
|
+
_readFileContent = "";
|
|
101
|
+
_readFileJson = void 0;
|
|
102
|
+
get content() {
|
|
103
|
+
return this._content;
|
|
104
|
+
}
|
|
105
|
+
get filename() {
|
|
106
|
+
const path = this._path;
|
|
107
|
+
if (!path) {
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
return path;
|
|
111
|
+
}
|
|
112
|
+
get path() {
|
|
113
|
+
return this._path;
|
|
114
|
+
}
|
|
115
|
+
static async create(path, opts = {}) {
|
|
116
|
+
const instance = new EditableJson();
|
|
117
|
+
instance.create(path);
|
|
118
|
+
return opts.data ? instance.update(opts.data) : instance;
|
|
119
|
+
}
|
|
120
|
+
static async load(path, opts = {}) {
|
|
121
|
+
const instance = new EditableJson();
|
|
122
|
+
if (!opts.create) {
|
|
123
|
+
return await instance.load(path);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return await instance.load(path);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (!err.message.includes("ENOENT") && !err.message.includes("no such file")) {
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
return instance.create(path);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
create(path) {
|
|
135
|
+
this._path = path;
|
|
136
|
+
this._content = {};
|
|
137
|
+
this._canSave = true;
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
fromContent(data) {
|
|
141
|
+
this._content = data;
|
|
142
|
+
this._canSave = false;
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
fromJSON(data) {
|
|
146
|
+
const parsed = parseJson(data);
|
|
147
|
+
const indent = (0, import_format.detectIndent)(data);
|
|
148
|
+
const newline = (0, import_format.detectNewline)(data);
|
|
149
|
+
parsed[identSymbol] = indent;
|
|
150
|
+
parsed[newlineSymbol] = newline;
|
|
151
|
+
this._content = parsed;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
async load(path, create) {
|
|
155
|
+
this._path = path;
|
|
156
|
+
let parseErr;
|
|
157
|
+
try {
|
|
158
|
+
this._readFileContent = await readFile(this.filename);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
if (!create) {
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
parseErr = err;
|
|
164
|
+
}
|
|
165
|
+
if (parseErr) {
|
|
166
|
+
throw parseErr;
|
|
167
|
+
}
|
|
168
|
+
this.fromJSON(this._readFileContent);
|
|
169
|
+
this._readFileJson = parseJson(this._readFileContent);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
update(content) {
|
|
173
|
+
this._content = {
|
|
174
|
+
...this._content,
|
|
175
|
+
...content
|
|
176
|
+
};
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
async save(options) {
|
|
180
|
+
if (!this._canSave || this.content === void 0) {
|
|
181
|
+
throw new Error("No file path to save to");
|
|
182
|
+
}
|
|
183
|
+
if (!(0, import_format.shouldSave)(
|
|
184
|
+
this.content,
|
|
185
|
+
this._readFileJson,
|
|
186
|
+
this._readFileContent,
|
|
187
|
+
options
|
|
188
|
+
)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const content = (0, import_format.stripFormattingSymbols)(
|
|
192
|
+
this.content
|
|
193
|
+
);
|
|
194
|
+
const sortedContent = options?.sort ? (0, import_format.sortKeys)(content) : content;
|
|
195
|
+
const formatting = (0, import_format.getFormattingFromContent)(
|
|
196
|
+
this.content
|
|
197
|
+
);
|
|
198
|
+
const fileContent = (0, import_format.stringifyWithFormatting)(sortedContent, formatting);
|
|
199
|
+
await retryWrite(this.filename, fileContent);
|
|
200
|
+
this._readFileContent = fileContent;
|
|
201
|
+
this._readFileJson = parseJson(fileContent);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
saveSync(options) {
|
|
205
|
+
if (!this._canSave || this.content === void 0) {
|
|
206
|
+
throw new Error("No file path to save to");
|
|
207
|
+
}
|
|
208
|
+
if (!(0, import_format.shouldSave)(
|
|
209
|
+
this.content,
|
|
210
|
+
this._readFileJson,
|
|
211
|
+
this._readFileContent,
|
|
212
|
+
options
|
|
213
|
+
)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const content = (0, import_format.stripFormattingSymbols)(
|
|
217
|
+
this.content
|
|
218
|
+
);
|
|
219
|
+
const sortedContent = options?.sort ? (0, import_format.sortKeys)(content) : content;
|
|
220
|
+
const formatting = (0, import_format.getFormattingFromContent)(
|
|
221
|
+
this.content
|
|
222
|
+
);
|
|
223
|
+
const fileContent = (0, import_format.stringifyWithFormatting)(sortedContent, formatting);
|
|
224
|
+
const fs = /* @__PURE__ */ getFs();
|
|
225
|
+
fs.writeFileSync(this.filename, fileContent);
|
|
226
|
+
this._readFileContent = fileContent;
|
|
227
|
+
this._readFileJson = parseJson(fileContent);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
willSave(options) {
|
|
231
|
+
if (!this._canSave || this.content === void 0) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return (0, import_format.shouldSave)(
|
|
235
|
+
this.content,
|
|
236
|
+
this._readFileJson,
|
|
237
|
+
this._readFileContent,
|
|
238
|
+
options
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return _EditableJsonClass;
|
|
244
|
+
}
|
|
245
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
246
|
+
0 && (module.exports = {
|
|
247
|
+
getEditableJsonClass
|
|
248
|
+
});
|