@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.
Files changed (47) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +1 -1
  3. package/dist/constants/node.js +1 -1
  4. package/dist/{package-default-node-range.js → constants/package-default-node-range.js} +1 -1
  5. package/dist/constants/packages.js +3 -3
  6. package/dist/{dlx-binary.d.ts → dlx/binary.d.ts} +2 -2
  7. package/dist/{dlx-binary.js → dlx/binary.js} +17 -17
  8. package/dist/dlx/cache.d.ts +25 -0
  9. package/dist/dlx/cache.js +32 -0
  10. package/dist/dlx/dir.d.ts +24 -0
  11. package/dist/dlx/dir.js +79 -0
  12. package/dist/{dlx-manifest.js → dlx/manifest.js} +7 -7
  13. package/dist/{dlx-package.d.ts → dlx/package.d.ts} +2 -2
  14. package/dist/{dlx-package.js → dlx/package.js} +16 -16
  15. package/dist/dlx/packages.d.ts +24 -0
  16. package/dist/dlx/packages.js +125 -0
  17. package/dist/dlx/paths.d.ts +31 -0
  18. package/dist/dlx/paths.js +75 -0
  19. package/dist/fs.d.ts +34 -22
  20. package/dist/fs.js +3 -3
  21. package/dist/http-request.d.ts +46 -0
  22. package/dist/http-request.js +18 -1
  23. package/dist/json/edit.d.ts +16 -0
  24. package/dist/json/edit.js +248 -0
  25. package/dist/json/format.d.ts +140 -0
  26. package/dist/json/format.js +121 -0
  27. package/dist/json/parse.d.ts +76 -0
  28. package/dist/{json.js → json/parse.js} +4 -4
  29. package/dist/json/types.d.ts +229 -0
  30. package/dist/json/types.js +17 -0
  31. package/dist/packages/{editable.js → edit.js} +18 -32
  32. package/dist/packages/operations.js +3 -3
  33. package/dist/packages.d.ts +2 -2
  34. package/dist/packages.js +5 -5
  35. package/package.json +62 -37
  36. package/dist/dlx.d.ts +0 -104
  37. package/dist/dlx.js +0 -220
  38. package/dist/json.d.ts +0 -196
  39. /package/dist/{lifecycle-script-names.d.ts → constants/lifecycle-script-names.d.ts} +0 -0
  40. /package/dist/{lifecycle-script-names.js → constants/lifecycle-script-names.js} +0 -0
  41. /package/dist/{maintained-node-versions.d.ts → constants/maintained-node-versions.d.ts} +0 -0
  42. /package/dist/{maintained-node-versions.js → constants/maintained-node-versions.js} +0 -0
  43. /package/dist/{package-default-node-range.d.ts → constants/package-default-node-range.d.ts} +0 -0
  44. /package/dist/{package-default-socket-categories.d.ts → constants/package-default-socket-categories.d.ts} +0 -0
  45. /package/dist/{package-default-socket-categories.js → constants/package-default-socket-categories.js} +0 -0
  46. /package/dist/{dlx-manifest.d.ts → dlx/manifest.d.ts} +0 -0
  47. /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
- * Uses `del` for safer deletion that prevents removing cwd and above by default.
582
- * Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories.
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 a single file
591
- * await safeDelete('./temp-file.txt')
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 multiple paths
597
- * await safeDelete(['./dist', './coverage'])
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
- * Uses `del` for safer deletion that prevents removing cwd and above by default.
607
- * Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories.
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 a single file
616
- * safeDeleteSync('./temp-file.txt')
626
+ * // Delete files within cwd (safe by default)
627
+ * safeDeleteSync('./build')
628
+ * safeDeleteSync('./dist')
617
629
  *
618
- * // Delete a directory recursively
619
- * safeDeleteSync('./build', { recursive: true })
630
+ * // Delete with glob patterns
631
+ * safeDeleteSync(['./temp/**', '!./temp/keep.txt'])
620
632
  *
621
- * // Delete multiple paths with globs
622
- * safeDeleteSync(['./dist/**', './coverage/**'])
633
+ * // Delete multiple paths
634
+ * safeDeleteSync(['./coverage', './reports'])
623
635
  *
624
- * // Force delete a protected path (use with caution)
625
- * safeDeleteSync('./important', { force: true })
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 import_json = require("./json");
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, import_json.jsonParse)(content, {
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, import_json.jsonParse)(content, {
501
+ return (0, import_parse.jsonParse)(content, {
502
502
  filepath: String(filepath),
503
503
  reviver,
504
504
  throws: shouldThrow
@@ -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.
@@ -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
+ });