├── .eslintrc
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── examples
└── sample
│ ├── index.ts
│ ├── package-lock.json
│ └── package.json
├── img
└── workflow.png
├── jest.config.js
├── package-lock.json
├── package.json
├── src
├── constants
│ └── index.ts
├── controllers
│ ├── execution.ts
│ ├── index.ts
│ ├── task.ts
│ └── workflow.ts
├── core.ts
├── crudable.ts
├── interpreter
│ └── index.ts
├── localCrud.ts
├── microflow.ts
├── storage
│ └── index.ts
├── types.ts
└── utils
│ ├── convert.ts
│ ├── index.ts
│ ├── task.ts
│ └── xstate.ts
├── tests
├── atomicNodeInitial.test.ts
├── taskMapSyncNode.test.ts
├── taskNodeInitial.test.ts
├── taskSyncNode.test.ts
├── taskToken.test.ts
└── utils
│ └── index.test.ts
└── tsconfig.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": ["@typescript-eslint"],
5 | "rules": {
6 | "quotes": [2, "single", { "avoidEscape": true }],
7 | "@typescript-eslint/no-explicit-any": 0
8 | },
9 | "extends": [
10 | "eslint:recommended",
11 | "plugin:@typescript-eslint/eslint-recommended",
12 | "plugin:@typescript-eslint/recommended"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | test
4 | .DS_Store
5 | .history
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": true,
7 | "trailingComma": "none",
8 | "bracketSpacing": true
9 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Karan Chhabra
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Welcome to microflow 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | > Finite state machine based HTTP microservice orchestration
18 |
19 | ### 🏠 [Homepage](https://github.com/krn0x2/microflow#readme)
20 |
21 | ## Purpose
22 |
23 | Microflow helps you build and run complex workflows which are composed of HTTP microservices and manual (human moderator) stages, all by definiing a JSON workflow blueprint. It is built on robust concepts of __finite state machine__, and allows you to control input/output data as template variables (think jsonpath, handlebars).
24 | A workflow consists of manual states and __task__ states (aka HTTP workers which could be sync/async).
25 |
26 |
27 |
28 | ## Install
29 | npm
30 | ```sh
31 | npm i --save microflow@alpha
32 | ```
33 |
34 | ## Documentation
35 |
36 | The `Microflow` class provides various methods to author/execute/infer workflow and workflow instances
37 |
38 | ```javascript
39 | import { Microflow } from "microflow";
40 |
41 | const flow = new Microflow({
42 | jwt: {
43 | secretOrPublicKey: 'dummySecretKey',
44 | sign: {
45 | expiresIn: '1h'
46 | }
47 | }
48 | });
49 |
50 | const {
51 | // task interface
52 | task,
53 | // workflow interface
54 | workflow,
55 | // execution interface
56 | execution,
57 | } = flow;
58 | ```
59 |
60 | ## Usage
61 |
62 | ```javascript
63 | import { Microflow } from "microflow";
64 |
65 | const flow = new Microflow({
66 | // bring your own persistence here
67 | // (implements MicroflowStorage)
68 | storage: undefined,
69 | jwt: {
70 | secretOrPublicKey: 'dummySecretKey',
71 | sign: {
72 | expiresIn: '1h'
73 | }
74 | }
75 | });
76 |
77 | // Authoring task and workflow
78 |
79 | // Register a task
80 | const task = await flow.task.create({
81 | // Recognisiable identified for the task
82 | id: 'airflow',
83 | // type of task (only 'http' supported right now)
84 | type: 'http',
85 | // supported (https://github.com/axios/axios/blob/master/index.d.ts#L44)
86 | config: {
87 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
88 | headers: {
89 | 'Cache-Control': 'no-cache',
90 | 'Content-Type': 'application/json'
91 | },
92 | data: {
93 | conf: {
94 | // $ is a reference to 'parameters' object of a task in the workflow
95 | actualData: '$.data',
96 | token: '$.token',
97 | envKey: '$.envKey',
98 | executionId: '$.executionId'
99 | }
100 | },
101 | method: 'post'
102 | }
103 | });
104 |
105 | const { id: taskId } = await task.data();
106 |
107 | // Create a workflow
108 | const workflow = await flow.workflow.create({
109 | id: 'sample',
110 | config: {
111 | initial: 'auto_test_1',
112 | states: {
113 | auto_test_1: {
114 | type: 'task',
115 | taskId,
116 | parameters: {
117 | //example of constant
118 | dagId: 'dag1',
119 | // $ = input event data to the task state
120 | data: '$',
121 | // $$ = Execution context object
122 | token: '$$.task.token',
123 | // $$$ = process.env aka environment variables
124 | envKey: '$$$.myKey1',
125 | executionId: '$$.executionId'
126 | },
127 | onDone: {
128 | target: 'ready_for_approval',
129 | resultSelector: {
130 | a: 'a',
131 | b: 'b',
132 | out: '$'
133 | },
134 | resultPath: '$.pipeline1.success'
135 | },
136 | onError: {
137 | target: 'failed',
138 | resultSelector: {
139 | c: 'c',
140 | d: 'd',
141 | out: '$'
142 | },
143 | resultPath: '$.pipeline1.error'
144 | }
145 | },
146 | ready_for_approval: {
147 | type: 'atomic',
148 | on: {
149 | reject: {
150 | target: 'failed',
151 | resultPath: '$.reject.data'
152 | },
153 | approve: {
154 | target: 'auto_test_2',
155 | resultPath: '$.approval.data'
156 | }
157 | }
158 | },
159 | auto_test_2: {
160 | type: 'task',
161 | taskId,
162 | parameters: {
163 | dagId: 'dag2',
164 | data: '$',
165 | token: '$$.task.token',
166 | envKey: '$$$.myKey1',
167 | executionId: '$$.executionId'
168 | },
169 | onDone: {
170 | target: 'done',
171 | resultSelector: {
172 | e: 'e',
173 | out: '$'
174 | },
175 | resultPath: '$.pipeline2.success'
176 | },
177 | onError: {
178 | target: 'failed',
179 | resultSelector: {
180 | f: 'f',
181 | out: '$'
182 | },
183 | resultPath: '$.pipeline2.error'
184 | }
185 | },
186 | done: {
187 | type: 'final'
188 | },
189 | failed: {
190 | type: 'final'
191 | }
192 | }
193 | }
194 | });
195 |
196 | // start an execution with initial data
197 | const execution = await workflow.start({
198 | input1: 'val1',
199 | input2: 'val2'
200 | });
201 |
202 | // Sending events to an execution
203 | await execution.send({
204 | type: 'success-auto_test_1',
205 | data: {
206 | test_a_result: true,
207 | test_b_result: false
208 | }
209 | });
210 |
211 | await execution.send({
212 | type: 'approve',
213 | data: {
214 | message: 'The acceptance test was fine'
215 | }
216 | });
217 |
218 | await execution.send({
219 | type: 'success-auto_test_2',
220 | data: {
221 | test_c_result: true
222 | }
223 | });
224 |
225 | const { completed, output, state } = await execution.data();
226 |
227 | console.log(output);
228 | /*
229 | {
230 | "input1": "val1",
231 | "input2": "val2",
232 | "pipeline1": {
233 | "success": {
234 | "a": "a",
235 | "b": "b",
236 | "out": {
237 | "test_a_result": true,
238 | "test_b_result": false
239 | }
240 | }
241 | },
242 | "approval": {
243 | "data": {
244 | "message": "The acceptance test was fine"
245 | }
246 | },
247 | "pipeline2": {
248 | "success": {
249 | "e": "e",
250 | "out": {
251 | "test_c_result": true
252 | }
253 | }
254 | }
255 | }
256 | */
257 |
258 | ```
259 | ## Storage
260 |
261 | ```javascript
262 | import { Microflow } from "microflow";
263 | import { MicroflowStorage } from "microflow/types";
264 |
265 | // define your own storage from MicroflowStorage abstract class
266 | class MyStorage implements IMicroflowStorage {
267 | workflow: ICrudable;
268 | task: ICrudable;
269 | execution: ICrudable;
270 | // define CRUD functions
271 | constructor(){
272 | this.workflow = {
273 | //define CRUD implementation here
274 | }
275 |
276 | this.task = {
277 | //define CRUD implementation here
278 | }
279 |
280 | this.execution = {
281 | //define CRUD implementation here
282 | }
283 | }
284 | }
285 |
286 |
287 | const store = new MyStorage();
288 |
289 | // create an instance of microflow with custom store injected
290 | const flow = new Microflow({
291 | storage: store,
292 | jwt: {
293 | secretOrPublicKey: 'dummySecretKey',
294 | sign: {
295 | expiresIn: '1h'
296 | }
297 | }
298 | });
299 | ```
300 |
301 | ## Examples
302 |
303 | Navigate to `examples/basic` to run a sample express project with ephemeral storage.
304 |
305 | ## Run tests
306 |
307 | ```sh
308 | npm run test
309 | ```
310 |
311 | ## Author
312 |
313 | 👤 **Karan Chhabra**
314 |
315 | * Github: [@krn0x2](https://github.com/krn0x2)
316 | * LinkedIn: [@krn0x2](https://linkedin.com/in/krn0x2)
317 |
318 | ## 🤝 Contributing
319 |
320 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/krn0x2/microflow/issues). You can also take a look at the [contributing guide](https://github.com/krn0x2/microflow/blob/master/CONTRIBUTING.md).
321 |
322 | ## Show your support
323 |
324 | Give a ⭐️ if this project helped you!
325 |
326 | ## 📝 License
327 |
328 | Copyright © 2020 [Karan Chhabra](https://github.com/krn0x2).
329 | This project is [MIT](https://github.com/krn0x2/microflow/blob/master/LICENSE) licensed.
330 |
--------------------------------------------------------------------------------
/examples/sample/index.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import bodyParser from 'body-parser';
3 | import { Microflow } from '../../src/microflow';
4 |
5 | const app = express();
6 | const port = 5000;
7 | const PATH = 'microflow';
8 |
9 | const microflowService = new Microflow({
10 | jwt: {
11 | secretOrPublicKey: 'shhhhh',
12 | sign: {
13 | expiresIn: '24h'
14 | }
15 | }
16 | });
17 |
18 | app.use(bodyParser.json());
19 |
20 | // Register task
21 | app.post(`/${PATH}/task`, async (req, res) => {
22 | const { body } = req;
23 | const task = await microflowService.task.create(body);
24 | const response = await task.data();
25 | return res.status(200).json(response);
26 | });
27 |
28 | // Query task
29 | app.get(`/${PATH}/task/:id`, async (req, res) => {
30 | const { params } = req;
31 | const { id } = params;
32 | const task = await microflowService.task.read(id);
33 | const response = await task.data();
34 | return res.status(200).json(response);
35 | });
36 |
37 | // Register workflow
38 | app.post(`/${PATH}/workflow`, async (req, res) => {
39 | const { body } = req;
40 | const workflow = await microflowService.workflow.create(body);
41 | const response = await workflow.data();
42 | return res.status(200).json(response);
43 | });
44 |
45 | // Query workflow
46 | app.get(`/${PATH}/workflow/:id`, async (req, res) => {
47 | const { params } = req;
48 | const { id } = params;
49 | const workflow = await microflowService.workflow.read(id);
50 | const response = await workflow.data();
51 | return res.status(200).json(response);
52 | });
53 |
54 | // Update workflow
55 | app.put(`/${PATH}/workflow`, async (req, res) => {
56 | const { body } = req;
57 | const { id } = body;
58 | const workflow = await microflowService.workflow.update(id, body);
59 | const response = await workflow.data();
60 | return res.status(200).json(response);
61 | });
62 |
63 | // Start workflow instance
64 | app.post(`/${PATH}/workflow/:id/start`, async (req, res) => {
65 | const { params, body } = req;
66 | const { id } = params;
67 | const workflow = await microflowService.workflow.read(id);
68 | const execution = await workflow.start(body);
69 | const response = await execution.data();
70 | return res.status(200).json(response);
71 | });
72 |
73 | // Send event to workflow instance
74 | app.post(`/${PATH}/workflow/instance/:id/event`, async (req, res) => {
75 | const { params, body } = req;
76 | const { id } = params;
77 | const execution = await microflowService.execution.read(id);
78 | await execution.send(body);
79 | const response = await execution.data();
80 | return res.status(200).json(response);
81 | });
82 |
83 | // Send event to workflow instance
84 | app.get(`/${PATH}/task-success`, async (req, res) => {
85 | const { body, query } = req;
86 | const { token } = query;
87 | console.log(token);
88 | const execution = await microflowService.sendTaskSuccess(
89 | token as string,
90 | body
91 | );
92 | const response = await execution.data();
93 | return res.status(200).json(response);
94 | });
95 |
96 | app.get(`/${PATH}/task-failure`, async (req, res) => {
97 | const { body, query } = req;
98 | const { token } = query;
99 | console.log(token);
100 | const execution = await microflowService.sendTaskFailure(
101 | token as string,
102 | body
103 | );
104 | const response = await execution.data();
105 | return res.status(200).json(response);
106 | });
107 |
108 | // Query workflow instance
109 | app.get(`/${PATH}/workflow/instance/:id`, async (req, res) => {
110 | const { id } = req.params;
111 | const execution = await microflowService.execution.read(id);
112 | const response = await execution.data();
113 | res.status(200).json(response);
114 | });
115 |
116 | // Healthcheck
117 | app.get(`/${PATH}`, (req, res) => {
118 | res.status(200).json({ message: 'Started' });
119 | });
120 |
121 | app.listen(port, () => {
122 | console.log(`endpoints listening at http://localhost:${port}`);
123 | });
124 |
--------------------------------------------------------------------------------
/examples/sample/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/body-parser": {
8 | "version": "1.19.0",
9 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
10 | "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
11 | "dev": true,
12 | "requires": {
13 | "@types/connect": "*",
14 | "@types/node": "*"
15 | }
16 | },
17 | "@types/connect": {
18 | "version": "3.4.34",
19 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz",
20 | "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==",
21 | "dev": true,
22 | "requires": {
23 | "@types/node": "*"
24 | }
25 | },
26 | "@types/express": {
27 | "version": "4.17.9",
28 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.9.tgz",
29 | "integrity": "sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==",
30 | "dev": true,
31 | "requires": {
32 | "@types/body-parser": "*",
33 | "@types/express-serve-static-core": "*",
34 | "@types/qs": "*",
35 | "@types/serve-static": "*"
36 | }
37 | },
38 | "@types/express-jwt": {
39 | "version": "0.0.42",
40 | "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz",
41 | "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==",
42 | "dev": true,
43 | "requires": {
44 | "@types/express": "*",
45 | "@types/express-unless": "*"
46 | }
47 | },
48 | "@types/express-serve-static-core": {
49 | "version": "4.17.14",
50 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.14.tgz",
51 | "integrity": "sha512-uFTLwu94TfUFMToXNgRZikwPuZdOtDgs3syBtAIr/OXorL1kJqUJT9qCLnRZ5KBOWfZQikQ2xKgR2tnDj1OgDA==",
52 | "dev": true,
53 | "requires": {
54 | "@types/node": "*",
55 | "@types/qs": "*",
56 | "@types/range-parser": "*"
57 | }
58 | },
59 | "@types/express-unless": {
60 | "version": "0.5.1",
61 | "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz",
62 | "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==",
63 | "dev": true,
64 | "requires": {
65 | "@types/express": "*"
66 | }
67 | },
68 | "@types/mime": {
69 | "version": "2.0.3",
70 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
71 | "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==",
72 | "dev": true
73 | },
74 | "@types/node": {
75 | "version": "14.14.11",
76 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.11.tgz",
77 | "integrity": "sha512-BJ97wAUuU3NUiUCp44xzUFquQEvnk1wu7q4CMEUYKJWjdkr0YWYDsm4RFtAvxYsNjLsKcrFt6RvK8r+mnzMbEQ=="
78 | },
79 | "@types/qs": {
80 | "version": "6.9.5",
81 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
82 | "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==",
83 | "dev": true
84 | },
85 | "@types/range-parser": {
86 | "version": "1.2.3",
87 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
88 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
89 | "dev": true
90 | },
91 | "@types/serve-static": {
92 | "version": "1.13.8",
93 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz",
94 | "integrity": "sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==",
95 | "dev": true,
96 | "requires": {
97 | "@types/mime": "*",
98 | "@types/node": "*"
99 | }
100 | },
101 | "abbrev": {
102 | "version": "1.1.1",
103 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
104 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
105 | },
106 | "accepts": {
107 | "version": "1.3.7",
108 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
109 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
110 | "requires": {
111 | "mime-types": "~2.1.24",
112 | "negotiator": "0.6.2"
113 | }
114 | },
115 | "ajv": {
116 | "version": "6.12.6",
117 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
118 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
119 | "optional": true,
120 | "requires": {
121 | "fast-deep-equal": "^3.1.1",
122 | "fast-json-stable-stringify": "^2.0.0",
123 | "json-schema-traverse": "^0.4.1",
124 | "uri-js": "^4.2.2"
125 | }
126 | },
127 | "ansi-regex": {
128 | "version": "2.1.1",
129 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
130 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
131 | },
132 | "any-promise": {
133 | "version": "1.3.0",
134 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
135 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
136 | },
137 | "aproba": {
138 | "version": "1.2.0",
139 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
140 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
141 | },
142 | "are-we-there-yet": {
143 | "version": "1.1.5",
144 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
145 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
146 | "requires": {
147 | "delegates": "^1.0.0",
148 | "readable-stream": "^2.0.6"
149 | }
150 | },
151 | "arg": {
152 | "version": "4.1.3",
153 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
154 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
155 | "dev": true
156 | },
157 | "array-flatten": {
158 | "version": "1.1.1",
159 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
160 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
161 | },
162 | "asn1": {
163 | "version": "0.2.4",
164 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
165 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
166 | "optional": true,
167 | "requires": {
168 | "safer-buffer": "~2.1.0"
169 | }
170 | },
171 | "assert-plus": {
172 | "version": "1.0.0",
173 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
174 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
175 | "optional": true
176 | },
177 | "async": {
178 | "version": "1.5.2",
179 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
180 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
181 | },
182 | "asynckit": {
183 | "version": "0.4.0",
184 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
185 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
186 | "optional": true
187 | },
188 | "aws-sign2": {
189 | "version": "0.7.0",
190 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
191 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
192 | "optional": true
193 | },
194 | "aws4": {
195 | "version": "1.11.0",
196 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
197 | "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
198 | "optional": true
199 | },
200 | "axios": {
201 | "version": "0.21.1",
202 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
203 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
204 | "requires": {
205 | "follow-redirects": "^1.10.0"
206 | }
207 | },
208 | "balanced-match": {
209 | "version": "1.0.0",
210 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
211 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
212 | },
213 | "bcrypt-pbkdf": {
214 | "version": "1.0.2",
215 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
216 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
217 | "optional": true,
218 | "requires": {
219 | "tweetnacl": "^0.14.3"
220 | }
221 | },
222 | "block-stream": {
223 | "version": "0.0.9",
224 | "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
225 | "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
226 | "optional": true,
227 | "requires": {
228 | "inherits": "~2.0.0"
229 | }
230 | },
231 | "bluebird": {
232 | "version": "3.7.2",
233 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
234 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
235 | },
236 | "body-parser": {
237 | "version": "1.19.0",
238 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
239 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
240 | "requires": {
241 | "bytes": "3.1.0",
242 | "content-type": "~1.0.4",
243 | "debug": "2.6.9",
244 | "depd": "~1.1.2",
245 | "http-errors": "1.7.2",
246 | "iconv-lite": "0.4.24",
247 | "on-finished": "~2.3.0",
248 | "qs": "6.7.0",
249 | "raw-body": "2.4.0",
250 | "type-is": "~1.6.17"
251 | },
252 | "dependencies": {
253 | "debug": {
254 | "version": "2.6.9",
255 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
256 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
257 | "requires": {
258 | "ms": "2.0.0"
259 | }
260 | },
261 | "ms": {
262 | "version": "2.0.0",
263 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
264 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
265 | },
266 | "qs": {
267 | "version": "6.7.0",
268 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
269 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
270 | }
271 | }
272 | },
273 | "brace-expansion": {
274 | "version": "1.1.11",
275 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
276 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
277 | "requires": {
278 | "balanced-match": "^1.0.0",
279 | "concat-map": "0.0.1"
280 | }
281 | },
282 | "buffer-equal-constant-time": {
283 | "version": "1.0.1",
284 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
285 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
286 | },
287 | "buffer-from": {
288 | "version": "1.1.1",
289 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
290 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
291 | "dev": true
292 | },
293 | "bytes": {
294 | "version": "3.1.0",
295 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
296 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
297 | },
298 | "caseless": {
299 | "version": "0.12.0",
300 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
301 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
302 | "optional": true
303 | },
304 | "chownr": {
305 | "version": "1.1.4",
306 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
307 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
308 | },
309 | "code-point-at": {
310 | "version": "1.1.0",
311 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
312 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
313 | },
314 | "combined-stream": {
315 | "version": "1.0.8",
316 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
317 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
318 | "optional": true,
319 | "requires": {
320 | "delayed-stream": "~1.0.0"
321 | }
322 | },
323 | "concat-map": {
324 | "version": "0.0.1",
325 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
326 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
327 | },
328 | "console-control-strings": {
329 | "version": "1.1.0",
330 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
331 | "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
332 | },
333 | "content-disposition": {
334 | "version": "0.5.3",
335 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
336 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
337 | "requires": {
338 | "safe-buffer": "5.1.2"
339 | }
340 | },
341 | "content-type": {
342 | "version": "1.0.4",
343 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
344 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
345 | },
346 | "cookie": {
347 | "version": "0.4.0",
348 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
349 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
350 | },
351 | "cookie-signature": {
352 | "version": "1.0.6",
353 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
354 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
355 | },
356 | "core-util-is": {
357 | "version": "1.0.2",
358 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
359 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
360 | },
361 | "create-require": {
362 | "version": "1.1.1",
363 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
364 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
365 | "dev": true
366 | },
367 | "dashdash": {
368 | "version": "1.14.1",
369 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
370 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
371 | "optional": true,
372 | "requires": {
373 | "assert-plus": "^1.0.0"
374 | }
375 | },
376 | "debug": {
377 | "version": "4.3.1",
378 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
379 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
380 | "requires": {
381 | "ms": "2.1.2"
382 | }
383 | },
384 | "deep-extend": {
385 | "version": "0.6.0",
386 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
387 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
388 | },
389 | "delayed-stream": {
390 | "version": "1.0.0",
391 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
392 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
393 | "optional": true
394 | },
395 | "delegates": {
396 | "version": "1.0.0",
397 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
398 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
399 | },
400 | "depd": {
401 | "version": "1.1.2",
402 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
403 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
404 | },
405 | "destroy": {
406 | "version": "1.0.4",
407 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
408 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
409 | },
410 | "detect-libc": {
411 | "version": "1.0.3",
412 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
413 | "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
414 | },
415 | "diff": {
416 | "version": "4.0.2",
417 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
418 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
419 | "dev": true
420 | },
421 | "dottie": {
422 | "version": "2.0.2",
423 | "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
424 | "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg=="
425 | },
426 | "ecc-jsbn": {
427 | "version": "0.1.2",
428 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
429 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
430 | "optional": true,
431 | "requires": {
432 | "jsbn": "~0.1.0",
433 | "safer-buffer": "^2.1.0"
434 | }
435 | },
436 | "ecdsa-sig-formatter": {
437 | "version": "1.0.11",
438 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
439 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
440 | "requires": {
441 | "safe-buffer": "^5.0.1"
442 | }
443 | },
444 | "ee-first": {
445 | "version": "1.1.1",
446 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
447 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
448 | },
449 | "encodeurl": {
450 | "version": "1.0.2",
451 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
452 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
453 | },
454 | "escape-html": {
455 | "version": "1.0.3",
456 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
457 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
458 | },
459 | "etag": {
460 | "version": "1.8.1",
461 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
462 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
463 | },
464 | "express": {
465 | "version": "4.17.1",
466 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
467 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
468 | "requires": {
469 | "accepts": "~1.3.7",
470 | "array-flatten": "1.1.1",
471 | "body-parser": "1.19.0",
472 | "content-disposition": "0.5.3",
473 | "content-type": "~1.0.4",
474 | "cookie": "0.4.0",
475 | "cookie-signature": "1.0.6",
476 | "debug": "2.6.9",
477 | "depd": "~1.1.2",
478 | "encodeurl": "~1.0.2",
479 | "escape-html": "~1.0.3",
480 | "etag": "~1.8.1",
481 | "finalhandler": "~1.1.2",
482 | "fresh": "0.5.2",
483 | "merge-descriptors": "1.0.1",
484 | "methods": "~1.1.2",
485 | "on-finished": "~2.3.0",
486 | "parseurl": "~1.3.3",
487 | "path-to-regexp": "0.1.7",
488 | "proxy-addr": "~2.0.5",
489 | "qs": "6.7.0",
490 | "range-parser": "~1.2.1",
491 | "safe-buffer": "5.1.2",
492 | "send": "0.17.1",
493 | "serve-static": "1.14.1",
494 | "setprototypeof": "1.1.1",
495 | "statuses": "~1.5.0",
496 | "type-is": "~1.6.18",
497 | "utils-merge": "1.0.1",
498 | "vary": "~1.1.2"
499 | },
500 | "dependencies": {
501 | "debug": {
502 | "version": "2.6.9",
503 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
504 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
505 | "requires": {
506 | "ms": "2.0.0"
507 | }
508 | },
509 | "ms": {
510 | "version": "2.0.0",
511 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
512 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
513 | },
514 | "qs": {
515 | "version": "6.7.0",
516 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
517 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
518 | }
519 | }
520 | },
521 | "express-jwt": {
522 | "version": "6.0.0",
523 | "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-6.0.0.tgz",
524 | "integrity": "sha512-C26y9myRjx7CyhZ+BAT3p+gQyRCoDZ7qo8plCvLDaRT6je6ALIAQknT6XLVQGFKwIy/Ux7lvM2MNap5dt0T7gA==",
525 | "requires": {
526 | "async": "^1.5.0",
527 | "express-unless": "^0.3.0",
528 | "jsonwebtoken": "^8.1.0",
529 | "lodash.set": "^4.0.0"
530 | }
531 | },
532 | "express-unless": {
533 | "version": "0.3.1",
534 | "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz",
535 | "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA="
536 | },
537 | "extend": {
538 | "version": "3.0.2",
539 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
540 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
541 | "optional": true
542 | },
543 | "extsprintf": {
544 | "version": "1.3.0",
545 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
546 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
547 | "optional": true
548 | },
549 | "fast-deep-equal": {
550 | "version": "3.1.3",
551 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
552 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
553 | "optional": true
554 | },
555 | "fast-json-stable-stringify": {
556 | "version": "2.1.0",
557 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
558 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
559 | "optional": true
560 | },
561 | "finalhandler": {
562 | "version": "1.1.2",
563 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
564 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
565 | "requires": {
566 | "debug": "2.6.9",
567 | "encodeurl": "~1.0.2",
568 | "escape-html": "~1.0.3",
569 | "on-finished": "~2.3.0",
570 | "parseurl": "~1.3.3",
571 | "statuses": "~1.5.0",
572 | "unpipe": "~1.0.0"
573 | },
574 | "dependencies": {
575 | "debug": {
576 | "version": "2.6.9",
577 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
578 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
579 | "requires": {
580 | "ms": "2.0.0"
581 | }
582 | },
583 | "ms": {
584 | "version": "2.0.0",
585 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
586 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
587 | }
588 | }
589 | },
590 | "follow-redirects": {
591 | "version": "1.13.1",
592 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
593 | "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
594 | },
595 | "forever-agent": {
596 | "version": "0.6.1",
597 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
598 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
599 | "optional": true
600 | },
601 | "form-data": {
602 | "version": "2.3.3",
603 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
604 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
605 | "optional": true,
606 | "requires": {
607 | "asynckit": "^0.4.0",
608 | "combined-stream": "^1.0.6",
609 | "mime-types": "^2.1.12"
610 | }
611 | },
612 | "forwarded": {
613 | "version": "0.1.2",
614 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
615 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
616 | },
617 | "fresh": {
618 | "version": "0.5.2",
619 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
620 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
621 | },
622 | "fs-minipass": {
623 | "version": "1.2.7",
624 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
625 | "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
626 | "requires": {
627 | "minipass": "^2.6.0"
628 | }
629 | },
630 | "fs.realpath": {
631 | "version": "1.0.0",
632 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
633 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
634 | },
635 | "fstream": {
636 | "version": "1.0.12",
637 | "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
638 | "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
639 | "optional": true,
640 | "requires": {
641 | "graceful-fs": "^4.1.2",
642 | "inherits": "~2.0.0",
643 | "mkdirp": ">=0.5 0",
644 | "rimraf": "2"
645 | }
646 | },
647 | "gauge": {
648 | "version": "2.7.4",
649 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
650 | "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
651 | "requires": {
652 | "aproba": "^1.0.3",
653 | "console-control-strings": "^1.0.0",
654 | "has-unicode": "^2.0.0",
655 | "object-assign": "^4.1.0",
656 | "signal-exit": "^3.0.0",
657 | "string-width": "^1.0.1",
658 | "strip-ansi": "^3.0.1",
659 | "wide-align": "^1.1.0"
660 | }
661 | },
662 | "getpass": {
663 | "version": "0.1.7",
664 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
665 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
666 | "optional": true,
667 | "requires": {
668 | "assert-plus": "^1.0.0"
669 | }
670 | },
671 | "glob": {
672 | "version": "7.1.6",
673 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
674 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
675 | "requires": {
676 | "fs.realpath": "^1.0.0",
677 | "inflight": "^1.0.4",
678 | "inherits": "2",
679 | "minimatch": "^3.0.4",
680 | "once": "^1.3.0",
681 | "path-is-absolute": "^1.0.0"
682 | }
683 | },
684 | "graceful-fs": {
685 | "version": "4.2.4",
686 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
687 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
688 | "optional": true
689 | },
690 | "handlebars": {
691 | "version": "4.7.6",
692 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
693 | "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
694 | "requires": {
695 | "minimist": "^1.2.5",
696 | "neo-async": "^2.6.0",
697 | "source-map": "^0.6.1",
698 | "uglify-js": "^3.1.4",
699 | "wordwrap": "^1.0.0"
700 | }
701 | },
702 | "har-schema": {
703 | "version": "2.0.0",
704 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
705 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
706 | "optional": true
707 | },
708 | "har-validator": {
709 | "version": "5.1.5",
710 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
711 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
712 | "optional": true,
713 | "requires": {
714 | "ajv": "^6.12.3",
715 | "har-schema": "^2.0.0"
716 | }
717 | },
718 | "has-unicode": {
719 | "version": "2.0.1",
720 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
721 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
722 | },
723 | "http-errors": {
724 | "version": "1.7.2",
725 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
726 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
727 | "requires": {
728 | "depd": "~1.1.2",
729 | "inherits": "2.0.3",
730 | "setprototypeof": "1.1.1",
731 | "statuses": ">= 1.5.0 < 2",
732 | "toidentifier": "1.0.0"
733 | },
734 | "dependencies": {
735 | "inherits": {
736 | "version": "2.0.3",
737 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
738 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
739 | }
740 | }
741 | },
742 | "http-signature": {
743 | "version": "1.2.0",
744 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
745 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
746 | "optional": true,
747 | "requires": {
748 | "assert-plus": "^1.0.0",
749 | "jsprim": "^1.2.2",
750 | "sshpk": "^1.7.0"
751 | }
752 | },
753 | "iconv-lite": {
754 | "version": "0.4.24",
755 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
756 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
757 | "requires": {
758 | "safer-buffer": ">= 2.1.2 < 3"
759 | }
760 | },
761 | "ignore-walk": {
762 | "version": "3.0.3",
763 | "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
764 | "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
765 | "requires": {
766 | "minimatch": "^3.0.4"
767 | }
768 | },
769 | "inflection": {
770 | "version": "1.12.0",
771 | "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
772 | "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
773 | },
774 | "inflight": {
775 | "version": "1.0.6",
776 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
777 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
778 | "requires": {
779 | "once": "^1.3.0",
780 | "wrappy": "1"
781 | }
782 | },
783 | "inherits": {
784 | "version": "2.0.4",
785 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
786 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
787 | },
788 | "ini": {
789 | "version": "1.3.5",
790 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
791 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
792 | },
793 | "ipaddr.js": {
794 | "version": "1.9.1",
795 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
796 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
797 | },
798 | "is-fullwidth-code-point": {
799 | "version": "1.0.0",
800 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
801 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
802 | "requires": {
803 | "number-is-nan": "^1.0.0"
804 | }
805 | },
806 | "is-typedarray": {
807 | "version": "1.0.0",
808 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
809 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
810 | "optional": true
811 | },
812 | "isarray": {
813 | "version": "1.0.0",
814 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
815 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
816 | },
817 | "isexe": {
818 | "version": "2.0.0",
819 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
820 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
821 | "optional": true
822 | },
823 | "isstream": {
824 | "version": "0.1.2",
825 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
826 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
827 | "optional": true
828 | },
829 | "jsbn": {
830 | "version": "0.1.1",
831 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
832 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
833 | "optional": true
834 | },
835 | "json-schema": {
836 | "version": "0.2.3",
837 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
838 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
839 | "optional": true
840 | },
841 | "json-schema-traverse": {
842 | "version": "0.4.1",
843 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
844 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
845 | "optional": true
846 | },
847 | "json-stringify-safe": {
848 | "version": "5.0.1",
849 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
850 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
851 | "optional": true
852 | },
853 | "jsonpath-plus": {
854 | "version": "4.0.0",
855 | "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-4.0.0.tgz",
856 | "integrity": "sha512-e0Jtg4KAzDJKKwzbLaUtinCn0RZseWBVRTRGihSpvFlM3wTR7ExSp+PTdeTsDrLNJUe7L7JYJe8mblHX5SCT6A=="
857 | },
858 | "jsonwebtoken": {
859 | "version": "8.5.1",
860 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
861 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
862 | "requires": {
863 | "jws": "^3.2.2",
864 | "lodash.includes": "^4.3.0",
865 | "lodash.isboolean": "^3.0.3",
866 | "lodash.isinteger": "^4.0.4",
867 | "lodash.isnumber": "^3.0.3",
868 | "lodash.isplainobject": "^4.0.6",
869 | "lodash.isstring": "^4.0.1",
870 | "lodash.once": "^4.0.0",
871 | "ms": "^2.1.1",
872 | "semver": "^5.6.0"
873 | },
874 | "dependencies": {
875 | "semver": {
876 | "version": "5.7.1",
877 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
878 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
879 | }
880 | }
881 | },
882 | "jsprim": {
883 | "version": "1.4.1",
884 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
885 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
886 | "optional": true,
887 | "requires": {
888 | "assert-plus": "1.0.0",
889 | "extsprintf": "1.3.0",
890 | "json-schema": "0.2.3",
891 | "verror": "1.10.0"
892 | }
893 | },
894 | "jwa": {
895 | "version": "1.4.1",
896 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
897 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
898 | "requires": {
899 | "buffer-equal-constant-time": "1.0.1",
900 | "ecdsa-sig-formatter": "1.0.11",
901 | "safe-buffer": "^5.0.1"
902 | }
903 | },
904 | "jws": {
905 | "version": "3.2.2",
906 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
907 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
908 | "requires": {
909 | "jwa": "^1.4.1",
910 | "safe-buffer": "^5.0.1"
911 | }
912 | },
913 | "lodash": {
914 | "version": "4.17.20",
915 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
916 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
917 | },
918 | "lodash.includes": {
919 | "version": "4.3.0",
920 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
921 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
922 | },
923 | "lodash.isboolean": {
924 | "version": "3.0.3",
925 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
926 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
927 | },
928 | "lodash.isinteger": {
929 | "version": "4.0.4",
930 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
931 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
932 | },
933 | "lodash.isnumber": {
934 | "version": "3.0.3",
935 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
936 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
937 | },
938 | "lodash.isplainobject": {
939 | "version": "4.0.6",
940 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
941 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
942 | },
943 | "lodash.isstring": {
944 | "version": "4.0.1",
945 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
946 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
947 | },
948 | "lodash.once": {
949 | "version": "4.1.1",
950 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
951 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
952 | },
953 | "lodash.set": {
954 | "version": "4.3.2",
955 | "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
956 | "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
957 | },
958 | "lru-cache": {
959 | "version": "6.0.0",
960 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
961 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
962 | "requires": {
963 | "yallist": "^4.0.0"
964 | }
965 | },
966 | "make-error": {
967 | "version": "1.3.6",
968 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
969 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
970 | "dev": true
971 | },
972 | "media-typer": {
973 | "version": "0.3.0",
974 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
975 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
976 | },
977 | "merge-descriptors": {
978 | "version": "1.0.1",
979 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
980 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
981 | },
982 | "methods": {
983 | "version": "1.1.2",
984 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
985 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
986 | },
987 | "microflow": {
988 | "version": "3.0.0-alpha.2",
989 | "resolved": "https://registry.npmjs.org/microflow/-/microflow-3.0.0-alpha.2.tgz",
990 | "integrity": "sha512-tA639DyBuiaOmteZpNtPLPp3HMOZaTWPqJ0KKNdLAwnJI0+dtxbNc10FaP8u4UuogrS+VeXsagOv44HZt0CHlA==",
991 | "requires": {
992 | "axios": "^0.21.0",
993 | "bluebird": "^3.7.2",
994 | "handlebars": "^4.7.6",
995 | "jsonpath-plus": "^4.0.0",
996 | "jsonwebtoken": "^8.5.1",
997 | "lodash": "^4.17.20",
998 | "nanoid": "^3.1.20",
999 | "xstate": "^4.14.0"
1000 | }
1001 | },
1002 | "mime": {
1003 | "version": "1.6.0",
1004 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1005 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
1006 | },
1007 | "mime-db": {
1008 | "version": "1.44.0",
1009 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
1010 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
1011 | },
1012 | "mime-types": {
1013 | "version": "2.1.27",
1014 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
1015 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
1016 | "requires": {
1017 | "mime-db": "1.44.0"
1018 | }
1019 | },
1020 | "minimatch": {
1021 | "version": "3.0.4",
1022 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1023 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1024 | "requires": {
1025 | "brace-expansion": "^1.1.7"
1026 | }
1027 | },
1028 | "minimist": {
1029 | "version": "1.2.5",
1030 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
1031 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
1032 | },
1033 | "minipass": {
1034 | "version": "2.9.0",
1035 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
1036 | "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
1037 | "requires": {
1038 | "safe-buffer": "^5.1.2",
1039 | "yallist": "^3.0.0"
1040 | },
1041 | "dependencies": {
1042 | "yallist": {
1043 | "version": "3.1.1",
1044 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1045 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
1046 | }
1047 | }
1048 | },
1049 | "minizlib": {
1050 | "version": "1.3.3",
1051 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
1052 | "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
1053 | "requires": {
1054 | "minipass": "^2.9.0"
1055 | }
1056 | },
1057 | "mkdirp": {
1058 | "version": "0.5.5",
1059 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
1060 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
1061 | "requires": {
1062 | "minimist": "^1.2.5"
1063 | }
1064 | },
1065 | "moment": {
1066 | "version": "2.29.1",
1067 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
1068 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
1069 | },
1070 | "moment-timezone": {
1071 | "version": "0.5.32",
1072 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz",
1073 | "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==",
1074 | "requires": {
1075 | "moment": ">= 2.9.0"
1076 | }
1077 | },
1078 | "ms": {
1079 | "version": "2.1.2",
1080 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1081 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1082 | },
1083 | "nanoid": {
1084 | "version": "3.1.20",
1085 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
1086 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw=="
1087 | },
1088 | "needle": {
1089 | "version": "2.5.2",
1090 | "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
1091 | "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
1092 | "requires": {
1093 | "debug": "^3.2.6",
1094 | "iconv-lite": "^0.4.4",
1095 | "sax": "^1.2.4"
1096 | },
1097 | "dependencies": {
1098 | "debug": {
1099 | "version": "3.2.7",
1100 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
1101 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
1102 | "requires": {
1103 | "ms": "^2.1.1"
1104 | }
1105 | }
1106 | }
1107 | },
1108 | "negotiator": {
1109 | "version": "0.6.2",
1110 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
1111 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
1112 | },
1113 | "neo-async": {
1114 | "version": "2.6.2",
1115 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
1116 | "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
1117 | },
1118 | "node-addon-api": {
1119 | "version": "2.0.0",
1120 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
1121 | "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
1122 | },
1123 | "node-gyp": {
1124 | "version": "3.8.0",
1125 | "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
1126 | "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
1127 | "optional": true,
1128 | "requires": {
1129 | "fstream": "^1.0.0",
1130 | "glob": "^7.0.3",
1131 | "graceful-fs": "^4.1.2",
1132 | "mkdirp": "^0.5.0",
1133 | "nopt": "2 || 3",
1134 | "npmlog": "0 || 1 || 2 || 3 || 4",
1135 | "osenv": "0",
1136 | "request": "^2.87.0",
1137 | "rimraf": "2",
1138 | "semver": "~5.3.0",
1139 | "tar": "^2.0.0",
1140 | "which": "1"
1141 | },
1142 | "dependencies": {
1143 | "semver": {
1144 | "version": "5.3.0",
1145 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
1146 | "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
1147 | "optional": true
1148 | }
1149 | }
1150 | },
1151 | "node-pre-gyp": {
1152 | "version": "0.11.0",
1153 | "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
1154 | "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
1155 | "requires": {
1156 | "detect-libc": "^1.0.2",
1157 | "mkdirp": "^0.5.1",
1158 | "needle": "^2.2.1",
1159 | "nopt": "^4.0.1",
1160 | "npm-packlist": "^1.1.6",
1161 | "npmlog": "^4.0.2",
1162 | "rc": "^1.2.7",
1163 | "rimraf": "^2.6.1",
1164 | "semver": "^5.3.0",
1165 | "tar": "^4"
1166 | },
1167 | "dependencies": {
1168 | "nopt": {
1169 | "version": "4.0.3",
1170 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
1171 | "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
1172 | "requires": {
1173 | "abbrev": "1",
1174 | "osenv": "^0.1.4"
1175 | }
1176 | },
1177 | "semver": {
1178 | "version": "5.7.1",
1179 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1180 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
1181 | },
1182 | "tar": {
1183 | "version": "4.4.13",
1184 | "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
1185 | "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
1186 | "requires": {
1187 | "chownr": "^1.1.1",
1188 | "fs-minipass": "^1.2.5",
1189 | "minipass": "^2.8.6",
1190 | "minizlib": "^1.2.1",
1191 | "mkdirp": "^0.5.0",
1192 | "safe-buffer": "^5.1.2",
1193 | "yallist": "^3.0.3"
1194 | }
1195 | },
1196 | "yallist": {
1197 | "version": "3.1.1",
1198 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1199 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
1200 | }
1201 | }
1202 | },
1203 | "nopt": {
1204 | "version": "3.0.6",
1205 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
1206 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
1207 | "optional": true,
1208 | "requires": {
1209 | "abbrev": "1"
1210 | }
1211 | },
1212 | "npm-bundled": {
1213 | "version": "1.1.1",
1214 | "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
1215 | "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
1216 | "requires": {
1217 | "npm-normalize-package-bin": "^1.0.1"
1218 | }
1219 | },
1220 | "npm-normalize-package-bin": {
1221 | "version": "1.0.1",
1222 | "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
1223 | "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
1224 | },
1225 | "npm-packlist": {
1226 | "version": "1.4.8",
1227 | "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
1228 | "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
1229 | "requires": {
1230 | "ignore-walk": "^3.0.1",
1231 | "npm-bundled": "^1.0.1",
1232 | "npm-normalize-package-bin": "^1.0.1"
1233 | }
1234 | },
1235 | "npmlog": {
1236 | "version": "4.1.2",
1237 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
1238 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
1239 | "requires": {
1240 | "are-we-there-yet": "~1.1.2",
1241 | "console-control-strings": "~1.1.0",
1242 | "gauge": "~2.7.3",
1243 | "set-blocking": "~2.0.0"
1244 | }
1245 | },
1246 | "number-is-nan": {
1247 | "version": "1.0.1",
1248 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
1249 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
1250 | },
1251 | "oauth-sign": {
1252 | "version": "0.9.0",
1253 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
1254 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
1255 | "optional": true
1256 | },
1257 | "object-assign": {
1258 | "version": "4.1.1",
1259 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1260 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
1261 | },
1262 | "on-finished": {
1263 | "version": "2.3.0",
1264 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1265 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
1266 | "requires": {
1267 | "ee-first": "1.1.1"
1268 | }
1269 | },
1270 | "once": {
1271 | "version": "1.4.0",
1272 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1273 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1274 | "requires": {
1275 | "wrappy": "1"
1276 | }
1277 | },
1278 | "os-homedir": {
1279 | "version": "1.0.2",
1280 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
1281 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
1282 | },
1283 | "os-tmpdir": {
1284 | "version": "1.0.2",
1285 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
1286 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
1287 | },
1288 | "osenv": {
1289 | "version": "0.1.5",
1290 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
1291 | "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
1292 | "requires": {
1293 | "os-homedir": "^1.0.0",
1294 | "os-tmpdir": "^1.0.0"
1295 | }
1296 | },
1297 | "parseurl": {
1298 | "version": "1.3.3",
1299 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1300 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
1301 | },
1302 | "path-is-absolute": {
1303 | "version": "1.0.1",
1304 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1305 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
1306 | },
1307 | "path-to-regexp": {
1308 | "version": "0.1.7",
1309 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1310 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
1311 | },
1312 | "performance-now": {
1313 | "version": "2.1.0",
1314 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
1315 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
1316 | "optional": true
1317 | },
1318 | "process-nextick-args": {
1319 | "version": "2.0.1",
1320 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
1321 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
1322 | },
1323 | "proxy-addr": {
1324 | "version": "2.0.6",
1325 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
1326 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
1327 | "requires": {
1328 | "forwarded": "~0.1.2",
1329 | "ipaddr.js": "1.9.1"
1330 | }
1331 | },
1332 | "psl": {
1333 | "version": "1.8.0",
1334 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
1335 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
1336 | "optional": true
1337 | },
1338 | "punycode": {
1339 | "version": "2.1.1",
1340 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
1341 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
1342 | "optional": true
1343 | },
1344 | "qs": {
1345 | "version": "6.5.2",
1346 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
1347 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
1348 | "optional": true
1349 | },
1350 | "range-parser": {
1351 | "version": "1.2.1",
1352 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1353 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
1354 | },
1355 | "raw-body": {
1356 | "version": "2.4.0",
1357 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
1358 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
1359 | "requires": {
1360 | "bytes": "3.1.0",
1361 | "http-errors": "1.7.2",
1362 | "iconv-lite": "0.4.24",
1363 | "unpipe": "1.0.0"
1364 | }
1365 | },
1366 | "rc": {
1367 | "version": "1.2.8",
1368 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
1369 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
1370 | "requires": {
1371 | "deep-extend": "^0.6.0",
1372 | "ini": "~1.3.0",
1373 | "minimist": "^1.2.0",
1374 | "strip-json-comments": "~2.0.1"
1375 | }
1376 | },
1377 | "readable-stream": {
1378 | "version": "2.3.7",
1379 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
1380 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
1381 | "requires": {
1382 | "core-util-is": "~1.0.0",
1383 | "inherits": "~2.0.3",
1384 | "isarray": "~1.0.0",
1385 | "process-nextick-args": "~2.0.0",
1386 | "safe-buffer": "~5.1.1",
1387 | "string_decoder": "~1.1.1",
1388 | "util-deprecate": "~1.0.1"
1389 | }
1390 | },
1391 | "request": {
1392 | "version": "2.88.2",
1393 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
1394 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
1395 | "optional": true,
1396 | "requires": {
1397 | "aws-sign2": "~0.7.0",
1398 | "aws4": "^1.8.0",
1399 | "caseless": "~0.12.0",
1400 | "combined-stream": "~1.0.6",
1401 | "extend": "~3.0.2",
1402 | "forever-agent": "~0.6.1",
1403 | "form-data": "~2.3.2",
1404 | "har-validator": "~5.1.3",
1405 | "http-signature": "~1.2.0",
1406 | "is-typedarray": "~1.0.0",
1407 | "isstream": "~0.1.2",
1408 | "json-stringify-safe": "~5.0.1",
1409 | "mime-types": "~2.1.19",
1410 | "oauth-sign": "~0.9.0",
1411 | "performance-now": "^2.1.0",
1412 | "qs": "~6.5.2",
1413 | "safe-buffer": "^5.1.2",
1414 | "tough-cookie": "~2.5.0",
1415 | "tunnel-agent": "^0.6.0",
1416 | "uuid": "^3.3.2"
1417 | },
1418 | "dependencies": {
1419 | "uuid": {
1420 | "version": "3.4.0",
1421 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
1422 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
1423 | "optional": true
1424 | }
1425 | }
1426 | },
1427 | "retry-as-promised": {
1428 | "version": "3.2.0",
1429 | "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz",
1430 | "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==",
1431 | "requires": {
1432 | "any-promise": "^1.3.0"
1433 | }
1434 | },
1435 | "rimraf": {
1436 | "version": "2.7.1",
1437 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
1438 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
1439 | "requires": {
1440 | "glob": "^7.1.3"
1441 | }
1442 | },
1443 | "safe-buffer": {
1444 | "version": "5.1.2",
1445 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1446 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
1447 | },
1448 | "safer-buffer": {
1449 | "version": "2.1.2",
1450 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1451 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1452 | },
1453 | "sax": {
1454 | "version": "1.2.4",
1455 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
1456 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
1457 | },
1458 | "semver": {
1459 | "version": "7.3.4",
1460 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
1461 | "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
1462 | "requires": {
1463 | "lru-cache": "^6.0.0"
1464 | }
1465 | },
1466 | "send": {
1467 | "version": "0.17.1",
1468 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
1469 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
1470 | "requires": {
1471 | "debug": "2.6.9",
1472 | "depd": "~1.1.2",
1473 | "destroy": "~1.0.4",
1474 | "encodeurl": "~1.0.2",
1475 | "escape-html": "~1.0.3",
1476 | "etag": "~1.8.1",
1477 | "fresh": "0.5.2",
1478 | "http-errors": "~1.7.2",
1479 | "mime": "1.6.0",
1480 | "ms": "2.1.1",
1481 | "on-finished": "~2.3.0",
1482 | "range-parser": "~1.2.1",
1483 | "statuses": "~1.5.0"
1484 | },
1485 | "dependencies": {
1486 | "debug": {
1487 | "version": "2.6.9",
1488 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1489 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1490 | "requires": {
1491 | "ms": "2.0.0"
1492 | },
1493 | "dependencies": {
1494 | "ms": {
1495 | "version": "2.0.0",
1496 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1497 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1498 | }
1499 | }
1500 | },
1501 | "ms": {
1502 | "version": "2.1.1",
1503 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
1504 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
1505 | }
1506 | }
1507 | },
1508 | "sequelize": {
1509 | "version": "6.3.5",
1510 | "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.3.5.tgz",
1511 | "integrity": "sha512-MiwiPkYSA8NWttRKAXdU9h0TxP6HAc1fl7qZmMO/VQqQOND83G4nZLXd0kWILtAoT9cxtZgFqeb/MPYgEeXwsw==",
1512 | "requires": {
1513 | "debug": "^4.1.1",
1514 | "dottie": "^2.0.0",
1515 | "inflection": "1.12.0",
1516 | "lodash": "^4.17.15",
1517 | "moment": "^2.26.0",
1518 | "moment-timezone": "^0.5.31",
1519 | "retry-as-promised": "^3.2.0",
1520 | "semver": "^7.3.2",
1521 | "sequelize-pool": "^6.0.0",
1522 | "toposort-class": "^1.0.1",
1523 | "uuid": "^8.1.0",
1524 | "validator": "^10.11.0",
1525 | "wkx": "^0.5.0"
1526 | }
1527 | },
1528 | "sequelize-pool": {
1529 | "version": "6.1.0",
1530 | "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz",
1531 | "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg=="
1532 | },
1533 | "serve-static": {
1534 | "version": "1.14.1",
1535 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
1536 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
1537 | "requires": {
1538 | "encodeurl": "~1.0.2",
1539 | "escape-html": "~1.0.3",
1540 | "parseurl": "~1.3.3",
1541 | "send": "0.17.1"
1542 | }
1543 | },
1544 | "set-blocking": {
1545 | "version": "2.0.0",
1546 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
1547 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
1548 | },
1549 | "setprototypeof": {
1550 | "version": "1.1.1",
1551 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
1552 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
1553 | },
1554 | "signal-exit": {
1555 | "version": "3.0.3",
1556 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
1557 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
1558 | },
1559 | "source-map": {
1560 | "version": "0.6.1",
1561 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
1562 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
1563 | },
1564 | "source-map-support": {
1565 | "version": "0.5.19",
1566 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
1567 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
1568 | "dev": true,
1569 | "requires": {
1570 | "buffer-from": "^1.0.0",
1571 | "source-map": "^0.6.0"
1572 | }
1573 | },
1574 | "sqlite3": {
1575 | "version": "5.0.0",
1576 | "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.0.tgz",
1577 | "integrity": "sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw==",
1578 | "requires": {
1579 | "node-addon-api": "2.0.0",
1580 | "node-gyp": "3.x",
1581 | "node-pre-gyp": "^0.11.0"
1582 | }
1583 | },
1584 | "sshpk": {
1585 | "version": "1.16.1",
1586 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
1587 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
1588 | "optional": true,
1589 | "requires": {
1590 | "asn1": "~0.2.3",
1591 | "assert-plus": "^1.0.0",
1592 | "bcrypt-pbkdf": "^1.0.0",
1593 | "dashdash": "^1.12.0",
1594 | "ecc-jsbn": "~0.1.1",
1595 | "getpass": "^0.1.1",
1596 | "jsbn": "~0.1.0",
1597 | "safer-buffer": "^2.0.2",
1598 | "tweetnacl": "~0.14.0"
1599 | }
1600 | },
1601 | "statuses": {
1602 | "version": "1.5.0",
1603 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1604 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
1605 | },
1606 | "string-width": {
1607 | "version": "1.0.2",
1608 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
1609 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
1610 | "requires": {
1611 | "code-point-at": "^1.0.0",
1612 | "is-fullwidth-code-point": "^1.0.0",
1613 | "strip-ansi": "^3.0.0"
1614 | }
1615 | },
1616 | "string_decoder": {
1617 | "version": "1.1.1",
1618 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
1619 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
1620 | "requires": {
1621 | "safe-buffer": "~5.1.0"
1622 | }
1623 | },
1624 | "strip-ansi": {
1625 | "version": "3.0.1",
1626 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1627 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1628 | "requires": {
1629 | "ansi-regex": "^2.0.0"
1630 | }
1631 | },
1632 | "strip-json-comments": {
1633 | "version": "2.0.1",
1634 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
1635 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
1636 | },
1637 | "tar": {
1638 | "version": "2.2.2",
1639 | "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
1640 | "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
1641 | "optional": true,
1642 | "requires": {
1643 | "block-stream": "*",
1644 | "fstream": "^1.0.12",
1645 | "inherits": "2"
1646 | }
1647 | },
1648 | "toidentifier": {
1649 | "version": "1.0.0",
1650 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
1651 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
1652 | },
1653 | "toposort-class": {
1654 | "version": "1.0.1",
1655 | "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz",
1656 | "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
1657 | },
1658 | "tough-cookie": {
1659 | "version": "2.5.0",
1660 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
1661 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
1662 | "optional": true,
1663 | "requires": {
1664 | "psl": "^1.1.28",
1665 | "punycode": "^2.1.1"
1666 | }
1667 | },
1668 | "ts-node": {
1669 | "version": "9.1.1",
1670 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
1671 | "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
1672 | "dev": true,
1673 | "requires": {
1674 | "arg": "^4.1.0",
1675 | "create-require": "^1.1.0",
1676 | "diff": "^4.0.1",
1677 | "make-error": "^1.1.1",
1678 | "source-map-support": "^0.5.17",
1679 | "yn": "3.1.1"
1680 | }
1681 | },
1682 | "tunnel-agent": {
1683 | "version": "0.6.0",
1684 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
1685 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
1686 | "optional": true,
1687 | "requires": {
1688 | "safe-buffer": "^5.0.1"
1689 | }
1690 | },
1691 | "tweetnacl": {
1692 | "version": "0.14.5",
1693 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
1694 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
1695 | "optional": true
1696 | },
1697 | "type-is": {
1698 | "version": "1.6.18",
1699 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1700 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1701 | "requires": {
1702 | "media-typer": "0.3.0",
1703 | "mime-types": "~2.1.24"
1704 | }
1705 | },
1706 | "uglify-js": {
1707 | "version": "3.12.4",
1708 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.4.tgz",
1709 | "integrity": "sha512-L5i5jg/SHkEqzN18gQMTWsZk3KelRsfD1wUVNqtq0kzqWQqcJjyL8yc1o8hJgRrWqrAl2mUFbhfznEIoi7zi2A==",
1710 | "optional": true
1711 | },
1712 | "unpipe": {
1713 | "version": "1.0.0",
1714 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1715 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
1716 | },
1717 | "uri-js": {
1718 | "version": "4.4.0",
1719 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
1720 | "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
1721 | "optional": true,
1722 | "requires": {
1723 | "punycode": "^2.1.0"
1724 | }
1725 | },
1726 | "util-deprecate": {
1727 | "version": "1.0.2",
1728 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1729 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
1730 | },
1731 | "utils-merge": {
1732 | "version": "1.0.1",
1733 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1734 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
1735 | },
1736 | "uuid": {
1737 | "version": "8.3.1",
1738 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
1739 | "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
1740 | },
1741 | "validator": {
1742 | "version": "10.11.0",
1743 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
1744 | "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw=="
1745 | },
1746 | "vary": {
1747 | "version": "1.1.2",
1748 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1749 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
1750 | },
1751 | "verror": {
1752 | "version": "1.10.0",
1753 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
1754 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
1755 | "optional": true,
1756 | "requires": {
1757 | "assert-plus": "^1.0.0",
1758 | "core-util-is": "1.0.2",
1759 | "extsprintf": "^1.2.0"
1760 | }
1761 | },
1762 | "which": {
1763 | "version": "1.3.1",
1764 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1765 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1766 | "optional": true,
1767 | "requires": {
1768 | "isexe": "^2.0.0"
1769 | }
1770 | },
1771 | "wide-align": {
1772 | "version": "1.1.3",
1773 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
1774 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
1775 | "requires": {
1776 | "string-width": "^1.0.2 || 2"
1777 | }
1778 | },
1779 | "wkx": {
1780 | "version": "0.5.0",
1781 | "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz",
1782 | "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==",
1783 | "requires": {
1784 | "@types/node": "*"
1785 | }
1786 | },
1787 | "wordwrap": {
1788 | "version": "1.0.0",
1789 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
1790 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
1791 | },
1792 | "wrappy": {
1793 | "version": "1.0.2",
1794 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1795 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
1796 | },
1797 | "xstate": {
1798 | "version": "4.15.3",
1799 | "resolved": "https://registry.npmjs.org/xstate/-/xstate-4.15.3.tgz",
1800 | "integrity": "sha512-nf4zzLNs5W57stMZib9UG9PA5ywu89INsaXBMZf7iQxkYD9apbIOQcK8nu/iVZEDOVE+vR8GQnTaOg/8iDSK5Q=="
1801 | },
1802 | "yallist": {
1803 | "version": "4.0.0",
1804 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1805 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
1806 | },
1807 | "yn": {
1808 | "version": "3.1.1",
1809 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
1810 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
1811 | "dev": true
1812 | }
1813 | }
1814 | }
1815 |
--------------------------------------------------------------------------------
/examples/sample/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "dependencies": {
7 | "body-parser": "^1.19.0",
8 | "express": "^4.17.1",
9 | "express-jwt": "^6.0.0",
10 | "microflow": "^3.0.0-alpha.2",
11 | "sequelize": "^6.3.5",
12 | "sqlite3": "^5.0.0"
13 | },
14 | "devDependencies": {
15 | "@types/body-parser": "^1.19.0",
16 | "@types/express": "^4.17.9",
17 | "@types/express-jwt": "0.0.42",
18 | "@types/node": "^14.14.11",
19 | "ts-node": "^9.1.1"
20 | },
21 | "scripts": {
22 | "test": "echo \"Error: no test specified\" && exit 1"
23 | },
24 | "author": "",
25 | "license": "ISC"
26 | }
27 |
--------------------------------------------------------------------------------
/img/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krn0x2/microflow/769d642ae86755b5d6cb564a81219fe4c57997ec/img/workflow.png
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'node',
4 | testTimeout: 20000
5 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "microflow",
3 | "version": "3.0.0-alpha.8",
4 | "description": "Finite state machine based HTTP microservice orchestration",
5 | "keywords": [
6 | "workflow",
7 | "state machine",
8 | "orchestration",
9 | "finite state machine",
10 | "step function"
11 | ],
12 | "files": [
13 | "lib/**/*.js",
14 | "lib/**/*.d.ts"
15 | ],
16 | "main": "lib/microflow.js",
17 | "dependencies": {
18 | "axios": "^0.21.0",
19 | "bluebird": "^3.7.2",
20 | "handlebars": "^4.7.6",
21 | "jsonpath-plus": "^4.0.0",
22 | "jsonwebtoken": "^8.5.1",
23 | "lodash": "^4.17.20",
24 | "nanoid": "^3.1.20",
25 | "xstate": "^4.14.0"
26 | },
27 | "devDependencies": {
28 | "@types/bluebird": "^3.5.33",
29 | "@types/jest": "^26.0.19",
30 | "@types/jsonwebtoken": "^8.5.0",
31 | "@types/lodash": "^4.14.165",
32 | "@typescript-eslint/eslint-plugin": "^4.8.0",
33 | "@typescript-eslint/parser": "^4.8.0",
34 | "eslint": "^7.13.0",
35 | "jest": "^26.6.3",
36 | "ts-jest": "^26.4.4",
37 | "typescript": "^4.0.5"
38 | },
39 | "scripts": {
40 | "build": "tsc",
41 | "test": "jest"
42 | },
43 | "repository": {
44 | "type": "git",
45 | "url": "git+https://github.com/krn0x2/microflow.git"
46 | },
47 | "author": {
48 | "name": "Karan Chhabra"
49 | },
50 | "license": "MIT",
51 | "bugs": {
52 | "url": "https://github.com/krn0x2/microflow/issues"
53 | },
54 | "homepage": "https://github.com/krn0x2/microflow#readme"
55 | }
56 |
--------------------------------------------------------------------------------
/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export const MICROFLOW = {
2 | CONCURRENCY: 20,
3 | STATES: {
4 | TASK: 'task',
5 | TASK_SYNC: 'taskSync',
6 | TASK_MAP_SYNC: 'taskMapSync',
7 | ATOMIC: 'atomic',
8 | FINAL: 'final'
9 | },
10 | ERRORS: {
11 | WORKFLOW: {
12 | FAILED_TO_FETCH: 'FAILED_TO_FETCH'
13 | }
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/controllers/execution.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import { Promise as BluebirdPromise } from 'bluebird';
3 | import { State } from 'xstate';
4 | import { WorkflowInterpreter } from '../interpreter';
5 | import { IExecution, WorkflowEvent } from '../types';
6 | import { getMachine } from '../utils/xstate';
7 | import { EntityController } from '.';
8 |
9 | export class Execution extends EntityController {
10 | async send(event: WorkflowEvent): Promise {
11 | const { definition, currentJson } = await this.data();
12 | const fetchMachine = getMachine(
13 | definition,
14 | this.jwt.secretOrPublicKey,
15 | this.jwt.sign
16 | );
17 | const previousState = State.create(currentJson) as State<
18 | any,
19 | WorkflowEvent
20 | >;
21 | const resolvedState = fetchMachine.resolveState(previousState);
22 | const { nextEvents } = resolvedState;
23 | const { type } = event;
24 | if (resolvedState.done || !_.includes(nextEvents, type)) return this;
25 |
26 | const service = new WorkflowInterpreter(fetchMachine).start(resolvedState);
27 | return new BluebirdPromise((res) => {
28 | service
29 | .onTransition(async (state) => {
30 | if (state.changed !== undefined && _.isEmpty(state.children)) {
31 | await this.update({
32 | currentJson: state,
33 | state: state.value,
34 | output: state.event.data,
35 | completed: state.done
36 | });
37 | res(this);
38 | }
39 | })
40 | .send(event);
41 | })
42 | .timeout(this.timeout)
43 | .catch(() => {
44 | return this;
45 | })
46 | .finally(() => service.stop());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/controllers/index.ts:
--------------------------------------------------------------------------------
1 | import { ICrudable, IModel } from '../crudable';
2 | import { IJwt, IMicroflowStorage } from '../types';
3 |
4 | export class EntityController {
5 | constructor(
6 | private model: T,
7 | private store: ICrudable,
8 | protected storage: IMicroflowStorage,
9 | protected jwt: IJwt,
10 | protected timeout: number = 10000
11 | ) {
12 | }
13 |
14 | async data(): Promise {
15 | return this.store.read(this.model.id);
16 | }
17 |
18 | async update(data: Partial>): Promise {
19 | return this.store.update(this.model.id, data);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/controllers/task.ts:
--------------------------------------------------------------------------------
1 | import { EntityController } from '.';
2 | import { ITask } from '../types';
3 |
4 | export class Task extends EntityController {}
5 |
--------------------------------------------------------------------------------
/src/controllers/workflow.ts:
--------------------------------------------------------------------------------
1 | import {
2 | IExecution,
3 | IJwt,
4 | IMicroflowStorage,
5 | ITask,
6 | IWorkflow
7 | } from '../types';
8 | import { Execution } from './execution';
9 | import { transformConfig } from '../utils/convert';
10 | import { getMachine } from '../utils/xstate';
11 | import { ICrudable } from '../crudable';
12 | import { EntityController } from '.';
13 | import { MicroflowCore } from '../core';
14 |
15 | export class Workflow extends EntityController {
16 | private getTask: (id: string) => Promise;
17 | private execution: MicroflowCore;
18 |
19 | constructor(
20 | model: IWorkflow,
21 | store: ICrudable,
22 | storage: IMicroflowStorage,
23 | jwt: IJwt,
24 | timeout: number,
25 | ) {
26 | super(model, store, storage, jwt);
27 | this.getTask = storage.task.read.bind(storage.task);
28 | this.execution = new MicroflowCore(
29 | Execution,
30 | storage.execution,
31 | storage,
32 | this.jwt,
33 | timeout,
34 | );
35 | this.execution.create = this.execution.create.bind(this.execution);
36 | this.execution.update = this.execution.update.bind(this.execution);
37 | }
38 |
39 | async start(
40 | data: Record = {},
41 | name?: string
42 | ): Promise {
43 | const { config } = await this.data();
44 | const definition = await transformConfig(config, this.getTask);
45 | const workflowMachine = getMachine(
46 | definition,
47 | this.jwt.secretOrPublicKey,
48 | this.jwt.sign
49 | );
50 | const { initialState } = workflowMachine;
51 | const execution = await this.execution.create({
52 | name,
53 | config,
54 | definition,
55 | state: initialState.value,
56 | completed: initialState.done
57 | });
58 | const { id: executionId } = await execution.data();
59 | const { initialState: currentJson } = workflowMachine.withContext({
60 | wfid: executionId,
61 | name
62 | });
63 | await this.execution.update(executionId, {
64 | currentJson
65 | });
66 | return execution.send({ type: 'data', data });
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/core.ts:
--------------------------------------------------------------------------------
1 | import { ICrudable, IModel } from './crudable';
2 | import { IJwt, IMicroflowStorage } from './types';
3 |
4 | interface EntityConstructor {
5 | new (model: T, store: ICrudable, storage: IMicroflowStorage, jwt: IJwt, timeout: number): U;
6 | }
7 |
8 | export class MicroflowCore {
9 | constructor(
10 | private ctor: EntityConstructor,
11 | private store: ICrudable,
12 | private storage?: IMicroflowStorage,
13 | private jwt?: IJwt,
14 | private timeout?: number,
15 | ) {
16 | }
17 |
18 | async create(data: Omit & { id?: T['id'] }): Promise {
19 | const model = await this.store.create(data);
20 | return new this.ctor(model, this.store, this.storage, this.jwt, this.timeout);
21 | }
22 |
23 | async read(id: T['id']): Promise {
24 | const model = await this.store.read(id);
25 | return new this.ctor(model, this.store, this.storage, this.jwt, this.timeout);
26 | }
27 |
28 | async update(id: T['id'], data: Partial>): Promise {
29 | const model = await this.store.update(id, data);
30 | return new this.ctor(model, this.store, this.storage, this.jwt, this.timeout);
31 | }
32 |
33 | async delete(id: T['id']): Promise {
34 | return this.store.delete(id);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/crudable.ts:
--------------------------------------------------------------------------------
1 | export interface IModel {
2 | id: string;
3 | }
4 |
5 | export interface ICrudable {
6 | create(data: Omit & { id?: T['id'] }): Promise;
7 | read(id: T['id']): Promise;
8 | update(id: T['id'], data: Partial>): Promise;
9 | delete(id: T['id']): Promise;
10 | }
11 |
12 | export class Crudable implements ICrudable {
13 | create(data: Omit & { id?: T['id'] }): Promise {
14 | throw new Error('Method not implemented.');
15 | }
16 | read(id: T['id']): Promise {
17 | throw new Error('Method not implemented.');
18 | }
19 | update(id: T['id'], data: Partial>): Promise {
20 | throw new Error('Method not implemented.');
21 | }
22 | delete(id: T['id']): Promise {
23 | throw new Error('Method not implemented.');
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/interpreter/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Interpreter,
3 | StateSchema,
4 | EventData,
5 | Typestate,
6 | State,
7 | InterpreterOptions,
8 | StateMachine,
9 | SCXML
10 | } from 'xstate';
11 | import _ from 'lodash';
12 | import { transform, setOnPath } from '../utils';
13 | import { WorkflowEvent } from '../types';
14 |
15 | export class WorkflowInterpreter extends Interpreter<
16 | any,
17 | StateSchema,
18 | WorkflowEvent,
19 | Typestate
20 | > {
21 | oldSend: any;
22 | constructor(
23 | machine: StateMachine>,
24 | options?: Partial
25 | ) {
26 | super(machine, options);
27 | this.oldSend = this.send.bind(this);
28 | this.send = (
29 | event: WorkflowEvent | SCXML.Event,
30 | payload?: EventData
31 | ): State> => {
32 | const isSCXML = _.get(event, '$$type') === 'scxml';
33 | const data = isSCXML
34 | ? _.get(event, 'data.data', {})
35 | : _.get(event, 'data', {});
36 | const lastEventData = _.get(this.state.event, 'data', {});
37 | const isMapNode = _.get(
38 | this.state,
39 | 'activities.task.activity.src.isMap',
40 | false
41 | );
42 | const { context: ctx } = this.state;
43 | const { transitions } = this.machine.transition(this.state, event);
44 | const transition = _.head(transitions);
45 | const resultSelector = _.get(transition, 'resultSelector');
46 | const resultPath = _.get(transition, 'resultPath');
47 | const resultSelected = isMapNode
48 | ? data.map((value, index) =>
49 | transform(
50 | resultSelector,
51 | {
52 | _: data,
53 | _task: {
54 | executionId: ctx.wfid,
55 | name: ctx.name,
56 | item: { value, index }
57 | },
58 | _env: process.env
59 | },
60 | value
61 | )
62 | )
63 | : transform(resultSelector, {
64 | _: data,
65 | _env: process.env
66 | });
67 | const result = setOnPath(lastEventData, resultPath, resultSelected);
68 | _.set(event, isSCXML ? 'data.data' : 'data', result);
69 | return this.oldSend(event, payload);
70 | };
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/localCrud.ts:
--------------------------------------------------------------------------------
1 | import { nanoid } from 'nanoid';
2 | import { ICrudable, IModel } from './crudable';
3 |
4 | export class LocalCrud implements ICrudable {
5 | store: Map;
6 |
7 | constructor(map: Map) {
8 | this.store = map;
9 | }
10 |
11 | async create(data: Omit & { id?: T['id'] }): Promise {
12 | const id = data.id || nanoid();
13 | this.store.set(id, { id, ...data } as T);
14 | return this.store.get(id);
15 | }
16 |
17 | async read(id: T['id']): Promise {
18 | if (!this.store.has(id)) throw new Error(`Item with id = ${id} not found`);
19 | return this.store.get(id);
20 | }
21 |
22 | async update(id: T['id'], data: Partial>): Promise {
23 | if (!this.store.has(id)) throw new Error(`Item with id = ${id} not found`);
24 | this.store.set(id, { ...this.store.get(id), ...data });
25 | return this.store.get(id);
26 | }
27 |
28 | async delete(id: T['id']): Promise {
29 | if (!this.store.has(id)) throw new Error(`Item with id = ${id} not found`);
30 | this.store.delete(id);
31 | return id;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/microflow.ts:
--------------------------------------------------------------------------------
1 | import jwt from 'jsonwebtoken';
2 | import { DefaultStorage } from './storage';
3 | import { Task } from './controllers/task';
4 | import { Workflow } from './controllers/workflow';
5 | import { Execution } from './controllers/execution';
6 | import {
7 | IMicroflowStorage,
8 | IMicroflowConfig,
9 | TaskTokenClaims,
10 | IWorkflow,
11 | ITask,
12 | IExecution
13 | } from './types';
14 | import { MicroflowCore } from './core';
15 |
16 | export class Microflow {
17 | private secret: jwt.Secret;
18 | private signOptions: jwt.SignOptions;
19 | private verifyOptions: jwt.VerifyOptions;
20 |
21 | public storage: IMicroflowStorage;
22 |
23 | public workflow: MicroflowCore;
24 | public task: MicroflowCore;
25 | public execution: MicroflowCore;
26 |
27 | constructor(config: IMicroflowConfig) {
28 | const { storage, jwt, timeout } = config;
29 | const { secretOrPublicKey, sign, verify } = jwt;
30 | this.secret = secretOrPublicKey;
31 | this.signOptions = sign;
32 | this.verifyOptions = verify;
33 | if (!storage) {
34 | this.storage = new DefaultStorage();
35 | } else {
36 | this.storage = storage;
37 | }
38 | this.workflow = new MicroflowCore(
39 | Workflow,
40 | this.storage.workflow,
41 | this.storage,
42 | { secretOrPublicKey: this.secret, sign: this.signOptions },
43 | timeout
44 | );
45 | this.task = new MicroflowCore(Task, this.storage.task);
46 | this.execution = new MicroflowCore(
47 | Execution,
48 | this.storage.execution,
49 | this.storage,
50 | { secretOrPublicKey: this.secret, sign: this.signOptions },
51 | timeout,
52 | );
53 | }
54 |
55 | private claimsFromTaskToken(token): TaskTokenClaims {
56 | return jwt.verify(
57 | token,
58 | this.secret,
59 | this.verifyOptions
60 | ) as TaskTokenClaims;
61 | }
62 |
63 | async sendTaskSuccess(
64 | token: string,
65 | data: Record
66 | ): Promise {
67 | try {
68 | const { taskEventSuffix, workflowInstanceId } = this.claimsFromTaskToken(
69 | token
70 | );
71 | const execution = await this.execution.read(workflowInstanceId);
72 | return execution.send({
73 | type: `success-${taskEventSuffix}`,
74 | data
75 | });
76 | } catch (ex) {
77 | throw {
78 | message: 'Task token invalid'
79 | };
80 | }
81 | }
82 |
83 | async sendTaskFailure(
84 | token: string,
85 | data: Record
86 | ): Promise {
87 | try {
88 | const { taskEventSuffix, workflowInstanceId } = this.claimsFromTaskToken(
89 | token
90 | );
91 | const execution = await this.execution.read(workflowInstanceId);
92 | return execution.send({
93 | type: `failure-${taskEventSuffix}`,
94 | data
95 | });
96 | } catch (ex) {
97 | throw {
98 | message: 'Task token invalid'
99 | };
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/storage/index.ts:
--------------------------------------------------------------------------------
1 | import { ICrudable } from '../crudable';
2 | import { LocalCrud } from '../localCrud';
3 | import { ITask, IWorkflow, IExecution, IMicroflowStorage } from '../types';
4 |
5 | export class DefaultStorage implements IMicroflowStorage {
6 | private memory = {
7 | workflows: new Map(),
8 | tasks: new Map(),
9 | executions: new Map()
10 | };
11 | workflow: ICrudable;
12 | task: ICrudable;
13 | execution: ICrudable;
14 |
15 | constructor() {
16 | this.workflow = new LocalCrud(this.memory.workflows);
17 | this.task = new LocalCrud(this.memory.tasks);
18 | this.execution = new LocalCrud(this.memory.executions);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import jwt from 'jsonwebtoken';
2 | import {
3 | EventObject,
4 | MachineConfig,
5 | State,
6 | StateSchema,
7 | StateValue,
8 | Typestate
9 | } from 'xstate';
10 | import { ICrudable, IModel } from './crudable';
11 |
12 | export class Error {
13 | code: number;
14 | message: string;
15 | constructor(code: number, message: string) {
16 | this.code = code;
17 | this.message = message;
18 | }
19 | }
20 |
21 | export type SingleOrArray = T[] | T;
22 |
23 | export type ITransform =
24 | | SingleOrArray
25 | | SingleOrArray
26 | | SingleOrArray>;
27 |
28 | export interface IMicroflowConfig {
29 | storage?: IMicroflowStorage;
30 | timeout?: number;
31 | jwt: IJwt;
32 | }
33 |
34 | export interface IJwt {
35 | secretOrPublicKey: jwt.Secret;
36 | sign?: jwt.SignOptions;
37 | verify?: jwt.VerifyOptions;
38 | }
39 |
40 | export interface IWorkflow extends IModel {
41 | config: MicroflowDefinition;
42 | }
43 |
44 | export interface ITask extends IModel {
45 | type: 'http' | 'other';
46 | config: any;
47 | }
48 |
49 | export interface IExecution extends IModel {
50 | name?: string;
51 | config: MicroflowDefinition;
52 | definition: MachineConfig;
53 | currentJson?: State, Typestate>;
54 | state: StateValue;
55 | output?: Record;
56 | completed: boolean;
57 | }
58 |
59 | export interface WorkflowInput {
60 | id: string;
61 | config: MicroflowDefinition;
62 | }
63 |
64 | export interface TaskInput {
65 | id: string;
66 | type: 'http' | 'other';
67 | config: any;
68 | }
69 |
70 | export interface WorkflowInstanceInput {
71 | definition: any;
72 | currentJson: any;
73 | }
74 |
75 | export interface WorkflowEvent extends EventObject {
76 | data?: Record;
77 | }
78 |
79 | export interface TaskTokenClaims {
80 | workflowInstanceId: string;
81 | taskEventSuffix: string;
82 | }
83 |
84 | export type SendEventError = {
85 | message: string;
86 | };
87 |
88 | export type SendEventSuccess = {
89 | currentState: StateValue;
90 | completed: boolean;
91 | nextEvents: string[];
92 | };
93 |
94 | export type StartWorkflowResponse = {
95 | id: string;
96 | };
97 |
98 | export type SendEventResponse = SendEventSuccess;
99 |
100 | export type MicroflowStateTypes =
101 | | 'task'
102 | | 'taskSync'
103 | | 'taskMapSync'
104 | | 'pass'
105 | | 'atomic'
106 | | 'final';
107 |
108 | export interface TransitionConfig {
109 | target: string;
110 | meta?: Record;
111 | resultSelector?: Record;
112 | resultPath?: string;
113 | }
114 |
115 | export interface StateNodeConfig {
116 | type: MicroflowStateTypes;
117 | meta?: Record;
118 | on?: Record;
119 | }
120 |
121 | export interface AtomicNodeConfig extends StateNodeConfig {
122 | type: 'atomic';
123 | }
124 |
125 | export interface FinalNodeConfig {
126 | type: 'final';
127 | meta?: Record;
128 | }
129 |
130 | export interface PassNodeConfig extends StateNodeConfig {
131 | type: 'pass';
132 | onDone?: TransitionConfig;
133 | onError?: TransitionConfig;
134 | }
135 |
136 | export interface TaskNodeConfig extends StateNodeConfig {
137 | type: 'task';
138 | taskId: string;
139 | parameters?: Record;
140 | onDone?: TransitionConfig;
141 | onError?: TransitionConfig;
142 | }
143 |
144 | export interface TaskNodeSyncConfig extends StateNodeConfig {
145 | type: 'taskSync';
146 | taskId: string;
147 | parameters?: Record;
148 | onDone?: TransitionConfig;
149 | onError?: TransitionConfig;
150 | }
151 |
152 | export interface TaskMapNodeSyncConfig extends StateNodeConfig {
153 | type: 'taskMapSync';
154 | taskId: string;
155 | itemsPath: string;
156 | parameters?: Record;
157 | onDone?: TransitionConfig;
158 | onError?: TransitionConfig;
159 | }
160 |
161 | export type TMicroflowNode =
162 | | TaskNodeConfig
163 | | TaskNodeSyncConfig
164 | | TaskMapNodeSyncConfig
165 | | PassNodeConfig
166 | | AtomicNodeConfig
167 | | FinalNodeConfig;
168 |
169 | export interface MicroflowDefinition {
170 | initial: string;
171 | meta?: Record;
172 | states: Record;
173 | context?: Record;
174 | }
175 |
176 | export interface IMicroflowStorage {
177 | workflow: ICrudable;
178 | task: ICrudable;
179 | execution: ICrudable;
180 | }
181 |
--------------------------------------------------------------------------------
/src/utils/convert.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import { map } from 'bluebird';
3 | import { MachineConfig, StateNodeConfig as XStateNodeConfig } from 'xstate';
4 |
5 | import {
6 | AtomicNodeConfig,
7 | FinalNodeConfig,
8 | ITask,
9 | MicroflowDefinition,
10 | TaskNodeConfig,
11 | WorkflowEvent,
12 | TransitionConfig,
13 | TaskNodeSyncConfig,
14 | TMicroflowNode,
15 | TaskMapNodeSyncConfig,
16 | PassNodeConfig
17 | } from '../types';
18 | import { MICROFLOW } from '../constants';
19 | import { nanoid } from 'nanoid';
20 |
21 | export async function transformConfig(
22 | config: MicroflowDefinition,
23 | getTask: (id: string) => Promise
24 | ): Promise> {
25 | const states = _.get(config, 'states', {});
26 | const uniqueTaskIds = _.chain(states)
27 | .mapValues()
28 | .filter((s) => {
29 | const type = _.get(s, 'type');
30 | return (
31 | type === MICROFLOW.STATES.TASK ||
32 | type === MICROFLOW.STATES.TASK_SYNC ||
33 | type === MICROFLOW.STATES.TASK_MAP_SYNC
34 | );
35 | })
36 | .map('taskId')
37 | .uniq()
38 | .value();
39 | const tasks = await map(uniqueTaskIds, getTask, {
40 | concurrency: MICROFLOW.CONCURRENCY
41 | });
42 | const taskMap = _.keyBy(tasks, 'id');
43 | return microflowToXstateConfig(config, taskMap);
44 | }
45 |
46 | export function microflowToXstateConfig(
47 | microflowConfig: MicroflowDefinition,
48 | taskDictionary: _.Dictionary
49 | ): MachineConfig {
50 | const { states } = microflowConfig;
51 | const newStates = _.mapValues(states, (s: TMicroflowNode, name) => {
52 | if (s.type === 'task')
53 | return microflowTaskToXstateNode(s, taskDictionary[s.taskId], name);
54 | else if (s.type === 'taskSync')
55 | return microflowTaskSyncToXstateNode(s, taskDictionary[s.taskId]);
56 | else if (s.type === 'taskMapSync')
57 | return microflowTaskMapSyncToXstateNode(s, taskDictionary[s.taskId]);
58 | else if (s.type === 'atomic') return microflowAtomicToXstateNode(s);
59 | else if (s.type === 'pass') return microflowPassToXstateNode(s);
60 | else return microflowFinalToXstateNode(s);
61 | }) as Record>;
62 |
63 | const initialNodeName = nanoid();
64 | return {
65 | id: 'main',
66 | initial: initialNodeName,
67 | meta: microflowConfig.meta,
68 | states: {
69 | [initialNodeName]: {
70 | on: {
71 | data: {
72 | target: microflowConfig.initial,
73 | resultPath: '$'
74 | } as TransitionConfig
75 | }
76 | },
77 | ...newStates
78 | }
79 | };
80 | }
81 |
82 | export function microflowTaskToXstateNode(
83 | s: TaskNodeConfig,
84 | task: ITask,
85 | name?: string
86 | ): XStateNodeConfig {
87 | // const taskEventSuffix = nanoid();
88 | const taskEventSuffix = name;
89 | return {
90 | type: 'compound',
91 | initial: 'starting',
92 | meta: s.meta,
93 | states: {
94 | starting: {
95 | invoke: {
96 | src: {
97 | type: s.type,
98 | taskId: s.taskId,
99 | task,
100 | parameters: s.parameters,
101 | taskEventSuffix
102 | },
103 | onDone: {
104 | target: 'started'
105 | },
106 | onError: {
107 | target: 'failed_to_start',
108 | resultPath: `$.errors.${taskEventSuffix}`
109 | } as TransitionConfig
110 | }
111 | },
112 | started: {
113 | on: {
114 | [`success-${taskEventSuffix}`]: {
115 | ...s.onDone,
116 | target: '#main.' + s.onDone.target
117 | },
118 | [`failure-${taskEventSuffix}`]: {
119 | ...s.onError,
120 | target: '#main.' + s.onError.target
121 | }
122 | }
123 | },
124 | failed_to_start: {}
125 | },
126 | on: s.on
127 | };
128 | }
129 |
130 | export function microflowTaskSyncToXstateNode(
131 | s: TaskNodeSyncConfig,
132 | task: ITask
133 | ): XStateNodeConfig {
134 | return {
135 | type: 'atomic',
136 | meta: s.meta,
137 | invoke: {
138 | src: {
139 | type: 'task',
140 | taskId: s.taskId,
141 | task,
142 | parameters: s.parameters
143 | },
144 | onDone: s.onDone,
145 | onError: s.onError
146 | },
147 | on: s.on
148 | };
149 | }
150 |
151 | export function microflowTaskMapSyncToXstateNode(
152 | s: TaskMapNodeSyncConfig,
153 | task: ITask
154 | ): XStateNodeConfig {
155 | return {
156 | type: 'atomic',
157 | meta: s.meta,
158 | invoke: {
159 | src: {
160 | type: 'task',
161 | taskId: s.taskId,
162 | task,
163 | isMap: true,
164 | itemsPath: s.itemsPath ? s.itemsPath : '$',
165 | parameters: s.parameters
166 | },
167 | onDone: s.onDone,
168 | onError: s.onError
169 | },
170 | on: s.on
171 | };
172 | }
173 |
174 | export function microflowAtomicToXstateNode(
175 | s: AtomicNodeConfig
176 | ): XStateNodeConfig {
177 | return {
178 | type: 'atomic',
179 | meta: s.meta,
180 | on: s.on
181 | };
182 | }
183 |
184 | export function microflowPassToXstateNode(
185 | s: PassNodeConfig
186 | ): XStateNodeConfig {
187 | return {
188 | type: 'atomic',
189 | meta: s.meta,
190 | invoke: {
191 | src: {
192 | type: 'transform'
193 | },
194 | onDone: s.onDone,
195 | onError: s.onError
196 | }
197 | };
198 | }
199 |
200 | export function microflowFinalToXstateNode(
201 | s: FinalNodeConfig
202 | ): XStateNodeConfig {
203 | return {
204 | type: 'final',
205 | meta: s.meta
206 | };
207 | }
208 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { JSONPath } from 'jsonpath-plus';
2 | import * as _ from 'lodash';
3 | import * as handlebars from 'handlebars';
4 | import { ITransform } from '../types';
5 |
6 | handlebars.registerHelper('json', function(context) {
7 | return JSON.stringify(context);
8 | });
9 |
10 | const transform = >(
11 | obj: T,
12 | root: Record = {},
13 | def: Record = root._,
14 | original = true
15 | ): T => {
16 | if (_.isString(obj)) {
17 | if (_.startsWith(obj, '$.') || obj === '$')
18 | return JSONPath({ path: obj, json: root, wrap: false });
19 | return handlebars.compile(obj)(root) as T;
20 | } else if (_.isArray(obj)) {
21 | return _.map(obj, (x) => transform(x, root, def, false)) as T;
22 | } else if (_.isObject(obj)) {
23 | return _.mapValues(obj, (x) => transform(x, root, def, false)) as T;
24 | } else if (_.isUndefined(obj)) {
25 | if (original) return def as T;
26 | else return undefined;
27 | } else {
28 | return obj;
29 | }
30 | };
31 |
32 | const setOnPath = (
33 | root: Record,
34 | path: string,
35 | obj: Record
36 | ): Record => {
37 | if (path === '$') return obj;
38 | if (path === undefined) return root;
39 | const lodashPath = _.trimStart(path, '$.');
40 | return _.set(_.clone(root), lodashPath, obj);
41 | };
42 |
43 | export { transform, setOnPath };
44 |
--------------------------------------------------------------------------------
/src/utils/task.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import bluebird from 'bluebird';
3 | import { JSONPath } from 'jsonpath-plus';
4 | import jwt from 'jsonwebtoken';
5 | import { InvokeCreator } from 'xstate';
6 | import { transform } from './index';
7 |
8 | export const taskCreator = (
9 | secret: jwt.Secret,
10 | signOptions: jwt.SignOptions
11 | ): InvokeCreator => async (
12 | ctx: Record,
13 | { data },
14 | { src }
15 | ) => {
16 | const { taskEventSuffix, task, parameters, itemsPath, isMap } = src;
17 | const token = jwt.sign(
18 | { workflowInstanceId: ctx.wfid, taskEventSuffix },
19 | secret,
20 | signOptions
21 | );
22 | const dataInputs = isMap
23 | ? JSONPath({
24 | path: itemsPath,
25 | json: data
26 | })[0] || []
27 | : [data];
28 | const response = await bluebird.map(dataInputs, async (value, index) => {
29 | const enrichedParameters = transform(parameters, {
30 | _: data,
31 | _task: {
32 | executionId: ctx.wfid,
33 | name: ctx.name,
34 | token,
35 | item: { value, index }
36 | },
37 | _env: process.env
38 | });
39 | const taskResolved = transform(task.config, enrichedParameters);
40 | const res = await axios.request(taskResolved);
41 | return res.data;
42 | });
43 | return isMap ? response : response[0];
44 | };
45 |
46 | export const transformerInvoke: InvokeCreator = async (
47 | _ctx: Record,
48 | { data }
49 | ) => {
50 | return data;
51 | };
52 |
--------------------------------------------------------------------------------
/src/utils/xstate.ts:
--------------------------------------------------------------------------------
1 | import jwt from 'jsonwebtoken';
2 | import {
3 | AnyEventObject,
4 | Machine,
5 | MachineConfig,
6 | StateMachine,
7 | Typestate
8 | } from 'xstate';
9 | import { WorkflowEvent } from '../types';
10 | import { taskCreator, transformerInvoke } from './task';
11 |
12 | export function getMachine(
13 | config: MachineConfig,
14 | secret: jwt.Secret,
15 | signOptions: jwt.SignOptions
16 | ): StateMachine> {
17 | return Machine(config, {
18 | services: {
19 | task: taskCreator(secret, signOptions),
20 | transform: transformerInvoke
21 | }
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/tests/atomicNodeInitial.test.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | jest.mock('axios');
3 | const mockedAxios = axios as jest.Mocked;
4 | mockedAxios.request.mockResolvedValue({});
5 | import { Microflow } from '../src/microflow';
6 |
7 | const flow = new Microflow({
8 | jwt: {
9 | secretOrPublicKey: 'shhhhh',
10 | sign: {
11 | expiresIn: '24h'
12 | }
13 | }
14 | });
15 |
16 | test('test transitions', async () => {
17 | const task = await flow.task.create({
18 | id: 'airflow',
19 | type: 'http',
20 | config: {
21 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
22 | headers: {
23 | 'Cache-Control': 'no-cache',
24 | 'Content-Type': 'application/json'
25 | },
26 | data: {
27 | conf: {
28 | actualData: '$.data',
29 | token: '$.token',
30 | envKey: '$.envKey',
31 | executionId: '$.executionId'
32 | }
33 | },
34 | method: 'post'
35 | }
36 | });
37 |
38 | const { id: taskId } = await task.data();
39 |
40 | const workflow = await flow.workflow.create({
41 | id: 'sample',
42 | config: {
43 | initial: 'waiting',
44 | states: {
45 | waiting: {
46 | type: 'atomic',
47 | on: {
48 | start_test: {
49 | target: 'auto_test_1',
50 | resultPath: '$.trial'
51 | }
52 | }
53 | },
54 | auto_test_1: {
55 | type: 'task',
56 | taskId,
57 | parameters: {
58 | dagId: 'dag1',
59 | data: '$._',
60 | token: '$._task.token',
61 | envKey: '$._env.myKey1',
62 | executionId: '$._task.executionId'
63 | },
64 | onDone: {
65 | target: 'ready_for_approval',
66 | resultSelector: {
67 | a: 'a',
68 | b: 'b',
69 | out: '$._'
70 | },
71 | resultPath: '$.pipeline1.success'
72 | },
73 | onError: {
74 | target: 'failed',
75 | resultSelector: {
76 | c: 'c',
77 | d: 'd',
78 | out: '$._'
79 | },
80 | resultPath: '$.pipeline1.error'
81 | }
82 | },
83 | ready_for_approval: {
84 | type: 'atomic',
85 | on: {
86 | reject: {
87 | target: 'failed',
88 | resultPath: '$.reject.data'
89 | },
90 | approve: {
91 | target: 'auto_test_2',
92 | resultPath: '$.approval.data'
93 | }
94 | }
95 | },
96 | auto_test_2: {
97 | type: 'task',
98 | taskId,
99 | parameters: {
100 | dagId: 'dag2',
101 | data: 'karan',
102 | token: '$._task.token',
103 | envKey: '$._env.myKey2',
104 | executionId: '$._task.executionId'
105 | },
106 | onDone: {
107 | target: 'done',
108 | resultSelector: {
109 | e: 'e',
110 | out: '$._'
111 | },
112 | resultPath: '$.pipeline2.success'
113 | },
114 | onError: {
115 | target: 'failed',
116 | resultSelector: {
117 | f: 'f',
118 | out: '$._'
119 | },
120 | resultPath: '$.pipeline2.error'
121 | }
122 | },
123 | done: {
124 | type: 'final'
125 | },
126 | failed: {
127 | type: 'final'
128 | }
129 | }
130 | }
131 | });
132 |
133 | const execution = await workflow.start({
134 | input1: 'karan',
135 | input2: 'chhabra'
136 | });
137 |
138 | await execution.send({
139 | type: 'start_test',
140 | data: { a: 1, b: 2 }
141 | });
142 |
143 | await execution.send({
144 | type: 'success-auto_test_1',
145 | data: {
146 | ok: 'cupid'
147 | }
148 | });
149 |
150 | await execution.send({
151 | type: 'approve',
152 | data: {
153 | notok: 'cupido'
154 | }
155 | });
156 |
157 | await execution.send({
158 | type: 'success-auto_test_2',
159 | data: {
160 | c: 'wee'
161 | }
162 | });
163 |
164 | const { completed, output } = await execution.data();
165 | expect(completed).toBe(true);
166 | expect(output).toMatchObject({
167 | input1: 'karan',
168 | input2: 'chhabra',
169 | trial: { a: 1, b: 2 },
170 | pipeline1: {
171 | success: {
172 | a: 'a',
173 | b: 'b',
174 | out: {
175 | ok: 'cupid'
176 | }
177 | }
178 | },
179 | approval: {
180 | data: {
181 | notok: 'cupido'
182 | }
183 | },
184 | pipeline2: {
185 | success: {
186 | e: 'e',
187 | out: {
188 | c: 'wee'
189 | }
190 | }
191 | }
192 | });
193 | });
194 |
--------------------------------------------------------------------------------
/tests/taskMapSyncNode.test.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | jest.mock('axios');
3 | const mockedAxios = axios as jest.Mocked;
4 | mockedAxios.request.mockResolvedValue({});
5 | import { Microflow } from '../src/microflow';
6 |
7 | const flow = new Microflow({
8 | jwt: {
9 | secretOrPublicKey: 'shhhhh',
10 | sign: {
11 | expiresIn: '24h'
12 | }
13 | }
14 | });
15 |
16 | test('test transitions', async () => {
17 | const task = await flow.task.create({
18 | id: 'airflow',
19 | type: 'http',
20 | config: {
21 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
22 | headers: {
23 | 'Cache-Control': 'no-cache',
24 | 'Content-Type': 'application/json'
25 | },
26 | data: {
27 | conf: {
28 | actualData: '$.data',
29 | token: '$.token',
30 | envKey: '$.envKey',
31 | executionId: '$.executionId'
32 | }
33 | },
34 | method: 'post'
35 | }
36 | });
37 |
38 | const { id: taskId } = await task.data();
39 |
40 | // Create a workflow
41 | const workflow = await flow.workflow.create({
42 | id: 'sample',
43 | config: {
44 | initial: 'auto_test_1',
45 | states: {
46 | auto_test_1: {
47 | type: 'taskMapSync',
48 | taskId,
49 | itemsPath: '$.iterateOn',
50 | parameters: {
51 | dagId: 'dag1',
52 | data: '$._',
53 | token: '$._task.token',
54 | envKey: '$._env.myKey1',
55 | executionId: '$._task.executionId'
56 | },
57 | onDone: {
58 | target: 'ready_for_approval',
59 | resultSelector: {
60 | a: 'a',
61 | b: 'b',
62 | out: '$._'
63 | },
64 | resultPath: '$.pipeline1.success'
65 | },
66 | onError: {
67 | target: 'failed',
68 | resultSelector: {
69 | c: 'c',
70 | d: 'd',
71 | out: '$._'
72 | },
73 | resultPath: '$.pipeline1.error'
74 | }
75 | },
76 | ready_for_approval: {
77 | type: 'atomic',
78 | on: {
79 | reject: {
80 | target: 'failed',
81 | resultPath: '$.reject.data'
82 | },
83 | approve: {
84 | target: 'auto_test_2',
85 | resultPath: '$.approval.data'
86 | }
87 | }
88 | },
89 | auto_test_2: {
90 | type: 'taskSync',
91 | taskId,
92 | parameters: {
93 | dagId: 'dag2',
94 | data: '$._',
95 | token: '$._task.token',
96 | envKey: '$._env.myKey1',
97 | executionId: '$._task.executionId'
98 | },
99 | onDone: {
100 | target: 'done',
101 | resultSelector: {
102 | e: 'e',
103 | out: '$._'
104 | },
105 | resultPath: '$.pipeline2.success'
106 | },
107 | onError: {
108 | target: 'failed',
109 | resultSelector: {
110 | f: 'f',
111 | out: '$._'
112 | },
113 | resultPath: '$.pipeline2.error'
114 | }
115 | },
116 | done: {
117 | type: 'final'
118 | },
119 | failed: {
120 | type: 'final'
121 | }
122 | }
123 | }
124 | });
125 |
126 | // start an execution with initial data
127 | const execution = await workflow.start({
128 | input1: 'val1',
129 | input2: 'val2',
130 | iterateOn: [1, 2, 3]
131 | });
132 |
133 | await execution.send({
134 | type: 'approve',
135 | data: {
136 | message: 'The acceptance test was fine'
137 | }
138 | });
139 |
140 | const { completed, output } = await execution.data();
141 | expect(completed).toBe(true);
142 | expect(output).toMatchObject({
143 | input1: 'val1',
144 | input2: 'val2',
145 | pipeline1: {
146 | success: [
147 | {
148 | a: 'a',
149 | b: 'b',
150 | out: {}
151 | },
152 | {
153 | a: 'a',
154 | b: 'b',
155 | out: {}
156 | },
157 | {
158 | a: 'a',
159 | b: 'b',
160 | out: {}
161 | }
162 | ]
163 | },
164 | approval: { data: { message: 'The acceptance test was fine' } },
165 | pipeline2: {
166 | success: {
167 | e: 'e',
168 | out: {}
169 | }
170 | }
171 | });
172 | });
173 |
--------------------------------------------------------------------------------
/tests/taskNodeInitial.test.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | jest.mock('axios');
3 | const mockedAxios = axios as jest.Mocked;
4 | mockedAxios.request.mockResolvedValue({});
5 | import { Microflow } from '../src/microflow';
6 |
7 | const flow = new Microflow({
8 | jwt: {
9 | secretOrPublicKey: 'shhhhh',
10 | sign: {
11 | expiresIn: '24h'
12 | }
13 | }
14 | });
15 |
16 | test('test transitions', async () => {
17 | const task = await flow.task.create({
18 | id: 'airflow',
19 | type: 'http',
20 | config: {
21 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
22 | headers: {
23 | 'Cache-Control': 'no-cache',
24 | 'Content-Type': 'application/json'
25 | },
26 | data: {
27 | conf: {
28 | actualData: '$.data',
29 | token: '$.token',
30 | envKey: '$.envKey',
31 | executionId: '$.executionId'
32 | }
33 | },
34 | method: 'post'
35 | }
36 | });
37 |
38 | const { id: taskId } = await task.data();
39 |
40 | // Create a workflow
41 | const workflow = await flow.workflow.create({
42 | id: 'sample',
43 | config: {
44 | initial: 'auto_test_1',
45 | states: {
46 | auto_test_1: {
47 | type: 'task',
48 | taskId,
49 | parameters: {
50 | dagId: 'dag1',
51 | data: '$._',
52 | token: '$._task.token',
53 | envKey: '$._env.myKey1',
54 | executionId: '$._task.executionId'
55 | },
56 | onDone: {
57 | target: 'ready_for_approval',
58 | resultSelector: {
59 | a: 'a',
60 | b: 'b',
61 | out: '$._'
62 | },
63 | resultPath: '$.pipeline1.success'
64 | },
65 | onError: {
66 | target: 'failed',
67 | resultSelector: {
68 | c: 'c',
69 | d: 'd',
70 | out: '$._'
71 | },
72 | resultPath: '$.pipeline1.error'
73 | }
74 | },
75 | ready_for_approval: {
76 | type: 'atomic',
77 | on: {
78 | reject: {
79 | target: 'failed',
80 | resultPath: '$.reject.data'
81 | },
82 | approve: {
83 | target: 'auto_test_2',
84 | resultPath: '$.approval.data'
85 | }
86 | }
87 | },
88 | auto_test_2: {
89 | type: 'task',
90 | taskId,
91 | parameters: {
92 | dagId: 'dag2',
93 | data: '$._',
94 | token: '$._task.token',
95 | envKey: '$._env.myKey1',
96 | executionId: '$._task.executionId'
97 | },
98 | onDone: {
99 | target: 'done',
100 | resultSelector: {
101 | e: 'e',
102 | out: '$._'
103 | },
104 | resultPath: '$.pipeline2.success'
105 | },
106 | onError: {
107 | target: 'failed',
108 | resultSelector: {
109 | f: 'f',
110 | out: '$._'
111 | },
112 | resultPath: '$.pipeline2.error'
113 | }
114 | },
115 | done: {
116 | type: 'final'
117 | },
118 | failed: {
119 | type: 'final'
120 | }
121 | }
122 | }
123 | });
124 |
125 | // start an execution with initial data
126 | const execution = await workflow.start({
127 | input1: 'val1',
128 | input2: 'val2'
129 | });
130 |
131 | // Sending events to an execution
132 | await execution.send({
133 | type: 'success-auto_test_1',
134 | data: {
135 | test_a_result: true,
136 | test_b_result: false
137 | }
138 | });
139 |
140 | await execution.send({
141 | type: 'approve',
142 | data: {
143 | message: 'The acceptance test was fine'
144 | }
145 | });
146 |
147 | await execution.send({
148 | type: 'success-auto_test_2',
149 | data: {
150 | test_c_result: true
151 | }
152 | });
153 |
154 | const { completed, output } = await execution.data();
155 | expect(completed).toBe(true);
156 | expect(output).toMatchObject({
157 | input1: 'val1',
158 | input2: 'val2',
159 | pipeline1: {
160 | success: {
161 | a: 'a',
162 | b: 'b',
163 | out: {
164 | test_a_result: true,
165 | test_b_result: false
166 | }
167 | }
168 | },
169 | approval: {
170 | data: {
171 | message: 'The acceptance test was fine'
172 | }
173 | },
174 | pipeline2: {
175 | success: {
176 | e: 'e',
177 | out: {
178 | test_c_result: true
179 | }
180 | }
181 | }
182 | });
183 | });
184 |
--------------------------------------------------------------------------------
/tests/taskSyncNode.test.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | jest.mock('axios');
3 | const mockedAxios = axios as jest.Mocked;
4 | mockedAxios.request.mockResolvedValue({});
5 | import { Microflow } from '../src/microflow';
6 |
7 | const flow = new Microflow({
8 | jwt: {
9 | secretOrPublicKey: 'shhhhh',
10 | sign: {
11 | expiresIn: '24h'
12 | }
13 | }
14 | });
15 |
16 | test('test transitions', async () => {
17 | const task = await flow.task.create({
18 | id: 'airflow',
19 | type: 'http',
20 | config: {
21 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
22 | headers: {
23 | 'Cache-Control': 'no-cache',
24 | 'Content-Type': 'application/json'
25 | },
26 | data: {
27 | conf: {
28 | actualData: '$.data',
29 | token: '$.token',
30 | envKey: '$.envKey',
31 | executionId: '$.executionId'
32 | }
33 | },
34 | method: 'post'
35 | }
36 | });
37 |
38 | const { id: taskId } = await task.data();
39 |
40 | // Create a workflow
41 | const workflow = await flow.workflow.create({
42 | id: 'sample',
43 | config: {
44 | initial: 'auto_test_1',
45 | states: {
46 | auto_test_1: {
47 | type: 'taskSync',
48 | taskId,
49 | parameters: {
50 | dagId: 'dag1',
51 | data: '$._',
52 | token: '$._task.token',
53 | envKey: '$._env.myKey1',
54 | executionId: '$._task.executionId'
55 | },
56 | onDone: {
57 | target: 'ready_for_approval',
58 | resultSelector: {
59 | a: 'a',
60 | b: 'b',
61 | out: '$._'
62 | },
63 | resultPath: '$.pipeline1.success'
64 | },
65 | onError: {
66 | target: 'failed',
67 | resultSelector: {
68 | c: 'c',
69 | d: 'd',
70 | out: '$._'
71 | },
72 | resultPath: '$.pipeline1.error'
73 | }
74 | },
75 | ready_for_approval: {
76 | type: 'atomic',
77 | on: {
78 | reject: {
79 | target: 'failed',
80 | resultPath: '$.reject.data'
81 | },
82 | approve: {
83 | target: 'auto_test_2',
84 | resultPath: '$.approval.data'
85 | }
86 | }
87 | },
88 | auto_test_2: {
89 | type: 'taskSync',
90 | taskId,
91 | parameters: {
92 | dagId: 'dag2',
93 | data: '$._',
94 | token: '$._task.token',
95 | envKey: '$._env.myKey1',
96 | executionId: '$._task.executionId'
97 | },
98 | onDone: {
99 | target: 'done',
100 | resultSelector: {
101 | e: 'e',
102 | out: '$._'
103 | },
104 | resultPath: '$.pipeline2.success'
105 | },
106 | onError: {
107 | target: 'failed',
108 | resultSelector: {
109 | f: 'f',
110 | out: '$._'
111 | },
112 | resultPath: '$.pipeline2.error'
113 | }
114 | },
115 | done: {
116 | type: 'final'
117 | },
118 | failed: {
119 | type: 'final'
120 | }
121 | }
122 | }
123 | });
124 |
125 | // start an execution with initial data
126 | const execution = await workflow.start({
127 | input1: 'val1',
128 | input2: 'val2'
129 | });
130 |
131 | await execution.send({
132 | type: 'approve',
133 | data: {
134 | message: 'The acceptance test was fine'
135 | }
136 | });
137 |
138 | const { completed, output } = await execution.data();
139 | expect(completed).toBe(true);
140 | expect(output).toMatchObject({
141 | input1: 'val1',
142 | input2: 'val2',
143 | pipeline1: {
144 | success: {
145 | a: 'a',
146 | b: 'b',
147 | out: {}
148 | }
149 | },
150 | approval: { data: { message: 'The acceptance test was fine' } },
151 | pipeline2: {
152 | success: {
153 | e: 'e',
154 | out: {}
155 | }
156 | }
157 | });
158 | });
159 |
--------------------------------------------------------------------------------
/tests/taskToken.test.ts:
--------------------------------------------------------------------------------
1 | import jwt from 'jsonwebtoken';
2 | import axios from 'axios';
3 | jest.mock('axios');
4 | const mockedAxios = axios as jest.Mocked;
5 | mockedAxios.request.mockResolvedValue({});
6 | import { Microflow } from '../src/microflow';
7 |
8 | const getTaskToken = (id, task) =>
9 | jwt.sign({ workflowInstanceId: id, taskEventSuffix: task }, 'shhhhh', {
10 | expiresIn: '24h'
11 | });
12 |
13 | const flow = new Microflow({
14 | jwt: {
15 | secretOrPublicKey: 'shhhhh',
16 | sign: {
17 | expiresIn: '24h'
18 | }
19 | }
20 | });
21 |
22 | test('test transitions with tokens', async () => {
23 | const task = await flow.task.create({
24 | id: 'airflow',
25 | type: 'http',
26 | config: {
27 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
28 | headers: {
29 | 'Cache-Control': 'no-cache',
30 | 'Content-Type': 'application/json'
31 | },
32 | data: {
33 | conf: {
34 | actualData: '$.data',
35 | token: '$.token',
36 | envKey: '$.envKey',
37 | executionId: '$.executionId'
38 | }
39 | },
40 | method: 'post'
41 | }
42 | });
43 |
44 | const { id: taskId } = await task.data();
45 |
46 | const workflow = await flow.workflow.create({
47 | id: 'sample',
48 | config: {
49 | initial: 'waiting',
50 | states: {
51 | waiting: {
52 | type: 'atomic',
53 | on: {
54 | start_test: {
55 | target: 'auto_test_1',
56 | resultPath: '$'
57 | }
58 | }
59 | },
60 | auto_test_1: {
61 | type: 'task',
62 | taskId: 'airflow',
63 | parameters: {
64 | dagId: 'dag1',
65 | data: '$._',
66 | token: '$._task.token',
67 | envKey: '$._env.myKey1',
68 | executionId: '$._task.executionId'
69 | },
70 | onDone: {
71 | target: 'ready_for_approval',
72 | resultSelector: {
73 | a: 'a',
74 | b: 'b',
75 | out: '$._'
76 | },
77 | resultPath: '$.pipeline1.success'
78 | },
79 | onError: {
80 | target: 'failed',
81 | resultSelector: {
82 | c: 'c',
83 | d: 'd',
84 | out: '$._'
85 | },
86 | resultPath: '$.pipeline1.error'
87 | }
88 | },
89 | ready_for_approval: {
90 | type: 'atomic',
91 | on: {
92 | reject: {
93 | target: 'failed',
94 | resultPath: '$.reject.data'
95 | },
96 | approve: {
97 | target: 'auto_test_2',
98 | resultPath: '$.approval.data'
99 | }
100 | }
101 | },
102 | auto_test_2: {
103 | type: 'task',
104 | taskId: taskId,
105 | parameters: {
106 | dagId: 'dag2',
107 | data: '$._',
108 | token: '$._task.token',
109 | envKey: '$._env.myKey1',
110 | executionId: '$._task.executionId'
111 | },
112 | onDone: {
113 | target: 'done',
114 | resultSelector: {
115 | e: 'e',
116 | out: '$._'
117 | },
118 | resultPath: '$.pipeline2.success'
119 | },
120 | onError: {
121 | target: 'failed',
122 | resultSelector: {
123 | f: 'f',
124 | out: '$._'
125 | },
126 | resultPath: '$.pipeline2.error'
127 | }
128 | },
129 | done: {
130 | type: 'final'
131 | },
132 | failed: {
133 | type: 'final'
134 | }
135 | }
136 | }
137 | });
138 |
139 | const execution = await workflow.start();
140 |
141 | const { id: executionId } = await execution.data();
142 |
143 | await execution.send({
144 | type: 'start_test',
145 | data: { input1: 'val1', input2: 'val2' }
146 | });
147 |
148 | await flow.sendTaskSuccess(getTaskToken(executionId, 'auto_test_1'), {
149 | test_a_result: true,
150 | test_b_result: false
151 | });
152 |
153 | await execution.send({
154 | type: 'approve',
155 | data: {
156 | message: 'The acceptance test was fine'
157 | }
158 | });
159 |
160 | await flow.sendTaskSuccess(getTaskToken(executionId, 'auto_test_2'), {
161 | test_c_result: true
162 | });
163 |
164 | const { completed, output } = await execution.data();
165 | expect(completed).toBe(true);
166 | expect(output).toMatchObject({
167 | input1: 'val1',
168 | input2: 'val2',
169 | pipeline1: {
170 | success: {
171 | a: 'a',
172 | b: 'b',
173 | out: {
174 | test_a_result: true,
175 | test_b_result: false
176 | }
177 | }
178 | },
179 | approval: {
180 | data: {
181 | message: 'The acceptance test was fine'
182 | }
183 | },
184 | pipeline2: {
185 | success: {
186 | e: 'e',
187 | out: {
188 | test_c_result: true
189 | }
190 | }
191 | }
192 | });
193 | });
194 |
--------------------------------------------------------------------------------
/tests/utils/index.test.ts:
--------------------------------------------------------------------------------
1 | import { transform, setOnPath } from '../../src/utils/index';
2 |
3 | describe('transform function', () => {
4 | test('object input and root', () => {
5 | const output = transform(
6 | {
7 | a: '$',
8 | b: '$.key1',
9 | c: '$.jabber',
10 | d: 'constant',
11 | e: {
12 | f: 'constant',
13 | g: '$.key1',
14 | h: '$'
15 | }
16 | },
17 | {
18 | key1: 'val1',
19 | key2: 'val2'
20 | }
21 | );
22 | expect(output).toEqual({
23 | a: { key1: 'val1', key2: 'val2' },
24 | b: 'val1',
25 | c: undefined,
26 | d: 'constant',
27 | e: { f: 'constant', g: 'val1', h: { key1: 'val1', key2: 'val2' } }
28 | });
29 | });
30 |
31 | test('object input and root, access array indices', () => {
32 | const output = transform(
33 | {
34 | a: '$',
35 | b: '$.key1[1]',
36 | c: '$.jabber',
37 | d: 'constant',
38 | e: {
39 | f: 'constant',
40 | g: '$.key1',
41 | h: '$'
42 | }
43 | },
44 | {
45 | key1: ['val1', 'val2'],
46 | key2: 'val3'
47 | }
48 | );
49 | expect(output).toEqual({
50 | a: { key1: ['val1', 'val2'], key2: 'val3' },
51 | b: 'val2',
52 | c: undefined,
53 | d: 'constant',
54 | e: {
55 | f: 'constant',
56 | g: ['val1', 'val2'],
57 | h: {
58 | key1: ['val1', 'val2'],
59 | key2: 'val3'
60 | }
61 | }
62 | });
63 | });
64 |
65 | test('null input, object root', () => {
66 | const output = transform(null, {
67 | key1: 'val1',
68 | key2: 'val2'
69 | });
70 | expect(output).toBeNull();
71 | });
72 |
73 | test('string input, object root', () => {
74 | const output = transform('$.key1', {
75 | key1: 'val1',
76 | key2: 'val2'
77 | });
78 | expect(output).toEqual('val1');
79 | });
80 |
81 | test('object input, empty root', () => {
82 | const output = transform(
83 | {
84 | a: '$',
85 | b: '$.key1',
86 | c: '$.jabber',
87 | d: 'constant',
88 | e: {
89 | f: 'constant',
90 | g: '$.key1',
91 | h: '$'
92 | }
93 | },
94 | {}
95 | );
96 | expect(output).toEqual({
97 | a: {},
98 | b: undefined,
99 | c: undefined,
100 | d: 'constant',
101 | e: {
102 | f: 'constant',
103 | g: undefined,
104 | h: {}
105 | }
106 | });
107 | });
108 |
109 | test('object(with array key) input and root', () => {
110 | const output = transform(
111 | {
112 | url: 'http://localhost:1000/api/experimental/dags/{{dagId}}/dag_runs',
113 | headers: {
114 | 'Cache-Control': 'no-cache',
115 | 'Content-Type': 'application/json'
116 | },
117 | data: {
118 | conf: {
119 | key: ['arrayVal']
120 | }
121 | },
122 | method: 'post'
123 | },
124 | {
125 | dagId: 'dag1',
126 | data: '$'
127 | }
128 | );
129 | expect(output).toEqual({
130 | url: 'http://localhost:1000/api/experimental/dags/dag1/dag_runs',
131 | headers: {
132 | 'Cache-Control': 'no-cache',
133 | 'Content-Type': 'application/json'
134 | },
135 | data: {
136 | conf: {
137 | key: ['arrayVal']
138 | }
139 | },
140 | method: 'post'
141 | });
142 | });
143 | });
144 |
145 | describe('setOnPath function', () => {
146 | test('default path', () => {
147 | const output = setOnPath({ a: 1, b: 2 }, '$', { c: 3, d: 4 });
148 | expect(output).toEqual({ c: 3, d: 4 });
149 | });
150 |
151 | test('empty object, default path', () => {
152 | const output = setOnPath({ a: 1, b: 2 }, '$', {});
153 | expect(output).toEqual({});
154 | });
155 |
156 | test('empty object', () => {
157 | const output = setOnPath({ a: 1, b: 2 }, '$.c', {});
158 | expect(output).toEqual({ a: 1, b: 2, c: {} });
159 | });
160 |
161 | test('empty root, default path', () => {
162 | const output = setOnPath({}, '$', { c: 3, d: 4 });
163 | expect(output).toEqual({ c: 3, d: 4 });
164 | });
165 |
166 | test('empty root', () => {
167 | const output = setOnPath({}, '$.a', { c: 3, d: 4 });
168 | expect(output).toEqual({ a: { c: 3, d: 4 } });
169 | });
170 |
171 | test('should overwrite key', () => {
172 | const output = setOnPath({ a: 1, b: 2 }, '$.b', { c: 3, d: 4 });
173 | expect(output).toEqual({ a: 1, b: { c: 3, d: 4 } });
174 | });
175 |
176 | test('should write nested key', () => {
177 | const output = setOnPath({ a: 1, b: 2 }, '$.c.d', { c: 3, d: 4 });
178 | expect(output).toEqual({ a: 1, b: 2, c: { d: { c: 3, d: 4 } } });
179 | });
180 |
181 | test('should overwrite nested key', () => {
182 | const output = setOnPath({ a: 1, b: 2, c: { d: 3 } }, '$.c.d', {
183 | c: 3,
184 | d: 4
185 | });
186 | expect(output).toEqual({ a: 1, b: 2, c: { d: { c: 3, d: 4 } } });
187 | });
188 | });
189 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "outDir": "./lib",
5 | "declaration": true,
6 | "declarationMap": true,
7 | "moduleResolution": "node",
8 | "esModuleInterop": true,
9 | "module": "commonjs",
10 | "allowJs": true,
11 | "target": "ES2018"
12 | },
13 | "include": ["./src/**/*"]
14 | }
15 |
--------------------------------------------------------------------------------