├── example
├── .gitignore
├── webpack.config.js
├── web
│ └── index.html
├── backend.ol
├── package.json
├── js
│ └── index.js
├── main.ol
└── package-lock.json
├── webpack.config.js
├── package.json
├── .gitignore
├── jo.js
├── lib
└── jo.ts
├── README.md
├── LICENSE
└── tsconfig.json
/example/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
--------------------------------------------------------------------------------
/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | mode: 'development',
5 | entry: './js/index.js',
6 | output: {
7 | filename: 'bundle.js',
8 | path: path.resolve(__dirname, 'web', 'js'),
9 | },
10 | resolve: {
11 | extensions: ['.ts', '.js'],
12 | },
13 | }
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hello, World!
5 |
6 | Result from Jo
7 |
9 | Result from Jor
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/backend.ol:
--------------------------------------------------------------------------------
1 |
2 |
3 | interface BackendInterface {
4 | RequestResponse:
5 | test( void )( int )
6 | }
7 |
8 | service Backend {
9 | execution: concurrent
10 |
11 | inputPort Input {
12 | location: "local"
13 | interfaces: BackendInterface
14 | }
15 |
16 | main {
17 | test()(res){
18 | res = 1
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './lib/jo.ts',
5 | module: {
6 | rules: [
7 | {
8 | test: /\.tsx?$/,
9 | use: 'ts-loader',
10 | exclude: /node_modules/,
11 | },
12 | ],
13 | },
14 | resolve: {
15 | extensions: ['.tsx', '.ts', '.js'],
16 | },
17 | output: {
18 | filename: 'jo.js',
19 | path: path.resolve(__dirname, 'dist'),
20 | libraryTarget: "commonjs2"
21 | },
22 | mode: "development"
23 | };
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "main.ol",
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "repo": "",
8 | "keywords": [],
9 | "author": "nau",
10 | "license": "ISC",
11 | "scripts": {
12 | "watch": "nodemon --ignore 'web' -e js --signal SIGINT --exec 'npm run build && npm run start-jolie'",
13 | "start-jolie": "jolie main.ol",
14 | "build": "webpack"
15 | },
16 | "devDependencies": {
17 | "nodemon": "^2.0.19"
18 | },
19 | "jolie": {
20 | "dependencies": {
21 | "@jolie/leonardo": "0.4.1"
22 | },
23 | "maven": {
24 | "dependencies": {},
25 | "indirectDependencies": {}
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jolie/jo",
3 | "version": "1.0.2",
4 | "description": "A thin JavaScript library for accessing microservice APIs",
5 | "main": "dist/jo.js",
6 | "types": "dist/index.d.ts",
7 | "files": [
8 | "/dist"
9 | ],
10 | "scripts": {
11 | "test": "echo \"Error: no test specified\" && exit 1",
12 | "build-dev": "webpack --mode=development",
13 | "run-example": "npm --prefix example run watch",
14 | "build-prod": "webpack --mode=production"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/fmontesi/jo.git"
19 | },
20 | "keywords": [
21 | "jolie",
22 | "microservices",
23 | "jo",
24 | "rest"
25 | ],
26 | "author": {
27 | "name": "Fabrizio Montesi",
28 | "email": "famontesi@gmail.com",
29 | "url": "https://www.fabriziomontesi.com"
30 | },
31 | "license": "Apache-2.0",
32 | "bugs": {
33 | "url": "https://github.com/fmontesi/jo/issues"
34 | },
35 | "homepage": "https://github.com/fmontesi/jo#readme",
36 | "devDependencies": {
37 | "ts-loader": "^9.3.1",
38 | "typescript": "^4.8.2",
39 | "webpack": "^5.74.0",
40 | "webpack-cli": "^4.10.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/example/js/index.js:
--------------------------------------------------------------------------------
1 | const {Jo, Jor, JoHelp} = require("../../dist/jo")
2 | // Call operation search on the exposed service ChuckNorris
3 | Jo("ChuckNorris").search({ query: "Computer" })
4 | .then(response => {
5 | // Pick a random joke
6 | // api.chucknorris.io returns jokes in a "result" array subelement
7 | if (response.result) {
8 | for (const joke of response.result) {
9 | const li = document.createElement("li")
10 | const text = document.createTextNode(joke.value)
11 | li.appendChild(text)
12 | document.getElementById('hello-jo').appendChild(li)
13 | }
14 | }
15 | })
16 | .catch(JoHelp.parseError).catch(alert);
17 |
18 | Jor("ChuckNorris").search.get({ query: "Earth" })
19 | .then(response => {
20 | // Pick a random joke
21 | // api.chucknorris.io returns jokes in a "result" array subelement
22 | if (response.result) {
23 | for (const joke of response.result) {
24 | const li = document.createElement("li")
25 | const text = document.createTextNode(joke.value)
26 | li.appendChild(text)
27 | document.getElementById('hello-jor').appendChild(li)
28 | }
29 | }
30 | })
31 | .catch(JoHelp.parseError).catch(alert);
32 |
33 |
34 | Jo.test()
35 | .then(response => {
36 | // response = {$:1}
37 | console.log("Jo test response", response)
38 | })
39 | .catch(JoHelp.parseError).catch(alert);
40 |
41 | Jor.test.get()
42 | .then(response => {
43 | // response = {$:1}
44 | console.log("Jor test response", response)
45 | })
46 | .catch(JoHelp.parseError).catch(alert);
47 | document.getElementById('hello-webpack').innerText = 'Hello from webpack!'
48 |
49 | export default {}
--------------------------------------------------------------------------------
/example/main.ol:
--------------------------------------------------------------------------------
1 | from protocols.http import DefaultOperationHttpRequest
2 | from console import Console
3 | from string-utils import StringUtils
4 | from @jolie.leonardo import WebFiles
5 | from .backend import Backend
6 |
7 |
8 | /// Operations offered through the web interface
9 | interface WebInterface {
10 | RequestResponse:
11 | /// Generic GET request
12 | get( DefaultOperationHttpRequest )( undefined )
13 | }
14 |
15 | service Main {
16 | execution: concurrent
17 |
18 | embed Console as console
19 | embed WebFiles as webFiles
20 | embed StringUtils as stringUtils
21 | embed Backend as backend
22 |
23 | // Output port to contact api.chucknorris.io
24 | outputPort Chuck {
25 | Location: "socket://api.chucknorris.io:443/" // Use TCP/IP, port 443
26 | Protocol: https {
27 | .osc.search.method = "get"; // HTTP method for operation search
28 | .osc.search.alias = "jokes/search?query=%{query}" // URI template for operation search
29 | }
30 | RequestResponse: search // Request-response operation declaration (search)
31 | }
32 |
33 | inputPort WebInput {
34 | location: "socket://localhost:8080"
35 | protocol: http {
36 | format -> httpParams.format
37 | contentType -> httpParams.contentType
38 | cacheControl.maxAge -> httpParams.cacheControl.maxAge
39 | redirect -> redirect
40 | statusCode -> statusCode
41 | default.get = "get"
42 | }
43 | Redirects:
44 | ChuckNorris => Chuck
45 | aggregates: backend
46 | interfaces: WebInterface
47 | }
48 |
49 | init {
50 | global.wwwDir = "web"
51 | format = "html"
52 |
53 | println@console( "Server started at " + global.inputPorts.WebInput.location )()
54 | }
55 |
56 | main {
57 | get( request )( response ) {
58 | scope( get ) {
59 | install(
60 | FileNotFound =>
61 | statusCode = 404,
62 | MovedPermanently =>
63 | redirect = get.MovedPermanently
64 | statusCode = 301
65 | )
66 | get@webFiles( {
67 | target = request.operation
68 | wwwDir = global.wwwDir
69 | } )( getResult )
70 | httpParams -> getResult.httpParams
71 | response -> getResult.content
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | # jolie related field
133 | packages/
134 | lib/
135 | example/web/js/bundle.js
136 |
--------------------------------------------------------------------------------
/jo.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Fabrizio Montesi
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | (function() {
18 | "use strict";
19 |
20 | function toJson( response )
21 | {
22 | return response.json();
23 | }
24 |
25 | function genError( response )
26 | {
27 | if( response.ok ) {
28 | return Promise.resolve( response );
29 | } else {
30 | const error = new Error( response.statusText );
31 | return response.json().then( json => {
32 | error.isFault = true;
33 | error.fault = json.error;
34 | throw error;
35 | } );
36 | }
37 | }
38 |
39 | function jolieFetch( operation, params )
40 | {
41 | if ( typeof params.service === 'undefined' ) {
42 | operation = '/' + operation;
43 | } else {
44 | if ( !params.service.startsWith( '/' ) ) {
45 | params.service = '/' + params.service;
46 | }
47 | operation = '/!' + params.service + '!/' + operation;
48 | }
49 | params.headers = { "Content-Type": "application/json" };
50 |
51 | if ( typeof params.method === 'undefined' ) {
52 | params.method = 'POST';
53 | }
54 |
55 | if ( typeof params.data !== 'undefined' ) {
56 | if ( params.method === 'GET' || params.method === 'HEAD' ) {
57 | operation += "?" + JSON.stringify( params.data );
58 | } else {
59 | params.body = JSON.stringify( params.data );
60 | }
61 | }
62 |
63 | return fetch( operation, params ).then( genError ).then( toJson );
64 | }
65 |
66 | function initParams( data, params )
67 | {
68 | if ( typeof params === 'undefined' ) {
69 | params = {};
70 | }
71 | if ( typeof data !== 'undefined' ) {
72 | params.data = data;
73 | }
74 | return params;
75 | }
76 |
77 | const proxyBuilder = ( service ) => {
78 | return new Proxy( {}, {
79 | get: ( target, prop, receiver ) => {
80 | return ( data, params ) => {
81 | params = initParams( data, params );
82 | params.service = service;
83 | return jolieFetch( prop, params );
84 | }
85 | }
86 | } );
87 | }
88 |
89 | const buildHttpVerbs = ( buildFetch ) => { return {
90 | get: ( data, params ) => {
91 | return buildFetch( data, params, 'GET' );
92 | },
93 | post: ( data, params ) => {
94 | return buildFetch( data, params, 'POST' );
95 | },
96 | delete: ( data, params ) => {
97 | return buildFetch( data, params, 'DELETE' );
98 | },
99 | head: ( data, params ) => {
100 | return buildFetch( data, params, 'HEAD' );
101 | },
102 | patch: ( data, params ) => {
103 | return buildFetch( data, params, 'PATCH' );
104 | },
105 | options: ( data, params ) => {
106 | return buildFetch( data, params, 'OPTIONS' );
107 | },
108 | put: ( data, params ) => {
109 | return buildFetch( data, params, 'PUT' );
110 | }
111 | } };
112 |
113 | const resourceProxyBuilder = ( service ) => {
114 | return new Proxy( {}, {
115 | get: ( target, prop, receiver ) => {
116 | const buildFetch = ( data, params, method ) => {
117 | params = initParams( data, params );
118 | params.service = service;
119 | params.method = method;
120 | return jolieFetch( prop, params );
121 | };
122 | return buildHttpVerbs( buildFetch );
123 | }
124 | } )
125 | }
126 |
127 | window.Jo = new Proxy( proxyBuilder, {
128 | get: ( target, prop, receiver ) => {
129 | return ( data, params ) => {
130 | params = initParams( data, params );
131 | return jolieFetch( prop, params );
132 | }
133 | }
134 | } );
135 |
136 | window.Jor = new Proxy( resourceProxyBuilder, {
137 | get: ( target, prop, receiver ) => {
138 | return ( data, params ) => {
139 | const buildFetch = ( data, params, method ) => {
140 | params = initParams( data, params );
141 | params.method = method;
142 | return jolieFetch( prop, params );
143 | };
144 | return buildHttpVerbs( buildFetch );
145 | }
146 | }
147 | } );
148 |
149 | window.JoHelp = {
150 | parseError: error => {
151 | if ( error.isFault ) {
152 | return Promise.reject( JSON.stringify( error.fault ) );
153 | } else {
154 | return Promise.reject( error.message );
155 | }
156 | }
157 | };
158 | }());
159 |
--------------------------------------------------------------------------------
/lib/jo.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Fabrizio Montesi , et. al.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | interface JolieFault extends Error {
18 | isFault: boolean;
19 | fault: Error;
20 | }
21 |
22 | interface FetchParams extends RequestInit {
23 | service: string;
24 | headers: Record;
25 | method: string;
26 | data: any;
27 | }
28 |
29 | interface FetchBuilder {
30 | (
31 | data: Object,
32 | params: any,
33 | method: "GET" | "POST" | "DELETE" | "HEAD" | "PATCH" | "OPTIONS" | "PUT"
34 | ): Promise;
35 | }
36 |
37 | function toJson(response: Response): Promise {
38 | return response.json();
39 | }
40 |
41 | function genError(response: Response): Promise {
42 | if (response.ok) {
43 | return Promise.resolve(response);
44 | } else {
45 | return response.json().then((json) => {
46 | const error: JolieFault = {
47 | name: response.statusText,
48 | message: response.statusText,
49 | isFault: true,
50 | fault: json.error,
51 | };
52 | error.isFault = true;
53 | error.fault = json.error;
54 | throw error;
55 | });
56 | }
57 | }
58 |
59 | function jolieFetch(operation: string, params: FetchParams): Promise {
60 | if (typeof params.service === "undefined") {
61 | operation = "/" + operation;
62 | } else {
63 | if (!params.service.startsWith("/")) {
64 | params.service = "/" + params.service;
65 | }
66 | operation = "/!" + params.service + "!/" + operation;
67 | }
68 | params.headers = { "Content-Type": "application/json" };
69 |
70 | if (typeof params.method === "undefined") {
71 | params.method = "POST";
72 | }
73 |
74 | if (typeof params.data !== "undefined") {
75 | if (params.method === "GET" || params.method === "HEAD") {
76 | operation += "?" + JSON.stringify(params.data);
77 | } else {
78 | params.body = JSON.stringify(params.data);
79 | }
80 | }
81 |
82 | return fetch(operation, params).then(genError).then(toJson);
83 | }
84 |
85 | function initParams(data: Object, params: any): FetchParams {
86 | if (typeof params === "undefined") {
87 | params = {};
88 | }
89 | if (typeof data !== "undefined") {
90 | params.data = data;
91 | }
92 | return params;
93 | }
94 |
95 | const proxyBuilder = (service: string) => {
96 | return new Proxy(
97 | {},
98 | {
99 | get: (target, prop, receiver) => {
100 | return (data: Object, params:any) => {
101 | params = initParams(data, params);
102 | params.service = service;
103 | return jolieFetch(prop.toString(), params);
104 | };
105 | },
106 | }
107 | );
108 | };
109 |
110 | const buildHttpVerbs = (buildFetch: FetchBuilder) => {
111 | return {
112 | get: (data: Object, params: any) => {
113 | return buildFetch(data, params, "GET");
114 | },
115 | post: (data: Object, params: any) => {
116 | return buildFetch(data, params, "POST");
117 | },
118 | delete: (data: Object, params: any) => {
119 | return buildFetch(data, params, "DELETE");
120 | },
121 | head: (data: Object, params: any) => {
122 | return buildFetch(data, params, "HEAD");
123 | },
124 | patch: (data: Object, params: any) => {
125 | return buildFetch(data, params, "PATCH");
126 | },
127 | options: (data: Object, params: any) => {
128 | return buildFetch(data, params, "OPTIONS");
129 | },
130 | put: (data: Object, params: any) => {
131 | return buildFetch(data, params, "PUT");
132 | },
133 | };
134 | };
135 |
136 | const resourceProxyBuilder = (service: string) => {
137 | return new Proxy(
138 | {},
139 | {
140 | get: (target, prop, receiver) => {
141 | const buildFetch: FetchBuilder = (data, params, method) => {
142 | const fetchParams: FetchParams = initParams(data, params);
143 | fetchParams.service = service;
144 | fetchParams.method = method;
145 | return jolieFetch(prop.toString(), fetchParams);
146 | };
147 | return buildHttpVerbs(buildFetch);
148 | },
149 | }
150 | );
151 | };
152 |
153 | export const Jo = new Proxy(proxyBuilder, {
154 | get: (target, prop, receiver) => {
155 | return (data: Object, params: any) => {
156 | const fetchParams = initParams(data, params);
157 | return jolieFetch(prop.toString(), fetchParams);
158 | };
159 | },
160 | });
161 |
162 | export const Jor = new Proxy(resourceProxyBuilder, {
163 | get: (target, prop, receiver) => {
164 | const buildFetch: FetchBuilder = (data, params, method) => {
165 | const fetchParams = initParams(data, params);
166 | fetchParams.method = method;
167 | return jolieFetch(prop.toString(), fetchParams);
168 | };
169 | return buildHttpVerbs(buildFetch);
170 | },
171 | });
172 |
173 | export const JoHelp = {
174 | parseError: (error: JolieFault) => {
175 | if (error.isFault) {
176 | return Promise.reject(JSON.stringify(error.fault));
177 | } else {
178 | return Promise.reject(error.message);
179 | }
180 | },
181 | };
182 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jo
2 |
3 | Access web APIs as JavaScript objects. Jo (pronounced "Yo!") is a modest and thin library to write simpler client code.
4 |
5 | 
6 |
7 | Jo supports both verb-oriented APIs (e.g., `/getJoke?{id:1}`) and resource-oriented APIs (e.g., `/jokes/1`).
8 | You can use both interchangeably, which is useful when you have to interact with microservices that adopt different API styles.
9 |
10 | Jo can be used with any web server. It uses JSON as data format (more formats could be added in the future). It includes native support for [Jolie](https://www.jolie-lang.org/) API Gateways (aka [Jolie redirections](https://jolielang.gitbook.io/docs/architectural-composition/redirection)).
11 |
12 | # Installation
13 |
14 | ```
15 | npm i @jolie/jo
16 | ```
17 |
18 | # Usage: Verb-oriented APIs
19 |
20 | Verb-oriented APIs can be accessed through the global `Jo` object.
21 |
22 | ## Invoking an operation from the server
23 |
24 | Syntax: `Jo.operation( [data, params] )` where
25 | - `operation` is the operation you want to invoke;
26 | - the optional `data` to be sent is a JSON object;
27 | - the optional `params` are parameters for the underlying `fetch` operation (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), for example in case you need to specify HTTP headers.
28 |
29 | Invoking this method returns a promise.
30 |
31 | This method uses POST as default HTTP method. To change it, use the optional parameters. For example, to use GET: `Jo.operation( data, { method: 'GET' } )`.
32 |
33 | ### Example
34 |
35 | Suppose the originating web server offers an operation `greet` that returns a string given a tree with subnode `name`.
36 | (Nanoservices are great for examples, not so much for production: do this at home only!)
37 |
38 | You can invoke it as follows.
39 |
40 | ```javascript
41 | import {Jo} from '@jolie/jo'
42 |
43 | Jo.greet( { name: "Homer" } )
44 | .then( response => console.log( response.greeting ) ) // Jo uses promises
45 | .catch( error => { // an error occurred
46 | if ( error.isFault ) { // It's an application error
47 | console.log( JSON.stringify( error.fault ) );
48 | } else { // It's a middleware error
49 | console.log( error.message );
50 | }
51 | } );
52 | ```
53 |
54 | Here is how this operation would be implemented in a Jolie service.
55 |
56 | ```jolie
57 | greet( request )( response ) {
58 | response.greeting = "Hello " + request.name
59 | }
60 | ```
61 |
62 | ## Catching errors, the alternative way
63 |
64 | Distinguishing application and middleware errors might be boring.
65 | Use `JoHelp.parseError` (`JoHelp` is pronounced "Yo! Help!") to get that part done for you automatically. You will get a string containing the error message, if it is a middleware error, or a JSON.stringify of the carried data, if it is an application error.
66 |
67 | ```javascript
68 | Jo.greet( { name: "Homer" } )
69 | .then( response => console.log( response.greeting ) )
70 | .catch( JoHelp.parseError ).catch( console.log );
71 | ```
72 |
73 | ## Jolie Redirections
74 |
75 | Jo supports [redirection](https://jolielang.gitbook.io/docs/architectural-composition/redirection) (not to be confused with HTTP redirections), the Jolie primitive to build API gateways with named subservices. (Unnamed subservices in the gateway, obtained by [aggregation](https://jolielang.gitbook.io/docs/architectural-composition/aggregation), are available as normal operations, so they can be called with the previous syntax.)
76 |
77 | Suppose that the originating Jolie service has a redirection table as follows.
78 | ```jolie
79 | Redirects: Greeter => GreeterService
80 | ```
81 |
82 | If `GreeterService` has our operation `greet`, we can invoke it as follows.
83 |
84 | ```javascript
85 | Jo("Greeter").greet( { name: "Homer" } )
86 | .then( response => console.log( response.greeting ) )
87 | .catch( JoHelp.parseError ).catch( console.log );
88 | ```
89 |
90 | If your Jolie API gateway points to another API gateway, you can nest them!
91 |
92 | ```javascript
93 | Jo("SubGateway1/SubGateway2/Greeter").greet( { name: "Homer" } )
94 | .then( response => console.log( response.greeting ) )
95 | .catch( JoHelp.parseError ).catch( console.log );
96 | ```
97 |
98 | # Usage: Resource-oriented APIs
99 |
100 | Resource-oriented APIs can be accessed through the global `Jor` object.
101 |
102 | Syntax: `Jor.resource.http_method( data [, params] )` where
103 | - `resource` is the name of the resource you want to access;
104 | - `http_method` is an HTTP method (use lowercase): `get`, `post`, `delete`, `put`, `head`, `patch`, or `options`;
105 | - the `data` to be sent is a JSON object;
106 | - the optional `params` are parameters for the underlying `fetch` operation (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
107 |
108 | If `resource` has a name that cannot be written in JavaScript, you can use the alternative syntax `Jor["resource"].http_method( data [, params] )`.
109 |
110 | ### Example
111 |
112 | Suppose the web server offers a resource `/jokes`, and you want to get all of them.
113 |
114 | ```javascript
115 | import {Jor} from '@jolie/jo'
116 |
117 | Jor.jokes.get()
118 | .then( response => /* handle all the jokes */ )
119 | .catch( JoHelp.parseError ).catch( console.log );
120 | ```
121 |
122 | ## Redirections
123 |
124 | Redirections are supported by `Jor` just as for verb-based APIs. Suppose the server offers the `/jokes` resource through the subservice `ChuckNorris`. Then we can access it as follows.
125 |
126 | ```javascript
127 | Jor("ChuckNorris").jokes.get()
128 | .then( response => /* handle all the jokes */ )
129 | .catch( JoHelp.parseError ).catch( console.log );
130 | ```
131 |
132 | # Installation
133 |
134 | ```html
135 |
136 | ```
137 |
138 | Or, download Jo (https://raw.githubusercontent.com/fmontesi/jo/master/lib/jo.js) and use it locally.
139 |
140 | Pull requests with better ways to distribute Jo are welcome.
141 |
142 | # Dependencies
143 |
144 | There are no dependencies on other libraries. However, Jo uses some recent features offered by web browsers.
145 |
146 | - Jo uses [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to perform asynchronous calls. If you want to use Jo with older browsers, use a [polyfill for fetch](https://github.com/github/fetch). Check which browsers support fetch here: https://caniuse.com/#feat=fetch.
147 |
148 | - Jo uses some modern JavaScript features. [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) is used to implement the magic of calling the operations of your Jolie server as if they were native methods (`Jo.operation`). We also use [Arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). Check which browsers support Proxy at https://caniuse.com/#feat=proxy, and which support arrow functions at https://caniuse.com/#feat=arrow-functions. If you want to use Jo in browsers that do not support these features, you can try compiling Jo with [Babel](https://babeljs.io/).
149 |
150 | # FAQ
151 |
152 | ## How do I handle basic values in root nodes sent by Jolie? (AKA response.$)
153 |
154 | In JSON, an element can either be a basic value (e.g., strings, numbers), an object, or an array.
155 | In Jolie, there are no restrictions: an element is always a tree, and each node can contain _both_ a basic value and subnodes (similarly to markup languages).
156 | For example, this is valid Jolie: `"Homer" { .children[0] = "Bart", .children[1] = "Lisa" }`. It gives a tree containing the string `Homer` in its root node, which has an array subnode `children` with two elements. If you receive this tree using `Jo` in variable `response`, you can access the value contained in the root node (`"Homer"` in our example) by `response.$`.
157 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | // "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26 |
27 | /* Modules */
28 | "module": "commonjs", /* Specify what module code is generated. */
29 | // "rootDir": "./", /* Specify the root folder within your source files. */
30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
38 | // "resolveJsonModule": true, /* Enable importing .json files. */
39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
40 |
41 | /* JavaScript Support */
42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
45 |
46 | /* Emit */
47 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
52 | "outDir": "./dist", /* Specify an output folder for all emitted files. */
53 | // "removeComments": true, /* Disable emitting comments. */
54 | // "noEmit": true, /* Disable emitting files from a compilation. */
55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
63 | // "newLine": "crlf", /* Set the newline character for emitting files. */
64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
70 |
71 | /* Interop Constraints */
72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
77 |
78 | /* Type Checking */
79 | "strict": true, /* Enable all strict type-checking options. */
80 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
98 |
99 | /* Completeness */
100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
102 | },
103 | "include": ["lib/**/*"],
104 | }
105 |
--------------------------------------------------------------------------------
/example/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "example",
9 | "version": "0.1.0",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "nodemon": "^2.0.19"
13 | }
14 | },
15 | "node_modules/abbrev": {
16 | "version": "1.1.1",
17 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
18 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
19 | "dev": true
20 | },
21 | "node_modules/anymatch": {
22 | "version": "3.1.2",
23 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
24 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
25 | "dev": true,
26 | "dependencies": {
27 | "normalize-path": "^3.0.0",
28 | "picomatch": "^2.0.4"
29 | },
30 | "engines": {
31 | "node": ">= 8"
32 | }
33 | },
34 | "node_modules/balanced-match": {
35 | "version": "1.0.2",
36 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
37 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
38 | "dev": true
39 | },
40 | "node_modules/binary-extensions": {
41 | "version": "2.2.0",
42 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
43 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
44 | "dev": true,
45 | "engines": {
46 | "node": ">=8"
47 | }
48 | },
49 | "node_modules/brace-expansion": {
50 | "version": "1.1.11",
51 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
52 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
53 | "dev": true,
54 | "dependencies": {
55 | "balanced-match": "^1.0.0",
56 | "concat-map": "0.0.1"
57 | }
58 | },
59 | "node_modules/braces": {
60 | "version": "3.0.2",
61 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
62 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
63 | "dev": true,
64 | "dependencies": {
65 | "fill-range": "^7.0.1"
66 | },
67 | "engines": {
68 | "node": ">=8"
69 | }
70 | },
71 | "node_modules/chokidar": {
72 | "version": "3.5.3",
73 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
74 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
75 | "dev": true,
76 | "funding": [
77 | {
78 | "type": "individual",
79 | "url": "https://paulmillr.com/funding/"
80 | }
81 | ],
82 | "dependencies": {
83 | "anymatch": "~3.1.2",
84 | "braces": "~3.0.2",
85 | "glob-parent": "~5.1.2",
86 | "is-binary-path": "~2.1.0",
87 | "is-glob": "~4.0.1",
88 | "normalize-path": "~3.0.0",
89 | "readdirp": "~3.6.0"
90 | },
91 | "engines": {
92 | "node": ">= 8.10.0"
93 | },
94 | "optionalDependencies": {
95 | "fsevents": "~2.3.2"
96 | }
97 | },
98 | "node_modules/concat-map": {
99 | "version": "0.0.1",
100 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
101 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
102 | "dev": true
103 | },
104 | "node_modules/debug": {
105 | "version": "3.2.7",
106 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
107 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
108 | "dev": true,
109 | "dependencies": {
110 | "ms": "^2.1.1"
111 | }
112 | },
113 | "node_modules/fill-range": {
114 | "version": "7.0.1",
115 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
116 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
117 | "dev": true,
118 | "dependencies": {
119 | "to-regex-range": "^5.0.1"
120 | },
121 | "engines": {
122 | "node": ">=8"
123 | }
124 | },
125 | "node_modules/fsevents": {
126 | "version": "2.3.2",
127 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
128 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
129 | "dev": true,
130 | "hasInstallScript": true,
131 | "optional": true,
132 | "os": [
133 | "darwin"
134 | ],
135 | "engines": {
136 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
137 | }
138 | },
139 | "node_modules/glob-parent": {
140 | "version": "5.1.2",
141 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
142 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
143 | "dev": true,
144 | "dependencies": {
145 | "is-glob": "^4.0.1"
146 | },
147 | "engines": {
148 | "node": ">= 6"
149 | }
150 | },
151 | "node_modules/has-flag": {
152 | "version": "3.0.0",
153 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
154 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
155 | "dev": true,
156 | "engines": {
157 | "node": ">=4"
158 | }
159 | },
160 | "node_modules/ignore-by-default": {
161 | "version": "1.0.1",
162 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
163 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
164 | "dev": true
165 | },
166 | "node_modules/is-binary-path": {
167 | "version": "2.1.0",
168 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
169 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
170 | "dev": true,
171 | "dependencies": {
172 | "binary-extensions": "^2.0.0"
173 | },
174 | "engines": {
175 | "node": ">=8"
176 | }
177 | },
178 | "node_modules/is-extglob": {
179 | "version": "2.1.1",
180 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
181 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
182 | "dev": true,
183 | "engines": {
184 | "node": ">=0.10.0"
185 | }
186 | },
187 | "node_modules/is-glob": {
188 | "version": "4.0.3",
189 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
190 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
191 | "dev": true,
192 | "dependencies": {
193 | "is-extglob": "^2.1.1"
194 | },
195 | "engines": {
196 | "node": ">=0.10.0"
197 | }
198 | },
199 | "node_modules/is-number": {
200 | "version": "7.0.0",
201 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
202 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
203 | "dev": true,
204 | "engines": {
205 | "node": ">=0.12.0"
206 | }
207 | },
208 | "node_modules/minimatch": {
209 | "version": "3.1.2",
210 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
211 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
212 | "dev": true,
213 | "dependencies": {
214 | "brace-expansion": "^1.1.7"
215 | },
216 | "engines": {
217 | "node": "*"
218 | }
219 | },
220 | "node_modules/ms": {
221 | "version": "2.1.3",
222 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
223 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
224 | "dev": true
225 | },
226 | "node_modules/nodemon": {
227 | "version": "2.0.19",
228 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
229 | "integrity": "sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==",
230 | "dev": true,
231 | "hasInstallScript": true,
232 | "dependencies": {
233 | "chokidar": "^3.5.2",
234 | "debug": "^3.2.7",
235 | "ignore-by-default": "^1.0.1",
236 | "minimatch": "^3.0.4",
237 | "pstree.remy": "^1.1.8",
238 | "semver": "^5.7.1",
239 | "simple-update-notifier": "^1.0.7",
240 | "supports-color": "^5.5.0",
241 | "touch": "^3.1.0",
242 | "undefsafe": "^2.0.5"
243 | },
244 | "bin": {
245 | "nodemon": "bin/nodemon.js"
246 | },
247 | "engines": {
248 | "node": ">=8.10.0"
249 | },
250 | "funding": {
251 | "type": "opencollective",
252 | "url": "https://opencollective.com/nodemon"
253 | }
254 | },
255 | "node_modules/nopt": {
256 | "version": "1.0.10",
257 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
258 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
259 | "dev": true,
260 | "dependencies": {
261 | "abbrev": "1"
262 | },
263 | "bin": {
264 | "nopt": "bin/nopt.js"
265 | },
266 | "engines": {
267 | "node": "*"
268 | }
269 | },
270 | "node_modules/normalize-path": {
271 | "version": "3.0.0",
272 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
273 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
274 | "dev": true,
275 | "engines": {
276 | "node": ">=0.10.0"
277 | }
278 | },
279 | "node_modules/picomatch": {
280 | "version": "2.3.1",
281 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
282 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
283 | "dev": true,
284 | "engines": {
285 | "node": ">=8.6"
286 | },
287 | "funding": {
288 | "url": "https://github.com/sponsors/jonschlinkert"
289 | }
290 | },
291 | "node_modules/pstree.remy": {
292 | "version": "1.1.8",
293 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
294 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
295 | "dev": true
296 | },
297 | "node_modules/readdirp": {
298 | "version": "3.6.0",
299 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
300 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
301 | "dev": true,
302 | "dependencies": {
303 | "picomatch": "^2.2.1"
304 | },
305 | "engines": {
306 | "node": ">=8.10.0"
307 | }
308 | },
309 | "node_modules/semver": {
310 | "version": "5.7.1",
311 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
312 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
313 | "dev": true,
314 | "bin": {
315 | "semver": "bin/semver"
316 | }
317 | },
318 | "node_modules/simple-update-notifier": {
319 | "version": "1.0.7",
320 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
321 | "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
322 | "dev": true,
323 | "dependencies": {
324 | "semver": "~7.0.0"
325 | },
326 | "engines": {
327 | "node": ">=8.10.0"
328 | }
329 | },
330 | "node_modules/simple-update-notifier/node_modules/semver": {
331 | "version": "7.0.0",
332 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
333 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
334 | "dev": true,
335 | "bin": {
336 | "semver": "bin/semver.js"
337 | }
338 | },
339 | "node_modules/supports-color": {
340 | "version": "5.5.0",
341 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
342 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
343 | "dev": true,
344 | "dependencies": {
345 | "has-flag": "^3.0.0"
346 | },
347 | "engines": {
348 | "node": ">=4"
349 | }
350 | },
351 | "node_modules/to-regex-range": {
352 | "version": "5.0.1",
353 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
354 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
355 | "dev": true,
356 | "dependencies": {
357 | "is-number": "^7.0.0"
358 | },
359 | "engines": {
360 | "node": ">=8.0"
361 | }
362 | },
363 | "node_modules/touch": {
364 | "version": "3.1.0",
365 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
366 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
367 | "dev": true,
368 | "dependencies": {
369 | "nopt": "~1.0.10"
370 | },
371 | "bin": {
372 | "nodetouch": "bin/nodetouch.js"
373 | }
374 | },
375 | "node_modules/undefsafe": {
376 | "version": "2.0.5",
377 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
378 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
379 | "dev": true
380 | }
381 | },
382 | "dependencies": {
383 | "abbrev": {
384 | "version": "1.1.1",
385 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
386 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
387 | "dev": true
388 | },
389 | "anymatch": {
390 | "version": "3.1.2",
391 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
392 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
393 | "dev": true,
394 | "requires": {
395 | "normalize-path": "^3.0.0",
396 | "picomatch": "^2.0.4"
397 | }
398 | },
399 | "balanced-match": {
400 | "version": "1.0.2",
401 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
402 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
403 | "dev": true
404 | },
405 | "binary-extensions": {
406 | "version": "2.2.0",
407 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
408 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
409 | "dev": true
410 | },
411 | "brace-expansion": {
412 | "version": "1.1.11",
413 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
414 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
415 | "dev": true,
416 | "requires": {
417 | "balanced-match": "^1.0.0",
418 | "concat-map": "0.0.1"
419 | }
420 | },
421 | "braces": {
422 | "version": "3.0.2",
423 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
424 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
425 | "dev": true,
426 | "requires": {
427 | "fill-range": "^7.0.1"
428 | }
429 | },
430 | "chokidar": {
431 | "version": "3.5.3",
432 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
433 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
434 | "dev": true,
435 | "requires": {
436 | "anymatch": "~3.1.2",
437 | "braces": "~3.0.2",
438 | "fsevents": "~2.3.2",
439 | "glob-parent": "~5.1.2",
440 | "is-binary-path": "~2.1.0",
441 | "is-glob": "~4.0.1",
442 | "normalize-path": "~3.0.0",
443 | "readdirp": "~3.6.0"
444 | }
445 | },
446 | "concat-map": {
447 | "version": "0.0.1",
448 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
449 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
450 | "dev": true
451 | },
452 | "debug": {
453 | "version": "3.2.7",
454 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
455 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
456 | "dev": true,
457 | "requires": {
458 | "ms": "^2.1.1"
459 | }
460 | },
461 | "fill-range": {
462 | "version": "7.0.1",
463 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
464 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
465 | "dev": true,
466 | "requires": {
467 | "to-regex-range": "^5.0.1"
468 | }
469 | },
470 | "fsevents": {
471 | "version": "2.3.2",
472 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
473 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
474 | "dev": true,
475 | "optional": true
476 | },
477 | "glob-parent": {
478 | "version": "5.1.2",
479 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
480 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
481 | "dev": true,
482 | "requires": {
483 | "is-glob": "^4.0.1"
484 | }
485 | },
486 | "has-flag": {
487 | "version": "3.0.0",
488 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
489 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
490 | "dev": true
491 | },
492 | "ignore-by-default": {
493 | "version": "1.0.1",
494 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
495 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
496 | "dev": true
497 | },
498 | "is-binary-path": {
499 | "version": "2.1.0",
500 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
501 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
502 | "dev": true,
503 | "requires": {
504 | "binary-extensions": "^2.0.0"
505 | }
506 | },
507 | "is-extglob": {
508 | "version": "2.1.1",
509 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
510 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
511 | "dev": true
512 | },
513 | "is-glob": {
514 | "version": "4.0.3",
515 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
516 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
517 | "dev": true,
518 | "requires": {
519 | "is-extglob": "^2.1.1"
520 | }
521 | },
522 | "is-number": {
523 | "version": "7.0.0",
524 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
525 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
526 | "dev": true
527 | },
528 | "minimatch": {
529 | "version": "3.1.2",
530 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
531 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
532 | "dev": true,
533 | "requires": {
534 | "brace-expansion": "^1.1.7"
535 | }
536 | },
537 | "ms": {
538 | "version": "2.1.3",
539 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
540 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
541 | "dev": true
542 | },
543 | "nodemon": {
544 | "version": "2.0.19",
545 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
546 | "integrity": "sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==",
547 | "dev": true,
548 | "requires": {
549 | "chokidar": "^3.5.2",
550 | "debug": "^3.2.7",
551 | "ignore-by-default": "^1.0.1",
552 | "minimatch": "^3.0.4",
553 | "pstree.remy": "^1.1.8",
554 | "semver": "^5.7.1",
555 | "simple-update-notifier": "^1.0.7",
556 | "supports-color": "^5.5.0",
557 | "touch": "^3.1.0",
558 | "undefsafe": "^2.0.5"
559 | }
560 | },
561 | "nopt": {
562 | "version": "1.0.10",
563 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
564 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
565 | "dev": true,
566 | "requires": {
567 | "abbrev": "1"
568 | }
569 | },
570 | "normalize-path": {
571 | "version": "3.0.0",
572 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
573 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
574 | "dev": true
575 | },
576 | "picomatch": {
577 | "version": "2.3.1",
578 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
579 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
580 | "dev": true
581 | },
582 | "pstree.remy": {
583 | "version": "1.1.8",
584 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
585 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
586 | "dev": true
587 | },
588 | "readdirp": {
589 | "version": "3.6.0",
590 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
591 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
592 | "dev": true,
593 | "requires": {
594 | "picomatch": "^2.2.1"
595 | }
596 | },
597 | "semver": {
598 | "version": "5.7.1",
599 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
600 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
601 | "dev": true
602 | },
603 | "simple-update-notifier": {
604 | "version": "1.0.7",
605 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
606 | "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
607 | "dev": true,
608 | "requires": {
609 | "semver": "~7.0.0"
610 | },
611 | "dependencies": {
612 | "semver": {
613 | "version": "7.0.0",
614 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
615 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
616 | "dev": true
617 | }
618 | }
619 | },
620 | "supports-color": {
621 | "version": "5.5.0",
622 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
623 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
624 | "dev": true,
625 | "requires": {
626 | "has-flag": "^3.0.0"
627 | }
628 | },
629 | "to-regex-range": {
630 | "version": "5.0.1",
631 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
632 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
633 | "dev": true,
634 | "requires": {
635 | "is-number": "^7.0.0"
636 | }
637 | },
638 | "touch": {
639 | "version": "3.1.0",
640 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
641 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
642 | "dev": true,
643 | "requires": {
644 | "nopt": "~1.0.10"
645 | }
646 | },
647 | "undefsafe": {
648 | "version": "2.0.5",
649 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
650 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
651 | "dev": true
652 | }
653 | }
654 | }
655 |
--------------------------------------------------------------------------------