jinaga-server 1.1.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/authentication/authentication-device.d.ts +3 -2
- package/dist/authentication/authentication-device.js +6 -0
- package/dist/authentication/authentication-device.js.map +1 -1
- package/dist/authentication/authentication-impl.d.ts +3 -2
- package/dist/authentication/authentication-impl.js +6 -0
- package/dist/authentication/authentication-impl.js.map +1 -1
- package/dist/authentication/authentication-noop.d.ts +20 -0
- package/dist/authentication/authentication-noop.js +52 -0
- package/dist/authentication/authentication-noop.js.map +1 -0
- package/dist/authentication/authentication-session.d.ts +3 -2
- package/dist/authentication/authentication-session.js +6 -0
- package/dist/authentication/authentication-session.js.map +1 -1
- package/dist/authentication.d.ts +3 -2
- package/dist/authentication.js +6 -0
- package/dist/authentication.js.map +1 -1
- package/dist/authorization/authorization-keystore.d.ts +3 -4
- package/dist/authorization/authorization-keystore.js +9 -4
- package/dist/authorization/authorization-keystore.js.map +1 -1
- package/dist/debug.d.ts +1 -0
- package/dist/debug.js +30 -0
- package/dist/debug.js.map +1 -0
- package/dist/http/router.js.map +1 -1
- package/dist/jinaga-server.js +15 -7
- package/dist/jinaga-server.js.map +1 -1
- package/dist/memory/memory-keystore.js +0 -6
- package/dist/memory/memory-keystore.js.map +1 -1
- package/dist/postgres/connection.js +1 -0
- package/dist/postgres/connection.js.map +1 -1
- package/dist/postgres/maps.d.ts +7 -6
- package/dist/postgres/maps.js +9 -1
- package/dist/postgres/maps.js.map +1 -1
- package/dist/postgres/postgres-keystore.js +0 -6
- package/dist/postgres/postgres-keystore.js.map +1 -1
- package/dist/postgres/postgres-store.d.ts +1 -2
- package/dist/postgres/postgres-store.js +29 -24
- package/dist/postgres/postgres-store.js.map +1 -1
- package/dist/postgres/specification-result-sql.d.ts +4 -4
- package/dist/postgres/specification-result-sql.js +34 -24
- package/dist/postgres/specification-result-sql.js.map +1 -1
- package/dist/postgres/specification-sql.js.map +1 -1
- package/dist/postgres/sql.d.ts +1 -1
- package/dist/postgres/sql.js +15 -7
- package/dist/postgres/sql.js.map +1 -1
- package/dist/util/maps.d.ts +0 -0
- package/dist/util/maps.js +2 -0
- package/dist/util/maps.js.map +1 -0
- package/docs/sql/SQL Generator (projection).png +0 -0
- package/docs/sql/SQLFromSpecification.md +2 -0
- package/integration-test/jinaga-test/Dockerfile +2 -2
- package/integration-test/jinaga-test/jinaga.test.js +3 -3
- package/integration-test/jinaga-test/package-lock.json +745 -3484
- package/integration-test/jinaga-test/package.json +2 -0
- package/integration-test/run.sh +3 -3
- package/package.json +5 -6
- package/replicator/index.ts +31 -0
- package/replicator/package-lock.json +855 -0
- package/replicator/package.json +27 -0
- package/replicator/tsconfig.json +15 -0
- package/src/authentication/authentication-device.ts +11 -1
- package/src/authentication/authentication-impl.ts +9 -2
- package/src/authentication/authentication-noop.ts +65 -0
- package/src/authentication/authentication-session.ts +11 -1
- package/src/authentication.ts +12 -2
- package/src/authorization/authorization-keystore.ts +12 -7
- package/src/debug.ts +30 -0
- package/src/http/router.ts +2 -2
- package/src/jinaga-server.ts +18 -8
- package/src/memory/memory-keystore.ts +0 -6
- package/src/postgres/connection.ts +2 -1
- package/src/postgres/maps.ts +8 -0
- package/src/postgres/postgres-keystore.ts +2 -8
- package/src/postgres/postgres-store.ts +37 -30
- package/src/postgres/specification-result-sql.ts +56 -45
- package/src/postgres/specification-sql.ts +1 -1
- package/src/postgres/sql.ts +23 -12
- package/src/util/maps.ts +0 -0
- package/test/postgres/postgresReadSpec.ts +4 -1
- package/test/postgres/postgresSpec.ts +31 -31
- package/test/postgres/postgresSpecificationSpec.ts +1 -1
- package/tsconfig.json +1 -0
- package/tsconfig.test.json +26 -0
- package/docs/specification.txt +0 -41
- package/interactive/index.js +0 -56
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Step,
|
|
11
11
|
} from "jinaga";
|
|
12
12
|
|
|
13
|
-
import { addFactType, addRole, emptyFactTypeMap, emptyRoleMap, getFactTypeId, getRoleId } from "../../src/postgres/maps";
|
|
13
|
+
import { addFactType, addRole, emptyFactTypeMap, emptyRoleMap, ensureGetFactTypeId, getFactTypeId, getRoleId } from "../../src/postgres/maps";
|
|
14
14
|
import { sqlFromSteps } from "../../src/postgres/sql";
|
|
15
15
|
import { distinct } from "../../src/util/fn";
|
|
16
16
|
|
|
@@ -24,8 +24,8 @@ describe('Postgres', () => {
|
|
|
24
24
|
const factTypes = allFactTypes(query.steps).filter(t => t !== 'Unknown').reduce(
|
|
25
25
|
(f, factType, i) => addFactType(f, factType, i + 1),
|
|
26
26
|
emptyFactTypeMap());
|
|
27
|
-
let roleMap = allRoles(query.steps, 'Root').filter(r => r.role !== 'unknown').reduce(
|
|
28
|
-
(r, role, i) => addRole(r,
|
|
27
|
+
let roleMap = allRoles(query.steps, 'Root').filter(r => r.type !== 'Unknown' && r.role !== 'unknown').reduce(
|
|
28
|
+
(r, role, i) => addRole(r, ensureGetFactTypeId(factTypes, role.type), role.role, i + 1),
|
|
29
29
|
emptyRoleMap());
|
|
30
30
|
const sqlQuery = sqlFromSteps(start, query.steps, factTypes, roleMap);
|
|
31
31
|
return sqlQuery ? { sql: sqlQuery.sql, parameters: sqlQuery.parameters, pathLength: sqlQuery.pathLength, empty: sqlQuery.empty, factTypes, roleMap } : null;
|
|
@@ -61,7 +61,7 @@ describe('Postgres', () => {
|
|
|
61
61
|
);
|
|
62
62
|
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
63
63
|
expect(parameters[1]).toEqual(startHash);
|
|
64
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap,
|
|
64
|
+
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Successor'), 'predecessor'));
|
|
65
65
|
expect(pathLength).toEqual(1);
|
|
66
66
|
});
|
|
67
67
|
|
|
@@ -76,7 +76,7 @@ describe('Postgres', () => {
|
|
|
76
76
|
);
|
|
77
77
|
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
78
78
|
expect(parameters[1]).toEqual(startHash);
|
|
79
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap,
|
|
79
|
+
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Root'), 'parent'));
|
|
80
80
|
expect(pathLength).toEqual(1);
|
|
81
81
|
});
|
|
82
82
|
|
|
@@ -94,8 +94,8 @@ describe('Postgres', () => {
|
|
|
94
94
|
);
|
|
95
95
|
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
96
96
|
expect(parameters[1]).toEqual(startHash);
|
|
97
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap,
|
|
98
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap,
|
|
97
|
+
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Successor'), 'predecessor'));
|
|
98
|
+
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Grandchild'), 'successor'));
|
|
99
99
|
expect(pathLength).toEqual(1);
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -113,8 +113,8 @@ describe('Postgres', () => {
|
|
|
113
113
|
);
|
|
114
114
|
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
115
115
|
expect(parameters[1]).toEqual(startHash);
|
|
116
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap,
|
|
117
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap,
|
|
116
|
+
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Successor'), 'predecessor'));
|
|
117
|
+
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'IntegrationTest.Grandchild'), 'successor'));
|
|
118
118
|
expect(pathLength).toEqual(1);
|
|
119
119
|
});
|
|
120
120
|
|
|
@@ -132,8 +132,8 @@ describe('Postgres', () => {
|
|
|
132
132
|
);
|
|
133
133
|
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
134
134
|
expect(parameters[1]).toEqual(startHash);
|
|
135
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap,
|
|
136
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap,
|
|
135
|
+
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'parent'));
|
|
136
|
+
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'uncle'));
|
|
137
137
|
expect(pathLength).toEqual(1);
|
|
138
138
|
});
|
|
139
139
|
|
|
@@ -151,8 +151,8 @@ describe('Postgres', () => {
|
|
|
151
151
|
);
|
|
152
152
|
expect(parameters[0]).toEqual(getFactTypeId(factTypes, 'Root'));
|
|
153
153
|
expect(parameters[1]).toEqual(startHash);
|
|
154
|
-
expect(parameters[2]).toEqual(getRoleId(roleMap,
|
|
155
|
-
expect(parameters[3]).toEqual(getRoleId(roleMap,
|
|
154
|
+
expect(parameters[2]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'parent'));
|
|
155
|
+
expect(parameters[3]).toEqual(getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'uncle'));
|
|
156
156
|
expect(pathLength).toEqual(1);
|
|
157
157
|
});
|
|
158
158
|
|
|
@@ -174,9 +174,9 @@ describe('Postgres', () => {
|
|
|
174
174
|
expect(parameters).toEqual([
|
|
175
175
|
getFactTypeId(factTypes, 'Root'),
|
|
176
176
|
startHash,
|
|
177
|
-
getRoleId(roleMap,
|
|
178
|
-
getRoleId(roleMap,
|
|
179
|
-
getRoleId(roleMap,
|
|
177
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Child'), 'parent'),
|
|
178
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Condition'), 'condition'),
|
|
179
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Other'), 'other')
|
|
180
180
|
]);
|
|
181
181
|
});
|
|
182
182
|
|
|
@@ -200,10 +200,10 @@ describe('Postgres', () => {
|
|
|
200
200
|
expect(parameters).toEqual([
|
|
201
201
|
getFactTypeId(factTypes, 'Root'),
|
|
202
202
|
startHash,
|
|
203
|
-
getRoleId(roleMap,
|
|
204
|
-
getRoleId(roleMap,
|
|
205
|
-
getRoleId(roleMap,
|
|
206
|
-
getRoleId(roleMap,
|
|
203
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Identifier'), 'root'),
|
|
204
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Identifier'), 'prior'),
|
|
205
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Identifier'), 'identified'),
|
|
206
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Delete'), 'identified')
|
|
207
207
|
]);
|
|
208
208
|
});
|
|
209
209
|
|
|
@@ -230,10 +230,10 @@ describe('Postgres', () => {
|
|
|
230
230
|
expect(parameters).toEqual([
|
|
231
231
|
getFactTypeId(factTypes, 'Root'),
|
|
232
232
|
startHash,
|
|
233
|
-
getRoleId(roleMap,
|
|
234
|
-
getRoleId(roleMap,
|
|
235
|
-
getRoleId(roleMap,
|
|
236
|
-
getRoleId(roleMap,
|
|
233
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'user'),
|
|
234
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'project'),
|
|
235
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Task'), 'project'),
|
|
236
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Task.Title'), 'task')
|
|
237
237
|
]);
|
|
238
238
|
expect(pathLength).toEqual(4);
|
|
239
239
|
});
|
|
@@ -281,7 +281,7 @@ describe('Postgres', () => {
|
|
|
281
281
|
expect(parameters).toEqual([
|
|
282
282
|
getFactTypeId(factTypes, 'Root'),
|
|
283
283
|
startHash,
|
|
284
|
-
getRoleId(roleMap,
|
|
284
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'root')
|
|
285
285
|
]);
|
|
286
286
|
});
|
|
287
287
|
|
|
@@ -298,7 +298,7 @@ describe('Postgres', () => {
|
|
|
298
298
|
expect(parameters).toEqual([
|
|
299
299
|
getFactTypeId(factTypes, 'Root'),
|
|
300
300
|
startHash,
|
|
301
|
-
getRoleId(roleMap,
|
|
301
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'Assignment'), 'root')
|
|
302
302
|
]);
|
|
303
303
|
});
|
|
304
304
|
|
|
@@ -332,11 +332,11 @@ describe('Postgres', () => {
|
|
|
332
332
|
')' +
|
|
333
333
|
')');
|
|
334
334
|
expect(parameters).toEqual([
|
|
335
|
-
getFactTypeId(factTypes, '
|
|
335
|
+
getFactTypeId(factTypes, 'Root'),
|
|
336
336
|
startHash,
|
|
337
|
-
getRoleId(roleMap,
|
|
338
|
-
getRoleId(roleMap,
|
|
339
|
-
getRoleId(roleMap,
|
|
337
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'ImprovingU.Idea'), 'catalog'),
|
|
338
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'ImprovingU.Idea.Deletion'), 'idea'),
|
|
339
|
+
getRoleId(roleMap, ensureGetFactTypeId(factTypes, 'ImprovingU.Idea.Restore'), 'ideaDeletion')
|
|
340
340
|
]);
|
|
341
341
|
});
|
|
342
342
|
});
|
|
@@ -348,7 +348,7 @@ function allFactTypes(steps: Step[]): string[] {
|
|
|
348
348
|
const childFactTypes = steps
|
|
349
349
|
.filter(step => step instanceof ExistentialCondition)
|
|
350
350
|
.flatMap(step => allFactTypes((step as ExistentialCondition).steps));
|
|
351
|
-
return [...factTypes, ...childFactTypes].filter(distinct);
|
|
351
|
+
return ["Root", ...factTypes, ...childFactTypes].filter(distinct);
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
function allRoles(steps: Step[], initialType: string) {
|
|
@@ -28,7 +28,7 @@ function sqlFor(descriptiveString: string, bookmarks: string[] = []) {
|
|
|
28
28
|
// Filter out the roles named "unknown", and those of unknown fact types.
|
|
29
29
|
let roleMap = getAllRoles(specification).filter(r => r.name !== 'unknown').reduce(
|
|
30
30
|
(r, role, i) => {
|
|
31
|
-
const factTypeId = getFactTypeId(factTypes, role.
|
|
31
|
+
const factTypeId = getFactTypeId(factTypes, role.successorType);
|
|
32
32
|
if (!factTypeId) {
|
|
33
33
|
return r;
|
|
34
34
|
}
|
package/tsconfig.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "es6",
|
|
5
|
+
"lib": [ "ES2019" ],
|
|
6
|
+
"noImplicitAny": true,
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"esModuleInterop": true,
|
|
15
|
+
"baseUrl": ".",
|
|
16
|
+
"paths": {
|
|
17
|
+
"*": [
|
|
18
|
+
"./node_modules/*",
|
|
19
|
+
"./types/*"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"include": [
|
|
24
|
+
"test/**/*.ts"
|
|
25
|
+
]
|
|
26
|
+
}
|
package/docs/specification.txt
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
let user: Jinaga.User = me
|
|
2
|
-
let semester: ImprovingU.Semester = #Ws6RWV8bWmP7ibBlzwL2UoKKwg6E1iItQPFpX/mvU5u6FLwHJAbRd0SbH+P1hoMn4QndhArLqJqVReRqJdoyBg==
|
|
3
|
-
|
|
4
|
-
(semester: ImprovingU.Semester, user: Jinaga.User) {
|
|
5
|
-
catalog: ImprovingU.Catalog [
|
|
6
|
-
catalog->_in:ImprovingU.Semester = semester
|
|
7
|
-
]
|
|
8
|
-
} => {
|
|
9
|
-
office = catalog.office
|
|
10
|
-
courses = {
|
|
11
|
-
course: ImprovingU.Course [
|
|
12
|
-
course->_in:ImprovingU.Catalog = catalog
|
|
13
|
-
!E {
|
|
14
|
-
delete: ImprovingU.Course.Delete [
|
|
15
|
-
delete->course:ImprovingU.Course = course
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
} => {
|
|
20
|
-
titles = {
|
|
21
|
-
title: ImprovingU.Course.Title [
|
|
22
|
-
title->course: ImprovingU.Course = course
|
|
23
|
-
!E {
|
|
24
|
-
next: ImprovingU.Course.Title [
|
|
25
|
-
next->prior: ImprovingU.Course.Title = title
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
} => title.value
|
|
30
|
-
instructors = {
|
|
31
|
-
instructor: ImprovingU.Course.Instructor [
|
|
32
|
-
instructor->course: ImprovingU.Course = course
|
|
33
|
-
!E {
|
|
34
|
-
next: ImprovingU.Course.Instructor [
|
|
35
|
-
next->prior: ImprovingU.Course.Instructor = instructor
|
|
36
|
-
]
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
} => instructor.value
|
|
40
|
-
}
|
|
41
|
-
}
|
package/interactive/index.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const { SpecificationParser } = require("../dist/specification/specification-parser");
|
|
2
|
-
const { dehydrateFact } = require("../dist/fact/hydrate");
|
|
3
|
-
const { PostgresStore } = require("../dist/postgres/postgres-store")
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
|
|
6
|
-
var user = {"publicKey":"-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAIBsKomutukULWw2zoTW2ECMrM8VmD2xvfpl3R4qh1whzuXV+A4EfRKMb/UAjEfw\n5nBmWvcObGyYUgygKrlNeOhf3MnDj706rej6ln9cKGL++ZNsJgJsogaAtmkPihWVGi908fdP\nLQrWTF5be0b/ZP258Zs3CTpcRTpTvhzS5TC1AgMBAAE=\n-----END RSA PUBLIC KEY-----\n","type":"Jinaga.User"};
|
|
7
|
-
|
|
8
|
-
async function run() {
|
|
9
|
-
try {
|
|
10
|
-
var postgresStore = new PostgresStore("postgresql://dev:devpw@localhost:5432/improvingu");
|
|
11
|
-
try {
|
|
12
|
-
var input = fs.readFileSync(0, 'utf-8');
|
|
13
|
-
const parser = new SpecificationParser(input);
|
|
14
|
-
parser.skipWhitespace();
|
|
15
|
-
const userRecord = dehydrateFact(user)[0];
|
|
16
|
-
const knownFacts = {
|
|
17
|
-
me: {
|
|
18
|
-
fact: userRecord,
|
|
19
|
-
reference: {
|
|
20
|
-
type: userRecord.type,
|
|
21
|
-
hash: userRecord.hash
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
var declaration = parser.parseDeclaration(knownFacts);
|
|
26
|
-
var specification = parser.parseSpecification();
|
|
27
|
-
|
|
28
|
-
// Select starting facts that match the inputs
|
|
29
|
-
const start = specification.given.map(input => {
|
|
30
|
-
const fact = declaration[input.name];
|
|
31
|
-
if (!fact) {
|
|
32
|
-
throw new Error(`No fact named ${input.name} was declared`);
|
|
33
|
-
}
|
|
34
|
-
return fact.reference;
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const args = process.argv.slice(2);
|
|
38
|
-
const produceResults = args.includes("--results");
|
|
39
|
-
if (produceResults) {
|
|
40
|
-
const results = await postgresStore.resultsFromSpecification(start, specification);
|
|
41
|
-
console.log(JSON.stringify(results, null, 2));
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
const streams = await postgresStore.streamsFromSpecification(start, [], 3, specification);
|
|
45
|
-
console.log(JSON.stringify(streams, null, 2));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
finally {
|
|
49
|
-
postgresStore.close();
|
|
50
|
-
}
|
|
51
|
-
} catch (e) {
|
|
52
|
-
console.error(e);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
run();
|