@acodeninja/persist 3.0.0-next.14 → 3.0.0-next.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acodeninja/persist",
3
- "version": "3.0.0-next.14",
3
+ "version": "3.0.0-next.16",
4
4
  "description": "A JSON based data modelling and persistence module with alternate storage mechanisms.",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/Connection.js CHANGED
@@ -7,6 +7,14 @@ import Model from './data/Model.js';
7
7
  import SearchIndex from './data/SearchIndex.js';
8
8
  import _ from 'lodash';
9
9
 
10
+ /**
11
+ * Represents a transactional operation to be executed, typically queued and later committed.
12
+ *
13
+ * Stores the method to invoke, the arguments to apply, and tracks the result or error state
14
+ * of the transaction once it's processed.
15
+ *
16
+ * @class Transaction
17
+ */
10
18
  export class Transaction {
11
19
  constructor(method, ...args) {
12
20
  this.method = method;
@@ -81,6 +89,12 @@ export default class Connection {
81
89
  async hydrate(dryModel) {
82
90
  const hydratedModels = {};
83
91
 
92
+ /**
93
+ * Recursively hydrates a single model and its nested properties.
94
+ *
95
+ * @param {Object|Model} modelToProcess - The model instance to hydrate.
96
+ * @returns {Promise<Model>} The hydrated model instance.
97
+ */
84
98
  const hydrateModel = async (modelToProcess) => {
85
99
  hydratedModels[modelToProcess.id] = modelToProcess;
86
100
 
@@ -97,6 +111,12 @@ export default class Connection {
97
111
  return modelToProcess;
98
112
  };
99
113
 
114
+ /**
115
+ * Hydrates a nested sub-model if it hasn't already been hydrated.
116
+ *
117
+ * @param {Object} property - The sub-model with a known ID but incomplete data.
118
+ * @returns {Promise<Model>} The fully hydrated sub-model.
119
+ */
100
120
  const hydrateSubModel = async (property) => {
101
121
  if (hydratedModels[property.id]) {
102
122
  return hydratedModels[property.id];
@@ -109,6 +129,12 @@ export default class Connection {
109
129
  return hydratedSubModel;
110
130
  };
111
131
 
132
+ /**
133
+ * Hydrates a list of related sub-models.
134
+ *
135
+ * @param {Array<Object>} property - Array of dry sub-models.
136
+ * @returns {Promise<Array<Model>>} Array of hydrated sub-models.
137
+ */
112
138
  const hydrateModelList = async (property) => {
113
139
  const newModelList = await Promise.all(property.map(subModel => {
114
140
  if (hydratedModels[subModel.id]) {
@@ -165,7 +191,7 @@ export default class Connection {
165
191
 
166
192
  if (
167
193
  Boolean(modelToProcess.constructor.indexedProperties().length) &&
168
- this.#indexedFieldsHaveChanged(currentModel, modelToProcess)
194
+ (!currentModel || JSON.stringify(currentModel.toIndexData()) !== JSON.stringify(modelToProcess.toIndexData()))
169
195
  ) {
170
196
  const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id);
171
197
  modelsToReindex[modelToProcessConstructor] = modelsToReindex[modelToProcessConstructor] || [];
@@ -174,7 +200,7 @@ export default class Connection {
174
200
 
175
201
  if (
176
202
  Boolean(modelToProcess.constructor.searchProperties().length) &&
177
- this.#searchableFieldsHaveChanged(currentModel, modelToProcess)
203
+ (!currentModel || JSON.stringify(currentModel.toSearchData()) !== JSON.stringify(modelToProcess.toSearchData()))
178
204
  ) {
179
205
  const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id);
180
206
  modelsToReindexSearch[modelToProcessConstructor] = modelsToReindexSearch[modelToProcessConstructor] || [];
@@ -458,28 +484,6 @@ export default class Connection {
458
484
  return constructor;
459
485
  }
460
486
 
461
- /**
462
- * Decide if two models indexable fields are different
463
- * @param {Model} currentModel
464
- * @param {Model} modelToProcess
465
- * @return {boolean}
466
- * @private
467
- */
468
- #indexedFieldsHaveChanged(currentModel, modelToProcess) {
469
- return !currentModel || JSON.stringify(currentModel.toIndexData()) !== JSON.stringify(modelToProcess.toIndexData());
470
- }
471
-
472
- /**
473
- * Decide if two models searchable fields have changed
474
- * @param {Model} currentModel
475
- * @param {Model} modelToProcess
476
- * @return {boolean}
477
- * @private
478
- */
479
- #searchableFieldsHaveChanged(currentModel, modelToProcess) {
480
- return !currentModel || JSON.stringify(currentModel.toSearchData()) !== JSON.stringify(modelToProcess.toSearchData());
481
- }
482
-
483
487
  /**
484
488
  * Get model classes that are directly linked to the given model in either direction
485
489
  * @param {Model.constructor} model
@@ -560,22 +564,26 @@ export default class Connection {
560
564
  }
561
565
 
562
566
  /**
567
+ * Base class for errors that occur during connection operations.
568
+ *
563
569
  * @class ConnectionError
564
570
  * @extends Error
565
571
  */
566
572
  export class ConnectionError extends Error {
567
- /**
568
- * @param {String} message
569
- */
570
- constructor(message) {
571
- super(message);
572
- }
573
573
  }
574
574
 
575
+ /**
576
+ * Thrown when a connection is created with missing arguments.
577
+ *
578
+ * @class MissingArgumentsConnectionError
579
+ * @extends ConnectionError
580
+ */
575
581
  export class MissingArgumentsConnectionError extends ConnectionError {
576
582
  }
577
583
 
578
584
  /**
585
+ * Thrown when a model class is not registered.
586
+ *
579
587
  * @class ModelNotRegisteredConnectionError
580
588
  * @extends ConnectionError
581
589
  */
@@ -590,12 +598,23 @@ export class ModelNotRegisteredConnectionError extends ConnectionError {
590
598
  }
591
599
  }
592
600
 
601
+ /**
602
+ * Base class for errors that occur during transactions.
603
+ *
604
+ * @class TransactionError
605
+ * @extends {Error}
606
+ */
593
607
  class TransactionError extends Error {
594
- constructor(message) {
595
- super(message);
596
- }
597
608
  }
598
609
 
610
+ /**
611
+ * Thrown when a transaction fails to commit.
612
+ *
613
+ * Contains the original error and the list of transactions involved.
614
+ *
615
+ * @class CommitFailedTransactionError
616
+ * @extends {TransactionError}
617
+ */
599
618
  export class CommitFailedTransactionError extends TransactionError {
600
619
  /**
601
620
  *
package/src/Schema.js CHANGED
@@ -26,6 +26,15 @@ class Schema {
26
26
  ajvErrors(validation);
27
27
  ajvFormats(validation);
28
28
 
29
+ /**
30
+ * Recursively builds a JSON-schema-like object from a model or schema segment.
31
+ *
32
+ * Handles both `Model` instances and schema property definitions,
33
+ * including nested models and required property rules.
34
+ *
35
+ * @param {Object|Model|Type} schemaSegment - A model or a property descriptor.
36
+ * @returns {Object} A JSON schema representation of the input segment.
37
+ */
29
38
  function BuildSchema(schemaSegment) {
30
39
  const thisSchema = {};
31
40
 
package/src/data/Model.js CHANGED
@@ -151,6 +151,14 @@ class Model {
151
151
  * @static
152
152
  */
153
153
  static get required() {
154
+ /**
155
+ * A subclass of the current model with the `_required` flag set to `true`.
156
+ * Used to indicate that the property is required during validation or schema generation.
157
+ *
158
+ * @class
159
+ * @extends {Model}
160
+ * @private
161
+ */
154
162
  class Required extends this {
155
163
  static _required = true;
156
164
  }
@@ -1,5 +1,10 @@
1
1
  import lunr from 'lunr';
2
2
 
3
+ /**
4
+ * Represents a single search result with the associated model instance and its relevance score.
5
+ *
6
+ * @class SearchResult
7
+ */
3
8
  export class SearchResult {
4
9
  constructor(model, score) {
5
10
  this.model = model;
@@ -7,11 +12,24 @@ export class SearchResult {
7
12
  }
8
13
  }
9
14
 
15
+ /**
16
+ * A full-text search index wrapper using Lunr.js for a given model.
17
+ * Supports indexing and querying model data.
18
+ *
19
+ * @class SearchIndex
20
+ */
10
21
  export default class SearchIndex {
11
22
  #index;
12
23
  #model;
13
24
  #compiledIndex;
14
25
 
26
+ /**
27
+ * Initializes the search index for the provided model.
28
+ *
29
+ * @param {Model} model - The model definition to use for indexing.
30
+ * @param {Object.<string, Object>} index - A dictionary of model data, keyed by ID.
31
+ * @throws {NoIndexAvailableSearchIndexError} If the model has no searchable properties.
32
+ */
15
33
  constructor(model, index) {
16
34
  this.#index = index;
17
35
  this.#model = model;
@@ -21,9 +39,10 @@ export default class SearchIndex {
21
39
  }
22
40
 
23
41
  /**
42
+ * Performs a search query on the compiled Lunr index.
24
43
  *
25
- * @param {string} query
26
- * @return {Array<SearchResult>}
44
+ * @param {string} query - The search string.
45
+ * @return {Array<SearchResult>} An array of search results with model instances and scores.
27
46
  */
28
47
  search(query) {
29
48
  return this.searchIndex
@@ -31,10 +50,21 @@ export default class SearchIndex {
31
50
  .map(doc => new SearchResult(this.#model.fromData(this.#index[doc.ref]), doc.score));
32
51
  }
33
52
 
53
+ /**
54
+ * Lazily compiles and returns the Lunr index instance.
55
+ *
56
+ * @return {lunr.Index} The compiled Lunr index.
57
+ */
34
58
  get searchIndex() {
35
59
  return this.#compiledIndex ?? this.#compileIndex();
36
60
  }
37
61
 
62
+ /**
63
+ * Compiles the Lunr index using the model's search properties.
64
+ *
65
+ * @return {lunr.Index} The compiled Lunr index.
66
+ * @private
67
+ */
38
68
  #compileIndex() {
39
69
  const model = this.#model;
40
70
  const index = this.#index;
@@ -54,9 +84,21 @@ export default class SearchIndex {
54
84
  }
55
85
  }
56
86
 
87
+ /**
88
+ * Base error class for search index-related exceptions.
89
+ *
90
+ * @class SearchIndexError
91
+ * @extends {Error}
92
+ */
57
93
  export class SearchIndexError extends Error {
58
94
  }
59
95
 
96
+ /**
97
+ * Thrown when a model does not have any properties defined for indexing.
98
+ *
99
+ * @class NoIndexAvailableSearchIndexError
100
+ * @extends {SearchIndexError}
101
+ */
60
102
  export class NoIndexAvailableSearchIndexError extends SearchIndexError {
61
103
  constructor(model) {
62
104
  super(`The model ${model.name} has no search properties`);
@@ -53,6 +53,14 @@ class Type {
53
53
  * @returns {Type} A subclass of the current type with `_required` set to `true`.
54
54
  */
55
55
  static get required() {
56
+ /**
57
+ * A subclass of the current type with the `_required` flag set to `true`.
58
+ * Used to indicate that the property is required during validation or schema generation.
59
+ *
60
+ * @class
61
+ * @extends {Type}
62
+ * @private
63
+ */
56
64
  class Required extends this {
57
65
  static _required = true;
58
66
  }