├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── browser.js ├── docs ├── data │ └── index.json ├── example.html ├── example.js └── static-json-db.min.js ├── index.js ├── package-lock.json ├── package.json └── test └── test.js /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Test 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [10.x, 12.x, 14.x] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - run: npm ci 26 | - run: npm run build --if-present 27 | - run: npm test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | test/tmp 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # Snowpack dependency directory (https://snowpack.dev/) 48 | web_modules/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Microbundle cache 60 | .rpt2_cache/ 61 | .rts2_cache_cjs/ 62 | .rts2_cache_es/ 63 | .rts2_cache_umd/ 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # Yarn Integrity file 72 | .yarn-integrity 73 | 74 | # dotenv environment variables file 75 | .env 76 | .env.test 77 | 78 | # parcel-bundler cache (https://parceljs.org/) 79 | .cache 80 | .parcel-cache 81 | 82 | # Next.js build output 83 | .next 84 | out 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and not Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | 111 | # Stores VSCode versions used for testing VSCode extensions 112 | .vscode-test 113 | 114 | # yarn v2 115 | .yarn/cache 116 | .yarn/unplugged 117 | .yarn/build-state.yml 118 | .yarn/install-state.gz 119 | .pnp.* 120 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Jason Ling Xiaowei 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # static-json-db 4 | ## The Native Jamstack Database. 5 | [![Build Status](https://github.com/jlxw/static-json-db/workflows/Test/badge.svg)](https://github.com/jlxw/static-json-db/actions?query=workflow%3ATest) 6 | 7 | A NoSQL key-value database stored as a directory tree of small JSON files which can be deployed as part of a static website and queried from client browsers in an efficient manner. Data is stored in JSON files which are branched into smaller JSON files as size tresholds are met. Minified JS for browser ~1.5KB. 8 | 9 | ### Demo / Proof of Concept 10 | - [www.playliststar.com](https://www.playliststar.com/) 11 | 12 | ### Example Usage 13 | 14 | #### Node.js 15 | ```sh 16 | $ npm install static-json-db 17 | ``` 18 | 19 | ```js 20 | const StaticJsonDb = require('static-json-db') 21 | const db = new StaticJsonDb('data'); // using directory 'data' 22 | 23 | (async () => { 24 | await db.set('key1','Hello World1') 25 | await db.set('key2','Hello World2') 26 | await db.set('abc','def') 27 | 28 | console.log(await db.get('key1')) // Hello World1 29 | 30 | // get all keys starting with 'key' 31 | console.log(await db.getAll('key')) // { key1: 'Hello World1', key2: 'Hello World2' } 32 | 33 | await db.delete('key2') 34 | 35 | // get all keys 36 | console.log(await db.getAll()) // { key1: 'Hello World1', abc: 'def' } 37 | 38 | //write to disk 39 | await db.write() 40 | })(); 41 | ``` 42 | 43 | #### HTML 44 | https://jlxw.github.io/static-json-db/example.html 45 | ```html 46 | 47 | 48 | 49 | 56 | 57 | 58 | ``` 59 | ### Notes 60 | - If you need to modify in place objects returned by .get() or sent to .set(), use .getSafe() or .setSafe() instead 61 | 62 | ### Why? 63 | Hosting for JAMStack / static websites are almost free now, and can be very easily and cheaply scaled. static-json-db can be a lower-cost alternative to traditional databases that do not need to be updated frequently. The proof of concept [www.playliststar.com](https://www.playliststar.com/) is updated on an hourly basis with ~200K tracks across ~7K playlists. 64 | 65 | In the future I imagine adding a real-time component to complement static-json-db. Probably a very simple server that appends to a flat JSON file, which is served as a static file, and periodically "merged" with the "main" db. 66 | 67 | ### License 68 | 69 | MIT 70 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | class StaticJsonDb { 2 | constructor(dir,options={}) { 3 | this.options = {...{ 4 | case_insensitive_fs: false 5 | }, ...options} 6 | this.dir = dir 7 | this.resetCache() 8 | this.getShardPromises = {} 9 | } 10 | 11 | resetCache() { 12 | this.Cache = { 13 | d: {}, 14 | shards: {}, 15 | shards_to_write: {}, 16 | } 17 | } 18 | 19 | async getAll(prefix="",o={},f=false,abortCallback) { 20 | if(!f && this.options.case_insensitive_fs) { prefix = prefix.replace(/[A-Z]/g,'($&') } 21 | let i = await this.findShard(prefix,undefined,abortCallback) 22 | for(let [k,v] of Object.entries(i.d)) { 23 | if(f || k.startsWith(prefix)) { 24 | if(this.options.case_insensitive_fs) { k = k.replace(/\(/g,'') } 25 | o[k]=v 26 | } 27 | } 28 | if(abortCallback && abortCallback()===false){return o} 29 | let p = [] 30 | for(let k of i.s) { 31 | if(f || k.startsWith(prefix)) { 32 | p.push(this.getAll(k,o,true,abortCallback)) 33 | } 34 | } 35 | await Promise.all(p) 36 | return o 37 | } 38 | 39 | async get(k,abortCallback) { 40 | if(this.options.case_insensitive_fs) { k = k.replace(/[A-Z]/g,'($&') } 41 | return (await this.findShard(k,undefined,abortCallback)).d[k] 42 | } 43 | 44 | shardPath(key) { 45 | if(key.length>0) { 46 | let o=this.dir 47 | for (let i = 0; i < key.length; i += 2) { 48 | o=new URL(key.substring(i, i + 2),o+'/') 49 | } 50 | return o+'.json' 51 | } else { 52 | return new URL('index.json',this.dir+'/') 53 | } 54 | } 55 | 56 | async findShard(k,from_shard_key="",abortCallback) { 57 | let i=await this.getShard(from_shard_key) 58 | if(i.d[k]) { 59 | return i 60 | } else { 61 | if(abortCallback && abortCallback()===false){return i} 62 | let shard_key = i.s.find(i=>k.startsWith(i)) 63 | if(shard_key) { 64 | // console.log(k,shard_key) 65 | return this.findShard(k,shard_key) 66 | } else { 67 | return i 68 | } 69 | } 70 | } 71 | 72 | async getShard(shard_key) { 73 | if(this.Cache.shards[shard_key]) { 74 | return this.Cache.shards[shard_key] 75 | } else { 76 | this.getShardPromises[shard_key] = this.getShardPromises[shard_key] || this.getShard2(shard_key) 77 | let o = await this.getShardPromises[shard_key] 78 | delete this.getShardPromises[shard_key] 79 | return o 80 | } 81 | } 82 | async getShard2(shard_key) { 83 | let i=await this.getShard3(shard_key) 84 | let o={d:{},s:[],key:shard_key} 85 | 86 | for(let [k,v] of Object.entries(i.d)) { 87 | o.d[shard_key+k]=v 88 | } 89 | for(let k of i.s) { o.s.push(shard_key+k) } 90 | 91 | this.Cache.shards[shard_key]=o 92 | return o 93 | } 94 | getShard3(shard_key) { 95 | return(fetch(this.shardPath(shard_key)).then(i=>i.json())) 96 | } 97 | } 98 | 99 | if (typeof module === 'object' && module.exports) { module.exports = StaticJsonDb } -------------------------------------------------------------------------------- /docs/data/index.json: -------------------------------------------------------------------------------- 1 | {"d":{"key1":"Hello World1","abc":"def"},"s":[]} -------------------------------------------------------------------------------- /docs/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /docs/example.js: -------------------------------------------------------------------------------- 1 | const StaticJsonDb = require('../index.js') 2 | const db = new StaticJsonDb('data'); // using directory 'data' 3 | 4 | (async () => { 5 | await db.set('key1','Hello World1') 6 | await db.set('key2','Hello World2') 7 | await db.set('abc','def') 8 | 9 | console.log(await db.get('key1')) // Hello World1 10 | 11 | // get all keys starting with 'key' 12 | console.log(await db.getAll('key')) // { key1: 'Hello World1', key2: 'Hello World2' } 13 | 14 | await db.delete('key2') 15 | 16 | // get all keys 17 | console.log(await db.getAll()) // { key1: 'Hello World1', abc: 'def' } 18 | 19 | //write to disk 20 | await db.write() 21 | })(); -------------------------------------------------------------------------------- /docs/static-json-db.min.js: -------------------------------------------------------------------------------- 1 | class StaticJsonDb{constructor(t,s={}){this.options={case_insensitive_fs:!1,...s},this.dir=t,this.resetCache(),this.getShardPromises={}}resetCache(){this.Cache={d:{},shards:{},shards_to_write:{}}}async getAll(t="",s={},e=!1,i){!e&&this.options.case_insensitive_fs&&(t=t.replace(/[A-Z]/g,"($&"));let r=await this.findShard(t,void 0,i);for(let[i,h]of Object.entries(r.d))(e||i.startsWith(t))&&(this.options.case_insensitive_fs&&(i=i.replace(/\(/g,"")),s[i]=h);if(i&&!1===i())return s;let h=[];for(let a of r.s)(e||a.startsWith(t))&&h.push(this.getAll(a,s,!0,i));return await Promise.all(h),s}async get(t,s){return this.options.case_insensitive_fs&&(t=t.replace(/[A-Z]/g,"($&")),(await this.findShard(t,void 0,s)).d[t]}shardPath(t){if(t.length>0){let s=this.dir;for(let e=0;et.startsWith(s));return s?this.findShard(t,s):i}}async getShard(t){if(this.Cache.shards[t])return this.Cache.shards[t];{this.getShardPromises[t]=this.getShardPromises[t]||this.getShard2(t);let s=await this.getShardPromises[t];return delete this.getShardPromises[t],s}}async getShard2(t){let s=await this.getShard3(t),e={d:{},s:[],key:t};for(let[i,r]of Object.entries(s.d))e.d[t+i]=r;for(let i of s.s)e.s.push(t+i);return this.Cache.shards[t]=e,e}getShard3(t){return fetch(this.shardPath(t)).then(t=>t.json())}} -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | 6 | const BrowserStaticJsonDb = require('./browser.js') 7 | 8 | module.exports = class StaticJsonDb extends BrowserStaticJsonDb { 9 | constructor(dir,options={}) { 10 | super(dir,options) 11 | this.options = {...{ 12 | shard_threshold: 16384, 13 | }, ...options} 14 | if(!fs.existsSync(this.shardPath(""))) { 15 | this.setShard({ 16 | key:"", 17 | d:{}, 18 | s:[] 19 | }) 20 | } 21 | } 22 | 23 | shardPath(key) { 24 | if(key.length>0) { 25 | let paths=[] 26 | for (let i = 0; i < key.length; i += 2) { 27 | paths.push(key.substring(i, i + 2)); 28 | } 29 | return path.join(this.dir,...paths)+'.json' 30 | } else { 31 | return path.join(this.dir,'index.json') 32 | } 33 | } 34 | 35 | async getShard3(shard_key) { 36 | return( 37 | JSON.parse( 38 | await fs.promises.readFile(this.shardPath(shard_key)) 39 | ) 40 | ) 41 | } 42 | 43 | async delete(k) { 44 | let shard = await this.findShard(k) 45 | delete shard.d[k] 46 | this.setShard(shard) 47 | } 48 | 49 | async set(k,v) { 50 | if(!k.match(/^[A-Za-z0-9_-]*$/)) {throw('key must be filename/url safe: '+k)} 51 | if(this.options.case_insensitive_fs) { k = k.replace(/[A-Z]/g,'($&') } 52 | let shard = await this.findShard(k) 53 | // if(JSON.stringify(shard.d[k])!=JSON.stringify(v)) { //can't do this, does not work if object is changed in place 54 | shard.d_keys = shard.d_keys || Object.keys(shard.d) 55 | if(!shard.d[k]) { shard.d_keys.push(k) } 56 | shard.d[k]=v 57 | let make_shard = this.checkMakeShard(shard,k) 58 | if(make_shard) { 59 | this.setShard(make_shard) 60 | Object.keys(make_shard.d).forEach(i=>delete shard.d[i]) 61 | delete shard.d_keys 62 | shard.s.push(make_shard.key) 63 | shard.s = shard.s.filter(i=>!make_shard.s.includes(i)) 64 | } 65 | this.setShard(shard) 66 | // } 67 | } 68 | 69 | checkMakeShard(i_shard,k) { 70 | let shard={d:{},s:[]} 71 | //1 - get potential shard key, which does not encompass all keys in i_shard 72 | let shard_key_length = i_shard.key.length 73 | let i_shard_d_keys = i_shard.d_keys 74 | 75 | if(i_shard_d_keys.length <= 1){return} 76 | let shard_d_keys 77 | do { 78 | shard_key_length += 1 79 | shard.key = k.substring(0,shard_key_length) 80 | shard_d_keys = i_shard_d_keys.filter(i=>i.startsWith(shard.key)) 81 | } while( 82 | shard_key_length < k.length && 83 | shard_d_keys.length == i_shard_d_keys.length 84 | ) 85 | if(shard_d_keys.length == i_shard_d_keys.length){return} //TODO: this is too arbitrary? 86 | 87 | //2 - create potential shard and check size 88 | for(let k of shard_d_keys) { 89 | shard.d[k]=i_shard.d[k] 90 | } 91 | if(JSON.stringify(shard.d).length > this.options.shard_threshold) { 92 | for(let v of i_shard.s) { 93 | if(v.startsWith(shard.key)) {shard.s.push(v)} 94 | } 95 | return shard 96 | } 97 | } 98 | 99 | setShard(shard) { 100 | this.Cache.shards[shard.key]=shard 101 | this.Cache.shards_to_write[shard.key]=true 102 | } 103 | 104 | async write() { 105 | for(let k of Object.keys(this.Cache.shards_to_write)) { 106 | await this.writeShard(this.Cache.shards[k]) 107 | delete this.Cache.shards_to_write[k] 108 | } 109 | } 110 | 111 | async writeShard(shard) { 112 | let fn = this.shardPath(shard.key) 113 | let o = {d:{},s:[]} 114 | if(shard.key.length>0) { 115 | for(let [k,v] of Object.entries(shard.d)) { o.d[k.substring(shard.key.length)]=v } 116 | for(let i of shard.s) { o.s.push(i.substring(shard.key.length)) } 117 | } else { 118 | o.d = shard.d 119 | o.s = shard.s 120 | } 121 | fs.mkdirSync(path.dirname(fn),{recursive:true}) 122 | fs.writeFileSync(fn+'.tmp',JSON.stringify(o)); 123 | fs.renameSync(fn+'.tmp',fn); 124 | } 125 | 126 | async getSafe(k) { 127 | return JSON.parse(JSON.stringify(await this.get(k))) 128 | } 129 | 130 | async setSafe(k,v) { 131 | return await this.set(k,JSON.parse(JSON.stringify(v))) 132 | } 133 | 134 | async optimize() { 135 | let o = new StaticJsonDb(this.dir+'.tmp',this.options) 136 | for(let [k,v] of Object.entries(await this.getAll())) { 137 | await o.set(k,v) 138 | } 139 | await o.write() 140 | fs.renameSync(this.dir,this.dir+'.old'); 141 | fs.renameSync(this.dir+'.tmp',this.dir); 142 | this.resetCache() 143 | } 144 | } 145 | 146 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "static-json-db", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-colors": { 8 | "version": "4.1.1", 9 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 10 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 11 | "dev": true 12 | }, 13 | "ansi-regex": { 14 | "version": "3.0.0", 15 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 16 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 17 | "dev": true 18 | }, 19 | "ansi-styles": { 20 | "version": "3.2.1", 21 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 22 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 23 | "dev": true, 24 | "requires": { 25 | "color-convert": "^1.9.0" 26 | } 27 | }, 28 | "anymatch": { 29 | "version": "3.1.1", 30 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 31 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 32 | "dev": true, 33 | "requires": { 34 | "normalize-path": "^3.0.0", 35 | "picomatch": "^2.0.4" 36 | } 37 | }, 38 | "argparse": { 39 | "version": "1.0.10", 40 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 41 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 42 | "dev": true, 43 | "requires": { 44 | "sprintf-js": "~1.0.2" 45 | } 46 | }, 47 | "array.prototype.map": { 48 | "version": "1.0.2", 49 | "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", 50 | "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", 51 | "dev": true, 52 | "requires": { 53 | "define-properties": "^1.1.3", 54 | "es-abstract": "^1.17.0-next.1", 55 | "es-array-method-boxes-properly": "^1.0.0", 56 | "is-string": "^1.0.4" 57 | } 58 | }, 59 | "balanced-match": { 60 | "version": "1.0.0", 61 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 62 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 63 | "dev": true 64 | }, 65 | "binary-extensions": { 66 | "version": "2.1.0", 67 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", 68 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", 69 | "dev": true 70 | }, 71 | "brace-expansion": { 72 | "version": "1.1.11", 73 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 74 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 75 | "dev": true, 76 | "requires": { 77 | "balanced-match": "^1.0.0", 78 | "concat-map": "0.0.1" 79 | } 80 | }, 81 | "braces": { 82 | "version": "3.0.2", 83 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 84 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 85 | "dev": true, 86 | "requires": { 87 | "fill-range": "^7.0.1" 88 | } 89 | }, 90 | "browser-stdout": { 91 | "version": "1.3.1", 92 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 93 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 94 | "dev": true 95 | }, 96 | "camelcase": { 97 | "version": "5.3.1", 98 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 99 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 100 | "dev": true 101 | }, 102 | "chalk": { 103 | "version": "2.4.2", 104 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 105 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 106 | "dev": true, 107 | "requires": { 108 | "ansi-styles": "^3.2.1", 109 | "escape-string-regexp": "^1.0.5", 110 | "supports-color": "^5.3.0" 111 | }, 112 | "dependencies": { 113 | "supports-color": { 114 | "version": "5.5.0", 115 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 116 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 117 | "dev": true, 118 | "requires": { 119 | "has-flag": "^3.0.0" 120 | } 121 | } 122 | } 123 | }, 124 | "chokidar": { 125 | "version": "3.3.1", 126 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", 127 | "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", 128 | "dev": true, 129 | "requires": { 130 | "anymatch": "~3.1.1", 131 | "braces": "~3.0.2", 132 | "fsevents": "~2.1.2", 133 | "glob-parent": "~5.1.0", 134 | "is-binary-path": "~2.1.0", 135 | "is-glob": "~4.0.1", 136 | "normalize-path": "~3.0.0", 137 | "readdirp": "~3.3.0" 138 | } 139 | }, 140 | "cliui": { 141 | "version": "5.0.0", 142 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 143 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 144 | "dev": true, 145 | "requires": { 146 | "string-width": "^3.1.0", 147 | "strip-ansi": "^5.2.0", 148 | "wrap-ansi": "^5.1.0" 149 | }, 150 | "dependencies": { 151 | "ansi-regex": { 152 | "version": "4.1.0", 153 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 154 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 155 | "dev": true 156 | }, 157 | "string-width": { 158 | "version": "3.1.0", 159 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 160 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 161 | "dev": true, 162 | "requires": { 163 | "emoji-regex": "^7.0.1", 164 | "is-fullwidth-code-point": "^2.0.0", 165 | "strip-ansi": "^5.1.0" 166 | } 167 | }, 168 | "strip-ansi": { 169 | "version": "5.2.0", 170 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 171 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 172 | "dev": true, 173 | "requires": { 174 | "ansi-regex": "^4.1.0" 175 | } 176 | } 177 | } 178 | }, 179 | "color-convert": { 180 | "version": "1.9.3", 181 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 182 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 183 | "dev": true, 184 | "requires": { 185 | "color-name": "1.1.3" 186 | } 187 | }, 188 | "color-name": { 189 | "version": "1.1.3", 190 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 191 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 192 | "dev": true 193 | }, 194 | "concat-map": { 195 | "version": "0.0.1", 196 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 197 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 198 | "dev": true 199 | }, 200 | "debug": { 201 | "version": "3.2.6", 202 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 203 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 204 | "dev": true, 205 | "requires": { 206 | "ms": "^2.1.1" 207 | } 208 | }, 209 | "decamelize": { 210 | "version": "1.2.0", 211 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 212 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 213 | "dev": true 214 | }, 215 | "define-properties": { 216 | "version": "1.1.3", 217 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 218 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 219 | "dev": true, 220 | "requires": { 221 | "object-keys": "^1.0.12" 222 | } 223 | }, 224 | "diff": { 225 | "version": "4.0.2", 226 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 227 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 228 | "dev": true 229 | }, 230 | "emoji-regex": { 231 | "version": "7.0.3", 232 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 233 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 234 | "dev": true 235 | }, 236 | "es-abstract": { 237 | "version": "1.17.6", 238 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", 239 | "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", 240 | "dev": true, 241 | "requires": { 242 | "es-to-primitive": "^1.2.1", 243 | "function-bind": "^1.1.1", 244 | "has": "^1.0.3", 245 | "has-symbols": "^1.0.1", 246 | "is-callable": "^1.2.0", 247 | "is-regex": "^1.1.0", 248 | "object-inspect": "^1.7.0", 249 | "object-keys": "^1.1.1", 250 | "object.assign": "^4.1.0", 251 | "string.prototype.trimend": "^1.0.1", 252 | "string.prototype.trimstart": "^1.0.1" 253 | } 254 | }, 255 | "es-array-method-boxes-properly": { 256 | "version": "1.0.0", 257 | "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", 258 | "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", 259 | "dev": true 260 | }, 261 | "es-get-iterator": { 262 | "version": "1.1.0", 263 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", 264 | "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", 265 | "dev": true, 266 | "requires": { 267 | "es-abstract": "^1.17.4", 268 | "has-symbols": "^1.0.1", 269 | "is-arguments": "^1.0.4", 270 | "is-map": "^2.0.1", 271 | "is-set": "^2.0.1", 272 | "is-string": "^1.0.5", 273 | "isarray": "^2.0.5" 274 | } 275 | }, 276 | "es-to-primitive": { 277 | "version": "1.2.1", 278 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 279 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 280 | "dev": true, 281 | "requires": { 282 | "is-callable": "^1.1.4", 283 | "is-date-object": "^1.0.1", 284 | "is-symbol": "^1.0.2" 285 | } 286 | }, 287 | "escape-string-regexp": { 288 | "version": "1.0.5", 289 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 290 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 291 | "dev": true 292 | }, 293 | "esprima": { 294 | "version": "4.0.1", 295 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 296 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 297 | "dev": true 298 | }, 299 | "fill-range": { 300 | "version": "7.0.1", 301 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 302 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 303 | "dev": true, 304 | "requires": { 305 | "to-regex-range": "^5.0.1" 306 | } 307 | }, 308 | "find-up": { 309 | "version": "4.1.0", 310 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 311 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 312 | "dev": true, 313 | "requires": { 314 | "locate-path": "^5.0.0", 315 | "path-exists": "^4.0.0" 316 | } 317 | }, 318 | "flat": { 319 | "version": "4.1.0", 320 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 321 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 322 | "dev": true, 323 | "requires": { 324 | "is-buffer": "~2.0.3" 325 | } 326 | }, 327 | "fs.realpath": { 328 | "version": "1.0.0", 329 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 330 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 331 | "dev": true 332 | }, 333 | "fsevents": { 334 | "version": "2.1.3", 335 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 336 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 337 | "dev": true, 338 | "optional": true 339 | }, 340 | "function-bind": { 341 | "version": "1.1.1", 342 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 343 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 344 | "dev": true 345 | }, 346 | "get-caller-file": { 347 | "version": "2.0.5", 348 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 349 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 350 | "dev": true 351 | }, 352 | "glob": { 353 | "version": "7.1.6", 354 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 355 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 356 | "dev": true, 357 | "requires": { 358 | "fs.realpath": "^1.0.0", 359 | "inflight": "^1.0.4", 360 | "inherits": "2", 361 | "minimatch": "^3.0.4", 362 | "once": "^1.3.0", 363 | "path-is-absolute": "^1.0.0" 364 | } 365 | }, 366 | "glob-parent": { 367 | "version": "5.1.1", 368 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 369 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 370 | "dev": true, 371 | "requires": { 372 | "is-glob": "^4.0.1" 373 | } 374 | }, 375 | "growl": { 376 | "version": "1.10.5", 377 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 378 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 379 | "dev": true 380 | }, 381 | "has": { 382 | "version": "1.0.3", 383 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 384 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 385 | "dev": true, 386 | "requires": { 387 | "function-bind": "^1.1.1" 388 | } 389 | }, 390 | "has-flag": { 391 | "version": "3.0.0", 392 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 393 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 394 | "dev": true 395 | }, 396 | "has-symbols": { 397 | "version": "1.0.1", 398 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 399 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 400 | "dev": true 401 | }, 402 | "he": { 403 | "version": "1.2.0", 404 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 405 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 406 | "dev": true 407 | }, 408 | "inflight": { 409 | "version": "1.0.6", 410 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 411 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 412 | "dev": true, 413 | "requires": { 414 | "once": "^1.3.0", 415 | "wrappy": "1" 416 | } 417 | }, 418 | "inherits": { 419 | "version": "2.0.4", 420 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 421 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 422 | "dev": true 423 | }, 424 | "is-arguments": { 425 | "version": "1.0.4", 426 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", 427 | "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", 428 | "dev": true 429 | }, 430 | "is-binary-path": { 431 | "version": "2.1.0", 432 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 433 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 434 | "dev": true, 435 | "requires": { 436 | "binary-extensions": "^2.0.0" 437 | } 438 | }, 439 | "is-buffer": { 440 | "version": "2.0.4", 441 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 442 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", 443 | "dev": true 444 | }, 445 | "is-callable": { 446 | "version": "1.2.0", 447 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", 448 | "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", 449 | "dev": true 450 | }, 451 | "is-date-object": { 452 | "version": "1.0.2", 453 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 454 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", 455 | "dev": true 456 | }, 457 | "is-extglob": { 458 | "version": "2.1.1", 459 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 460 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 461 | "dev": true 462 | }, 463 | "is-fullwidth-code-point": { 464 | "version": "2.0.0", 465 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 466 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 467 | "dev": true 468 | }, 469 | "is-glob": { 470 | "version": "4.0.1", 471 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 472 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 473 | "dev": true, 474 | "requires": { 475 | "is-extglob": "^2.1.1" 476 | } 477 | }, 478 | "is-map": { 479 | "version": "2.0.1", 480 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", 481 | "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", 482 | "dev": true 483 | }, 484 | "is-number": { 485 | "version": "7.0.0", 486 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 487 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 488 | "dev": true 489 | }, 490 | "is-regex": { 491 | "version": "1.1.0", 492 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", 493 | "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", 494 | "dev": true, 495 | "requires": { 496 | "has-symbols": "^1.0.1" 497 | } 498 | }, 499 | "is-set": { 500 | "version": "2.0.1", 501 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", 502 | "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", 503 | "dev": true 504 | }, 505 | "is-string": { 506 | "version": "1.0.5", 507 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", 508 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", 509 | "dev": true 510 | }, 511 | "is-symbol": { 512 | "version": "1.0.3", 513 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 514 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 515 | "dev": true, 516 | "requires": { 517 | "has-symbols": "^1.0.1" 518 | } 519 | }, 520 | "isarray": { 521 | "version": "2.0.5", 522 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 523 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 524 | "dev": true 525 | }, 526 | "isexe": { 527 | "version": "2.0.0", 528 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 529 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 530 | "dev": true 531 | }, 532 | "iterate-iterator": { 533 | "version": "1.0.1", 534 | "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", 535 | "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", 536 | "dev": true 537 | }, 538 | "iterate-value": { 539 | "version": "1.0.2", 540 | "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", 541 | "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", 542 | "dev": true, 543 | "requires": { 544 | "es-get-iterator": "^1.0.2", 545 | "iterate-iterator": "^1.0.1" 546 | } 547 | }, 548 | "js-yaml": { 549 | "version": "3.13.1", 550 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 551 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 552 | "dev": true, 553 | "requires": { 554 | "argparse": "^1.0.7", 555 | "esprima": "^4.0.0" 556 | } 557 | }, 558 | "locate-path": { 559 | "version": "5.0.0", 560 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 561 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 562 | "dev": true, 563 | "requires": { 564 | "p-locate": "^4.1.0" 565 | } 566 | }, 567 | "lodash": { 568 | "version": "4.17.19", 569 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 570 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", 571 | "dev": true 572 | }, 573 | "log-symbols": { 574 | "version": "3.0.0", 575 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", 576 | "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", 577 | "dev": true, 578 | "requires": { 579 | "chalk": "^2.4.2" 580 | } 581 | }, 582 | "minimatch": { 583 | "version": "3.0.4", 584 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 585 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 586 | "dev": true, 587 | "requires": { 588 | "brace-expansion": "^1.1.7" 589 | } 590 | }, 591 | "mocha": { 592 | "version": "8.0.1", 593 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz", 594 | "integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==", 595 | "dev": true, 596 | "requires": { 597 | "ansi-colors": "4.1.1", 598 | "browser-stdout": "1.3.1", 599 | "chokidar": "3.3.1", 600 | "debug": "3.2.6", 601 | "diff": "4.0.2", 602 | "escape-string-regexp": "1.0.5", 603 | "find-up": "4.1.0", 604 | "glob": "7.1.6", 605 | "growl": "1.10.5", 606 | "he": "1.2.0", 607 | "js-yaml": "3.13.1", 608 | "log-symbols": "3.0.0", 609 | "minimatch": "3.0.4", 610 | "ms": "2.1.2", 611 | "object.assign": "4.1.0", 612 | "promise.allsettled": "1.0.2", 613 | "serialize-javascript": "3.0.0", 614 | "strip-json-comments": "3.0.1", 615 | "supports-color": "7.1.0", 616 | "which": "2.0.2", 617 | "wide-align": "1.1.3", 618 | "workerpool": "6.0.0", 619 | "yargs": "13.3.2", 620 | "yargs-parser": "13.1.2", 621 | "yargs-unparser": "1.6.0" 622 | } 623 | }, 624 | "ms": { 625 | "version": "2.1.2", 626 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 627 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 628 | "dev": true 629 | }, 630 | "normalize-path": { 631 | "version": "3.0.0", 632 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 633 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 634 | "dev": true 635 | }, 636 | "object-inspect": { 637 | "version": "1.8.0", 638 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", 639 | "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", 640 | "dev": true 641 | }, 642 | "object-keys": { 643 | "version": "1.1.1", 644 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 645 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 646 | "dev": true 647 | }, 648 | "object.assign": { 649 | "version": "4.1.0", 650 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 651 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 652 | "dev": true, 653 | "requires": { 654 | "define-properties": "^1.1.2", 655 | "function-bind": "^1.1.1", 656 | "has-symbols": "^1.0.0", 657 | "object-keys": "^1.0.11" 658 | } 659 | }, 660 | "once": { 661 | "version": "1.4.0", 662 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 663 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 664 | "dev": true, 665 | "requires": { 666 | "wrappy": "1" 667 | } 668 | }, 669 | "p-limit": { 670 | "version": "2.3.0", 671 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 672 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 673 | "dev": true, 674 | "requires": { 675 | "p-try": "^2.0.0" 676 | } 677 | }, 678 | "p-locate": { 679 | "version": "4.1.0", 680 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 681 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 682 | "dev": true, 683 | "requires": { 684 | "p-limit": "^2.2.0" 685 | } 686 | }, 687 | "p-try": { 688 | "version": "2.2.0", 689 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 690 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 691 | "dev": true 692 | }, 693 | "path-exists": { 694 | "version": "4.0.0", 695 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 696 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 697 | "dev": true 698 | }, 699 | "path-is-absolute": { 700 | "version": "1.0.1", 701 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 702 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 703 | "dev": true 704 | }, 705 | "picomatch": { 706 | "version": "2.2.2", 707 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 708 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 709 | "dev": true 710 | }, 711 | "promise.allsettled": { 712 | "version": "1.0.2", 713 | "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", 714 | "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", 715 | "dev": true, 716 | "requires": { 717 | "array.prototype.map": "^1.0.1", 718 | "define-properties": "^1.1.3", 719 | "es-abstract": "^1.17.0-next.1", 720 | "function-bind": "^1.1.1", 721 | "iterate-value": "^1.0.0" 722 | } 723 | }, 724 | "readdirp": { 725 | "version": "3.3.0", 726 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", 727 | "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", 728 | "dev": true, 729 | "requires": { 730 | "picomatch": "^2.0.7" 731 | } 732 | }, 733 | "require-directory": { 734 | "version": "2.1.1", 735 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 736 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 737 | "dev": true 738 | }, 739 | "require-main-filename": { 740 | "version": "2.0.0", 741 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 742 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 743 | "dev": true 744 | }, 745 | "serialize-javascript": { 746 | "version": "3.0.0", 747 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", 748 | "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", 749 | "dev": true 750 | }, 751 | "set-blocking": { 752 | "version": "2.0.0", 753 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 754 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 755 | "dev": true 756 | }, 757 | "slugid": { 758 | "version": "2.0.0", 759 | "resolved": "https://registry.npmjs.org/slugid/-/slugid-2.0.0.tgz", 760 | "integrity": "sha512-zTCivUfTk2GC6MU4Fjcz0iXwAjhe0NweMJqpfWcGrBbrm2dWtVAUupAonfsc7ysw4M0kZ934Nle5ljwM2dR+/g==", 761 | "dev": true, 762 | "requires": { 763 | "uuid": "^3.2.1", 764 | "uuid-parse": "^1.0.0" 765 | } 766 | }, 767 | "sprintf-js": { 768 | "version": "1.0.3", 769 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 770 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 771 | "dev": true 772 | }, 773 | "string-width": { 774 | "version": "2.1.1", 775 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 776 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 777 | "dev": true, 778 | "requires": { 779 | "is-fullwidth-code-point": "^2.0.0", 780 | "strip-ansi": "^4.0.0" 781 | } 782 | }, 783 | "string.prototype.trimend": { 784 | "version": "1.0.1", 785 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", 786 | "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", 787 | "dev": true, 788 | "requires": { 789 | "define-properties": "^1.1.3", 790 | "es-abstract": "^1.17.5" 791 | } 792 | }, 793 | "string.prototype.trimstart": { 794 | "version": "1.0.1", 795 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", 796 | "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", 797 | "dev": true, 798 | "requires": { 799 | "define-properties": "^1.1.3", 800 | "es-abstract": "^1.17.5" 801 | } 802 | }, 803 | "strip-ansi": { 804 | "version": "4.0.0", 805 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 806 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 807 | "dev": true, 808 | "requires": { 809 | "ansi-regex": "^3.0.0" 810 | } 811 | }, 812 | "strip-json-comments": { 813 | "version": "3.0.1", 814 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", 815 | "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", 816 | "dev": true 817 | }, 818 | "supports-color": { 819 | "version": "7.1.0", 820 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 821 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 822 | "dev": true, 823 | "requires": { 824 | "has-flag": "^4.0.0" 825 | }, 826 | "dependencies": { 827 | "has-flag": { 828 | "version": "4.0.0", 829 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 830 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 831 | "dev": true 832 | } 833 | } 834 | }, 835 | "to-regex-range": { 836 | "version": "5.0.1", 837 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 838 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 839 | "dev": true, 840 | "requires": { 841 | "is-number": "^7.0.0" 842 | } 843 | }, 844 | "uuid": { 845 | "version": "3.4.0", 846 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 847 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 848 | "dev": true 849 | }, 850 | "uuid-parse": { 851 | "version": "1.1.0", 852 | "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", 853 | "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==", 854 | "dev": true 855 | }, 856 | "which": { 857 | "version": "2.0.2", 858 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 859 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 860 | "dev": true, 861 | "requires": { 862 | "isexe": "^2.0.0" 863 | } 864 | }, 865 | "which-module": { 866 | "version": "2.0.0", 867 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 868 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 869 | "dev": true 870 | }, 871 | "wide-align": { 872 | "version": "1.1.3", 873 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 874 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 875 | "dev": true, 876 | "requires": { 877 | "string-width": "^1.0.2 || 2" 878 | } 879 | }, 880 | "workerpool": { 881 | "version": "6.0.0", 882 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", 883 | "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", 884 | "dev": true 885 | }, 886 | "wrap-ansi": { 887 | "version": "5.1.0", 888 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 889 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 890 | "dev": true, 891 | "requires": { 892 | "ansi-styles": "^3.2.0", 893 | "string-width": "^3.0.0", 894 | "strip-ansi": "^5.0.0" 895 | }, 896 | "dependencies": { 897 | "ansi-regex": { 898 | "version": "4.1.0", 899 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 900 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 901 | "dev": true 902 | }, 903 | "string-width": { 904 | "version": "3.1.0", 905 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 906 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 907 | "dev": true, 908 | "requires": { 909 | "emoji-regex": "^7.0.1", 910 | "is-fullwidth-code-point": "^2.0.0", 911 | "strip-ansi": "^5.1.0" 912 | } 913 | }, 914 | "strip-ansi": { 915 | "version": "5.2.0", 916 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 917 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 918 | "dev": true, 919 | "requires": { 920 | "ansi-regex": "^4.1.0" 921 | } 922 | } 923 | } 924 | }, 925 | "wrappy": { 926 | "version": "1.0.2", 927 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 928 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 929 | "dev": true 930 | }, 931 | "y18n": { 932 | "version": "4.0.0", 933 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 934 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 935 | "dev": true 936 | }, 937 | "yargs": { 938 | "version": "13.3.2", 939 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 940 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 941 | "dev": true, 942 | "requires": { 943 | "cliui": "^5.0.0", 944 | "find-up": "^3.0.0", 945 | "get-caller-file": "^2.0.1", 946 | "require-directory": "^2.1.1", 947 | "require-main-filename": "^2.0.0", 948 | "set-blocking": "^2.0.0", 949 | "string-width": "^3.0.0", 950 | "which-module": "^2.0.0", 951 | "y18n": "^4.0.0", 952 | "yargs-parser": "^13.1.2" 953 | }, 954 | "dependencies": { 955 | "ansi-regex": { 956 | "version": "4.1.0", 957 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 958 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 959 | "dev": true 960 | }, 961 | "find-up": { 962 | "version": "3.0.0", 963 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 964 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 965 | "dev": true, 966 | "requires": { 967 | "locate-path": "^3.0.0" 968 | } 969 | }, 970 | "locate-path": { 971 | "version": "3.0.0", 972 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 973 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 974 | "dev": true, 975 | "requires": { 976 | "p-locate": "^3.0.0", 977 | "path-exists": "^3.0.0" 978 | } 979 | }, 980 | "p-locate": { 981 | "version": "3.0.0", 982 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 983 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 984 | "dev": true, 985 | "requires": { 986 | "p-limit": "^2.0.0" 987 | } 988 | }, 989 | "path-exists": { 990 | "version": "3.0.0", 991 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 992 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 993 | "dev": true 994 | }, 995 | "string-width": { 996 | "version": "3.1.0", 997 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 998 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 999 | "dev": true, 1000 | "requires": { 1001 | "emoji-regex": "^7.0.1", 1002 | "is-fullwidth-code-point": "^2.0.0", 1003 | "strip-ansi": "^5.1.0" 1004 | } 1005 | }, 1006 | "strip-ansi": { 1007 | "version": "5.2.0", 1008 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1009 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1010 | "dev": true, 1011 | "requires": { 1012 | "ansi-regex": "^4.1.0" 1013 | } 1014 | } 1015 | } 1016 | }, 1017 | "yargs-parser": { 1018 | "version": "13.1.2", 1019 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 1020 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 1021 | "dev": true, 1022 | "requires": { 1023 | "camelcase": "^5.0.0", 1024 | "decamelize": "^1.2.0" 1025 | } 1026 | }, 1027 | "yargs-unparser": { 1028 | "version": "1.6.0", 1029 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", 1030 | "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", 1031 | "dev": true, 1032 | "requires": { 1033 | "flat": "^4.1.0", 1034 | "lodash": "^4.17.15", 1035 | "yargs": "^13.3.0" 1036 | } 1037 | } 1038 | } 1039 | } 1040 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "static-json-db", 3 | "version": "0.0.2", 4 | "description": "The Native Jamstack Database. A NoSQL key-value database stored as a directory tree of small JSON files which can be deployed as part of a static website and queried from client browsers in an efficient manner.", 5 | "main": "index.js", 6 | "files": [ 7 | "browser.js" 8 | ], 9 | "homepage": "https://github.com/jlxw/static-json-db", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/jlxw/static-json-db.git" 13 | }, 14 | "scripts": { 15 | "test": "mocha" 16 | }, 17 | "keywords": [ 18 | "static", 19 | "json", 20 | "db", 21 | "jamstack", 22 | "nosql" 23 | ], 24 | "author": "Jason Ling Xiaowei", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "mocha": "^8.0.1", 28 | "slugid": "^2.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var slugid = require('slugid'); 4 | 5 | var assert = require('assert') 6 | const { execSync } = require('child_process'); 7 | 8 | execSync('rm -rf test/tmp/*',{stdio: 'inherit'}) 9 | 10 | const StaticJsonDb = require('../index.js') 11 | const sjdb = new StaticJsonDb('test/tmp',{shard_threshold: 1000}) 12 | 13 | 14 | describe('sjdb', function() { 15 | describe('set()', function() { 16 | it('test long key doesnt create new shard by itself', async function() { 17 | let sjdb = new StaticJsonDb('test/tmp',{shard_threshold: 30}) 18 | let k='sakldgjhajlkghsalkghsakgjhasdjkghasljgsalasbglsjagslajg', v='world' 19 | await sjdb.set(k,v) 20 | await sjdb.write() 21 | assert.equal((await (new StaticJsonDb('test/tmp')).getShard("")).d[k],v) 22 | await sjdb.set(k,v) 23 | await sjdb.set(k,v) 24 | await sjdb.set(k,v) 25 | await sjdb.write() 26 | assert.equal((await (new StaticJsonDb('test/tmp')).getShard("")).d[k],v) 27 | assert.equal((await (new StaticJsonDb('test/tmp')).get(k)),v); 28 | assert.equal((await (new StaticJsonDb('test/tmp')).getAll(""))[k],v); 29 | 30 | let i = await (new StaticJsonDb('test/tmp')).getAll() 31 | assert.equal(Object.keys(i).length,1); 32 | }); 33 | }) 34 | describe('set()', function() { 35 | 36 | it('test 1 key', async function() { 37 | let k='Hello', v='World' 38 | await sjdb.set(k,v) 39 | await sjdb.write() 40 | assert.equal(await (new StaticJsonDb('test/tmp')).get(k),v); 41 | assert.equal((await (new StaticJsonDb('test/tmp')).getAll(""))[k],v); 42 | 43 | let i = await (new StaticJsonDb('test/tmp')).getAll() 44 | assert.equal(Object.keys(i).length,1); 45 | }); 46 | 47 | it('test write change in place', async function() { 48 | let k='Hello', v=['World'] 49 | await sjdb.set(k,v) 50 | await sjdb.write() 51 | v.push('2') 52 | await sjdb.set(k,v) 53 | await sjdb.write() 54 | assert.equal(JSON.stringify(await (new StaticJsonDb('test/tmp')).get(k)),JSON.stringify(v)); 55 | }); 56 | 57 | it('test keys', async function() { 58 | await sjdb.set('abc','world') 59 | await sjdb.set('dbe','world') 60 | await sjdb.set('hello','world') 61 | await sjdb.set('hello1','world1') 62 | await sjdb.set('hello2','world') 63 | await sjdb.set('hello3','world') 64 | await sjdb.set('hello4','world') 65 | await sjdb.set('hello5','world') 66 | await sjdb.set('hello6','world') 67 | await sjdb.set('hello7','world') 68 | await sjdb.set('hello41','world') 69 | await sjdb.set('hello42','world') 70 | await sjdb.set('hello43','world') 71 | await sjdb.set('hello44','world') 72 | await sjdb.set('hello45','world') 73 | await sjdb.set('hello455','world') 74 | await sjdb.set('hello456','world1') 75 | await sjdb.set('hello46','world46') 76 | await sjdb.write() 77 | assert.equal(await (new StaticJsonDb('test/tmp')).get('hello1'),'world1'); 78 | assert.equal(await (new StaticJsonDb('test/tmp')).get('hello46'),'world46'); 79 | 80 | let i = await (new StaticJsonDb('test/tmp')).getAll("hello4") 81 | assert.equal(Object.keys(i).length,9); 82 | assert.equal(i['hello46'],'world46'); 83 | 84 | await sjdb.delete('hello456') 85 | await sjdb.write() 86 | assert.equal(await (new StaticJsonDb('test/tmp')).get('hello456'),undefined); 87 | }); 88 | 89 | it('test 1000 keys with prefix', async function() { 90 | this.timeout(20000) 91 | let m = mock_data(2000,'abcd') 92 | m = m.concat(mock_data(2000,'abc')) 93 | for(let [k,v] of m) { 94 | await sjdb.set(k,v) 95 | } 96 | await sjdb.write() 97 | let sjdb2=new StaticJsonDb('test/tmp') 98 | for(let [k,v] of m) { 99 | assert.equal(await sjdb2.get(k),v); 100 | } 101 | }); 102 | 103 | it('test 10000 keys', async function() { 104 | this.timeout(20000) 105 | let m = mock_data(10000) 106 | for(let [k,v] of m) { 107 | await sjdb.set(k,v) 108 | } 109 | await sjdb.write() 110 | let sjdb2=new StaticJsonDb('test/tmp') 111 | for(let [k,v] of m) { 112 | assert.equal(await sjdb2.get(k),v); 113 | } 114 | }); 115 | 116 | }); 117 | }); 118 | 119 | function mock_data(keys=100,prefix="") { 120 | let o=[] 121 | for(let i=0;i