@acodeninja/persist 2.2.1 → 2.2.3
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/.deepsource.toml +10 -0
- package/package.json +1 -1
- package/src/Persist.js +3 -1
- package/src/Query.js +91 -35
- package/src/SchemaCompiler.js +68 -17
- package/src/Transactions.js +80 -2
- package/src/engine/Engine.js +228 -53
- package/src/engine/FileEngine.js +118 -32
- package/src/engine/HTTPEngine.js +156 -34
- package/src/engine/S3Engine.js +132 -34
- package/src/type/Model.js +139 -24
- package/src/type/Type.js +42 -8
- package/src/type/complex/ArrayType.js +54 -1
- package/src/type/complex/CustomType.js +35 -1
- package/src/type/resolved/ResolvedType.js +39 -1
- package/src/type/resolved/SlugType.js +41 -1
- package/src/type/simple/BooleanType.js +16 -1
- package/src/type/simple/DateType.js +28 -1
- package/src/type/simple/NumberType.js +16 -1
- package/src/type/simple/SimpleType.js +11 -1
- package/src/type/simple/StringType.js +16 -1
package/.deepsource.toml
ADDED
package/package.json
CHANGED
package/src/Persist.js
CHANGED
@@ -4,7 +4,7 @@ import enableTransactions from './Transactions.js';
|
|
4
4
|
/**
|
5
5
|
* @class Persist
|
6
6
|
*/
|
7
|
-
|
7
|
+
class Persist {
|
8
8
|
static _engine = {};
|
9
9
|
/**
|
10
10
|
* @memberof Persist
|
@@ -42,3 +42,5 @@ export default class Persist {
|
|
42
42
|
engine.configure(configuration);
|
43
43
|
}
|
44
44
|
}
|
45
|
+
|
46
|
+
export default Persist;
|
package/src/Query.js
CHANGED
@@ -1,61 +1,117 @@
|
|
1
1
|
/**
|
2
|
-
*
|
3
|
-
*
|
4
|
-
*
|
5
|
-
*
|
6
|
-
*
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
*
|
2
|
+
* The `Query` class is responsible for executing searches on an indexed dataset
|
3
|
+
* based on a structured query. It supports various query types including value matches,
|
4
|
+
* contains matches, and nested queries.
|
5
|
+
*
|
6
|
+
* @example
|
7
|
+
* // The object has the property `title` witch exactly equals `test`.
|
8
|
+
* const query = new Query({title: 'test'});
|
9
|
+
* const query = new Query({title: {$is: 'test'}});
|
10
|
+
*
|
11
|
+
* // The object has the property `list` witch contains the string `test`.
|
12
|
+
* const query = new Query({list: {$contains: 'test'}});
|
13
|
+
*
|
14
|
+
* // The object has the property `string` witch contains the string `es`.
|
15
|
+
* const query = new Query({string: {$contains: 'es'}});
|
16
|
+
*
|
17
|
+
* // The object has the property `list` contains an object
|
18
|
+
* // with a property `string` that contains the string `test`.
|
19
|
+
* const query = new Query({
|
20
|
+
* list: {
|
21
|
+
* $contains: {
|
22
|
+
* string: {
|
23
|
+
* $contains: 'test'
|
24
|
+
* }
|
25
|
+
* }
|
26
|
+
* }
|
27
|
+
* });
|
11
28
|
*/
|
12
29
|
class Query {
|
30
|
+
/**
|
31
|
+
* The query object that defines the search criteria.
|
32
|
+
* @type {Object}
|
33
|
+
*/
|
13
34
|
query;
|
14
35
|
|
15
36
|
/**
|
37
|
+
* Constructs a new `Query` instance with the provided query object.
|
16
38
|
*
|
17
|
-
* @param {
|
39
|
+
* @param {Object} query - The structured query object defining the search criteria.
|
18
40
|
*/
|
19
41
|
constructor(query) {
|
20
42
|
this.query = query;
|
21
43
|
}
|
22
44
|
|
23
45
|
/**
|
24
|
-
*
|
46
|
+
* Executes the query against a model's index and returns the matching results.
|
25
47
|
*
|
26
|
-
* @param {
|
27
|
-
* @param {
|
48
|
+
* @param {Model.constructor} model - The model class that contains the `fromData` method for constructing models from data.
|
49
|
+
* @param {Object<string, Model>} index - The index dataset to search through.
|
50
|
+
* @returns {Array<Model>} The models that match the query.
|
28
51
|
*/
|
29
52
|
execute(model, index) {
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
53
|
+
return Object.values(index)
|
54
|
+
.filter(m =>
|
55
|
+
this._splitQuery(this.query)
|
56
|
+
.map(query => Boolean(this._matchesQuery(m, query)))
|
57
|
+
.every(c => c),
|
58
|
+
)
|
59
|
+
.map(m => model.fromData(m));
|
60
|
+
}
|
36
61
|
|
37
|
-
|
62
|
+
/**
|
63
|
+
* Recursively checks if a subject matches a given query.
|
64
|
+
*
|
65
|
+
* This function supports matching:
|
66
|
+
* - Primitive values directly (`string`, `number`, `boolean`)
|
67
|
+
* - The `$is` property for exact matches
|
68
|
+
* - The `$contains` property for substring or array element matches
|
69
|
+
*
|
70
|
+
* @private
|
71
|
+
* @param {*} subject - The subject to be matched.
|
72
|
+
* @param {Object} [inputQuery=this.query] - The query to match against. Defaults to `this.query` if not provided.
|
73
|
+
* @returns {boolean} True if the subject matches the query, otherwise false.
|
74
|
+
*/
|
75
|
+
_matchesQuery(subject, inputQuery = this.query) {
|
76
|
+
if (['string', 'number', 'boolean'].includes(typeof inputQuery)) return subject === inputQuery;
|
38
77
|
|
39
|
-
|
40
|
-
if (subject === inputQuery.$is) return true;
|
78
|
+
if (inputQuery?.$is !== undefined && subject === inputQuery.$is) return true;
|
41
79
|
|
42
|
-
|
43
|
-
|
80
|
+
if (inputQuery?.$contains !== undefined) {
|
81
|
+
if (subject.includes?.(inputQuery.$contains)) return true;
|
44
82
|
|
45
|
-
|
46
|
-
|
47
|
-
}
|
83
|
+
for (const value of subject) {
|
84
|
+
if (this._matchesQuery(value, inputQuery.$contains)) return true;
|
48
85
|
}
|
86
|
+
}
|
49
87
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
};
|
88
|
+
for (const key of Object.keys(inputQuery)) {
|
89
|
+
if (!['$is', '$contains'].includes(key))
|
90
|
+
if (this._matchesQuery(subject[key], inputQuery[key])) return true;
|
91
|
+
}
|
55
92
|
|
56
|
-
return
|
57
|
-
|
58
|
-
|
93
|
+
return false;
|
94
|
+
};
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Recursively splits an object into an array of objects,
|
98
|
+
* where each key-value pair from the input query becomes a separate object.
|
99
|
+
*
|
100
|
+
* If the value of a key is a nested object (and not an array),
|
101
|
+
* the function recursively splits it, preserving the parent key.
|
102
|
+
*
|
103
|
+
* @private
|
104
|
+
* @param {Object} query - The input object to be split into individual key-value pairs.
|
105
|
+
* @returns {Array<Object>} An array of objects, where each object contains a single key-value pair
|
106
|
+
* from the original query or its nested objects.
|
107
|
+
*/
|
108
|
+
_splitQuery(query) {
|
109
|
+
return Object.entries(query)
|
110
|
+
.flatMap(([key, value]) =>
|
111
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
112
|
+
? this._splitQuery(value).map(nestedObj => ({[key]: nestedObj}))
|
113
|
+
: {[key]: value},
|
114
|
+
);
|
59
115
|
}
|
60
116
|
}
|
61
117
|
|
package/src/SchemaCompiler.js
CHANGED
@@ -4,13 +4,21 @@ import ajvErrors from 'ajv-errors';
|
|
4
4
|
import ajvFormats from 'ajv-formats';
|
5
5
|
|
6
6
|
/**
|
7
|
-
*
|
7
|
+
* A class responsible for compiling raw schema definitions into a format that can be validated using the AJV (Another JSON Validator) library.
|
8
8
|
*/
|
9
|
-
|
9
|
+
class SchemaCompiler {
|
10
10
|
/**
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
11
|
+
* Compiles a raw schema into a validation-ready schema, and returns a class that extends `CompiledSchema`.
|
12
|
+
*
|
13
|
+
* This method converts a given schema into a JSON schema-like format, setting up properties, types, formats, and validation rules.
|
14
|
+
* It uses AJV for the validation process and integrates with model types and their specific validation rules.
|
15
|
+
*
|
16
|
+
* @param {Object|Model} rawSchema - The raw schema or model definition to be compiled.
|
17
|
+
* @returns {CompiledSchema} - A class that extends `CompiledSchema`, with the compiled schema and validator attached.
|
18
|
+
*
|
19
|
+
* @example
|
20
|
+
* const schemaClass = SchemaCompiler.compile(MyModelSchema);
|
21
|
+
* const isValid = schemaClass.validate(data); // Throws ValidationError if data is invalid.
|
14
22
|
*/
|
15
23
|
static compile(rawSchema) {
|
16
24
|
const validation = new ajv({allErrors: true});
|
@@ -27,7 +35,7 @@ export default class SchemaCompiler {
|
|
27
35
|
|
28
36
|
if (Type.Model.isModel(rawSchema)) {
|
29
37
|
schema.required.push('id');
|
30
|
-
schema.properties
|
38
|
+
schema.properties.id = {type: 'string'};
|
31
39
|
}
|
32
40
|
|
33
41
|
for (const [name, type] of Object.entries(rawSchema)) {
|
@@ -88,7 +96,20 @@ export default class SchemaCompiler {
|
|
88
96
|
}
|
89
97
|
|
90
98
|
class Schema extends CompiledSchema {
|
99
|
+
/**
|
100
|
+
* The compiled schema definition.
|
101
|
+
* @type {Object}
|
102
|
+
* @static
|
103
|
+
* @private
|
104
|
+
*/
|
91
105
|
static _schema = schema;
|
106
|
+
|
107
|
+
/**
|
108
|
+
* The AJV validator function compiled from the schema.
|
109
|
+
* @type {Function}
|
110
|
+
* @static
|
111
|
+
* @private
|
112
|
+
*/
|
92
113
|
static _validator = validation.compile(schema);
|
93
114
|
}
|
94
115
|
|
@@ -96,20 +117,36 @@ export default class SchemaCompiler {
|
|
96
117
|
}
|
97
118
|
}
|
98
119
|
|
120
|
+
|
99
121
|
/**
|
100
|
-
*
|
101
|
-
*
|
102
|
-
* @property {Function} _validator
|
122
|
+
* Represents a compiled schema used for validating data models.
|
123
|
+
* This class provides a mechanism to validate data using a precompiled schema and a validator function.
|
103
124
|
*/
|
104
125
|
export class CompiledSchema {
|
126
|
+
/**
|
127
|
+
* The schema definition for validation, typically a precompiled JSON schema or similar.
|
128
|
+
* @type {?Object}
|
129
|
+
* @static
|
130
|
+
* @private
|
131
|
+
*/
|
105
132
|
static _schema = null;
|
133
|
+
|
134
|
+
/**
|
135
|
+
* The validator function used to validate data against the schema.
|
136
|
+
* @type {?Function}
|
137
|
+
* @static
|
138
|
+
* @private
|
139
|
+
*/
|
106
140
|
static _validator = null;
|
107
141
|
|
108
142
|
/**
|
109
|
-
*
|
110
|
-
*
|
111
|
-
*
|
112
|
-
*
|
143
|
+
* Validates the given data against the compiled schema.
|
144
|
+
*
|
145
|
+
* If the data is an instance of a model, it will be converted to a plain object via `toData()` before validation.
|
146
|
+
*
|
147
|
+
* @param {Object|Model} data - The data or model instance to be validated.
|
148
|
+
* @returns {boolean} - Returns `true` if the data is valid according to the schema.
|
149
|
+
* @throws {ValidationError} - Throws a `ValidationError` if the data is invalid.
|
113
150
|
*/
|
114
151
|
static validate(data) {
|
115
152
|
let inputData = Object.assign({}, data);
|
@@ -127,15 +164,29 @@ export class CompiledSchema {
|
|
127
164
|
}
|
128
165
|
|
129
166
|
/**
|
130
|
-
*
|
131
|
-
*
|
132
|
-
* @property {object[]} errors
|
133
|
-
* @property {object} data
|
167
|
+
* Represents a validation error that occurs when a model or data fails validation.
|
168
|
+
* Extends the built-in JavaScript `Error` class.
|
134
169
|
*/
|
135
170
|
export class ValidationError extends Error {
|
171
|
+
/**
|
172
|
+
* Creates an instance of `ValidationError`.
|
173
|
+
*
|
174
|
+
* @param {Object} data - The data that failed validation.
|
175
|
+
* @param {Array<Object>} errors - A list of validation errors, each typically containing details about what failed.
|
176
|
+
*/
|
136
177
|
constructor(data, errors) {
|
137
178
|
super('Validation failed');
|
179
|
+
/**
|
180
|
+
* An array of validation errors, containing details about each failed validation.
|
181
|
+
* @type {Array<Object>}
|
182
|
+
*/
|
138
183
|
this.errors = errors;
|
184
|
+
/**
|
185
|
+
* The data that caused the validation error.
|
186
|
+
* @type {Object}
|
187
|
+
*/
|
139
188
|
this.data = data;
|
140
189
|
}
|
141
190
|
}
|
191
|
+
|
192
|
+
export default SchemaCompiler;
|
package/src/Transactions.js
CHANGED
@@ -1,32 +1,110 @@
|
|
1
|
+
/**
|
2
|
+
* Class representing a transaction-related error.
|
3
|
+
*
|
4
|
+
* @class TransactionError
|
5
|
+
* @extends Error
|
6
|
+
*/
|
1
7
|
class TransactionError extends Error {
|
2
8
|
}
|
3
9
|
|
10
|
+
/**
|
11
|
+
* Error thrown when a transaction is already committed.
|
12
|
+
*
|
13
|
+
* @class TransactionCommittedError
|
14
|
+
* @extends TransactionError
|
15
|
+
*/
|
4
16
|
export class TransactionCommittedError extends TransactionError {
|
17
|
+
/**
|
18
|
+
* Creates an instance of TransactionCommittedError.
|
19
|
+
* This error is thrown when attempting to commit an already committed transaction.
|
20
|
+
* @property {string} message - The error message.
|
21
|
+
*/
|
5
22
|
message = 'Transaction was already committed.';
|
6
23
|
}
|
7
24
|
|
25
|
+
/**
|
26
|
+
* Enables transaction support for the provided engine.
|
27
|
+
*
|
28
|
+
* This function enhances an engine class with transaction capabilities, allowing multiple
|
29
|
+
* changes to be grouped into a single transaction that can be committed or rolled back.
|
30
|
+
*
|
31
|
+
* @param {Engine.constructor} engine - The base engine class to be enhanced with transaction support.
|
32
|
+
* @returns {TransactionalEngine.constructor} TransactionalEngine - The enhanced engine class with transaction functionality.
|
33
|
+
*/
|
8
34
|
export default function enableTransactions(engine) {
|
35
|
+
/**
|
36
|
+
* A class representing an engine with transaction capabilities.
|
37
|
+
* @class TransactionalEngine
|
38
|
+
* @extends {engine}
|
39
|
+
*/
|
9
40
|
class TransactionalEngine extends engine {
|
10
41
|
}
|
11
42
|
|
43
|
+
/**
|
44
|
+
* Starts a transaction on the engine. Returns a Transaction class that can handle
|
45
|
+
* put, commit, and rollback actions for the transaction.
|
46
|
+
*
|
47
|
+
* @returns {Transaction.constructor} Transaction - A class that manages the transaction's operations.
|
48
|
+
*/
|
12
49
|
TransactionalEngine.start = () => {
|
50
|
+
/**
|
51
|
+
* A class representing an active transaction on the engine.
|
52
|
+
* Contains methods to put changes, commit the transaction, or roll back in case of failure.
|
53
|
+
*
|
54
|
+
* @class Transaction
|
55
|
+
*/
|
13
56
|
class Transaction extends TransactionalEngine {
|
57
|
+
/**
|
58
|
+
* @property {Array<Object>} transactions - An array storing all the operations within the transaction.
|
59
|
+
* @static
|
60
|
+
*/
|
14
61
|
static transactions = [];
|
62
|
+
|
63
|
+
/**
|
64
|
+
* @property {boolean} committed - Indicates if the transaction has been committed.
|
65
|
+
* @static
|
66
|
+
*/
|
15
67
|
static committed = false;
|
68
|
+
|
69
|
+
/**
|
70
|
+
* @property {boolean} failed - Indicates if the transaction has failed.
|
71
|
+
* @static
|
72
|
+
*/
|
16
73
|
static failed = false;
|
17
74
|
|
18
|
-
|
75
|
+
/**
|
76
|
+
* Adds a model to the transaction queue.
|
77
|
+
*
|
78
|
+
* @param {Object} model - The model to be added to the transaction.
|
79
|
+
* @returns {Promise<void>} A promise that resolves once the model is added.
|
80
|
+
*/
|
81
|
+
static put(model) {
|
19
82
|
this.transactions.push({
|
20
83
|
hasRun: false,
|
21
84
|
hasRolledBack: false,
|
22
85
|
model,
|
23
86
|
});
|
87
|
+
|
88
|
+
return Promise.resolve();
|
24
89
|
}
|
25
90
|
|
91
|
+
/**
|
92
|
+
* Checks if the transaction has already been committed. If true, throws a TransactionCommittedError.
|
93
|
+
*
|
94
|
+
* @throws {TransactionCommittedError} If the transaction has already been committed.
|
95
|
+
* @private
|
96
|
+
*/
|
26
97
|
static _checkCommitted() {
|
27
98
|
if (this.committed) throw new TransactionCommittedError();
|
28
99
|
}
|
29
100
|
|
101
|
+
/**
|
102
|
+
* Commits the transaction, applying all the changes to the engine.
|
103
|
+
* Rolls back if any part of the transaction fails.
|
104
|
+
*
|
105
|
+
* @returns {Promise<void>} A promise that resolves once the transaction is committed, or rejects if an error occurs.
|
106
|
+
* @throws {Error} If any operation in the transaction fails.
|
107
|
+
*/
|
30
108
|
static async commit() {
|
31
109
|
this._checkCommitted();
|
32
110
|
|
@@ -34,7 +112,7 @@ export default function enableTransactions(engine) {
|
|
34
112
|
for (const [index, {model}] of this.transactions.entries()) {
|
35
113
|
try {
|
36
114
|
this.transactions[index].original = await engine.get(model.constructor, model.id);
|
37
|
-
} catch (
|
115
|
+
} catch (_error) {
|
38
116
|
this.transactions[index].original = null;
|
39
117
|
}
|
40
118
|
|