├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── lib.db.js ├── npm_scripts.sh ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.00 2 | *.01 3 | *.lock 4 | *.log 5 | *~ 6 | .* 7 | core 8 | data 9 | electron-lite 10 | example.js 11 | external 12 | package-lock.json 13 | node_modules 14 | tmp 15 | utility2 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | # init xvfb 3 | # http://docs.travis-ci.com/user/gui-and-headless-browsers/ 4 | - export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start 5 | # override $NODE_VERSION 6 | - CI_COMMIT_MESSAGE="$(git log -1 --pretty=%s)" && if (printf "$CI_COMMIT_MESSAGE" | grep -qE "\bNODE_VERSION="); then nvm install "$(printf "$CI_COMMIT_MESSAGE" | sed -e "s/.*\bNODE_VERSION=//" -e "s/ .*//")" && node --version && npm --version; fi 7 | branches: 8 | only: 9 | - /^alpha$/ 10 | - /^beta$/ 11 | - /^benchmark\..*/ 12 | - /^cron$/ 13 | - /^docker\..*/ 14 | - /^master$/ 15 | - /^publish$/ 16 | - /^task$/ 17 | cache: 18 | npm: false 19 | env: 20 | global: 21 | # this value is auto-created by shTravisCryptoAesEncryptYml 22 | - secure: F26poEnNolgqaqfx1w6TnDmH9mD3rUIPSZO6dGAAx2SZgb1ImuV1LWcI39Ke+X/Irn1Nzn+uBA3b3aNBYzjdbXWRPH1CnDLr6bNRRTTyaGEaCn72FqR97CDab0jrPFbi7V2g2Fl8RMes8cmAE68qeboBKPtMMttLs3vXEFPM6+SGhSw2f6GXu3g4QFuDcanSIKnvCV0rwSjcFUHm64SG3hkvxrZ0Zd0zUtIH/uZBJ5t6nBZXcX48XQWFSBSrwIW/IUSvIxrK1z5ybPbW+1P3tsAAXt9EuyDq/CMbgKVTZjAXPCEJ7tlsDZNHQxWC3gAFc/e88S+QTIrEDu1LBGZrZHxCEpxry9LyKyYxI5uR9oHGSr5w2aNAMYPJ2ZMBuwmJwdZtPL0LISBaxfdTpbVQ1YEbW3LJz1tuH/sOLUqGz/uvGAVPBEPDublObOmsRqYAPo2TlvHWwf9KdgCkxwGkZW9T9oh0HczKAq3sWquoW7dAawSxuP5aotgasahYnuicq5oxN3dlqEWkCqOsuO116KjnDfE+mYslr07yelYHBDBt0IwwDxPzOAMqIj42nFaSVbkMQPY4dPEgoFXF5vulYNKsUR5qW36QfEfFaj8rktHwyYEHsIOERAYwT7rfDnSgeUqcbzd2UTx+0u24MpAo7JEGClA6mzP1atsRzjVPk+U= # CRYPTO_AES_KEY 23 | language: 24 | - node_js 25 | node_js: 26 | # - 8 27 | - node 28 | script: 29 | - npm run build-ci 30 | services: 31 | - docker 32 | # http://docs.travis-ci.com/user/workers/container-based-infrastructure 33 | sudo: 34 | false 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License (https://opensource.org/licenses/MIT) 2 | 3 | Copyright 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # db-lite 2 | this zero-dependency package will provide a persistent, in-browser database, with a working web-demo 3 | 4 | # live web demo 5 | - [https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app) 6 | 7 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png)](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app) 8 | 9 | 10 | 11 | [![travis-ci.org build-status](https://api.travis-ci.org/kaizhu256/node-db-lite.svg)](https://travis-ci.org/kaizhu256/node-db-lite) [![coverage](https://kaizhu256.github.io/node-db-lite/build/coverage.badge.svg)](https://kaizhu256.github.io/node-db-lite/build/coverage.html/index.html) 12 | 13 | [![NPM](https://nodei.co/npm/db-lite.png?downloads=true)](https://www.npmjs.com/package/db-lite) 14 | 15 | [![build commit status](https://kaizhu256.github.io/node-db-lite/build/build.badge.svg)](https://travis-ci.org/kaizhu256/node-db-lite) 16 | 17 | | git-branch : | [master](https://github.com/kaizhu256/node-db-lite/tree/master) | [beta](https://github.com/kaizhu256/node-db-lite/tree/beta) | [alpha](https://github.com/kaizhu256/node-db-lite/tree/alpha)| 18 | |--:|:--|:--|:--| 19 | | test-server-github : | [![github.com test-server](https://kaizhu256.github.io/node-db-lite/GitHub-Mark-32px.png)](https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/app) | [![github.com test-server](https://kaizhu256.github.io/node-db-lite/GitHub-Mark-32px.png)](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app) | [![github.com test-server](https://kaizhu256.github.io/node-db-lite/GitHub-Mark-32px.png)](https://kaizhu256.github.io/node-db-lite/build..alpha..travis-ci.org/app)| 20 | | test-server-heroku : | [![heroku.com test-server](https://kaizhu256.github.io/node-db-lite/heroku-logo.75x25.png)](https://h1-db-master.herokuapp.com) | [![heroku.com test-server](https://kaizhu256.github.io/node-db-lite/heroku-logo.75x25.png)](https://h1-db-beta.herokuapp.com) | [![heroku.com test-server](https://kaizhu256.github.io/node-db-lite/heroku-logo.75x25.png)](https://h1-db-alpha.herokuapp.com)| 21 | | test-report : | [![test-report](https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/test-report.badge.svg)](https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/test-report.html) | [![test-report](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/test-report.badge.svg)](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/test-report.html) | [![test-report](https://kaizhu256.github.io/node-db-lite/build..alpha..travis-ci.org/test-report.badge.svg)](https://kaizhu256.github.io/node-db-lite/build..alpha..travis-ci.org/test-report.html)| 22 | | coverage : | [![coverage](https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/coverage.badge.svg)](https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/coverage.html/index.html) | [![coverage](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/coverage.badge.svg)](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/coverage.html/index.html) | [![coverage](https://kaizhu256.github.io/node-db-lite/build..alpha..travis-ci.org/coverage.badge.svg)](https://kaizhu256.github.io/node-db-lite/build..alpha..travis-ci.org/coverage.html/index.html)| 23 | | build-artifacts : | [![build-artifacts](https://kaizhu256.github.io/node-db-lite/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-db-lite/tree/gh-pages/build..master..travis-ci.org) | [![build-artifacts](https://kaizhu256.github.io/node-db-lite/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-db-lite/tree/gh-pages/build..beta..travis-ci.org) | [![build-artifacts](https://kaizhu256.github.io/node-db-lite/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-db-lite/tree/gh-pages/build..alpha..travis-ci.org)| 24 | 25 | [![npmPackageListing](https://kaizhu256.github.io/node-db-lite/build/screenshot.npmPackageListing.svg)](https://github.com/kaizhu256/node-db-lite) 26 | 27 | ![npmPackageDependencyTree](https://kaizhu256.github.io/node-db-lite/build/screenshot.npmPackageDependencyTree.svg) 28 | 29 | 30 | 31 | # table of contents 32 | 1. [cdn download](#cdn-download) 33 | 1. [documentation](#documentation) 34 | 1. [quickstart standalone app](#quickstart-standalone-app) 35 | 1. [quickstart example.js](#quickstart-examplejs) 36 | 1. [extra screenshots](#extra-screenshots) 37 | 1. [package.json](#packagejson) 38 | 1. [changelog of last 50 commits](#changelog-of-last-50-commits) 39 | 1. [internal build script](#internal-build-script) 40 | 1. [misc](#misc) 41 | 42 | 43 | 44 | # cdn download 45 | - [https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app/assets.db.js](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app/assets.db.js) 46 | 47 | 48 | 49 | # documentation 50 | #### cli help 51 | ![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.npmPackageCliHelp.svg) 52 | 53 | #### api doc 54 | - [https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/apidoc.html](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/apidoc.html) 55 | 56 | [![apidoc](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fapidoc.html.png)](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/apidoc.html) 57 | 58 | #### todo 59 | - fix IndexedDB in edge 60 | - deprecate and remove unnecessary stable-sort 61 | - none 62 | 63 | #### changelog 2019.8.20 64 | - npm publish 2019.8.20 65 | - fix demo 66 | - jslint - remove allow-method-chain-newline hack 67 | - jslint - refactor files to 80 chr column-limit 68 | - rename var request to req, response to res, local.errorDefault to local.errDefault, error to err, option to opt, event to evt, nextMiddleware to next 69 | - revamp ui event-handling with window.domOnEventDelegateDict 70 | - update build 71 | - none 72 | 73 | #### this package requires 74 | - darwin or linux os 75 | 76 | 77 | 78 | # quickstart standalone app 79 | #### to run this example, follow instruction in script below 80 | - [assets.app.js](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app/assets.app.js) 81 | ```shell 82 | # example.sh 83 | 84 | # this shell script will download and run a web-demo of db-lite as a standalone app 85 | 86 | # 1. download standalone app 87 | curl -O https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/app/assets.app.js 88 | # 2. run standalone app 89 | PORT=8081 node ./assets.app.js 90 | # 3. open a browser to http://127.0.0.1:8081 and play with web-demo 91 | # 4. edit file assets.app.js to suit your needs 92 | ``` 93 | 94 | #### output from browser 95 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleSh.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/app/assets.example.html) 96 | 97 | #### output from shell 98 | ![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleSh.svg) 99 | 100 | 101 | 102 | # quickstart example.js 103 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/app/assets.example.html) 104 | 105 | #### to run this example, follow instruction in script below 106 | - [example.js](https://kaizhu256.github.io/node-db-lite/build..beta..travis-ci.org/example.js) 107 | ```javascript 108 | /* 109 | example.js 110 | 111 | this script will run a web-demo of db-lite 112 | 113 | instruction 114 | 1. save this script as example.js 115 | 2. run shell-command: 116 | $ npm install db-lite && PORT=8081 node example.js 117 | 3. open a browser to http://127.0.0.1:8081 and play with web-demo 118 | 4. edit this script to suit your needs 119 | */ 120 | 121 | 122 | 123 | /* istanbul instrument in package db */ 124 | /* istanbul ignore next */ 125 | /* jslint utility2:true */ 126 | (function (globalThis) { 127 | "use strict"; 128 | var consoleError; 129 | var local; 130 | // init globalThis 131 | (function () { 132 | try { 133 | globalThis = Function("return this")(); // jslint ignore:line 134 | } catch (ignore) {} 135 | }()); 136 | globalThis.globalThis = globalThis; 137 | // init debug_inline 138 | if (!globalThis["debug\u0049nline"]) { 139 | consoleError = console.error; 140 | globalThis["debug\u0049nline"] = function () { 141 | /* 142 | * this function will both print to stderr 143 | * and return [0] 144 | */ 145 | var argList; 146 | argList = Array.from(arguments); // jslint ignore:line 147 | // debug arguments 148 | globalThis["debug\u0049nlineArguments"] = argList; 149 | consoleError("\n\ndebug\u0049nline"); 150 | consoleError.apply(console, argList); 151 | consoleError("\n"); 152 | // return arg0 for inspection 153 | return argList[0]; 154 | }; 155 | } 156 | // init local 157 | local = {}; 158 | local.local = local; 159 | globalThis.globalLocal = local; 160 | // init isBrowser 161 | local.isBrowser = ( 162 | typeof window === "object" 163 | && window === globalThis 164 | && typeof window.XMLHttpRequest === "function" 165 | && window.document 166 | && typeof window.document.querySelector === "function" 167 | ); 168 | // init function 169 | local.assertThrow = function (passed, message) { 170 | /* 171 | * this function will throw err. if is falsy 172 | */ 173 | var err; 174 | if (passed) { 175 | return; 176 | } 177 | err = ( 178 | // ternary-operator 179 | ( 180 | message 181 | && typeof message.message === "string" 182 | && typeof message.stack === "string" 183 | ) 184 | // if message is errObj, then leave as is 185 | ? message 186 | : new Error( 187 | typeof message === "string" 188 | // if message is a string, then leave as is 189 | ? message 190 | // else JSON.stringify message 191 | : JSON.stringify(message, null, 4) 192 | ) 193 | ); 194 | throw err; 195 | }; 196 | local.functionOrNop = function (fnc) { 197 | /* 198 | * this function will if exists, 199 | * them return , 200 | * else return 201 | */ 202 | return fnc || local.nop; 203 | }; 204 | local.identity = function (value) { 205 | /* 206 | * this function will return 207 | */ 208 | return value; 209 | }; 210 | local.nop = function () { 211 | /* 212 | * this function will do nothing 213 | */ 214 | return; 215 | }; 216 | local.objectAssignDefault = function (target, source) { 217 | /* 218 | * this function will if items from are 219 | * null, undefined, or empty-string, 220 | * then overwrite them with items from 221 | */ 222 | target = target || {}; 223 | Object.keys(source || {}).forEach(function (key) { 224 | if ( 225 | target[key] === null 226 | || target[key] === undefined 227 | || target[key] === "" 228 | ) { 229 | target[key] = target[key] || source[key]; 230 | } 231 | }); 232 | return target; 233 | }; 234 | // require builtin 235 | if (!local.isBrowser) { 236 | local.assert = require("assert"); 237 | local.buffer = require("buffer"); 238 | local.child_process = require("child_process"); 239 | local.cluster = require("cluster"); 240 | local.crypto = require("crypto"); 241 | local.dgram = require("dgram"); 242 | local.dns = require("dns"); 243 | local.domain = require("domain"); 244 | local.events = require("events"); 245 | local.fs = require("fs"); 246 | local.http = require("http"); 247 | local.https = require("https"); 248 | local.net = require("net"); 249 | local.os = require("os"); 250 | local.path = require("path"); 251 | local.querystring = require("querystring"); 252 | local.readline = require("readline"); 253 | local.repl = require("repl"); 254 | local.stream = require("stream"); 255 | local.string_decoder = require("string_decoder"); 256 | local.timers = require("timers"); 257 | local.tls = require("tls"); 258 | local.tty = require("tty"); 259 | local.url = require("url"); 260 | local.util = require("util"); 261 | local.vm = require("vm"); 262 | local.zlib = require("zlib"); 263 | } 264 | }(this)); 265 | 266 | 267 | 268 | (function (local) { 269 | "use strict"; 270 | 271 | 272 | 273 | // run shared js-env code - init-before 274 | (function () { 275 | // init local 276 | local = ( 277 | globalThis.utility2_rollup 278 | || globalThis.utility2_db 279 | || require("db-lite") 280 | ); 281 | // init exports 282 | globalThis.local = local; 283 | }()); 284 | 285 | 286 | 287 | /* istanbul ignore next */ 288 | // run browser js-env code - init-test 289 | (function () { 290 | if (!local.isBrowser) { 291 | return; 292 | } 293 | // log stderr and stdout to #outputStdout1 294 | ["error", "log"].forEach(function (key) { 295 | var argList; 296 | var elem; 297 | var fnc; 298 | elem = document.querySelector( 299 | "#outputStdout1" 300 | ); 301 | if (!elem) { 302 | return; 303 | } 304 | fnc = console[key]; 305 | console[key] = function () { 306 | argList = Array.from(arguments); // jslint ignore:line 307 | fnc.apply(console, argList); 308 | // append text to #outputStdout1 309 | elem.textContent += argList.map(function (arg) { 310 | return ( 311 | typeof arg === "string" 312 | ? arg 313 | : JSON.stringify(arg, null, 4) 314 | ); 315 | }).join(" ").replace(( 316 | /\u001b\[\d*m/g 317 | ), "") + "\n"; 318 | // scroll textarea to bottom 319 | elem.scrollTop = elem.scrollHeight; 320 | }; 321 | }); 322 | local.objectAssignDefault(local, globalThis.domOnEventDelegateDict); 323 | globalThis.domOnEventDelegateDict = local; 324 | local.onEventDomDb = local.db && local.db.onEventDomDb; 325 | local.testRunBrowser = function (evt) { 326 | /* 327 | * this function will run browser-tests 328 | */ 329 | switch ( 330 | !evt.ctrlKey 331 | && !evt.metaKey 332 | && ( 333 | evt.modeInit 334 | || (evt.type + "." + (evt.target && evt.target.id)) 335 | ) 336 | ) { 337 | // custom-case 338 | case "dbExportButton1": 339 | case "dbImportButton1": 340 | case "dbImportInput1": 341 | case "dbResetButton1": 342 | local.db.onEventDomDb(event); 343 | break; 344 | case "click.buttonEval1": 345 | case true: 346 | // try to eval input-code 347 | globalThis.domOnEventDelegateDict.domOnEventResetOutput(); 348 | try { 349 | eval( // jslint ignore:line 350 | document.querySelector("#inputTextarea1").value 351 | ); 352 | } catch (errCaught) { 353 | console.error(errCaught); 354 | } 355 | break; 356 | // run browser-tests 357 | default: 358 | if ( 359 | (evt.target && evt.target.id) !== "testRunButton1" 360 | && !(evt.modeInit && ( 361 | /\bmodeTest=1\b/ 362 | ).test(location.search)) 363 | ) { 364 | return; 365 | } 366 | // show browser-tests 367 | if (document.querySelector( 368 | "#testReportDiv1" 369 | ).style.maxHeight === "0px") { 370 | globalThis.domOnEventDelegateDict.domOnEventResetOutput(); 371 | local.uiAnimateSlideDown(document.querySelector( 372 | "#testReportDiv1" 373 | )); 374 | document.querySelector( 375 | "#testRunButton1" 376 | ).textContent = "hide internal test"; 377 | local.modeTest = 1; 378 | local.testRunDefault(local); 379 | return; 380 | } 381 | // hide browser-tests 382 | local.uiAnimateSlideUp(document.querySelector( 383 | "#testReportDiv1" 384 | )); 385 | document.querySelector( 386 | "#testRunButton1" 387 | ).textContent = "run internal test"; 388 | } 389 | }; 390 | 391 | local.testRunBrowser({ 392 | modeInit: true 393 | }); 394 | }()); 395 | 396 | 397 | 398 | /* istanbul ignore next */ 399 | // run node js-env code - init-test 400 | (function () { 401 | if (local.isBrowser) { 402 | return; 403 | } 404 | // init exports 405 | module.exports = local; 406 | /* validateLineSortedReset */ 407 | // init assets 408 | local.assetsDict = local.assetsDict || {}; 409 | [ 410 | "assets.index.template.html", 411 | "assets.swgg.swagger.json", 412 | "assets.swgg.swagger.server.json" 413 | ].forEach(function (file) { 414 | file = "/" + file; 415 | local.assetsDict[file] = local.assetsDict[file] || ""; 416 | if (local.fs.existsSync(local.__dirname + file)) { 417 | local.assetsDict[file] = local.fs.readFileSync( 418 | local.__dirname + file, 419 | "utf8" 420 | ); 421 | } 422 | }); 423 | /* jslint ignore:start */ 424 | local.assetsDict["/assets.index.template.html"] = '\ 425 | \n\ 426 | \n\ 427 | \n\ 428 | \n\ 429 | \n\ 430 | \n\ 431 | {{env.npm_package_name}} ({{env.npm_package_version}})\n\ 432 | \n\ 553 | \n\ 554 | \n\ 555 |
\n\ 556 | \n\ 557 | \n\ 558 | \n\ 559 | \n\ 768 |

\n\ 769 | \n\ 777 | {{env.npm_package_name}} ({{env.npm_package_version}})\n\ 778 | \n\ 781 |

\n\ 782 |

{{env.npm_package_description}}

\n\ 783 | \n\ 788 | \n\ 789 | \n\ 790 | \n\ 791 |
\n\ 792 |
\n\ 793 |
\n\ 794 | \n\ 800 | \n\ 857 |
\n\ 858 | \n\ 859 |
\n\
 860 | \n\
 865 | \n\
 866 | \n\
 867 | \n\
 868 | \n\
 869 | \n\
 870 | \n\
 871 | \n\
 872 | \n\
 875 | 
\n\ 876 | [ this app was created with\n\ 877 | utility2\n\ 878 | ]\n\ 879 |
\n\ 880 | \n\ 881 | \n\ 882 | '; 883 | /* jslint ignore:end */ 884 | /* validateLineSortedReset */ 885 | /* jslint ignore:start */ 886 | local.assetsDict["/assets.db.js"] = 887 | local.assetsDict["/assets.db.js"] || 888 | local.fs.readFileSync(local.__dirname + "/lib.db.js", "utf8" 889 | ).replace((/^#!\//), "// "); 890 | /* jslint ignore:end */ 891 | /* validateLineSortedReset */ 892 | local.assetsDict["/"] = local.assetsDict[ 893 | "/assets.index.template.html" 894 | ].replace(( 895 | /\{\{env\.(\w+?)\}\}/g 896 | ), function (match0, match1) { 897 | switch (match1) { 898 | case "npm_package_description": 899 | return "the greatest app in the world!"; 900 | case "npm_package_name": 901 | return "db-lite"; 902 | case "npm_package_nameLib": 903 | return "db"; 904 | case "npm_package_version": 905 | return "0.0.1"; 906 | default: 907 | return match0; 908 | } 909 | }); 910 | local.assetsDict["/assets.example.html"] = local.assetsDict["/"]; 911 | local.assetsDict["/index.html"] = local.assetsDict["/"]; 912 | // init cli 913 | if (module !== require.main || globalThis.utility2_rollup) { 914 | return; 915 | } 916 | /* validateLineSortedReset */ 917 | local.assetsDict["/assets.example.js"] = ( 918 | local.assetsDict["/assets.example.js"] 919 | || local.fs.readFileSync(__filename, "utf8") 920 | ); 921 | local.assetsDict["/favicon.ico"] = local.assetsDict["/favicon.ico"] || ""; 922 | // if $npm_config_timeout_exit exists, 923 | // then exit this process after $npm_config_timeout_exit ms 924 | if (Number(process.env.npm_config_timeout_exit)) { 925 | setTimeout(process.exit, Number(process.env.npm_config_timeout_exit)); 926 | } 927 | // start server 928 | if (globalThis.utility2_serverHttp1) { 929 | return; 930 | } 931 | process.env.PORT = process.env.PORT || "8081"; 932 | console.error("http-server listening on port " + process.env.PORT); 933 | local.http.createServer(function (req, res) { 934 | req.urlParsed = local.url.parse(req.url); 935 | if (local.assetsDict[req.urlParsed.pathname] !== undefined) { 936 | res.end(local.assetsDict[req.urlParsed.pathname]); 937 | return; 938 | } 939 | res.statusCode = 404; 940 | res.end(); 941 | }).listen(process.env.PORT); 942 | }()); 943 | }()); 944 | ``` 945 | 946 | #### output from browser 947 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/app/assets.example.html) 948 | 949 | #### output from shell 950 | ![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.svg) 951 | 952 | 953 | 954 | # extra screenshots 955 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fapidoc.html.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fapidoc.html.png) 956 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fapidoc.html.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fapidoc.html.png) 957 | 958 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fcoverage.lib.html.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fcoverage.lib.html.png) 959 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fcoverage.lib.html.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Fcoverage.lib.html.png) 960 | 961 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Ftest-report.html.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Ftest-report.html.png) 962 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Ftest-report.html.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.buildCi.browser.%252Ftmp%252Fbuild%252Ftest-report.html.png) 963 | 964 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp%252Fassets.swgg.html.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp%252Fassets.swgg.html.png) 965 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp%252Fassets.swgg.html.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp%252Fassets.swgg.html.png) 966 | 967 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png) 968 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithub.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png) 969 | 970 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithubTest.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithubTest.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png) 971 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithubTest.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployGithubTest.browser.%252Fnode-db-lite%252Fbuild%252Fapp.png) 972 | 973 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252Fassets.swgg.html.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252Fassets.swgg.html.png) 974 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252Fassets.swgg.html.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252Fassets.swgg.html.png) 975 | 976 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252F.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252F.png) 977 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHeroku.browser.%252F.png) 978 | 979 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHerokuTest.browser.%252F.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHerokuTest.browser.%252F.png) 980 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHerokuTest.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.deployHerokuTest.browser.%252F.png) 981 | 982 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.npmTest.browser.%252F.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.npmTest.browser.%252F.png) 983 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.npmTest.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.npmTest.browser.%252F.png) 984 | 985 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.browser.%252F.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.browser.%252F.png) 986 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleJs.browser.%252F.png) 987 | 988 | 1. [https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleSh.browser.%252F.png](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleSh.browser.%252F.png) 989 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleSh.browser.%252F.png)](https://kaizhu256.github.io/node-db-lite/build/screenshot.testExampleSh.browser.%252F.png) 990 | 991 | 992 | 993 | # package.json 994 | ```json 995 | { 996 | "author": "kai zhu ", 997 | "bin": { 998 | "db-lite": "lib.db.js" 999 | }, 1000 | "description": "this zero-dependency package will provide a persistent, in-browser database, with a working web-demo", 1001 | "devDependencies": { 1002 | "electron-lite": "kaizhu256/node-electron-lite#alpha", 1003 | "utility2": "kaizhu256/node-utility2#alpha" 1004 | }, 1005 | "engines": { 1006 | "node": ">=10.0" 1007 | }, 1008 | "homepage": "https://github.com/kaizhu256/node-db-lite", 1009 | "keywords": [ 1010 | "database", 1011 | "embedded", 1012 | "indexeddb", 1013 | "nedb" 1014 | ], 1015 | "license": "MIT", 1016 | "main": "lib.db.js", 1017 | "name": "db-lite", 1018 | "nameAliasPublish": "esdb nanodb", 1019 | "nameLib": "db", 1020 | "nameOriginal": "db-lite", 1021 | "os": [ 1022 | "darwin", 1023 | "linux" 1024 | ], 1025 | "repository": { 1026 | "type": "git", 1027 | "url": "https://github.com/kaizhu256/node-db-lite.git" 1028 | }, 1029 | "scripts": { 1030 | "build-ci": "./npm_scripts.sh", 1031 | "env": "env", 1032 | "eval": "./npm_scripts.sh", 1033 | "heroku-postbuild": "./npm_scripts.sh", 1034 | "postinstall": "./npm_scripts.sh", 1035 | "start": "./npm_scripts.sh", 1036 | "test": "./npm_scripts.sh", 1037 | "utility2": "./npm_scripts.sh" 1038 | }, 1039 | "version": "2019.8.20" 1040 | } 1041 | ``` 1042 | 1043 | 1044 | 1045 | # changelog of last 50 commits 1046 | [![screenshot](https://kaizhu256.github.io/node-db-lite/build/screenshot.gitLog.svg)](https://github.com/kaizhu256/node-db-lite/commits) 1047 | 1048 | 1049 | 1050 | # internal build script 1051 | - build_ci.sh 1052 | ```shell 1053 | # build_ci.sh 1054 | 1055 | # this shell script will run the build for this package 1056 | 1057 | shBuildCiAfter () {(set -e 1058 | # shDeployCustom 1059 | shDeployGithub 1060 | shDeployHeroku 1061 | shReadmeTest example.sh 1062 | )} 1063 | 1064 | shBuildCiBefore () {(set -e 1065 | shNpmTestPublished 1066 | shReadmeTest example.js 1067 | )} 1068 | 1069 | # run shBuildCi 1070 | eval "$(utility2 source)" 1071 | shBuildCi 1072 | ``` 1073 | 1074 | 1075 | 1076 | # misc 1077 | - this package was created with [utility2](https://github.com/kaizhu256/node-utility2) 1078 | -------------------------------------------------------------------------------- /lib.db.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * lib.db.js (2019.8.20) 4 | * https://github.com/kaizhu256/node-db-lite 5 | * this zero-dependency package will provide a persistent, in-browser database, with a working web-demo 6 | * 7 | * browser example: 8 | 9 | 23 | * 24 | * node example: 25 | var db, dbTable1; 26 | utility2_db = require("./assets.db-lite.js"); 27 | dbTable1 = global.dbTable1 = utility2_db.dbTableCreateOne({ 28 | name: "dbTable1" 29 | }); 30 | dbTable1.idIndexCreate({ name: "field1" }); 31 | dbTable1.crudSetOneById({ field1: "hello", field2: "world" }); 32 | console.log(dbTable1.crudGetManyByQuery({ 33 | limit: Infinity, 34 | query: { field1: "hello" }, 35 | skip: 0, 36 | sort: [{ fieldName: 'field1', isDescending: false }] 37 | })); 38 | */ 39 | 40 | 41 | 42 | /* istanbul instrument in package db */ 43 | /* istanbul ignore next */ 44 | /* jslint utility2:true */ 45 | (function (globalThis) { 46 | "use strict"; 47 | var consoleError; 48 | var local; 49 | // init globalThis 50 | (function () { 51 | try { 52 | globalThis = Function("return this")(); // jslint ignore:line 53 | } catch (ignore) {} 54 | }()); 55 | globalThis.globalThis = globalThis; 56 | // init debug_inline 57 | if (!globalThis["debug\u0049nline"]) { 58 | consoleError = console.error; 59 | globalThis["debug\u0049nline"] = function () { 60 | /* 61 | * this function will both print to stderr 62 | * and return [0] 63 | */ 64 | var argList; 65 | argList = Array.from(arguments); // jslint ignore:line 66 | // debug arguments 67 | globalThis["debug\u0049nlineArguments"] = argList; 68 | consoleError("\n\ndebug\u0049nline"); 69 | consoleError.apply(console, argList); 70 | consoleError("\n"); 71 | // return arg0 for inspection 72 | return argList[0]; 73 | }; 74 | } 75 | // init local 76 | local = {}; 77 | local.local = local; 78 | globalThis.globalLocal = local; 79 | // init isBrowser 80 | local.isBrowser = ( 81 | typeof window === "object" 82 | && window === globalThis 83 | && typeof window.XMLHttpRequest === "function" 84 | && window.document 85 | && typeof window.document.querySelector === "function" 86 | ); 87 | // init function 88 | local.assertThrow = function (passed, message) { 89 | /* 90 | * this function will throw err. if is falsy 91 | */ 92 | var err; 93 | if (passed) { 94 | return; 95 | } 96 | err = ( 97 | // ternary-operator 98 | ( 99 | message 100 | && typeof message.message === "string" 101 | && typeof message.stack === "string" 102 | ) 103 | // if message is errObj, then leave as is 104 | ? message 105 | : new Error( 106 | typeof message === "string" 107 | // if message is a string, then leave as is 108 | ? message 109 | // else JSON.stringify message 110 | : JSON.stringify(message, null, 4) 111 | ) 112 | ); 113 | throw err; 114 | }; 115 | local.functionOrNop = function (fnc) { 116 | /* 117 | * this function will if exists, 118 | * them return , 119 | * else return 120 | */ 121 | return fnc || local.nop; 122 | }; 123 | local.identity = function (value) { 124 | /* 125 | * this function will return 126 | */ 127 | return value; 128 | }; 129 | local.nop = function () { 130 | /* 131 | * this function will do nothing 132 | */ 133 | return; 134 | }; 135 | local.objectAssignDefault = function (target, source) { 136 | /* 137 | * this function will if items from are 138 | * null, undefined, or empty-string, 139 | * then overwrite them with items from 140 | */ 141 | target = target || {}; 142 | Object.keys(source || {}).forEach(function (key) { 143 | if ( 144 | target[key] === null 145 | || target[key] === undefined 146 | || target[key] === "" 147 | ) { 148 | target[key] = target[key] || source[key]; 149 | } 150 | }); 151 | return target; 152 | }; 153 | // require builtin 154 | if (!local.isBrowser) { 155 | local.assert = require("assert"); 156 | local.buffer = require("buffer"); 157 | local.child_process = require("child_process"); 158 | local.cluster = require("cluster"); 159 | local.crypto = require("crypto"); 160 | local.dgram = require("dgram"); 161 | local.dns = require("dns"); 162 | local.domain = require("domain"); 163 | local.events = require("events"); 164 | local.fs = require("fs"); 165 | local.http = require("http"); 166 | local.https = require("https"); 167 | local.net = require("net"); 168 | local.os = require("os"); 169 | local.path = require("path"); 170 | local.querystring = require("querystring"); 171 | local.readline = require("readline"); 172 | local.repl = require("repl"); 173 | local.stream = require("stream"); 174 | local.string_decoder = require("string_decoder"); 175 | local.timers = require("timers"); 176 | local.tls = require("tls"); 177 | local.tty = require("tty"); 178 | local.url = require("url"); 179 | local.util = require("util"); 180 | local.vm = require("vm"); 181 | local.zlib = require("zlib"); 182 | } 183 | }(this)); 184 | 185 | 186 | 187 | (function (local) { 188 | "use strict"; 189 | 190 | 191 | 192 | /* istanbul ignore next */ 193 | // run shared js-env code - init-before 194 | (function () { 195 | // init local 196 | local = ( 197 | globalThis.utility2_rollup 198 | // || globalThis.utility2_rollup_old 199 | // || require("./assets.utility2.rollup.js") 200 | || globalThis.globalLocal 201 | ); 202 | // init exports 203 | if (local.isBrowser) { 204 | globalThis.utility2_db = local; 205 | } else { 206 | module.exports = local; 207 | module.exports.__dirname = __dirname; 208 | } 209 | // init lib main 210 | local.db = local; 211 | 212 | 213 | 214 | /* validateLineSortedReset */ 215 | local.cliRun = function (opt) { 216 | /* 217 | * this function will run the cli with given 218 | */ 219 | local.cliDict._eval = local.cliDict._eval || function () { 220 | /* 221 | * 222 | * will eval 223 | */ 224 | globalThis.local = local; 225 | local.vm.runInThisContext(process.argv[3]); 226 | }; 227 | local.cliDict["--eval"] = local.cliDict["--eval"] || local.cliDict._eval; 228 | local.cliDict["-e"] = local.cliDict["-e"] || local.cliDict._eval; 229 | local.cliDict._help = local.cliDict._help || function () { 230 | /* 231 | * 232 | * will print help 233 | */ 234 | var commandList; 235 | var file; 236 | var packageJson; 237 | var text; 238 | var textDict; 239 | commandList = [ 240 | { 241 | argList: " ...", 242 | description: "usage:", 243 | command: [ 244 | "" 245 | ] 246 | }, { 247 | argList: "'console.log(\"hello world\")'", 248 | description: "example:", 249 | command: [ 250 | "--eval" 251 | ] 252 | } 253 | ]; 254 | file = __filename.replace(( 255 | /.*\// 256 | ), ""); 257 | opt = Object.assign({}, opt); 258 | packageJson = require("./package.json"); 259 | // validate comment 260 | opt.rgxComment = opt.rgxComment || ( 261 | /\)\u0020\{\n(?:|\u0020{4})\/\*\n(?:\u0020|\u0020{5})\*((?:\u0020<[^>]*?>|\u0020\.\.\.)*?)\n(?:\u0020|\u0020{5})\*\u0020(will\u0020.*?\S)\n(?:\u0020|\u0020{5})\*\/\n(?:\u0020{4}|\u0020{8})\S/ 262 | ); 263 | textDict = {}; 264 | Object.keys(local.cliDict).sort().forEach(function (key, ii) { 265 | if (key[0] === "_" && key !== "_default") { 266 | return; 267 | } 268 | text = String(local.cliDict[key]); 269 | if (key === "_default") { 270 | key = ""; 271 | } 272 | textDict[text] = textDict[text] || (ii + 2); 273 | ii = textDict[text]; 274 | if (commandList[ii]) { 275 | commandList[ii].command.push(key); 276 | return; 277 | } 278 | try { 279 | commandList[ii] = opt.rgxComment.exec(text); 280 | commandList[ii] = { 281 | argList: (commandList[ii][1] || "").trim(), 282 | command: [ 283 | key 284 | ], 285 | description: commandList[ii][2] 286 | }; 287 | } catch (ignore) { 288 | local.assertThrow(null, new Error( 289 | "cliRun - cannot parse comment in COMMAND " 290 | + key 291 | + ":\nnew RegExp(" 292 | + JSON.stringify(opt.rgxComment.source) 293 | + ").exec(" + JSON.stringify(text).replace(( 294 | /\\\\/g 295 | ), "\u0000").replace(( 296 | /\\n/g 297 | ), "\\n\\\n").replace(( 298 | /\u0000/g 299 | ), "\\\\") + ");" 300 | )); 301 | } 302 | }); 303 | text = ""; 304 | text += packageJson.name + " (" + packageJson.version + ")\n\n"; 305 | text += commandList.filter(function (elem) { 306 | return elem; 307 | }).map(function (elem, ii) { 308 | elem.command = elem.command.filter(function (elem) { 309 | return elem; 310 | }); 311 | switch (ii) { 312 | case 0: 313 | case 1: 314 | elem.argList = [ 315 | elem.argList 316 | ]; 317 | break; 318 | default: 319 | elem.argList = elem.argList.split(" "); 320 | elem.description = ( 321 | "# COMMAND " 322 | + (elem.command[0] || "") + "\n# " 323 | + elem.description 324 | ); 325 | } 326 | return ( 327 | elem.description + "\n " + file 328 | + (" " + elem.command.sort().join("|") + " ").replace(( 329 | /^\u0020{4}$/ 330 | ), " ") 331 | + elem.argList.join(" ") 332 | ); 333 | }).join("\n\n"); 334 | console.log(text); 335 | }; 336 | local.cliDict["--help"] = local.cliDict["--help"] || local.cliDict._help; 337 | local.cliDict["-h"] = local.cliDict["-h"] || local.cliDict._help; 338 | local.cliDict._default = local.cliDict._default || local.cliDict._help; 339 | local.cliDict.help = local.cliDict.help || local.cliDict._help; 340 | local.cliDict._interactive = local.cliDict._interactive || function () { 341 | /* 342 | * 343 | * will start interactive-mode 344 | */ 345 | globalThis.local = local; 346 | local.identity(local.replStart || require("repl").start)({ 347 | useGlobal: true 348 | }); 349 | }; 350 | local.cliDict["--interactive"] = ( 351 | local.cliDict["--interactive"] 352 | || local.cliDict._interactive 353 | ); 354 | local.cliDict["-i"] = local.cliDict["-i"] || local.cliDict._interactive; 355 | local.cliDict._version = local.cliDict._version || function () { 356 | /* 357 | * 358 | * will print version 359 | */ 360 | console.log(require(__dirname + "/package.json").version); 361 | }; 362 | local.cliDict["--version"] = ( 363 | local.cliDict["--version"] 364 | || local.cliDict._version 365 | ); 366 | local.cliDict["-v"] = local.cliDict["-v"] || local.cliDict._version; 367 | // default to --help command if no arguments are given 368 | if (process.argv.length <= 2) { 369 | local.cliDict._help(); 370 | return; 371 | } 372 | if (local.cliDict[process.argv[2]]) { 373 | local.cliDict[process.argv[2]](); 374 | return; 375 | } 376 | local.cliDict._default(); 377 | }; 378 | 379 | local.jsonCopy = function (obj) { 380 | /* 381 | * this function will deep-copy obj 382 | */ 383 | return ( 384 | obj === undefined 385 | ? undefined 386 | : JSON.parse(JSON.stringify(obj)) 387 | ); 388 | }; 389 | 390 | local.jsonStringifyOrdered = function (obj, replacer, space) { 391 | /* 392 | * this function will JSON.stringify , 393 | * with object-keys sorted and circular-references removed 394 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Syntax 395 | */ 396 | var circularSet; 397 | var stringify; 398 | var tmp; 399 | stringify = function (obj) { 400 | /* 401 | * this function will recursively JSON.stringify obj, 402 | * with object-keys sorted and circular-references removed 403 | */ 404 | // if obj is not an object or function, then JSON.stringify as normal 405 | if (!( 406 | obj 407 | && typeof obj === "object" 408 | && typeof obj.toJSON !== "function" 409 | )) { 410 | return JSON.stringify(obj); 411 | } 412 | // ignore circular-reference 413 | if (circularSet.has(obj)) { 414 | return; 415 | } 416 | circularSet.add(obj); 417 | // if obj is an array, then recurse its items 418 | if (Array.isArray(obj)) { 419 | tmp = "[" + obj.map(function (obj) { 420 | // recurse 421 | tmp = stringify(obj); 422 | return ( 423 | typeof tmp === "string" 424 | ? tmp 425 | : "null" 426 | ); 427 | }).join(",") + "]"; 428 | circularSet.delete(obj); 429 | return tmp; 430 | } 431 | // if obj is not an array, 432 | // then recurse its items with object-keys sorted 433 | tmp = "{" + Object.keys(obj).sort().map(function (key) { 434 | // recurse 435 | tmp = stringify(obj[key]); 436 | if (typeof tmp === "string") { 437 | return JSON.stringify(key) + ":" + tmp; 438 | } 439 | }).filter(function (obj) { 440 | return typeof obj === "string"; 441 | }).join(",") + "}"; 442 | circularSet.delete(obj); 443 | return tmp; 444 | }; 445 | circularSet = new Set(); 446 | return JSON.stringify(( 447 | (typeof obj === "object" && obj) 448 | // recurse 449 | ? JSON.parse(stringify(obj)) 450 | : obj 451 | ), replacer, space); 452 | }; 453 | 454 | local.listShuffle = function (list) { 455 | /* 456 | * this function will inplace shuffle using fisher-yates algorithm 457 | * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 458 | */ 459 | var ii; 460 | var random; 461 | var swap; 462 | ii = list.length; 463 | while (ii > 1) { 464 | ii -= 1; 465 | random = Math.floor(Math.random() * (ii + 1)); 466 | swap = list[ii]; 467 | list[ii] = list[random]; 468 | list[random] = swap; 469 | } 470 | return list; 471 | }; 472 | 473 | local.objectSetOverride = function (dict, overrides, depth, env) { 474 | /* 475 | * this function will recursively set overrides for items in dict 476 | */ 477 | dict = dict || {}; 478 | env = env || (typeof process === "object" && process.env) || {}; 479 | overrides = overrides || {}; 480 | Object.keys(overrides).forEach(function (key) { 481 | var dict2; 482 | var overrides2; 483 | dict2 = dict[key]; 484 | overrides2 = overrides[key]; 485 | if (overrides2 === undefined) { 486 | return; 487 | } 488 | // if both dict2 and overrides2 are non-null and non-array objects, 489 | // then recurse with dict2 and overrides2 490 | if ( 491 | depth > 1 492 | // dict2 is a non-null and non-array object 493 | && typeof dict2 === "object" && dict2 && !Array.isArray(dict2) 494 | // overrides2 is a non-null and non-array object 495 | && typeof overrides2 === "object" && overrides2 496 | && !Array.isArray(overrides2) 497 | ) { 498 | local.objectSetOverride(dict2, overrides2, depth - 1, env); 499 | return; 500 | } 501 | // else set dict[key] with overrides[key] 502 | dict[key] = ( 503 | dict === env 504 | // if dict is env, then overrides falsy-value with empty-string 505 | ? overrides2 || "" 506 | : overrides2 507 | ); 508 | }); 509 | return dict; 510 | }; 511 | 512 | local.onErrorDefault = function (err) { 513 | /* 514 | * this function will if exists, then print it to stderr 515 | */ 516 | if (err) { 517 | console.error(err); 518 | } 519 | return err; 520 | }; 521 | 522 | local.onErrorWithStack = function (onError) { 523 | /* 524 | * this function will create wrapper around 525 | * that will append current-stack to err.stack 526 | */ 527 | var onError2; 528 | var stack; 529 | stack = new Error().stack.replace(( 530 | /(.*?)\n.*?$/m 531 | ), "$1"); 532 | onError2 = function (err, data, meta) { 533 | // append current-stack to err.stack 534 | if ( 535 | err 536 | && typeof err.stack === "string" 537 | && err !== local.errDefault 538 | && String(err.stack).indexOf(stack.split("\n")[2]) < 0 539 | ) { 540 | err.stack += "\n" + stack; 541 | } 542 | onError(err, data, meta); 543 | }; 544 | // debug onError 545 | onError2.toString = function () { 546 | return String(onError); 547 | }; 548 | return onError2; 549 | }; 550 | 551 | local.onParallel = function (onError, onEach, onRetry) { 552 | /* 553 | * this function will create a function that will 554 | * 1. run async tasks in parallel 555 | * 2. if counter === 0 or err occurred, then call onError(err) 556 | */ 557 | var onParallel; 558 | onError = local.onErrorWithStack(onError); 559 | onEach = onEach || local.nop; 560 | onRetry = onRetry || local.nop; 561 | onParallel = function (err, data) { 562 | if (onRetry(err, data)) { 563 | return; 564 | } 565 | // decrement counter 566 | onParallel.counter -= 1; 567 | // validate counter 568 | if (!(onParallel.counter >= 0 || err || onParallel.err)) { 569 | err = new Error( 570 | "invalid onParallel.counter = " + onParallel.counter 571 | ); 572 | // ensure onError is run only once 573 | } else if (onParallel.counter < 0) { 574 | return; 575 | } 576 | // handle err 577 | if (err) { 578 | onParallel.err = err; 579 | // ensure counter <= 0 580 | onParallel.counter = -Math.abs(onParallel.counter); 581 | } 582 | // call onError when isDone 583 | if (onParallel.counter <= 0) { 584 | onError(err, data); 585 | return; 586 | } 587 | onEach(); 588 | }; 589 | // init counter 590 | onParallel.counter = 0; 591 | // return callback 592 | return onParallel; 593 | }; 594 | 595 | local.replStart = function () { 596 | /* 597 | * this function will start the repl-debugger 598 | */ 599 | var that; 600 | if (globalThis.utility2_repl1) { 601 | return; 602 | } 603 | // start repl 604 | that = require("repl").start({ 605 | useGlobal: true 606 | }); 607 | globalThis.utility2_repl1 = that; 608 | that.onError = function (err) { 609 | /* 610 | * this function will debug repl-err 611 | */ 612 | globalThis.utility2_debugReplError = err; 613 | console.error(err); 614 | }; 615 | // save eval-function 616 | that.evalDefault = that.eval; 617 | // hook custom-eval-function 618 | that.eval = function (script, context, file, onError) { 619 | var onError2; 620 | onError2 = function (err, data) { 621 | // debug err 622 | globalThis.utility2_debugReplError = ( 623 | err || globalThis.utility2_debugReplError 624 | ); 625 | onError(err, data); 626 | }; 627 | script.replace(( 628 | /^(\S+)\u0020(.*?)\n/ 629 | ), function (ignore, match1, match2) { 630 | switch (match1) { 631 | // syntax-sugar - run async shell-command 632 | case "$": 633 | switch (match2) { 634 | // syntax-sugar - run git diff 635 | case "git diff": 636 | match2 = "git diff --color | cat"; 637 | break; 638 | // syntax-sugar - run git log 639 | case "git log": 640 | match2 = "git log -n 4 | cat"; 641 | break; 642 | // syntax-sugar - run git log 643 | case "ll": 644 | match2 = "ls -Fal"; 645 | break; 646 | } 647 | // source lib.utility2.sh 648 | if ( 649 | process.platform !== "win32" 650 | && process.env.npm_config_dir_utility2 && (match2 !== ":") 651 | ) { 652 | match2 = ( 653 | ". " + process.env.npm_config_dir_utility2 654 | + "/lib.utility2.sh;" + match2 655 | ); 656 | } 657 | // run async shell-command 658 | require("child_process").spawn(match2, { 659 | shell: true, 660 | stdio: [ 661 | "ignore", 1, 2 662 | ] 663 | // on shell exit, print return prompt 664 | }).on("exit", function (exitCode) { 665 | console.error("exit-code " + exitCode); 666 | that.evalDefault( 667 | "\n", 668 | context, 669 | file, 670 | onError2 671 | ); 672 | }); 673 | script = "\n"; 674 | break; 675 | // syntax-sugar - map text with charCodeAt 676 | case "charCode": 677 | console.error( 678 | match2.split("").map(function (chr) { 679 | return ( 680 | "\\u" 681 | + chr.charCodeAt(0).toString(16).padStart(4, 0) 682 | ); 683 | }).join("") 684 | ); 685 | script = "\n"; 686 | break; 687 | // syntax-sugar - sort chr 688 | case "charSort": 689 | console.error(JSON.stringify(match2.split("").sort().join(""))); 690 | script = "\n"; 691 | break; 692 | // syntax-sugar - grep current dir 693 | case "grep": 694 | // run async shell-command 695 | require("child_process").spawn(( 696 | "find . -type f | grep -v -E " 697 | /* jslint ignore:start */ 698 | + '"\ 699 | /\\.|~\$|(\\b|_)(\\.\\d|\ 700 | archive|artifact|\ 701 | bower_component|build|\ 702 | coverage|\ 703 | doc|\ 704 | external|\ 705 | fixture|\ 706 | git_module|\ 707 | jquery|\ 708 | log|\ 709 | min|misc|mock|\ 710 | node_module|\ 711 | raw|\rollup|\ 712 | swp|\ 713 | tmp|\ 714 | vendor)s{0,1}(\\b|_)\ 715 | " ' 716 | /* jslint ignore:end */ 717 | + "| tr \"\\n\" \"\\000\" | xargs -0 grep -HIin -E \"" 718 | + match2 + "\"" 719 | ), { 720 | shell: true, 721 | stdio: [ 722 | "ignore", 1, 2 723 | ] 724 | // on shell exit, print return prompt 725 | }).on("exit", function (exitCode) { 726 | console.error("exit-code " + exitCode); 727 | that.evalDefault( 728 | "\n", 729 | context, 730 | file, 731 | onError2 732 | ); 733 | }); 734 | script = "\n"; 735 | break; 736 | // syntax-sugar - list object's keys, sorted by item-type 737 | // console.error(Object.keys(global).map(function(key){return(typeof global[key]==='object'&&global[key]&&global[key]===global[key]?'global':typeof global[key])+' '+key;}).sort().join('\n')) // jslint ignore:line 738 | case "keys": 739 | script = ( 740 | "console.error(Object.keys(" + match2 741 | + ").map(function(key){return(" 742 | + "typeof " + match2 + "[key]==='object'&&" 743 | + match2 + "[key]&&" 744 | + match2 + "[key]===global[key]" 745 | + "?'global'" 746 | + ":typeof " + match2 + "[key]" 747 | + ")+' '+key;" 748 | + "}).sort().join('\\n'))\n" 749 | ); 750 | break; 751 | // syntax-sugar - print stringified arg 752 | case "print": 753 | script = "console.error(String(" + match2 + "))\n"; 754 | break; 755 | // syntax-sugar - read file 756 | case "readFile": 757 | try { 758 | console.error(JSON.stringify( 759 | require("fs").readFileSync(match2, "utf8") 760 | )); 761 | } catch (errCaught) { 762 | console.error(errCaught); 763 | } 764 | script = "\n"; 765 | break; 766 | } 767 | }); 768 | // eval the script 769 | that.evalDefault(script, context, file, onError2); 770 | }; 771 | that.socket = { 772 | end: local.nop, 773 | on: local.nop, 774 | write: local.nop 775 | }; 776 | // init process.stdout 777 | process.stdout._writeDefault = ( 778 | process.stdout._writeDefault 779 | || process.stdout._write 780 | ); 781 | process.stdout._write = function (chunk, encoding, callback) { 782 | process.stdout._writeDefault(chunk, encoding, callback); 783 | // hack-istanbul - ignore else-statement 784 | local.nop(that.socket.writable && (function () { 785 | that.socket.write(chunk, encoding); 786 | }())); 787 | }; 788 | // start serverRepl1 789 | globalThis.utility2_serverRepl1 = require("net").createServer(function ( 790 | socket 791 | ) { 792 | // init socket 793 | that.socket = socket; 794 | that.socket.on("data", that.write.bind(that)); 795 | that.socket.on("error", that.onError); 796 | that.socket.setKeepAlive(true); 797 | }); 798 | // hack-istanbul - ignore else-statement 799 | local.nop(process.env.PORT_REPL && (function () { 800 | console.error( 801 | "repl-server listening on port " + process.env.PORT_REPL 802 | ); 803 | globalThis.utility2_serverRepl1.listen(process.env.PORT_REPL); 804 | }())); 805 | }; 806 | 807 | local.setTimeoutOnError = function (onError, timeout, err, data) { 808 | /* 809 | * this function will after timeout has passed, 810 | * then call (, ) 811 | */ 812 | if (typeof onError === "function") { 813 | setTimeout(function () { 814 | onError(err, data); 815 | }, timeout); 816 | } 817 | return data; 818 | }; 819 | }()); 820 | 821 | 822 | 823 | // run shared js-env code - lib.storage.js 824 | (function (local) { 825 | var child_process; 826 | var clear; 827 | var defer; 828 | var deferList; 829 | var fs; 830 | var getItem; 831 | var init; 832 | var keys; 833 | var length; 834 | var os; 835 | var removeItem; 836 | var setItem; 837 | var storage; 838 | var storageDir; 839 | 840 | storageDir = "tmp/storage." + ( 841 | local.isBrowser 842 | ? "undefined" 843 | : process.env.NODE_ENV 844 | ); 845 | if (!local.isBrowser) { 846 | // require modules 847 | child_process = require("child_process"); 848 | fs = require("fs"); 849 | os = require("os"); 850 | } 851 | 852 | clear = function (onError) { 853 | /* 854 | * this function will clear storage 855 | */ 856 | defer({ 857 | action: "clear" 858 | }, onError); 859 | }; 860 | 861 | defer = function (opt, onError) { 862 | /* 863 | * this function will defer .action until storage is ready 864 | */ 865 | var data; 866 | var isDone; 867 | var objectStore; 868 | var onError2; 869 | var req; 870 | var tmp; 871 | onError = onError || function (err) { 872 | // validate no err occurred 873 | local.assertThrow(!err, err); 874 | }; 875 | if (!storage) { 876 | deferList.push(function () { 877 | defer(opt, onError); 878 | }); 879 | init(); 880 | return; 881 | } 882 | if (local.isBrowser) { 883 | onError2 = function () { 884 | /* istanbul ignore next */ 885 | if (isDone) { 886 | return; 887 | } 888 | isDone = true; 889 | onError( 890 | req && (req.error || req.transaction.error), 891 | data || req.result || "" 892 | ); 893 | }; 894 | switch (opt.action) { 895 | case "clear": 896 | case "removeItem": 897 | case "setItem": 898 | objectStore = storage.transaction( 899 | storageDir, 900 | "readwrite" 901 | ).objectStore(storageDir); 902 | break; 903 | default: 904 | objectStore = storage.transaction( 905 | storageDir, 906 | "readonly" 907 | ).objectStore(storageDir); 908 | } 909 | switch (opt.action) { 910 | case "clear": 911 | req = objectStore.clear(); 912 | break; 913 | case "getItem": 914 | req = objectStore.get(String(opt.key)); 915 | break; 916 | case "keys": 917 | data = []; 918 | req = objectStore.openCursor(); 919 | req.onsuccess = function () { 920 | if (!req.result) { 921 | onError2(); 922 | return; 923 | } 924 | data.push(req.result.key); 925 | req.result.continue(); 926 | }; 927 | break; 928 | case "length": 929 | req = objectStore.count(); 930 | break; 931 | case "removeItem": 932 | req = objectStore.delete(String(opt.key)); 933 | break; 934 | case "setItem": 935 | req = objectStore.put(opt.value, String(opt.key)); 936 | break; 937 | } 938 | [ 939 | "onabort", "onerror", "onsuccess" 940 | ].forEach(function (handler) { 941 | req[handler] = req[handler] || onError2; 942 | }); 943 | // debug req 944 | local._debugStorageReq = req; 945 | } else { 946 | switch (opt.action) { 947 | case "clear": 948 | child_process.spawnSync("rm -f " + storage + "/*", { 949 | shell: true, 950 | stdio: [ 951 | "ignore", 1, 2 952 | ] 953 | }); 954 | setTimeout(onError); 955 | break; 956 | case "getItem": 957 | fs.readFile( 958 | storage + "/" + encodeURIComponent(String(opt.key)), 959 | "utf8", 960 | function (ignore, data) { 961 | onError(null, data || ""); 962 | } 963 | ); 964 | break; 965 | case "keys": 966 | fs.readdir(storage, function (err, data) { 967 | onError(err, data && data.map(decodeURIComponent)); 968 | }); 969 | break; 970 | case "length": 971 | fs.readdir(storage, function (err, data) { 972 | onError(err, data && data.length); 973 | }); 974 | break; 975 | case "removeItem": 976 | fs.unlink( 977 | storage + "/" + encodeURIComponent(String(opt.key)), 978 | // ignore err 979 | function () { 980 | onError(); 981 | } 982 | ); 983 | break; 984 | case "setItem": 985 | tmp = os.tmpdir() + "/" + Date.now() + Math.random(); 986 | // save to tmp 987 | fs.writeFile(tmp, opt.value, function (err) { 988 | // validate no err occurred 989 | local.assertThrow(!err, err); 990 | // rename tmp to key 991 | fs.rename( 992 | tmp, 993 | storage + "/" + encodeURIComponent(String(opt.key)), 994 | onError 995 | ); 996 | }); 997 | break; 998 | } 999 | } 1000 | }; 1001 | 1002 | deferList = []; 1003 | 1004 | getItem = function (key, onError) { 1005 | /* 1006 | * this function will get the item with given key from storage 1007 | */ 1008 | defer({ 1009 | action: "getItem", 1010 | key 1011 | }, onError); 1012 | }; 1013 | 1014 | init = function () { 1015 | /* 1016 | * this function will init storage 1017 | */ 1018 | var onError; 1019 | var req; 1020 | onError = function (err) { 1021 | // validate no err occurred 1022 | local.assertThrow(!err, err); 1023 | if (local.isBrowser) { 1024 | storage = globalThis[storageDir]; 1025 | } 1026 | while (deferList.length) { 1027 | deferList.shift()(); 1028 | } 1029 | }; 1030 | if (local.isBrowser) { 1031 | storage = globalThis[storageDir]; 1032 | } 1033 | if (storage) { 1034 | onError(); 1035 | return; 1036 | } 1037 | if (local.isBrowser) { 1038 | // init indexedDB 1039 | try { 1040 | req = globalThis.indexedDB.open(storageDir); 1041 | // debug req 1042 | local._debugStorageReqIndexedDB = req; 1043 | req.onerror = onError; 1044 | req.onsuccess = function () { 1045 | globalThis[storageDir] = req.result; 1046 | onError(); 1047 | }; 1048 | req.onupgradeneeded = function () { 1049 | if (!req.result.objectStoreNames.contains(storageDir)) { 1050 | req.result.createObjectStore(storageDir); 1051 | } 1052 | }; 1053 | } catch (ignore) {} 1054 | } else { 1055 | // mkdirp storage 1056 | storage = storageDir; 1057 | child_process.spawnSync("mkdir", [ 1058 | "-p", storage 1059 | ], { 1060 | stdio: [ 1061 | "ignore", 1, 2 1062 | ] 1063 | }); 1064 | onError(); 1065 | } 1066 | }; 1067 | 1068 | keys = function (onError) { 1069 | /* 1070 | * this function will get all the keys in storage 1071 | */ 1072 | defer({ 1073 | action: "keys" 1074 | }, onError); 1075 | }; 1076 | 1077 | length = function (onError) { 1078 | /* 1079 | * this function will get the number of items in storage 1080 | */ 1081 | defer({ 1082 | action: "length" 1083 | }, onError); 1084 | }; 1085 | 1086 | removeItem = function (key, onError) { 1087 | /* 1088 | * this function will remove the item with given key from storage 1089 | */ 1090 | defer({ 1091 | action: "removeItem", 1092 | key 1093 | }, onError); 1094 | }; 1095 | 1096 | setItem = function (key, value, onError) { 1097 | /* 1098 | * this function will set the item with given key and value to storage 1099 | */ 1100 | defer({ 1101 | action: "setItem", 1102 | key, 1103 | value 1104 | }, onError); 1105 | }; 1106 | 1107 | // init local 1108 | local.storage = storage; 1109 | local.storageClear = clear; 1110 | local.storageDefer = defer; 1111 | local.storageDeferList = deferList; 1112 | local.storageDir = storageDir; 1113 | local.storageGetItem = getItem; 1114 | local.storageInit = init; 1115 | local.storageKeys = keys; 1116 | local.storageLength = length; 1117 | local.storageRemoveItem = removeItem; 1118 | local.storageSetItem = setItem; 1119 | }(local)); 1120 | 1121 | 1122 | 1123 | // run shared js-env code - lib.dbTable.js 1124 | (function () { 1125 | local._DbTable = function (opt) { 1126 | /* 1127 | * this function will create a dbTable 1128 | */ 1129 | this.name = String(opt.name); 1130 | // register dbTable in dbTableDict 1131 | local.dbTableDict[this.name] = this; 1132 | this.dbRowList = []; 1133 | this.isDirty = null; 1134 | this.idIndexList = [ 1135 | { 1136 | isInteger: false, 1137 | name: "_id", 1138 | dict: {} 1139 | } 1140 | ]; 1141 | this.onSaveList = []; 1142 | this.sizeLimit = opt.sizeLimit || 0; 1143 | }; 1144 | 1145 | local._DbTable.prototype._cleanup = function () { 1146 | /* 1147 | * this function will cleanup soft-deleted records from the dbTable 1148 | */ 1149 | var dbRow; 1150 | var ii; 1151 | var list; 1152 | if (!this.isDirty && this.dbRowList.length <= this.sizeLimit) { 1153 | return; 1154 | } 1155 | this.isDirty = null; 1156 | // cleanup dbRowList 1157 | list = this.dbRowList; 1158 | this.dbRowList = []; 1159 | // optimization - while-loop 1160 | ii = 0; 1161 | while (ii < list.length) { 1162 | dbRow = list[ii]; 1163 | // cleanup isRemoved 1164 | if (!dbRow.$isRemoved) { 1165 | this.dbRowList.push(dbRow); 1166 | } 1167 | ii += 1; 1168 | } 1169 | if (this.sizeLimit && this.dbRowList.length >= 1.5 * this.sizeLimit) { 1170 | this.dbRowList = this._crudGetManyByQuery( 1171 | {}, 1172 | this.sortDefault, 1173 | 0, 1174 | this.sizeLimit 1175 | ); 1176 | } 1177 | }; 1178 | 1179 | local._DbTable.prototype._crudGetManyByQuery = function ( 1180 | query, 1181 | sort, 1182 | skip, 1183 | limit, 1184 | shuffle 1185 | ) { 1186 | /* 1187 | * this function will get the dbRow's in the dbTable, 1188 | * with given query, sort, skip, and limit 1189 | */ 1190 | var ii; 1191 | var result; 1192 | result = this.dbRowList; 1193 | // get by query 1194 | if (result.length && query && Object.keys(query).length) { 1195 | result = local.dbRowListGetManyByQuery(this.dbRowList, query); 1196 | } 1197 | // sort 1198 | (sort || []).forEach(function (element) { 1199 | // bug-workaround - v8 does not have stable-sort 1200 | // optimization - while-loop 1201 | ii = 0; 1202 | while (ii < result.length) { 1203 | result[ii].$ii = ii; 1204 | ii += 1; 1205 | } 1206 | result.sort(function (aa, bb) { 1207 | return local.sortCompare( 1208 | local.dbRowGetItem(aa, element.fieldName), 1209 | local.dbRowGetItem(bb, element.fieldName), 1210 | aa.$ii, 1211 | bb.$ii 1212 | ); 1213 | }); 1214 | if (element.isDescending) { 1215 | result.reverse(); 1216 | } 1217 | }); 1218 | // skip 1219 | result = result.slice(skip || 0); 1220 | // shuffle 1221 | local.functionOrNop(shuffle && local.listShuffle)(result); 1222 | // limit 1223 | result = result.slice(0, limit || Infinity); 1224 | return result; 1225 | }; 1226 | 1227 | local._DbTable.prototype._crudGetOneById = function (idDict) { 1228 | /* 1229 | * this function will get the dbRow in the dbTable with given idDict 1230 | */ 1231 | var id; 1232 | var result; 1233 | idDict = local.objectSetOverride(idDict); 1234 | result = null; 1235 | this.idIndexList.some(function (idIndex) { 1236 | id = idDict[idIndex.name]; 1237 | // optimization - hasOwnProperty 1238 | if (idIndex.dict.hasOwnProperty(id)) { 1239 | result = idIndex.dict[id]; 1240 | return result; 1241 | } 1242 | }); 1243 | return result; 1244 | }; 1245 | 1246 | local._DbTable.prototype._crudRemoveOneById = function (idDict, circularSet) { 1247 | /* 1248 | * this function will remove the dbRow from the dbTable with given idDict 1249 | */ 1250 | var id; 1251 | var result; 1252 | var that; 1253 | if (!idDict) { 1254 | return null; 1255 | } 1256 | that = this; 1257 | circularSet = circularSet || new Set([ 1258 | idDict 1259 | ]); 1260 | result = null; 1261 | that.idIndexList.forEach(function (idIndex) { 1262 | id = idDict[idIndex.name]; 1263 | // optimization - hasOwnProperty 1264 | if (!idIndex.dict.hasOwnProperty(id)) { 1265 | return; 1266 | } 1267 | result = idIndex.dict[id]; 1268 | delete idIndex.dict[id]; 1269 | // optimization - soft-delete 1270 | result.$isRemoved = true; 1271 | that.isDirty = true; 1272 | if (circularSet.has(result)) { 1273 | return; 1274 | } 1275 | circularSet.add(result); 1276 | // recurse 1277 | that._crudRemoveOneById(result, circularSet); 1278 | }); 1279 | that.save(); 1280 | return result; 1281 | }; 1282 | 1283 | local._DbTable.prototype._crudSetOneById = function (dbRow) { 1284 | /* 1285 | * this function will set the dbRow into the dbTable with given dbRow._id 1286 | * WARNING - existing dbRow with conflicting dbRow._id will be removed 1287 | */ 1288 | var existing; 1289 | var id; 1290 | var normalize; 1291 | var timeNow; 1292 | normalize = function (dbRow) { 1293 | /* 1294 | * this function will recursively normalize dbRow 1295 | */ 1296 | switch (typeof dbRow) { 1297 | case "boolean": 1298 | case "string": 1299 | return dbRow; 1300 | case "number": 1301 | return ( 1302 | Number.isFinite(dbRow) 1303 | ? dbRow 1304 | : undefined 1305 | ); 1306 | case "object": 1307 | if (!dbRow) { 1308 | return; 1309 | } 1310 | break; 1311 | default: 1312 | return; 1313 | } 1314 | Object.keys(dbRow).forEach(function (key) { 1315 | dbRow[key] = ( 1316 | (key[0] === "$" || key.indexOf(".") >= 0) 1317 | // invalid-property 1318 | ? undefined 1319 | // recurse 1320 | : normalize(dbRow[key]) 1321 | ); 1322 | }); 1323 | return dbRow; 1324 | }; 1325 | dbRow = local.jsonCopy(local.objectSetOverride(dbRow)); 1326 | // update timestamp 1327 | timeNow = new Date().toISOString(); 1328 | dbRow._timeCreated = dbRow._timeCreated || timeNow; 1329 | if (!local.modeImport) { 1330 | dbRow._timeUpdated = timeNow; 1331 | } 1332 | // normalize 1333 | dbRow = normalize(dbRow); 1334 | dbRow = local.jsonCopy(dbRow); 1335 | // remove existing dbRow 1336 | existing = this._crudRemoveOneById(dbRow) || dbRow; 1337 | this.idIndexList.forEach(function (idIndex) { 1338 | // auto-set id 1339 | id = local.dbRowSetId(existing, idIndex); 1340 | // copy id from existing to dbRow 1341 | dbRow[idIndex.name] = id; 1342 | // set dbRow 1343 | idIndex.dict[id] = dbRow; 1344 | }); 1345 | // update dbRowList 1346 | this.dbRowList.push(dbRow); 1347 | this.save(); 1348 | return dbRow; 1349 | }; 1350 | 1351 | local._DbTable.prototype._crudUpdateOneById = function (dbRow) { 1352 | /* 1353 | * this function will update the dbRow in the dbTable, 1354 | * if it exists with given dbRow._id 1355 | * WARNING 1356 | * existing dbRow's with conflicting unique-keys (besides the one being updated) 1357 | * will be removed 1358 | */ 1359 | var id; 1360 | var result; 1361 | dbRow = local.jsonCopy(local.objectSetOverride(dbRow)); 1362 | result = {}; 1363 | this.idIndexList.some(function (idIndex) { 1364 | id = dbRow[idIndex.name]; 1365 | // optimization - hasOwnProperty 1366 | if (idIndex.dict.hasOwnProperty(id)) { 1367 | result = idIndex.dict[id]; 1368 | return true; 1369 | } 1370 | }); 1371 | // remove existing dbRow 1372 | result = local.jsonCopy(this._crudRemoveOneById(result)) || result; 1373 | // update dbRow 1374 | delete dbRow._timeCreated; 1375 | local.objectSetOverride(result, dbRow, 10); 1376 | // replace dbRow 1377 | result = this._crudSetOneById(result); 1378 | return result; 1379 | }; 1380 | 1381 | local._DbTable.prototype.crudCountAll = function (onError) { 1382 | /* 1383 | * this function will count all of dbRow's in dbTable 1384 | */ 1385 | this._cleanup(); 1386 | return local.setTimeoutOnError(onError, 0, null, this.dbRowList.length); 1387 | }; 1388 | 1389 | local._DbTable.prototype.crudCountManyByQuery = function (query, onError) { 1390 | /* 1391 | * this function will count the number of dbRow's in dbTable with given query 1392 | */ 1393 | this._cleanup(); 1394 | return local.setTimeoutOnError( 1395 | onError, 1396 | 0, 1397 | null, 1398 | this._crudGetManyByQuery(query).length 1399 | ); 1400 | }; 1401 | 1402 | local._DbTable.prototype.crudGetManyById = function (idDictList, onError) { 1403 | /* 1404 | * this function will get the dbRow's in the dbTable with given idDictList 1405 | */ 1406 | var that; 1407 | this._cleanup(); 1408 | that = this; 1409 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1410 | (idDictList || []).map(function (idDict) { 1411 | return that._crudGetOneById(idDict); 1412 | }) 1413 | )); 1414 | }; 1415 | 1416 | local._DbTable.prototype.crudGetManyByQuery = function (opt, onError) { 1417 | /* 1418 | * this function will get the dbRow's in the dbTable with given .query 1419 | */ 1420 | this._cleanup(); 1421 | opt = local.objectSetOverride(opt); 1422 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1423 | this._crudGetManyByQuery( 1424 | opt.query, 1425 | opt.sort || this.sortDefault, 1426 | opt.skip, 1427 | opt.limit, 1428 | opt.shuffle 1429 | ), 1430 | opt.fieldList 1431 | )); 1432 | }; 1433 | 1434 | local._DbTable.prototype.crudGetOneById = function (idDict, onError) { 1435 | /* 1436 | * this function will get the dbRow in the dbTable with given idDict 1437 | */ 1438 | this._cleanup(); 1439 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1440 | this._crudGetOneById(idDict) 1441 | )); 1442 | }; 1443 | 1444 | local._DbTable.prototype.crudGetOneByQuery = function (query, onError) { 1445 | /* 1446 | * this function will get the dbRow in the dbTable with given query 1447 | */ 1448 | var ii; 1449 | var result; 1450 | this._cleanup(); 1451 | // optimization - while-loop 1452 | ii = 0; 1453 | while (ii < this.dbRowList.length) { 1454 | result = local.dbRowListGetManyByQuery([ 1455 | this.dbRowList[ii] 1456 | ], query)[0]; 1457 | if (result) { 1458 | break; 1459 | } 1460 | ii += 1; 1461 | } 1462 | return local.setTimeoutOnError( 1463 | onError, 1464 | 0, 1465 | null, 1466 | local.dbRowProject(result) 1467 | ); 1468 | }; 1469 | 1470 | local._DbTable.prototype.crudGetOneByRandom = function (onError) { 1471 | /* 1472 | * this function will get a random dbRow in the dbTable 1473 | */ 1474 | this._cleanup(); 1475 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1476 | this.dbRowList[Math.floor(Math.random() * this.dbRowList.length)] 1477 | )); 1478 | }; 1479 | 1480 | local._DbTable.prototype.crudRemoveAll = function (onError) { 1481 | /* 1482 | * this function will remove all of the dbRow's from the dbTable 1483 | */ 1484 | var idIndexList; 1485 | // save idIndexList 1486 | idIndexList = this.idIndexList; 1487 | // reset dbTable 1488 | local._DbTable.call(this, this); 1489 | // restore idIndexList 1490 | local.dbTableCreateOne({ 1491 | name: this.name, 1492 | idIndexCreateList: idIndexList 1493 | }, onError); 1494 | }; 1495 | 1496 | local._DbTable.prototype.crudRemoveManyById = function (idDictList, onError) { 1497 | /* 1498 | * this function will remove the dbRow's from the dbTable with given idDictList 1499 | */ 1500 | var that; 1501 | that = this; 1502 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1503 | (idDictList || []).map(function (dbRow) { 1504 | return that._crudRemoveOneById(dbRow); 1505 | }) 1506 | )); 1507 | }; 1508 | 1509 | local._DbTable.prototype.crudRemoveManyByQuery = function (query, onError) { 1510 | /* 1511 | * this function will remove the dbRow's from the dbTable with given query 1512 | */ 1513 | var that; 1514 | that = this; 1515 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1516 | that._crudGetManyByQuery(query).map(function (dbRow) { 1517 | return that._crudRemoveOneById(dbRow); 1518 | }) 1519 | )); 1520 | }; 1521 | 1522 | local._DbTable.prototype.crudRemoveOneById = function (idDict, onError) { 1523 | /* 1524 | * this function will remove the dbRow from the dbTable with given idDict 1525 | */ 1526 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1527 | this._crudRemoveOneById(idDict) 1528 | )); 1529 | }; 1530 | 1531 | local._DbTable.prototype.crudSetManyById = function (dbRowList, onError) { 1532 | /* 1533 | * this function will set the dbRowList into the dbTable 1534 | */ 1535 | var that; 1536 | that = this; 1537 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1538 | (dbRowList || []).map(function (dbRow) { 1539 | return that._crudSetOneById(dbRow); 1540 | }) 1541 | )); 1542 | }; 1543 | 1544 | local._DbTable.prototype.crudSetOneById = function (dbRow, onError) { 1545 | /* 1546 | * this function will set the dbRow into the dbTable with given dbRow._id 1547 | */ 1548 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1549 | this._crudSetOneById(dbRow) 1550 | )); 1551 | }; 1552 | 1553 | local._DbTable.prototype.crudUpdateManyById = function (dbRowList, onError) { 1554 | /* 1555 | * this function will update the dbRowList in the dbTable, 1556 | * if they exist with given dbRow._id's 1557 | */ 1558 | var that; 1559 | that = this; 1560 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1561 | (dbRowList || []).map(function (dbRow) { 1562 | return that._crudUpdateOneById(dbRow); 1563 | }) 1564 | )); 1565 | }; 1566 | 1567 | local._DbTable.prototype.crudUpdateManyByQuery = function ( 1568 | query, 1569 | dbRow, 1570 | onError 1571 | ) { 1572 | /* 1573 | * this function will update the dbRow's in the dbTable with given query 1574 | */ 1575 | var result; 1576 | var that; 1577 | var tmp; 1578 | that = this; 1579 | tmp = local.jsonCopy(local.objectSetOverride(dbRow)); 1580 | result = that._crudGetManyByQuery(query).map(function (dbRow) { 1581 | tmp._id = dbRow._id; 1582 | return that._crudUpdateOneById(tmp); 1583 | }); 1584 | return local.setTimeoutOnError(onError, 0, null, result); 1585 | }; 1586 | 1587 | local._DbTable.prototype.crudUpdateOneById = function (dbRow, onError) { 1588 | /* 1589 | * this function will update the dbRow in the dbTable, 1590 | * if it exists with given dbRow._id 1591 | */ 1592 | return local.setTimeoutOnError(onError, 0, null, local.dbRowProject( 1593 | this._crudUpdateOneById(dbRow) 1594 | )); 1595 | }; 1596 | 1597 | local._DbTable.prototype.drop = function (onError) { 1598 | /* 1599 | * this function will drop the dbTable 1600 | */ 1601 | console.error("db - dropping dbTable " + this.name + " ..."); 1602 | // cancel pending save 1603 | this.timerSave = null; 1604 | while (this.onSaveList.length) { 1605 | this.onSaveList.shift()(); 1606 | } 1607 | // reset dbTable 1608 | local._DbTable.call(this, this); 1609 | // clear persistence 1610 | local.storageRemoveItem("dbTable." + this.name + ".json", onError); 1611 | }; 1612 | 1613 | local._DbTable.prototype.export = function (onError) { 1614 | /* 1615 | * this function will export the db 1616 | */ 1617 | var result; 1618 | var that; 1619 | this._cleanup(); 1620 | that = this; 1621 | result = ""; 1622 | that.idIndexList.forEach(function (idIndex) { 1623 | result += that.name + " idIndexCreate " + JSON.stringify({ 1624 | isInteger: idIndex.isInteger, 1625 | name: idIndex.name 1626 | }) + "\n"; 1627 | }); 1628 | result += that.name + " sizeLimit " + that.sizeLimit + "\n"; 1629 | result += that.name + " sortDefault " + JSON.stringify( 1630 | that.sortDefault 1631 | ) + "\n"; 1632 | that.crudGetManyByQuery({}).forEach(function (dbRow) { 1633 | result += that.name + " dbRowSet " + JSON.stringify(dbRow) + "\n"; 1634 | }); 1635 | return local.setTimeoutOnError(onError, 0, null, result.trim()); 1636 | }; 1637 | 1638 | local._DbTable.prototype.idIndexCreate = function (opt, onError) { 1639 | /* 1640 | * this function will create an idIndex with given .name 1641 | */ 1642 | var dbRow; 1643 | var idIndex; 1644 | var ii; 1645 | var name; 1646 | opt = local.objectSetOverride(opt); 1647 | name = String(opt.name); 1648 | // disallow idIndex with dot-name 1649 | if (name.indexOf(".") >= 0 || name === "_id") { 1650 | return local.setTimeoutOnError(onError); 1651 | } 1652 | // remove existing idIndex 1653 | this.idIndexRemove(opt); 1654 | // init idIndex 1655 | idIndex = { 1656 | dict: {}, 1657 | isInteger: Boolean(opt.isInteger), 1658 | name 1659 | }; 1660 | this.idIndexList.push(idIndex); 1661 | // populate idIndex with dbRowList 1662 | // optimization - while-loop 1663 | ii = 0; 1664 | while (ii < this.dbRowList.length) { 1665 | dbRow = this.dbRowList[ii]; 1666 | // auto-set id 1667 | if (!dbRow.$isRemoved) { 1668 | idIndex.dict[local.dbRowSetId(dbRow, idIndex)] = dbRow; 1669 | } 1670 | ii += 1; 1671 | } 1672 | this.save(); 1673 | return local.setTimeoutOnError(onError); 1674 | }; 1675 | 1676 | local._DbTable.prototype.idIndexRemove = function (opt, onError) { 1677 | /* 1678 | * this function will remove the idIndex with given .name 1679 | */ 1680 | var name; 1681 | opt = local.objectSetOverride(opt); 1682 | name = String(opt.name); 1683 | this.idIndexList = this.idIndexList.filter(function (idIndex) { 1684 | return idIndex.name !== name || idIndex.name === "_id"; 1685 | }); 1686 | this.save(); 1687 | return local.setTimeoutOnError(onError); 1688 | }; 1689 | 1690 | local._DbTable.prototype.save = function (onError) { 1691 | /* 1692 | * this function will save the dbTable to storage 1693 | */ 1694 | var that; 1695 | that = this; 1696 | if (local.modeImport) { 1697 | return; 1698 | } 1699 | if (onError) { 1700 | that.onSaveList.push(onError); 1701 | } 1702 | // throttle storage-writes to once every 1000 ms 1703 | that.timerSave = that.timerSave || setTimeout(function () { 1704 | that.timerSave = null; 1705 | local.storageSetItem( 1706 | "dbTable." + that.name + ".json", 1707 | that.export(), 1708 | function (err) { 1709 | while (that.onSaveList.length) { 1710 | that.onSaveList.shift()(err); 1711 | } 1712 | } 1713 | ); 1714 | }, 1000); 1715 | }; 1716 | 1717 | local.dbCrudRemoveAll = function (onError) { 1718 | /* 1719 | * this function will remove all dbRow's from the db 1720 | */ 1721 | var onParallel; 1722 | onParallel = local.onParallel(function (err) { 1723 | local.setTimeoutOnError(onError, 0, err); 1724 | }); 1725 | onParallel.counter += 1; 1726 | Object.keys(local.dbTableDict).forEach(function (key) { 1727 | onParallel.counter += 1; 1728 | local.dbTableDict[key].crudRemoveAll(onParallel); 1729 | }); 1730 | onParallel(); 1731 | }; 1732 | 1733 | local.dbDrop = function (onError) { 1734 | /* 1735 | * this function will drop the db 1736 | */ 1737 | var onParallel; 1738 | console.error("db - dropping database ..."); 1739 | onParallel = local.onParallel(function (err) { 1740 | local.setTimeoutOnError(onError, 0, err); 1741 | }); 1742 | onParallel.counter += 1; 1743 | onParallel.counter += 1; 1744 | local.storageClear(onParallel); 1745 | Object.keys(local.dbTableDict).forEach(function (key) { 1746 | onParallel.counter += 1; 1747 | local.dbTableDict[key].drop(onParallel); 1748 | }); 1749 | onParallel(); 1750 | }; 1751 | 1752 | local.dbExport = function (onError) { 1753 | /* 1754 | * this function will export the db as serialized text 1755 | */ 1756 | var result; 1757 | result = ""; 1758 | Object.keys(local.dbTableDict).forEach(function (key) { 1759 | console.error( 1760 | "db - exporting dbTable " + local.dbTableDict[key].name + " ..." 1761 | ); 1762 | result += local.dbTableDict[key].export(); 1763 | result += "\n\n"; 1764 | }); 1765 | return local.setTimeoutOnError(onError, 0, null, result.trim()); 1766 | }; 1767 | 1768 | local.dbImport = function (text, onError) { 1769 | /* 1770 | * this function will import the serialized text into the db 1771 | */ 1772 | var dbTable; 1773 | var dbTableDict; 1774 | dbTableDict = {}; 1775 | local.modeImport = true; 1776 | setTimeout(function () { 1777 | local.modeImport = null; 1778 | }); 1779 | text.replace(( 1780 | /^(\w\S*?)\u0020(\S+?)\u0020(\S.*?)$/gm 1781 | ), function (match0, match1, match2, match3) { 1782 | switch (match2) { 1783 | case "dbRowSet": 1784 | dbTableDict[match1] = true; 1785 | dbTable = local.dbTableCreateOne({ 1786 | isLoaded: true, 1787 | name: match1 1788 | }); 1789 | dbTable.crudSetOneById(JSON.parse(match3)); 1790 | break; 1791 | case "idIndexCreate": 1792 | dbTableDict[match1] = true; 1793 | dbTable = local.dbTableCreateOne({ 1794 | isLoaded: true, 1795 | name: match1 1796 | }); 1797 | dbTable.idIndexCreate(JSON.parse(match3)); 1798 | break; 1799 | case "sizeLimit": 1800 | dbTableDict[match1] = true; 1801 | dbTable = local.dbTableCreateOne({ 1802 | isLoaded: true, 1803 | name: match1 1804 | }); 1805 | dbTable.sizeLimit = JSON.parse(match3); 1806 | break; 1807 | case "sortDefault": 1808 | dbTableDict[match1] = true; 1809 | dbTable = local.dbTableCreateOne({ 1810 | isLoaded: true, 1811 | name: match1 1812 | }); 1813 | dbTable.sortDefault = JSON.parse(match3); 1814 | break; 1815 | default: 1816 | local.onErrorDefault(new Error( 1817 | "db - dbImport - invalid operation - " + match0 1818 | )); 1819 | } 1820 | }); 1821 | Object.keys(dbTableDict).forEach(function (name) { 1822 | console.error("db - importing dbTable " + name + " ..."); 1823 | }); 1824 | local.modeImport = null; 1825 | return local.setTimeoutOnError(onError); 1826 | }; 1827 | 1828 | local.dbLoad = function (onError) { 1829 | /* 1830 | * this function will load the db from storage 1831 | */ 1832 | var onParallel; 1833 | onParallel = local.onParallel(function (err) { 1834 | local.setTimeoutOnError(onError, 0, err); 1835 | }); 1836 | local.storageKeys(function (err, data) { 1837 | onParallel.counter += 1; 1838 | // validate no err occurred 1839 | onParallel.counter += 1; 1840 | onParallel(err); 1841 | (data || []).forEach(function (key) { 1842 | if (key.indexOf("dbTable.") !== 0) { 1843 | return; 1844 | } 1845 | onParallel.counter += 1; 1846 | local.storageGetItem(key, function (err, data) { 1847 | onParallel.counter += 1; 1848 | onParallel(err); 1849 | local.dbImport(data, onParallel); 1850 | }); 1851 | }); 1852 | onParallel(); 1853 | }); 1854 | }; 1855 | 1856 | local.dbReset = function (dbSeedList, onError) { 1857 | /* 1858 | * this function will drop and seed the db with given dbSeedList 1859 | */ 1860 | var onParallel; 1861 | onParallel = globalThis.utility2_onReadyBefore || local.onParallel(onError); 1862 | onParallel.counter += 1; 1863 | // drop db 1864 | onParallel.counter += 1; 1865 | local.dbDrop(function (err) { 1866 | local.onErrorDefault(err); 1867 | // seed db 1868 | local.dbSeed( 1869 | dbSeedList, 1870 | !globalThis.utility2_onReadyBefore && onParallel 1871 | ); 1872 | local.functionOrNop(globalThis.utility2_onReadyBefore)(); 1873 | }); 1874 | local.functionOrNop(globalThis.utility2_onReadyAfter)(onError); 1875 | onParallel(); 1876 | }; 1877 | 1878 | local.dbRowGetItem = function (dbRow, key) { 1879 | /* 1880 | * this function will get the item with given key from dbRow 1881 | */ 1882 | var ii; 1883 | var value; 1884 | value = dbRow; 1885 | key = String(key).split("."); 1886 | // optimization - while-loop 1887 | ii = 0; 1888 | while (ii < key.length && typeof value === "object" && value) { 1889 | value = value[key[ii]]; 1890 | ii += 1; 1891 | } 1892 | return ( 1893 | value === undefined 1894 | ? null 1895 | : value 1896 | ); 1897 | }; 1898 | 1899 | local.dbRowListGetManyByOperator = function ( 1900 | dbRowList, 1901 | fieldName, 1902 | operator, 1903 | bb, 1904 | not 1905 | ) { 1906 | /* 1907 | * this function will get the dbRow's in dbRowList with given operator 1908 | */ 1909 | var fieldValue; 1910 | var ii; 1911 | var jj; 1912 | var result; 1913 | var test; 1914 | var typeof2; 1915 | result = []; 1916 | typeof2 = typeof bb; 1917 | if (bb && typeof2 === "object") { 1918 | switch (operator) { 1919 | case "$in": 1920 | case "$nin": 1921 | case "$regex": 1922 | break; 1923 | default: 1924 | return result; 1925 | } 1926 | } 1927 | switch (operator) { 1928 | case "$eq": 1929 | test = function (aa, bb) { 1930 | return aa === bb; 1931 | }; 1932 | break; 1933 | case "$exists": 1934 | bb = !bb; 1935 | test = function (aa, bb) { 1936 | return !((aa === null) ^ bb); // jslint ignore:line 1937 | }; 1938 | break; 1939 | case "$gt": 1940 | test = function (aa, bb, typeof1, typeof2) { 1941 | return typeof1 === typeof2 && aa > bb; 1942 | }; 1943 | break; 1944 | case "$gte": 1945 | test = function (aa, bb, typeof1, typeof2) { 1946 | return typeof1 === typeof2 && aa >= bb; 1947 | }; 1948 | break; 1949 | case "$in": 1950 | if (bb && typeof bb.indexOf === "function") { 1951 | if (typeof2 === "string") { 1952 | test = function (aa, bb, typeof1, typeof2) { 1953 | return typeof1 === typeof2 && bb.indexOf(aa) >= 0; 1954 | }; 1955 | } else { 1956 | test = function (aa, bb) { 1957 | return bb.indexOf(aa) >= 0; 1958 | }; 1959 | } 1960 | } 1961 | break; 1962 | case "$lt": 1963 | test = function (aa, bb, typeof1, typeof2) { 1964 | return typeof1 === typeof2 && aa < bb; 1965 | }; 1966 | break; 1967 | case "$lte": 1968 | test = function (aa, bb, typeof1, typeof2) { 1969 | return typeof1 === typeof2 && aa <= bb; 1970 | }; 1971 | break; 1972 | case "$ne": 1973 | test = function (aa, bb) { 1974 | return aa !== bb; 1975 | }; 1976 | break; 1977 | case "$nin": 1978 | if (bb && typeof bb.indexOf === "function") { 1979 | if (typeof2 === "string") { 1980 | test = function (aa, bb, typeof1, typeof2) { 1981 | return typeof1 === typeof2 && bb.indexOf(aa) < 0; 1982 | }; 1983 | } else { 1984 | test = function (aa, bb) { 1985 | return bb.indexOf(aa) < 0; 1986 | }; 1987 | } 1988 | } 1989 | break; 1990 | case "$regex": 1991 | if (bb && typeof bb.test === "function") { 1992 | test = function (aa, bb) { 1993 | return bb.test(aa); 1994 | }; 1995 | } 1996 | break; 1997 | case "$typeof": 1998 | test = function (ignore, bb, typeof1) { 1999 | return typeof1 === bb; 2000 | }; 2001 | break; 2002 | } 2003 | if (!test) { 2004 | return result; 2005 | } 2006 | // optimization - while-loop 2007 | ii = dbRowList.length; 2008 | while (ii >= 1) { 2009 | ii -= 1; 2010 | fieldValue = local.dbRowGetItem(dbRowList[ii], fieldName); 2011 | // normalize to list 2012 | if (!Array.isArray(fieldValue)) { 2013 | fieldValue = [ 2014 | fieldValue 2015 | ]; 2016 | } 2017 | // optimization - while-loop 2018 | jj = fieldValue.length; 2019 | while (jj >= 1) { 2020 | jj -= 1; 2021 | if (Boolean( 2022 | not ^ test(fieldValue[jj], bb, typeof fieldValue[jj], typeof2) 2023 | )) { 2024 | result.push(dbRowList[ii]); 2025 | break; 2026 | } 2027 | } 2028 | } 2029 | return result; 2030 | }; 2031 | 2032 | local.dbRowListGetManyByQuery = function (dbRowList, query, fieldName, not) { 2033 | /* 2034 | * this function will get the dbRow's in dbRowList with given query 2035 | */ 2036 | var bb; 2037 | var dbRowDict; 2038 | var result; 2039 | // optimization - convert to boolean 2040 | not = Boolean(not); 2041 | result = dbRowList; 2042 | if (!(typeof query === "object" && query)) { 2043 | result = local.dbRowListGetManyByOperator( 2044 | result, 2045 | fieldName, 2046 | "$eq", 2047 | query, 2048 | not 2049 | ); 2050 | return result; 2051 | } 2052 | Object.keys(query).some(function (key) { 2053 | bb = query[key]; 2054 | switch (key) { 2055 | case "$not": 2056 | key = fieldName; 2057 | not = !not; 2058 | break; 2059 | case "$or": 2060 | if (!Array.isArray(bb)) { 2061 | break; 2062 | } 2063 | dbRowDict = {}; 2064 | bb.forEach(function (query) { 2065 | // recurse 2066 | local.dbRowListGetManyByQuery(result, query).forEach(function ( 2067 | dbRow 2068 | ) { 2069 | dbRowDict[dbRow._id] = dbRow; 2070 | }); 2071 | }); 2072 | result = Object.keys(dbRowDict).map(function (id) { 2073 | return dbRowDict[id]; 2074 | }); 2075 | return !result.length; 2076 | } 2077 | if (key[0] === "$") { 2078 | result = local.dbRowListGetManyByOperator( 2079 | result, 2080 | fieldName, 2081 | key, 2082 | bb, 2083 | not 2084 | ); 2085 | return !result.length; 2086 | } 2087 | // recurse 2088 | result = local.dbRowListGetManyByQuery(result, bb, key, not); 2089 | return !result.length; 2090 | }); 2091 | return result; 2092 | }; 2093 | 2094 | local.dbRowProject = function (dbRow, fieldList) { 2095 | /* 2096 | * this function will deepcopy and project the dbRow with given fieldList 2097 | */ 2098 | var result; 2099 | if (!dbRow) { 2100 | return null; 2101 | } 2102 | // handle list-case 2103 | if (Array.isArray(dbRow)) { 2104 | return dbRow.map(function (dbRow) { 2105 | // recurse 2106 | return local.dbRowProject(dbRow, fieldList); 2107 | }); 2108 | } 2109 | // normalize to list 2110 | if (!(Array.isArray(fieldList) && fieldList.length)) { 2111 | fieldList = Object.keys(dbRow); 2112 | } 2113 | result = {}; 2114 | fieldList.forEach(function (key) { 2115 | if (key[0] !== "$") { 2116 | result[key] = dbRow[key]; 2117 | } 2118 | }); 2119 | return JSON.parse(local.jsonStringifyOrdered(result)); 2120 | }; 2121 | 2122 | local.dbRowSetId = function (dbRow, idIndex) { 2123 | /* 2124 | * this function will if does not exist, 2125 | * then set a random and unique id into dbRow for given idIndex, 2126 | */ 2127 | var id; 2128 | id = dbRow[idIndex.name]; 2129 | if (typeof id !== "number" && typeof id !== "string") { 2130 | do { 2131 | id = ( 2132 | idIndex.isInteger 2133 | ? (1 + Math.random()) * 0x10000000000000 2134 | : "a" + ( 2135 | (1 + Math.random()) * 0x10000000000000 2136 | ).toString(36).slice(1) 2137 | ); 2138 | // optimization - hasOwnProperty 2139 | } while (idIndex.dict.hasOwnProperty(id)); 2140 | dbRow[idIndex.name] = id; 2141 | } 2142 | return id; 2143 | }; 2144 | 2145 | local.dbSave = function (onError) { 2146 | /* 2147 | * this function will save the db to storage 2148 | */ 2149 | var onParallel; 2150 | onParallel = local.onParallel(function (err) { 2151 | local.setTimeoutOnError(onError, 0, err); 2152 | }); 2153 | onParallel.counter += 1; 2154 | Object.keys(local.dbTableDict).forEach(function (key) { 2155 | onParallel.counter += 1; 2156 | local.dbTableDict[key].save(onParallel); 2157 | }); 2158 | onParallel(); 2159 | }; 2160 | 2161 | local.dbSeed = function (dbSeedList, onError) { 2162 | /* 2163 | * this function will seed the db with given dbSeedList 2164 | */ 2165 | var dbTableDict; 2166 | var onParallel; 2167 | dbTableDict = {}; 2168 | onParallel = globalThis.utility2_onReadyBefore || local.onParallel(onError); 2169 | onParallel.counter += 1; 2170 | // seed db 2171 | onParallel.counter += 1; 2172 | local.dbTableCreateMany(dbSeedList, onParallel); 2173 | (dbSeedList || []).forEach(function (opt) { 2174 | dbTableDict[opt.name] = true; 2175 | }); 2176 | Object.keys(dbTableDict).forEach(function (name) { 2177 | console.error("db - seeding dbTable " + name + " ..."); 2178 | }); 2179 | local.functionOrNop(globalThis.utility2_onReadyAfter)(onError); 2180 | onParallel(); 2181 | }; 2182 | 2183 | local.dbTableCreateMany = function (optionList, onError) { 2184 | /* 2185 | * this function will create many dbTables with given optionList 2186 | */ 2187 | var onParallel; 2188 | var result; 2189 | onParallel = local.onParallel(function (err) { 2190 | local.setTimeoutOnError(onError, 0, err, result); 2191 | }); 2192 | onParallel.counter += 1; 2193 | result = (optionList || []).map(function (opt) { 2194 | onParallel.counter += 1; 2195 | return local.dbTableCreateOne(opt, onParallel); 2196 | }); 2197 | return local.setTimeoutOnError(onParallel, 0, null, result); 2198 | }; 2199 | 2200 | local.dbTableCreateOne = function (opt, onError) { 2201 | /* 2202 | * this function will create a dbTable with given 2203 | */ 2204 | var DbTable; 2205 | var that; 2206 | opt = local.objectSetOverride(opt); 2207 | // register dbTable 2208 | DbTable = local._DbTable; 2209 | local.dbTableDict[opt.name] = local.dbTableDict[opt.name] || new DbTable( 2210 | opt 2211 | ); 2212 | that = local.dbTableDict[opt.name]; 2213 | that.sortDefault = ( 2214 | opt.sortDefault 2215 | || that.sortDefault 2216 | || [ 2217 | { 2218 | fieldName: "_timeUpdated", 2219 | isDescending: true 2220 | } 2221 | ] 2222 | ); 2223 | // remove idIndex 2224 | (opt.idIndexRemoveList || []).forEach(function (idIndex) { 2225 | that.idIndexRemove(idIndex); 2226 | }); 2227 | // create idIndex 2228 | (opt.idIndexCreateList || []).forEach(function (idIndex) { 2229 | that.idIndexCreate(idIndex); 2230 | }); 2231 | // upsert dbRow 2232 | that.crudSetManyById(opt.dbRowList); 2233 | // restore dbTable from persistent-storage 2234 | that.isLoaded = that.isLoaded || opt.isLoaded; 2235 | if (!that.isLoaded) { 2236 | local.storageGetItem("dbTable." + that.name + ".json", function ( 2237 | err, 2238 | data 2239 | ) { 2240 | // validate no err occurred 2241 | local.assertThrow(!err, err); 2242 | if (!that.isLoaded) { 2243 | local.dbImport(data); 2244 | } 2245 | that.isLoaded = true; 2246 | local.setTimeoutOnError(onError, 0, null, that); 2247 | }); 2248 | return that; 2249 | } 2250 | return local.setTimeoutOnError(onError, 0, null, that); 2251 | }; 2252 | 2253 | local.dbTableDict = {}; 2254 | 2255 | local.onEventDomDb = function (evt) { 2256 | /* 2257 | * this function will handle db dom-events 2258 | */ 2259 | var ajaxProgressUpdate; 2260 | var reader; 2261 | var tmp; 2262 | var utility2; 2263 | utility2 = globalThis.utility2 || {}; 2264 | ajaxProgressUpdate = utility2.ajaxProgressUpdate || local.nop; 2265 | switch (evt.target.dataset.oneventDb) { 2266 | case "dbExport": 2267 | tmp = URL.createObjectURL(new globalThis.Blob([ 2268 | local.dbExport() 2269 | ])); 2270 | document.querySelector( 2271 | "#dbExportA1" 2272 | ).href = tmp; 2273 | document.querySelector( 2274 | "#dbExportA1" 2275 | ).click(); 2276 | setTimeout(function () { 2277 | URL.revokeObjectURL(tmp); 2278 | }, 30000); 2279 | break; 2280 | case "dbImport": 2281 | document.querySelector( 2282 | "[data-onevent-db='dbImportInput']" 2283 | ).click(); 2284 | break; 2285 | case "dbImportInput": 2286 | if (evt.type !== "change") { 2287 | return; 2288 | } 2289 | ajaxProgressUpdate(); 2290 | reader = new FileReader(); 2291 | reader.addEventListener("load", function () { 2292 | local.dbImport(reader.result); 2293 | ajaxProgressUpdate(); 2294 | }); 2295 | reader.readAsText(evt.target.files[0]); 2296 | break; 2297 | case "dbReset": 2298 | ajaxProgressUpdate(); 2299 | local.dbReset(globalThis.utility2_dbSeedList, function (err) { 2300 | local.onErrorDefault(err); 2301 | if ( 2302 | utility2.uiEventListenerDict 2303 | && typeof utility2.uiEventListenerDict[".onEventUiReload"] 2304 | === "function" 2305 | ) { 2306 | utility2.uiEventListenerDict[".onEventUiReload"](); 2307 | } 2308 | }); 2309 | break; 2310 | } 2311 | }; 2312 | 2313 | local.sortCompare = function (aa, bb, ii, jj) { 2314 | /* 2315 | * this function will compare aa vs bb and return: 2316 | * -1 if aa < bb 2317 | * 0 if aa === bb 2318 | * 1 if aa > bb 2319 | * the priority for comparing different typeof's is: 2320 | * null < boolean < number < string < object < undefined 2321 | */ 2322 | var typeof1; 2323 | var typeof2; 2324 | if (aa === bb) { 2325 | return ( 2326 | ii < jj 2327 | ? -1 2328 | : 1 2329 | ); 2330 | } 2331 | if (aa === null) { 2332 | return -1; 2333 | } 2334 | if (bb === null) { 2335 | return 1; 2336 | } 2337 | typeof1 = typeof aa; 2338 | typeof2 = typeof bb; 2339 | if (typeof1 === typeof2) { 2340 | return ( 2341 | typeof1 === "object" 2342 | ? 0 2343 | : aa > bb 2344 | ? 1 2345 | : -1 2346 | ); 2347 | } 2348 | if (typeof1 === "boolean") { 2349 | return -1; 2350 | } 2351 | if (typeof2 === "boolean") { 2352 | return 1; 2353 | } 2354 | if (typeof1 === "number") { 2355 | return -1; 2356 | } 2357 | if (typeof2 === "number") { 2358 | return 1; 2359 | } 2360 | if (typeof1 === "string") { 2361 | return -1; 2362 | } 2363 | if (typeof2 === "string") { 2364 | return 1; 2365 | } 2366 | return 0; 2367 | }; 2368 | }()); 2369 | 2370 | 2371 | 2372 | // run node js-env code - init-after 2373 | /* istanbul ignore next */ 2374 | (function () { 2375 | if (local.isBrowser) { 2376 | return; 2377 | } 2378 | 2379 | 2380 | 2381 | local.cliDict = {}; 2382 | 2383 | local.cliDict.dbTableCrudGetManyByQuery = function () { 2384 | /* 2385 | * 2386 | * will get from with json , 2387 | */ 2388 | local.dbTableCreateOne({ 2389 | name: process.argv[3] 2390 | }, function (err, that) { 2391 | // validate no err occurred 2392 | local.assertThrow(!err, err); 2393 | console.log(JSON.stringify(that.crudGetManyByQuery( 2394 | JSON.parse(process.argv[4] || "{}") 2395 | ), null, 4)); 2396 | }); 2397 | }; 2398 | 2399 | local.cliDict.dbTableCrudRemoveManyByQuery = function () { 2400 | /* 2401 | * 2402 | * will remove from with json , 2403 | */ 2404 | local.dbTableCreateOne({ 2405 | name: process.argv[3] 2406 | }, function (err, that) { 2407 | // validate no err occurred 2408 | local.assertThrow(!err, err); 2409 | console.log(JSON.stringify(that.crudRemoveManyByQuery( 2410 | JSON.parse(process.argv[4]) 2411 | ), null, 4)); 2412 | }); 2413 | }; 2414 | 2415 | local.cliDict.dbTableCrudSetManyById = function () { 2416 | /* 2417 | * 2418 | * will set in , 2419 | */ 2420 | local.dbTableCreateOne({ 2421 | name: process.argv[3] 2422 | }, function (err, that) { 2423 | // validate no err occurred 2424 | local.assertThrow(!err, err); 2425 | that.crudSetManyById(JSON.parse(process.argv[4])); 2426 | }); 2427 | }; 2428 | 2429 | local.cliDict.dbTableHeaderDictGet = function () { 2430 | /* 2431 | * 2432 | * will get from , 2433 | */ 2434 | local.dbTableCreateOne({ 2435 | name: process.argv[3] 2436 | }, function (err, that) { 2437 | // validate no err occurred 2438 | local.assertThrow(!err, err); 2439 | var tmp; 2440 | tmp = []; 2441 | that.idIndexList.forEach(function (idIndex) { 2442 | tmp.push({ 2443 | isInteger: idIndex.isInteger, 2444 | name: idIndex.name 2445 | }); 2446 | }); 2447 | console.log(JSON.stringify({ 2448 | idIndexList: tmp, 2449 | sizeLimit: that.sizeLimit, 2450 | sortDefault: that.sortDefault 2451 | }, null, 4)); 2452 | }); 2453 | }; 2454 | 2455 | local.cliDict.dbTableHeaderDictSet = function () { 2456 | /* 2457 | * 2458 | * will set in , 2459 | */ 2460 | local.dbTableCreateOne({ 2461 | name: process.argv[3] 2462 | }, function (err, that) { 2463 | // validate no err occurred 2464 | local.assertThrow(!err, err); 2465 | local.tmp = JSON.parse(process.argv[4]); 2466 | that.sizeLimit = local.tmp.sizeLimit || that.sizeLimit; 2467 | that.sortDefault = local.tmp.sortDefault || that.sortDefault; 2468 | that.save(); 2469 | local.tmp = []; 2470 | that.idIndexList.forEach(function (idIndex) { 2471 | local.tmp.push({ 2472 | isInteger: idIndex.isInteger, 2473 | name: idIndex.name 2474 | }); 2475 | }); 2476 | local.cliDict.dbTableHeaderDictGet(); 2477 | }); 2478 | }; 2479 | 2480 | local.cliDict.dbTableIdIndexCreate = function () { 2481 | /* 2482 | * 2483 | * will create in , 2484 | */ 2485 | local.dbTableCreateOne({ 2486 | name: process.argv[3] 2487 | }, function (err, that) { 2488 | // validate no err occurred 2489 | local.assertThrow(!err, err); 2490 | that.idIndexCreate(JSON.parse(process.argv[4])); 2491 | that.save(); 2492 | local.tmp = []; 2493 | that.idIndexList.forEach(function (idIndex) { 2494 | local.tmp.push({ 2495 | isInteger: idIndex.isInteger, 2496 | name: idIndex.name 2497 | }); 2498 | }); 2499 | local.cliDict.dbTableHeaderDictGet(); 2500 | }); 2501 | }; 2502 | 2503 | local.cliDict.dbTableIdIndexRemove = function () { 2504 | /* 2505 | * 2506 | * will remove from , 2507 | */ 2508 | local.dbTableCreateOne({ 2509 | name: process.argv[3] 2510 | }, function (err, that) { 2511 | // validate no err occurred 2512 | local.assertThrow(!err, err); 2513 | that.idIndexRemove(JSON.parse(process.argv[4])); 2514 | that.save(); 2515 | local.cliDict.dbTableHeaderDictGet(); 2516 | }); 2517 | }; 2518 | 2519 | local.cliDict.dbTableList = function () { 2520 | /* 2521 | * 2522 | * will get from db, 2523 | */ 2524 | local.storageKeys(function (err, data) { 2525 | // validate no err occurred 2526 | local.assertThrow(!err, err); 2527 | console.log(JSON.stringify(data.map(function (element) { 2528 | return element.split(".").slice(1, -1).join("."); 2529 | }), null, 4)); 2530 | }); 2531 | }; 2532 | 2533 | local.cliDict.dbTableRemove = function () { 2534 | /* 2535 | * 2536 | * will remove from db, 2537 | */ 2538 | local.storageRemoveItem("dbTable." + process.argv[3] + ".json", function ( 2539 | err 2540 | ) { 2541 | // validate no err occurred 2542 | local.assertThrow(!err, err); 2543 | local.cliDict.dbTableList(); 2544 | }); 2545 | }; 2546 | 2547 | // run the cli 2548 | if (module === require.main && !globalThis.utility2_rollup) { 2549 | local.cliRun(); 2550 | } 2551 | }()); 2552 | }()); 2553 | -------------------------------------------------------------------------------- /npm_scripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # jslint utility2:true 3 | 4 | shMain () {(set -e 5 | # this function will run the main program 6 | printf "running command 'npm run $*' ...\n" 1>&2 7 | ARG1="$1" 8 | # run command - custom 9 | case "$1" in 10 | esac 11 | # run command - default 12 | case "$ARG1" in 13 | build-ci) 14 | if [ "$npm_package_nameLib" = utility2 ] 15 | then 16 | ./lib.utility2.sh shReadmeTest build_ci.sh 17 | return 18 | fi 19 | utility2 shReadmeTest build_ci.sh 20 | ;; 21 | eval) 22 | shift 23 | "$@" 24 | ;; 25 | heroku-postbuild) 26 | if [ "$npm_package_nameLib" = utility2 ] 27 | then 28 | ./lib.utility2.sh shDeployHeroku 29 | return 30 | fi 31 | npm install kaizhu256/node-utility2#alpha --prefix . 32 | utility2 shDeployHeroku 33 | ;; 34 | start) 35 | export PORT=${PORT:-8080} 36 | if [ -f assets.app.js ] 37 | then 38 | node assets.app.js 39 | else 40 | if [ "$npm_package_nameLib" = utility2 ] 41 | then 42 | export npm_config_mode_auto_restart=1 43 | ./lib.utility2.sh shRun shIstanbulCover test.js 44 | return 45 | fi 46 | utility2 start test.js 47 | fi 48 | ;; 49 | test) 50 | if [ "$npm_package_nameLib" = utility2 ] 51 | then 52 | export PORT=$(./lib.utility2.sh shServerPortRandom) 53 | export PORT_REPL=$(./lib.utility2.sh shServerPortRandom) 54 | export npm_config_mode_auto_restart=1 55 | export npm_config_timeout_default=60000 56 | ./lib.utility2.sh test test.js 57 | return 58 | fi 59 | export PORT=$(utility2 shServerPortRandom) 60 | utility2 test test.js 61 | ;; 62 | utility2) 63 | shift 64 | if [ "$npm_package_nameLib" = utility2 ] 65 | then 66 | ./lib.utility2.sh "$@" 67 | return 68 | fi 69 | utility2 "$@" 70 | ;; 71 | esac 72 | printf "... finished running command 'npm run $*'\n" 1>&2 73 | )} 74 | 75 | # run command 76 | shMain "$npm_lifecycle_event" "$(node -e 'console.log( 77 | JSON.parse(process.env.npm_config_argv).original.join(" ").replace((/^(?:run )?\S+ /), "") 78 | )')" 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "kai zhu ", 3 | "bin": { 4 | "db-lite": "lib.db.js" 5 | }, 6 | "description": "this zero-dependency package will provide a persistent, in-browser database, with a working web-demo", 7 | "devDependencies": { 8 | "electron-lite": "kaizhu256/node-electron-lite#alpha", 9 | "utility2": "kaizhu256/node-utility2#alpha" 10 | }, 11 | "engines": { 12 | "node": ">=10.0" 13 | }, 14 | "homepage": "https://github.com/kaizhu256/node-db-lite", 15 | "keywords": [ 16 | "database", 17 | "embedded", 18 | "indexeddb", 19 | "nedb" 20 | ], 21 | "license": "MIT", 22 | "main": "lib.db.js", 23 | "name": "db-lite", 24 | "nameAliasPublish": "esdb nanodb", 25 | "nameLib": "db", 26 | "nameOriginal": "db-lite", 27 | "os": [ 28 | "darwin", 29 | "linux" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/kaizhu256/node-db-lite.git" 34 | }, 35 | "scripts": { 36 | "build-ci": "./npm_scripts.sh", 37 | "env": "env", 38 | "eval": "./npm_scripts.sh", 39 | "heroku-postbuild": "./npm_scripts.sh", 40 | "postinstall": "./npm_scripts.sh", 41 | "start": "./npm_scripts.sh", 42 | "test": "./npm_scripts.sh", 43 | "utility2": "./npm_scripts.sh" 44 | }, 45 | "version": "2019.8.20" 46 | } 47 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* istanbul instrument in package db */ 2 | /* istanbul ignore next */ 3 | /* jslint utility2:true */ 4 | (function (globalThis) { 5 | "use strict"; 6 | var consoleError; 7 | var local; 8 | // init globalThis 9 | (function () { 10 | try { 11 | globalThis = Function("return this")(); // jslint ignore:line 12 | } catch (ignore) {} 13 | }()); 14 | globalThis.globalThis = globalThis; 15 | // init debug_inline 16 | if (!globalThis["debug\u0049nline"]) { 17 | consoleError = console.error; 18 | globalThis["debug\u0049nline"] = function () { 19 | /* 20 | * this function will both print to stderr 21 | * and return [0] 22 | */ 23 | var argList; 24 | argList = Array.from(arguments); // jslint ignore:line 25 | // debug arguments 26 | globalThis["debug\u0049nlineArguments"] = argList; 27 | consoleError("\n\ndebug\u0049nline"); 28 | consoleError.apply(console, argList); 29 | consoleError("\n"); 30 | // return arg0 for inspection 31 | return argList[0]; 32 | }; 33 | } 34 | // init local 35 | local = {}; 36 | local.local = local; 37 | globalThis.globalLocal = local; 38 | // init isBrowser 39 | local.isBrowser = ( 40 | typeof window === "object" 41 | && window === globalThis 42 | && typeof window.XMLHttpRequest === "function" 43 | && window.document 44 | && typeof window.document.querySelector === "function" 45 | ); 46 | // init function 47 | local.assertThrow = function (passed, message) { 48 | /* 49 | * this function will throw err. if is falsy 50 | */ 51 | var err; 52 | if (passed) { 53 | return; 54 | } 55 | err = ( 56 | // ternary-operator 57 | ( 58 | message 59 | && typeof message.message === "string" 60 | && typeof message.stack === "string" 61 | ) 62 | // if message is errObj, then leave as is 63 | ? message 64 | : new Error( 65 | typeof message === "string" 66 | // if message is a string, then leave as is 67 | ? message 68 | // else JSON.stringify message 69 | : JSON.stringify(message, null, 4) 70 | ) 71 | ); 72 | throw err; 73 | }; 74 | local.functionOrNop = function (fnc) { 75 | /* 76 | * this function will if exists, 77 | * them return , 78 | * else return 79 | */ 80 | return fnc || local.nop; 81 | }; 82 | local.identity = function (value) { 83 | /* 84 | * this function will return 85 | */ 86 | return value; 87 | }; 88 | local.nop = function () { 89 | /* 90 | * this function will do nothing 91 | */ 92 | return; 93 | }; 94 | local.objectAssignDefault = function (target, source) { 95 | /* 96 | * this function will if items from are 97 | * null, undefined, or empty-string, 98 | * then overwrite them with items from 99 | */ 100 | target = target || {}; 101 | Object.keys(source || {}).forEach(function (key) { 102 | if ( 103 | target[key] === null 104 | || target[key] === undefined 105 | || target[key] === "" 106 | ) { 107 | target[key] = target[key] || source[key]; 108 | } 109 | }); 110 | return target; 111 | }; 112 | // require builtin 113 | if (!local.isBrowser) { 114 | local.assert = require("assert"); 115 | local.buffer = require("buffer"); 116 | local.child_process = require("child_process"); 117 | local.cluster = require("cluster"); 118 | local.crypto = require("crypto"); 119 | local.dgram = require("dgram"); 120 | local.dns = require("dns"); 121 | local.domain = require("domain"); 122 | local.events = require("events"); 123 | local.fs = require("fs"); 124 | local.http = require("http"); 125 | local.https = require("https"); 126 | local.net = require("net"); 127 | local.os = require("os"); 128 | local.path = require("path"); 129 | local.querystring = require("querystring"); 130 | local.readline = require("readline"); 131 | local.repl = require("repl"); 132 | local.stream = require("stream"); 133 | local.string_decoder = require("string_decoder"); 134 | local.timers = require("timers"); 135 | local.tls = require("tls"); 136 | local.tty = require("tty"); 137 | local.url = require("url"); 138 | local.util = require("util"); 139 | local.vm = require("vm"); 140 | local.zlib = require("zlib"); 141 | } 142 | }(this)); 143 | 144 | 145 | 146 | (function (local) { 147 | "use strict"; 148 | 149 | 150 | 151 | // run shared js-env code - init-before 152 | (function () { 153 | // init local 154 | local = ( 155 | globalThis.utility2 || require("utility2") 156 | ).requireReadme(); 157 | globalThis.local = local; 158 | // init test 159 | local.testRunDefault(local); 160 | }()); 161 | 162 | 163 | 164 | // run shared js-env code - function 165 | (function () { 166 | local.testCase_dbLoad_err = function (opt, onError) { 167 | /* 168 | * this function will test dbLoad's err handling-behavior 169 | */ 170 | local.testMock([ 171 | [ 172 | local, { 173 | storageKeys: function (fnc) { 174 | fnc(local.errDefault); 175 | } 176 | } 177 | ] 178 | ], function (onError) { 179 | local.dbLoad(function (err) { 180 | // validate err occurred 181 | local.assertThrow(err, err); 182 | }); 183 | onError(null, opt); 184 | }, onError); 185 | }; 186 | 187 | local.testCase_dbTable_crudGetManyByQuery = function (opt, onError) { 188 | /* 189 | * this function will test dbTable's crudGetManyByQuery handling-behavior 190 | */ 191 | opt = {}; 192 | // test dbTableCreateOne's create handling-behavior 193 | opt.dbTable = local.dbTableCreateOne({ 194 | name: "testCase_dbTable_crudGetManyByQuery" 195 | }); 196 | // validate dbRowCount 197 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 198 | // test isDirty handling-behavior 199 | opt.dbTable.crudRemoveOneById(opt.dbTable.crudSetOneById({ 200 | field1: "dirty" 201 | })); 202 | // test null-case handling-behavior 203 | opt.data = opt.dbTable.crudGetManyByQuery({ 204 | // test shuffle handling-behavior 205 | shuffle: true 206 | }); 207 | local.assertJsonEqual(opt.data.length, 0); 208 | local.assertJsonEqual(opt.data, []); 209 | opt.data = ([ 210 | [], 211 | [[], "", 0, {}, false, null, undefined], 212 | -0.5, 213 | -1, 214 | -Infinity, 215 | 0, 216 | 0.5, 217 | 1, 218 | Infinity, 219 | NaN, 220 | {}, 221 | false, 222 | null, 223 | true, 224 | undefined 225 | ]).sort().map(function (element) { 226 | return { 227 | field1: element 228 | }; 229 | }); 230 | opt.data = opt.data.concat(opt.data.map(function (dbRow) { 231 | return { 232 | field1: JSON.stringify(dbRow.field1) 233 | }; 234 | })); 235 | // test dbTableCreateOne's crudSetManyById handling-behavior 236 | opt.data = opt.dbTable.crudSetManyById(opt.data); 237 | // validate dbRowCount 238 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 30); 239 | // validate data 240 | [ 241 | [], 242 | [[], "", 0, {}, false, null, undefined], 243 | -0.5, 244 | -1, 245 | undefined, 246 | 0, 247 | 0.5, 248 | 1, 249 | undefined, 250 | undefined, 251 | {}, 252 | false, 253 | undefined, 254 | true, 255 | undefined, 256 | "[]", 257 | "[[],\"\",0,{},false,null,null]", 258 | "-0.5", 259 | "-1", 260 | "null", 261 | "0", 262 | "0.5", 263 | "1", 264 | "null", 265 | "null", 266 | "{}", 267 | "false", 268 | "null", 269 | "true", 270 | undefined 271 | ].forEach(function (element, ii) { 272 | local.assertJsonEqual(element, opt.data[ii].field1); 273 | }); 274 | // test null-case handling-behavior 275 | opt.data = opt.dbTable.crudGetManyByQuery({ 276 | query: { 277 | field1: { 278 | $undefined: null 279 | } 280 | }, 281 | sort: [ 282 | { 283 | fieldName: "field1" 284 | } 285 | ] 286 | }); 287 | local.assertJsonEqual(opt.data.length, 0); 288 | local.assertJsonEqual(opt.data, []); 289 | // test null-case handling-behavior 290 | opt.data = opt.dbTable.crudGetManyByQuery({ 291 | query: { 292 | field1: { 293 | $undefined: {} 294 | } 295 | }, 296 | sort: [ 297 | { 298 | fieldName: "field1" 299 | } 300 | ] 301 | }); 302 | local.assertJsonEqual(opt.data.length, 0); 303 | local.assertJsonEqual(opt.data, []); 304 | // test $eq's boolean handling-behavior 305 | opt.data = opt.dbTable.crudGetManyByQuery({ 306 | query: { 307 | field1: true 308 | }, 309 | sort: [ 310 | { 311 | fieldName: "field1" 312 | } 313 | ] 314 | }).map(function (dbRow) { 315 | return dbRow.field1; 316 | }); 317 | local.assertJsonEqual(opt.data.length, 1); 318 | local.assertJsonEqual(opt.data, [ 319 | true 320 | ]); 321 | // test $eq's null-case handling-behavior 322 | opt.data = opt.dbTable.crudGetManyByQuery({ 323 | query: { 324 | field1: null 325 | }, 326 | sort: [ 327 | { 328 | fieldName: "field1" 329 | } 330 | ] 331 | }).map(function (dbRow) { 332 | return dbRow.field1; 333 | }); 334 | local.assertJsonEqual(opt.data.length, 7); 335 | local.assertJsonEqual( 336 | opt.data.slice(0, -1), 337 | [ 338 | null, null, null, null, null, null 339 | ] 340 | ); 341 | // test $eq's number handling-behavior 342 | opt.data = opt.dbTable.crudGetManyByQuery({ 343 | query: { 344 | field1: 0 345 | }, 346 | sort: [ 347 | { 348 | fieldName: "field1" 349 | } 350 | ] 351 | }).map(function (dbRow) { 352 | return dbRow.field1; 353 | }); 354 | local.assertJsonEqual(opt.data.length, 2); 355 | local.assertJsonEqual(opt.data.slice(0, -1), [ 356 | 0 357 | ]); 358 | // test $eq's string handling-behavior 359 | opt.data = opt.dbTable.crudGetManyByQuery({ 360 | query: { 361 | field1: "{}" 362 | }, 363 | sort: [ 364 | { 365 | fieldName: "field1" 366 | } 367 | ] 368 | }).map(function (dbRow) { 369 | return dbRow.field1; 370 | }); 371 | local.assertJsonEqual(opt.data.length, 1); 372 | local.assertJsonEqual(opt.data, [ 373 | "{}" 374 | ]); 375 | // test $exists's false handling-behavior 376 | opt.data = opt.dbTable.crudGetManyByQuery({ 377 | query: { 378 | field1: { 379 | $exists: false 380 | } 381 | }, 382 | sort: [ 383 | { 384 | fieldName: "field1" 385 | } 386 | ] 387 | }).map(function (dbRow) { 388 | return dbRow.field1; 389 | }); 390 | local.assertJsonEqual(opt.data.length, 7); 391 | // test $exists's null-case handling-behavior 392 | opt.data = opt.dbTable.crudGetManyByQuery({ 393 | query: { 394 | field1: { 395 | $exists: null 396 | } 397 | }, 398 | sort: [ 399 | { 400 | fieldName: "field1" 401 | } 402 | ] 403 | }).map(function (dbRow) { 404 | return dbRow.field1; 405 | }); 406 | local.assertJsonEqual(opt.data.length, 7); 407 | // test $exists's true handling-behavior 408 | opt.data = opt.dbTable.crudGetManyByQuery({ 409 | query: { 410 | field1: { 411 | $exists: true 412 | } 413 | }, 414 | sort: [ 415 | { 416 | fieldName: "field1" 417 | } 418 | ] 419 | }).map(function (dbRow) { 420 | return dbRow.field1; 421 | }); 422 | local.assertJsonEqual(opt.data.length, 23); 423 | // test $gt's boolean handling-behavior 424 | // test $lt's boolean handling-behavior 425 | opt.data = opt.dbTable.crudGetManyByQuery({ 426 | query: { 427 | field1: { 428 | $gt: false, 429 | $lt: true 430 | } 431 | }, 432 | sort: [ 433 | { 434 | fieldName: "field1" 435 | } 436 | ] 437 | }); 438 | local.assertJsonEqual(opt.data.length, 0); 439 | local.assertJsonEqual(opt.data, []); 440 | // test $gt's null-case handling-behavior 441 | // test $lt's null-case handling-behavior 442 | opt.data = opt.dbTable.crudGetManyByQuery({ 443 | query: { 444 | field1: { 445 | $gt: false, 446 | $lt: true 447 | } 448 | }, 449 | sort: [ 450 | { 451 | fieldName: "field1" 452 | } 453 | ] 454 | }); 455 | local.assertJsonEqual(opt.data.length, 0); 456 | local.assertJsonEqual(opt.data, []); 457 | // test $gt's number handling-behavior 458 | // test $lt's number handling-behavior 459 | opt.data = opt.dbTable.crudGetManyByQuery({ 460 | query: { 461 | field1: { 462 | $gt: -1, 463 | $lt: 1 464 | } 465 | }, 466 | sort: [ 467 | { 468 | fieldName: "field1" 469 | } 470 | ] 471 | }).map(function (dbRow) { 472 | return dbRow.field1; 473 | }); 474 | local.assertJsonEqual(opt.data.length, 4); 475 | local.assertJsonEqual(opt.data.slice(0, -1), [ 476 | -0.5, 0, 0.5 477 | ]); 478 | // test $gt's string handling-behavior 479 | // test $lt's string handling-behavior 480 | opt.data = opt.dbTable.crudGetManyByQuery({ 481 | query: { 482 | field1: { 483 | $gt: "false", 484 | $lt: "true" 485 | } 486 | }, 487 | sort: [ 488 | { 489 | fieldName: "field1" 490 | } 491 | ] 492 | }).map(function (dbRow) { 493 | return dbRow.field1; 494 | }); 495 | local.assertJsonEqual(opt.data.length, 4); 496 | local.assertJsonEqual(opt.data, [ 497 | "null", "null", "null", "null" 498 | ]); 499 | // test $gte's boolean handling-behavior 500 | // test $lte's boolean handling-behavior 501 | // test $ne's boolean handling-behavior 502 | opt.data = opt.dbTable.crudGetManyByQuery({ 503 | query: { 504 | field1: { 505 | $gte: false, 506 | $lte: true, 507 | $ne: false 508 | } 509 | }, 510 | sort: [ 511 | { 512 | fieldName: "field1" 513 | } 514 | ] 515 | }).map(function (dbRow) { 516 | return dbRow.field1; 517 | }); 518 | local.assertJsonEqual(opt.data.length, 2); 519 | local.assertJsonEqual(opt.data.slice(0, -1), [ 520 | true 521 | ]); 522 | // test $gte's null-case handling-behavior 523 | // test $lte's null-case handling-behavior 524 | // test $ne's null-case handling-behavior 525 | opt.data = opt.dbTable.crudGetManyByQuery({ 526 | query: { 527 | field1: { 528 | $gte: null, 529 | $lte: null, 530 | $ne: null 531 | } 532 | }, 533 | sort: [ 534 | { 535 | fieldName: "field1" 536 | } 537 | ] 538 | }).map(function (dbRow) { 539 | return dbRow.field1; 540 | }); 541 | local.assertJsonEqual(opt.data.length, 1); 542 | local.assertJsonEqual(opt.data.slice(0, -1), []); 543 | // test $gte's number handling-behavior 544 | // test $lte's number handling-behavior 545 | // test $ne's number handling-behavior 546 | opt.data = opt.dbTable.crudGetManyByQuery({ 547 | query: { 548 | field1: { 549 | $gte: -1, 550 | $lte: 1, 551 | $ne: 0 552 | } 553 | }, 554 | sort: [ 555 | { 556 | fieldName: "field1" 557 | } 558 | ] 559 | }).map(function (dbRow) { 560 | return dbRow.field1; 561 | }); 562 | local.assertJsonEqual(opt.data.length, 5); 563 | local.assertJsonEqual(opt.data.slice(0, -1), [ 564 | -1, -0.5, 0.5, 1 565 | ]); 566 | // test $gte's number handling-behavior 567 | // test $lte's number handling-behavior 568 | // test $ne's number handling-behavior 569 | // test fieldList handling-behavior 570 | // test limit handling-behavior 571 | // test skip handling-behavior 572 | // test sort's isDescending handling-behavior 573 | opt.data = opt.dbTable.crudGetManyByQuery({ 574 | fieldList: [ 575 | "field1" 576 | ], 577 | limit: 2, 578 | query: { 579 | field1: { 580 | $gte: -1, 581 | $lte: 1, 582 | $ne: 0 583 | } 584 | }, 585 | skip: 2, 586 | sort: [ 587 | { 588 | fieldName: "field1", 589 | isDescending: true 590 | } 591 | ] 592 | }).map(function (dbRow) { 593 | return dbRow.field1; 594 | }); 595 | local.assertJsonEqual(opt.data.length, 2); 596 | local.assertJsonEqual(opt.data.slice(), [ 597 | 0.5, -0.5 598 | ]); 599 | // test $gte's string handling-behavior 600 | // test $lte's string handling-behavior 601 | // test $ne's string handling-behavior 602 | opt.data = opt.dbTable.crudGetManyByQuery({ 603 | query: { 604 | field1: { 605 | $gte: "false", 606 | $lte: "true", 607 | $ne: "null" 608 | } 609 | }, 610 | sort: [ 611 | { 612 | fieldName: "field1" 613 | } 614 | ] 615 | }).map(function (dbRow) { 616 | return dbRow.field1; 617 | }); 618 | local.assertJsonEqual(opt.data.length, 2); 619 | local.assertJsonEqual(opt.data, [ 620 | "false", "true" 621 | ]); 622 | // test $in's list handling-behavior 623 | opt.data = opt.dbTable.crudGetManyByQuery({ 624 | query: { 625 | field1: { 626 | $in: [ 627 | true, 1 628 | ] 629 | } 630 | }, 631 | sort: [ 632 | { 633 | fieldName: "field1" 634 | } 635 | ] 636 | }).map(function (dbRow) { 637 | return dbRow.field1; 638 | }); 639 | local.assertJsonEqual(opt.data.length, 2); 640 | local.assertJsonEqual(opt.data, [ 641 | true, 1 642 | ]); 643 | // test $in's null-case handling-behavior 644 | opt.data = opt.dbTable.crudGetManyByQuery({ 645 | query: { 646 | field1: { 647 | $in: null 648 | } 649 | }, 650 | sort: [ 651 | { 652 | fieldName: "field1" 653 | } 654 | ] 655 | }); 656 | local.assertJsonEqual(opt.data.length, 0); 657 | local.assertJsonEqual(opt.data, []); 658 | // test $in's string handling-behavior 659 | opt.data = opt.dbTable.crudGetManyByQuery({ 660 | query: { 661 | field1: { 662 | $in: "0.5" 663 | } 664 | }, 665 | sort: [ 666 | { 667 | fieldName: "field1" 668 | } 669 | ] 670 | }).map(function (dbRow) { 671 | return dbRow.field1; 672 | }); 673 | local.assertJsonEqual(opt.data.length, 3); 674 | local.assertJsonEqual(opt.data.slice(0, -1), [ 675 | "0", "0.5" 676 | ]); 677 | // test $nin's list handling-behavior 678 | opt.data = opt.dbTable.crudGetManyByQuery({ 679 | query: { 680 | field1: { 681 | $nin: [ 682 | 0, null 683 | ] 684 | } 685 | }, 686 | sort: [ 687 | { 688 | fieldName: "field1" 689 | } 690 | ] 691 | }).map(function (dbRow) { 692 | return dbRow.field1; 693 | }); 694 | local.assertJsonEqual(opt.data.length, 22); 695 | // test $nin's null-case handling-behavior 696 | opt.data = opt.dbTable.crudGetManyByQuery({ 697 | query: { 698 | field1: { 699 | $nin: null 700 | } 701 | }, 702 | sort: [ 703 | { 704 | fieldName: "field1" 705 | } 706 | ] 707 | }); 708 | local.assertJsonEqual(opt.data.length, 0); 709 | local.assertJsonEqual(opt.data, []); 710 | // test $nin's string handling-behavior 711 | opt.data = opt.dbTable.crudGetManyByQuery({ 712 | query: { 713 | field1: { 714 | $nin: "[[],\"\",0,1,{},false,null,true]" 715 | } 716 | }, 717 | sort: [ 718 | { 719 | fieldName: "field1" 720 | } 721 | ] 722 | }).map(function (dbRow) { 723 | return dbRow.field1; 724 | }); 725 | local.assertJsonEqual(opt.data.length, 4); 726 | local.assertJsonEqual(opt.data.slice(0, -1), [ 727 | "-0.5", "-1", "0.5" 728 | ]); 729 | // test $not's number handling-behavior 730 | opt.data = opt.dbTable.crudGetManyByQuery({ 731 | query: { 732 | field1: { 733 | $not: { 734 | $gte: 0 735 | } 736 | } 737 | }, 738 | sort: [ 739 | { 740 | fieldName: "field1" 741 | } 742 | ] 743 | }).map(function (dbRow) { 744 | return dbRow.field1; 745 | }); 746 | local.assertJsonEqual(opt.data.length, 26); 747 | local.assertJsonEqual(opt.data.slice(0, 15), [ 748 | null, 749 | null, 750 | null, 751 | null, 752 | null, 753 | null, 754 | false, 755 | true, 756 | -1, 757 | -0.5, 758 | "-0.5", 759 | "-1", 760 | "0", 761 | "0.5", 762 | "1" 763 | ]); 764 | // test $not's string handling-behavior 765 | opt.data = opt.dbTable.crudGetManyByQuery({ 766 | query: { 767 | field1: { 768 | $not: { 769 | $gte: "0" 770 | } 771 | } 772 | }, 773 | sort: [ 774 | { 775 | fieldName: "field1" 776 | } 777 | ] 778 | }).map(function (dbRow) { 779 | return dbRow.field1; 780 | }); 781 | local.assertJsonEqual(opt.data.length, 17); 782 | local.assertJsonEqual(opt.data.slice(0, -1), [ 783 | null, 784 | null, 785 | null, 786 | null, 787 | null, 788 | null, 789 | false, 790 | true, 791 | -1, 792 | -0.5, 793 | 0, 794 | 0.5, 795 | 1, 796 | "-0.5", 797 | "-1", 798 | {} 799 | ]); 800 | // test $or's null-case handling-behavior 801 | opt.data = opt.dbTable.crudGetManyByQuery({ 802 | query: { 803 | $or: null 804 | }, 805 | sort: [ 806 | { 807 | fieldName: "field1" 808 | } 809 | ] 810 | }); 811 | local.assertJsonEqual(opt.data.length, 0); 812 | local.assertJsonEqual(opt.data, []); 813 | // test $or's empty-list handling-behavior 814 | opt.data = opt.dbTable.crudGetManyByQuery({ 815 | query: { 816 | $or: [] 817 | }, 818 | sort: [ 819 | { 820 | fieldName: "field1" 821 | } 822 | ] 823 | }); 824 | local.assertJsonEqual(opt.data.length, 0); 825 | local.assertJsonEqual(opt.data, []); 826 | // test $or's list handling-behavior 827 | opt.data = opt.dbTable.crudGetManyByQuery({ 828 | query: { 829 | $or: [ 830 | { 831 | field1: { 832 | $eq: -0.5 833 | } 834 | }, { 835 | field1: { 836 | $eq: 0 837 | } 838 | }, { 839 | field1: { 840 | $eq: 0.5 841 | } 842 | } 843 | ] 844 | }, 845 | sort: [ 846 | { 847 | fieldName: "field1" 848 | } 849 | ] 850 | }).map(function (dbRow) { 851 | return dbRow.field1; 852 | }); 853 | local.assertJsonEqual(opt.data.length, 4); 854 | local.assertJsonEqual(opt.data.slice(0, -1), [ 855 | -0.5, 0, 0.5 856 | ]); 857 | // test $regex's regex handling-behavior 858 | opt.data = opt.dbTable.crudGetManyByQuery({ 859 | query: { 860 | field1: { 861 | $regex: ( 862 | /1|true/ 863 | ) 864 | } 865 | }, 866 | sort: [ 867 | { 868 | fieldName: "field1" 869 | } 870 | ] 871 | }).map(function (dbRow) { 872 | return dbRow.field1; 873 | }); 874 | local.assertJsonEqual(opt.data.length, 6); 875 | local.assertJsonEqual(opt.data, [ 876 | true, -1, 1, "-1", "1", "true" 877 | ]); 878 | // test $regex's null-case handling-behavior 879 | opt.data = opt.dbTable.crudGetManyByQuery({ 880 | query: { 881 | field1: { 882 | $regex: null 883 | } 884 | }, 885 | sort: [ 886 | { 887 | fieldName: "field1" 888 | } 889 | ] 890 | }); 891 | local.assertJsonEqual(opt.data.length, 0); 892 | local.assertJsonEqual(opt.data, []); 893 | // test $typeof's boolean handling-behavior 894 | opt.data = opt.dbTable.crudGetManyByQuery({ 895 | query: { 896 | field1: { 897 | $typeof: "boolean" 898 | } 899 | }, 900 | sort: [ 901 | { 902 | fieldName: "field1" 903 | } 904 | ] 905 | }).map(function (dbRow) { 906 | return dbRow.field1; 907 | }); 908 | local.assertJsonEqual(opt.data.length, 3); 909 | // test $typeof's null-case handling-behavior 910 | opt.data = opt.dbTable.crudGetManyByQuery({ 911 | query: { 912 | field1: { 913 | $typeof: null 914 | } 915 | }, 916 | sort: [ 917 | { 918 | fieldName: "field1" 919 | } 920 | ] 921 | }); 922 | local.assertJsonEqual(opt.data.length, 0); 923 | local.assertJsonEqual(opt.data, []); 924 | // test $typeof's number handling-behavior 925 | opt.data = opt.dbTable.crudGetManyByQuery({ 926 | query: { 927 | field1: { 928 | $typeof: "number" 929 | } 930 | }, 931 | sort: [ 932 | { 933 | fieldName: "field1" 934 | } 935 | ] 936 | }).map(function (dbRow) { 937 | return dbRow.field1; 938 | }); 939 | local.assertJsonEqual(opt.data.length, 6); 940 | // test $typeof's object handling-behavior 941 | opt.data = opt.dbTable.crudGetManyByQuery({ 942 | query: { 943 | field1: { 944 | $typeof: "object" 945 | } 946 | }, 947 | sort: [ 948 | { 949 | fieldName: "field1" 950 | } 951 | ] 952 | }).map(function (dbRow) { 953 | return dbRow.field1; 954 | }); 955 | local.assertJsonEqual(opt.data.length, 8); 956 | // test $typeof's string handling-behavior 957 | opt.data = opt.dbTable.crudGetManyByQuery({ 958 | query: { 959 | field1: { 960 | $typeof: "string" 961 | } 962 | }, 963 | sort: [ 964 | { 965 | fieldName: "field1" 966 | } 967 | ] 968 | }).map(function (dbRow) { 969 | return dbRow.field1; 970 | }); 971 | local.assertJsonEqual(opt.data.length, 15); 972 | // test $typeof's symbol handling-behavior 973 | opt.data = opt.dbTable.crudGetManyByQuery({ 974 | query: { 975 | field1: { 976 | $typeof: "symbol" 977 | } 978 | }, 979 | sort: [ 980 | { 981 | fieldName: "field1" 982 | } 983 | ] 984 | }); 985 | local.assertJsonEqual(opt.data.length, 0); 986 | local.assertJsonEqual(opt.data, []); 987 | // test $typeof's undefined handling-behavior 988 | opt.data = opt.dbTable.crudGetManyByQuery({ 989 | query: { 990 | field1: { 991 | $typeof: "undefined" 992 | } 993 | }, 994 | sort: [ 995 | { 996 | fieldName: "field1" 997 | } 998 | ] 999 | }); 1000 | local.assertJsonEqual(opt.data.length, 0); 1001 | local.assertJsonEqual(opt.data, []); 1002 | onError(); 1003 | }; 1004 | 1005 | local.testCase_dbTable_crudNullCase = function (opt, onError) { 1006 | /* 1007 | * this function will test dbTable's crud null-case handling-behavior 1008 | */ 1009 | opt = {}; 1010 | // test dbTableCreateMany's null-case handling-behavior 1011 | local.dbTableCreateMany(); 1012 | // test dbTableCreateOne's onError handling-behavior 1013 | opt.dbTable = local.dbTableCreateOne({ 1014 | name: "testCase_dbTable_crudNullCase" 1015 | }, local.onErrorDefault); 1016 | // test dbTableCreateOne's null-case handling-behavior 1017 | opt.dbTable = local.dbTableCreateOne({ 1018 | name: "testCase_dbTable_crudNullCase" 1019 | }); 1020 | // test crudRemoveAll's null-case handling-behavior 1021 | opt.dbTable.crudRemoveAll(); 1022 | // test cancel-pending-save handling-behavior 1023 | opt.dbTable.save(local.nop); 1024 | // test drop's null-case handling-behavior 1025 | opt.dbTable.drop(); 1026 | // test idIndexCreate's null-case handling-behavior 1027 | opt.dbTable.idIndexCreate({ 1028 | name: "_id" 1029 | }); 1030 | // test idIndexRemove's null-case handling-behavior 1031 | opt.dbTable.idIndexRemove({ 1032 | name: "_id" 1033 | }); 1034 | // validate dbRowCount 1035 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1036 | // test crudCountManyByQuery's null-case handling-behavior 1037 | opt.data = opt.dbTable.crudCountManyByQuery(); 1038 | // validate dbRowCount 1039 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1040 | // validate data 1041 | local.assertJsonEqual(opt.data, 0); 1042 | // test crudGetManyById's null-case handling-behavior 1043 | opt.data = opt.dbTable.crudGetManyById(); 1044 | // validate dbRowCount 1045 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1046 | // validate data 1047 | local.assertJsonEqual(opt.data, []); 1048 | // test crudGetManyByQuery's null-case handling-behavior 1049 | opt.data = opt.dbTable.crudGetManyByQuery(); 1050 | // validate dbRowCount 1051 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1052 | // validate data 1053 | local.assertJsonEqual(opt.data, []); 1054 | // test crudGetOneById's null-case handling-behavior 1055 | opt.data = opt.dbTable.crudGetOneById(); 1056 | // validate dbRowCount 1057 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1058 | // validate data 1059 | local.assertJsonEqual(opt.data, null); 1060 | // test crudGetOneByRandom's null-case handling-behavior 1061 | opt.data = opt.dbTable.crudGetOneByRandom(); 1062 | // validate dbRowCount 1063 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1064 | // validate data 1065 | local.assertJsonEqual(opt.data, null); 1066 | // test crudGetOneByQuery's null-case handling-behavior 1067 | opt.data = opt.dbTable.crudGetOneByQuery(); 1068 | // validate dbRowCount 1069 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1070 | // validate data 1071 | local.assertJsonEqual(opt.data, null); 1072 | // test crudRemoveManyById's null-case handling-behavior 1073 | opt.data = opt.dbTable.crudRemoveManyById(); 1074 | // validate dbRowCount 1075 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1076 | // validate data 1077 | local.assertJsonEqual(opt.data, []); 1078 | // test crudRemoveManyByQuery's null-case handling-behavior 1079 | opt.data = opt.dbTable.crudRemoveManyByQuery(); 1080 | // validate dbRowCount 1081 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1082 | // validate data 1083 | local.assertJsonEqual(opt.data, []); 1084 | // test crudRemoveOneById's null-case handling-behavior 1085 | opt.data = opt.dbTable.crudRemoveOneById(); 1086 | // validate dbRowCount 1087 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1088 | // validate data 1089 | local.assertJsonEqual(opt.data, null); 1090 | // test crudUpdateManyById's null-case handling-behavior 1091 | opt.data = opt.dbTable.crudUpdateManyById(); 1092 | // validate dbRowCount 1093 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1094 | // validate data 1095 | local.assertJsonEqual(opt.data, []); 1096 | // test crudUpdateManyByQuery's null-case handling-behavior 1097 | opt.data = opt.dbTable.crudUpdateManyByQuery(); 1098 | // validate dbRowCount 1099 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1100 | // validate data 1101 | local.assertJsonEqual(opt.data, []); 1102 | // test crudSetOneById's and crudUpdateOneById's null-case handling-behavior 1103 | [ 1104 | "crudSetOneById", "crudUpdateOneById" 1105 | ].forEach(function (operation) { 1106 | opt.data = opt.dbTable[operation](); 1107 | // validate dbRowCount 1108 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 1); 1109 | opt._id = opt.data._id; 1110 | // validate timestamp 1111 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1112 | // test crudRemoveOneById's soft-delete handling-behavior 1113 | opt.data = opt.dbTable.crudRemoveOneById(opt); 1114 | // validate dbRowCount 1115 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1116 | // validate data 1117 | local.assertJsonEqual(opt.data._id, opt._id); 1118 | }); 1119 | // test crudGetOneById's null-case handling-behavior 1120 | opt.data = opt.dbTable.crudGetOneById(opt); 1121 | // validate dbRowCount 1122 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1123 | // validate data 1124 | local.assertJsonEqual(opt.data, null); 1125 | // test crudRemoveOneById's null-case handling-behavior 1126 | opt.data = opt.dbTable.crudRemoveOneById(opt); 1127 | // validate dbRowCount 1128 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1129 | // validate data 1130 | local.assertJsonEqual(opt.data, null); 1131 | onError(); 1132 | }; 1133 | 1134 | local.testCase_dbTable_crudXxxById = function (opt, onError) { 1135 | /* 1136 | * this function will test dbTable's crudXxxById handling-behavior 1137 | */ 1138 | opt = {}; 1139 | // test dbTableCreateMany's create handling-behavior 1140 | opt.dbTable = local.dbTableCreateMany([ 1141 | { 1142 | idIndexCreateList: [ 1143 | null 1144 | ], 1145 | idIndexRemoveList: [ 1146 | null 1147 | ], 1148 | name: "testCase_dbTable_crudXxxById" 1149 | } 1150 | ])[0]; 1151 | // validate dbRowCount 1152 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1153 | // test crudSetManyById's insert handling-behavior 1154 | opt.data = opt.dbTable.crudSetManyById([ 1155 | null, null 1156 | ]); 1157 | // validate dbRowCount 1158 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1159 | // validate data 1160 | local.assertJsonEqual(opt.data.length, 2); 1161 | // test crudRemoveManyById's soft-delete handling-behavior 1162 | opt.data = opt.dbTable.crudRemoveManyById(opt.data); 1163 | // validate dbRowCount 1164 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 0); 1165 | // validate data 1166 | local.assertJsonEqual(opt.data.length, 2); 1167 | // test crudSetManyById's insert handling-behavior 1168 | opt.data = opt.dbTable.crudSetManyById([ 1169 | null, { 1170 | field1: 1, 1171 | field2: 2, 1172 | field3: 3 1173 | } 1174 | ])[1]; 1175 | // validate dbRowCount 1176 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1177 | // validate timestamp 1178 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1179 | // validate data 1180 | local.assertJsonNotEqual(opt.data._id, opt._id); 1181 | local.assertJsonEqual(opt.data.id2, undefined); 1182 | local.assertJsonEqual(opt.data.field1, 1); 1183 | local.assertJsonEqual(opt.data.field2, 2); 1184 | local.assertJsonEqual(opt.data.field3, 3); 1185 | // test idIndexCreate's create handling-behavior 1186 | // hack-istanbul - $isRemoved 1187 | opt.dbTable.crudSetOneById({ 1188 | _id: "undefined" 1189 | }); 1190 | opt.dbTable.crudRemoveOneById({ 1191 | _id: "undefined" 1192 | }); 1193 | opt._id = opt.data._id; 1194 | opt.dbTable.idIndexCreate({ 1195 | isInteger: true, 1196 | name: "id2" 1197 | }); 1198 | // test crudGetManyById's get handling-behavior 1199 | opt.data = opt.dbTable.crudGetManyById([ 1200 | { 1201 | _id: opt._id 1202 | } 1203 | ])[0]; 1204 | // validate dbRowCount 1205 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1206 | // validate data 1207 | local.assertJsonEqual(opt.data._id, opt._id); 1208 | local.assertJsonNotEqual(opt.data.id2, opt.id2); 1209 | local.assertJsonEqual(opt.data.field1, 1); 1210 | local.assertJsonEqual(opt.data.field2, 2); 1211 | // test crudUpdateManyById's update handling-behavior 1212 | opt.id2 = opt.data.id2; 1213 | opt.data = opt.dbTable.crudUpdateManyById([ 1214 | { 1215 | id2: opt.id2, 1216 | field2: NaN, 1217 | field3: [ 1218 | new Date(0) 1219 | ] 1220 | } 1221 | ])[0]; 1222 | // validate dbRowCount 1223 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1224 | // validate timestamp 1225 | local.assertThrow(opt.data._timeCreated <= opt.data._timeUpdated, opt.data); 1226 | // validate data 1227 | local.assertJsonEqual(opt.data._id, opt._id); 1228 | local.assertJsonEqual(opt.data.id2, opt.id2); 1229 | local.assertJsonEqual(opt.data.field1, 1); 1230 | local.assertJsonEqual(opt.data.field2, undefined); 1231 | local.assertJsonEqual(opt.data.field3, [ 1232 | "1970-01-01T00:00:00.000Z" 1233 | ]); 1234 | // test crudSetManyById's replace handling-behavior 1235 | opt.data = opt.dbTable.crudSetManyById([ 1236 | { 1237 | id2: opt.id2 1238 | } 1239 | ])[0]; 1240 | // validate dbRowCount 1241 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1242 | // validate timestamp 1243 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1244 | // validate data 1245 | local.assertJsonEqual(opt.data._id, opt._id); 1246 | local.assertJsonEqual(opt.data.id2, opt.id2); 1247 | local.assertJsonEqual(opt.data.field1, undefined); 1248 | local.assertJsonEqual(opt.data.field2, undefined); 1249 | local.assertJsonEqual(opt.data.field3, undefined); 1250 | // test crudUpdateManyById's update handling-behavior 1251 | opt.data = opt.dbTable.crudUpdateManyById([ 1252 | { 1253 | id2: opt.id2, 1254 | field1: 1 1255 | } 1256 | ])[0]; 1257 | // validate dbRowCount 1258 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1259 | // validate timestamp 1260 | local.assertThrow(opt.data._timeCreated <= opt.data._timeUpdated, opt.data); 1261 | // validate data 1262 | local.assertJsonEqual(opt.data._id, opt._id); 1263 | local.assertJsonEqual(opt.data.id2, opt.id2); 1264 | local.assertJsonEqual(opt.data.field1, 1); 1265 | local.assertJsonEqual(opt.data.field2, undefined); 1266 | local.assertJsonEqual(opt.data.field3, undefined); 1267 | // test crudRemoveManyById's soft-delete handling-behavior 1268 | opt.data = opt.dbTable.crudRemoveManyById([ 1269 | opt 1270 | ])[0]; 1271 | // validate dbRowCount 1272 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 1); 1273 | // validate data 1274 | local.assertJsonEqual(opt.data._id, opt._id); 1275 | local.assertJsonEqual(opt.data.id2, opt.id2); 1276 | local.assertJsonEqual(opt.data.field1, 1); 1277 | local.assertJsonEqual(opt.data.field2, undefined); 1278 | local.assertJsonEqual(opt.data.field3, undefined); 1279 | // test crudSetManyById's re-insert handling-behavior 1280 | opt.data = opt.dbTable.crudSetManyById([ 1281 | { 1282 | id2: opt.id2 1283 | } 1284 | ])[0]; 1285 | // validate dbRowCount 1286 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1287 | // validate timestamp 1288 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1289 | // validate data 1290 | local.assertJsonNotEqual(opt.data._id, opt._id); 1291 | local.assertJsonEqual(opt.data.id2, opt.id2); 1292 | local.assertJsonEqual(opt.data.field1, undefined); 1293 | local.assertJsonEqual(opt.data.field2, undefined); 1294 | local.assertJsonEqual(opt.data.field3, undefined); 1295 | // test crudRemoveManyById's soft-delete handling-behavior 1296 | opt._id = opt.data._id; 1297 | opt.data = opt.dbTable.crudRemoveManyById([ 1298 | opt 1299 | ])[0]; 1300 | // validate dbRowCount 1301 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 1); 1302 | // validate data 1303 | local.assertJsonEqual(opt.data._id, opt._id); 1304 | local.assertJsonEqual(opt.data.id2, opt.id2); 1305 | local.assertJsonEqual(opt.data.field1, undefined); 1306 | local.assertJsonEqual(opt.data.field2, undefined); 1307 | local.assertJsonEqual(opt.data.field3, undefined); 1308 | onError(); 1309 | }; 1310 | 1311 | local.testCase_dbTable_crudXxxByQuery = function (opt, onError) { 1312 | /* 1313 | * this function will test dbTable's crudXxxByQuery handling-behavior 1314 | */ 1315 | opt = {}; 1316 | // test dbTableCreateMany's create handling-behavior 1317 | opt.dbTable = local.dbTableCreateMany([ 1318 | { 1319 | idIndexCreateList: [ 1320 | null 1321 | ], 1322 | idIndexRemoveList: [ 1323 | null 1324 | ], 1325 | name: "testCase_dbTable_crudXxxByQuery" 1326 | } 1327 | ])[0]; 1328 | // drop dbTable 1329 | opt.dbTable.drop(); 1330 | // validate dbRowCount 1331 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 0); 1332 | // test crudSetManyById's insert handling-behavior 1333 | opt.data = opt.dbTable.crudSetManyById([ 1334 | null, null 1335 | ]); 1336 | // validate dbRowCount 1337 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1338 | // validate data 1339 | local.assertJsonEqual(opt.data.length, 2); 1340 | // test crudRemoveManyByQuery's soft-delete handling-behavior 1341 | opt.data = opt.dbTable.crudRemoveManyByQuery(); 1342 | // validate dbRowCount 1343 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 0); 1344 | // validate data 1345 | local.assertJsonEqual(opt.data.length, 2); 1346 | // test crudSetManyById's insert handling-behavior 1347 | opt.data = opt.dbTable.crudSetManyById([ 1348 | null, { 1349 | field1: 1, 1350 | field2: 2, 1351 | field3: 3 1352 | } 1353 | ])[1]; 1354 | // validate dbRowCount 1355 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1356 | // validate timestamp 1357 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1358 | // validate data 1359 | local.assertJsonNotEqual(opt.data._id, opt._id); 1360 | local.assertJsonEqual(opt.data.id2, undefined); 1361 | local.assertJsonEqual(opt.data.field1, 1); 1362 | local.assertJsonEqual(opt.data.field2, 2); 1363 | local.assertJsonEqual(opt.data.field3, 3); 1364 | // test idIndexCreate's create handling-behavior 1365 | opt._id = opt.data._id; 1366 | opt.dbTable.idIndexCreate({ 1367 | isInteger: true, 1368 | name: "id2" 1369 | }); 1370 | // test crudGetManyByQuery's get handling-behavior 1371 | opt.data = opt.dbTable.crudGetManyByQuery({ 1372 | query: { 1373 | _id: opt._id 1374 | } 1375 | })[0]; 1376 | // validate dbRowCount 1377 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1378 | // validate data 1379 | local.assertJsonEqual(opt.data._id, opt._id); 1380 | local.assertJsonNotEqual(opt.data.id2, opt.id2); 1381 | local.assertJsonEqual(opt.data.field1, 1); 1382 | local.assertJsonEqual(opt.data.field2, 2); 1383 | // test crudGetOneByQuery's get handling-behavior 1384 | opt.data = opt.dbTable.crudGetOneByQuery({ 1385 | _id: opt._id 1386 | }); 1387 | // validate dbRowCount 1388 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1389 | // validate data 1390 | local.assertJsonEqual(opt.data._id, opt._id); 1391 | local.assertJsonNotEqual(opt.data.id2, opt.id2); 1392 | local.assertJsonEqual(opt.data.field1, 1); 1393 | local.assertJsonEqual(opt.data.field2, 2); 1394 | // test crudUpdateManyByQuery's update handling-behavior 1395 | opt.id2 = opt.data.id2; 1396 | opt.data = opt.dbTable.crudUpdateManyByQuery({ 1397 | id2: opt.id2 1398 | }, { 1399 | id2: opt.id2, 1400 | field2: NaN, 1401 | field3: [ 1402 | new Date(0) 1403 | ] 1404 | })[0]; 1405 | // validate dbRowCount 1406 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1407 | // validate timestamp 1408 | local.assertThrow(opt.data._timeCreated <= opt.data._timeUpdated, opt.data); 1409 | // validate data 1410 | local.assertJsonEqual(opt.data._id, opt._id); 1411 | local.assertJsonEqual(opt.data.id2, opt.id2); 1412 | local.assertJsonEqual(opt.data.field1, 1); 1413 | local.assertJsonEqual(opt.data.field2, undefined); 1414 | local.assertJsonEqual(opt.data.field3, [ 1415 | "1970-01-01T00:00:00.000Z" 1416 | ]); 1417 | // test crudSetManyById's replace handling-behavior 1418 | opt.data = opt.dbTable.crudSetManyById([ 1419 | { 1420 | id2: opt.id2 1421 | } 1422 | ])[0]; 1423 | // validate dbRowCount 1424 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1425 | // validate timestamp 1426 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1427 | // validate data 1428 | local.assertJsonEqual(opt.data._id, opt._id); 1429 | local.assertJsonEqual(opt.data.id2, opt.id2); 1430 | local.assertJsonEqual(opt.data.field1, undefined); 1431 | local.assertJsonEqual(opt.data.field2, undefined); 1432 | local.assertJsonEqual(opt.data.field3, undefined); 1433 | // test crudUpdateManyByQuery's update handling-behavior 1434 | opt.data = opt.dbTable.crudUpdateManyByQuery({ 1435 | id2: opt.id2 1436 | }, { 1437 | id2: opt.id2, 1438 | field1: 1 1439 | })[0]; 1440 | // validate dbRowCount 1441 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1442 | // validate timestamp 1443 | local.assertThrow(opt.data._timeCreated <= opt.data._timeUpdated, opt.data); 1444 | // validate data 1445 | local.assertJsonEqual(opt.data._id, opt._id); 1446 | local.assertJsonEqual(opt.data.id2, opt.id2); 1447 | local.assertJsonEqual(opt.data.field1, 1); 1448 | local.assertJsonEqual(opt.data.field2, undefined); 1449 | local.assertJsonEqual(opt.data.field3, undefined); 1450 | // test crudRemoveManyByQuery's soft-delete handling-behavior 1451 | opt.data = opt.dbTable.crudRemoveManyByQuery({ 1452 | _id: opt._id 1453 | })[0]; 1454 | // validate dbRowCount 1455 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 1); 1456 | // validate data 1457 | local.assertJsonEqual(opt.data._id, opt._id); 1458 | local.assertJsonEqual(opt.data.id2, opt.id2); 1459 | local.assertJsonEqual(opt.data.field1, 1); 1460 | local.assertJsonEqual(opt.data.field2, undefined); 1461 | local.assertJsonEqual(opt.data.field3, undefined); 1462 | // test crudSetManyById's re-insert handling-behavior 1463 | opt.data = opt.dbTable.crudSetManyById([ 1464 | { 1465 | id2: opt.id2 1466 | } 1467 | ])[0]; 1468 | // validate dbRowCount 1469 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 2); 1470 | // validate timestamp 1471 | local.assertJsonEqual(opt.data._timeCreated, opt.data._timeUpdated); 1472 | // validate data 1473 | local.assertJsonNotEqual(opt.data._id, opt._id); 1474 | local.assertJsonEqual(opt.data.id2, opt.id2); 1475 | local.assertJsonEqual(opt.data.field1, undefined); 1476 | local.assertJsonEqual(opt.data.field2, undefined); 1477 | local.assertJsonEqual(opt.data.field3, undefined); 1478 | // test crudRemoveManyByQuery's soft-delete handling-behavior 1479 | opt._id = opt.data._id; 1480 | opt.data = opt.dbTable.crudRemoveManyByQuery({ 1481 | _id: opt._id 1482 | })[0]; 1483 | // validate dbRowCount 1484 | local.assertJsonEqual(opt.dbTable.crudCountManyByQuery(), 1); 1485 | // validate data 1486 | local.assertJsonEqual(opt.data._id, opt._id); 1487 | local.assertJsonEqual(opt.data.id2, opt.id2); 1488 | local.assertJsonEqual(opt.data.field1, undefined); 1489 | local.assertJsonEqual(opt.data.field2, undefined); 1490 | local.assertJsonEqual(opt.data.field3, undefined); 1491 | onError(); 1492 | }; 1493 | 1494 | local.testCase_dbTable_persistence = function (opt, onError) { 1495 | /* 1496 | * this function will test dbTable's persistence handling-behavior 1497 | */ 1498 | opt = {}; 1499 | // remove all dbRow's from db 1500 | local.dbCrudRemoveAll(); 1501 | // drop db 1502 | local.dbDrop(); 1503 | // save db 1504 | local.dbSave(); 1505 | // load db 1506 | local.dbLoad(); 1507 | // import db 1508 | local.dbImport( 1509 | "testCase_dbTable_persistence idIndexCreate {\"name\":\"_id\"}\n" 1510 | + "testCase_dbTable_persistence idIndexCreate {\"name\":\"id2\"}\n" 1511 | + "testCase_dbTable_persistence sizeLimit 0\n" 1512 | + "testCase_dbTable_persistence sortDefault []\n" 1513 | + "testCase_dbTable_persistence dbRowSet {\"_id\":\"id1\"}\n" 1514 | + "undefined undefined undefined" 1515 | ); 1516 | opt.dbTable = local.dbTableCreateOne({ 1517 | name: "testCase_dbTable_persistence" 1518 | }); 1519 | opt.data = local.dbExport(); 1520 | // validate dbTable has idIndex._id 1521 | local.assertThrow(opt.data.indexOf( 1522 | "testCase_dbTable_persistence idIndexCreate" 1523 | + " {\"isInteger\":false,\"name\":\"_id\"}" 1524 | ) >= 0, opt.data); 1525 | // validate dbTable has idIndex.id2 1526 | local.assertThrow(opt.data.indexOf( 1527 | "testCase_dbTable_persistence idIndexCreate" 1528 | + " {\"isInteger\":false,\"name\":\"id2\"}" 1529 | ) >= 0, opt.data); 1530 | // validate dbTable has dbRow1 1531 | local.assertThrow(opt.data.indexOf( 1532 | "testCase_dbTable_persistence dbRowSet {\"_id\":\"id1\"," 1533 | ) >= 0, opt.data); 1534 | // remove all dbRow's from dbTable 1535 | opt.dbTable.crudRemoveAll(); 1536 | opt.data = opt.dbTable.export(); 1537 | // validate dbTable has idIndex._id 1538 | local.assertThrow(opt.data.indexOf( 1539 | "testCase_dbTable_persistence idIndexCreate" 1540 | + " {\"isInteger\":false,\"name\":\"_id\"}" 1541 | ) >= 0, opt.data); 1542 | // validate dbTable has idIndex.id2 1543 | local.assertThrow(opt.data.indexOf( 1544 | "testCase_dbTable_persistence idIndexCreate" 1545 | + " {\"isInteger\":false,\"name\":\"id2\"}" 1546 | ) >= 0, opt.data); 1547 | // validate dbTable has no dbRow1 1548 | local.assertThrow(opt.data.indexOf( 1549 | "testCase_dbTable_persistence dbRowSet {\"_id\":\"id1\"," 1550 | ) < 0, opt.data); 1551 | // drop dbTable 1552 | opt.dbTable.drop(); 1553 | opt.data = opt.dbTable.export(); 1554 | // validate dbTable has idIndex._id 1555 | local.assertThrow(opt.data.indexOf( 1556 | "testCase_dbTable_persistence idIndexCreate" 1557 | + " {\"isInteger\":false,\"name\":\"_id\"}" 1558 | ) >= 0, opt.data); 1559 | // validate dbTable has no idIndex.id2 1560 | local.assertThrow(opt.data.indexOf( 1561 | "testCase_dbTable_persistence idIndexCreate" 1562 | + " {\"isInteger\":false,\"name\":\"id2\"}" 1563 | ) < 0, opt.data); 1564 | // validate dbTable has no dbRow1 1565 | local.assertThrow(opt.data.indexOf( 1566 | "testCase_dbTable_persistence dbRowSet {\"_id\":\"id1\"," 1567 | ) < 0, opt.data); 1568 | // save db 1569 | opt.dbTable.save(function (err) { 1570 | // validate no err occurred 1571 | local.assertThrow(!err, err); 1572 | // load db 1573 | local.dbLoad(onError); 1574 | }); 1575 | }; 1576 | 1577 | local.testCase_dbTable_sizeLimit = function (opt, onError) { 1578 | /* 1579 | * this function will test dbTable's sizeLimit handling-behavior 1580 | */ 1581 | opt = {}; 1582 | opt.dbTable = local.dbTableCreateOne({ 1583 | name: "testCase_dbTable_sizeLimit", 1584 | sizeLimit: 2 1585 | }); 1586 | opt.dbTable.crudSetOneById({}); 1587 | // validate dbRowCount 1588 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 1); 1589 | opt.dbTable.crudSetOneById({}); 1590 | // validate dbRowCount 1591 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1592 | opt.dbTable.crudSetOneById({}); 1593 | // validate dbRowCount 1594 | local.assertJsonEqual(opt.dbTable.crudCountAll(), 2); 1595 | onError(); 1596 | }; 1597 | 1598 | local.testCase_onEventDomDb_default = function (opt, onError) { 1599 | /* 1600 | * this function will test onEventDomDb's default handling-behavior 1601 | */ 1602 | if (!local.isBrowser) { 1603 | onError(null, opt); 1604 | return; 1605 | } 1606 | opt = {}; 1607 | opt.addEventListener = local.nop; 1608 | opt.click = local.nop; 1609 | opt.files = []; 1610 | local.testMock([ 1611 | [ 1612 | document, { 1613 | querySelector: function () { 1614 | return opt; 1615 | } 1616 | } 1617 | ], [ 1618 | local, { 1619 | dbDrop: function (onError) { 1620 | onError(); 1621 | }, 1622 | dbExport: local.nop, 1623 | dbImport: local.nop 1624 | } 1625 | ], [ 1626 | globalThis, { 1627 | FileReader: function () { 1628 | this.addEventListener = function (_, fnc) { 1629 | fnc(_); 1630 | }; 1631 | this.readAsText = local.nop; 1632 | }, 1633 | setTimeout: function (fnc) { 1634 | fnc(); 1635 | }, 1636 | utility2: null, 1637 | utility2_dbSeedList: null, 1638 | utility2_onReadyAfter: null, 1639 | utility2_onReadyBefore: null 1640 | } 1641 | ] 1642 | ], function (onError) { 1643 | [ 1644 | "dbExportButton1", 1645 | "dbImportButton1", 1646 | "dbImportInput1", 1647 | "dbResetButton1" 1648 | ].forEach(function (id) { 1649 | [ 1650 | "change", "click" 1651 | ].forEach(function (type) { 1652 | [ 1653 | 0, 1 1654 | ].forEach(function (ii) { 1655 | opt.files[0] = ii; 1656 | local.onEventDomDb({ 1657 | target: { 1658 | dataset: {}, 1659 | id 1660 | }, 1661 | type 1662 | }); 1663 | globalThis.utility2_dbSeedList = ii && [ 1664 | { 1665 | name: "dbTable1" 1666 | } 1667 | ]; 1668 | }); 1669 | }); 1670 | }); 1671 | onError(); 1672 | }, onError); 1673 | }; 1674 | 1675 | local.testCase_sortCompare_default = function (opt, onError) { 1676 | /* 1677 | * this function will test sortCompare's default handling-behavior 1678 | */ 1679 | opt = {}; 1680 | opt.data = ([ 1681 | undefined, 1682 | [], 1683 | "", 1684 | -1, -Infinity, 0, 0, 1, Infinity, 1685 | {}, 1686 | "a", "aa", 1687 | false, false, null, null, true, true 1688 | ]).sort(); 1689 | opt.data = opt.data.sort(local.sortCompare); 1690 | local.assertJsonEqual(opt.data.slice(0, -3), [ 1691 | null, null, 1692 | false, false, true, true, 1693 | -Infinity, -1, 0, 0, 1, Infinity, 1694 | "", "a", "aa" 1695 | ]); 1696 | opt.data = opt.data.reverse().sort(local.sortCompare); 1697 | local.assertJsonEqual(opt.data.slice(0, -3), [ 1698 | null, null, 1699 | false, false, true, true, 1700 | -Infinity, -1, 0, 0, 1, Infinity, 1701 | "", "a", "aa" 1702 | ]); 1703 | // hack-istanbul 1704 | opt.data.forEach(function (aa) { 1705 | [{}, null, Symbol()].forEach(function (bb) { 1706 | local.sortCompare(aa, bb); 1707 | local.sortCompare(bb, aa); 1708 | }); 1709 | }); 1710 | onError(); 1711 | }; 1712 | 1713 | local.testCase_storageXxx_misc = function (opt, onError) { 1714 | /* 1715 | * this function will test storageXxx's misc handling-behavior 1716 | */ 1717 | var onParallel; 1718 | // hack-jslint 1719 | local.nop(opt); 1720 | onParallel = local.onParallel(onError); 1721 | onParallel.counter += 1; 1722 | // test storageInit's init handling-behavior 1723 | local.storageInit(); 1724 | // test storageInit's re-init handling-behavior 1725 | local.storageInit(); 1726 | // test crud handling-behavior 1727 | onParallel.counter += 1; 1728 | local.storageClear(onParallel); 1729 | onParallel.counter += 1; 1730 | local.storageGetItem("undefined", onParallel); 1731 | onParallel.counter += 1; 1732 | local.storageKeys(onParallel); 1733 | onParallel.counter += 1; 1734 | local.storageLength(onParallel); 1735 | onParallel.counter += 1; 1736 | local.storageRemoveItem("undefined", onParallel); 1737 | onParallel.counter += 1; 1738 | local.storageSetItem("undefined", "undefined", onParallel); 1739 | //!! onParallel.counter += 1; 1740 | //!! local.storageKeys(function () { 1741 | //!! if (local.isBrowser) { 1742 | //!! // test indexedDB's onupgradeneeded handling-behavior 1743 | //!! local._debugStorageRequestIndexedDB.onupgradeneeded(); 1744 | //!! } 1745 | //!! onParallel(); 1746 | //!! }); 1747 | onParallel(); 1748 | }; 1749 | }()); 1750 | }()); 1751 | --------------------------------------------------------------------------------