@iobroker/db-objects-file 7.2.3 → 8.0.0-alpha.1-20260921-e5941ca8a

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 (25) hide show
  1. package/build/cjs/index.js +5 -3
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/cjs/lib/objects/objectsInMemFileDB.d.ts +288 -59
  4. package/build/cjs/lib/objects/objectsInMemFileDB.js +225 -109
  5. package/build/cjs/lib/objects/objectsInMemFileDB.js.map +3 -3
  6. package/build/cjs/lib/objects/objectsInMemServerClass.d.ts +41 -8
  7. package/build/cjs/lib/objects/objectsInMemServerClass.js +27 -2
  8. package/build/cjs/lib/objects/objectsInMemServerClass.js.map +3 -3
  9. package/build/cjs/lib/objects/objectsInMemServerRedis.d.ts +50 -30
  10. package/build/cjs/lib/objects/objectsInMemServerRedis.js +72 -37
  11. package/build/cjs/lib/objects/objectsInMemServerRedis.js.map +4 -4
  12. package/build/esm/lib/objects/objectsInMemFileDB.d.ts +288 -59
  13. package/build/esm/lib/objects/objectsInMemFileDB.d.ts.map +1 -1
  14. package/build/esm/lib/objects/objectsInMemFileDB.js +228 -126
  15. package/build/esm/lib/objects/objectsInMemFileDB.js.map +1 -1
  16. package/build/esm/lib/objects/objectsInMemServerClass.d.ts +41 -8
  17. package/build/esm/lib/objects/objectsInMemServerClass.d.ts.map +1 -1
  18. package/build/esm/lib/objects/objectsInMemServerClass.js +25 -1
  19. package/build/esm/lib/objects/objectsInMemServerClass.js.map +1 -1
  20. package/build/esm/lib/objects/objectsInMemServerRedis.d.ts +50 -30
  21. package/build/esm/lib/objects/objectsInMemServerRedis.d.ts.map +1 -1
  22. package/build/esm/lib/objects/objectsInMemServerRedis.js +89 -70
  23. package/build/esm/lib/objects/objectsInMemServerRedis.js.map +1 -1
  24. package/build/tsconfig.build.tsbuildinfo +1 -1
  25. package/package.json +7 -7
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
9
  var __export = (target, all) => {
9
10
  for (var name in all)
10
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -38,56 +39,69 @@ var import_db_base2 = require("@iobroker/db-base");
38
39
  var import_db_objects_redis = require("@iobroker/db-objects-redis");
39
40
  var import_deep_clone = __toESM(require("deep-clone"), 1);
40
41
  class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
42
+ static {
43
+ __name(this, "ObjectsInMemoryFileDB");
44
+ }
45
+ META_ID = "**META**";
46
+ fileOptions = {};
47
+ files = {};
48
+ writeTimer = null;
49
+ writeIds = [];
50
+ defaultNewAcl;
51
+ writeFileInterval;
52
+ objectsDir;
53
+ // cached meta information for file operations
54
+ existingMetaObjects = {};
55
+ /**
56
+ * @param settings Settings for the objects database
57
+ */
41
58
  constructor(settings) {
42
- settings = settings || {};
43
- settings.fileDB = settings.fileDB || {
59
+ settings.fileDB ??= {
44
60
  fileName: "objects.json",
45
61
  backupDirName: "backup-objects"
46
62
  };
47
63
  super(settings);
48
- if (!this.change) {
49
- this.change = (id) => {
50
- this.log.silly(`${this.namespace} objects change: ${id} ${JSON.stringify(this.change)}`);
51
- };
52
- }
53
- this.META_ID = "**META**";
54
- this.fileOptions = {};
55
- this.files = {};
56
- this.writeTimer = null;
57
- this.writeIds = [];
58
- this.preserveSettings = ["custom"];
64
+ this.change ||= (id) => {
65
+ this.log.silly(`${this.namespace} objects change: ${id} ${JSON.stringify(this.change)}`);
66
+ };
59
67
  this.defaultNewAcl = this.settings.defaultNewAcl || null;
60
68
  this.namespace = this.settings.namespace || this.settings.hostname || "";
61
- this.writeFileInterval = this.settings.connection && typeof this.settings.connection.writeFileInterval === "number" ? parseInt(this.settings.connection.writeFileInterval) : 5e3;
69
+ this.writeFileInterval = this.settings.connection && typeof this.settings.connection.writeFileInterval === "number" ? this.settings.connection.writeFileInterval : 5e3;
62
70
  if (!settings.jsonlDB) {
63
71
  this.log.silly(`${this.namespace} Objects DB uses file write interval of ${this.writeFileInterval} ms`);
64
72
  }
65
73
  this.objectsDir = import_node_path.default.join(this.dataDir, "files");
66
- this.existingMetaObjects = {};
67
- for (const obj of Object.values(this.dataset)) {
68
- if (import_db_base2.tools.isObject(obj) && obj.acl && obj.acl.permissions && !obj.acl.object) {
69
- obj.acl.object = obj.acl.permissions;
70
- delete obj.acl.permissions;
71
- }
72
- }
73
74
  const configObj = this.dataset["system.config"];
74
- if (configObj && configObj.common && configObj.common.defaultNewAcl) {
75
+ if (configObj?.common?.defaultNewAcl) {
75
76
  this.defaultNewAcl = (0, import_deep_clone.default)(configObj.common.defaultNewAcl);
76
77
  }
77
78
  }
78
- // internal functionality
79
+ /**
80
+ * Normalize a file name by collapsing slashes and backslashes into a single forward slash
81
+ *
82
+ * @param name The file name to normalize
83
+ */
79
84
  _normalizeFilename(name) {
80
85
  return name ? name.replace(/[/\\]+/g, "/") : name;
81
86
  }
82
87
  // -------------- FILE FUNCTIONS -------------------------------------------
83
- // internal functionality
88
+ /**
89
+ * Schedule writing the file settings (_data.json) to disk
90
+ *
91
+ * @param id The object ID whose file settings should be saved, or a boolean used as the force flag
92
+ * @param force If true, write the settings immediately instead of debounced
93
+ */
84
94
  _saveFileSettings(id, force) {
85
95
  if (typeof id === "boolean") {
86
96
  force = id;
87
97
  id = void 0;
88
98
  }
89
- id !== void 0 && !this.writeIds.includes(id) && this.writeIds.push(id);
90
- this.writeTimer && clearTimeout(this.writeTimer);
99
+ if (id !== void 0 && !this.writeIds.includes(id)) {
100
+ this.writeIds.push(id);
101
+ }
102
+ if (this.writeTimer) {
103
+ clearTimeout(this.writeTimer);
104
+ }
91
105
  if (force) {
92
106
  this.writeTimer = null;
93
107
  for (const writeId of this.writeIds) {
@@ -115,7 +129,11 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
115
129
  }, 1e3);
116
130
  }
117
131
  }
118
- // internal functionality
132
+ /**
133
+ * Load the file settings (_data.json) for the given object ID into memory
134
+ *
135
+ * @param id The object ID whose file settings should be loaded
136
+ */
119
137
  _loadFileSettings(id) {
120
138
  if (!this.fileOptions[id]) {
121
139
  const location = import_node_path.default.join(this.objectsDir, id, "_data.json");
@@ -148,7 +166,11 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
148
166
  }
149
167
  }
150
168
  }
151
- // server only functionality
169
+ /**
170
+ * Synchronize the in-memory file metadata with the files actually present on disk (server only)
171
+ *
172
+ * @param limitId Optional object ID to limit the synchronization to
173
+ */
152
174
  syncFileDirectory(limitId) {
153
175
  const resNotifies = [];
154
176
  let resSynced = 0;
@@ -158,7 +180,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
158
180
  list.forEach((file) => {
159
181
  file = `${dir}/${file}`;
160
182
  const stat = import_fs_extra.default.statSync(file);
161
- if (stat && stat.isDirectory()) {
183
+ if (stat?.isDirectory()) {
162
184
  results = results.concat(getAllFiles(file));
163
185
  } else {
164
186
  results.push(file);
@@ -166,6 +188,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
166
188
  });
167
189
  return results;
168
190
  }
191
+ __name(getAllFiles, "getAllFiles");
169
192
  const res = this._getObjectView("system", "meta", null);
170
193
  const metaIds = res.rows.map((obj) => obj.id).filter((id) => !limitId || limitId === id);
171
194
  if (!import_fs_extra.default.existsSync(this.objectsDir)) {
@@ -208,9 +231,9 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
208
231
  this.fileOptions[dir][localFile] = {
209
232
  createdAt: fileStat.ctimeMs,
210
233
  acl: {
211
- owner: this.defaultNewAcl && this.defaultNewAcl.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
212
- ownerGroup: this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
213
- permissions: this.defaultNewAcl && this.defaultNewAcl.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_RW | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ
234
+ owner: this.defaultNewAcl?.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
235
+ ownerGroup: this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
236
+ permissions: this.defaultNewAcl?.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_RW | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ
214
237
  // 0x644
215
238
  },
216
239
  mimeType: _mimeType,
@@ -222,27 +245,36 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
222
245
  });
223
246
  this._saveFileSettings(dir);
224
247
  resSynced += dirSynced;
225
- dirSynced && resNotifies.push(`Added ${dirSynced} Files in Directory "${dir}"`);
248
+ if (dirSynced) {
249
+ resNotifies.push(`Added ${dirSynced} Files in Directory "${dir}"`);
250
+ }
226
251
  });
227
252
  return {
228
253
  numberSuccess: resSynced,
229
254
  notifications: resNotifies
230
255
  };
231
256
  }
232
- // needed by server
257
+ /**
258
+ * Write a file into an object's file storage (used by the server)
259
+ *
260
+ * @param id The object ID owning the file
261
+ * @param name The file name
262
+ * @param data The file content
263
+ * @param options Optional write options, or the mime type as a string
264
+ */
233
265
  _writeFile(id, name, data, options) {
234
266
  if (typeof options === "string") {
235
267
  options = { mimeType: options };
236
268
  }
237
- if (options && options.acl) {
269
+ if (options?.acl) {
238
270
  options.acl = null;
239
271
  }
240
272
  const _path = import_db_objects_redis.objectsUtils.sanitizePath(id, name);
241
273
  id = _path.id;
242
274
  name = _path.name;
243
- options = options || {};
275
+ options ||= {};
244
276
  this._loadFileSettings(id);
245
- this.files[id] = this.files[id] || {};
277
+ this.files[id] ||= {};
246
278
  try {
247
279
  if (!import_fs_extra.default.existsSync(this.objectsDir)) {
248
280
  import_fs_extra.default.mkdirSync(this.objectsDir);
@@ -259,16 +291,16 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
259
291
  const mime = import_db_objects_redis.objectsUtils.getMimeType(ext);
260
292
  const _mimeType = mime.mimeType;
261
293
  const isBinary = mime.isBinary;
262
- this.fileOptions[id][name] = this.fileOptions[id][name] || { createdAt: Date.now() };
263
- this.fileOptions[id][name].acl = this.fileOptions[id][name].acl || {
264
- owner: options.user || this.defaultNewAcl && this.defaultNewAcl.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
265
- ownerGroup: options.group || this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
266
- permissions: options.mode || this.defaultNewAcl && this.defaultNewAcl.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_RW | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ
294
+ this.fileOptions[id][name] ||= { createdAt: Date.now() };
295
+ this.fileOptions[id][name].acl ||= {
296
+ owner: options.user || this.defaultNewAcl?.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
297
+ ownerGroup: options.group || this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
298
+ permissions: options.mode || this.defaultNewAcl?.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_RW | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ
267
299
  // 0x644
268
300
  };
269
301
  this.fileOptions[id][name].mimeType = options.mimeType || _mimeType;
270
302
  this.fileOptions[id][name].binary = isBinary;
271
- this.fileOptions[id][name].acl.ownerGroup = this.fileOptions[id][name].acl.ownerGroup || this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP;
303
+ this.fileOptions[id][name].acl.ownerGroup ||= this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP;
272
304
  this.fileOptions[id][name].modifiedAt = Date.now();
273
305
  try {
274
306
  import_fs_extra.default.ensureDirSync(import_node_path.default.join(this.objectsDir, id, import_node_path.default.dirname(name)));
@@ -289,28 +321,34 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
289
321
  setImmediate((name2, size) => {
290
322
  this.log.silly(`${this.namespace} memory publish ${id} ${JSON.stringify({ name: name2, file: true, size })}`);
291
323
  this.publishAll("files", `${id}$%$${name2}`, size);
292
- }, name, data.byteLength);
324
+ }, name, Buffer.byteLength(data));
293
325
  }
294
- // needed by server
326
+ /**
327
+ * Read a file from an object's file storage (used by the server)
328
+ *
329
+ * @param id The object ID owning the file
330
+ * @param name The file name
331
+ * @param options Optional read options
332
+ */
295
333
  _readFile(id, name, options) {
296
- if (options && options.acl) {
334
+ if (options?.acl) {
297
335
  options.acl = null;
298
336
  }
299
337
  const _path = import_db_objects_redis.objectsUtils.sanitizePath(id, name);
300
338
  id = _path.id;
301
339
  name = _path.name;
302
- options = options || {};
340
+ options ||= {};
303
341
  try {
304
342
  this._loadFileSettings(id);
305
- this.files[id] = this.files[id] || {};
343
+ this.files[id] ||= {};
306
344
  if (!this.files[id][name] || this.settings.connection.noFileCache || options.noFileCache) {
307
345
  const location = import_node_path.default.join(this.objectsDir, id, name);
308
346
  if (import_fs_extra.default.existsSync(location)) {
309
- this.fileOptions[id][name] = this.fileOptions[id][name] || {
347
+ this.fileOptions[id][name] ||= {
310
348
  acl: {
311
- owner: this.defaultNewAcl && this.defaultNewAcl.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
312
- ownerGroup: this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
313
- permissions: this.defaultNewAcl && this.defaultNewAcl.file.permissions || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_ALL
349
+ owner: this.defaultNewAcl?.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
350
+ ownerGroup: this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
351
+ permissions: this.defaultNewAcl?.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_ALL
314
352
  // 777
315
353
  }
316
354
  };
@@ -318,9 +356,9 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
318
356
  this.fileOptions[id][name] = {
319
357
  mimeType: this.fileOptions[id][name],
320
358
  acl: {
321
- owner: this.defaultNewAcl && this.defaultNewAcl.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
322
- ownerGroup: this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
323
- permissions: this.defaultNewAcl && this.defaultNewAcl.file.permissions || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_ALL
359
+ owner: this.defaultNewAcl?.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
360
+ ownerGroup: this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
361
+ permissions: this.defaultNewAcl?.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_ALL
324
362
  // 777
325
363
  }
326
364
  };
@@ -332,10 +370,8 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
332
370
  this.fileOptions[id][name].binary = mimeType.isBinary;
333
371
  this.fileOptions[id][name].mimeType = mimeType.mimeType;
334
372
  }
335
- if (!this.fileOptions[id][name].binary) {
336
- if (this.files[id][name]) {
337
- this.files[id][name] = this.files[id][name].toString();
338
- }
373
+ if (!this.fileOptions[id][name].binary && this.files[id][name]) {
374
+ this.files[id][name] = this.files[id][name].toString();
339
375
  }
340
376
  } else {
341
377
  if (this.fileOptions[id][name] !== void 0) {
@@ -348,9 +384,9 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
348
384
  }
349
385
  if (this.fileOptions[id][name] && !this.fileOptions[id][name].acl) {
350
386
  this.fileOptions[id][name].acl = {
351
- owner: this.defaultNewAcl && this.defaultNewAcl.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
352
- ownerGroup: this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
353
- permissions: this.defaultNewAcl && this.defaultNewAcl.file.permissions || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_RW
387
+ owner: this.defaultNewAcl?.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
388
+ ownerGroup: this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
389
+ permissions: this.defaultNewAcl?.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_ALL | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_RW
354
390
  // 776
355
391
  };
356
392
  }
@@ -394,7 +430,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
394
430
  *
395
431
  * @param id id of the namespace
396
432
  * @param [name] name of the file
397
- * @returns
433
+ * @returns true if the file exists
398
434
  */
399
435
  // needed by server
400
436
  _fileExists(id, name) {
@@ -418,7 +454,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
418
454
  *
419
455
  * @param id id of the namespace
420
456
  * @param [name] name of the directory
421
- * @returns
457
+ * @returns true if the directory exists
422
458
  */
423
459
  // special functionality only for Server (used together with SyncFileDirectory)
424
460
  dirExists(id, name) {
@@ -437,7 +473,12 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
437
473
  return false;
438
474
  }
439
475
  }
440
- // needed by server
476
+ /**
477
+ * Delete a file or directory from an object's file storage (used by the server)
478
+ *
479
+ * @param id The object ID owning the file
480
+ * @param name The file or directory name to delete
481
+ */
441
482
  _unlink(id, name) {
442
483
  const _path = import_db_objects_redis.objectsUtils.sanitizePath(id, name);
443
484
  id = _path.id;
@@ -458,7 +499,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
458
499
  if (this.fileOptions[id]) {
459
500
  delete this.fileOptions[id];
460
501
  }
461
- if (this.files[id] && this.files[id]) {
502
+ if (this.files[id]) {
462
503
  delete this.files[id];
463
504
  }
464
505
  } else {
@@ -469,10 +510,10 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
469
510
  this.log.error(`${this.namespace} Cannot delete file "${import_node_path.default.join(id, name)}": ${e.message}`);
470
511
  throw e;
471
512
  }
472
- if (this.fileOptions[id][name]) {
513
+ if (this.fileOptions[id]?.[name]) {
473
514
  delete this.fileOptions[id][name];
474
515
  }
475
- if (this.files[id] && this.files[id][name]) {
516
+ if (this.files[id]?.[name]) {
476
517
  delete this.files[id][name];
477
518
  }
478
519
  this._saveFileSettings(id, true);
@@ -483,9 +524,15 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
483
524
  }, id, name);
484
525
  }
485
526
  }
486
- // needed by server
527
+ /**
528
+ * List the contents of a directory in an object's file storage (used by the server)
529
+ *
530
+ * @param id The object ID owning the files
531
+ * @param name The directory name to list
532
+ * @param options Optional read options
533
+ */
487
534
  _readDir(id, name, options) {
488
- if (options && options.acl) {
535
+ if (options?.acl) {
489
536
  options.acl = null;
490
537
  }
491
538
  if ((id === "" || id === "/" || id === "*") && (name === "" || name === "*")) {
@@ -494,7 +541,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
494
541
  id = _path.id;
495
542
  name = _path.name;
496
543
  }
497
- options = options || {};
544
+ options ||= {};
498
545
  const _files = [];
499
546
  if (id && id === "*") {
500
547
  id = "";
@@ -506,9 +553,8 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
506
553
  const len = name ? name.length : 0;
507
554
  for (const f2 of Object.keys(this.fileOptions[id])) {
508
555
  if (!name || f2.substring(0, len) === name) {
509
- let rest = f2.substring(len);
510
- rest = rest.split("/", 2);
511
- if (rest[0] && _files.indexOf(rest[0]) === -1) {
556
+ const rest = f2.substring(len).split("/", 2);
557
+ if (rest[0] && !_files.includes(rest[0])) {
512
558
  _files.push(rest[0]);
513
559
  }
514
560
  }
@@ -520,7 +566,7 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
520
566
  if (dirFiles[i] === ".." || dirFiles[i] === ".") {
521
567
  continue;
522
568
  }
523
- if (dirFiles[i] !== "_data.json" && _files.indexOf(dirFiles[i]) === -1) {
569
+ if (dirFiles[i] !== "_data.json" && !_files.includes(dirFiles[i])) {
524
570
  _files.push(dirFiles[i]);
525
571
  }
526
572
  }
@@ -536,23 +582,23 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
536
582
  if (import_fs_extra.default.existsSync(import_node_path.default.join(location, file))) {
537
583
  try {
538
584
  const stats = import_fs_extra.default.statSync(import_node_path.default.join(location, file));
539
- const acl = this.fileOptions[id][name + file] && this.fileOptions[id][name + file].acl ? (0, import_deep_clone.default)(this.fileOptions[id][name + file].acl) : {
585
+ const acl = this.fileOptions[id][name + file]?.acl ? (0, import_deep_clone.default)(this.fileOptions[id][name + file].acl) : {
540
586
  read: true,
541
587
  write: true,
542
- owner: this.defaultNewAcl && this.defaultNewAcl.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
543
- ownerGroup: this.defaultNewAcl && this.defaultNewAcl.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
544
- permissions: this.defaultNewAcl && this.defaultNewAcl.file.permissions || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_RW | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ
588
+ owner: this.defaultNewAcl?.owner || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER,
589
+ ownerGroup: this.defaultNewAcl?.ownerGroup || import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP,
590
+ permissions: this.defaultNewAcl?.file || import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_RW | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ
545
591
  };
546
592
  if (options.filter && acl) {
547
- if (!options.acl.file.write) {
593
+ if (!options.acl?.file.write) {
548
594
  acl.permissions &= ~(import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_WRITE | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_WRITE | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_WRITE);
549
595
  }
550
- if (!options.acl.file.read) {
596
+ if (!options.acl?.file.read) {
551
597
  acl.permissions &= ~(import_db_objects_redis.objectsUtils.CONSTS.ACCESS_USER_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_READ | import_db_objects_redis.objectsUtils.CONSTS.ACCESS_EVERY_READ);
552
598
  }
553
- if (options.user !== import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER && options.groups.includes(import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP)) {
599
+ if (options.user !== import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_USER && options.groups?.includes(import_db_objects_redis.objectsUtils.CONSTS.SYSTEM_ADMIN_GROUP)) {
554
600
  if (acl.owner !== options.user) {
555
- if (options.groups.includes(acl.ownerGroup)) {
601
+ if (options.groups?.includes(acl.ownerGroup)) {
556
602
  if (!(acl.permissions & import_db_objects_redis.objectsUtils.CONSTS.ACCESS_GROUP_RW)) {
557
603
  continue;
558
604
  }
@@ -592,7 +638,13 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
592
638
  }
593
639
  return res;
594
640
  }
595
- // needed by server
641
+ /**
642
+ * Rename a file or directory in an object's file storage (used by the server)
643
+ *
644
+ * @param id The object ID owning the file
645
+ * @param oldName The current file or directory name
646
+ * @param newName The new file or directory name
647
+ */
596
648
  _rename(id, oldName, newName) {
597
649
  const _path = import_db_objects_redis.objectsUtils.sanitizePath(id, oldName);
598
650
  id = _path.id;
@@ -622,7 +674,11 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
622
674
  });
623
675
  this._saveFileSettings(id, true);
624
676
  }
625
- // internal functionality
677
+ /**
678
+ * Create a deep clone of the given object
679
+ *
680
+ * @param obj The object to clone
681
+ */
626
682
  _clone(obj) {
627
683
  if (obj === null || obj === void 0 || !import_db_base2.tools.isObject(obj)) {
628
684
  return obj;
@@ -633,18 +689,40 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
633
689
  }
634
690
  return temp;
635
691
  }
692
+ /**
693
+ * Subscribe a client to meta changes
694
+ *
695
+ * @param client The client to subscribe
696
+ * @param pattern The pattern of meta IDs to subscribe to
697
+ */
636
698
  _subscribeMeta(client, pattern) {
637
699
  this.handleSubscribe(client, "meta", pattern);
638
700
  }
639
- // needed by server
701
+ /**
702
+ * Subscribe a client to object changes (used by the server)
703
+ *
704
+ * @param client The client to subscribe
705
+ * @param pattern The pattern of object IDs to subscribe to
706
+ */
640
707
  _subscribeConfigForClient(client, pattern) {
641
708
  this.handleSubscribe(client, "objects", pattern);
642
709
  }
643
- // needed by server
710
+ /**
711
+ * Unsubscribe a client from object changes (used by the server)
712
+ *
713
+ * @param client The client to unsubscribe
714
+ * @param pattern The pattern of object IDs to unsubscribe from
715
+ */
644
716
  _unsubscribeConfigForClient(client, pattern) {
645
- this.handleUnsubscribe(client, "objects", pattern);
717
+ this.handleUnsubscribe(client, "objects", pattern).catch((e) => this.log.error(`${this.namespace} Cannot unsubscribe client from objects: ${e.message}`));
646
718
  }
647
- // needed by server
719
+ /**
720
+ * Subscribe a client to file changes of an object (used by the server)
721
+ *
722
+ * @param client The client to subscribe
723
+ * @param id The object ID owning the files
724
+ * @param pattern One or more file name patterns to subscribe to
725
+ */
648
726
  _subscribeFileForClient(client, id, pattern) {
649
727
  if (Array.isArray(pattern)) {
650
728
  pattern.forEach((pattern2) => this.handleSubscribe(client, "files", `${id}$%$${pattern2}`));
@@ -652,32 +730,53 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
652
730
  this.handleSubscribe(client, "files", `${id}$%$${pattern}`);
653
731
  }
654
732
  }
655
- // needed by server
733
+ /**
734
+ * Unsubscribe a client from file changes of an object (used by the server)
735
+ *
736
+ * @param client The client to unsubscribe
737
+ * @param id The object ID owning the files
738
+ * @param pattern One or more file name patterns to unsubscribe from
739
+ */
656
740
  _unsubscribeFileForClient(client, id, pattern) {
657
741
  if (Array.isArray(pattern)) {
658
742
  pattern.forEach((pattern2) => this.handleUnsubscribe(client, "files", `${id}$%$${pattern2}`));
659
743
  } else {
660
- this.handleUnsubscribe(client, "files", `${id}$%$${pattern}`);
744
+ this.handleUnsubscribe(client, "files", `${id}$%$${pattern}`).catch((e) => this.log.error(`${this.namespace} Cannot unsubscribe client from files: ${e.message}`));
661
745
  }
662
746
  }
663
- // needed by server
747
+ /**
748
+ * Get a single object by its ID (used by the server)
749
+ *
750
+ * @param id The object ID to read
751
+ */
664
752
  _getObject(id) {
665
753
  return this.dataset[id];
666
754
  }
667
- // needed by server
755
+ /**
756
+ * Get all object IDs matching the given pattern, sorted (used by the server)
757
+ *
758
+ * @param pattern The pattern to match object IDs against
759
+ */
668
760
  _getKeys(pattern) {
669
761
  const r = new RegExp(import_db_base2.tools.pattern2RegEx(pattern));
670
762
  const result2 = Object.keys(this.dataset).filter((id) => r.test(id) && id !== this.META_ID);
671
763
  result2.sort();
672
764
  return result2;
673
765
  }
674
- // needed by server
766
+ /**
767
+ * Get the values of the given object IDs (used by the server)
768
+ *
769
+ * @param keys The object IDs to read
770
+ */
675
771
  _getObjects(keys) {
676
772
  if (!keys) {
677
773
  throw new Error("no keys");
678
774
  }
679
- return keys.map((id) => this.dataset[id]);
775
+ return keys.map((id) => id ? this.dataset[id] : void 0);
680
776
  }
777
+ /**
778
+ * Get the meta dictionary, creating it if it does not exist yet
779
+ */
681
780
  _ensureMetaDict() {
682
781
  let meta = this.dataset[this.META_ID];
683
782
  if (!meta) {
@@ -689,8 +788,8 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
689
788
  /**
690
789
  * Get value of given meta id
691
790
  *
692
- * @param id
693
- * @returns
791
+ * @param id The meta ID to read
792
+ * @returns the stored meta value
694
793
  */
695
794
  getMeta(id) {
696
795
  const meta = this._ensureMetaDict();
@@ -699,8 +798,8 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
699
798
  /**
700
799
  * Sets given value to id in metaNamespace
701
800
  *
702
- * @param id
703
- * @param value
801
+ * @param id The meta ID to write
802
+ * @param value The value to store
704
803
  */
705
804
  setMeta(id, value) {
706
805
  const meta = this._ensureMetaDict();
@@ -710,18 +809,21 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
710
809
  this.log.silly(`${this.namespace} memory publish meta ${id} ${value}`);
711
810
  this.publishAll("meta", id, value);
712
811
  });
713
- if (!this.stateTimer) {
714
- this.stateTimer = setTimeout(() => this.saveState(), this.writeFileInterval);
715
- }
812
+ this.stateTimer ||= setTimeout(() => this.saveState(), this.writeFileInterval);
716
813
  }
717
- // needed by server
814
+ /**
815
+ * Directly set an object value and publish the change (used by the server)
816
+ *
817
+ * @param id The object ID to set
818
+ * @param obj The object to store
819
+ */
718
820
  _setObjectDirect(id, obj) {
719
821
  this.dataset[id] = obj;
720
822
  if (obj.type === "meta" && this.existingMetaObjects[id] === false) {
721
823
  this.existingMetaObjects[id] = true;
722
824
  }
723
825
  setImmediate(() => this.publishAll("objects", id, obj));
724
- this.stateTimer = this.stateTimer || setTimeout(() => this.saveState(), this.writeFileInterval);
826
+ this.stateTimer ||= setTimeout(() => this.saveState(), this.writeFileInterval);
725
827
  }
726
828
  /**
727
829
  * Delete the given object from the dataset
@@ -745,14 +847,22 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
745
847
  this.stateTimer = setTimeout(() => this.saveState(), this.writeFileInterval);
746
848
  }
747
849
  }
748
- // internal functionality
850
+ /**
851
+ * Apply a view's map function over all objects and collect the matching rows
852
+ *
853
+ * @param func The view definition containing the map function
854
+ * @param func.map The map function source code to apply
855
+ * @param func.reduce Optional reduce function (only the built-in '_stats' is supported)
856
+ * @param params Query parameters such as startkey and endkey
857
+ */
749
858
  _applyView(func, params) {
750
859
  const result = {
751
860
  rows: []
752
861
  };
753
- function _emit_(id, obj) {
754
- result.rows.push({ id, value: obj });
862
+ function _emit_(id, value) {
863
+ result.rows.push({ id, value });
755
864
  }
865
+ __name(_emit_, "_emit_");
756
866
  const f = eval(`(${func.map.replace(/emit/g, "_emit_")})`);
757
867
  for (const [id, obj] of Object.entries(this.dataset)) {
758
868
  if (params) {
@@ -786,14 +896,20 @@ class ObjectsInMemoryFileDB extends import_db_base.InMemoryFileDB {
786
896
  }
787
897
  return result;
788
898
  }
789
- // needed by server
899
+ /**
900
+ * Run a predefined object view (design document) and return the matching rows (used by the server)
901
+ *
902
+ * @param design The design document name
903
+ * @param search The view name within the design document
904
+ * @param params Query parameters such as startkey and endkey
905
+ */
790
906
  _getObjectView(design, search, params2) {
791
907
  const designObj = this.dataset[`_design/${design}`];
792
908
  if (!designObj) {
793
909
  this.log.error(`${this.namespace} Cannot find view "${design}"`);
794
910
  throw new Error(`Cannot find view "${design}"`);
795
911
  }
796
- if (!(designObj.views && designObj.views[search])) {
912
+ if (!designObj.views?.[search]) {
797
913
  this.log.warn(`${this.namespace} Cannot find search "${search}" in "${design}"`);
798
914
  throw new Error(`Cannot find search "${search}" in "${design}"`);
799
915
  }