snowbase 6.0.2 → 6.0.5

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.
@@ -1,170 +1,170 @@
1
- import {
2
- writeFileSync,
3
- existsSync,
4
- mkdirSync,
5
- copyFileSync,
6
- statSync,
7
- readdirSync,
8
- unlinkSync,
9
- } from "fs";
10
- import { read, write, randomId5 } from "../helpers/utils.js";
11
- import { encrypt, decrypt } from "../helpers/crypto.js";
12
- import { resolve, join } from "path";
13
-
14
- /**
15
- * @fileoverview FileStorage is a storage class that stores data in a file.
16
- * @class FileStorage
17
- */
18
- export default class FileStorage {
19
- /**
20
- * @description Creates a new instance of FileStorage.
21
- * @param {Object} options - Options for the FileStorage.
22
- * @param {string} options.baseDir - The base directory for the database.
23
- * @param {boolean} [options.encryption] - Whether to encrypt the data.
24
- * @param {string} [options.secret] - The secret key for encryption.
25
- * @param {boolean} [options.backup] - Whether to backup the data.
26
- * @param {boolean} [options.logging] - Whether to log the data.
27
- * @param {function(any): never} options.error - Function to handle errors.
28
- * @param {function(...any): void} options.emit - Function to emit events.
29
- * @param {function(string, any): void} options.log - Function to log the data.
30
- * @memberof FileStorage
31
- * @constructor
32
- */
33
- constructor({
34
- baseDir,
35
- encryption = false,
36
- secret = "",
37
- backup = false,
38
- logging = false,
39
- error,
40
- emit,
41
- log,
42
- }) {
43
- this.baseDir = resolve(baseDir) || resolve("./snowbase");
44
- this.encryption = encryption;
45
- this.secret = secret;
46
- this.backup = backup;
47
- this.logging = logging;
48
- this._error = error;
49
- this.emit = emit;
50
- this._log = log;
51
- this.path = join(this.baseDir, "snowbase.json");
52
-
53
- if (this.logging) {
54
- this.logsDir = join(this.baseDir, "logs");
55
- }
56
- if (this.backup) {
57
- this.backupDir = join(this.baseDir, "backups");
58
- }
59
- this.path = join(this.baseDir, "snowbase.json");
60
- if (this.logsDir) this.logsPath = join(this.logsDir, "snowbase.log");
61
-
62
- //Creating directories if they don't exist
63
- mkdirSync(this.baseDir, { recursive: true });
64
- if (this.backup && this.backupDir)
65
- mkdirSync(this.backupDir, { recursive: true }); //only if the user wants backup
66
- if (this.logging && this.logsDir)
67
- mkdirSync(this.logsDir, { recursive: true }); //only if the user wants logging
68
-
69
- //Creating the database file if it doesn't exist
70
- if (!existsSync(this.path)) {
71
- const initialData = this.encryption
72
- ? encrypt({}, this.secret)
73
- : JSON.stringify({});
74
- writeFileSync(this.path, initialData, "utf8");
75
- /** @type {Record<string, any>|null} */
76
- this._cache = {};
77
- /** @type {import("node:fs").Stats} */
78
- const stat = statSync(this.path);
79
- /** @type {number} Last known modification time of the database file */
80
- this._cacheMtime = stat.mtimeMs;
81
- /** @type {number} Last known size of the database file */
82
- this._cacheSize = stat.size;
83
- } else {
84
- /** @type {Record<string, any>|null} */
85
- this._cache = null;
86
- /** @type {number} */
87
- this._cacheMtime = 0;
88
- }
89
- if (this.logging && this.logsPath && !existsSync(this.logsPath))
90
- writeFileSync(this.logsPath, "", "utf8");
91
- }
92
- /**
93
- * @returns {Record<string, any>} The data from the database
94
- */
95
- read() {
96
- // Validate cache freshness by comparing file mtime
97
- const stat = statSync(this.path);
98
- const currentMtime = stat.mtimeMs;
99
- if (
100
- this._cache !== null &&
101
- currentMtime === this._cacheMtime &&
102
- stat.size === this._cacheSize
103
- ) {
104
- return this._cache;
105
- }
106
- const data = read(
107
- this.path,
108
- this._error.bind(this),
109
- this.encryption,
110
- this.secret || "",
111
- );
112
- if (data === null || Array.isArray(data) || typeof data !== "object") {
113
- this._error("[INVALID_DATABASE] The database root must be an object.");
114
- }
115
- this._cache = data;
116
- this._cacheMtime = currentMtime;
117
- this._cacheSize = stat.size;
118
- return data;
119
- }
120
-
121
- /**
122
- * @param {Object} data - Data to write
123
- * @returns {void}
124
- */
125
- write(data) {
126
- write(
127
- this.path,
128
- data,
129
- this._error.bind(this),
130
- this.emit.bind(this),
131
- this.encryption,
132
- this.secret,
133
- );
134
- this.createBackup();
135
- this._cache = data;
136
- this._cacheMtime = statSync(this.path).mtimeMs;
137
- }
138
-
139
- createBackup() {
140
- if (!this.backup || !this.backupDir) return false;
141
- let maxBackups = 20;
142
- const backupDir = this.backupDir || join(this.baseDir, "backups");
143
- mkdirSync(backupDir, { recursive: true });
144
- //Getting all backup files
145
- const files = readdirSync(backupDir)
146
- .filter((f) => f.startsWith("backup_"))
147
- .map((file) => {
148
- const fullPath = join(backupDir, file);
149
- const stats = statSync(fullPath);
150
-
151
- return {
152
- path: fullPath,
153
- time: stats.mtimeMs,
154
- };
155
- })
156
- .sort((a, b) => a.time - b.time);
157
-
158
- if (files.length - maxBackups > 0) {
159
- for (let i = 0; i < files.length - maxBackups; i++) {
160
- unlinkSync(files[i].path);
161
- }
162
- }
163
- //Creates a new backup
164
- const stamp = new Date().toISOString().replace(/[:.]/g, "-");
165
- const backupFile = join(backupDir, `backup_${stamp}-${randomId5()}.json`);
166
- copyFileSync(this.path, backupFile);
167
- this._log("create backup", { backupFile });
168
- return backupFile;
169
- }
170
- }
1
+ import {
2
+ writeFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ copyFileSync,
6
+ statSync,
7
+ readdirSync,
8
+ unlinkSync,
9
+ } from "fs";
10
+ import { read, write, randomId5 } from "../helpers/utils.js";
11
+ import { encrypt, decrypt } from "../helpers/crypto.js";
12
+ import { resolve, join } from "path";
13
+
14
+ /**
15
+ * @fileoverview FileStorage is a storage class that stores data in a file.
16
+ * @class FileStorage
17
+ */
18
+ export default class FileStorage {
19
+ /**
20
+ * @description Creates a new instance of FileStorage.
21
+ * @param {Object} options - Options for the FileStorage.
22
+ * @param {string} options.baseDir - The base directory for the database.
23
+ * @param {boolean} [options.encryption] - Whether to encrypt the data.
24
+ * @param {string} [options.secret] - The secret key for encryption.
25
+ * @param {boolean} [options.backup] - Whether to backup the data.
26
+ * @param {boolean} [options.logging] - Whether to log the data.
27
+ * @param {function(any): never} options.error - Function to handle errors.
28
+ * @param {function(...any): void} options.emit - Function to emit events.
29
+ * @param {function(string, any): void} options.log - Function to log the data.
30
+ * @memberof FileStorage
31
+ * @constructor
32
+ */
33
+ constructor({
34
+ baseDir,
35
+ encryption = false,
36
+ secret = "",
37
+ backup = false,
38
+ logging = false,
39
+ error,
40
+ emit,
41
+ log,
42
+ }) {
43
+ this.baseDir = resolve(baseDir) || resolve("./snowbase");
44
+ this.encryption = encryption;
45
+ this.secret = secret;
46
+ this.backup = backup;
47
+ this.logging = logging;
48
+ this._error = error;
49
+ this.emit = emit;
50
+ this._log = log;
51
+ this.path = join(this.baseDir, "snowbase.json");
52
+
53
+ if (this.logging) {
54
+ this.logsDir = join(this.baseDir, "logs");
55
+ }
56
+ if (this.backup) {
57
+ this.backupDir = join(this.baseDir, "backups");
58
+ }
59
+ this.path = join(this.baseDir, "snowbase.json");
60
+ if (this.logsDir) this.logsPath = join(this.logsDir, "snowbase.log");
61
+
62
+ //Creating directories if they don't exist
63
+ mkdirSync(this.baseDir, { recursive: true });
64
+ if (this.backup && this.backupDir)
65
+ mkdirSync(this.backupDir, { recursive: true }); //only if the user wants backup
66
+ if (this.logging && this.logsDir)
67
+ mkdirSync(this.logsDir, { recursive: true }); //only if the user wants logging
68
+
69
+ //Creating the database file if it doesn't exist
70
+ if (!existsSync(this.path)) {
71
+ const initialData = this.encryption
72
+ ? encrypt({}, this.secret)
73
+ : JSON.stringify({});
74
+ writeFileSync(this.path, initialData, "utf8");
75
+ /** @type {Record<string, any>|null} */
76
+ this._cache = {};
77
+ /** @type {import("node:fs").Stats} */
78
+ const stat = statSync(this.path);
79
+ /** @type {number} Last known modification time of the database file */
80
+ this._cacheMtime = stat.mtimeMs;
81
+ /** @type {number} Last known size of the database file */
82
+ this._cacheSize = stat.size;
83
+ } else {
84
+ /** @type {Record<string, any>|null} */
85
+ this._cache = null;
86
+ /** @type {number} */
87
+ this._cacheMtime = 0;
88
+ }
89
+ if (this.logging && this.logsPath && !existsSync(this.logsPath))
90
+ writeFileSync(this.logsPath, "", "utf8");
91
+ }
92
+ /**
93
+ * @returns {Record<string, any>} The data from the database
94
+ */
95
+ read() {
96
+ // Validate cache freshness by comparing file mtime
97
+ const stat = statSync(this.path);
98
+ const currentMtime = stat.mtimeMs;
99
+ if (
100
+ this._cache !== null &&
101
+ currentMtime === this._cacheMtime &&
102
+ stat.size === this._cacheSize
103
+ ) {
104
+ return this._cache;
105
+ }
106
+ const data = read(
107
+ this.path,
108
+ this._error.bind(this),
109
+ this.encryption,
110
+ this.secret || "",
111
+ );
112
+ if (data === null || Array.isArray(data) || typeof data !== "object") {
113
+ this._error("[INVALID_DATABASE] The database root must be an object.");
114
+ }
115
+ this._cache = data;
116
+ this._cacheMtime = currentMtime;
117
+ this._cacheSize = stat.size;
118
+ return data;
119
+ }
120
+
121
+ /**
122
+ * @param {Object} data - Data to write
123
+ * @returns {void}
124
+ */
125
+ write(data) {
126
+ write(
127
+ this.path,
128
+ data,
129
+ this._error.bind(this),
130
+ this.emit.bind(this),
131
+ this.encryption,
132
+ this.secret,
133
+ );
134
+ this.createBackup();
135
+ this._cache = data;
136
+ this._cacheMtime = statSync(this.path).mtimeMs;
137
+ }
138
+
139
+ createBackup() {
140
+ if (!this.backup || !this.backupDir) return false;
141
+ let maxBackups = 20;
142
+ const backupDir = this.backupDir || join(this.baseDir, "backups");
143
+ mkdirSync(backupDir, { recursive: true });
144
+ //Getting all backup files
145
+ const files = readdirSync(backupDir)
146
+ .filter((f) => f.startsWith("backup_"))
147
+ .map((file) => {
148
+ const fullPath = join(backupDir, file);
149
+ const stats = statSync(fullPath);
150
+
151
+ return {
152
+ path: fullPath,
153
+ time: stats.mtimeMs,
154
+ };
155
+ })
156
+ .sort((a, b) => a.time - b.time);
157
+
158
+ if (files.length - maxBackups > 0) {
159
+ for (let i = 0; i < files.length - maxBackups; i++) {
160
+ unlinkSync(files[i].path);
161
+ }
162
+ }
163
+ //Creates a new backup
164
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
165
+ const backupFile = join(backupDir, `backup_${stamp}-${randomId5()}.json`);
166
+ copyFileSync(this.path, backupFile);
167
+ this._log("create backup", { backupFile });
168
+ return backupFile;
169
+ }
170
+ }
@@ -1,167 +1,167 @@
1
- import { writeFileSync, existsSync, mkdirSync, statSync } from "fs";
2
- import { stat, mkdir, readdir, unlink, copyFile } from "node:fs/promises";
3
- import { read, write, randomId5 } from "../helpers/utilsAsync.js";
4
- import { encrypt, decrypt } from "../helpers/crypto.js";
5
- import { resolve, join } from "path";
6
-
7
- /**
8
- * @fileoverview FileStorage is a storage class that stores data in a file.
9
- * @class FileStorage
10
- */
11
- export default class FileStorageAsync {
12
- /**
13
- * @description Creates a new instance of FileStorage.
14
- * @param {Object} options - Options for the FileStorage.
15
- * @param {string} options.baseDir - The base directory for the database.
16
- * @param {boolean} [options.encryption] - Whether to encrypt the data.
17
- * @param {string} [options.secret] - The secret key for encryption.
18
- * @param {boolean} [options.backup] - Whether to backup the data.
19
- * @param {boolean} [options.logging] - Whether to log the data.
20
- * @param {*} options.error - Function to handle errors.
21
- * @param {function(...any): void} options.emit - Function to emit events.
22
- * @param {function(string, any): void} options.log - Function to log the data.
23
- * @memberof FileStorage
24
- * @constructor
25
- */
26
- constructor({
27
- baseDir,
28
- encryption = false,
29
- secret = "",
30
- backup = false,
31
- logging = false,
32
- error,
33
- emit,
34
- log,
35
- }) {
36
- this.baseDir = resolve(baseDir) || resolve("./snowbase");
37
- this.encryption = encryption;
38
- this.secret = secret;
39
- this.backup = backup;
40
- this.logging = logging;
41
- this._error = error;
42
- this.emit = emit;
43
- this._log = log;
44
- this.path = join(this.baseDir, "snowbase.json");
45
-
46
- if (this.logging) {
47
- this.logsDir = join(this.baseDir, "logs");
48
- }
49
- if (this.backup) {
50
- this.backupDir = join(this.baseDir, "backups");
51
- }
52
- this.path = join(this.baseDir, "snowbase.json");
53
- if (this.logsDir) this.logsPath = join(this.logsDir, "snowbase.log");
54
-
55
- //Creating directories if they don't exist
56
- mkdirSync(this.baseDir, { recursive: true });
57
- if (this.backup && this.backupDir)
58
- mkdirSync(this.backupDir, { recursive: true }); //only if the user wants backup
59
- if (this.logging && this.logsDir)
60
- mkdirSync(this.logsDir, { recursive: true }); //only if the user wants logging
61
-
62
- //Creating the database file if it doesn't exist
63
- if (!existsSync(this.path)) {
64
- const initialData = this.encryption
65
- ? encrypt({}, this.secret)
66
- : JSON.stringify({});
67
- writeFileSync(this.path, initialData, "utf8");
68
- /** @type {Record<string, any>|null} */
69
- this._cache = {};
70
- /** @type {import("node:fs").Stats} */
71
- const stat = statSync(this.path);
72
- /** @type {number} Last known modification time of the database file */
73
- this._cacheMtime = stat.mtimeMs;
74
- /** @type {number} Last known size of the database file */
75
- this._cacheSize = stat.size;
76
- } else {
77
- /** @type {Record<string, any>|null} */
78
- this._cache = null;
79
- /** @type {number} */
80
- this._cacheMtime = 0;
81
- }
82
- if (this.logging && this.logsPath && !existsSync(this.logsPath))
83
- writeFileSync(this.logsPath, "", "utf8");
84
- }
85
- /**
86
- * @returns {Promise<Record<string, any>>} The data from the database
87
- */
88
- async readAsync() {
89
- // Validate cache freshness by comparing file mtime
90
- const fileStat = await stat(this.path);
91
- const currentMtime = fileStat.mtimeMs;
92
- if (
93
- this._cache !== null &&
94
- currentMtime === this._cacheMtime &&
95
- fileStat.size === this._cacheSize
96
- ) {
97
- return this._cache;
98
- }
99
- const data = await read(
100
- this.path,
101
- this._error.bind(this),
102
- this.encryption,
103
- this.secret || "",
104
- );
105
- if (data === null || Array.isArray(data) || typeof data !== "object") {
106
- this._error("[INVALID_DATABASE] The database root must be an object.");
107
- }
108
- this._cache = data;
109
- this._cacheMtime = currentMtime;
110
- this._cacheSize = fileStat.size;
111
- return data;
112
- }
113
-
114
- /**
115
- * @param {Object} data - Data to write
116
- * @returns {Promise<void>}
117
- */
118
- async writeAsync(data) {
119
- await write(
120
- this.path,
121
- data,
122
- this._error.bind(this),
123
- this.emit.bind(this),
124
- this.encryption,
125
- this.secret,
126
- );
127
- await this.createBackupAsync();
128
- this._cache = data;
129
- this._cacheMtime = (await stat(this.path)).mtimeMs;
130
- }
131
-
132
- async createBackupAsync() {
133
- if (!this.backup || !this.backupDir) return;
134
- const maxBackups = 20;
135
- const backupDir = this.backupDir || join(this.baseDir, "backups");
136
- await mkdir(backupDir, { recursive: true });
137
- //Getting all backup files
138
- const files = (
139
- await Promise.all(
140
- (await readdir(backupDir))
141
- .filter((f) => f.startsWith("backup_"))
142
- .map(async (file) => {
143
- const fullPath = join(backupDir, file);
144
- const stats = await stat(fullPath);
145
-
146
- return {
147
- path: fullPath,
148
- time: stats.mtimeMs,
149
- };
150
- }),
151
- )
152
- ).sort((a, b) => a.time - b.time);
153
-
154
- if (files.length - maxBackups > 0) {
155
- for (let i = 0; i < files.length - maxBackups; i++) {
156
- await unlink(files[i].path);
157
- }
158
- }
159
- //Creates a new backup
160
- const stamp = new Date().toISOString().replace(/[:.]/g, "-");
161
- const backupFile = join(backupDir, `backup_${stamp}-${randomId5()}.json`);
162
-
163
- await copyFile(this.path, backupFile);
164
- this._log("create backup", { backupFile });
165
- return backupFile;
166
- }
167
- }
1
+ import { writeFileSync, existsSync, mkdirSync, statSync } from "fs";
2
+ import { stat, mkdir, readdir, unlink, copyFile } from "node:fs/promises";
3
+ import { read, write, randomId5 } from "../helpers/utilsAsync.js";
4
+ import { encrypt, decrypt } from "../helpers/crypto.js";
5
+ import { resolve, join } from "path";
6
+
7
+ /**
8
+ * @fileoverview FileStorage is a storage class that stores data in a file.
9
+ * @class FileStorage
10
+ */
11
+ export default class FileStorageAsync {
12
+ /**
13
+ * @description Creates a new instance of FileStorage.
14
+ * @param {Object} options - Options for the FileStorage.
15
+ * @param {string} options.baseDir - The base directory for the database.
16
+ * @param {boolean} [options.encryption] - Whether to encrypt the data.
17
+ * @param {string} [options.secret] - The secret key for encryption.
18
+ * @param {boolean} [options.backup] - Whether to backup the data.
19
+ * @param {boolean} [options.logging] - Whether to log the data.
20
+ * @param {*} options.error - Function to handle errors.
21
+ * @param {function(...any): void} options.emit - Function to emit events.
22
+ * @param {function(string, any): void} options.log - Function to log the data.
23
+ * @memberof FileStorage
24
+ * @constructor
25
+ */
26
+ constructor({
27
+ baseDir,
28
+ encryption = false,
29
+ secret = "",
30
+ backup = false,
31
+ logging = false,
32
+ error,
33
+ emit,
34
+ log,
35
+ }) {
36
+ this.baseDir = resolve(baseDir) || resolve("./snowbase");
37
+ this.encryption = encryption;
38
+ this.secret = secret;
39
+ this.backup = backup;
40
+ this.logging = logging;
41
+ this._error = error;
42
+ this.emit = emit;
43
+ this._log = log;
44
+ this.path = join(this.baseDir, "snowbase.json");
45
+
46
+ if (this.logging) {
47
+ this.logsDir = join(this.baseDir, "logs");
48
+ }
49
+ if (this.backup) {
50
+ this.backupDir = join(this.baseDir, "backups");
51
+ }
52
+ this.path = join(this.baseDir, "snowbase.json");
53
+ if (this.logsDir) this.logsPath = join(this.logsDir, "snowbase.log");
54
+
55
+ //Creating directories if they don't exist
56
+ mkdirSync(this.baseDir, { recursive: true });
57
+ if (this.backup && this.backupDir)
58
+ mkdirSync(this.backupDir, { recursive: true }); //only if the user wants backup
59
+ if (this.logging && this.logsDir)
60
+ mkdirSync(this.logsDir, { recursive: true }); //only if the user wants logging
61
+
62
+ //Creating the database file if it doesn't exist
63
+ if (!existsSync(this.path)) {
64
+ const initialData = this.encryption
65
+ ? encrypt({}, this.secret)
66
+ : JSON.stringify({});
67
+ writeFileSync(this.path, initialData, "utf8");
68
+ /** @type {Record<string, any>|null} */
69
+ this._cache = {};
70
+ /** @type {import("node:fs").Stats} */
71
+ const stat = statSync(this.path);
72
+ /** @type {number} Last known modification time of the database file */
73
+ this._cacheMtime = stat.mtimeMs;
74
+ /** @type {number} Last known size of the database file */
75
+ this._cacheSize = stat.size;
76
+ } else {
77
+ /** @type {Record<string, any>|null} */
78
+ this._cache = null;
79
+ /** @type {number} */
80
+ this._cacheMtime = 0;
81
+ }
82
+ if (this.logging && this.logsPath && !existsSync(this.logsPath))
83
+ writeFileSync(this.logsPath, "", "utf8");
84
+ }
85
+ /**
86
+ * @returns {Promise<Record<string, any>>} The data from the database
87
+ */
88
+ async readAsync() {
89
+ // Validate cache freshness by comparing file mtime
90
+ const fileStat = await stat(this.path);
91
+ const currentMtime = fileStat.mtimeMs;
92
+ if (
93
+ this._cache !== null &&
94
+ currentMtime === this._cacheMtime &&
95
+ fileStat.size === this._cacheSize
96
+ ) {
97
+ return this._cache;
98
+ }
99
+ const data = await read(
100
+ this.path,
101
+ this._error.bind(this),
102
+ this.encryption,
103
+ this.secret || "",
104
+ );
105
+ if (data === null || Array.isArray(data) || typeof data !== "object") {
106
+ this._error("[INVALID_DATABASE] The database root must be an object.");
107
+ }
108
+ this._cache = data;
109
+ this._cacheMtime = currentMtime;
110
+ this._cacheSize = fileStat.size;
111
+ return data;
112
+ }
113
+
114
+ /**
115
+ * @param {Object} data - Data to write
116
+ * @returns {Promise<void>}
117
+ */
118
+ async writeAsync(data) {
119
+ await write(
120
+ this.path,
121
+ data,
122
+ this._error.bind(this),
123
+ this.emit.bind(this),
124
+ this.encryption,
125
+ this.secret,
126
+ );
127
+ await this.createBackupAsync();
128
+ this._cache = data;
129
+ this._cacheMtime = (await stat(this.path)).mtimeMs;
130
+ }
131
+
132
+ async createBackupAsync() {
133
+ if (!this.backup || !this.backupDir) return;
134
+ const maxBackups = 20;
135
+ const backupDir = this.backupDir || join(this.baseDir, "backups");
136
+ await mkdir(backupDir, { recursive: true });
137
+ //Getting all backup files
138
+ const files = (
139
+ await Promise.all(
140
+ (await readdir(backupDir))
141
+ .filter((f) => f.startsWith("backup_"))
142
+ .map(async (file) => {
143
+ const fullPath = join(backupDir, file);
144
+ const stats = await stat(fullPath);
145
+
146
+ return {
147
+ path: fullPath,
148
+ time: stats.mtimeMs,
149
+ };
150
+ }),
151
+ )
152
+ ).sort((a, b) => a.time - b.time);
153
+
154
+ if (files.length - maxBackups > 0) {
155
+ for (let i = 0; i < files.length - maxBackups; i++) {
156
+ await unlink(files[i].path);
157
+ }
158
+ }
159
+ //Creates a new backup
160
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
161
+ const backupFile = join(backupDir, `backup_${stamp}-${randomId5()}.json`);
162
+
163
+ await copyFile(this.path, backupFile);
164
+ this._log("create backup", { backupFile });
165
+ return backupFile;
166
+ }
167
+ }