├── .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 | [](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