@microphi/json-db 2.3.0 → 2.5.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Small, fast and simple file based json-db super powered by [minisearch](https://github.com/lucaong/minisearch)
4
4
 
5
5
  ## Install
6
- With your preferend package manager install
6
+ With your preferred package manager install
7
7
  ```
8
8
  @microphi/json-db
9
9
  ```
@@ -51,3 +51,11 @@ const storage = new JsonStorage<User>('users', '.data', {
51
51
  The third object in the JsonStorage constructor is the `Options` object of minisearch which documentation can be found [here](https://lucaong.github.io/minisearch/classes/MiniSearch.MiniSearch.html#constructor)
52
52
 
53
53
  JsonStorage uses internally rxjs to queue the operations on the index and so to avoid too many read/write to occur simultaneously. By default `index.json` is written on disk after 3 second of inactivity. This can be changed using the fourth argument of the constructor specifying the time in milliseconds. Also a fifth argument can be passed to change the default rxjs scheduler (asapScheduler).
54
+
55
+ #### Minisearch
56
+ minisearch functionalities are available through the `search` field.
57
+ ```typescript
58
+ storage.search.search('batman', { fields: ['name'] });
59
+ ```
60
+ Remember documents are added/updated/removed to minisearch automatically.
61
+ For more info on minisearch please consult their [repo](https://github.com/lucaong/minisearch).
package/lib/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- export * from './lib/list/list';
2
1
  export * from './lib/storage/json-storage';
3
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
- export * from './lib/list/list';
2
- export * from './lib/storage/json-storage';
3
- //# sourceMappingURL=index.js.map
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/storage/json-storage"), exports);
@@ -1,6 +1,6 @@
1
1
  import MiniSearch, { Options } from 'minisearch';
2
2
  import { Scheduler, Subject } from 'rxjs';
3
- import { List } from '../list/list';
3
+ import { List } from '@microphi/list';
4
4
  /**
5
5
  * Mark T as having auto trailing attributes
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"json-storage.d.ts","sourceRoot":"","sources":["../../../src/lib/storage/json-storage.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAgD,MAAM,MAAM,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG;IACzC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAA;AAED,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM;;IAwBrC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAGhB,OAAO,CAAC,SAAS,CAAC;IAxBpB,YAAY,mBAA0B;IAEtC,KAAK;YAAgC,MAAM;cAAQ,MAAM;QAAgB;IAEzE,IAAI,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,CAM1B;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAE9B;IAED,OAAO,CAAC,OAAO,CAAiB;gBAItB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EACxB,kBAAkB,CAAC,EAAE,OAAO,EAC5B,QAAQ,CAAC,EAAE,MAAM,EACT,SAAS,CAAC,uBAAW;IAgEzB,SAAS;IAMf,GAAG,CAAC,EAAE,EAAE,MAAM;IAIR,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;IA+B7B,MAAM,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE;IAqC7B,QAAQ;IAIR,SAAS;IAqBT,MAAM;YAMQ,WAAW;IAuBzB,OAAO,CAAC,SAAS;IAIX,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAC,SAAS,CAAC;YAsB7B,QAAQ;YAKR,OAAO;CAKtB"}
1
+ {"version":3,"file":"json-storage.d.ts","sourceRoot":"","sources":["../../../src/lib/storage/json-storage.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAgD,MAAM,MAAM,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG;IACzC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAA;AAED,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM;;IAwBrC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAGhB,OAAO,CAAC,SAAS,CAAC;IAxBpB,YAAY,mBAA0B;IAEtC,KAAK;YAAgC,MAAM;cAAQ,MAAM;QAAgB;IAEzE,IAAI,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,CAM1B;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAE9B;IAED,OAAO,CAAC,OAAO,CAAiB;gBAItB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EACxB,kBAAkB,CAAC,EAAE,OAAO,EAC5B,QAAQ,CAAC,EAAE,MAAM,EACT,SAAS,CAAC,uBAAW;IAgEzB,SAAS;IAMf,GAAG,CAAC,EAAE,EAAE,MAAM;IAIR,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;IA+B7B,MAAM,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE;IAqC7B,QAAQ;IAIR,SAAS;IAqBT,MAAM;YAMQ,WAAW;IAuBzB,OAAO,CAAC,SAAS;IAIX,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAC,SAAS,CAAC;YAsB7B,QAAQ;YAKR,OAAO;CAKtB"}
@@ -1,12 +1,15 @@
1
+ "use strict";
1
2
  var _JsonStorage_l;
2
- import { __classPrivateFieldGet } from "tslib";
3
- import { getDebugger } from '@microphi/debug';
4
- import MiniSearch from 'minisearch';
5
- import { close, exists, existsSync, mkdir, openSync, readFileSync, rmSync, unlink, writeFileSync } from 'node:fs';
6
- import { dirname, join } from 'node:path';
7
- import { Subject, asapScheduler, debounceTime, from, switchMap } from 'rxjs';
8
- import { List } from '../list/list';
9
- export class JsonStorage {
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.JsonStorage = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const debug_1 = require("@microphi/debug");
7
+ const minisearch_1 = require("minisearch");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = require("node:path");
10
+ const rxjs_1 = require("rxjs");
11
+ const list_1 = require("@microphi/list");
12
+ class JsonStorage {
10
13
  get search() {
11
14
  if (!this._search) {
12
15
  throw Error(`Need to enable search by passing Minisearch Options to constructor. ${this.entity}`);
@@ -20,30 +23,30 @@ export class JsonStorage {
20
23
  this.entity = entity;
21
24
  this.basePath = basePath;
22
25
  this.scheduler = scheduler;
23
- _JsonStorage_l.set(this, getDebugger(`JsonStorage:${this.entity}`));
24
- this.syncPending$ = new Subject();
25
- this.index = new List('id', []);
26
- this.index.operations$.pipe(debounceTime(debounce || 3000, this.scheduler || asapScheduler), switchMap(() => {
27
- return from(this.saveIndex());
26
+ _JsonStorage_l.set(this, (0, debug_1.getDebugger)(`JsonStorage:${this.entity}`));
27
+ this.syncPending$ = new rxjs_1.Subject();
28
+ this.index = new list_1.List('id', []);
29
+ this.index.operations$.pipe((0, rxjs_1.debounceTime)(debounce || 3000, this.scheduler || rxjs_1.asapScheduler), (0, rxjs_1.switchMap)(() => {
30
+ return (0, rxjs_1.from)(this.saveIndex());
28
31
  })).subscribe(() => {
29
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `index has been quiet for ${debounce}ms. Updated`);
32
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `index has been quiet for ${debounce}ms. Updated`);
30
33
  });
31
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'creating service');
34
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'creating service');
32
35
  if (searchIndexOptions) {
33
- this._search = new MiniSearch(searchIndexOptions);
36
+ this._search = new minisearch_1.default(searchIndexOptions);
34
37
  }
35
38
  this.getIndex().then((data) => {
36
39
  this.index.upsert(...data);
37
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `loaded index for ${data.length} items`);
38
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'getting documents');
40
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `loaded index for ${data.length} items`);
41
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'getting documents');
39
42
  const documents = [...this.index].map(({ id }) => {
40
43
  return this.get(id);
41
44
  });
42
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `got ${documents.length} documents`);
45
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `got ${documents.length} documents`);
43
46
  if (searchIndexOptions) {
44
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'reading files to generate search index');
47
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'reading files to generate search index');
45
48
  Promise.all(documents).then((actualDocs) => {
46
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'rebuilding search index with', actualDocs.length);
49
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'rebuilding search index with', actualDocs.length);
47
50
  actualDocs.forEach((doc) => {
48
51
  try {
49
52
  if (doc && doc['id']) {
@@ -53,20 +56,20 @@ export class JsonStorage {
53
56
  }
54
57
  catch (err) {
55
58
  console.error('Error while loading entity into search index');
56
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, doc);
59
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, doc);
57
60
  // console.error(actualDocs);
58
61
  console.error(err);
59
62
  }
60
63
  });
61
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'search index rebuilt');
62
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `handling ${this._search.documentCount} documents for free search`);
64
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'search index rebuilt');
65
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `handling ${this._search.documentCount} documents for free search`);
63
66
  });
64
67
  }
65
68
  });
66
69
  }
67
70
  async saveIndex() {
68
71
  const path = await this.storageFile('index');
69
- writeFileSync(path, JSON.stringify([...this.index]));
72
+ (0, node_fs_1.writeFileSync)(path, JSON.stringify([...this.index]));
70
73
  return Promise.resolve('ok');
71
74
  }
72
75
  has(id) {
@@ -113,7 +116,7 @@ export class JsonStorage {
113
116
  this._search?.remove(doc);
114
117
  }
115
118
  // finally remove json file
116
- unlink(entry.path, (err) => {
119
+ (0, node_fs_1.unlink)(entry.path, (err) => {
117
120
  if (err)
118
121
  reject(err);
119
122
  resolve('ok');
@@ -132,9 +135,9 @@ export class JsonStorage {
132
135
  this._search.removeAll();
133
136
  }
134
137
  return new Promise((resolve) => {
135
- exists(`${this.basePath}/${this.entity}`, (exists) => {
138
+ (0, node_fs_1.exists)(`${this.basePath}/${this.entity}`, (exists) => {
136
139
  if (exists) {
137
- rmSync(`${this.basePath}/${this.entity}`, { recursive: true });
140
+ (0, node_fs_1.rmSync)(`${this.basePath}/${this.entity}`, { recursive: true });
138
141
  }
139
142
  resolve('ok');
140
143
  });
@@ -146,16 +149,16 @@ export class JsonStorage {
146
149
  }
147
150
  async storageFile(key) {
148
151
  const path = this.buildPath(key);
149
- const folder = dirname(path);
150
- if (existsSync(path)) {
152
+ const folder = (0, node_path_1.dirname)(path);
153
+ if ((0, node_fs_1.existsSync)(path)) {
151
154
  return Promise.resolve(path);
152
155
  }
153
156
  return new Promise((resolve, reject) => {
154
- mkdir(folder, { recursive: true }, (err) => {
157
+ (0, node_fs_1.mkdir)(folder, { recursive: true }, (err) => {
155
158
  if (err)
156
159
  reject(err);
157
- const fd = openSync(path, 'w');
158
- close(fd, (err) => {
160
+ const fd = (0, node_fs_1.openSync)(path, 'w');
161
+ (0, node_fs_1.close)(fd, (err) => {
159
162
  if (err)
160
163
  reject(err);
161
164
  resolve(path);
@@ -164,11 +167,11 @@ export class JsonStorage {
164
167
  });
165
168
  }
166
169
  buildPath(id) {
167
- return join(this.basePath, this.entity, `${id}.json`);
170
+ return (0, node_path_1.join)(this.basePath, this.entity, `${id}.json`);
168
171
  }
169
172
  async get(id) {
170
173
  const path = await this.storageFile(id);
171
- const data = readFileSync(path, { encoding: 'utf8' });
174
+ const data = (0, node_fs_1.readFileSync)(path, { encoding: 'utf8' });
172
175
  let entity;
173
176
  try {
174
177
  entity = JSON.parse(data);
@@ -176,22 +179,22 @@ export class JsonStorage {
176
179
  }
177
180
  catch (e) {
178
181
  // console.log(e);
179
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `error while trying to load data for item with id ${id}`);
180
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'item does not exists');
181
- __classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'clearing index');
182
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, `error while trying to load data for item with id ${id}`);
183
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'item does not exists');
184
+ tslib_1.__classPrivateFieldGet(this, _JsonStorage_l, "f").call(this, 'clearing index');
182
185
  this.index.delete({ id, path });
183
186
  }
184
187
  return Promise.resolve(undefined);
185
188
  }
186
189
  async getIndex() {
187
- const index = readFileSync(await this.storageFile('index'), { encoding: 'utf8' });
190
+ const index = (0, node_fs_1.readFileSync)(await this.storageFile('index'), { encoding: 'utf8' });
188
191
  return JSON.parse(index || '[]');
189
192
  }
190
193
  async setItem(id, doc) {
191
- writeFileSync(join(this.basePath, this.entity, `${id}.json`), JSON.stringify({ id, ...doc }));
194
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(this.basePath, this.entity, `${id}.json`), JSON.stringify({ id, ...doc }));
192
195
  // this.l('wrote doc to disk', id, doc);
193
196
  return Promise.resolve('ok');
194
197
  }
195
198
  }
199
+ exports.JsonStorage = JsonStorage;
196
200
  _JsonStorage_l = new WeakMap();
197
- //# sourceMappingURL=json-storage.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microphi/json-db",
3
- "version": "2.3.0",
3
+ "version": "2.5.1",
4
4
  "description": "Small, fast and simple file based json-db",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -29,7 +29,7 @@
29
29
  "lint": "eslint ./src"
30
30
  },
31
31
  "dependencies": {
32
- "@microphi/debug": "^2.3.0"
32
+ "@microphi/debug": "^2.5.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@babel/plugin-transform-modules-commonjs": "^7.23.3",
@@ -39,8 +39,9 @@
39
39
  "utility-types": "^3.10.0"
40
40
  },
41
41
  "peerDependencies": {
42
+ "@microphi/list": "^2.3.0",
42
43
  "minisearch": "^6.3.0",
43
44
  "rxjs": "~7.8.0"
44
45
  },
45
- "gitHead": "6b2df1f7209ccda9b934bbdbcf71f59acc383380"
46
+ "gitHead": "85be3ad7bf34a32e5b386d817b71ef3f6703b397"
46
47
  }
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC","sourcesContent":["export * from './lib/list/list';\nexport * from './lib/storage/json-storage';\n"]}
@@ -1,28 +0,0 @@
1
- import { Subject } from 'rxjs';
2
- import { $Keys, FunctionKeys } from 'utility-types';
3
- export interface Operation {
4
- op: FunctionKeys<List<any>>;
5
- payload?: unknown;
6
- timestamp: Date;
7
- }
8
- export declare class List<EntityType extends object> implements Iterable<EntityType> {
9
- private IDField;
10
- operations$: Subject<Operation>;
11
- readonly entities: Map<string, EntityType>;
12
- /**
13
- * maps entities position from the original array to their ids
14
- */
15
- private readonly ids;
16
- get size(): number;
17
- has(id: string): boolean;
18
- get(id: string): EntityType | undefined;
19
- private getId;
20
- constructor(IDField: $Keys<EntityType>, entities?: EntityType[]);
21
- [Symbol.iterator](): IterableIterator<EntityType>;
22
- upsert(...entities: EntityType[]): void;
23
- prepend(...entities: EntityType[]): void;
24
- clear(): void;
25
- delete(...entities: EntityType[]): void;
26
- append(...entities: EntityType[]): void;
27
- }
28
- //# sourceMappingURL=list.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/lib/list/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGpD,MAAM,WAAW,SAAS;IAExB,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,IAAI,CAAC,UAAU,SAAS,MAAM,CAAE,YAAW,QAAQ,CAAC,UAAU,CAAC;IAgC9D,OAAO,CAAC,OAAO;IA9BpB,WAAW,qBAA4B;IAE9C,SAAgB,QAAQ,0BAAiC;IAEzD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IAEpC,IAAW,IAAI,WAOd;IAEM,GAAG,CAAC,EAAE,EAAE,MAAM;IAId,GAAG,CAAC,EAAE,EAAE,MAAM;IAIrB,OAAO,CAAC,KAAK;gBAIO,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,QAAQ,GAAE,UAAU,EAAO;IAW1E,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAa3C,MAAM,CAAC,GAAG,QAAQ,EAAE,UAAU,EAAE;IAehC,OAAO,CAAC,GAAG,QAAQ,EAAE,UAAU,EAAE;IAgBjC,KAAK;IAQL,MAAM,CAAC,GAAG,QAAQ,EAAE,UAAU,EAAE;IAehC,MAAM,CAAC,GAAG,QAAQ,EAAE,UAAU,EAAE;CAgBxC"}
@@ -1,94 +0,0 @@
1
- import { Subject } from 'rxjs';
2
- export class List {
3
- get size() {
4
- if (this.ids.length !== this.entities.size) {
5
- throw new Error(`Size Mismatch ${this.ids.length} !== ${this.entities.size}`);
6
- }
7
- return this.entities.size;
8
- }
9
- has(id) {
10
- return this.entities.has(id);
11
- }
12
- get(id) {
13
- return this.entities.get(id);
14
- }
15
- getId(entity) {
16
- return entity[this.IDField];
17
- }
18
- constructor(IDField, entities = []) {
19
- this.IDField = IDField;
20
- this.operations$ = new Subject();
21
- this.entities = new Map();
22
- /**
23
- * maps entities position from the original array to their ids
24
- */
25
- this.ids = [];
26
- this.ids = entities.map((e) => this.getId(e));
27
- entities
28
- .forEach((item) => {
29
- const id = this.getId(item);
30
- this.entities.set(id, item);
31
- });
32
- }
33
- *[Symbol.iterator]() {
34
- for (const idx in this.ids) {
35
- const id = this.ids[idx];
36
- const entity = this.entities.get(id);
37
- if (entity) {
38
- yield entity;
39
- }
40
- else {
41
- throw new Error(`Cannot get entity with id ${id}`);
42
- }
43
- }
44
- }
45
- upsert(...entities) {
46
- entities.forEach((e) => {
47
- const id = this.getId(e);
48
- if (!this.entities.has(id)) {
49
- this.ids.push(id);
50
- }
51
- this.entities.set(id, e);
52
- });
53
- this.operations$.next({ op: 'upsert', payload: entities, timestamp: new Date() });
54
- }
55
- prepend(...entities) {
56
- entities.reverse().forEach((e) => {
57
- const id = this.getId(e);
58
- if (this.entities.has(id)) {
59
- this.delete(e);
60
- }
61
- this.ids.unshift(id);
62
- this.entities.set(id, e);
63
- });
64
- this.operations$.next({ op: 'prepend', payload: entities, timestamp: new Date() });
65
- }
66
- clear() {
67
- this.ids.length = 0;
68
- this.entities.clear();
69
- this.operations$.next({ op: 'clear', timestamp: new Date() });
70
- }
71
- delete(...entities) {
72
- entities.forEach((e) => {
73
- const id = this.getId(e);
74
- const indexOfId = this.ids.findIndex((_id) => _id === id);
75
- if (indexOfId >= 0) {
76
- this.ids.splice(indexOfId, 1);
77
- this.entities.delete(id);
78
- }
79
- });
80
- this.operations$.next({ op: 'delete', payload: entities, timestamp: new Date() });
81
- }
82
- append(...entities) {
83
- entities.forEach((e) => {
84
- const id = this.getId(e);
85
- if (this.entities.has(id)) {
86
- this.delete(e);
87
- }
88
- this.ids.push(id);
89
- this.entities.set(id, e);
90
- });
91
- this.operations$.next({ op: 'append', payload: entities, timestamp: new Date() });
92
- }
93
- }
94
- //# sourceMappingURL=list.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/lib/list/list.ts"],"names":[],"mappings":"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","sourcesContent":["import { Subject } from 'rxjs';\nimport { $Keys, FunctionKeys } from 'utility-types';\n\n\nexport interface Operation {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n op: FunctionKeys<List<any>>;\n payload?: unknown;\n timestamp: Date;\n}\n\nexport class List<EntityType extends object> implements Iterable<EntityType> {\n\n public operations$ = new Subject<Operation>();\n\n public readonly entities = new Map<string, EntityType>();\n\n /**\n * maps entities position from the original array to their ids\n */\n private readonly ids: string[] = [];\n\n public get size() {\n if (this.ids.length !== this.entities.size) {\n\n throw new Error(`Size Mismatch ${this.ids.length} !== ${this.entities.size}`);\n }\n\n return this.entities.size;\n }\n\n public has(id: string) {\n return this.entities.has(id);\n }\n\n public get(id: string) {\n return this.entities.get(id);\n }\n\n private getId(entity: EntityType): string {\n return entity[this.IDField] as string;\n }\n\n constructor(private IDField: $Keys<EntityType>, entities: EntityType[] = []) {\n\n this.ids = entities.map((e) => this.getId(e));\n\n entities\n .forEach((item) => {\n const id = this.getId(item);\n this.entities.set(id, item);\n });\n }\n\n *[Symbol.iterator](): IterableIterator<EntityType> {\n for (const idx in this.ids) {\n const id = this.ids[idx];\n\n const entity = this.entities.get(id);\n if (entity) {\n yield entity;\n } else {\n throw new Error(`Cannot get entity with id ${id}`);\n }\n }\n }\n\n public upsert(...entities: EntityType[]) {\n entities.forEach((e) => {\n const id = this.getId(e);\n\n if (!this.entities.has(id)) {\n this.ids.push(id);\n }\n\n this.entities.set(id, e);\n\n });\n\n this.operations$.next({ op: 'upsert', payload: entities, timestamp: new Date() });\n }\n\n public prepend(...entities: EntityType[]) {\n entities.reverse().forEach((e) => {\n const id = this.getId(e);\n\n if (this.entities.has(id)) {\n this.delete(e);\n }\n\n this.ids.unshift(id);\n\n this.entities.set(id, e);\n\n });\n this.operations$.next({ op: 'prepend', payload: entities, timestamp: new Date() });\n }\n\n public clear() {\n\n this.ids.length = 0;\n this.entities.clear();\n this.operations$.next({ op: 'clear', timestamp: new Date() });\n\n }\n\n public delete(...entities: EntityType[]) {\n\n entities.forEach((e) => {\n const id = this.getId(e);\n const indexOfId = this.ids.findIndex((_id) => _id === id);\n\n if (indexOfId >= 0) {\n this.ids.splice(indexOfId, 1);\n this.entities.delete(id);\n }\n });\n\n this.operations$.next({ op: 'delete', payload: entities, timestamp: new Date() });\n }\n\n public append(...entities: EntityType[]) {\n entities.forEach((e) => {\n const id = this.getId(e);\n\n if (this.entities.has(id)) {\n this.delete(e);\n }\n\n this.ids.push(id);\n this.entities.set(id, e);\n });\n\n this.operations$.next({ op: 'append', payload: entities, timestamp: new Date() });\n }\n\n\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"json-storage.js","sourceRoot":"","sources":["../../../src/lib/storage/json-storage.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,UAAuB,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAa,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAgBpC,MAAM,OAAO,WAAW;IAQtB,IAAI,MAAM;QACR,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,KAAK,CAAC,uEAAuE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACpG,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,CAAC,KAAoB;QAC7B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAKD,YACU,MAAc,EACd,QAAgB,EACxB,kBAA4B,EAC5B,QAAiB,EACT,SAAqB;QAJrB,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAQ;QAGhB,cAAS,GAAT,SAAS,CAAY;QA1B/B,yBAAK,WAAW,CAAC,eAAe,IAAI,CAAC,MAAM,EAAE,CAAC,EAAC;QAE/C,iBAAY,GAAG,IAAI,OAAO,EAAW,CAAC;QAEtC,UAAK,GAAG,IAAI,IAAI,CAA+C,IAAI,EAAE,EAAE,CAAC,CAAC;QAyBvE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CACzB,YAAY,CAAC,QAAQ,IAAI,IAAK,EAAE,IAAI,CAAC,SAAS,IAAI,aAAa,CAAC,EAChE,SAAS,CAAC,GAAG,EAAE;YACb,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CACH,CAAC,SAAS,CAAC,GAAG,EAAE;YACf,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,4BAA4B,QAAQ,aAAa,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,kBAAkB,CAAC,CAAC;QAE5B,IAAI,kBAAkB,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAE5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;YAE3B,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,oBAAoB,IAAI,CAAC,MAAM,QAAQ,CAAC,CAAC;YACjD,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,mBAAmB,CAAC,CAAC;YAE7B,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;gBAC/C,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtB,CAAC,CAAC,CAAC;YACH,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,OAAO,SAAS,CAAC,MAAM,YAAY,CAAC,CAAC;YAE7C,IAAI,kBAAkB,EAAE,CAAC;gBACvB,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,wCAAwC,CAAC,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;oBAEzC,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,8BAA8B,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;oBAE3D,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;wBAEzB,IAAI,CAAC;4BAEH,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gCACrB,kDAAkD;gCAElD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;4BACxB,CAAC;wBAEH,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;4BAC9D,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,GAAG,CAAC,CAAC;4BACb,6BAA6B;4BAC7B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;wBACrB,CAAC;oBAEH,CAAC,CAAC,CAAC;oBAEH,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,sBAAsB,CAAC,CAAC;oBAChC,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,YAAY,IAAI,CAAC,OAAO,CAAC,aAAa,4BAA4B,CAAC,CAAC;gBAE9E,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IAEL,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC7C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAG,IAAmB;QAEjC,KAAK,MAAM,EAAC,EAAE,EAAE,GAAG,GAAG,EAAC,IAAI,IAAI,EAAE,CAAC;YAChC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACtC,QAAQ,EAAE,SAAS;aACpB,CAAC,CAAC,CAAC;gBACF,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,SAAS;aACnB,CAAC;YAEF,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;gBAChB,EAAE,EAAE,EAAE;gBACN,IAAI,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAChC,GAAG,UAAU;aACd,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,GAAQ,CAAC,CAAC;YAEjC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBACzB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAC,EAAE,EAAE,GAAG,GAAG,EAAM,CAAC,CAAC;gBAC1C,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAC,EAAE,EAAE,GAAG,GAAG,EAAM,CAAC,CAAC;gBACtC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAG,GAAa;QAC3B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAErC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBAErB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAEjC,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;oBAExB,IAAI,KAAK,EAAE,CAAC;wBAEV,gCAAgC;wBAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;wBAEzB,IAAI,GAAG,EAAE,CAAC;4BACR,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;wBAC5B,CAAC;wBAED,2BAA2B;wBAC3B,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;4BACzB,IAAI,GAAG;gCAAE,MAAM,CAAC,GAAG,CAAC,CAAC;4BAErB,OAAO,CAAC,IAAI,CAAC,CAAC;wBAChB,CAAC,CAAC,CAAC;oBACL,CAAC;gBAGH,CAAC,CAAC,CAAC;YACL,CAAC;QAEH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,SAAS;QACP,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAEnB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QAC3B,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE;gBACnD,IAAI,MAAM,EAAE,CAAC;oBAEX,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAEjE,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC,CAAC;YAEhB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM;QACJ,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAE7D,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzC,IAAI,GAAG;oBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;gBAErB,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC/B,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;oBAChB,IAAI,GAAG;wBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;oBAErB,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,CAAC,CAAC;YAEL,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,SAAS,CAAC,EAAU;QAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;QACpD,IAAI,MAAS,CAAC;QAEd,IAAI,CAAC;YAEH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAE1B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,kBAAkB;YAClB,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,oDAAoD,EAAE,EAAE,CAAC,CAAC;YAClE,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,sBAAsB,CAAC,CAAC;YAChC,uBAAA,IAAI,sBAAG,MAAP,IAAI,EAAI,gBAAgB,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAC,EAAE,EAAE,IAAI,EAAC,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAGO,KAAK,CAAC,QAAQ;QACpB,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;QAChF,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IACnC,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAM;QACtC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAC,EAAE,EAAE,GAAG,GAAG,EAAC,CAAC,CAAC,CAAC;QAC5F,wCAAwC;QACxC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACF","sourcesContent":["import { getDebugger } from '@microphi/debug';\nimport MiniSearch, { Options } from 'minisearch';\nimport { close, exists, existsSync, mkdir, openSync, readFileSync, rmSync, unlink, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { Scheduler, Subject, asapScheduler, debounceTime, from, switchMap } from 'rxjs';\nimport { List } from '../list/list';\n\n/**\n * Mark T as having auto trailing attributes\n *\n * `modified` and `created` iso string date;\n */\nexport type withAutoTrail<T> = T & {\n modified?: string;\n created?: string;\n}\n\nexport type withId<T extends object> = T & {\n id: string;\n}\n\nexport class JsonStorage<T extends object> {\n\n #l = getDebugger(`JsonStorage:${this.entity}`);\n\n syncPending$ = new Subject<boolean>();\n\n index = new List<withAutoTrail<{ id: string; path: string; }>>('id', []);\n\n get search(): MiniSearch<T> {\n if (!this._search) {\n throw Error(`Need to enable search by passing Minisearch Options to constructor. ${this.entity}`);\n }\n\n return this._search;\n }\n\n set search(value: MiniSearch<T>) {\n this._search = value;\n }\n\n private _search!: MiniSearch<T>;\n\n\n constructor(\n private entity: string,\n private basePath: string,\n searchIndexOptions?: Options,\n debounce?: number,\n private scheduler?: Scheduler,\n ) {\n\n this.index.operations$.pipe(\n debounceTime(debounce || 3_000, this.scheduler || asapScheduler),\n switchMap(() => {\n return from(this.saveIndex());\n })\n ).subscribe(() => {\n this.#l(`index has been quiet for ${debounce}ms. Updated`);\n });\n\n this.#l('creating service');\n\n if (searchIndexOptions) {\n this._search = new MiniSearch(searchIndexOptions);\n }\n\n this.getIndex().then((data) => {\n\n this.index.upsert(...data);\n\n this.#l(`loaded index for ${data.length} items`);\n this.#l('getting documents');\n\n const documents = [...this.index].map(({ id }) => {\n return this.get(id);\n });\n this.#l(`got ${documents.length} documents`);\n\n if (searchIndexOptions) {\n this.#l('reading files to generate search index');\n Promise.all(documents).then((actualDocs) => {\n\n this.#l('rebuilding search index with', actualDocs.length);\n\n actualDocs.forEach((doc) => {\n\n try {\n\n if (doc && doc['id']) {\n // this.l(`adding document with id ${doc['id']}`);\n\n this._search.add(doc);\n }\n\n } catch (err) {\n console.error('Error while loading entity into search index');\n this.#l(doc);\n // console.error(actualDocs);\n console.error(err);\n }\n\n });\n\n this.#l('search index rebuilt');\n this.#l(`handling ${this._search.documentCount} documents for free search`);\n\n });\n }\n });\n\n }\n\n async saveIndex() {\n const path = await this.storageFile('index');\n writeFileSync(path, JSON.stringify([...this.index]));\n return Promise.resolve('ok');\n }\n\n has(id: string) {\n return this.index.has(id);\n }\n\n async upsert(...docs: (withId<T>)[]) {\n\n for (const {id, ...doc} of docs) {\n const timestamp = new Date().toISOString();\n const updateTime = this.index.has(id) ? {\n modified: timestamp,\n } : {\n modified: timestamp,\n created: timestamp,\n };\n\n this.index.upsert({\n id: id,\n path: await this.storageFile(id),\n ...updateTime\n });\n\n await this.setItem(id, doc as T);\n\n if (this._search) {\n if (this._search.has(id)) {\n this._search.replace({id, ...doc} as T);\n } else {\n this._search.add({id, ...doc} as T);\n }\n }\n }\n\n return Promise.resolve();\n }\n\n async remove(...ids: string[]) {\n return new Promise((resolve, reject) => {\n\n for (const id of ids) {\n\n const entry = this.index.get(id);\n\n if (!entry) {\n resolve('ok');\n }\n\n this.get(id).then((doc) => {\n\n if (entry) {\n\n // remove index and search entry\n this.index.delete(entry);\n\n if (doc) {\n this._search?.remove(doc);\n }\n\n // finally remove json file\n unlink(entry.path, (err) => {\n if (err) reject(err);\n\n resolve('ok');\n });\n }\n\n\n });\n }\n\n });\n }\n\n getCount() {\n return this.index.size;\n }\n\n deleteAll() {\n this.index.clear();\n\n if (this._search) {\n this._search.removeAll();\n }\n\n return new Promise((resolve) => {\n exists(`${this.basePath}/${this.entity}`, (exists) => {\n if (exists) {\n\n rmSync(`${this.basePath}/${this.entity}`, { recursive: true });\n\n }\n\n resolve('ok');\n\n });\n });\n }\n\n getAll() {\n const items$ = [...this.index].map(({ id }) => this.get(id));\n\n return Promise.all(items$);\n }\n\n private async storageFile(key: string): Promise<string> {\n const path = this.buildPath(key);\n const folder = dirname(path);\n\n if (existsSync(path)) {\n return Promise.resolve(path);\n }\n\n return new Promise((resolve, reject) => {\n mkdir(folder, { recursive: true }, (err) => {\n if (err) reject(err);\n\n const fd = openSync(path, 'w');\n close(fd, (err) => {\n if (err) reject(err);\n\n resolve(path);\n });\n\n });\n });\n }\n\n private buildPath(id: string) {\n return join(this.basePath, this.entity, `${id}.json`);\n }\n\n async get(id: string): Promise<T|undefined> {\n const path = await this.storageFile(id);\n const data = readFileSync(path, {encoding: 'utf8'});\n let entity: T;\n\n try {\n\n entity = JSON.parse(data);\n\n return entity;\n } catch (e) {\n // console.log(e);\n this.#l(`error while trying to load data for item with id ${id}`);\n this.#l('item does not exists');\n this.#l('clearing index');\n this.index.delete({id, path});\n }\n\n return Promise.resolve(undefined);\n }\n\n\n private async getIndex(): Promise<{id: string; path: string;}[]> {\n const index = readFileSync(await this.storageFile('index'), {encoding: 'utf8'});\n return JSON.parse(index || '[]');\n }\n\n private async setItem(id: string, doc: T) {\n writeFileSync(join(this.basePath, this.entity, `${id}.json`), JSON.stringify({id, ...doc}));\n // this.l('wrote doc to disk', id, doc);\n return Promise.resolve('ok');\n }\n}\n"]}