@makano/rew 1.5.4 → 1.5.7

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.
@@ -6,7 +6,7 @@ const sleep = require('../functions/sleep');
6
6
  const { match } = require('../functions/match');
7
7
  const { map } = require('../functions/map');
8
8
  const { typex, typeis, typedef, typei, int, float, num, str, bool, typef } = require('../functions/types');
9
- const { isEmpty, clone, deepClone, merge, uniqueId, compose, curry, getters, setters, deepMerge } = require('../functions/core');
9
+ const { isEmpty, clone, deepClone, merge, uniqueId, compose, curry, getters, setters, deepMerge, randFrom, pickRandom } = require('../functions/core');
10
10
  const { print, input, clear, printf } = require('../functions/stdout');
11
11
  const { curl } = require('../functions/curl');
12
12
  const { wait } = require('../functions/wait');
@@ -64,4 +64,7 @@ module.exports = {
64
64
  print,
65
65
  printf,
66
66
  input,
67
+
68
+ randFrom,
69
+ pickRandom
67
70
  };
@@ -114,7 +114,23 @@ function setters(object, setters) {
114
114
  configurable: true
115
115
  });
116
116
  }
117
- }
117
+ }
118
+ }
119
+
120
+ function withOut(object, ...keys){
121
+ let o = {...object};
122
+ for(let key of keys) delete o[key];
123
+ return o;
124
+ }
125
+
126
+ function randFrom(min,max) {
127
+ return Math.floor(Math.random()*(max-min+1)+min);
128
+ }
129
+
130
+ function pickRandom(...stuff){
131
+ return stuff[
132
+ randFrom(0, stuff.length - 1)
133
+ ];
118
134
  }
119
135
 
120
136
  module.exports = {
@@ -129,5 +145,8 @@ module.exports = {
129
145
  compose,
130
146
  curry,
131
147
  getters,
132
- setters
148
+ setters,
149
+ withOut,
150
+ randFrom,
151
+ pickRandom
133
152
  };
@@ -10,7 +10,7 @@ module.exports.curl = function curl(options, url){
10
10
  if(options.url && !url){
11
11
  url = options.url
12
12
  }
13
- const method = options.x || "GET";
13
+ const method = options.x || options.method || "GET";
14
14
  const f = future.promise(fetch(url, {
15
15
  ...options,
16
16
  method
@@ -20,7 +20,7 @@ module.exports.cleanCache = () => {
20
20
  while(cachedFiles.length) cachedFiles.pop();
21
21
  };
22
22
  const lookUpInOtherApps = (fullPath) => {
23
- straceLog('===> WARN: LOOKUP SLOWS PROCESS');
23
+ straceLog('===> WARN: Lookup slows process');
24
24
  const con = conf({});
25
25
  const name = fullPath.indexOf('/') ? fullPath.split('/')[0] : fullPath;
26
26
  let dpath = fullPath.indexOf('/') ? fullPath.split('/')[1] : '';
@@ -41,7 +41,7 @@ const lookUpInOtherApps = (fullPath) => {
41
41
  };
42
42
 
43
43
  module.exports.imp = function (runPath, context) {
44
- return function (filename, options = {}) {
44
+ return function (filename, options = {}, defaultMode) {
45
45
  if (!options) options = {};
46
46
  if(filename == 'std' || filename == '#std') return {};
47
47
  let type = options.type ? options.type : filename.endsWith('.coffee') ? 'coffee' : (
@@ -51,10 +51,10 @@ module.exports.imp = function (runPath, context) {
51
51
  let exports,
52
52
  ispkg = findPackage(filename);
53
53
 
54
- straceLog('IMPORT for', filename, 'as', type);
54
+ straceLog('import file', '"' + filename + '"', 'as type', type);
55
55
 
56
56
  if (filename.startsWith('@') && context.app) {
57
- straceLog('===> FROM APP ROOT');
57
+ straceLog('===> from app root');
58
58
  filename = filename.replace('@', context.app.path);
59
59
  }
60
60
 
@@ -62,7 +62,7 @@ module.exports.imp = function (runPath, context) {
62
62
  if(path.extname(filepath) == '.qrew') options.qrew = true;
63
63
 
64
64
  const lookUp = () => {
65
- straceLog('===> LOOKUP()');
65
+ straceLog('===> lookUp()');
66
66
  const otherPath = lookUpInOtherApps(filename);
67
67
  if (!otherPath) throw new Error('Module "' + filename + '" not found');
68
68
  else filepath = otherPath;
@@ -80,7 +80,7 @@ module.exports.imp = function (runPath, context) {
80
80
  typeof ext == 'string' ? existsSync(filepath + ext) : existsSync(filepath + (ext.ext || '')),
81
81
  );
82
82
  if (resolve) {
83
- straceLog('===> RESOLVE()');
83
+ straceLog('===> resolve()');
84
84
  filepath += typeof resolve == 'string' ? resolve : resolve.ext;
85
85
  if (typeof resolve == 'object' && resolve.options) {
86
86
  if (resolve.options.type) type = resolve.options.type;
@@ -91,7 +91,7 @@ module.exports.imp = function (runPath, context) {
91
91
  }
92
92
 
93
93
  const exec = (coptions = {}) => {
94
- straceLog('===> EXECUTE() IMPORTFILE');
94
+ straceLog('===> execute(importFile)');
95
95
  const r = runPath(
96
96
  filepath,
97
97
  {
@@ -113,7 +113,7 @@ module.exports.imp = function (runPath, context) {
113
113
  }
114
114
 
115
115
  if (ispkg) {
116
- straceLog('===> FIND_PACKAGE()');
116
+ straceLog('===> findPackage()');
117
117
  const pkg = getPackage(filename)(context, options);
118
118
  exports = pkg._onImport ? pkg._onImport() : pkg;
119
119
  if(options.useDefaultForPackages) exports = { default: exports };
@@ -124,10 +124,10 @@ module.exports.imp = function (runPath, context) {
124
124
  } else if (type == 'js') {
125
125
  exports = exec({ compile: false });
126
126
  } else if (type == 'yaml' || type == 'json' || type == 'text') {
127
- straceLog('===> GET_RAW_FILE()');
127
+ straceLog('===> getRawFile()');
128
128
  const f = getFile(filepath);
129
129
  if (type == 'yaml') {
130
- straceLog('===> FROM_YAML()');
130
+ straceLog('===> fromYaml()');
131
131
  exports = _returns(options, importYaml(f.path, f));
132
132
  } else if (type == 'json') {
133
133
  straceLog('===>');
@@ -137,7 +137,7 @@ module.exports.imp = function (runPath, context) {
137
137
  exports = _returns(options, {});
138
138
  }
139
139
  } else {
140
- straceLog('===> FROM_TEXT');
140
+ straceLog('===> fromText()');
141
141
  exports = _returns(options, f.content);
142
142
  }
143
143
  }
@@ -168,6 +168,9 @@ module.exports.imp = function (runPath, context) {
168
168
  /**/ if(options.mock === null) return null;
169
169
  //**
170
170
 
171
+ //** Return defaults
172
+ /**/ if(defaultMode !== true && exports?.default) exports = exports.default;
173
+
171
174
  return exports;
172
175
  };
173
176
  };
@@ -1,4 +1,5 @@
1
1
  const jsYaml = require("js-yaml");
2
+ const { yamlFile } = require("../modules/yaml");
2
3
 
3
4
 
4
5
 
@@ -11,8 +12,11 @@ function jsons(thing){
11
12
  }
12
13
 
13
14
 
14
- function yaml(thing){
15
- return jsYaml.loadAll(thing)[0];
15
+ function yaml(thing, ...schema){
16
+ return yamlFile({
17
+ content: thing,
18
+ path: ''
19
+ }, schema);
16
20
  }
17
21
 
18
22
  function yamls(thing){
@@ -6,11 +6,11 @@ const { straceLog } = require('./strace');
6
6
 
7
7
  module.exports.findAppInfo = function (filepath) {
8
8
  const appPath = findAppPath(path.dirname(filepath));
9
- straceLog('FINDAPPINFO() for', filepath);
9
+ straceLog('findAppInfo() for', filepath);
10
10
  if (appPath) {
11
11
  const config = jsYaml.load(readFileSync(path.join(appPath, 'app.yaml')));
12
- straceLog('==> FOUND CONFIG AT', appPath);
13
- straceLog('==> APP PACKAGE', config?.manifest?.package);
12
+ straceLog('==> INFO Found config at:', '"' + appPath + '"');
13
+ straceLog('==> INFO App Package:', '"' + config?.manifest?.package + '"');
14
14
  return {
15
15
  path: appPath,
16
16
  config,
@@ -3,23 +3,19 @@ const fs = require('fs'); // Import the 'path' module
3
3
  const { straceLog } = require('./strace');
4
4
 
5
5
  module.exports.findAppPath = (currentDir = __dirname) => {
6
- // Check if app.yaml exists in the current directory
7
- straceLog('FINDAPP() for', currentDir);
6
+ straceLog('findApp() for', '"' + currentDir + '"');
8
7
  const appYamlPath = path.join(currentDir, 'app.yaml');
9
8
  if (fs.existsSync(appYamlPath)) {
10
9
 
11
- straceLog('==> FOUND PATH', appYamlPath);
10
+ straceLog('==> INFO found path', `"${appYamlPath}"`);
12
11
  return currentDir;
13
12
  }
14
13
 
15
- // If not found, move up a directory level
16
14
  const parentDir = path.dirname(currentDir);
17
15
 
18
- // Check if we reached the root directory
19
16
  if (parentDir === currentDir) {
20
- return null; // Not found
17
+ return null;
21
18
  }
22
19
 
23
- // Recursively call the function on the parent directory
24
20
  return module.exports.findAppPath(parentDir);
25
21
  };
@@ -1,5 +1,5 @@
1
1
  module.exports.straceLog = function(...logs){
2
2
  if(process.straceMode){
3
- console.log(`[${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()} ${new Date().getDate()}/${new Date().getMonth()}/${new Date().getFullYear()}] [${process.pid}]`, ...logs);
3
+ console.log(`[${new Date().toLocaleTimeString()} ${new Date().getDate()}/${new Date().getMonth()}/${new Date().getFullYear()}] [${process.pid}]`, ...logs.map(i => i.replace(/^([=>]+)/, '\x1b[34m$1\x1b[39m').replace(/WARN:/g, '\x1b[30m\x1b[43m WARN \x1b[39m\x1b[49m').replace(/INFO/g, '\x1b[30m\x1b[44m INFO \x1b[39m\x1b[49m').replace(/^"(.+)"$/, '\x1b[32m$1\x1b[39m').replace(/!([A-Z-+]+)/, (_, a) => `\x1b[30m\x1b[4${a.startsWith('-') ? '5' : a.startsWith('+') ? '6' : '2'}m ${a.replace(/^[-+]/, '')} \x1b[39m\x1b[49m`)));
4
4
  }
5
5
  }
@@ -137,15 +137,15 @@ const fnextToken = (i, tokens, type, value) => {
137
137
  function declareAlias(aliases, token) {
138
138
  const regex = /^#declare(\*)?\s+(\w+)\s+"([^"]+)"\s*=\s*([\s\S]*);$/;
139
139
  const match = token.value.trim().match(regex);
140
- straceLog('DECLARECASE()');
141
- straceLog('==> EXPERIMENTAL FEATURE DETECTED');
140
+ straceLog('declareCase()');
141
+ straceLog('==> WARN: Experimental feature detected');
142
142
 
143
143
  if (match) {
144
144
  const isPublic = !!match[1];
145
145
  const type = match[2] == "key" ? 'IDENTIFIER' : match[2];
146
146
  let name = match[3];
147
147
  let value = match[4].trim();
148
- straceLog('==> DECLARE', name, 'as', value);
148
+ straceLog('==> INFO !DECLARE', name, 'as', value);
149
149
 
150
150
  let aliasValue = value.startsWith('${')
151
151
  ? new Function('token', 'tokens', 'code', 'hooks', 'index', 'setIndex', value.slice(2, -1))
@@ -210,7 +210,7 @@ function declareAlias(aliases, token) {
210
210
  aliases[type][name] = aliasValue;
211
211
 
212
212
  if(isPublic){
213
- straceLog('==>', 'DECLARATION GLOBALIZED');
213
+ straceLog('==>', 'INFO Declaration Globalized');
214
214
  execOptions._syntaxAliases[type] = execOptions._syntaxAliases[type] || {};
215
215
  execOptions._syntaxAliases[type][name] = aliasValue;
216
216
  }
@@ -234,11 +234,11 @@ const stdTypes = (isPublic) => {
234
234
  return r;
235
235
  };
236
236
  const includeFile = (includeContent, options) => {
237
- straceLog('INCLUDE()', includeContent);
237
+ straceLog('include()', includeContent);
238
238
  const dontInclude = includeContent.startsWith('*');
239
239
  if(dontInclude) {
240
240
  includeContent = includeContent.slice(1);
241
- straceLog('==> IGNORING OUTPUT', includeContent);
241
+ straceLog('==> INFO ingoring output', includeContent);
242
242
  };
243
243
  const fp = path.resolve(path.dirname(options.filename || ''), includeContent);
244
244
  let packageName = options.filename ? (existsSync(fp) ? fp : includeContent) : includeContent;
@@ -257,13 +257,13 @@ const includeFile = (includeContent, options) => {
257
257
  if(packageName == 'std'){
258
258
  r = _inc(stdTypes(dontInclude), true);
259
259
  } else if (existsSync(packageName)) {
260
- straceLog('==> INCLUDE FILE', packageName);
260
+ straceLog('==> includeFile(', '"'+packageName+'"', ')');
261
261
  r = _inc(packageName);
262
262
  } else {
263
263
  const packageName = includeContent.match('/') ? includeContent.split('/')[0] : includeContent;
264
264
  const headerFile = includeContent.match('/') ? includeContent.replace(packageName+'/', '') : 'main.h.coffee';
265
265
  const pathname = path.join(CONFIG_PATH, packageName, 'app', headerFile);
266
- straceLog('==> INCLUDE PACKAGE', packageName);
266
+ straceLog('==> includePackage(', '"'+packageName+'"');
267
267
  if(existsSync(pathname)) r = _inc(pathname);
268
268
  }
269
269
  if(dontInclude){
@@ -277,12 +277,12 @@ function useImp(token, options){
277
277
  token.value.startsWith('"#') ||
278
278
  token.value.startsWith("'#")
279
279
  )){
280
- straceLog('==> IMP Uses HEADER');
280
+ straceLog('==> INFO imp() Uses HEADER');
281
281
  const dem = token.value.slice(0, 1);
282
282
  const value = token.value.slice(1, -1);
283
283
  let packageName = value.slice(1);
284
284
  token.value = dem+packageName+dem;
285
- straceLog('IMP() with HEADER for', packageName);
285
+ straceLog('imp() with header for', `"${packageName}"`);
286
286
  return includeFile(packageName !== 'std' ? packageName : '*'+packageName, options);
287
287
  }
288
288
  return '';
@@ -344,7 +344,7 @@ function insertAt(array, index, ...values) {
344
344
  }
345
345
 
346
346
  function compileRewStuff(content, options) {
347
- straceLog('TOKENIZE() for CURRENTFILE');
347
+ straceLog('tokeinze(currentFile)');
348
348
  const tokens = tokenizeCoffeeScript(content);
349
349
  let result = '';
350
350
  let multilineDeclareBuffer = [];
@@ -360,11 +360,16 @@ function compileRewStuff(content, options) {
360
360
  for (let i = 0; i < tokens.length; i++) {
361
361
  const token = tokens[i];
362
362
  let { token: nextToken, n } = gnextToken(i, 1, tokens) || {};
363
+ let { token: prevToken } = gnextToken(i, -2, tokens) || {};
363
364
 
364
365
  if(token.type == "COMMENT" && i < 2 && token.value.startsWith('#!')){
365
366
  continue;
366
367
  }
367
368
 
369
+ if(token.type == "IDENTIFIER" && token.value == "fn" && i < 2 && prevToken?.value !== "." && nextToken?.type === "IDENTIFIER"){
370
+ token.value = "function";
371
+ }
372
+
368
373
  if ((token.type === "COMMENT" && multilineDeclare) || (token.type !== "COMMENT" && multilineDeclare)) {
369
374
  if(token.type === "COMMENT"){
370
375
  multilineDeclareBuffer.push(token.value.startsWith('###') ? token.value.slice(3) : token.value.slice(1));
@@ -412,17 +417,17 @@ function compileRewStuff(content, options) {
412
417
 
413
418
  if (token.type === 'COMMENT' && token.value.slice(1).trim().startsWith('@jsx')) {
414
419
  options.jsx = true;
415
- straceLog('JSX() ENABLE WITH COMMENTS');
420
+ straceLog('jsx().with(comments)');
416
421
  if(token.value.split('@jsx')[1].trim()){
417
422
  options.jsxPragma = token.value.split('@jsx')[1].trim();
418
- straceLog('JSX() PRAGMA WITH', options.jsxPragma);
423
+ straceLog('jsx().withPragma(', `"${options.jsxPragma}"`, ')');
419
424
  }
420
425
  }
421
426
 
422
427
  if (token.type === 'COMMENT' && token.value.slice(1).trim() === '@cls') {
423
428
  options.cls = true;
424
- straceLog('CLI_SYNTAX() ENABLE');
425
- straceLog('===> HIGHLY EXPERIMENTAL FEATURE DETECTED');
429
+ straceLog('cliSyntax::enable()');
430
+ straceLog('===> WARN: HIGHLY EXPERIMENTAL FEATURE DETECTED');
426
431
  }
427
432
 
428
433
  if (options.cls && token.type === 'OTHER' && token.value === '-' && nextToken.value == '-' && tokens[i-1]?.type == 'WHITESPACE') {
@@ -442,12 +447,12 @@ function compileRewStuff(content, options) {
442
447
 
443
448
  if (tokens[i-1]?.value !== '.' && token.type === 'IDENTIFIER' && token.value === 'export' && !options.keepImports) {
444
449
  token.value = 'pub';
445
- straceLog('EXPORT() => TRANSLATING TO pub');
450
+ straceLog('INFO !TRANSLATE converting export to pub');
446
451
  }
447
452
 
448
453
  if (tokens[i-1]?.value !== '.' && token.type === 'IDENTIFIER' && token.value === 'package' && nextToken.type == 'STRING') {
449
454
  token.value = 'appPackage';
450
- straceLog('APP_PACKAGE_CHANGE()');
455
+ straceLog('changeAppPackage()');
451
456
  }
452
457
 
453
458
  if (
@@ -465,7 +470,7 @@ function compileRewStuff(content, options) {
465
470
  }
466
471
 
467
472
  if (tokens[i-1]?.value !== '.' && token.type === 'IDENTIFIER' && token.value === 'using' && !options.disableUse) {
468
- straceLog('USING()');
473
+ straceLog('!+DIRECTIVE using()');
469
474
  const next = nextToken?.value;
470
475
  if(next in USING_DEFAULT) {
471
476
  const { use } = USING_DEFAULT[next];
@@ -475,7 +480,7 @@ function compileRewStuff(content, options) {
475
480
 
476
481
  const { token: nextNextToken } = gnextToken(i, 3, tokens) || {};
477
482
  if(nextNextToken.value == "as") nextNextToken.value = ",";
478
- } else straceLog('==> UNKNOWN');
483
+ } else straceLog('==> !-UNKNOWN');
479
484
  }
480
485
 
481
486
  if (token.type === 'IDENTIFIER' && token.value === 'as' && !options.keepImports) {
@@ -493,15 +498,15 @@ function compileRewStuff(content, options) {
493
498
 
494
499
  if (tokens[i-1]?.value !== '.' && token.type === 'IDENTIFIER' && token.value === 'import' && !options.keepImports) {
495
500
  // console.log(nextToken.type);
496
- straceLog('IMPORT()');
497
- straceLog('==> WARN: SLOWS DOWN COMPILATION');
501
+ straceLog('import()');
502
+ straceLog('==> WARN: Slows compilation');
498
503
  let ind = i + n + 2;
499
504
  let isAs = false;
500
505
  let usedDefault = false;
501
506
 
502
507
  let defaultName;
503
508
  if (nextToken.type === 'STRING') {
504
- straceLog('==> SIMPLE');
509
+ straceLog('==> !SIMPLE');
505
510
  if(useImp(nextToken, options)) updateAliases(aliases);
506
511
  result += `inc ${nextToken.value}`;
507
512
  i += n;
@@ -538,7 +543,7 @@ function compileRewStuff(content, options) {
538
543
  .filter((t, i, arr) => !arr[i+1]?.match(':') && !arr[i-1]?.match(':'))
539
544
  .join(', ');
540
545
 
541
- straceLog('==>', exports, 'from', nameToken.value);
546
+ straceLog('==> !COMPLEX', exports, 'from', nameToken.value);
542
547
 
543
548
  if(useImp(nameToken, options)) updateAliases(aliases);
544
549
  result += `{ ${exports} } ${options.type == 'coffee' ? '=' : ':='} inc ${nameToken.value}`;
@@ -550,7 +555,7 @@ function compileRewStuff(content, options) {
550
555
  const nameToken = fnextToken(asToken.ti, tokens, 'STRING');
551
556
  const nextToken = fnextToken(asToken.ti + 1, tokens, 'IDENTIFIER');
552
557
  defaultName = nextToken.value;
553
- straceLog('==>', defaultName, 'from', nameToken.value);
558
+ straceLog('==> !COMPLEX', defaultName, 'from', nameToken.value);
554
559
  if(useImp(nameToken, options)) updateAliases(aliases);
555
560
  result += `${defaultName} ${options.type == 'coffee' ? '=' : ':='} inc ${nameToken.value}`;
556
561
  i = ind + 6;
@@ -570,7 +575,7 @@ function compileRewStuff(content, options) {
570
575
  .flat(1)
571
576
  .filter((t, i, arr) => !arr[i+1]?.match(':') && !arr[i-1]?.match(':'))
572
577
  .join(', ');
573
- straceLog('==>', defaultName, 'and', exports, 'from', nameToken.value);
578
+ straceLog('==> !COMPLEX', defaultName, 'and', exports, 'from', nameToken.value);
574
579
  if(useImp(nameToken, options)) updateAliases(aliases);
575
580
  result += `{ default: ${defaultName}, ${exports} } ${options.type == 'coffee' ? '=' : ':='} inc ${nameToken?.value || ''}`;
576
581
  i = closingBraceToken.ti + 4;
@@ -589,7 +594,7 @@ function compileRewStuff(content, options) {
589
594
  if (nextLastToken?.value == 'assert') {
590
595
  result += ', ';
591
596
  const assertionToken = gnextToken(nextLastToken.ti, 2, tokens);
592
- straceLog('==> ASSERT', assertionToken);
597
+ straceLog('==> !ASSERT', assertionToken);
593
598
  if(assertionToken.token.type == 'OTHER' && assertionToken.token.value == '{'){
594
599
  hooks.push({
595
600
  index: assertionToken.token.ti,
@@ -607,7 +612,7 @@ function compileRewStuff(content, options) {
607
612
  }
608
613
 
609
614
  if (tokens[i-1]?.value !== '.' && token.type === 'IDENTIFIER' && (token.value === 'imp' || token.value === 'inc')) {
610
- straceLog('IMP() Detected');
615
+ straceLog('!+DIRECTIVE imp()');
611
616
  let { token: t1 } = gnextToken(i, 1, tokens) || {};
612
617
  let { token: t2 } = gnextToken(i, 2, tokens) || {};
613
618
  let r = '';
@@ -634,7 +639,7 @@ function compileRewStuff(content, options) {
634
639
  nextToken.value &&
635
640
  nextToken.value !== 'undefined' && !options.keepImports
636
641
  ) {
637
- straceLog('PUB() Detected');
642
+ straceLog('!+DIRECTIVE pub()');
638
643
  let next = {...nextToken}, isClass = false;
639
644
  if(next.value == 'default'){
640
645
  i += 2;
@@ -643,7 +648,14 @@ function compileRewStuff(content, options) {
643
648
  next.value = gnextToken(i, n + 1, tokens)?.token.value || "default";
644
649
  isClass = true;
645
650
  }
646
- straceLog('==> PUBLIC', next.value);
651
+ if(next.value == 'fn' || next.value == 'function'){
652
+ next.value = gnextToken(i, n + 1, tokens)?.token.value || "default";
653
+ isClass = true;
654
+ if(nextToken && nextToken.value == "fn"){
655
+ nextToken.value = "function";
656
+ }
657
+ }
658
+ straceLog('==> !PUBLIC', next.value);
647
659
  hooks.push({
648
660
  index: i + 1,
649
661
  value: `"${next.value}", ${isClass ? `${next.value} = ` : ''}`,
@@ -653,13 +665,13 @@ function compileRewStuff(content, options) {
653
665
  const aliasType = aliases[token.type];
654
666
  // if(token.value == 'sidewest') console.log(aliases, token.value, token.type);
655
667
  if (aliasType && Object.keys(aliasType).includes(token.value)) {
656
- straceLog('ALIAS()', token.type);
668
+ straceLog('!+DIRECTIVE alias()', token.type);
657
669
  const aliasValue = aliasType[token.value];
658
670
  if (typeof aliasValue === 'function') {
659
- straceLog('==> EXECUTE ALIAS', token.value);
671
+ straceLog('==> INFO Execute alias:', token.value);
660
672
  result += aliasValue(token, tokens, result, hooks, i, (n) => i = n) || "";
661
673
  } else {
662
- straceLog('==> LITERAL ALIAS', token.value);
674
+ straceLog('==> INFO Literal alias:', token.value);
663
675
  result += aliasValue;
664
676
  }
665
677
  continue;
@@ -697,12 +709,12 @@ function compileRewStuff(content, options) {
697
709
  }
698
710
 
699
711
  const compileCivetStuff = (file, options) => {
700
- straceLog('COMPILE_CIVET() for CURRENTFILE');
712
+ straceLog('compileCivet(currentFile)');
701
713
  const preCompileOptions = {
702
714
  filename: file.path,
703
715
  ...options
704
716
  };
705
- straceLog('OPTION_PREPARE() for CURRENTFILE as', JSON.stringify(preCompileOptions));
717
+ straceLog('prepareOptions(currentFile).as(', `"${JSON.stringify(preCompileOptions)}"`, ')');
706
718
 
707
719
  if(options?.type == 'js' || file?.path?.endsWith('.js')){
708
720
  return {
@@ -724,7 +736,7 @@ const compileCivetStuff = (file, options) => {
724
736
  };
725
737
 
726
738
  let compiled = options.async ? compileCivet(prepared, compileOptions) : wait(compileCivet, prepared, compileOptions);
727
- straceLog('==> CIVET COMPILE <>');
739
+ straceLog('==> !COMPILER civetCompile(fileContent)');
728
740
 
729
741
  return {
730
742
  compiled,
@@ -745,10 +757,10 @@ const cpl = (module.exports.compile = function (file, options = {}) {
745
757
  compiledCode = result.compiled;
746
758
 
747
759
  const babelify = (code, options) => {
748
- straceLog('BABEL()');
749
- if(doJSX) straceLog('==> WITH JSX');
750
- if(doTypes) straceLog('==> WITH TYPES');
751
- if(doDecorators) straceLog('==> WITH DECORATORS');
760
+ straceLog('!COMPILER babel()');
761
+ if(doJSX) straceLog('==> INFO !-WITH JSX');
762
+ if(doTypes) straceLog('==> INFO !-WITH Types');
763
+ if(doDecorators) straceLog('==> INFO !-WITH DECORATORS');
752
764
  return babel.transformSync(code, {
753
765
  presets: [
754
766
  ...(doJSX ? [[babelReact, { throwIfNamespace: false, pragmaFrag: options.jsxPragmaFrag || execOptions.jsxPragmaFrag, pragma: options.jsxPragma || execOptions.jsxPragma }]] : [])
@@ -782,7 +794,7 @@ const cpl = (module.exports.compile = function (file, options = {}) {
782
794
  });
783
795
 
784
796
  module.exports.compileFile = function (filepath, options = {}) {
785
- straceLog('COMPILE() for CURRENTFILE');
797
+ straceLog('compile(currentFile)');
786
798
  const f = typeof filepath == "object" ? filepath : getFile(filepath);
787
799
  if(typeof filepath == "object") filepath = filepath.path;
788
800
  let qrew = false;
@@ -791,13 +803,13 @@ module.exports.compileFile = function (filepath, options = {}) {
791
803
  qrew = true
792
804
  f.content = from_qrew(readFileSync(f.path), options.package || findAppInfo(filepath)?.config.manifest.package || path.basename(filepath).split('.').slice(0, -1).join('.')).toString();
793
805
  options.type = f.content.split('\n')[0]?.match(/"initFile (.+)"/)?.[1]?.split('.').pop();
794
- straceLog('QREW_DECODE() as', options.type, 'for CURRENTFILE');
806
+ straceLog('decodeCrew(currentFile).as(', `"${options.type}"`,')');
795
807
  }
796
808
 
797
809
  let compiled_code = cpl(f, { ...options });
798
810
 
799
811
  if(options.onlyCompile && !qrew){
800
- straceLog('WRITE_AND_QUIT() for COMPILEDATA');
812
+ straceLog('writeAndQuit(compileData)');
801
813
  if(compiled_code instanceof Promise){
802
814
  compiled_code.then((r) => {
803
815
  console.log(r);
@@ -25,7 +25,7 @@ module.exports.prepareContext = function (
25
25
  filepath = "",
26
26
  runPath = () => {},
27
27
  ) {
28
- straceLog('PREPARE() NEW CONTEXT');
28
+ straceLog('!-NEW context()');
29
29
  if (mainFile == "") mainFile = filepath;
30
30
  /** @type {Record<string, any>} */
31
31
  let context = {
@@ -82,7 +82,7 @@ module.exports.prepareContext = function (
82
82
  this['@cb'] = cb;
83
83
  }
84
84
  });
85
- }, out: {...process.stdout, cols: process.stdout.columns, rows: process.stdout.rows, put: (...logs) => context.print(...logs), strace: (...logs) => straceLog('==> STRACE_OUT():', ...logs ), write: (logs) => context.printf(logs+'\n') }, in: {...process.stdin, read: (...args) => context.input(...args)}, define: (name, object) => {
85
+ }, out: {...process.stdout, cols: process.stdout.columns, rows: process.stdout.rows, put: (...logs) => context.print(...logs), strace: (...logs) => straceLog('==> !+OUT straceOut(): ', ...logs ), write: (logs) => context.printf(logs+'\n') }, in: {...process.stdin, read: (...args) => context.input(...args)}, define: (name, object) => {
86
86
  if(Array.isArray(name) && name.length == 2 && typeof name[0] == 'string'){
87
87
  object = name[1];
88
88
  name = name[0];
@@ -216,7 +216,7 @@ module.exports.prepareContext = function (
216
216
  try {
217
217
  if (package.startsWith("node:") || package.startsWith("pkg:"))
218
218
  throw new Error("");
219
- return context.imp(package, asserts);
219
+ return context.imp(package, asserts, true);
220
220
  } catch (e) {
221
221
  if(e.message.match('Module') && e.message.match('not found')){
222
222
  let pname = package.startsWith("pkg:") ? package.split("pkg:")[1] : package;
@@ -242,14 +242,14 @@ module.exports.prepareContext = function (
242
242
  if(context.app?.config?.exec?.['auto import']){
243
243
  const autoipath = path.join(context.app.path, context.app.config?.exec?.['auto import']);
244
244
  if(autoipath !== filepath){
245
- straceLog('==> AUTOIMPORT()', autoipath);
245
+ straceLog('==> !ACTION autoImport()', autoipath);
246
246
  const all = context.imp(path.relative(path.dirname(filepath), autoipath));
247
247
  for(let i in all) context[i] = all[i];
248
248
  }
249
249
  }
250
250
 
251
251
  if(!context.app){
252
- straceLog('==> APP NOT FOUND');
252
+ straceLog('==> WARN: App not found');
253
253
  context.appPackage = (packageName) => context.app = { config: { manifest: { package: packageName } } }
254
254
  } else {
255
255
  context.appPackage = context.mod = (packageName) => context.module.modset = packageName;
@@ -265,7 +265,7 @@ module.exports.prepareContext = function (
265
265
  context.module.main ||
266
266
  (options.fromMain == true && options.as == "main")
267
267
  ) {
268
- straceLog('==> RUNTIME() as MAIN for', filepath);
268
+ straceLog('==> !MAIN', filepath);
269
269
  context.opt = {
270
270
  set: (key, value) => (execOptions[key] = value),
271
271
  get: (key) => execOptions[key],
@@ -21,17 +21,17 @@ module.exports.runPath = function runPath(filepath, options = {}, custom_context
21
21
  if(filepath.endsWith('.coffee')) options.type = 'coffee';
22
22
  if(filepath.endsWith('.qrew')) options.type = 'qrew';
23
23
 
24
- straceLog('RUN() CURRENTFILE = ', filepath, 'as', options.type || 'UNKNOWN');
24
+ straceLog('!+DECLARE currentFile =', filepath, 'as', options.type || '!UNKNOWN');
25
25
 
26
26
  if(options.import?.async) options.async = true;
27
- if(options.async) straceLog('ASYNCMODE() for CURRENTFILE');
27
+ if(options.async) straceLog('==> !ASYNCMODE currentFile::async()');
28
28
  let { compiled_code, file } = compileFile(options.code ? { content: options.code, path: filepath } : filepath, options);
29
- straceLog('COMPILE_DONE() with COMPILEDATA');
29
+ straceLog('==> INFO Compile done with compileData');
30
30
  // context.module.compiled = compiled_code;
31
31
  // context.process.exit = (int) => process.exit(int);
32
32
 
33
33
  const doCode = () => {
34
- straceLog('RUNCODE() COMPILEDATA');
34
+ straceLog('runCode(compileData)');
35
35
  const context = options.import?.takeThisContext ? custom_context : prepareContext(custom_context, options, file.path, runPath);
36
36
 
37
37
  if(context.app){
@@ -51,6 +51,7 @@ module.exports.runPath = function runPath(filepath, options = {}, custom_context
51
51
  let execd = exec(compiled_code, context, file.content);
52
52
 
53
53
  if(context.module.main && (context.module.exports?.main || (typeof context.module.exports == "function" && context.module.exports.name == 'main'))){
54
+ straceLog('call(currentFile::mainFn)');
54
55
  const mainFn = context.module.exports.main ?? context.module.exports;
55
56
  let ctx = context;
56
57
  if(mainFn._class){
@@ -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.4",
3
+ "version": "1.5.7",
4
4
  "description": "A simple coffescript runtime and app manager",
5
5
  "main": "main.js",
6
6
  "directories": {