@proteinjs/db 1.54.0 → 1.54.2

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 (53) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/generated/index.js +1 -1
  3. package/dist/generated/index.js.map +1 -1
  4. package/dist/generated/test/index.js +1 -1
  5. package/dist/generated/test/index.js.map +1 -1
  6. package/dist/src/Columns.d.ts +12 -6
  7. package/dist/src/Columns.d.ts.map +1 -1
  8. package/dist/src/Columns.js +40 -65
  9. package/dist/src/Columns.js.map +1 -1
  10. package/dist/src/Db.d.ts.map +1 -1
  11. package/dist/src/Db.js +2 -5
  12. package/dist/src/Db.js.map +1 -1
  13. package/dist/src/Record.d.ts +19 -0
  14. package/dist/src/Record.d.ts.map +1 -1
  15. package/dist/src/Record.js +127 -45
  16. package/dist/src/Record.js.map +1 -1
  17. package/dist/src/Table.d.ts +18 -1
  18. package/dist/src/Table.d.ts.map +1 -1
  19. package/dist/src/encryption/EncryptionRecordHooks.d.ts +9 -4
  20. package/dist/src/encryption/EncryptionRecordHooks.d.ts.map +1 -1
  21. package/dist/src/encryption/EncryptionRecordHooks.js +65 -42
  22. package/dist/src/encryption/EncryptionRecordHooks.js.map +1 -1
  23. package/dist/src/services/TransactionRunnerService.d.ts +3 -1
  24. package/dist/src/services/TransactionRunnerService.d.ts.map +1 -1
  25. package/dist/src/transaction/TransactionRunner.d.ts +13 -5
  26. package/dist/src/transaction/TransactionRunner.d.ts.map +1 -1
  27. package/dist/src/transaction/TransactionRunner.js +48 -12
  28. package/dist/src/transaction/TransactionRunner.js.map +1 -1
  29. package/dist/test/DateTimeFieldReadOnFirstRead.test.d.ts +2 -0
  30. package/dist/test/DateTimeFieldReadOnFirstRead.test.d.ts.map +1 -0
  31. package/dist/test/DateTimeFieldReadOnFirstRead.test.js +238 -0
  32. package/dist/test/DateTimeFieldReadOnFirstRead.test.js.map +1 -0
  33. package/dist/test/HydrationBenchmark.test.d.ts +2 -0
  34. package/dist/test/HydrationBenchmark.test.d.ts.map +1 -0
  35. package/dist/test/HydrationBenchmark.test.js +299 -0
  36. package/dist/test/HydrationBenchmark.test.js.map +1 -0
  37. package/dist/test/SyncRowHydration.test.d.ts +2 -0
  38. package/dist/test/SyncRowHydration.test.d.ts.map +1 -0
  39. package/dist/test/SyncRowHydration.test.js +344 -0
  40. package/dist/test/SyncRowHydration.test.js.map +1 -0
  41. package/generated/index.ts +1 -1
  42. package/generated/test/index.ts +1 -1
  43. package/package.json +2 -2
  44. package/src/Columns.ts +14 -7
  45. package/src/Db.ts +3 -4
  46. package/src/Record.ts +151 -17
  47. package/src/Table.ts +21 -1
  48. package/src/encryption/EncryptionRecordHooks.ts +48 -17
  49. package/src/services/TransactionRunnerService.ts +3 -1
  50. package/src/transaction/TransactionRunner.ts +53 -13
  51. package/test/DateTimeFieldReadOnFirstRead.test.ts +162 -0
  52. package/test/HydrationBenchmark.test.ts +209 -0
  53. package/test/SyncRowHydration.test.ts +249 -0
@@ -0,0 +1,299 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ var __generator = (this && this.__generator) || function (thisArg, body) {
27
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
+ function verb(n) { return function (v) { return step([n, v]); }; }
30
+ function step(op) {
31
+ if (f) throw new TypeError("Generator is already executing.");
32
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
+ if (y = 0, t) op = [op[0] & 2, t.value];
35
+ switch (op[0]) {
36
+ case 0: case 1: t = op; break;
37
+ case 4: _.label++; return { value: op[1], done: false };
38
+ case 5: _.label++; y = op[1]; op = [0]; continue;
39
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
+ default:
41
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
+ if (t[2]) _.ops.pop();
46
+ _.trys.pop(); continue;
47
+ }
48
+ op = body.call(thisArg, _);
49
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
+ }
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ var async_hooks_1 = require("async_hooks");
55
+ var reflection_1 = require("@proteinjs/reflection");
56
+ var Db_1 = require("../src/Db");
57
+ var TableWatcherRunner_1 = require("../src/TableWatcherRunner");
58
+ var Table_1 = require("../src/Table");
59
+ var Record_1 = require("../src/Record");
60
+ var Columns_1 = require("../src/Columns");
61
+ var WideTable = /** @class */ (function (_super) {
62
+ __extends(WideTable, _super);
63
+ function WideTable() {
64
+ var _this = _super !== null && _super.apply(this, arguments) || this;
65
+ _this.name = 'hydration_wide';
66
+ _this.columns = (0, Record_1.withRecordColumns)({
67
+ title: new Columns_1.StringColumn('title'),
68
+ body: new Columns_1.StringColumn('body', undefined, 'MAX'),
69
+ count: new Columns_1.IntegerColumn('count'),
70
+ payload: new Columns_1.ObjectColumn('payload'),
71
+ t0: new Columns_1.DateTimeColumn('t0'),
72
+ t1: new Columns_1.DateTimeColumn('t1'),
73
+ t2: new Columns_1.DateTimeColumn('t2'),
74
+ t3: new Columns_1.DateTimeColumn('t3'),
75
+ t4: new Columns_1.DateTimeColumn('t4'),
76
+ t5: new Columns_1.DateTimeColumn('t5'),
77
+ t6: new Columns_1.DateTimeColumn('t6'),
78
+ t7: new Columns_1.DateTimeColumn('t7'),
79
+ t8: new Columns_1.DateTimeColumn('t8'),
80
+ t9: new Columns_1.DateTimeColumn('t9'),
81
+ });
82
+ return _this;
83
+ }
84
+ return WideTable;
85
+ }(Table_1.Table));
86
+ var wideTable = new WideTable();
87
+ var TestTransactionContextFactory = /** @class */ (function () {
88
+ function TestTransactionContextFactory() {
89
+ this.context = {};
90
+ }
91
+ TestTransactionContextFactory.prototype.getTransactionContext = function () {
92
+ return this.context;
93
+ };
94
+ TestTransactionContextFactory.prototype.runInContext = function (context, fn) {
95
+ return __awaiter(this, void 0, void 0, function () {
96
+ var previous;
97
+ return __generator(this, function (_a) {
98
+ switch (_a.label) {
99
+ case 0:
100
+ previous = this.context;
101
+ this.context = context;
102
+ _a.label = 1;
103
+ case 1:
104
+ _a.trys.push([1, , 3, 4]);
105
+ return [4 /*yield*/, fn()];
106
+ case 2: return [2 /*return*/, _a.sent()];
107
+ case 3:
108
+ this.context = previous;
109
+ return [7 /*endfinally*/];
110
+ case 4: return [2 /*return*/];
111
+ }
112
+ });
113
+ });
114
+ };
115
+ return TestTransactionContextFactory;
116
+ }());
117
+ var RowsDriver = /** @class */ (function () {
118
+ function RowsDriver(rows) {
119
+ this.rows = rows;
120
+ }
121
+ RowsDriver.prototype.getDbName = function () {
122
+ return 'test';
123
+ };
124
+ RowsDriver.prototype.createDbIfNotExists = function () {
125
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
126
+ return [2 /*return*/];
127
+ }); });
128
+ };
129
+ RowsDriver.prototype.createDb = function () {
130
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
131
+ return [2 /*return*/];
132
+ }); });
133
+ };
134
+ RowsDriver.prototype.dropDb = function () {
135
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
136
+ return [2 /*return*/];
137
+ }); });
138
+ };
139
+ RowsDriver.prototype.getTableManager = function () {
140
+ throw new Error('not used by this measure');
141
+ };
142
+ RowsDriver.prototype.runQuery = function () {
143
+ return __awaiter(this, void 0, void 0, function () {
144
+ return __generator(this, function (_a) {
145
+ return [2 /*return*/, this.rows];
146
+ });
147
+ });
148
+ };
149
+ RowsDriver.prototype.runDml = function () {
150
+ return __awaiter(this, void 0, void 0, function () {
151
+ return __generator(this, function (_a) {
152
+ return [2 /*return*/, 0];
153
+ });
154
+ });
155
+ };
156
+ RowsDriver.prototype.runTransaction = function (fn) {
157
+ return __awaiter(this, void 0, void 0, function () {
158
+ return __generator(this, function (_a) {
159
+ switch (_a.label) {
160
+ case 0: return [4 /*yield*/, fn({ id: 'transaction' })];
161
+ case 1: return [2 /*return*/, _a.sent()];
162
+ }
163
+ });
164
+ });
165
+ };
166
+ RowsDriver.prototype.getOperationDeadlineMs = function () {
167
+ return 1000;
168
+ };
169
+ return RowsDriver;
170
+ }());
171
+ var objectCache = function () { return reflection_1.SourceRepository.get().objectCache; };
172
+ var originalWatchers;
173
+ // No reflection graph in a unit test: the watcher registry is empty by declaration.
174
+ beforeAll(function () {
175
+ originalWatchers = objectCache()['@proteinjs/db/TableWatcher'];
176
+ objectCache()['@proteinjs/db/TableWatcher'] = [];
177
+ TableWatcherRunner_1.TableWatcherRunner.tableWatcherMap = undefined;
178
+ });
179
+ afterAll(function () {
180
+ if (originalWatchers) {
181
+ objectCache()['@proteinjs/db/TableWatcher'] = originalWatchers;
182
+ }
183
+ else {
184
+ delete objectCache()['@proteinjs/db/TableWatcher'];
185
+ }
186
+ TableWatcherRunner_1.TableWatcherRunner.tableWatcherMap = undefined;
187
+ });
188
+ var storedRows = function (count) {
189
+ return Array.from({ length: count }, function (_, row) {
190
+ var stored = {
191
+ id: "row-".concat(row),
192
+ created: new Date(1600000000000 + row),
193
+ updated: new Date(1600000000000 + row),
194
+ title: "title ".concat(row),
195
+ body: 'x'.repeat(400),
196
+ count: row,
197
+ payload: JSON.stringify({ tags: ['a', 'b', 'c'], n: row }),
198
+ };
199
+ for (var i = 0; i < 10; i++) {
200
+ stored["t".concat(i)] = new Date(1700000000000 + row * 100 + i);
201
+ }
202
+ return stored;
203
+ });
204
+ };
205
+ /** Wall time, promises created and microtask rounds while `act` runs. */
206
+ var measure = function (act) { return __awaiter(void 0, void 0, void 0, function () {
207
+ var promises, hook, microtaskRounds, running, round, started, ms;
208
+ return __generator(this, function (_a) {
209
+ switch (_a.label) {
210
+ case 0:
211
+ promises = 0;
212
+ hook = (0, async_hooks_1.createHook)({
213
+ init: function (_asyncId, type) {
214
+ if (type === 'PROMISE') {
215
+ promises++;
216
+ }
217
+ },
218
+ });
219
+ microtaskRounds = 0;
220
+ running = true;
221
+ round = function () {
222
+ if (!running) {
223
+ return;
224
+ }
225
+ microtaskRounds++;
226
+ queueMicrotask(round);
227
+ };
228
+ hook.enable();
229
+ started = process.hrtime.bigint();
230
+ queueMicrotask(round);
231
+ return [4 /*yield*/, act()];
232
+ case 1:
233
+ _a.sent();
234
+ running = false;
235
+ ms = Number(process.hrtime.bigint() - started) / 1000000;
236
+ hook.disable();
237
+ return [2 /*return*/, { ms: Math.round(ms * 10) / 10, promises: promises, microtaskRounds: microtaskRounds }];
238
+ }
239
+ });
240
+ }); };
241
+ describe('hydration measure', function () {
242
+ it('reports 1,000 rows with ten DateTime columns through Db.query (wall ms, promises, microtask rounds)', function () { return __awaiter(void 0, void 0, void 0, function () {
243
+ var rows, db, passes, records, i, _a, _b, readAll;
244
+ return __generator(this, function (_c) {
245
+ switch (_c.label) {
246
+ case 0:
247
+ rows = storedRows(1000);
248
+ db = new Db_1.Db(new RowsDriver(rows), function () { return wideTable; }, new TestTransactionContextFactory(), true);
249
+ // one warm-up pass, then the measured passes
250
+ return [4 /*yield*/, db.query(wideTable, {})];
251
+ case 1:
252
+ // one warm-up pass, then the measured passes
253
+ _c.sent();
254
+ passes = [];
255
+ records = [];
256
+ i = 0;
257
+ _c.label = 2;
258
+ case 2:
259
+ if (!(i < 5)) return [3 /*break*/, 5];
260
+ _b = (_a = passes).push;
261
+ return [4 /*yield*/, measure(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
262
+ switch (_a.label) {
263
+ case 0: return [4 /*yield*/, db.query(wideTable, {})];
264
+ case 1: return [2 /*return*/, (records = _a.sent())];
265
+ }
266
+ }); }); })];
267
+ case 3:
268
+ _b.apply(_a, [_c.sent()]);
269
+ _c.label = 4;
270
+ case 4:
271
+ i++;
272
+ return [3 /*break*/, 2];
273
+ case 5: return [4 /*yield*/, measure(function () { return __awaiter(void 0, void 0, void 0, function () {
274
+ var _i, records_1, record;
275
+ return __generator(this, function (_a) {
276
+ for (_i = 0, records_1 = records; _i < records_1.length; _i++) {
277
+ record = records_1[_i];
278
+ record.t0.valueOf();
279
+ record.created.valueOf();
280
+ }
281
+ return [2 /*return*/];
282
+ });
283
+ }); })];
284
+ case 6:
285
+ readAll = _c.sent();
286
+ process.stdout.write("hydration measure \u2014 1,000 rows \u00D7 (10 DateTime + created/updated + 4 plain columns), Db.query, 5 passes:\n" +
287
+ passes
288
+ .map(function (pass, i) {
289
+ return " pass ".concat(i + 1, ": ").concat(pass.ms, " ms \u00B7 ").concat(pass.promises, " promises \u00B7 ").concat(pass.microtaskRounds, " microtask rounds");
290
+ })
291
+ .join('\n') +
292
+ "\n then reading t0 + created on every record: ".concat(readAll.ms, " ms \u00B7 ").concat(readAll.promises, " promises\n"));
293
+ expect(records).toHaveLength(1000);
294
+ return [2 /*return*/];
295
+ }
296
+ });
297
+ }); });
298
+ });
299
+ //# sourceMappingURL=HydrationBenchmark.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HydrationBenchmark.test.js","sourceRoot":"","sources":["../../test/HydrationBenchmark.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=SyncRowHydration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyncRowHydration.test.d.ts","sourceRoot":"","sources":["../../test/SyncRowHydration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ var __generator = (this && this.__generator) || function (thisArg, body) {
27
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
+ function verb(n) { return function (v) { return step([n, v]); }; }
30
+ function step(op) {
31
+ if (f) throw new TypeError("Generator is already executing.");
32
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
+ if (y = 0, t) op = [op[0] & 2, t.value];
35
+ switch (op[0]) {
36
+ case 0: case 1: t = op; break;
37
+ case 4: _.label++; return { value: op[1], done: false };
38
+ case 5: _.label++; y = op[1]; op = [0]; continue;
39
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
+ default:
41
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
+ if (t[2]) _.ops.pop();
46
+ _.trys.pop(); continue;
47
+ }
48
+ op = body.call(thisArg, _);
49
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
+ }
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ var crypto_1 = require("crypto");
55
+ var reflection_1 = require("@proteinjs/reflection");
56
+ var Db_1 = require("../src/Db");
57
+ var TableWatcherRunner_1 = require("../src/TableWatcherRunner");
58
+ var Table_1 = require("../src/Table");
59
+ var Record_1 = require("../src/Record");
60
+ var Columns_1 = require("../src/Columns");
61
+ var DataKeyStore_1 = require("../src/encryption/DataKeyStore");
62
+ var EncryptionEnvelope_1 = require("../src/encryption/EncryptionEnvelope");
63
+ /** Where the loop stood when each cell was read: the row, and whether the sentinel had fired by then. */
64
+ var cellsRead = [];
65
+ var sentinelFired = false;
66
+ /**
67
+ * A column that notes, at each read, whether a microtask armed by the FIRST cell read has fired yet:
68
+ * a hydration that yields the loop between rows lets it fire mid-way; a synchronous one never does.
69
+ */
70
+ var ObservingColumn = /** @class */ (function () {
71
+ function ObservingColumn(name) {
72
+ this.name = name;
73
+ }
74
+ ObservingColumn.prototype.deserialize = function (storedValue) {
75
+ if (cellsRead.length === 0) {
76
+ queueMicrotask(function () {
77
+ sentinelFired = true;
78
+ });
79
+ }
80
+ cellsRead.push({ column: this.name, row: storedValue, sentinelFired: sentinelFired });
81
+ return storedValue;
82
+ };
83
+ return ObservingColumn;
84
+ }());
85
+ var DocTable = /** @class */ (function (_super) {
86
+ __extends(DocTable, _super);
87
+ function DocTable() {
88
+ var _this = _super !== null && _super.apply(this, arguments) || this;
89
+ _this.name = 'hydration_doc';
90
+ _this.columns = (0, Record_1.withRecordColumns)({
91
+ title: new Columns_1.StringColumn('title'),
92
+ first: new ObservingColumn('first'),
93
+ second: new ObservingColumn('second'),
94
+ });
95
+ return _this;
96
+ }
97
+ return DocTable;
98
+ }(Table_1.Table));
99
+ var docTable = new DocTable();
100
+ var TestTransactionContextFactory = /** @class */ (function () {
101
+ function TestTransactionContextFactory() {
102
+ this.context = {};
103
+ }
104
+ TestTransactionContextFactory.prototype.getTransactionContext = function () {
105
+ return this.context;
106
+ };
107
+ TestTransactionContextFactory.prototype.runInContext = function (context, fn) {
108
+ return __awaiter(this, void 0, void 0, function () {
109
+ var previous;
110
+ return __generator(this, function (_a) {
111
+ switch (_a.label) {
112
+ case 0:
113
+ previous = this.context;
114
+ this.context = context;
115
+ _a.label = 1;
116
+ case 1:
117
+ _a.trys.push([1, , 3, 4]);
118
+ return [4 /*yield*/, fn()];
119
+ case 2: return [2 /*return*/, _a.sent()];
120
+ case 3:
121
+ this.context = previous;
122
+ return [7 /*endfinally*/];
123
+ case 4: return [2 /*return*/];
124
+ }
125
+ });
126
+ });
127
+ };
128
+ return TestTransactionContextFactory;
129
+ }());
130
+ /** A driver that answers every query with the rows it was handed. */
131
+ var RowsDriver = /** @class */ (function () {
132
+ function RowsDriver(rows) {
133
+ this.rows = rows;
134
+ }
135
+ RowsDriver.prototype.getDbName = function () {
136
+ return 'test';
137
+ };
138
+ RowsDriver.prototype.createDbIfNotExists = function () {
139
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
140
+ return [2 /*return*/];
141
+ }); });
142
+ };
143
+ RowsDriver.prototype.createDb = function () {
144
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
145
+ return [2 /*return*/];
146
+ }); });
147
+ };
148
+ RowsDriver.prototype.dropDb = function () {
149
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
150
+ return [2 /*return*/];
151
+ }); });
152
+ };
153
+ RowsDriver.prototype.getTableManager = function () {
154
+ throw new Error('not used by these tests');
155
+ };
156
+ RowsDriver.prototype.runQuery = function () {
157
+ return __awaiter(this, void 0, void 0, function () {
158
+ return __generator(this, function (_a) {
159
+ return [2 /*return*/, this.rows];
160
+ });
161
+ });
162
+ };
163
+ RowsDriver.prototype.runDml = function () {
164
+ return __awaiter(this, void 0, void 0, function () {
165
+ return __generator(this, function (_a) {
166
+ return [2 /*return*/, 0];
167
+ });
168
+ });
169
+ };
170
+ RowsDriver.prototype.runTransaction = function (fn) {
171
+ return __awaiter(this, void 0, void 0, function () {
172
+ return __generator(this, function (_a) {
173
+ switch (_a.label) {
174
+ case 0: return [4 /*yield*/, fn({ id: 'transaction' })];
175
+ case 1: return [2 /*return*/, _a.sent()];
176
+ }
177
+ });
178
+ });
179
+ };
180
+ RowsDriver.prototype.getOperationDeadlineMs = function () {
181
+ return 1000;
182
+ };
183
+ return RowsDriver;
184
+ }());
185
+ var dbOver = function (rows) {
186
+ return new Db_1.Db(new RowsDriver(rows), function () { return docTable; }, new TestTransactionContextFactory(), true);
187
+ };
188
+ var storedRows = function (count) {
189
+ return Array.from({ length: count }, function (_, i) { return ({
190
+ id: "row-".concat(i),
191
+ created: new Date(1700000000000 + i),
192
+ updated: new Date(1700000000000 + i),
193
+ title: "title ".concat(i),
194
+ first: i,
195
+ second: i,
196
+ }); });
197
+ };
198
+ var objectCache = function () { return reflection_1.SourceRepository.get().objectCache; };
199
+ var originalWatchers;
200
+ // No reflection graph in a unit test: the watcher registry is empty by declaration.
201
+ beforeAll(function () {
202
+ originalWatchers = objectCache()['@proteinjs/db/TableWatcher'];
203
+ objectCache()['@proteinjs/db/TableWatcher'] = [];
204
+ TableWatcherRunner_1.TableWatcherRunner.tableWatcherMap = undefined;
205
+ });
206
+ afterAll(function () {
207
+ if (originalWatchers) {
208
+ objectCache()['@proteinjs/db/TableWatcher'] = originalWatchers;
209
+ }
210
+ else {
211
+ delete objectCache()['@proteinjs/db/TableWatcher'];
212
+ }
213
+ TableWatcherRunner_1.TableWatcherRunner.tableWatcherMap = undefined;
214
+ });
215
+ var keyFor = function (owner, version) { return ({
216
+ owner: owner,
217
+ version: version,
218
+ cipherKey: (0, crypto_1.randomBytes)(32),
219
+ indexKey: (0, crypto_1.randomBytes)(32),
220
+ }); };
221
+ describe('a result set hydrates in one synchronous pass when no row needs an await', function () {
222
+ beforeEach(function () {
223
+ cellsRead.length = 0;
224
+ sentinelFired = false;
225
+ });
226
+ it('a query over plain rows never yields the event loop between rows', function () { return __awaiter(void 0, void 0, void 0, function () {
227
+ var rows, records;
228
+ return __generator(this, function (_a) {
229
+ switch (_a.label) {
230
+ case 0:
231
+ rows = storedRows(200);
232
+ return [4 /*yield*/, dbOver(rows).query(docTable, {})];
233
+ case 1:
234
+ records = _a.sent();
235
+ expect(records).toHaveLength(200);
236
+ expect(records[199]).toMatchObject({ id: 'row-199', title: 'title 199', first: 199, second: 199 });
237
+ // Every cell of every row was read before the microtask armed by the first cell could fire.
238
+ expect(cellsRead).toHaveLength(400);
239
+ expect(cellsRead.filter(function (cell) { return cell.sentinelFired; }).map(function (cell) { return "".concat(cell.column, ":").concat(cell.row); })).toEqual([]);
240
+ expect(sentinelFired).toBe(true);
241
+ return [2 /*return*/];
242
+ }
243
+ });
244
+ }); });
245
+ it('the serializer answers a plain result set with the array itself, not a promise', function () {
246
+ var rows = storedRows(3);
247
+ var records = new Record_1.RecordSerializer(docTable).deserializeAll(rows);
248
+ expect(Array.isArray(records)).toBe(true);
249
+ expect(records.map(function (record) { return record.title; })).toEqual(['title 0', 'title 1', 'title 2']);
250
+ });
251
+ it('a column whose deserialize answers with a promise still settles, in column order', function () { return __awaiter(void 0, void 0, void 0, function () {
252
+ var LaterColumn, LaterTable, laterTable, rows, settling, records;
253
+ return __generator(this, function (_a) {
254
+ switch (_a.label) {
255
+ case 0:
256
+ LaterColumn = /** @class */ (function () {
257
+ function LaterColumn(name) {
258
+ this.name = name;
259
+ }
260
+ LaterColumn.prototype.deserialize = function (storedValue) {
261
+ return Promise.resolve(storedValue === null ? null : storedValue * 2);
262
+ };
263
+ return LaterColumn;
264
+ }());
265
+ LaterTable = /** @class */ (function (_super) {
266
+ __extends(LaterTable, _super);
267
+ function LaterTable() {
268
+ var _this = _super !== null && _super.apply(this, arguments) || this;
269
+ _this.name = 'hydration_later';
270
+ _this.columns = (0, Record_1.withRecordColumns)({
271
+ title: new Columns_1.StringColumn('title'),
272
+ later: new LaterColumn('later'),
273
+ after: new Columns_1.StringColumn('after'),
274
+ });
275
+ return _this;
276
+ }
277
+ return LaterTable;
278
+ }(Table_1.Table));
279
+ laterTable = new LaterTable();
280
+ rows = [
281
+ { id: 'a', created: new Date(), updated: new Date(), title: 'one', later: 21, after: 'x' },
282
+ { id: 'b', created: new Date(), updated: new Date(), title: 'two', later: null, after: 'y' },
283
+ ];
284
+ settling = new Record_1.RecordSerializer(laterTable).deserializeAll(rows);
285
+ expect(Array.isArray(settling)).toBe(false);
286
+ return [4 /*yield*/, settling];
287
+ case 1:
288
+ records = _a.sent();
289
+ expect(records.map(function (record) { return record.later; })).toEqual([42, null]);
290
+ expect(Object.keys(records[0])).toEqual(['id', 'created', 'updated', 'title', 'later', 'after']);
291
+ return [2 /*return*/];
292
+ }
293
+ });
294
+ }); });
295
+ it('a row holding a ciphertext envelope takes the async road and decrypts; its plain neighbors ride along', function () { return __awaiter(void 0, void 0, void 0, function () {
296
+ var key, envelope, keyRead, rows, settling, records;
297
+ return __generator(this, function (_a) {
298
+ switch (_a.label) {
299
+ case 0:
300
+ key = keyFor('owner-1', 4);
301
+ envelope = new EncryptionEnvelope_1.EncryptionEnvelope();
302
+ keyRead = jest.spyOn(DataKeyStore_1.DataKeyStore.prototype, 'getKeyByVersion').mockResolvedValue(key);
303
+ _a.label = 1;
304
+ case 1:
305
+ _a.trys.push([1, , 3, 4]);
306
+ rows = storedRows(3);
307
+ rows[1].title = envelope.encrypt('the plaintext', key);
308
+ settling = new Record_1.RecordSerializer(docTable).deserializeAll(rows);
309
+ expect(Array.isArray(settling)).toBe(false);
310
+ return [4 /*yield*/, settling];
311
+ case 2:
312
+ records = _a.sent();
313
+ expect(records.map(function (record) { return record.title; })).toEqual(['title 0', 'the plaintext', 'title 2']);
314
+ expect(keyRead).toHaveBeenCalledTimes(1);
315
+ expect(keyRead).toHaveBeenCalledWith('owner-1', 4);
316
+ // The plain rows were hydrated in the one pass; the envelope row landed when its key read settled.
317
+ expect(cellsRead.map(function (cell) { return cell.row; })).toEqual([0, 0, 2, 2, 1, 1]);
318
+ return [3 /*break*/, 4];
319
+ case 3:
320
+ keyRead.mockRestore();
321
+ return [7 /*endfinally*/];
322
+ case 4: return [2 /*return*/];
323
+ }
324
+ });
325
+ }); });
326
+ it('a stored column the table no longer declares is omitted; a column without deserialize passes its value through', function () { return __awaiter(void 0, void 0, void 0, function () {
327
+ var rows, record;
328
+ return __generator(this, function (_a) {
329
+ switch (_a.label) {
330
+ case 0:
331
+ rows = [
332
+ { id: 'a', created: new Date(), updated: new Date(), title: 'one', first: 1, second: 1, retired: 'gone' },
333
+ ];
334
+ return [4 /*yield*/, new Record_1.RecordSerializer(docTable).deserializeAll(rows)];
335
+ case 1:
336
+ record = (_a.sent())[0];
337
+ expect(record).toMatchObject({ id: 'a', title: 'one', first: 1, second: 1 });
338
+ expect('retired' in record).toBe(false);
339
+ return [2 /*return*/];
340
+ }
341
+ });
342
+ }); });
343
+ });
344
+ //# sourceMappingURL=SyncRowHydration.test.js.map