@makano/rew 1.5.5 → 1.5.8
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/lib/rew/const/default.js +4 -1
- package/lib/rew/functions/core.js +21 -2
- package/lib/rew/functions/curl.js +1 -1
- package/lib/rew/functions/import.js +4 -1
- package/lib/rew/functions/json.js +6 -2
- package/lib/rew/modules/compiler.js +3 -724
- package/lib/rew/modules/compiler.raw.js +749 -0
- package/lib/rew/modules/context.js +2 -1
- package/lib/rew/modules/yaml.js +9 -2
- package/lib/rew/pkgs/alpha.js +18 -0
- package/lib/rew/pkgs/conf.js +20 -3
- package/lib/rew/pkgs/modules/rune/db.js +9 -0
- package/lib/rew/pkgs/rune.js +179 -45
- package/lib/rew/pkgs/rune.schema.js +94 -0
- package/package.json +1 -1
@@ -177,6 +177,7 @@ module.exports.prepareContext = function (
|
|
177
177
|
get prototype(){
|
178
178
|
const globals = context.app?.config?.assets?.globals ?? '_';
|
179
179
|
const p = context.pjoin(context.app ? context.app.path : context.dirname(context.module.filename), globals);
|
180
|
+
// console.log(context.app);
|
180
181
|
if(!context.exists(p)) return {};
|
181
182
|
const files = context.ls(p);
|
182
183
|
const pr = {};
|
@@ -216,7 +217,7 @@ module.exports.prepareContext = function (
|
|
216
217
|
try {
|
217
218
|
if (package.startsWith("node:") || package.startsWith("pkg:"))
|
218
219
|
throw new Error("");
|
219
|
-
return context.imp(package, asserts);
|
220
|
+
return context.imp(package, asserts, true);
|
220
221
|
} catch (e) {
|
221
222
|
if(e.message.match('Module') && e.message.match('not found')){
|
222
223
|
let pname = package.startsWith("pkg:") ? package.split("pkg:")[1] : package;
|
package/lib/rew/modules/yaml.js
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
const yaml = require('js-yaml');
|
2
2
|
const path = require('path');
|
3
3
|
const { getFile } = require('./fs');
|
4
|
+
const { withOut } = require('../functions/core');
|
4
5
|
|
5
|
-
function yamlFile(file) {
|
6
|
+
function yamlFile(file, schemaProps) {
|
6
7
|
const schema = new yaml.Schema([
|
7
8
|
new yaml.Type('!import', {
|
8
9
|
kind: 'scalar',
|
@@ -20,11 +21,17 @@ function yamlFile(file) {
|
|
20
21
|
kind: 'scalar',
|
21
22
|
construct: (data) => (data == 'true' ? true : false),
|
22
23
|
}),
|
23
|
-
]
|
24
|
+
].concat(
|
25
|
+
(schemaProps || []).map((item) => new yaml.Type(item.key, {
|
26
|
+
...withOut(item, 'key')
|
27
|
+
}))
|
28
|
+
));
|
24
29
|
|
25
30
|
return file.content.startsWith('---') ? yaml.loadAll(file.content, { schema })[0] : yaml.load(file.content, { schema });
|
26
31
|
}
|
27
32
|
|
33
|
+
module.exports.yamlFile = yamlFile;
|
34
|
+
|
28
35
|
const importYaml = (module.exports.importYaml = function importYaml(filepath, file) {
|
29
36
|
if (!file) {
|
30
37
|
file = getFile(filepath);
|
@@ -0,0 +1,18 @@
|
|
1
|
+
const { pickRandom } = require("../const/default")
|
2
|
+
|
3
|
+
const alphabets = 'abcdefghijklmnopqrstuvwxyz';
|
4
|
+
const numericals = '0123456789';
|
5
|
+
|
6
|
+
module.exports = () => ({
|
7
|
+
alphabets,
|
8
|
+
numericals,
|
9
|
+
rn(length = 1){
|
10
|
+
return Array(length || 1).fill(0).map(() => pickRandom(...numericals.split(''))).join('');
|
11
|
+
},
|
12
|
+
rl(length = 1){
|
13
|
+
return Array(length || 1).fill(0).map(() => pickRandom(...alphabets.split(''))).join('');
|
14
|
+
},
|
15
|
+
rnl(length = 1){
|
16
|
+
return Array(length || 1).fill(0).map(() => pickRandom(...(alphabets + numericals).split(''))).join('');
|
17
|
+
}
|
18
|
+
})
|
package/lib/rew/pkgs/conf.js
CHANGED
@@ -3,6 +3,7 @@ const jsYaml = require('js-yaml');
|
|
3
3
|
const path = require('path');
|
4
4
|
const { CONFIG_PATH } = require('../const/config_path');
|
5
5
|
const { seededID } = require('../misc/seededid');
|
6
|
+
const { Usage } = require('../const/usage');
|
6
7
|
|
7
8
|
const createPackageRoot = (packageName) => {
|
8
9
|
const rootPath = path.join(CONFIG_PATH, packageName);
|
@@ -30,18 +31,22 @@ module.exports = (context) => ({
|
|
30
31
|
};
|
31
32
|
|
32
33
|
const setData = (optionCenter, key, value) => {
|
34
|
+
if(conf[optionCenter.name] === undefined) conf[optionCenter.name] = {};
|
35
|
+
// console.log(conf, optionCenter.name, key, value);
|
33
36
|
conf[optionCenter.name][key] = value;
|
34
37
|
fs.writeFileSync(optionCenter.root, dumpYaml(conf[optionCenter.name]));
|
35
38
|
return true;
|
36
39
|
};
|
37
40
|
|
38
41
|
const removeData = (optionCenter, key) => {
|
42
|
+
if(conf[optionCenter.name] === undefined) conf[optionCenter.name] = {};
|
39
43
|
delete conf[optionCenter.name][key];
|
40
44
|
fs.writeFileSync(optionCenter.root, dumpYaml(conf[optionCenter.name]));
|
41
45
|
return true;
|
42
46
|
};
|
43
47
|
|
44
48
|
const getData = (optionCenter, key) => {
|
49
|
+
if(conf[optionCenter.name] === undefined) conf[optionCenter.name] = {};
|
45
50
|
return conf[optionCenter.name][key];
|
46
51
|
};
|
47
52
|
|
@@ -88,7 +93,7 @@ module.exports = (context) => ({
|
|
88
93
|
get: (key, defaultValue) => getData(optionCenter, key) ?? defaultValue,
|
89
94
|
set: (key, value) => setData(optionCenter, key, value),
|
90
95
|
remove: (key) => removeData(optionCenter, key),
|
91
|
-
reset: () => fs.writeFileSync(optionCenter.root, dumpYaml(defaults))
|
96
|
+
reset: () => { fs.writeFileSync(optionCenter.root, dumpYaml(defaults)); conf[name] = defaults;},
|
92
97
|
getAll: (str = false) => (str ? dumpYaml(conf[name]) : conf[name]),
|
93
98
|
...optionCenter,
|
94
99
|
};
|
@@ -96,7 +101,7 @@ module.exports = (context) => ({
|
|
96
101
|
|
97
102
|
const defaultCenter = createOptionCenter('_default', { default: true });
|
98
103
|
|
99
|
-
|
104
|
+
const confNamespace = {
|
100
105
|
optionCenter: createOptionCenter,
|
101
106
|
staticFile: staticFile,
|
102
107
|
set: (key, value) => defaultCenter.set(key, value),
|
@@ -104,7 +109,19 @@ module.exports = (context) => ({
|
|
104
109
|
remove: (key) => defaultCenter.remove(key),
|
105
110
|
root: rootPath,
|
106
111
|
package: packageName,
|
107
|
-
loadYaml: (file) => jsYaml.load(fs.readFileSync(file, { encoding: 'utf-8' }))
|
112
|
+
loadYaml: (file) => jsYaml.load(fs.readFileSync(file, { encoding: 'utf-8' })),
|
113
|
+
prototype: {
|
114
|
+
class: (name, defaults = {}) => new Usage('conf::class', (cb) => {
|
115
|
+
const optionCenter = createOptionCenter(name, defaults);
|
116
|
+
return cb.call(confNamespace, optionCenter);
|
117
|
+
}),
|
118
|
+
center: (name, defaults = {}) => (cb) => new Usage('conf::class', () => {
|
119
|
+
const optionCenter = createOptionCenter(name, defaults);
|
120
|
+
return cb.call(confNamespace, optionCenter);
|
121
|
+
})
|
122
|
+
}
|
108
123
|
};
|
124
|
+
|
125
|
+
return confNamespace;
|
109
126
|
},
|
110
127
|
});
|
package/lib/rew/pkgs/rune.js
CHANGED
@@ -3,8 +3,11 @@ const { v4: uuidv4 } = require('uuid');
|
|
3
3
|
const path = require('path');
|
4
4
|
const { CONFIG_PATH } = require('../const/config_path');
|
5
5
|
const { serializeData, deserializeData, gen_key } = require('../misc/bin');
|
6
|
+
const RuneDB = require('./modules/rune/db');
|
7
|
+
const { typeis } = require('../const/default');
|
6
8
|
|
7
9
|
const ENCRYPTION_KEY = 'e6ad8b0792b9e0472ea44d1f3adfd1d503182efcce25991b05cc5ef83f307ffc';
|
10
|
+
const PAGE_LIMIT = 100;
|
8
11
|
|
9
12
|
class Change {
|
10
13
|
constructor(values) {
|
@@ -46,6 +49,37 @@ function getCollectionFromID(id) {
|
|
46
49
|
return eid(id.split('+')[0], -5);
|
47
50
|
}
|
48
51
|
|
52
|
+
function coltypedef(cb) {
|
53
|
+
const typedef = {};
|
54
|
+
const ctx = {
|
55
|
+
opt: (name, type, defaultValue) => {
|
56
|
+
typedef[name] = type;
|
57
|
+
if(typeof defaultValue !== "undefined"){
|
58
|
+
if(!typedef['@rune.default']){
|
59
|
+
typedef['@rune.default'] = {};
|
60
|
+
}
|
61
|
+
typedef['@rune.default'][name] = defaultValue;
|
62
|
+
}
|
63
|
+
},
|
64
|
+
req: (name, type, defaultValue) => {
|
65
|
+
if(!typedef['@rune.required']){
|
66
|
+
typedef['@rune.required'] = {};
|
67
|
+
}
|
68
|
+
typedef['@rune.required'][name] = true;
|
69
|
+
ctx.opt(name, type, defaultValue);
|
70
|
+
},
|
71
|
+
unique: (name, type, defaultValue) => {
|
72
|
+
if(!typedef['@rune.unique']){
|
73
|
+
typedef['@rune.unique'] = {};
|
74
|
+
}
|
75
|
+
typedef['@rune.unique'][name] = true;
|
76
|
+
ctx.req(name, type, defaultValue);
|
77
|
+
}
|
78
|
+
};
|
79
|
+
cb.call(ctx);
|
80
|
+
return typedef;
|
81
|
+
}
|
82
|
+
|
49
83
|
const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
50
84
|
const dbDirPath = path.join(dirname, dbName);
|
51
85
|
const mainFilePath = path.join(dbDirPath, 'main.bin');
|
@@ -109,10 +143,76 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
109
143
|
fs.writeFileSync(filePath, buffer);
|
110
144
|
};
|
111
145
|
|
112
|
-
const collection = (collectionName
|
146
|
+
const collection = (collectionName, {
|
147
|
+
model,
|
148
|
+
exclude
|
149
|
+
} = {}) => {
|
113
150
|
const collectionFilePath = path.join(dbDirPath, `${collectionName}.col`);
|
114
151
|
|
115
|
-
const
|
152
|
+
const validateFields = (definition, data, optional = false) => {
|
153
|
+
if(!definition){
|
154
|
+
return data;
|
155
|
+
}
|
156
|
+
|
157
|
+
const validatedData = {};
|
158
|
+
for (const [field, type] of Object.entries(definition)) {
|
159
|
+
if(field.startsWith('@rune.')) continue;
|
160
|
+
if(!data[field] && typeof definition['@rune.default']?.[field] !== "undefined") data[field] = definition['@rune.default']?.[field];
|
161
|
+
|
162
|
+
if(typeof data[field] == "function"){
|
163
|
+
data[field] = data[field](data, definition);
|
164
|
+
}
|
165
|
+
|
166
|
+
if (data[field] === undefined) {
|
167
|
+
if(optional) continue;
|
168
|
+
else if(definition['@rune.required']){
|
169
|
+
if(definition['@rune.required'][field]){
|
170
|
+
throw new ReferenceError(`Field ${field} is required, yet not provided.`)
|
171
|
+
} else continue;
|
172
|
+
} else continue;
|
173
|
+
}
|
174
|
+
|
175
|
+
const value = data[field];
|
176
|
+
if (!typeis(value, type) && value != type) {
|
177
|
+
throw new TypeError(`Invalid type for field "${field}". Expected ${
|
178
|
+
type?.type?.type || type?.type || type
|
179
|
+
}, got ${typeof value}`);
|
180
|
+
}
|
181
|
+
validatedData[field] = value;
|
182
|
+
}
|
183
|
+
return validatedData;
|
184
|
+
};
|
185
|
+
|
186
|
+
const validateUniqueFields = (record, data) => {
|
187
|
+
if(!model) return null;
|
188
|
+
if(!model['@rune.unique']) return null;
|
189
|
+
|
190
|
+
const uniqueFields = Object.keys(model['@rune.unique']);
|
191
|
+
|
192
|
+
return uniqueFields.find(
|
193
|
+
(field) => data.find(storedRecord => storedRecord[field] == record[field])
|
194
|
+
);
|
195
|
+
}
|
196
|
+
|
197
|
+
const applyFieldSelection = (record, select) => {
|
198
|
+
const newRecord = {...record};
|
199
|
+
if(exclude) for (const key of exclude) {
|
200
|
+
if (record[key]) delete newRecord[key];
|
201
|
+
}
|
202
|
+
if (!select) return newRecord;
|
203
|
+
const selectedRecord = {};
|
204
|
+
for (const key of Array.isArray(select) ? select : Object.keys(select)) {
|
205
|
+
if (record[key]) selectedRecord[key] = record[key];
|
206
|
+
}
|
207
|
+
return selectedRecord;
|
208
|
+
};
|
209
|
+
|
210
|
+
const insert = (record, fields) => {
|
211
|
+
|
212
|
+
if(Array.isArray(record)){
|
213
|
+
return record.map((item) => insert(item));
|
214
|
+
}
|
215
|
+
|
116
216
|
const mainData = readMainData();
|
117
217
|
if (!mainData.collections.includes(collectionName)) {
|
118
218
|
mainData.collections.push(collectionName);
|
@@ -123,20 +223,27 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
123
223
|
if (fs.existsSync(collectionFilePath)) {
|
124
224
|
data = readDataFile(collectionFilePath);
|
125
225
|
}
|
226
|
+
if(model){
|
227
|
+
record = validateFields(model, record);
|
228
|
+
const invalidUniqueFields = validateUniqueFields(record, data);
|
229
|
+
if(invalidUniqueFields){
|
230
|
+
throw new ReferenceError(`Duplicate value for field ${invalidUniqueFields}`);
|
231
|
+
}
|
232
|
+
}
|
126
233
|
const id = uuidv4();
|
127
234
|
record['@rune.id'] = generateID(id, collectionName);
|
128
235
|
data.push(record);
|
129
236
|
writeDataFile(collectionFilePath, data);
|
130
|
-
return record;
|
237
|
+
return applyFieldSelection(record, fields);
|
131
238
|
};
|
132
239
|
|
133
|
-
const read = (id,
|
240
|
+
const read = (id, fields) => {
|
134
241
|
if (typeof id == 'object' && '@rune.id' in id) id = id['@rune.id'];
|
135
242
|
if (!fs.existsSync(collectionFilePath)) return null;
|
136
243
|
const data = readDataFile(collectionFilePath);
|
137
244
|
const record = data.find((record) => record['@rune.id'] === id);
|
138
245
|
if (record) {
|
139
|
-
return evaluateRecord(record);
|
246
|
+
return applyFieldSelection(evaluateRecord(record), fields);
|
140
247
|
}
|
141
248
|
return null;
|
142
249
|
};
|
@@ -168,34 +275,29 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
168
275
|
return record;
|
169
276
|
};
|
170
277
|
|
171
|
-
const update = (caseRecord, newRecord) => {
|
172
|
-
|
173
|
-
let id;
|
174
|
-
if (typeof caseRecord === 'string') {
|
175
|
-
id = caseRecord;
|
176
|
-
} else if (typeof caseRecord === 'object') {
|
177
|
-
const data = readDataFile(collectionFilePath);
|
178
|
-
const record = data.find((record) => {
|
179
|
-
for (const key in caseRecord) {
|
180
|
-
if (record[key] !== caseRecord[key]) return false;
|
181
|
-
}
|
182
|
-
return true;
|
183
|
-
});
|
184
|
-
if (record) {
|
185
|
-
id = record['@rune.id'];
|
186
|
-
} else {
|
187
|
-
return null; // No matching record found
|
188
|
-
}
|
189
|
-
}
|
190
|
-
|
191
|
-
if (!id) return null;
|
192
|
-
|
278
|
+
const update = (caseRecord, newRecord, limit = 0, fields) => {
|
279
|
+
let updatedRecords = [];
|
193
280
|
const data = readDataFile(collectionFilePath);
|
194
|
-
const
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
281
|
+
const validatedNewRecord = model ? validateFields(model, newRecord, true) : newRecord;
|
282
|
+
const invalidUniqueFields = validateUniqueFields(validatedNewRecord, data);
|
283
|
+
if(invalidUniqueFields){
|
284
|
+
throw new ReferenceError(`Duplicate value for field ${invalidUniqueFields}`);
|
285
|
+
}
|
286
|
+
|
287
|
+
const matches = data.filter((record) => {
|
288
|
+
if (typeof caseRecord === 'string') {
|
289
|
+
return record['@rune.id'] === caseRecord;
|
290
|
+
} else if (typeof caseRecord === 'object') {
|
291
|
+
return Object.keys(caseRecord).every((key) => record[key] === caseRecord[key]);
|
292
|
+
}
|
293
|
+
return false;
|
294
|
+
});
|
295
|
+
if (matches.length === 0) return null;
|
296
|
+
|
297
|
+
matches.forEach((oldRecord, index) => {
|
298
|
+
if(limit > 0 && updatedRecords.length > limit) return;
|
299
|
+
for (const key in validatedNewRecord) {
|
300
|
+
const value = validatedNewRecord[key];
|
199
301
|
if (value instanceof PushChange) {
|
200
302
|
if (!oldRecord[key] || !Array.isArray(oldRecord[key])) {
|
201
303
|
oldRecord[key] = [];
|
@@ -211,17 +313,17 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
211
313
|
});
|
212
314
|
}
|
213
315
|
} else {
|
214
|
-
oldRecord[key] = value;
|
316
|
+
oldRecord[key] = typeof value == "function" ? value(oldRecord, index) : value;
|
215
317
|
}
|
216
318
|
}
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
return
|
319
|
+
updatedRecords.push(applyFieldSelection(evaluateRecord(oldRecord), fields));
|
320
|
+
});
|
321
|
+
writeDataFile(collectionFilePath, data);
|
322
|
+
|
323
|
+
return updatedRecords;
|
222
324
|
};
|
223
325
|
|
224
|
-
const find = (criteria) => {
|
326
|
+
const find = (criteria, fields, limit = 0, index = 0) => {
|
225
327
|
if (typeof criteria == 'string') return read(criteria);
|
226
328
|
if (!criteria || typeof criteria !== 'object') return null;
|
227
329
|
|
@@ -230,19 +332,19 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
230
332
|
|
231
333
|
const data = readDataFile(collectionFilePath);
|
232
334
|
const record =
|
233
|
-
data
|
335
|
+
data[limit > 0 || limit == -1 ? 'filter' : 'find']((record) => {
|
234
336
|
for (const key in criteria) {
|
235
337
|
if (record[key] !== criteria[key]) return false;
|
236
338
|
}
|
237
339
|
return true;
|
238
340
|
}) || null;
|
239
341
|
if (record) {
|
240
|
-
return evaluateRecord(record);
|
342
|
+
return Array.isArray(record) ? (limit > 0 ? record.slice(index, index + limit) : record).map(i => applyFieldSelection(evaluateRecord(i), fields)) : applyFieldSelection(evaluateRecord(record), fields);
|
241
343
|
}
|
242
344
|
return null;
|
243
345
|
};
|
244
346
|
|
245
|
-
const
|
347
|
+
const removeOne = (id) => {
|
246
348
|
if ('@rune.id' in id) id = id['@rune.id'];
|
247
349
|
let data = readDataFile(collectionFilePath);
|
248
350
|
const index = data.findIndex((record) => record['@rune.id'] === id);
|
@@ -254,10 +356,34 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
254
356
|
return false;
|
255
357
|
};
|
256
358
|
|
257
|
-
const
|
359
|
+
const remove = (criteria, limit = Infinity) => {
|
360
|
+
let data = readDataFile(collectionFilePath);
|
361
|
+
let deletedCount = 0;
|
362
|
+
|
363
|
+
const filteredData = data.filter((record, index) => {
|
364
|
+
if (deletedCount >= limit) return true;
|
365
|
+
|
366
|
+
const matches = Object.keys(criteria).every((key) => typeof criteria[key] == 'function' ? criteria[key](record[key], record, index) : record[key] === criteria[key]);
|
367
|
+
if (matches) {
|
368
|
+
deletedCount++;
|
369
|
+
return false;
|
370
|
+
}
|
371
|
+
|
372
|
+
return true;
|
373
|
+
});
|
374
|
+
|
375
|
+
if (deletedCount === 0) return false;
|
376
|
+
|
377
|
+
writeDataFile(collectionFilePath, filteredData);
|
378
|
+
|
379
|
+
return true;
|
380
|
+
};
|
381
|
+
|
382
|
+
|
383
|
+
const list = (fields) => {
|
258
384
|
if (!fs.existsSync(collectionFilePath)) return [];
|
259
385
|
const data = readDataFile(collectionFilePath);
|
260
|
-
return data.map((rec) => evaluateRecord(rec));
|
386
|
+
return data.map((rec) => applyFieldSelection(evaluateRecord(rec), fields));
|
261
387
|
};
|
262
388
|
|
263
389
|
const map = (cb, mutate = false) => {
|
@@ -296,6 +422,10 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
296
422
|
return sortedData;
|
297
423
|
};
|
298
424
|
|
425
|
+
const empty = () => {
|
426
|
+
writeDataFile(collectionFilePath, []);
|
427
|
+
}
|
428
|
+
|
299
429
|
if (!fs.existsSync(collectionFilePath)) writeDataFile(collectionFilePath, []);
|
300
430
|
|
301
431
|
return {
|
@@ -303,12 +433,14 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
303
433
|
read,
|
304
434
|
update,
|
305
435
|
remove,
|
436
|
+
removeOne,
|
306
437
|
find,
|
307
438
|
map,
|
308
439
|
transform,
|
309
440
|
filter,
|
310
441
|
sort,
|
311
442
|
list,
|
443
|
+
empty
|
312
444
|
};
|
313
445
|
};
|
314
446
|
|
@@ -387,9 +519,11 @@ const createDB = (dbName, dirname, dbData = {}, encryptionKey) => {
|
|
387
519
|
return { set, get, remove, list, transform };
|
388
520
|
};
|
389
521
|
|
522
|
+
collection.type = coltypedef;
|
523
|
+
|
390
524
|
readMainData();
|
391
525
|
|
392
|
-
return { setData, getData, collection, findRef, makeRef, map };
|
526
|
+
return new RuneDB({ setData, getData, collection, findRef, makeRef, map });
|
393
527
|
};
|
394
528
|
|
395
529
|
module.exports = (context) => ({
|
@@ -0,0 +1,94 @@
|
|
1
|
+
const { typeis } = require("../const/default");
|
2
|
+
const RuneDB = require("./modules/rune/db");
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
module.exports = (context) => ({
|
7
|
+
schema(runeDB) {
|
8
|
+
if (!runeDB) throw new ReferenceError('You should pass a rune database for the schema.');
|
9
|
+
if (!(runeDB instanceof RuneDB)) throw new TypeError('First argument is not a Rune database.');
|
10
|
+
|
11
|
+
const models = {};
|
12
|
+
|
13
|
+
const validateFields = (definition, data) => {
|
14
|
+
const validatedData = {};
|
15
|
+
for (const [field, type] of Object.entries(definition)) {
|
16
|
+
if (data[field] === undefined) continue;
|
17
|
+
const value = data[field];
|
18
|
+
if (!typeis(value, type) && value != type) {
|
19
|
+
throw new TypeError(`Invalid type for field "${field}". Expected ${
|
20
|
+
type?.type?.type || type?.type || type
|
21
|
+
}, got ${typeof value}`);
|
22
|
+
}
|
23
|
+
validatedData[field] = value;
|
24
|
+
}
|
25
|
+
return validatedData;
|
26
|
+
};
|
27
|
+
|
28
|
+
const applyFieldSelection = (record, select) => {
|
29
|
+
if (!select) return record;
|
30
|
+
if (Array.isArray(select)) select = Object.fromEntries(select.map(i => [i, true]));
|
31
|
+
const selectedRecord = {};
|
32
|
+
for (const key of Object.keys(select)) {
|
33
|
+
if (select[key]) selectedRecord[key] = record[key];
|
34
|
+
}
|
35
|
+
return selectedRecord;
|
36
|
+
};
|
37
|
+
|
38
|
+
return {
|
39
|
+
model(modelName, definition, options = {}) {
|
40
|
+
if (models[modelName]) throw new Error(`Model "${modelName}" is already defined.`);
|
41
|
+
|
42
|
+
const collection = runeDB.collection(modelName);
|
43
|
+
|
44
|
+
const modelAPI = {
|
45
|
+
create(record) {
|
46
|
+
const validatedRecord = validateFields(definition, record);
|
47
|
+
return collection.insert(validatedRecord);
|
48
|
+
},
|
49
|
+
|
50
|
+
findUnique(id, select) {
|
51
|
+
const record = collection.read(id);
|
52
|
+
if (!record) return null;
|
53
|
+
|
54
|
+
let finalRecord = record;
|
55
|
+
return applyFieldSelection(finalRecord, select);
|
56
|
+
},
|
57
|
+
|
58
|
+
findMany(where, select, cursor, count = 0) {
|
59
|
+
|
60
|
+
const records = collection.find(where, count == 0 ? -1 : count, cursor);
|
61
|
+
|
62
|
+
return select
|
63
|
+
? records.map((record) => applyFieldSelection(record, select))
|
64
|
+
: records;
|
65
|
+
},
|
66
|
+
|
67
|
+
update(identifier, updates, limit) {
|
68
|
+
return collection.update(identifier, updates, limit);
|
69
|
+
},
|
70
|
+
|
71
|
+
delete(identifier, limit) {
|
72
|
+
return collection.remove(identifier, limit);
|
73
|
+
},
|
74
|
+
|
75
|
+
list() {
|
76
|
+
return collection.list();
|
77
|
+
},
|
78
|
+
|
79
|
+
empty(){
|
80
|
+
return collection.empty();
|
81
|
+
}
|
82
|
+
};
|
83
|
+
|
84
|
+
models[modelName] = modelAPI;
|
85
|
+
return modelAPI;
|
86
|
+
},
|
87
|
+
|
88
|
+
useModel(modelName) {
|
89
|
+
if (!models[modelName]) throw new Error(`Model "${modelName}" is not defined.`);
|
90
|
+
return models[modelName];
|
91
|
+
},
|
92
|
+
};
|
93
|
+
},
|
94
|
+
});
|