@acodeninja/persist 2.3.0 → 2.3.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.
- package/README.md +21 -235
- package/docs/code-quirks.md +71 -0
- package/docs/model-property-types.md +173 -0
- package/docs/models-as-properties.md +159 -0
- package/docs/search-queries.md +47 -0
- package/docs/storage-engines.md +61 -0
- package/docs/structured-queries.md +107 -0
- package/docs/transactions.md +23 -0
- package/package.json +1 -1
- package/src/Persist.js +1 -1
- package/src/engine/Engine.js +6 -4
- package/src/engine/HTTPEngine.js +12 -12
- package/src/engine/S3Engine.js +5 -5
- package/src/type/Model.js +25 -5
- package/src/type/Type.js +8 -4
- package/src/type/complex/ArrayType.js +3 -3
- package/src/type/complex/CustomType.js +12 -4
- package/src/type/resolved/SlugType.js +8 -0
- package/src/type/simple/BooleanType.js +9 -5
- package/src/type/simple/DateType.js +14 -10
- package/src/type/simple/NumberType.js +9 -5
- package/src/type/simple/StringType.js +9 -5
@@ -0,0 +1,47 @@
|
|
1
|
+
# Search Queries
|
2
|
+
|
3
|
+
In addition to [structured queries](./structured-queries.md), persist also supports fuzzy search across fields indexed for search.
|
4
|
+
|
5
|
+
## Indexing Data for Search
|
6
|
+
|
7
|
+
To set index properties on a model for search, define the static function `searchProperties` as an arrow function that returns an array of fields that should be indexed for search.
|
8
|
+
|
9
|
+
Let's consider the following models:
|
10
|
+
|
11
|
+
```javascript
|
12
|
+
import Persist from "@acodeninja/persist";
|
13
|
+
|
14
|
+
export class Person extends Persist.Type.Model {
|
15
|
+
static {
|
16
|
+
this.name = Persist.Type.String.required;
|
17
|
+
this.address = () => Address;
|
18
|
+
this.searchProperties = () => ['name', 'address.address'];
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
export class Address extends Persist.Type.Model {
|
23
|
+
static {
|
24
|
+
this.address = Persist.Type.String.required;
|
25
|
+
this.postcode = Persist.Type.String.required;
|
26
|
+
this.searchProperties = () => ['address', 'postcode'];
|
27
|
+
}
|
28
|
+
}
|
29
|
+
```
|
30
|
+
|
31
|
+
Every time a `Person` model is put to a storage engine, the person's name and address are saved to the search index and can be queried.
|
32
|
+
|
33
|
+
## Searching
|
34
|
+
|
35
|
+
To search for any `Person` who lives on station road, the following search query can be run:
|
36
|
+
|
37
|
+
```javascript
|
38
|
+
import Persist from "@acodeninja/persist";
|
39
|
+
import Person from "./Person";
|
40
|
+
import FileEngine from "@acodeninja/persist/engine/file"
|
41
|
+
|
42
|
+
FileEngine
|
43
|
+
.configure(configuration)
|
44
|
+
.search(Person, 'station road');
|
45
|
+
```
|
46
|
+
|
47
|
+
This will find all matches for people who live at any address that includes `station road`.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Storage Engines
|
2
|
+
|
3
|
+
Persist makes several storage engines available for use with the library
|
4
|
+
|
5
|
+
## Filesystem Storage Engine
|
6
|
+
|
7
|
+
To store models using the local file system, use the `File` storage engine.
|
8
|
+
|
9
|
+
```javascript
|
10
|
+
import Persist from "@acodeninja/persist";
|
11
|
+
import FileEngine from "@acodeninja/persist/engine/file";
|
12
|
+
|
13
|
+
Persist.addEngine('local', FileEngine, {
|
14
|
+
path: '/app/storage',
|
15
|
+
});
|
16
|
+
|
17
|
+
export class Tag extends Persist.Type.Model {
|
18
|
+
static tag = Persist.Type.String.required;
|
19
|
+
}
|
20
|
+
|
21
|
+
await Persist.getEngine('local', FileEngine).put(new Tag({tag: 'documentation'}));
|
22
|
+
```
|
23
|
+
|
24
|
+
## HTTP Storage Engine
|
25
|
+
|
26
|
+
To store models using an HTTP server, use the `HTTP` storage engine. When using the `HTTP` engine in the browser, refer to [code quirks](./code-quirks.md#using-http-engine-in-browser).
|
27
|
+
|
28
|
+
```javascript
|
29
|
+
import Persist from "@acodeninja/persist";
|
30
|
+
import HTTPEngine from "@acodeninja/persist/engine/http";
|
31
|
+
|
32
|
+
Persist.addEngine('remote', HTTPEngine, {
|
33
|
+
host: 'https://api.example.com',
|
34
|
+
});
|
35
|
+
|
36
|
+
export class Tag extends Persist.Type.Model {
|
37
|
+
static tag = Persist.Type.String.required;
|
38
|
+
}
|
39
|
+
|
40
|
+
await Persist.getEngine('remote', HTTPEngine).put(new Tag({tag: 'documentation'}));
|
41
|
+
```
|
42
|
+
|
43
|
+
## S3 Storage Engine
|
44
|
+
|
45
|
+
To store models using an S3 Bucket, use the `S3` storage engine. To use the `S3` engine you must also add the `@aws-sdk/client-s3` dependency to your `package.json` file.
|
46
|
+
|
47
|
+
```javascript
|
48
|
+
import Persist from "@acodeninja/persist";
|
49
|
+
import S3Engine from "@acodeninja/persist/engine/s3";
|
50
|
+
|
51
|
+
Persist.addEngine('remote', S3Engine, {
|
52
|
+
bucket: 'test-bucket',
|
53
|
+
client: new S3Client(),
|
54
|
+
});
|
55
|
+
|
56
|
+
export class Tag extends Persist.Type.Model {
|
57
|
+
static tag = Persist.Type.String.required;
|
58
|
+
}
|
59
|
+
|
60
|
+
await Persist.getEngine('remote', S3Engine).put(new Tag({tag: 'documentation'}));
|
61
|
+
```
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Structured Queries
|
2
|
+
|
3
|
+
Use structured queries when you need to filter a collection of models using a series of exact and partial matching conditions.
|
4
|
+
|
5
|
+
## Indexing Data
|
6
|
+
|
7
|
+
To set index properties on a model, define the static function `indexProperties` as an arrow function that returns an array of fields that should be indexed for querying.
|
8
|
+
|
9
|
+
Let's consider the following models:
|
10
|
+
|
11
|
+
```javascript
|
12
|
+
import Persist from "@acodeninja/persist";
|
13
|
+
|
14
|
+
export class Person extends Persist.Type.Model {
|
15
|
+
static {
|
16
|
+
this.name = Persist.Type.String.required;
|
17
|
+
this.address = () => Address;
|
18
|
+
this.indexProperties = () => ['name', 'address.postcode'];
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
export class Address extends Persist.Type.Model {
|
23
|
+
static {
|
24
|
+
this.address = Persist.Type.String.required;
|
25
|
+
this.postcode = Persist.Type.String.required;
|
26
|
+
this.indexProperties = () => ['postcode'];
|
27
|
+
}
|
28
|
+
}
|
29
|
+
```
|
30
|
+
|
31
|
+
Every time a `Person` model is put to a storage engine, the person's name and address postcode are saved to the index and can be queried.
|
32
|
+
|
33
|
+
> [!NOTE]
|
34
|
+
> All fields included in the model index will be stored in the same file so be careful not to index fields that contain a lot of data.
|
35
|
+
|
36
|
+
## Querying Exact Matches
|
37
|
+
|
38
|
+
To query for a `Person` called `Joe Bloggs` an exact query can be written:
|
39
|
+
|
40
|
+
```javascript
|
41
|
+
import Persist from "@acodeninja/persist";
|
42
|
+
import Person from "./Person";
|
43
|
+
import FileEngine from "@acodeninja/persist/engine/file"
|
44
|
+
|
45
|
+
FileEngine
|
46
|
+
.configure(configuration)
|
47
|
+
.find(Person, {
|
48
|
+
name: {$is: 'Joe Bloggs'},
|
49
|
+
});
|
50
|
+
```
|
51
|
+
|
52
|
+
## Querying Partial Matches
|
53
|
+
|
54
|
+
To query for a `Person` with name `Joe` a contains query can be written:
|
55
|
+
|
56
|
+
```javascript
|
57
|
+
import Persist from "@acodeninja/persist";
|
58
|
+
import Person from "./Person";
|
59
|
+
import FileEngine from "@acodeninja/persist/engine/file"
|
60
|
+
|
61
|
+
FileEngine
|
62
|
+
.configure(configuration)
|
63
|
+
.find(Person, {
|
64
|
+
name: {$contains: 'Joe'},
|
65
|
+
});
|
66
|
+
```
|
67
|
+
|
68
|
+
## Querying Combination Matches
|
69
|
+
|
70
|
+
To query for a `Person` who lives at `SW1 1AA` a combination of contains and exact queries can be written:
|
71
|
+
|
72
|
+
```javascript
|
73
|
+
import Persist from "@acodeninja/persist";
|
74
|
+
import Person from "./Person";
|
75
|
+
import FileEngine from "@acodeninja/persist/engine/file"
|
76
|
+
|
77
|
+
FileEngine
|
78
|
+
.configure(configuration)
|
79
|
+
.find(Person, {
|
80
|
+
address: {
|
81
|
+
$contains: {
|
82
|
+
postcode: {$is: 'SW1 1AA'},
|
83
|
+
},
|
84
|
+
},
|
85
|
+
});
|
86
|
+
```
|
87
|
+
|
88
|
+
## Multiple Queries
|
89
|
+
|
90
|
+
To query for anyone called `Joe Bloggs` who lives in the `SW1` postcode area, we can combine queries:
|
91
|
+
|
92
|
+
```javascript
|
93
|
+
import Persist from "@acodeninja/persist";
|
94
|
+
import Person from "./Person";
|
95
|
+
import FileEngine from "@acodeninja/persist/engine/file"
|
96
|
+
|
97
|
+
FileEngine
|
98
|
+
.configure(configuration)
|
99
|
+
.find(Person, {
|
100
|
+
name: {$is: 'Joe Bloggs'},
|
101
|
+
address: {
|
102
|
+
$contains: {
|
103
|
+
postcode: {$contains: 'SW1'},
|
104
|
+
},
|
105
|
+
},
|
106
|
+
});
|
107
|
+
```
|
@@ -0,0 +1,23 @@
|
|
1
|
+
## Transactions
|
2
|
+
|
3
|
+
Create transactions to automatically roll back on failure.
|
4
|
+
|
5
|
+
```javascript
|
6
|
+
import Persist from "@acodeninja/persist";
|
7
|
+
import S3Engine from "@acodeninja/persist/engine/s3";
|
8
|
+
|
9
|
+
Persist.addEngine('remote', S3Engine, {
|
10
|
+
bucket: 'test-bucket',
|
11
|
+
client: new S3Client(),
|
12
|
+
transactions: true,
|
13
|
+
});
|
14
|
+
|
15
|
+
export class Tag extends Persist.Type.Model {
|
16
|
+
static tag = Persist.Type.String.required;
|
17
|
+
}
|
18
|
+
|
19
|
+
const transaction = Persist.getEngine('remote', S3Engine).start();
|
20
|
+
|
21
|
+
await transaction.put(new Tag({tag: 'documentation'}));
|
22
|
+
await transaction.commit();
|
23
|
+
```
|
package/package.json
CHANGED
package/src/Persist.js
CHANGED
package/src/engine/Engine.js
CHANGED
@@ -258,8 +258,10 @@ class Engine {
|
|
258
258
|
|
259
259
|
for (const [name, property] of Object.entries(modelToProcess)) {
|
260
260
|
if (Type.Model.isDryModel(property)) {
|
261
|
+
// skipcq: JS-0129
|
261
262
|
modelToProcess[name] = await hydrateSubModel(property, modelToProcess, name);
|
262
263
|
} else if (Array.isArray(property) && Type.Model.isDryModel(property[0])) {
|
264
|
+
// skipcq: JS-0129
|
263
265
|
modelToProcess[name] = await hydrateModelList(property, modelToProcess, name);
|
264
266
|
}
|
265
267
|
}
|
@@ -283,15 +285,15 @@ class Engine {
|
|
283
285
|
const hydrateModelList = async (property, modelToProcess, name) => {
|
284
286
|
const subModelClass = getSubModelClass(modelToProcess, name, true);
|
285
287
|
|
286
|
-
const newModelList = await Promise.all(property.map(
|
288
|
+
const newModelList = await Promise.all(property.map(subModel => {
|
287
289
|
if (hydratedModels[subModel.id]) {
|
288
290
|
return hydratedModels[subModel.id];
|
289
291
|
}
|
290
292
|
|
291
|
-
return
|
293
|
+
return this.get(subModelClass, subModel.id);
|
292
294
|
}));
|
293
295
|
|
294
|
-
return
|
296
|
+
return Promise.all(newModelList.map(async subModel => {
|
295
297
|
if (hydratedModels[subModel.id]) {
|
296
298
|
return hydratedModels[subModel.id];
|
297
299
|
}
|
@@ -312,7 +314,7 @@ class Engine {
|
|
312
314
|
return isArray ? constructorField._items : constructorField;
|
313
315
|
}
|
314
316
|
|
315
|
-
return
|
317
|
+
return hydrateModel(await this.get(model.constructor, model.id));
|
316
318
|
}
|
317
319
|
|
318
320
|
/**
|
package/src/engine/HTTPEngine.js
CHANGED
@@ -126,7 +126,7 @@ class HTTPEngine extends Engine {
|
|
126
126
|
*
|
127
127
|
* @throws {HTTPRequestFailedError} Thrown if the fetch request fails.
|
128
128
|
*/
|
129
|
-
static
|
129
|
+
static getById(id) {
|
130
130
|
this.checkConfiguration();
|
131
131
|
|
132
132
|
const url = new URL([
|
@@ -135,7 +135,7 @@ class HTTPEngine extends Engine {
|
|
135
135
|
`${id}.json`,
|
136
136
|
].filter(e => Boolean(e)).join('/'));
|
137
137
|
|
138
|
-
return
|
138
|
+
return this._processFetch(url, this._getReadOptions());
|
139
139
|
}
|
140
140
|
|
141
141
|
/**
|
@@ -177,7 +177,7 @@ class HTTPEngine extends Engine {
|
|
177
177
|
'_index.json',
|
178
178
|
].filter(e => Boolean(e)).join('/'));
|
179
179
|
|
180
|
-
return
|
180
|
+
return this._processFetch(url, {
|
181
181
|
...this._getWriteOptions(),
|
182
182
|
body: JSON.stringify({
|
183
183
|
...await this.getIndex(location),
|
@@ -199,7 +199,7 @@ class HTTPEngine extends Engine {
|
|
199
199
|
* @param {Model.constructor?} model - The model in the host where the index is stored.
|
200
200
|
* @returns {Promise<Object>} The index data in JSON format.
|
201
201
|
*/
|
202
|
-
static
|
202
|
+
static getIndex(model) {
|
203
203
|
const url = new URL([
|
204
204
|
this.configuration.host,
|
205
205
|
this.configuration.prefix,
|
@@ -207,7 +207,7 @@ class HTTPEngine extends Engine {
|
|
207
207
|
'_index.json',
|
208
208
|
].filter(e => Boolean(e)).join('/'));
|
209
209
|
|
210
|
-
return
|
210
|
+
return this._processFetch(url, this._getReadOptions(), {});
|
211
211
|
}
|
212
212
|
|
213
213
|
/**
|
@@ -216,7 +216,7 @@ class HTTPEngine extends Engine {
|
|
216
216
|
* @param {Model.constructor} model - The model whose compiled search index to retrieve.
|
217
217
|
* @returns {Promise<Object>} The compiled search index in JSON format.
|
218
218
|
*/
|
219
|
-
static
|
219
|
+
static getSearchIndexCompiled(model) {
|
220
220
|
const url = new URL([
|
221
221
|
this.configuration.host,
|
222
222
|
this.configuration.prefix,
|
@@ -224,7 +224,7 @@ class HTTPEngine extends Engine {
|
|
224
224
|
'_search_index.json',
|
225
225
|
].join('/'));
|
226
226
|
|
227
|
-
return
|
227
|
+
return this._processFetch(url, this._getReadOptions());
|
228
228
|
}
|
229
229
|
|
230
230
|
/**
|
@@ -233,7 +233,7 @@ class HTTPEngine extends Engine {
|
|
233
233
|
* @param {Model.constructor} model - The model whose raw search index to retrieve.
|
234
234
|
* @returns {Promise<Object>} The raw search index in JSON format, or an empty object if not found.
|
235
235
|
*/
|
236
|
-
static
|
236
|
+
static getSearchIndexRaw(model) {
|
237
237
|
const url = new URL([
|
238
238
|
this.configuration.host,
|
239
239
|
this.configuration.prefix,
|
@@ -241,7 +241,7 @@ class HTTPEngine extends Engine {
|
|
241
241
|
'_search_index_raw.json',
|
242
242
|
].join('/'));
|
243
243
|
|
244
|
-
return
|
244
|
+
return this._processFetch(url, this._getReadOptions()).catch(() => ({}));
|
245
245
|
}
|
246
246
|
|
247
247
|
/**
|
@@ -253,7 +253,7 @@ class HTTPEngine extends Engine {
|
|
253
253
|
*
|
254
254
|
* @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
|
255
255
|
*/
|
256
|
-
static
|
256
|
+
static putSearchIndexCompiled(model, compiledIndex) {
|
257
257
|
const url = new URL([
|
258
258
|
this.configuration.host,
|
259
259
|
this.configuration.prefix,
|
@@ -276,7 +276,7 @@ class HTTPEngine extends Engine {
|
|
276
276
|
*
|
277
277
|
* @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
|
278
278
|
*/
|
279
|
-
static
|
279
|
+
static putSearchIndexRaw(model, rawIndex) {
|
280
280
|
const url = new URL([
|
281
281
|
this.configuration.host,
|
282
282
|
this.configuration.prefix,
|
@@ -284,7 +284,7 @@ class HTTPEngine extends Engine {
|
|
284
284
|
'_search_index_raw.json',
|
285
285
|
].filter(e => Boolean(e)).join('/'));
|
286
286
|
|
287
|
-
return
|
287
|
+
return this._processFetch(url, {
|
288
288
|
...this._getWriteOptions(),
|
289
289
|
body: JSON.stringify(rawIndex),
|
290
290
|
});
|
package/src/engine/S3Engine.js
CHANGED
@@ -153,9 +153,9 @@ class S3Engine extends Engine {
|
|
153
153
|
* @param {Model.constructor} model - The model whose search index to retrieve.
|
154
154
|
* @returns {Promise<Object>} The compiled search index.
|
155
155
|
*/
|
156
|
-
static
|
157
|
-
return
|
158
|
-
Key: [this.configuration.prefix, model.
|
156
|
+
static getSearchIndexCompiled(model) {
|
157
|
+
return this.configuration.client.send(new GetObjectCommand({
|
158
|
+
Key: [this.configuration.prefix, model.toString(), '_search_index.json'].join('/'),
|
159
159
|
Bucket: this.configuration.bucket,
|
160
160
|
})).then(data => data.Body.transformToString())
|
161
161
|
.then(JSON.parse);
|
@@ -167,8 +167,8 @@ class S3Engine extends Engine {
|
|
167
167
|
* @param {Model.constructor} model - The model whose raw search index to retrieve.
|
168
168
|
* @returns {Promise<Object>} The raw search index, or an empty object if not found.
|
169
169
|
*/
|
170
|
-
static
|
171
|
-
return
|
170
|
+
static getSearchIndexRaw(model) {
|
171
|
+
return this.configuration.client.send(new GetObjectCommand({
|
172
172
|
Key: [this.configuration.prefix, model.toString(), '_search_index_raw.json'].join('/'),
|
173
173
|
Bucket: this.configuration.bucket,
|
174
174
|
})).then(data => data.Body.transformToString())
|
package/src/type/Model.js
CHANGED
@@ -40,7 +40,7 @@ class Model {
|
|
40
40
|
}
|
41
41
|
if (value?._resolved) {
|
42
42
|
Object.defineProperty(this, key, {
|
43
|
-
get
|
43
|
+
get() {
|
44
44
|
return value.resolve(this);
|
45
45
|
},
|
46
46
|
});
|
@@ -73,11 +73,11 @@ class Model {
|
|
73
73
|
(key, value) => {
|
74
74
|
if (!simple) {
|
75
75
|
if (this.constructor[key]) {
|
76
|
-
if (this.constructor[key].name.endsWith('
|
76
|
+
if (this.constructor[key].name.endsWith('Date')) {
|
77
77
|
return new Date(value);
|
78
78
|
}
|
79
79
|
|
80
|
-
if (this.constructor[key].name.endsWith('ArrayOf(Date)
|
80
|
+
if (this.constructor[key].name.endsWith('ArrayOf(Date)')) {
|
81
81
|
return value.map(d => new Date(d));
|
82
82
|
}
|
83
83
|
}
|
@@ -211,12 +211,12 @@ class Model {
|
|
211
211
|
for (const [name, value] of Object.entries(data)) {
|
212
212
|
if (this[name]?._resolved) continue;
|
213
213
|
|
214
|
-
if (this[name].name.endsWith('
|
214
|
+
if (this[name].name.endsWith('Date')) {
|
215
215
|
model[name] = new Date(value);
|
216
216
|
continue;
|
217
217
|
}
|
218
218
|
|
219
|
-
if (this[name].name.endsWith('ArrayOf(Date)
|
219
|
+
if (this[name].name.endsWith('ArrayOf(Date)')) {
|
220
220
|
model[name] = data[name].map(d => new Date(d));
|
221
221
|
continue;
|
222
222
|
}
|
@@ -259,6 +259,26 @@ class Model {
|
|
259
259
|
return false;
|
260
260
|
}
|
261
261
|
}
|
262
|
+
|
263
|
+
/**
|
264
|
+
* Set the name of the Model class
|
265
|
+
*
|
266
|
+
* Use this when your model might be minified to retain consistent class names.
|
267
|
+
*
|
268
|
+
* @param {string} name
|
269
|
+
* @static
|
270
|
+
*
|
271
|
+
* @example
|
272
|
+
* export default class TestModel {
|
273
|
+
* static {
|
274
|
+
* this.string = Persist.Type.String;
|
275
|
+
* Object.defineProperty(this, 'name', {value: 'TestModel'});
|
276
|
+
* }
|
277
|
+
* }
|
278
|
+
*/
|
279
|
+
static setMinifiedName(name) {
|
280
|
+
Object.defineProperty(this, 'name', {value: name});
|
281
|
+
}
|
262
282
|
}
|
263
283
|
|
264
284
|
export default Model;
|
package/src/type/Type.js
CHANGED
@@ -38,12 +38,12 @@ class Type {
|
|
38
38
|
static _schema = undefined;
|
39
39
|
|
40
40
|
/**
|
41
|
-
* Converts the class name to a string
|
41
|
+
* Converts the class name to a string
|
42
42
|
*
|
43
|
-
* @returns {string} The name of the type
|
43
|
+
* @returns {string} The name of the type.
|
44
44
|
*/
|
45
45
|
static toString() {
|
46
|
-
return this.name
|
46
|
+
return this.name;
|
47
47
|
}
|
48
48
|
|
49
49
|
/**
|
@@ -58,10 +58,14 @@ class Type {
|
|
58
58
|
}
|
59
59
|
|
60
60
|
// Define the class name as "Required<OriginalTypeName>"
|
61
|
-
Object.defineProperty(Required, 'name', {value: `Required${this.toString()}
|
61
|
+
Object.defineProperty(Required, 'name', {value: `Required${this.toString()}`});
|
62
62
|
|
63
63
|
return Required;
|
64
64
|
}
|
65
|
+
|
66
|
+
static {
|
67
|
+
Object.defineProperty(this, 'name', {value: 'Type'});
|
68
|
+
}
|
65
69
|
}
|
66
70
|
|
67
71
|
export default Type;
|
@@ -66,17 +66,17 @@ class ArrayType {
|
|
66
66
|
* @returns {string} The string representation of the required array type.
|
67
67
|
*/
|
68
68
|
static toString() {
|
69
|
-
return `RequiredArrayOf(${type})`;
|
69
|
+
return `RequiredArrayOf(${type.toString()})`;
|
70
70
|
}
|
71
71
|
}
|
72
72
|
|
73
|
-
Object.defineProperty(Required, 'name', {value: `Required${this.toString()}
|
73
|
+
Object.defineProperty(Required, 'name', {value: `Required${this.toString()}`});
|
74
74
|
|
75
75
|
return Required;
|
76
76
|
}
|
77
77
|
}
|
78
78
|
|
79
|
-
Object.defineProperty(ArrayOf, 'name', {value:
|
79
|
+
Object.defineProperty(ArrayOf, 'name', {value: ArrayOf.toString()});
|
80
80
|
|
81
81
|
return ArrayOf;
|
82
82
|
}
|
@@ -35,15 +35,23 @@ class CustomType {
|
|
35
35
|
* Represents a custom type defined by a JSON schema.
|
36
36
|
*/
|
37
37
|
class Custom extends Type {
|
38
|
-
|
39
|
-
|
38
|
+
static {
|
39
|
+
/** @type {string} The data type, which is 'object' */
|
40
|
+
this._type = 'object';
|
40
41
|
|
41
|
-
|
42
|
-
|
42
|
+
/** @type {Object} The JSON schema that defines the structure and validation rules */
|
43
|
+
this._schema = schema;
|
44
|
+
|
45
|
+
Object.defineProperty(this, 'name', {value: 'Custom'});
|
46
|
+
}
|
43
47
|
}
|
44
48
|
|
45
49
|
return Custom;
|
46
50
|
}
|
51
|
+
|
52
|
+
static {
|
53
|
+
Object.defineProperty(this, 'name', {value: 'Custom'});
|
54
|
+
}
|
47
55
|
}
|
48
56
|
|
49
57
|
export default CustomType;
|
@@ -53,12 +53,20 @@ class SlugType extends ResolvedType {
|
|
53
53
|
|
54
54
|
return slugify(model?.[property], {
|
55
55
|
lower: true,
|
56
|
+
strict: true,
|
57
|
+
trim: true,
|
56
58
|
});
|
57
59
|
}
|
58
60
|
}
|
59
61
|
|
62
|
+
Object.defineProperty(SlugOf, 'name', {value: `SlugOf(${property})`});
|
63
|
+
|
60
64
|
return SlugOf;
|
61
65
|
}
|
66
|
+
|
67
|
+
static {
|
68
|
+
Object.defineProperty(this, 'name', {value: 'Slug'});
|
69
|
+
}
|
62
70
|
}
|
63
71
|
|
64
72
|
export default SlugType;
|
@@ -10,11 +10,15 @@ import SimpleType from './SimpleType.js';
|
|
10
10
|
* @extends SimpleType
|
11
11
|
*/
|
12
12
|
class BooleanType extends SimpleType {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
static {
|
14
|
+
/**
|
15
|
+
* @static
|
16
|
+
* @property {string} _type - The type identifier for BooleanType, set to `'boolean'`.
|
17
|
+
*/
|
18
|
+
this._type = 'boolean';
|
19
|
+
|
20
|
+
Object.defineProperty(this, 'name', {value: 'Boolean'});
|
21
|
+
}
|
18
22
|
}
|
19
23
|
|
20
24
|
export default BooleanType;
|
@@ -10,17 +10,21 @@ import SimpleType from './SimpleType.js';
|
|
10
10
|
* @extends SimpleType
|
11
11
|
*/
|
12
12
|
class DateType extends SimpleType {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
static {
|
14
|
+
/**
|
15
|
+
* @static
|
16
|
+
* @property {string} _type - The type identifier for DateType, set to `'string'`.
|
17
|
+
*/
|
18
|
+
this._type = 'string';
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
/**
|
21
|
+
* @static
|
22
|
+
* @property {string} _format - The format for DateType, set to `'iso-date-time'`.
|
23
|
+
*/
|
24
|
+
this._format = 'iso-date-time';
|
25
|
+
|
26
|
+
Object.defineProperty(this, 'name', {value: 'Date'});
|
27
|
+
}
|
24
28
|
|
25
29
|
/**
|
26
30
|
* Checks if the given value is a valid date.
|
@@ -10,11 +10,15 @@ import SimpleType from './SimpleType.js';
|
|
10
10
|
* @extends SimpleType
|
11
11
|
*/
|
12
12
|
class NumberType extends SimpleType {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
static {
|
14
|
+
/**
|
15
|
+
* @static
|
16
|
+
* @property {string} _type - The type identifier for NumberType, set to `'number'`.
|
17
|
+
*/
|
18
|
+
this._type = 'number';
|
19
|
+
|
20
|
+
Object.defineProperty(this, 'name', {value: 'Number'});
|
21
|
+
}
|
18
22
|
}
|
19
23
|
|
20
24
|
export default NumberType;
|