@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.
@@ -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;
@@ -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
+ })
@@ -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)) && (conf[name] = 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
- return {
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
  });
@@ -0,0 +1,9 @@
1
+
2
+
3
+ module.exports = class RuneDB {
4
+ constructor(attrs){
5
+ for(let i in attrs){
6
+ this[i] = attrs[i];
7
+ }
8
+ }
9
+ };
@@ -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 insert = (record) => {
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, evaluate = true) => {
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 index = data.findIndex((record) => record['@rune.id'] === id);
195
- if (index !== -1) {
196
- const oldRecord = data[index];
197
- for (const key in newRecord) {
198
- const value = newRecord[key];
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
- data[index] = oldRecord;
218
- writeDataFile(collectionFilePath, data);
219
- return data[index];
220
- }
221
- return null;
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.find((record) => {
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 remove = (id) => {
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 list = () => {
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
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makano/rew",
3
- "version": "1.5.5",
3
+ "version": "1.5.8",
4
4
  "description": "A simple coffescript runtime and app manager",
5
5
  "main": "main.js",
6
6
  "directories": {