@acodeninja/persist 2.2.0 → 2.2.2

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,16 +1,36 @@
1
- import Engine, {EngineError, MissConfiguredError} from './Engine.js';
2
- import {dirname, join} from 'node:path';
1
+ import Engine, { EngineError, MissConfiguredError } from './Engine.js';
2
+ import { dirname, join } from 'node:path';
3
3
  import fs from 'node:fs/promises';
4
4
 
5
+ /**
6
+ * Custom error class for FileEngine-related errors.
7
+ * Extends the base `EngineError` class.
8
+ */
5
9
  class FileEngineError extends EngineError {}
6
10
 
11
+ /**
12
+ * Error thrown when writing to a file fails in `FileEngine`.
13
+ * Extends the `FileEngineError` class.
14
+ */
7
15
  class FailedWriteFileEngineError extends FileEngineError {}
8
16
 
9
17
  /**
18
+ * `FileEngine` class extends the base `Engine` class to implement
19
+ * file system-based storage and retrieval of model data.
20
+ *
10
21
  * @class FileEngine
11
22
  * @extends Engine
12
23
  */
13
- export default class FileEngine extends Engine {
24
+ class FileEngine extends Engine {
25
+ /**
26
+ * Configures the FileEngine with a given configuration object.
27
+ * Adds default `filesystem` configuration if not provided.
28
+ *
29
+ * @param {Object} configuration - Configuration settings for FileEngine.
30
+ * @param {Object} [configuration.filesystem] - Custom filesystem module (default: Node.js fs/promises).
31
+ * @param {Object} [configuration.path] - The absolute path on the filesystem to write models to.
32
+ * @returns {FileEngine} A configured instance of FileEngine.
33
+ */
14
34
  static configure(configuration) {
15
35
  if (!configuration.filesystem) {
16
36
  configuration.filesystem = fs;
@@ -18,42 +38,77 @@ export default class FileEngine extends Engine {
18
38
  return super.configure(configuration);
19
39
  }
20
40
 
41
+ /**
42
+ * Checks if the FileEngine has been configured correctly.
43
+ * Ensures that `path` and `filesystem` settings are present.
44
+ *
45
+ * @throws {MissConfiguredError} Throws if required configuration is missing.
46
+ */
21
47
  static checkConfiguration() {
22
- if (
23
- !this._configuration?.path ||
24
- !this._configuration?.filesystem
25
- ) throw new MissConfiguredError(this._configuration);
48
+ if (!this.configuration?.path || !this.configuration?.filesystem) {
49
+ throw new MissConfiguredError(this.configuration);
50
+ }
26
51
  }
27
52
 
28
- static async getById(id) {
29
- const filePath = join(this._configuration.path, `${id}.json`);
30
-
31
- return JSON.parse(await this._configuration.filesystem.readFile(filePath).then(f => f.toString()));
53
+ /**
54
+ * Retrieves a model by its ID from the file system.
55
+ *
56
+ * @param {string} id - The ID of the model to retrieve.
57
+ * @returns {Promise<Object>} The parsed model data.
58
+ * @throws {Error} Throws if the file cannot be read or parsed.
59
+ */
60
+ static getById(id) {
61
+ return this.configuration.filesystem
62
+ .readFile(join(this.configuration.path, `${id}.json`))
63
+ .then((b) => b.toString())
64
+ .then(JSON.parse);
32
65
  }
33
66
 
34
- static async getIndex(model) {
35
- return JSON.parse((await this._configuration.filesystem.readFile(join(this._configuration.path, model.name, '_index.json')).catch(() => '{}')).toString());
67
+ /**
68
+ * Retrieves the index for a given model from the file system.
69
+ *
70
+ * @param {Model.constructor?} model - The model for which the index is retrieved.
71
+ * @returns {Promise<Object>} The parsed index data.
72
+ * @throws {Error} Throws if the file cannot be read.
73
+ */
74
+ static getIndex(model) {
75
+ return this.configuration.filesystem
76
+ .readFile(join(this.configuration.path, model.toString(), '_index.json'))
77
+ .then((b) => b.toString())
78
+ .catch(() => '{}')
79
+ .then(JSON.parse);
36
80
  }
37
81
 
82
+ /**
83
+ * Saves a model to the file system.
84
+ *
85
+ * @param {Model} model - The model to save.
86
+ * @throws {FailedWriteFileEngineError} Throws if the model cannot be written to the file system.
87
+ */
38
88
  static async putModel(model) {
39
- const filePath = join(this._configuration.path, `${model.id}.json`);
40
-
89
+ const filePath = join(this.configuration.path, `${model.id}.json`);
41
90
  try {
42
- await this._configuration.filesystem.mkdir(dirname(filePath), {recursive: true});
43
- await this._configuration.filesystem.writeFile(filePath, JSON.stringify(model.toData()));
91
+ await this.configuration.filesystem.mkdir(dirname(filePath), { recursive: true });
92
+ await this.configuration.filesystem.writeFile(filePath, JSON.stringify(model.toData()));
44
93
  } catch (error) {
45
94
  throw new FailedWriteFileEngineError(`Failed to put file://${filePath}`, error);
46
95
  }
47
96
  }
48
97
 
98
+ /**
99
+ * Saves the index for multiple models to the file system.
100
+ *
101
+ * @param {Object} index - An object where keys are locations and values are arrays of models.
102
+ * @throws {FailedWriteFileEngineError} Throws if the index cannot be written to the file system.
103
+ */
49
104
  static async putIndex(index) {
50
105
  const processIndex = async (location, models) => {
51
- const modelIndex = Object.fromEntries(models.map(m => [m.id, m.toIndexData()]));
52
- const filePath = join(this._configuration.path, location, '_index.json');
53
- const currentIndex = JSON.parse((await this._configuration.filesystem.readFile(filePath).catch(() => '{}')).toString());
106
+ const modelIndex = Object.fromEntries(models.map((m) => [m.id, m.toIndexData()]));
107
+ const filePath = join(this.configuration.path, location, '_index.json');
108
+ const currentIndex = JSON.parse((await this.configuration.filesystem.readFile(filePath).catch(() => '{}')).toString());
54
109
 
55
110
  try {
56
- await this._configuration.filesystem.writeFile(filePath, JSON.stringify({
111
+ await this.configuration.filesystem.writeFile(filePath, JSON.stringify({
57
112
  ...currentIndex,
58
113
  ...modelIndex,
59
114
  }));
@@ -69,35 +124,66 @@ export default class FileEngine extends Engine {
69
124
  await processIndex('', Object.values(index).flat());
70
125
  }
71
126
 
72
- static async getSearchIndexCompiled(model) {
73
- return await this._configuration.filesystem.readFile(join(this._configuration.path, model.name, '_search_index.json'))
74
- .then(b => b.toString())
127
+ /**
128
+ * Retrieves the compiled search index for a model from the file system.
129
+ *
130
+ * @param {Model.constructor} model - The model for which the search index is retrieved.
131
+ * @returns {Promise<Object>} The parsed compiled search index.
132
+ * @throws {Error} Throws if the file cannot be read.
133
+ */
134
+ static getSearchIndexCompiled(model) {
135
+ return this.configuration.filesystem
136
+ .readFile(join(this.configuration.path, model.toString(), '_search_index.json'))
137
+ .then((b) => b.toString())
75
138
  .then(JSON.parse);
76
139
  }
77
140
 
78
- static async getSearchIndexRaw(model) {
79
- return await this._configuration.filesystem.readFile(join(this._configuration.path, model.name, '_search_index_raw.json'))
80
- .then(b => b.toString())
141
+ /**
142
+ * Retrieves the raw search index for a model from the file system.
143
+ *
144
+ * @param {Model.constructor} model - The model for which the raw search index is retrieved.
145
+ * @returns {Promise<Object>} The parsed raw search index.
146
+ * @throws {Error} Throws if the file cannot be read.
147
+ */
148
+ static getSearchIndexRaw(model) {
149
+ return this.configuration.filesystem
150
+ .readFile(join(this.configuration.path, model.toString(), '_search_index_raw.json'))
151
+ .then((b) => b.toString())
81
152
  .then(JSON.parse)
82
153
  .catch(() => ({}));
83
154
  }
84
155
 
156
+ /**
157
+ * Saves the compiled search index for a model to the file system.
158
+ *
159
+ * @param {Model.constructor} model - The model for which the compiled search index is saved.
160
+ * @param {Object} compiledIndex - The compiled search index to save.
161
+ * @throws {FailedWriteFileEngineError} Throws if the compiled index cannot be written to the file system.
162
+ */
85
163
  static async putSearchIndexCompiled(model, compiledIndex) {
86
- const filePath = join(this._configuration.path, model.name, '_search_index.json');
87
-
164
+ const filePath = join(this.configuration.path, model.toString(), '_search_index.json');
88
165
  try {
89
- await this._configuration.filesystem.writeFile(filePath, JSON.stringify(compiledIndex));
166
+ await this.configuration.filesystem.writeFile(filePath, JSON.stringify(compiledIndex));
90
167
  } catch (error) {
91
168
  throw new FailedWriteFileEngineError(`Failed to put file://${filePath}`, error);
92
169
  }
93
170
  }
94
171
 
172
+ /**
173
+ * Saves the raw search index for a model to the file system.
174
+ *
175
+ * @param {Model.constructor} model - The model for which the raw search index is saved.
176
+ * @param {Object} rawIndex - The raw search index to save.
177
+ * @throws {FailedWriteFileEngineError} Throws if the raw index cannot be written to the file system.
178
+ */
95
179
  static async putSearchIndexRaw(model, rawIndex) {
96
- const filePath = join(this._configuration.path, model.name, '_search_index_raw.json');
180
+ const filePath = join(this.configuration.path, model.toString(), '_search_index_raw.json');
97
181
  try {
98
- await this._configuration.filesystem.writeFile(filePath, JSON.stringify(rawIndex));
182
+ await this.configuration.filesystem.writeFile(filePath, JSON.stringify(rawIndex));
99
183
  } catch (error) {
100
184
  throw new FailedWriteFileEngineError(`Failed to put file://${filePath}`, error);
101
185
  }
102
186
  }
103
187
  }
188
+
189
+ export default FileEngine;
@@ -1,8 +1,21 @@
1
1
  import Engine, {EngineError, MissConfiguredError} from './Engine.js';
2
2
 
3
- export class HTTPEngineError extends EngineError {
4
- }
3
+ /**
4
+ * Represents an error specific to HTTP engine operations.
5
+ * @class HTTPEngineError
6
+ * @extends EngineError
7
+ */
8
+ export class HTTPEngineError extends EngineError {}
5
9
 
10
+ /**
11
+ * Error indicating a failed HTTP request.
12
+ * @class HTTPRequestFailedError
13
+ * @extends HTTPEngineError
14
+ *
15
+ * @param {string} url - The URL of the failed request.
16
+ * @param {Object} options - The options used in the fetch request.
17
+ * @param {Response} response - The HTTP response object.
18
+ */
6
19
  export class HTTPRequestFailedError extends HTTPEngineError {
7
20
  constructor(url, options, response) {
8
21
  const method = options.method?.toLowerCase() || 'get';
@@ -13,7 +26,25 @@ export class HTTPRequestFailedError extends HTTPEngineError {
13
26
  }
14
27
  }
15
28
 
16
- export default class HTTPEngine extends Engine {
29
+ /**
30
+ * HTTPEngine is an extension of the Engine class that provides methods for interacting with HTTP-based APIs.
31
+ * It uses the Fetch API for sending and receiving data.
32
+ *
33
+ * @class HTTPEngine
34
+ * @extends Engine
35
+ */
36
+ class HTTPEngine extends Engine {
37
+
38
+ /**
39
+ * Configures the HTTP engine with additional fetch options.
40
+ * Sets the Accept header to 'application/json' by default.
41
+ *
42
+ * @param {Object} configuration - Configuration object containing fetch options and other settings.
43
+ * @param {string} [configuration.host] - Hostname and protocol of the HTTP service to use (ie: https://example.com).
44
+ * @param {string?} [configuration.prefix] - The prefix on the host to perform operations against.
45
+ * @param {Object} [configuration.fetchOptions] - Fetch overrides to attach to all requests sent to the HTTP service.
46
+ * @returns {Object} The configured settings for the HTTP engine.
47
+ */
17
48
  static configure(configuration = {}) {
18
49
  configuration.fetchOptions = {
19
50
  ...(configuration.fetchOptions ?? {}),
@@ -26,16 +57,30 @@ export default class HTTPEngine extends Engine {
26
57
  return super.configure(configuration);
27
58
  }
28
59
 
60
+ /**
61
+ * Validates the engine's configuration, ensuring that the host is defined.
62
+ *
63
+ * @throws {MissConfiguredError} Thrown if the configuration is missing a host.
64
+ */
29
65
  static checkConfiguration() {
30
- if (
31
- !this._configuration?.host
32
- ) throw new MissConfiguredError(this._configuration);
66
+ if (!this.configuration?.host) throw new MissConfiguredError(this.configuration);
33
67
  }
34
68
 
69
+ /**
70
+ * Returns the fetch options for reading operations.
71
+ *
72
+ * @returns {Object} The fetch options for reading.
73
+ */
35
74
  static _getReadOptions() {
36
- return this._configuration.fetchOptions;
75
+ return this.configuration.fetchOptions;
37
76
  }
38
77
 
78
+ /**
79
+ * Returns the fetch options for writing (PUT) operations.
80
+ * Sets the method to PUT and adds a Content-Type header of 'application/json'.
81
+ *
82
+ * @returns {Object} The fetch options for writing.
83
+ */
39
84
  static _getWriteOptions() {
40
85
  return {
41
86
  ...this._getReadOptions(),
@@ -47,8 +92,18 @@ export default class HTTPEngine extends Engine {
47
92
  };
48
93
  }
49
94
 
95
+ /**
96
+ * Processes a fetch request with error handling. Throws an error if the response is not successful.
97
+ *
98
+ * @param {string|URL} url - The URL to fetch.
99
+ * @param {Object} options - The fetch options.
100
+ * @param {*} [defaultValue] - A default value to return if the fetch fails.
101
+ * @returns {Promise<Object>} The parsed JSON response.
102
+ *
103
+ * @throws {HTTPRequestFailedError} Thrown if the fetch request fails.
104
+ */
50
105
  static async _processFetch(url, options, defaultValue = undefined) {
51
- return this._configuration.fetch(url, options)
106
+ return this.configuration.fetch(url, options)
52
107
  .then(response => {
53
108
  if (!response.ok) {
54
109
  if (defaultValue !== undefined) {
@@ -63,22 +118,38 @@ export default class HTTPEngine extends Engine {
63
118
  .then(r => r.json());
64
119
  }
65
120
 
121
+ /**
122
+ * Retrieves an object by its ID from the server.
123
+ *
124
+ * @param {string} id - The ID of the object to retrieve.
125
+ * @returns {Promise<Object>} The retrieved object in JSON format.
126
+ *
127
+ * @throws {HTTPRequestFailedError} Thrown if the fetch request fails.
128
+ */
66
129
  static async getById(id) {
67
130
  this.checkConfiguration();
68
131
 
69
132
  const url = new URL([
70
- this._configuration.host,
71
- this._configuration.prefix,
133
+ this.configuration.host,
134
+ this.configuration.prefix,
72
135
  `${id}.json`,
73
136
  ].filter(e => !!e).join('/'));
74
137
 
75
138
  return await this._processFetch(url, this._getReadOptions());
76
139
  }
77
140
 
141
+ /**
142
+ * Uploads (puts) a model object to the server.
143
+ *
144
+ * @param {Model} model - The model object to upload.
145
+ * @returns {Promise<Object>} The response from the server.
146
+ *
147
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
148
+ */
78
149
  static async putModel(model) {
79
150
  const url = new URL([
80
- this._configuration.host,
81
- this._configuration.prefix,
151
+ this.configuration.host,
152
+ this.configuration.prefix,
82
153
  `${model.id}.json`,
83
154
  ].filter(e => !!e).join('/'));
84
155
 
@@ -88,12 +159,20 @@ export default class HTTPEngine extends Engine {
88
159
  });
89
160
  }
90
161
 
162
+ /**
163
+ * Uploads (puts) an index object to the server.
164
+ *
165
+ * @param {Object} index - The index data to upload, organized by location.
166
+ * @returns {Promise<void>}
167
+ *
168
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
169
+ */
91
170
  static async putIndex(index) {
92
171
  const processIndex = async (location, models) => {
93
172
  const modelIndex = Object.fromEntries(models.map(m => [m.id, m.toIndexData()]));
94
173
  const url = new URL([
95
- this._configuration.host,
96
- this._configuration.prefix,
174
+ this.configuration.host,
175
+ this.configuration.prefix,
97
176
  location,
98
177
  '_index.json',
99
178
  ].filter(e => !!e).join('/'));
@@ -114,16 +193,33 @@ export default class HTTPEngine extends Engine {
114
193
  await processIndex(null, Object.values(index).flat());
115
194
  }
116
195
 
117
- static async getIndex(location) {
118
- const url = new URL([this._configuration.host, this._configuration.prefix, location, '_index.json'].filter(e => !!e).join('/'));
196
+ /**
197
+ * Retrieves the index object from the server for the specified location.
198
+ *
199
+ * @param {Model.constructor?} model - The model in the host where the index is stored.
200
+ * @returns {Promise<Object>} The index data in JSON format.
201
+ */
202
+ static async getIndex(model) {
203
+ const url = new URL([
204
+ this.configuration.host,
205
+ this.configuration.prefix,
206
+ model?.toString(),
207
+ '_index.json',
208
+ ].filter(e => Boolean(e)).join('/'));
119
209
 
120
210
  return await this._processFetch(url, this._getReadOptions(), {});
121
211
  }
122
212
 
213
+ /**
214
+ * Retrieves the compiled search index for a model from the server.
215
+ *
216
+ * @param {Model.constructor} model - The model whose compiled search index to retrieve.
217
+ * @returns {Promise<Object>} The compiled search index in JSON format.
218
+ */
123
219
  static async getSearchIndexCompiled(model) {
124
220
  const url = new URL([
125
- this._configuration.host,
126
- this._configuration.prefix,
221
+ this.configuration.host,
222
+ this.configuration.prefix,
127
223
  model.toString(),
128
224
  '_search_index.json',
129
225
  ].join('/'));
@@ -131,10 +227,16 @@ export default class HTTPEngine extends Engine {
131
227
  return await this._processFetch(url, this._getReadOptions());
132
228
  }
133
229
 
230
+ /**
231
+ * Retrieves the raw (uncompiled) search index for a model from the server.
232
+ *
233
+ * @param {Model.constructor} model - The model whose raw search index to retrieve.
234
+ * @returns {Promise<Object>} The raw search index in JSON format, or an empty object if not found.
235
+ */
134
236
  static async getSearchIndexRaw(model) {
135
237
  const url = new URL([
136
- this._configuration.host,
137
- this._configuration.prefix,
238
+ this.configuration.host,
239
+ this.configuration.prefix,
138
240
  model.toString(),
139
241
  '_search_index_raw.json',
140
242
  ].join('/'));
@@ -142,11 +244,20 @@ export default class HTTPEngine extends Engine {
142
244
  return await this._processFetch(url, this._getReadOptions()).catch(() => ({}));
143
245
  }
144
246
 
247
+ /**
248
+ * Uploads (puts) a compiled search index for a model to the server.
249
+ *
250
+ * @param {Model.constructor} model - The model whose compiled search index to upload.
251
+ * @param {Object} compiledIndex - The compiled search index data.
252
+ * @returns {Promise<Object>} The response from the server.
253
+ *
254
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
255
+ */
145
256
  static async putSearchIndexCompiled(model, compiledIndex) {
146
257
  const url = new URL([
147
- this._configuration.host,
148
- this._configuration.prefix,
149
- model.name,
258
+ this.configuration.host,
259
+ this.configuration.prefix,
260
+ model.toString(),
150
261
  '_search_index.json',
151
262
  ].filter(e => !!e).join('/'));
152
263
 
@@ -156,11 +267,20 @@ export default class HTTPEngine extends Engine {
156
267
  });
157
268
  }
158
269
 
270
+ /**
271
+ * Uploads (puts) a raw search index for a model to the server.
272
+ *
273
+ * @param {Model.constructor} model - The model whose raw search index to upload.
274
+ * @param {Object} rawIndex - The raw search index data.
275
+ * @returns {Promise<Object>} The response from the server.
276
+ *
277
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
278
+ */
159
279
  static async putSearchIndexRaw(model, rawIndex) {
160
280
  const url = new URL([
161
- this._configuration.host,
162
- this._configuration.prefix,
163
- model.name,
281
+ this.configuration.host,
282
+ this.configuration.prefix,
283
+ model.toString(),
164
284
  '_search_index_raw.json',
165
285
  ].filter(e => !!e).join('/'));
166
286
 
@@ -170,3 +290,5 @@ export default class HTTPEngine extends Engine {
170
290
  });
171
291
  }
172
292
  }
293
+
294
+ export default HTTPEngine;