jinaga-server 1.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.
Files changed (127) hide show
  1. package/LICENSE +22 -0
  2. package/dist/authentication/authentication-device.d.ts +20 -0
  3. package/dist/authentication/authentication-device.js +61 -0
  4. package/dist/authentication/authentication-device.js.map +1 -0
  5. package/dist/authentication/authentication-impl.d.ts +21 -0
  6. package/dist/authentication/authentication-impl.js +61 -0
  7. package/dist/authentication/authentication-impl.js.map +1 -0
  8. package/dist/authentication/authentication-session.d.ts +23 -0
  9. package/dist/authentication/authentication-session.js +85 -0
  10. package/dist/authentication/authentication-session.js.map +1 -0
  11. package/dist/authentication/authentication.d.ts +5 -0
  12. package/dist/authentication/authentication.js +3 -0
  13. package/dist/authentication/authentication.js.map +1 -0
  14. package/dist/authentication.d.ts +17 -0
  15. package/dist/authentication.js +52 -0
  16. package/dist/authentication.js.map +1 -0
  17. package/dist/authorization/authorization-keystore.d.ts +13 -0
  18. package/dist/authorization/authorization-keystore.js +61 -0
  19. package/dist/authorization/authorization-keystore.js.map +1 -0
  20. package/dist/http/node-http.d.ts +7 -0
  21. package/dist/http/node-http.js +126 -0
  22. package/dist/http/node-http.js.map +1 -0
  23. package/dist/http/router.d.ts +20 -0
  24. package/dist/http/router.js +223 -0
  25. package/dist/http/router.js.map +1 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +6 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/jinaga-server.d.ts +18 -0
  30. package/dist/jinaga-server.js +101 -0
  31. package/dist/jinaga-server.js.map +1 -0
  32. package/dist/keystore.d.ts +9 -0
  33. package/dist/keystore.js +3 -0
  34. package/dist/keystore.js.map +1 -0
  35. package/dist/memory/memory-keystore.d.ts +16 -0
  36. package/dist/memory/memory-keystore.js +86 -0
  37. package/dist/memory/memory-keystore.js.map +1 -0
  38. package/dist/postgres/connection.d.ts +12 -0
  39. package/dist/postgres/connection.js +83 -0
  40. package/dist/postgres/connection.js.map +1 -0
  41. package/dist/postgres/edge-record.d.ts +9 -0
  42. package/dist/postgres/edge-record.js +30 -0
  43. package/dist/postgres/edge-record.js.map +1 -0
  44. package/dist/postgres/maps.d.ts +21 -0
  45. package/dist/postgres/maps.js +86 -0
  46. package/dist/postgres/maps.js.map +1 -0
  47. package/dist/postgres/postgres-keystore.d.ts +20 -0
  48. package/dist/postgres/postgres-keystore.js +168 -0
  49. package/dist/postgres/postgres-keystore.js.map +1 -0
  50. package/dist/postgres/postgres-store.d.ts +20 -0
  51. package/dist/postgres/postgres-store.js +717 -0
  52. package/dist/postgres/postgres-store.js.map +1 -0
  53. package/dist/postgres/query-description-builder.d.ts +13 -0
  54. package/dist/postgres/query-description-builder.js +104 -0
  55. package/dist/postgres/query-description-builder.js.map +1 -0
  56. package/dist/postgres/query-description.d.ts +71 -0
  57. package/dist/postgres/query-description.js +256 -0
  58. package/dist/postgres/query-description.js.map +1 -0
  59. package/dist/postgres/specification-result-sql.d.ts +41 -0
  60. package/dist/postgres/specification-result-sql.js +260 -0
  61. package/dist/postgres/specification-result-sql.js.map +1 -0
  62. package/dist/postgres/specification-sql.d.ts +3 -0
  63. package/dist/postgres/specification-sql.js +119 -0
  64. package/dist/postgres/specification-sql.js.map +1 -0
  65. package/dist/postgres/sql.d.ts +10 -0
  66. package/dist/postgres/sql.js +396 -0
  67. package/dist/postgres/sql.js.map +1 -0
  68. package/dist/util/fn.d.ts +6 -0
  69. package/dist/util/fn.js +81 -0
  70. package/dist/util/fn.js.map +1 -0
  71. package/dist/util/promise.d.ts +1 -0
  72. package/dist/util/promise.js +10 -0
  73. package/dist/util/promise.js.map +1 -0
  74. package/docs/registrations.sql +22 -0
  75. package/docs/specification.txt +41 -0
  76. package/docs/sql-generator.gv +25 -0
  77. package/integration-test/docker-compose.yaml +13 -0
  78. package/integration-test/jinaga-test/Dockerfile +12 -0
  79. package/integration-test/jinaga-test/jinaga.test.js +425 -0
  80. package/integration-test/jinaga-test/package-lock.json +6767 -0
  81. package/integration-test/jinaga-test/package.json +25 -0
  82. package/integration-test/postgres/0-init-app-db.sh +16 -0
  83. package/integration-test/postgres/1-setup.sql +219 -0
  84. package/integration-test/postgres/2-grant.sh +17 -0
  85. package/integration-test/postgres/Dockerfile +5 -0
  86. package/integration-test/run.sh +17 -0
  87. package/integration-test/start_db.sh +2 -0
  88. package/integration-test/stop_db.sh +3 -0
  89. package/integration-test/test.sh +8 -0
  90. package/interactive/index.js +56 -0
  91. package/jest.config.js +12 -0
  92. package/package.json +51 -0
  93. package/scripts/combined.sql +46 -0
  94. package/scripts/cte.sql +55 -0
  95. package/scripts/separate.sql +27 -0
  96. package/setup.sh +34 -0
  97. package/setup.sql +329 -0
  98. package/src/authentication/authentication-device.ts +68 -0
  99. package/src/authentication/authentication-impl.ts +59 -0
  100. package/src/authentication/authentication-session.ts +95 -0
  101. package/src/authentication/authentication.ts +6 -0
  102. package/src/authentication.ts +45 -0
  103. package/src/authorization/authorization-keystore.ts +67 -0
  104. package/src/http/node-http.ts +106 -0
  105. package/src/http/router.ts +237 -0
  106. package/src/index.ts +1 -0
  107. package/src/jinaga-server.ts +125 -0
  108. package/src/keystore.ts +10 -0
  109. package/src/memory/memory-keystore.ts +93 -0
  110. package/src/postgres/connection.ts +73 -0
  111. package/src/postgres/edge-record.ts +36 -0
  112. package/src/postgres/maps.ts +89 -0
  113. package/src/postgres/postgres-keystore.ts +167 -0
  114. package/src/postgres/postgres-store.ts +874 -0
  115. package/src/postgres/query-description-builder.ts +116 -0
  116. package/src/postgres/query-description.ts +385 -0
  117. package/src/postgres/specification-result-sql.ts +362 -0
  118. package/src/postgres/specification-sql.ts +138 -0
  119. package/src/postgres/sql.ts +482 -0
  120. package/src/util/fn.ts +63 -0
  121. package/src/util/promise.ts +5 -0
  122. package/test/authorization/authorizationSpec.ts +209 -0
  123. package/test/postgres/postgresSpec.ts +385 -0
  124. package/test/postgres/postgresSpecificationSpec.ts +742 -0
  125. package/test/reporter.js +25 -0
  126. package/tsconfig.json +25 -0
  127. package/upgrade.sh +21 -0
@@ -0,0 +1,717 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PostgresStore = void 0;
13
+ const jinaga_1 = require("jinaga");
14
+ const fn_1 = require("../util/fn");
15
+ const connection_1 = require("./connection");
16
+ const edge_record_1 = require("./edge-record");
17
+ const maps_1 = require("./maps");
18
+ const specification_result_sql_1 = require("./specification-result-sql");
19
+ const specification_sql_1 = require("./specification-sql");
20
+ const sql_1 = require("./sql");
21
+ function loadFactReference(r) {
22
+ return {
23
+ type: r.type,
24
+ hash: r.hash
25
+ };
26
+ }
27
+ function loadFactTuple(labels, row) {
28
+ const facts = labels.map(label => {
29
+ const hashColumn = `hash${label.index}`;
30
+ const hash = row[hashColumn];
31
+ if (hash === null) {
32
+ const columns = Object.keys(row);
33
+ throw new Error(`Cannot find column '${hashColumn}'. Available columns: ${columns.join(', ')}`);
34
+ }
35
+ const fact = {
36
+ type: label.type,
37
+ hash
38
+ };
39
+ return fact;
40
+ });
41
+ const bookmark = row.bookmark;
42
+ return {
43
+ facts,
44
+ bookmark: bookmark.join(".")
45
+ };
46
+ }
47
+ function loadFactPath(pathLength, factTypeNames, r) {
48
+ let path = [];
49
+ for (let i = 0; i < pathLength; i++) {
50
+ const hash = r['hash' + (i + 2)];
51
+ if (!hash) {
52
+ throw new Error(`Cannot find column 'hash${i + 2}'`);
53
+ }
54
+ path.push({
55
+ type: factTypeNames[i],
56
+ hash: hash
57
+ });
58
+ }
59
+ return path;
60
+ }
61
+ class PostgresStore {
62
+ constructor(postgresUri) {
63
+ this.factTypeMap = (0, maps_1.emptyFactTypeMap)();
64
+ this.roleMap = (0, maps_1.emptyRoleMap)();
65
+ this.connectionFactory = new connection_1.ConnectionFactory(postgresUri);
66
+ }
67
+ close() {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ yield this.connectionFactory.close();
70
+ });
71
+ }
72
+ save(envelopes) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ if (envelopes.length > 0) {
75
+ const facts = envelopes.map(e => e.fact);
76
+ if (facts.some(f => !f.hash || !f.type)) {
77
+ throw new Error('Attempted to save a fact with no hash or type.');
78
+ }
79
+ const { newEnvelopes, factTypes, roles } = yield this.connectionFactory.withTransaction((connection) => __awaiter(this, void 0, void 0, function* () {
80
+ const factTypes = yield storeFactTypes(facts, this.factTypeMap, connection);
81
+ const existingFacts = yield findExistingFacts(facts, factTypes, connection);
82
+ const newFacts = facts.filter(f => !(0, maps_1.hasFact)(existingFacts, f.hash, factTypes.get(f.type)));
83
+ if (newFacts.length === 0) {
84
+ return {
85
+ newEnvelopes: [],
86
+ factTypes,
87
+ roles: (0, maps_1.emptyRoleMap)()
88
+ };
89
+ }
90
+ const roles = yield storeRoles(newFacts, factTypes, (0, maps_1.copyRoleMap)(this.roleMap), connection);
91
+ const allFacts = yield insertFactsEdgesAndAncestors(newFacts, factTypes, existingFacts, connection, roles);
92
+ const newEnvelopes = envelopes.filter(envelope => newFacts.some((0, jinaga_1.factReferenceEquals)(envelope.fact)));
93
+ if (newEnvelopes.length === 0) {
94
+ return {
95
+ newEnvelopes,
96
+ factTypes,
97
+ roles
98
+ };
99
+ }
100
+ const publicKeys = yield storePublicKeys(newEnvelopes, connection);
101
+ yield insertSignatures(newEnvelopes, allFacts, factTypes, publicKeys, connection);
102
+ return {
103
+ newEnvelopes,
104
+ factTypes,
105
+ roles
106
+ };
107
+ }));
108
+ this.factTypeMap = (0, maps_1.mergeFactTypes)(this.factTypeMap, factTypes);
109
+ this.roleMap = (0, maps_1.mergeRoleMaps)(this.roleMap, roles);
110
+ return newEnvelopes;
111
+ }
112
+ else {
113
+ return [];
114
+ }
115
+ });
116
+ }
117
+ query(start, query) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ if (query.steps.length === 0) {
120
+ return [[start]];
121
+ }
122
+ try {
123
+ const factTypes = yield this.loadFactTypesFromSteps(query.steps, start.type);
124
+ const roleMap = yield this.loadRolesFromSteps(query.steps, factTypes, start.type);
125
+ const sqlQuery = (0, sql_1.sqlFromSteps)(start, query.steps, factTypes, roleMap);
126
+ if (!sqlQuery) {
127
+ throw new Error(`Could not generate SQL for query "${query.toDescriptiveString()}" starting at "${start.type}"`);
128
+ }
129
+ if (sqlQuery.empty) {
130
+ return [];
131
+ }
132
+ const { rows } = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
133
+ return yield connection.query(sqlQuery.sql, sqlQuery.parameters);
134
+ }));
135
+ return rows.map(row => loadFactPath(sqlQuery.pathLength, sqlQuery.factTypeNames, row));
136
+ }
137
+ catch (e) {
138
+ throw new Error(`Could not generate SQL for query "${query.toDescriptiveString()}" starting at "${start.type}": ${e}`);
139
+ }
140
+ });
141
+ }
142
+ read(start, specification) {
143
+ return __awaiter(this, void 0, void 0, function* () {
144
+ const factTypes = yield this.loadFactTypesFromSpecification(specification);
145
+ const roleMap = yield this.loadRolesFromSpecification(specification, factTypes);
146
+ const composer = (0, specification_result_sql_1.resultSqlFromSpecification)(start, specification, factTypes, roleMap);
147
+ if (composer === null) {
148
+ return [];
149
+ }
150
+ const sqlQueryTree = composer.getSqlQueries();
151
+ const resultSets = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
152
+ return yield executeQueryTree(sqlQueryTree, connection);
153
+ }));
154
+ return composer.compose(resultSets);
155
+ });
156
+ }
157
+ streamsFromSpecification(start, bookmarks, limit, specification) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ const factTypes = yield this.loadFactTypesFromSpecification(specification);
160
+ const roleMap = yield this.loadRolesFromSpecification(specification, factTypes);
161
+ const sqlQueries = (0, specification_sql_1.sqlFromSpecification)(start, bookmarks, limit, specification, factTypes, roleMap);
162
+ const streams = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
163
+ const streams = [];
164
+ for (const sqlQuery of sqlQueries) {
165
+ try {
166
+ const { rows } = yield connection.query(sqlQuery.sql, sqlQuery.parameters);
167
+ const tuples = rows.map(row => loadFactTuple(sqlQuery.labels, row));
168
+ streams.push({
169
+ labels: sqlQuery.labels.map(l => l.name),
170
+ tuples,
171
+ bookmark: tuples.length > 0 ? tuples[tuples.length - 1].bookmark : sqlQuery.bookmark
172
+ });
173
+ }
174
+ catch (error) {
175
+ throw new Error(`Could not execute query "${sqlQuery.sql}", parameters ${sqlQuery.parameters}:\n${error}`);
176
+ }
177
+ }
178
+ return streams;
179
+ }));
180
+ return streams;
181
+ });
182
+ }
183
+ loadFactTypesFromSteps(steps, startType) {
184
+ return __awaiter(this, void 0, void 0, function* () {
185
+ const factTypes = this.factTypeMap;
186
+ const unknownFactTypes = [...allFactTypes(steps), startType]
187
+ .filter(factType => !factTypes.has(factType))
188
+ .filter(fn_1.distinct);
189
+ if (unknownFactTypes.length > 0) {
190
+ const loadedFactTypes = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
191
+ return yield loadFactTypes(unknownFactTypes, connection);
192
+ }));
193
+ const merged = (0, maps_1.mergeFactTypes)(this.factTypeMap, loadedFactTypes);
194
+ this.factTypeMap = merged;
195
+ return merged;
196
+ }
197
+ return factTypes;
198
+ });
199
+ }
200
+ loadFactTypesFromSpecification(specification) {
201
+ return __awaiter(this, void 0, void 0, function* () {
202
+ const factTypes = this.factTypeMap;
203
+ const unknownFactTypes = (0, jinaga_1.getAllFactTypes)(specification)
204
+ .filter(factType => !factTypes.has(factType));
205
+ if (unknownFactTypes.length > 0) {
206
+ const loadedFactTypes = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
207
+ return yield loadFactTypes(unknownFactTypes, connection);
208
+ }));
209
+ const merged = (0, maps_1.mergeFactTypes)(this.factTypeMap, loadedFactTypes);
210
+ this.factTypeMap = merged;
211
+ return merged;
212
+ }
213
+ return factTypes;
214
+ });
215
+ }
216
+ loadRolesFromSteps(steps, factTypes, initialType) {
217
+ return __awaiter(this, void 0, void 0, function* () {
218
+ const roleMap = this.roleMap;
219
+ const unknownRoles = allRoles(steps, factTypes, (0, maps_1.getFactTypeId)(factTypes, initialType))
220
+ .filter(r => !(0, maps_1.hasRole)(roleMap, r.defining_fact_type_id, r.role));
221
+ if (unknownRoles.length > 0) {
222
+ const loadedRoles = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
223
+ return yield loadRoles(unknownRoles, roleMap, connection);
224
+ }));
225
+ const merged = (0, maps_1.mergeRoleMaps)(this.roleMap, loadedRoles);
226
+ this.roleMap = merged;
227
+ return merged;
228
+ }
229
+ return roleMap;
230
+ });
231
+ }
232
+ loadRolesFromSpecification(specification, factTypes) {
233
+ return __awaiter(this, void 0, void 0, function* () {
234
+ const roleMap = this.roleMap;
235
+ const unknownRoles = (0, jinaga_1.getAllRoles)(specification)
236
+ .map(r => ({
237
+ defining_fact_type_id: (0, maps_1.getFactTypeId)(factTypes, r.definingFactType),
238
+ role: r.name
239
+ }))
240
+ .filter(r => !(0, maps_1.hasRole)(roleMap, r.defining_fact_type_id, r.role));
241
+ if (unknownRoles.length > 0) {
242
+ const loadedRoles = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
243
+ return yield loadRoles(unknownRoles, roleMap, connection);
244
+ }));
245
+ const merged = (0, maps_1.mergeRoleMaps)(this.roleMap, loadedRoles);
246
+ this.roleMap = merged;
247
+ return merged;
248
+ }
249
+ return roleMap;
250
+ });
251
+ }
252
+ whichExist(references) {
253
+ return __awaiter(this, void 0, void 0, function* () {
254
+ if (references.length === 0) {
255
+ return [];
256
+ }
257
+ const factTypes = yield this.loadFactTypesFromReferences(references);
258
+ const factValues = references.map((f, i) => `(\$${i * 2 + 1}, \$${i * 2 + 2}::integer)`);
259
+ const factParameters = (0, fn_1.flatten)(references, (f) => [f.hash, factTypes.get(f.type)]);
260
+ const sql = 'SELECT f.fact_type_id, f.hash ' +
261
+ 'FROM public.fact f ' +
262
+ 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
263
+ ' ON v.fact_type_id = f.fact_type_id AND v.hash = f.hash ';
264
+ const result = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
265
+ return yield connection.query(sql, factParameters);
266
+ }));
267
+ const existing = references.filter(r => result.rows.some(row => row.fact_type_id === factTypes.get(r.type) && row.hash === r.hash));
268
+ return existing;
269
+ });
270
+ }
271
+ load(references) {
272
+ return __awaiter(this, void 0, void 0, function* () {
273
+ if (references.length === 0) {
274
+ return [];
275
+ }
276
+ const factTypes = yield this.loadFactTypesFromReferences(references);
277
+ const factValues = references.map((f, i) => `(\$${i * 2 + 1}, \$${i * 2 + 2}::integer)`);
278
+ const factParameters = (0, fn_1.flatten)(references, (f) => [f.hash, factTypes.get(f.type)]);
279
+ const sql = 'SELECT f.fact_type_id, t.name, f.hash, f.data ' +
280
+ 'FROM public.fact f ' +
281
+ 'JOIN public.fact_type t ' +
282
+ ' ON f.fact_type_id = t.fact_type_id ' +
283
+ 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
284
+ ' ON v.fact_type_id = f.fact_type_id AND v.hash = f.hash ' +
285
+ 'UNION ' +
286
+ 'SELECT f2.fact_type_id, t.name, f2.hash, f2.data ' +
287
+ 'FROM public.fact f1 ' +
288
+ 'JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id) ' +
289
+ ' ON v.fact_type_id = f1.fact_type_id AND v.hash = f1.hash ' +
290
+ 'JOIN public.ancestor a ' +
291
+ ' ON a.fact_id = f1.fact_id ' +
292
+ 'JOIN public.fact f2 ' +
293
+ ' ON f2.fact_id = a.ancestor_fact_id ' +
294
+ 'JOIN public.fact_type t ' +
295
+ ' ON t.fact_type_id = f2.fact_type_id;';
296
+ const result = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
297
+ return yield connection.query(sql, factParameters);
298
+ }));
299
+ const resultFactTypes = result.rows.reduce((factTypes, r) => (0, maps_1.addFactType)(factTypes, r.name, r.fact_type_id), (0, maps_1.emptyFactTypeMap)());
300
+ this.factTypeMap = (0, maps_1.mergeFactTypes)(this.factTypeMap, resultFactTypes);
301
+ return result.rows.map((r) => {
302
+ const { fields, predecessors } = r.data;
303
+ return {
304
+ type: r.name,
305
+ hash: r.hash,
306
+ fields,
307
+ predecessors
308
+ };
309
+ });
310
+ });
311
+ }
312
+ loadFactTypesFromReferences(references) {
313
+ return __awaiter(this, void 0, void 0, function* () {
314
+ const factTypes = this.factTypeMap;
315
+ const newFactTypes = references
316
+ .map(reference => reference.type)
317
+ .filter(type => !factTypes.has(type))
318
+ .filter(fn_1.distinct);
319
+ if (newFactTypes.length > 0) {
320
+ const loadedFactTypes = yield this.connectionFactory.with((connection) => __awaiter(this, void 0, void 0, function* () {
321
+ return yield loadFactTypes(newFactTypes, connection);
322
+ }));
323
+ const mergedFactTypes = (0, maps_1.mergeFactTypes)(factTypes, loadedFactTypes);
324
+ this.factTypeMap = mergedFactTypes;
325
+ return mergedFactTypes;
326
+ }
327
+ return factTypes;
328
+ });
329
+ }
330
+ }
331
+ exports.PostgresStore = PostgresStore;
332
+ function executeQueryTree(sqlQueryTree, connection) {
333
+ return __awaiter(this, void 0, void 0, function* () {
334
+ const sqlQuery = sqlQueryTree.sqlQuery;
335
+ const { rows } = yield connection.query(sqlQuery.sql, sqlQuery.parameters);
336
+ const resultSets = {
337
+ resultSet: rows,
338
+ childResultSets: []
339
+ };
340
+ for (const child of sqlQueryTree.childQueries) {
341
+ const childResultSet = yield executeQueryTree(child, connection);
342
+ resultSets.childResultSets.push(Object.assign({ name: child.name }, childResultSet));
343
+ }
344
+ return resultSets;
345
+ });
346
+ }
347
+ function predecessorTypes(predecessor) {
348
+ if (Array.isArray(predecessor)) {
349
+ return predecessor.map(p => p.type);
350
+ }
351
+ return [predecessor.type];
352
+ }
353
+ function predecessorCollectionTypes(predecessors) {
354
+ return Object.keys(predecessors).flatMap(key => predecessorTypes(predecessors[key]));
355
+ }
356
+ function storeFactTypes(facts, factTypes, connection) {
357
+ return __awaiter(this, void 0, void 0, function* () {
358
+ const newFactTypes = facts
359
+ .flatMap(fact => [fact.type, ...predecessorCollectionTypes(fact.predecessors)])
360
+ .filter(type => !factTypes.has(type))
361
+ .filter(fn_1.distinct);
362
+ if (newFactTypes.length === 0) {
363
+ return factTypes;
364
+ }
365
+ // Look up existing fact types
366
+ const loadedFactTypes = yield loadFactTypes(newFactTypes, connection);
367
+ const remainingNames = newFactTypes.filter(type => !loadedFactTypes.has(type));
368
+ if (remainingNames.length === 0) {
369
+ return (0, maps_1.mergeFactTypes)(loadedFactTypes, factTypes);
370
+ }
371
+ // Insert new fact types
372
+ const values = remainingNames.map((name, index) => `($${index + 1})`);
373
+ const insertSql = 'INSERT INTO public.fact_type (name) VALUES ' + values.join(', ') +
374
+ ' RETURNING fact_type_id, name;';
375
+ const { rows: newRows } = yield connection.query(insertSql, remainingNames);
376
+ if (newRows.length !== remainingNames.length) {
377
+ throw new Error('Failed to insert all new fact types.');
378
+ }
379
+ const allFactTypes = newRows.reduce((map, row) => (0, maps_1.addFactType)(map, row.name, row.fact_type_id), (0, maps_1.mergeFactTypes)(loadedFactTypes, factTypes));
380
+ return allFactTypes;
381
+ });
382
+ }
383
+ function loadFactTypes(factTypeNames, connection) {
384
+ return __awaiter(this, void 0, void 0, function* () {
385
+ const lookUpSql = 'SELECT name, fact_type_id FROM public.fact_type WHERE name=ANY($1);';
386
+ const { rows: existingRows } = yield connection.query(lookUpSql, [factTypeNames]);
387
+ const factTypeIds = existingRows.reduce((map, row) => (0, maps_1.addFactType)(map, row.name, row.fact_type_id), (0, maps_1.emptyFactTypeMap)());
388
+ return factTypeIds;
389
+ });
390
+ }
391
+ function storeRoles(facts, factTypes, roleMap, connection) {
392
+ return __awaiter(this, void 0, void 0, function* () {
393
+ // Find distinct roles
394
+ const roles = (0, fn_1.flatten)(facts, fact => {
395
+ const defining_fact_type_id = factTypes.get(fact.type);
396
+ return Object.keys(fact.predecessors).map(role => ({
397
+ role,
398
+ defining_fact_type_id
399
+ }));
400
+ }).filter((role, index, array) => array.findIndex(r => r.role === role.role &&
401
+ r.defining_fact_type_id === role.defining_fact_type_id) === index);
402
+ if (roles.length > 0) {
403
+ // Look up existing roles
404
+ const roleIds = yield loadRoles(roles, roleMap, connection);
405
+ const remainingRoles = roles.filter(role => !(0, maps_1.hasRole)(roleIds, role.defining_fact_type_id, role.role));
406
+ if (remainingRoles.length === 0) {
407
+ return roleIds;
408
+ }
409
+ // Insert new roles
410
+ const remainingRoleValues = remainingRoles.map((role, index) => `($${index * 2 + 1}, $${index * 2 + 2}::integer)`);
411
+ const insertSql = 'INSERT INTO public.role (name, defining_fact_type_id) VALUES ' +
412
+ remainingRoleValues.join(', ') +
413
+ ' RETURNING role_id, name, defining_fact_type_id;';
414
+ const remainingRoleParameters = (0, fn_1.flatten)(remainingRoles, (role) => [
415
+ role.role,
416
+ role.defining_fact_type_id
417
+ ]);
418
+ const { rows: newRows } = yield connection.query(insertSql, remainingRoleParameters);
419
+ if (newRows.length !== remainingRoles.length) {
420
+ throw new Error('Failed to insert all new roles.');
421
+ }
422
+ const allRoleIds = newRows.reduce((map, row) => (0, maps_1.addRole)(map, row.defining_fact_type_id, row.name, row.role_id), roleIds);
423
+ return allRoleIds;
424
+ }
425
+ else {
426
+ return roleMap;
427
+ }
428
+ });
429
+ }
430
+ function loadRoles(roles, roleMap, connection) {
431
+ return __awaiter(this, void 0, void 0, function* () {
432
+ const roleValues = roles.map((role, index) => `($${index * 2 + 1}, $${index * 2 + 2}::integer)`);
433
+ const roleParameters = (0, fn_1.flatten)(roles, (role) => [
434
+ role.role,
435
+ role.defining_fact_type_id
436
+ ]);
437
+ const lookUpSql = 'SELECT role.name, role.defining_fact_type_id, role.role_id' +
438
+ ' FROM public.role' +
439
+ ' JOIN (VALUES ' + roleValues.join(', ') + ') AS v (name, defining_fact_type_id)' +
440
+ ' ON v.name = role.name AND v.defining_fact_type_id = role.defining_fact_type_id;';
441
+ const { rows } = yield connection.query(lookUpSql, roleParameters);
442
+ const roleIds = rows.reduce((map, row) => (0, maps_1.addRole)(map, row.defining_fact_type_id, row.name, row.role_id), roleMap);
443
+ return roleIds;
444
+ });
445
+ }
446
+ function findExistingFacts(facts, factTypes, connection) {
447
+ return __awaiter(this, void 0, void 0, function* () {
448
+ if (facts.length > 0) {
449
+ const factReferences = facts.flatMap(fact => [
450
+ ...predecessorsOf(fact),
451
+ fact
452
+ ]);
453
+ const factValues = factReferences.map((f, i) => `(\$${i * 2 + 1}, \$${i * 2 + 2}::integer)`);
454
+ const factParameters = factReferences.flatMap((f) => [f.hash, factTypes.get(f.type)]);
455
+ const sql = 'SELECT fact_id, fact.fact_type_id, fact.hash' +
456
+ ' FROM public.fact' +
457
+ ' JOIN (VALUES ' + factValues.join(', ') + ') AS v (hash, fact_type_id)' +
458
+ ' ON v.fact_type_id = fact.fact_type_id AND v.hash = fact.hash;';
459
+ const { rows } = yield connection.query(sql, factParameters);
460
+ const existingFacts = rows.reduce((map, row) => (0, maps_1.addFact)(map, row.hash, row.fact_type_id, row.fact_id), (0, maps_1.emptyFactMap)());
461
+ return existingFacts;
462
+ }
463
+ else {
464
+ return (0, maps_1.emptyFactMap)();
465
+ }
466
+ });
467
+ }
468
+ function insertFactsEdgesAndAncestors(facts, factTypes, existingFacts, connection, roles) {
469
+ return __awaiter(this, void 0, void 0, function* () {
470
+ if (facts.length === 0) {
471
+ return (0, maps_1.emptyFactMap)();
472
+ }
473
+ const { sql, parameters } = sqlInsertFacts(facts, roles, factTypes);
474
+ const { rows } = yield connection.query(sql, parameters);
475
+ if (rows.length !== facts.length) {
476
+ throw new Error('Failed to insert all new facts.');
477
+ }
478
+ const allFacts = rows.reduce((map, row) => (0, maps_1.addFact)(map, row.hash, row.fact_type_id, row.fact_id), existingFacts);
479
+ return allFacts;
480
+ });
481
+ }
482
+ function sqlInsertFacts(facts, roles, factTypes) {
483
+ const factValues = facts.map((f, i) => `(\$${i * 3 + 1}, \$${i * 3 + 2}::integer, \$${i * 3 + 3}::jsonb)`);
484
+ const factParameters = (0, fn_1.flatten)(facts, (f) => [f.hash, factTypes.get(f.type), {
485
+ fields: f.fields,
486
+ predecessors: (0, jinaga_1.canonicalPredecessors)(f.predecessors)
487
+ }]);
488
+ const edgeRecords = (0, fn_1.flatten)(facts, fact => (0, edge_record_1.makeEdgeRecords)(fact));
489
+ if (edgeRecords.length > 0) {
490
+ return sqlInsertFactsEdgesAndAncestors(facts, factParameters, edgeRecords, factTypes, roles, factValues);
491
+ }
492
+ else {
493
+ return {
494
+ sql: 'INSERT INTO public.fact (hash, fact_type_id, data) VALUES ' +
495
+ factValues.join(', ') +
496
+ ' RETURNING fact_id, hash, fact_type_id;',
497
+ parameters: factParameters
498
+ };
499
+ }
500
+ }
501
+ function sqlInsertFactsEdgesAndAncestors(facts, factParameters, edgeRecords, factTypes, roles, factValues) {
502
+ let parameterOffset = factParameters.length;
503
+ const edgeValues = edgeRecords.map((e, i) => `(\$${i * 5 + 1 + parameterOffset}, \$${i * 5 + 2 + parameterOffset}::integer, \$${i * 5 + 3 + parameterOffset}, \$${i * 5 + 4 + parameterOffset}::integer, \$${i * 5 + 5 + parameterOffset}::integer)`);
504
+ const edgeParameters = (0, fn_1.flatten)(edgeRecords, (e) => {
505
+ const successor_fact_type_id = (0, maps_1.getFactTypeId)(factTypes, e.successor_type);
506
+ const predecessor_fact_type_id = (0, maps_1.getFactTypeId)(factTypes, e.predecessor_type);
507
+ return [
508
+ e.successor_hash,
509
+ successor_fact_type_id,
510
+ e.predecessor_hash,
511
+ predecessor_fact_type_id,
512
+ (0, maps_1.getRoleId)(roles, successor_fact_type_id, e.role)
513
+ ];
514
+ });
515
+ parameterOffset += edgeParameters.length;
516
+ const ancestors = ancestorRecords(facts);
517
+ const ancestorValues = ancestors.map((a, i) => `(\$${i * 4 + 1 + parameterOffset}, \$${i * 4 + 2 + parameterOffset}::integer, \$${i * 4 + 3 + parameterOffset}, \$${i * 4 + 4 + parameterOffset}::integer)`);
518
+ const ancestorParameters = (0, fn_1.flatten)(ancestors, (a) => [
519
+ a.fact.hash,
520
+ factTypes.get(a.fact.type),
521
+ a.ancestor.hash,
522
+ factTypes.get(a.ancestor.type)
523
+ ]);
524
+ const sql = `WITH new_fact AS (
525
+ SELECT hash, fact_type_id, data
526
+ FROM (VALUES ${factValues.join(', ')})
527
+ AS fv (hash, fact_type_id, data)
528
+ ),
529
+ new_edge AS (
530
+ SELECT successor_hash, successor_fact_type_id, predecessor_hash, predecessor_fact_type_id, role_id
531
+ FROM (VALUES ${edgeValues.join(', ')})
532
+ AS ev (successor_hash, successor_fact_type_id, predecessor_hash, predecessor_fact_type_id, role_id)
533
+ ),
534
+ new_ancestor AS (
535
+ SELECT hash, fact_type_id, ancestor_hash, ancestor_fact_type_id
536
+ FROM (VALUES ${ancestorValues.join(', ')})
537
+ AS ev (hash, fact_type_id, ancestor_hash, ancestor_fact_type_id)
538
+ ),
539
+ inserted_fact AS (
540
+ INSERT INTO public.fact (hash, fact_type_id, data)
541
+ SELECT hash, fact_type_id, data
542
+ FROM new_fact
543
+ RETURNING fact_id, fact_type_id, hash
544
+ ),
545
+ inserted_edge AS (
546
+ INSERT INTO public.edge
547
+ (role_id, successor_fact_id, predecessor_fact_id)
548
+ SELECT
549
+ new_edge.role_id,
550
+ successor.fact_id,
551
+ predecessor.fact_id
552
+ FROM new_edge
553
+ JOIN inserted_fact AS successor
554
+ ON successor.hash = new_edge.successor_hash
555
+ AND successor.fact_type_id = new_edge.successor_fact_type_id
556
+ JOIN (
557
+ SELECT fact_id, fact_type_id, hash
558
+ FROM inserted_fact
559
+ UNION ALL
560
+ SELECT fact_id, fact_type_id, hash
561
+ FROM public.fact
562
+ ) AS predecessor
563
+ ON predecessor.hash = new_edge.predecessor_hash
564
+ AND predecessor.fact_type_id = new_edge.predecessor_fact_type_id
565
+ ON CONFLICT DO NOTHING
566
+ ),
567
+ ancestor_id AS (
568
+ SELECT
569
+ fact.fact_id,
570
+ ancestor.fact_id AS ancestor_fact_id
571
+ FROM new_ancestor
572
+ JOIN inserted_fact AS fact
573
+ ON fact.hash = new_ancestor.hash
574
+ AND fact.fact_type_id = new_ancestor.fact_type_id
575
+ JOIN (
576
+ SELECT fact_id, fact_type_id, hash
577
+ FROM inserted_fact
578
+ UNION ALL
579
+ SELECT fact_id, fact_type_id, hash
580
+ FROM public.fact
581
+ ) AS ancestor
582
+ ON ancestor.hash = new_ancestor.ancestor_hash
583
+ AND ancestor.fact_type_id = new_ancestor.ancestor_fact_type_id
584
+ ),
585
+ inserted_ancestor AS (
586
+ INSERT INTO public.ancestor
587
+ (fact_id, ancestor_fact_id)
588
+ SELECT ancestor_id.fact_id, ancestor_id.ancestor_fact_id
589
+ FROM ancestor_id
590
+ UNION ALL
591
+ SELECT ancestor_id.fact_id, ancestor.ancestor_fact_id
592
+ FROM ancestor_id
593
+ JOIN public.ancestor
594
+ ON ancestor.fact_id = ancestor_id.ancestor_fact_id
595
+ ON CONFLICT DO NOTHING
596
+ )
597
+ SELECT fact_id, fact_type_id, hash
598
+ FROM inserted_fact;`;
599
+ const parameters = [...factParameters, ...edgeParameters, ...ancestorParameters];
600
+ return { sql, parameters };
601
+ }
602
+ function ancestorRecords(facts) {
603
+ return facts.flatMap(fact => {
604
+ const factReference = { hash: fact.hash, type: fact.type };
605
+ const ancestorReferences = recursivePredecessors(factReference, facts);
606
+ return ancestorReferences.map(ancestor => ({
607
+ fact: factReference,
608
+ ancestor
609
+ }));
610
+ });
611
+ }
612
+ function recursivePredecessors(factReference, facts) {
613
+ return facts
614
+ .filter(f => f.hash === factReference.hash && f.type === factReference.type)
615
+ .flatMap(fact => {
616
+ const predecessorReferences = Object.keys(fact.predecessors).flatMap(role => {
617
+ const predecessors = fact.predecessors[role];
618
+ if (Array.isArray(predecessors)) {
619
+ return predecessors;
620
+ }
621
+ else {
622
+ return [predecessors];
623
+ }
624
+ });
625
+ const ancestorReferences = predecessorReferences.flatMap(predecessor => recursivePredecessors(predecessor, facts));
626
+ return [...predecessorReferences, ...ancestorReferences];
627
+ });
628
+ }
629
+ function storePublicKeys(envelopes, connection) {
630
+ return __awaiter(this, void 0, void 0, function* () {
631
+ // Look up existing fact types
632
+ const publicKeys = (0, fn_1.flatten)(envelopes, e => e.signatures.map(s => s.publicKey))
633
+ .filter(fn_1.distinct);
634
+ const lookUpSql = 'SELECT public_key, public_key_id FROM public.public_key WHERE public_key=ANY($1);';
635
+ const { rows: existingRows } = yield connection.query(lookUpSql, [publicKeys]);
636
+ const publicKeyIds = existingRows.reduce((map, row) => (0, maps_1.addFactType)(map, row.public_key, row.public_key_id), (0, maps_1.emptyPublicKeyMap)());
637
+ const remainingPublicKeys = publicKeys.filter(pk => !publicKeyIds.has(pk));
638
+ if (remainingPublicKeys.length === 0) {
639
+ return publicKeyIds;
640
+ }
641
+ // Insert new fact types
642
+ const values = remainingPublicKeys.map((name, index) => `($${index + 1})`);
643
+ const insertSql = 'INSERT INTO public.public_key (public_key) VALUES ' + values.join(', ') +
644
+ ' RETURNING public_key, public_key_id;';
645
+ const { rows: newRows } = yield connection.query(insertSql, remainingPublicKeys);
646
+ if (newRows.length !== remainingPublicKeys.length) {
647
+ throw new Error('Failed to insert all new public keys.');
648
+ }
649
+ const allPublicKeyIds = newRows.reduce((map, row) => (0, maps_1.addFactType)(map, row.public_key, row.public_key_id), publicKeyIds);
650
+ return allPublicKeyIds;
651
+ });
652
+ }
653
+ function insertSignatures(envelopes, allFacts, factTypes, publicKey, connection) {
654
+ return __awaiter(this, void 0, void 0, function* () {
655
+ const signatureRecords = (0, fn_1.flatten)(envelopes, envelope => envelope.signatures.map(signature => ({
656
+ factId: (0, maps_1.getFactId)(allFacts, envelope.fact.hash, (0, maps_1.getFactTypeId)(factTypes, envelope.fact.type)),
657
+ publicKeyId: (0, maps_1.getPublicKeyId)(publicKey, signature.publicKey),
658
+ signature: signature.signature
659
+ })));
660
+ if (signatureRecords.length > 0) {
661
+ const signatureValues = signatureRecords.map((s, i) => `($${i * 3 + 1}::integer, $${i * 3 + 2}::integer, $${i * 3 + 3})`);
662
+ const signatureParameters = (0, fn_1.flatten)(signatureRecords, s => [s.factId, s.publicKeyId, s.signature]);
663
+ const sql = `INSERT INTO public.signature
664
+ (fact_id, public_key_id, signature)
665
+ (SELECT fact_id, public_key_id, signature
666
+ FROM (VALUES ${signatureValues.join(', ')}) AS v(fact_id, public_key_id, signature))
667
+ ON CONFLICT DO NOTHING`;
668
+ yield connection.query(sql, signatureParameters);
669
+ }
670
+ });
671
+ }
672
+ function allFactTypes(steps) {
673
+ const factTypes = steps
674
+ .filter(step => step instanceof jinaga_1.PropertyCondition && step.name === 'type')
675
+ .map(step => step.value);
676
+ const childFactTypes = steps
677
+ .filter(step => step instanceof jinaga_1.ExistentialCondition)
678
+ .flatMap(step => allFactTypes(step.steps));
679
+ return [...factTypes, ...childFactTypes].filter(fn_1.distinct);
680
+ }
681
+ function allRoles(steps, factTypes, initialTypeId) {
682
+ let roles = [];
683
+ let defining_fact_type_id = initialTypeId;
684
+ let role = undefined;
685
+ for (const step of steps) {
686
+ if (step instanceof jinaga_1.PropertyCondition) {
687
+ if (step.name === 'type') {
688
+ defining_fact_type_id = (0, maps_1.getFactTypeId)(factTypes, step.value);
689
+ if (defining_fact_type_id && role) {
690
+ roles.push({ defining_fact_type_id, role });
691
+ }
692
+ role = undefined;
693
+ }
694
+ }
695
+ else if (step instanceof jinaga_1.Join) {
696
+ if (step.direction === jinaga_1.Direction.Predecessor) {
697
+ if (defining_fact_type_id) {
698
+ roles.push({ defining_fact_type_id, role: step.role });
699
+ }
700
+ role = undefined;
701
+ }
702
+ else {
703
+ role = step.role;
704
+ }
705
+ defining_fact_type_id = undefined;
706
+ }
707
+ else if (step instanceof jinaga_1.ExistentialCondition) {
708
+ roles = roles.concat(allRoles(step.steps, factTypes, defining_fact_type_id));
709
+ }
710
+ }
711
+ return roles;
712
+ }
713
+ function predecessorsOf(fact) {
714
+ const references = Object.values(fact.predecessors).flatMap(predecessor => Array.isArray(predecessor) ? predecessor : [predecessor]);
715
+ return references;
716
+ }
717
+ //# sourceMappingURL=postgres-store.js.map