@acodeninja/persist 3.0.0-next.1 → 3.0.0-next.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/jest.config.cjs +26 -0
- package/package.json +7 -10
- package/src/Query.js +2 -2
- package/src/engine/StorageEngine.js +250 -0
- package/src/engine/storage/FileStorageEngine.js +7 -0
- package/src/engine/storage/HTTPStorageEngine.js +6 -0
- package/src/engine/storage/S3StorageEngine.js +7 -0
- package/src/engine/storage/StorageEngine.js +35 -0
- package/src/type/simple/BooleanType.js +4 -4
- package/src/type/simple/DateType.js +4 -4
- package/src/type/simple/NumberType.js +4 -4
- package/src/type/simple/StringType.js +4 -4
- package/src/type/simple/SimpleType.js +0 -14
package/jest.config.cjs
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
/** @type {import('jest').Config} */
|
2
|
+
const config = {
|
3
|
+
coveragePathIgnorePatterns: [
|
4
|
+
'node_modules',
|
5
|
+
'test/fixtures',
|
6
|
+
'test/mocks',
|
7
|
+
'test/scripts',
|
8
|
+
],
|
9
|
+
coverageThreshold: {
|
10
|
+
global: {
|
11
|
+
branches: 100,
|
12
|
+
functions: 100,
|
13
|
+
lines: 100,
|
14
|
+
statements: 100,
|
15
|
+
},
|
16
|
+
},
|
17
|
+
testMatch: [
|
18
|
+
'**/*.test.js',
|
19
|
+
],
|
20
|
+
watchPathIgnorePatterns: [
|
21
|
+
'coverage/',
|
22
|
+
'test/fixtures/minified',
|
23
|
+
],
|
24
|
+
};
|
25
|
+
|
26
|
+
module.exports = config;
|
package/package.json
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "@acodeninja/persist",
|
3
|
-
"version": "3.0.0-next.
|
3
|
+
"version": "3.0.0-next.2",
|
4
4
|
"description": "A JSON based data modelling and persistence module with alternate storage mechanisms.",
|
5
5
|
"type": "module",
|
6
6
|
"scripts": {
|
7
|
-
"test": "
|
8
|
-
"test:watch": "
|
9
|
-
"test:coverage": "
|
10
|
-
"test:coverage:report": "c8 --experimental-monocart --100 --lcov --reporter=console-details --reporter=v8 ava",
|
7
|
+
"test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest",
|
8
|
+
"test:watch": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest --watch",
|
9
|
+
"test:coverage": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest --collect-coverage",
|
11
10
|
"lint": "eslint ./",
|
12
11
|
"prepare": "husky"
|
13
12
|
},
|
@@ -38,14 +37,12 @@
|
|
38
37
|
"@commitlint/cli": "^19.6.1",
|
39
38
|
"@commitlint/config-conventional": "^19.6.0",
|
40
39
|
"@eslint/js": "^9.19.0",
|
40
|
+
"@jest/globals": "^29.7.0",
|
41
41
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
42
|
-
"ava": "^6.2.0",
|
43
|
-
"c8": "^10.1.3",
|
44
42
|
"eslint": "^9.19.0",
|
45
43
|
"globals": "^15.14.0",
|
46
44
|
"husky": "^9.1.7",
|
47
|
-
"
|
48
|
-
"semantic-release": "^24.2.1"
|
49
|
-
"sinon": "^19.0.2"
|
45
|
+
"jest": "^29.7.0",
|
46
|
+
"semantic-release": "^24.2.1"
|
50
47
|
}
|
51
48
|
}
|
package/src/Query.js
CHANGED
@@ -69,10 +69,10 @@ class Query {
|
|
69
69
|
*
|
70
70
|
* @private
|
71
71
|
* @param {*} subject - The subject to be matched.
|
72
|
-
* @param {Object}
|
72
|
+
* @param {Object} inputQuery - The query to match against.
|
73
73
|
* @returns {boolean} True if the subject matches the query, otherwise false.
|
74
74
|
*/
|
75
|
-
_matchesQuery(subject, inputQuery
|
75
|
+
_matchesQuery(subject, inputQuery) {
|
76
76
|
if (['string', 'number', 'boolean'].includes(typeof inputQuery)) return subject === inputQuery;
|
77
77
|
|
78
78
|
if (inputQuery?.$is !== undefined && subject === inputQuery.$is) return true;
|
@@ -0,0 +1,250 @@
|
|
1
|
+
import Type from '../type/index.js';
|
2
|
+
import _ from 'lodash';
|
3
|
+
|
4
|
+
export default class StorageEngine {
|
5
|
+
/**
|
6
|
+
* @param {Object} configuration
|
7
|
+
* @param {Array<Type.Model.constructor>?} models
|
8
|
+
*/
|
9
|
+
constructor(configuration = {}, models = null) {
|
10
|
+
this.configuration = configuration;
|
11
|
+
this.models = Object.fromEntries((models ?? []).map(model => [model.name, model]));
|
12
|
+
}
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Persists a model if it has changed, and updates all related models and their indexes
|
16
|
+
* @param {Type.Model} model
|
17
|
+
* @return {Promise<void>}
|
18
|
+
*/
|
19
|
+
async put(model) {
|
20
|
+
const processedModels = [];
|
21
|
+
const modelsToPut = [];
|
22
|
+
const modelsToReindex = {};
|
23
|
+
|
24
|
+
/**
|
25
|
+
* @param {Type.Model} modelToProcess
|
26
|
+
* @return {Promise<void>}
|
27
|
+
*/
|
28
|
+
const processModel = async (modelToProcess) => {
|
29
|
+
if (processedModels.includes(modelToProcess.id)) return;
|
30
|
+
|
31
|
+
processedModels.push(modelToProcess.id);
|
32
|
+
|
33
|
+
if (!Object.keys(this.models).includes(modelToProcess.constructor.name)) throw new ModelNotRegisteredStorageEngineError(modelToProcess, this);
|
34
|
+
|
35
|
+
modelToProcess.validate();
|
36
|
+
const currentModel = await this.get(modelToProcess.id).catch(() => null);
|
37
|
+
|
38
|
+
const modelToProcessHasChanged = (JSON.stringify(currentModel?.toData() || {}) !== JSON.stringify(modelToProcess.toData()));
|
39
|
+
|
40
|
+
if (modelToProcessHasChanged) modelsToPut.push(modelToProcess);
|
41
|
+
|
42
|
+
if (
|
43
|
+
Boolean(modelToProcess.constructor.indexedProperties().length) &&
|
44
|
+
this._indexedFieldsHaveChanged(currentModel, modelToProcess)
|
45
|
+
) {
|
46
|
+
const modelToProcessConstructor = this.getModelConstructorFromId(modelToProcess.id);
|
47
|
+
modelsToReindex[modelToProcessConstructor] = modelsToReindex[modelToProcessConstructor] || [];
|
48
|
+
modelsToReindex[modelToProcessConstructor].push(modelToProcess);
|
49
|
+
}
|
50
|
+
|
51
|
+
for (const [field, value] of Object.entries(modelToProcess)) {
|
52
|
+
if (Type.Model.isModel(value)) {
|
53
|
+
await processModel(modelToProcess[field]);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
await processModel(model);
|
59
|
+
|
60
|
+
await Promise.all(modelsToPut.map(m => this._putModel(m.toData())));
|
61
|
+
await Promise.all(Object.entries(modelsToReindex).map(async ([constructor, models]) => {
|
62
|
+
const modelConstructor = this.models[constructor];
|
63
|
+
const index = await this._getIndex(modelConstructor);
|
64
|
+
|
65
|
+
await this._putIndex(modelConstructor, {
|
66
|
+
...index || {},
|
67
|
+
...Object.fromEntries(models.map(m => [m.id, m.toIndexData()])),
|
68
|
+
});
|
69
|
+
}));
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Decide if two models indexable fields have changed
|
74
|
+
* @param {Type.Model} currentModel
|
75
|
+
* @param {Type.Model} modelToProcess
|
76
|
+
* @return {boolean}
|
77
|
+
* @private
|
78
|
+
*/
|
79
|
+
_indexedFieldsHaveChanged(currentModel, modelToProcess) {
|
80
|
+
return !currentModel || Boolean(
|
81
|
+
modelToProcess.constructor.indexedProperties()
|
82
|
+
.filter(field => JSON.stringify(_.get(currentModel, field)) !== JSON.stringify(_.get(modelToProcess, field)))
|
83
|
+
.length,
|
84
|
+
);
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Get a model by its id
|
89
|
+
* @param {string} modelId
|
90
|
+
* @return {Promise<void>}
|
91
|
+
*/
|
92
|
+
get(modelId) {
|
93
|
+
try {
|
94
|
+
this.getModelConstructorFromId(modelId);
|
95
|
+
} catch (e) {
|
96
|
+
return Promise.reject(e);
|
97
|
+
}
|
98
|
+
return this._getModel(modelId);
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Get the model constructor from a model id
|
103
|
+
* @param {string} modelId
|
104
|
+
* @return {Model.constructor}
|
105
|
+
*/
|
106
|
+
getModelConstructorFromId(modelId) {
|
107
|
+
const modelName = modelId.split('/')[0];
|
108
|
+
const constructor = this.models[modelName];
|
109
|
+
|
110
|
+
if (!constructor) throw new ModelNotRegisteredStorageEngineError(modelName, this);
|
111
|
+
|
112
|
+
return constructor;
|
113
|
+
}
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Get model classes that are directly linked to the given model in either direction
|
117
|
+
* @param {Type.Model.constructor} model
|
118
|
+
* @return {Record<string, Record<string, Type.Model.constructor>>}
|
119
|
+
*/
|
120
|
+
getLinksFor(model) {
|
121
|
+
return Object.fromEntries(
|
122
|
+
Object.entries(this.getAllModelLinks())
|
123
|
+
.filter(([modelName, links]) =>
|
124
|
+
model.name === modelName ||
|
125
|
+
Object.values(links).some((link) => link.name === model.name),
|
126
|
+
),
|
127
|
+
);
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Get all model links
|
132
|
+
* @return {Record<string, Record<string, Type.Model.constructor>>}
|
133
|
+
*/
|
134
|
+
getAllModelLinks() {
|
135
|
+
return Object.entries(this.models)
|
136
|
+
.map(([registeredModelName, registeredModelClass]) =>
|
137
|
+
Object.entries(registeredModelClass)
|
138
|
+
.map(([propertyName, propertyType]) => [
|
139
|
+
registeredModelName,
|
140
|
+
propertyName,
|
141
|
+
typeof propertyType === 'function' && !Type.Model.isModel(propertyType) ? propertyType() : propertyType,
|
142
|
+
])
|
143
|
+
.filter(([_m, _p, type]) => Type.Model.isModel(type))
|
144
|
+
.map(([containingModel, propertyName, propertyType]) => ({
|
145
|
+
containingModel,
|
146
|
+
propertyName,
|
147
|
+
propertyType,
|
148
|
+
})),
|
149
|
+
)
|
150
|
+
.flat()
|
151
|
+
.reduce((accumulator, {containingModel, propertyName, propertyType}) => ({
|
152
|
+
...accumulator,
|
153
|
+
[containingModel]: {
|
154
|
+
...accumulator[containingModel] || {},
|
155
|
+
[propertyName]: propertyType,
|
156
|
+
},
|
157
|
+
}), {});
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Update a model
|
162
|
+
* @param {Model} _model
|
163
|
+
* @throws MethodNotImplementedStorageEngineError
|
164
|
+
* @return Promise<void>
|
165
|
+
*/
|
166
|
+
_putModel(_model) {
|
167
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_putModel', this));
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* Get a model
|
172
|
+
* @param {string} _id
|
173
|
+
* @throws MethodNotImplementedStorageEngineError
|
174
|
+
* @throws ModelNotFoundStorageEngineError
|
175
|
+
* @return Promise<void>
|
176
|
+
*/
|
177
|
+
_getModel(_id) {
|
178
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_getModel', this));
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Get a model's index data
|
183
|
+
* @param {Model.constructor} _modelConstructor
|
184
|
+
* @throws MethodNotImplementedStorageEngineError
|
185
|
+
* @return Promise<void>
|
186
|
+
*/
|
187
|
+
_getIndex(_modelConstructor) {
|
188
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_getIndex', this));
|
189
|
+
}
|
190
|
+
|
191
|
+
/**
|
192
|
+
* Put a model's index data
|
193
|
+
* @param {Model.constructor} _modelConstructor
|
194
|
+
* @param {object} _data
|
195
|
+
* @throws MethodNotImplementedStorageEngineError
|
196
|
+
* @return Promise<void>
|
197
|
+
*/
|
198
|
+
_putIndex(_modelConstructor, _data) {
|
199
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_putIndex', this));
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
/**
|
204
|
+
* @class StorageEngineError
|
205
|
+
* @extends Error
|
206
|
+
*/
|
207
|
+
export class StorageEngineError extends Error {
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
* @class ModelNotRegisteredStorageEngineError
|
212
|
+
* @extends StorageEngineError
|
213
|
+
*/
|
214
|
+
export class ModelNotRegisteredStorageEngineError extends StorageEngineError {
|
215
|
+
/**
|
216
|
+
* @param {Type.Model} model
|
217
|
+
* @param {StorageEngine} storageEngine
|
218
|
+
*/
|
219
|
+
constructor(model, storageEngine) {
|
220
|
+
const modelName = typeof model === 'string' ? model : model.constructor.name;
|
221
|
+
super(`The model ${modelName} is not registered in the storage engine ${storageEngine.constructor.name}`);
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
/**
|
226
|
+
* @class MethodNotImplementedStorageEngineError
|
227
|
+
* @extends StorageEngineError
|
228
|
+
*/
|
229
|
+
export class MethodNotImplementedStorageEngineError extends StorageEngineError {
|
230
|
+
/**
|
231
|
+
* @param {string} method
|
232
|
+
* @param {StorageEngine} storageEngine
|
233
|
+
*/
|
234
|
+
constructor(method, storageEngine) {
|
235
|
+
super(`The method ${method} is not implemented in the storage engine ${storageEngine.constructor.name}`);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
/**
|
240
|
+
* @class ModelNotFoundStorageEngineError
|
241
|
+
* @extends StorageEngineError
|
242
|
+
*/
|
243
|
+
export class ModelNotFoundStorageEngineError extends StorageEngineError {
|
244
|
+
/**
|
245
|
+
* @param {string} modelId
|
246
|
+
*/
|
247
|
+
constructor(modelId) {
|
248
|
+
super(`The model ${modelId} was not found`);
|
249
|
+
}
|
250
|
+
}
|
@@ -113,6 +113,13 @@ class FileStorageEngine extends StorageEngine {
|
|
113
113
|
* @throws {FailedWriteFileStorageEngineError} Throws if the index cannot be written to the file system.
|
114
114
|
*/
|
115
115
|
static async putIndex(index) {
|
116
|
+
/**
|
117
|
+
* Process an index of models
|
118
|
+
* @param {string} location
|
119
|
+
* @param {Array<Model>} models
|
120
|
+
* @throws FailedWriteFileStorageEngineError
|
121
|
+
* @return {Promise<void>}
|
122
|
+
*/
|
116
123
|
const processIndex = async (location, models) => {
|
117
124
|
const filePath = join(this.configuration.path, location, '_index.json');
|
118
125
|
const currentIndex = JSON.parse((await this.configuration.filesystem.readFile(filePath).catch(() => '{}')).toString());
|
@@ -186,6 +186,12 @@ class HTTPStorageEngine extends StorageEngine {
|
|
186
186
|
* @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
|
187
187
|
*/
|
188
188
|
static async putIndex(index) {
|
189
|
+
/**
|
190
|
+
* Process an index of models
|
191
|
+
* @param {string} location
|
192
|
+
* @param {Array<Model>} models
|
193
|
+
* @return {Promise<void>}
|
194
|
+
*/
|
189
195
|
const processIndex = async (location, models) => {
|
190
196
|
const url = new URL([
|
191
197
|
this.configuration.host,
|
@@ -136,6 +136,13 @@ class S3StorageEngine extends StorageEngine {
|
|
136
136
|
* @throws {FailedPutS3StorageEngineError} Thrown if there is an error during the S3 PutObject operation.
|
137
137
|
*/
|
138
138
|
static async putIndex(index) {
|
139
|
+
/**
|
140
|
+
* Process an index of models
|
141
|
+
* @param {string} location
|
142
|
+
* @param {Array<Model>} models
|
143
|
+
* @throws FailedPutS3StorageEngineError
|
144
|
+
* @return {Promise<void>}
|
145
|
+
*/
|
139
146
|
const processIndex = async (location, models) => {
|
140
147
|
const Key = [this.configuration.prefix, location, '_index.json'].filter(e => Boolean(e)).join('/');
|
141
148
|
const currentIndex = await this.getIndex(location);
|
@@ -186,6 +186,11 @@ class StorageEngine {
|
|
186
186
|
const uploadedModels = [];
|
187
187
|
const indexUpdates = {};
|
188
188
|
|
189
|
+
/**
|
190
|
+
* Process a model, putting updates to the model and all linked models.
|
191
|
+
* @param {Model} m
|
192
|
+
* @return {Promise<void>}
|
193
|
+
*/
|
189
194
|
const processModel = async (m) => {
|
190
195
|
if (!uploadedModels.includes(m.id)) {
|
191
196
|
m.validate();
|
@@ -389,6 +394,11 @@ class StorageEngine {
|
|
389
394
|
this.checkConfiguration();
|
390
395
|
const hydratedModels = {};
|
391
396
|
|
397
|
+
/**
|
398
|
+
* Hydrate a model
|
399
|
+
* @param {Model} modelToProcess
|
400
|
+
* @return {Promise<Model>}
|
401
|
+
*/
|
392
402
|
const hydrateModel = async (modelToProcess) => {
|
393
403
|
hydratedModels[modelToProcess.id] = modelToProcess;
|
394
404
|
|
@@ -405,6 +415,13 @@ class StorageEngine {
|
|
405
415
|
return modelToProcess;
|
406
416
|
};
|
407
417
|
|
418
|
+
/**
|
419
|
+
* Hydrate a dry sub model
|
420
|
+
* @param property
|
421
|
+
* @param modelToProcess
|
422
|
+
* @param name
|
423
|
+
* @return {Promise<Model>}
|
424
|
+
*/
|
408
425
|
const hydrateSubModel = async (property, modelToProcess, name) => {
|
409
426
|
if (hydratedModels[property.id]) {
|
410
427
|
return hydratedModels[property.id];
|
@@ -418,6 +435,13 @@ class StorageEngine {
|
|
418
435
|
return hydratedSubModel;
|
419
436
|
};
|
420
437
|
|
438
|
+
/**
|
439
|
+
* Hydrate an array of dry models
|
440
|
+
* @param property
|
441
|
+
* @param modelToProcess
|
442
|
+
* @param name
|
443
|
+
* @return {Promise<Awaited<*>[]>}
|
444
|
+
*/
|
421
445
|
const hydrateModelList = async (property, modelToProcess, name) => {
|
422
446
|
const subModelClass = getSubModelClass(modelToProcess, name, true);
|
423
447
|
|
@@ -440,6 +464,13 @@ class StorageEngine {
|
|
440
464
|
}));
|
441
465
|
};
|
442
466
|
|
467
|
+
/**
|
468
|
+
* Get the class of a sub model
|
469
|
+
* @param modelToProcess
|
470
|
+
* @param name
|
471
|
+
* @param isArray
|
472
|
+
* @return {Model.constructor|Type}
|
473
|
+
*/
|
443
474
|
function getSubModelClass(modelToProcess, name, isArray = false) {
|
444
475
|
const constructorField = modelToProcess.constructor[name];
|
445
476
|
|
@@ -460,6 +491,10 @@ class StorageEngine {
|
|
460
491
|
* @returns {StorageEngine} A new engine instance with the applied configuration.
|
461
492
|
*/
|
462
493
|
static configure(configuration) {
|
494
|
+
/**
|
495
|
+
* @class ConfiguredStore
|
496
|
+
* @extends StorageEngine
|
497
|
+
*/
|
463
498
|
class ConfiguredStore extends this {
|
464
499
|
static configuration = configuration;
|
465
500
|
}
|
@@ -1,15 +1,15 @@
|
|
1
|
-
import
|
1
|
+
import Type from '../Type.js';
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Class representing a boolean type.
|
5
5
|
*
|
6
6
|
* This class is used to define and handle data of the boolean type.
|
7
|
-
* It extends the {@link
|
7
|
+
* It extends the {@link Type} class to represent string-specific behavior.
|
8
8
|
*
|
9
9
|
* @class BooleanType
|
10
|
-
* @extends
|
10
|
+
* @extends Type
|
11
11
|
*/
|
12
|
-
class BooleanType extends
|
12
|
+
class BooleanType extends Type {
|
13
13
|
static {
|
14
14
|
/**
|
15
15
|
* @static
|
@@ -1,15 +1,15 @@
|
|
1
|
-
import
|
1
|
+
import Type from '../Type.js';
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Class representing a date type with ISO date-time format.
|
5
5
|
*
|
6
6
|
* This class is used to define and handle data of the date type.
|
7
|
-
* It extends the {@link
|
7
|
+
* It extends the {@link Type} class to represent string-specific behavior.
|
8
8
|
*
|
9
9
|
* @class DateType
|
10
|
-
* @extends
|
10
|
+
* @extends Type
|
11
11
|
*/
|
12
|
-
class DateType extends
|
12
|
+
class DateType extends Type {
|
13
13
|
static {
|
14
14
|
/**
|
15
15
|
* @static
|
@@ -1,15 +1,15 @@
|
|
1
|
-
import
|
1
|
+
import Type from '../Type.js';
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Class representing a number type.
|
5
5
|
*
|
6
6
|
* This class is used to define and handle data of the number type.
|
7
|
-
* It extends the {@link
|
7
|
+
* It extends the {@link Type} class to represent string-specific behavior.
|
8
8
|
*
|
9
9
|
* @class NumberType
|
10
|
-
* @extends
|
10
|
+
* @extends Type
|
11
11
|
*/
|
12
|
-
class NumberType extends
|
12
|
+
class NumberType extends Type {
|
13
13
|
static {
|
14
14
|
/**
|
15
15
|
* @static
|
@@ -1,15 +1,15 @@
|
|
1
|
-
import
|
1
|
+
import Type from '../Type.js';
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Class representing a string type.
|
5
5
|
*
|
6
6
|
* This class is used to define and handle data of the string type.
|
7
|
-
* It extends the {@link
|
7
|
+
* It extends the {@link Type} class to represent string-specific behavior.
|
8
8
|
*
|
9
9
|
* @class StringType
|
10
|
-
* @extends
|
10
|
+
* @extends Type
|
11
11
|
*/
|
12
|
-
class StringType extends
|
12
|
+
class StringType extends Type {
|
13
13
|
static {
|
14
14
|
/**
|
15
15
|
* @static
|
@@ -1,14 +0,0 @@
|
|
1
|
-
import Type from '../Type.js';
|
2
|
-
|
3
|
-
/**
|
4
|
-
* Class representing a simple type.
|
5
|
-
*
|
6
|
-
* This serves as a base class for primitive or simple types such as string, number, or boolean.
|
7
|
-
*
|
8
|
-
* @class SimpleType
|
9
|
-
* @extends Type
|
10
|
-
*/
|
11
|
-
class SimpleType extends Type {
|
12
|
-
}
|
13
|
-
|
14
|
-
export default SimpleType;
|