@proteinjs/db-settings 1.3.46 → 1.3.47

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 (48) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/generated/index.d.ts.map +1 -1
  3. package/dist/generated/index.js +3 -1
  4. package/dist/generated/index.js.map +1 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/src/Settings.d.ts +10 -1
  10. package/dist/src/Settings.d.ts.map +1 -1
  11. package/dist/src/Settings.js +30 -9
  12. package/dist/src/Settings.js.map +1 -1
  13. package/dist/src/migrations/DeduplicateSettingRows.d.ts +34 -0
  14. package/dist/src/migrations/DeduplicateSettingRows.d.ts.map +1 -0
  15. package/dist/src/migrations/DeduplicateSettingRows.js +171 -0
  16. package/dist/src/migrations/DeduplicateSettingRows.js.map +1 -0
  17. package/dist/src/tables/SettingTable.d.ts +8 -0
  18. package/dist/src/tables/SettingTable.d.ts.map +1 -1
  19. package/dist/src/tables/SettingTable.js +10 -0
  20. package/dist/src/tables/SettingTable.js.map +1 -1
  21. package/dist/test/DeduplicateSettingRows.test.d.ts +3 -0
  22. package/dist/test/DeduplicateSettingRows.test.d.ts.map +1 -0
  23. package/dist/test/DeduplicateSettingRows.test.js +195 -0
  24. package/dist/test/DeduplicateSettingRows.test.js.map +1 -0
  25. package/dist/test/SettingsOneRowPerName.test.d.ts +3 -0
  26. package/dist/test/SettingsOneRowPerName.test.d.ts.map +1 -0
  27. package/dist/test/SettingsOneRowPerName.test.js +213 -0
  28. package/dist/test/SettingsOneRowPerName.test.js.map +1 -0
  29. package/dist/test/util/HeldFirstWriteDriver.d.ts +36 -0
  30. package/dist/test/util/HeldFirstWriteDriver.d.ts.map +1 -0
  31. package/dist/test/util/HeldFirstWriteDriver.js +130 -0
  32. package/dist/test/util/HeldFirstWriteDriver.js.map +1 -0
  33. package/dist/test/util/TestSession.d.ts +22 -0
  34. package/dist/test/util/TestSession.d.ts.map +1 -0
  35. package/dist/test/util/TestSession.js +49 -0
  36. package/dist/test/util/TestSession.js.map +1 -0
  37. package/generated/index.ts +3 -1
  38. package/index.ts +2 -0
  39. package/jest.config.js +2 -0
  40. package/package.json +9 -5
  41. package/src/Settings.ts +20 -5
  42. package/src/migrations/DeduplicateSettingRows.ts +78 -0
  43. package/src/tables/SettingTable.ts +11 -1
  44. package/test/DeduplicateSettingRows.test.ts +83 -0
  45. package/test/SettingsOneRowPerName.test.ts +104 -0
  46. package/test/setup.js +2 -0
  47. package/test/util/HeldFirstWriteDriver.ts +110 -0
  48. package/test/util/TestSession.ts +35 -0
@@ -0,0 +1,195 @@
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
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ 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;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var db_1 = require("@proteinjs/db");
43
+ var test_1 = require("@proteinjs/db-driver-spanner/test");
44
+ var moment_1 = __importDefault(require("moment"));
45
+ var DeduplicateSettingRows_1 = require("../src/migrations/DeduplicateSettingRows");
46
+ var SettingTable_1 = require("../src/tables/SettingTable");
47
+ var tables_1 = require("../src/tables/tables");
48
+ require("@proteinjs/db-transaction-context");
49
+ require("../generated/index");
50
+ /**
51
+ * The data step that carries a live database to one row per name: over a setting table as it
52
+ * stood before the unique index (duplicate rows of a (scope, name) in it), schema sync refuses to
53
+ * add the index; the migration keeps the newest row of every (scope, name) — by `updated`, then
54
+ * `created` — and deletes the rest, touching no other row; a second run deletes nothing; the index
55
+ * then lands. On a database without the table it does nothing.
56
+ */
57
+ var emulator = { projectId: 'proteinjs-test', instanceName: 'proteinjs-test', databaseName: 'test' };
58
+ describe('keeping one setting row per scope and name ahead of the unique index', function () {
59
+ var systemDb = (0, db_1.getDbAsSystem)();
60
+ var migration = new DeduplicateSettingRows_1.DeduplicateSettingRows().record;
61
+ var driver;
62
+ var dropTable;
63
+ var minutesAgo = function (minutes) { return (0, moment_1.default)().subtract(minutes, 'minutes'); };
64
+ var seed = function (row) { return systemDb.insert(tables_1.tables.Setting, row); };
65
+ var valuesLeft = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
66
+ switch (_a.label) {
67
+ case 0: return [4 /*yield*/, systemDb.query(tables_1.tables.Setting, {})];
68
+ case 1: return [2 /*return*/, (_a.sent()).map(function (row) { return "".concat(row.scope, "/").concat(row.name, "=").concat(row.value); }).sort()];
69
+ }
70
+ }); }); };
71
+ beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
72
+ var beforeTheIndex;
73
+ return __generator(this, function (_a) {
74
+ switch (_a.label) {
75
+ case 0: return [4 /*yield*/, test_1.SpannerEmulatorProvisioner.ensureProvisioned(emulator)];
76
+ case 1:
77
+ _a.sent();
78
+ driver = db_1.Db.getDefaultDbDriver();
79
+ dropTable = (0, test_1.getDropTestTable)(driver);
80
+ return [4 /*yield*/, dropTable(tables_1.tables.Setting)];
81
+ case 2:
82
+ _a.sent();
83
+ beforeTheIndex = new SettingTable_1.SettingTable();
84
+ beforeTheIndex.indexes = [];
85
+ return [4 /*yield*/, driver.getTableManager().loadTable(beforeTheIndex)];
86
+ case 3:
87
+ _a.sent();
88
+ return [2 /*return*/];
89
+ }
90
+ });
91
+ }); }, 120000);
92
+ afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
93
+ return __generator(this, function (_a) {
94
+ switch (_a.label) {
95
+ case 0: return [4 /*yield*/, dropTable(tables_1.tables.Setting)];
96
+ case 1:
97
+ _a.sent();
98
+ test_1.SpannerEmulatorProvisioner.release();
99
+ return [2 /*return*/];
100
+ }
101
+ });
102
+ }); }, 120000);
103
+ test('keeps the newest row of each (scope, name), deletes the rest, and the index then lands', function () { return __awaiter(void 0, void 0, void 0, function () {
104
+ var _a, _b, _c, _d, _e, _f, _g;
105
+ return __generator(this, function (_h) {
106
+ switch (_h.label) {
107
+ case 0:
108
+ // ada/theme three times over: the newest by `updated` is the one written last, not the one created last.
109
+ return [4 /*yield*/, seed({ scope: 'ada', name: 'theme', value: 'first', created: minutesAgo(30), updated: minutesAgo(30) })];
110
+ case 1:
111
+ // ada/theme three times over: the newest by `updated` is the one written last, not the one created last.
112
+ _h.sent();
113
+ return [4 /*yield*/, seed({ scope: 'ada', name: 'theme', value: 'newest', created: minutesAgo(20), updated: minutesAgo(5) })];
114
+ case 2:
115
+ _h.sent();
116
+ return [4 /*yield*/, seed({ scope: 'ada', name: 'theme', value: 'middle', created: minutesAgo(10), updated: minutesAgo(10) })];
117
+ case 3:
118
+ _h.sent();
119
+ // ada/font twice with one `updated`: the later `created` is the newest.
120
+ return [4 /*yield*/, seed({ scope: 'ada', name: 'font', value: 'serif', created: minutesAgo(20), updated: minutesAgo(1) })];
121
+ case 4:
122
+ // ada/font twice with one `updated`: the later `created` is the newest.
123
+ _h.sent();
124
+ return [4 /*yield*/, seed({ scope: 'ada', name: 'font', value: 'sans', created: minutesAgo(15), updated: minutesAgo(1) })];
125
+ case 5:
126
+ _h.sent();
127
+ // One row each: untouched.
128
+ return [4 /*yield*/, seed({ scope: 'ada', name: 'lang', value: 'en' })];
129
+ case 6:
130
+ // One row each: untouched.
131
+ _h.sent();
132
+ return [4 /*yield*/, seed({ scope: 'grace', name: 'theme', value: 'green' })];
133
+ case 7:
134
+ _h.sent();
135
+ return [4 /*yield*/, expect(driver.getTableManager().loadTable(tables_1.tables.Setting)).rejects.toThrow(db_1.DuplicateValuesForUniqueIndexError)];
136
+ case 8:
137
+ _h.sent();
138
+ _a = expect;
139
+ return [4 /*yield*/, migration.estimate()];
140
+ case 9:
141
+ _a.apply(void 0, [_h.sent()]).toEqual({ units: 3, unit: 'rows' });
142
+ _b = expect;
143
+ return [4 /*yield*/, migration.run()];
144
+ case 10:
145
+ _b.apply(void 0, [_h.sent()]).toEqual({ deleted: 3 });
146
+ _c = expect;
147
+ return [4 /*yield*/, valuesLeft()];
148
+ case 11:
149
+ _c.apply(void 0, [_h.sent()]).toEqual(['ada/font=sans', 'ada/lang=en', 'ada/theme=newest', 'grace/theme=green']);
150
+ _d = expect;
151
+ return [4 /*yield*/, migration.estimate()];
152
+ case 12:
153
+ _d.apply(void 0, [_h.sent()]).toEqual({ units: 0, unit: 'rows' });
154
+ _e = expect;
155
+ return [4 /*yield*/, migration.run()];
156
+ case 13:
157
+ _e.apply(void 0, [_h.sent()]).toEqual({ deleted: 0 });
158
+ _f = expect;
159
+ return [4 /*yield*/, valuesLeft()];
160
+ case 14:
161
+ _f.apply(void 0, [_h.sent()]).toEqual(['ada/font=sans', 'ada/lang=en', 'ada/theme=newest', 'grace/theme=green']);
162
+ return [4 /*yield*/, driver.getTableManager().loadTable(tables_1.tables.Setting)];
163
+ case 15:
164
+ _h.sent();
165
+ _g = expect;
166
+ return [4 /*yield*/, driver.getTableManager().schemaMetadata.getIndexes(tables_1.tables.Setting)];
167
+ case 16:
168
+ _g.apply(void 0, [_h.sent()]).toMatchObject({
169
+ setting_scope_name_unique: ['scope', 'name'],
170
+ });
171
+ return [2 /*return*/];
172
+ }
173
+ });
174
+ }); });
175
+ test('a database without the table: nothing to do', function () { return __awaiter(void 0, void 0, void 0, function () {
176
+ var _a, _b;
177
+ return __generator(this, function (_c) {
178
+ switch (_c.label) {
179
+ case 0: return [4 /*yield*/, dropTable(tables_1.tables.Setting)];
180
+ case 1:
181
+ _c.sent();
182
+ _a = expect;
183
+ return [4 /*yield*/, migration.estimate()];
184
+ case 2:
185
+ _a.apply(void 0, [_c.sent()]).toEqual({ units: 0, unit: 'rows' });
186
+ _b = expect;
187
+ return [4 /*yield*/, migration.run()];
188
+ case 3:
189
+ _b.apply(void 0, [_c.sent()]).toEqual({ deleted: 0 });
190
+ return [2 /*return*/];
191
+ }
192
+ });
193
+ }); });
194
+ });
195
+ //# sourceMappingURL=DeduplicateSettingRows.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeduplicateSettingRows.test.js","sourceRoot":"","sources":["../../test/DeduplicateSettingRows.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import '@proteinjs/db-transaction-context';
2
+ import '../generated/index';
3
+ //# sourceMappingURL=SettingsOneRowPerName.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsOneRowPerName.test.d.ts","sourceRoot":"","sources":["../../test/SettingsOneRowPerName.test.ts"],"names":[],"mappings":"AAQA,OAAO,mCAAmC,CAAC;AAC3C,OAAO,oBAAoB,CAAC"}
@@ -0,0 +1,213 @@
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
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ 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;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var db_1 = require("@proteinjs/db");
40
+ var test_1 = require("@proteinjs/db-driver-spanner/test");
41
+ var logger_1 = require("@proteinjs/logger");
42
+ var Settings_1 = require("../src/Settings");
43
+ var tables_1 = require("../src/tables/tables");
44
+ var HeldFirstWriteDriver_1 = require("./util/HeldFirstWriteDriver");
45
+ var TestSession_1 = require("./util/TestSession");
46
+ require("@proteinjs/db-transaction-context");
47
+ require("../generated/index");
48
+ /**
49
+ * One setting row per name within a scope, on the real stack (the Spanner emulator, the default
50
+ * driver and transaction context resolved through reflection — the path `getScopedDb` takes in
51
+ * production): two concurrent `set`s of a fresh name — each observing no row before either
52
+ * writes, the interleaving that created two rows — leave ONE row, `get` reads its value, a later
53
+ * `set` rewrites that row without the driver reporting a refused statement (the common act stays
54
+ * quiet), the table's unique index refuses a second row of the name in the scope by its typed
55
+ * error, and the same name in another scope is another person's row.
56
+ */
57
+ var emulator = { projectId: 'proteinjs-test', instanceName: 'proteinjs-test', databaseName: 'test' };
58
+ var ada = { id: 'settings-test-ada', email: 'ada@settings.test', name: 'Ada' };
59
+ var grace = { id: 'settings-test-grace', email: 'grace@settings.test', name: 'Grace' };
60
+ describe('one setting row per name within a scope', function () {
61
+ var settings = new Settings_1.Settings();
62
+ var systemDb = (0, db_1.getDbAsSystem)();
63
+ var driver;
64
+ var dropTable;
65
+ var rowsOf = function (scope, name) { return systemDb.query(tables_1.tables.Setting, { scope: scope, name: name }); };
66
+ beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
67
+ return __generator(this, function (_a) {
68
+ switch (_a.label) {
69
+ case 0: return [4 /*yield*/, test_1.SpannerEmulatorProvisioner.ensureProvisioned(emulator)];
70
+ case 1:
71
+ _a.sent();
72
+ driver = db_1.Db.getDefaultDbDriver();
73
+ dropTable = (0, test_1.getDropTestTable)(driver);
74
+ return [4 /*yield*/, dropTable(tables_1.tables.Setting)];
75
+ case 2:
76
+ _a.sent();
77
+ return [4 /*yield*/, driver.getTableManager().loadTable(tables_1.tables.Setting)];
78
+ case 3:
79
+ _a.sent();
80
+ TestSession_1.TestSession.as(ada);
81
+ return [2 /*return*/];
82
+ }
83
+ });
84
+ }); }, 120000);
85
+ afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
86
+ return __generator(this, function (_a) {
87
+ switch (_a.label) {
88
+ case 0: return [4 /*yield*/, dropTable(tables_1.tables.Setting)];
89
+ case 1:
90
+ _a.sent();
91
+ test_1.SpannerEmulatorProvisioner.release();
92
+ return [2 /*return*/];
93
+ }
94
+ });
95
+ }); }, 120000);
96
+ test('two concurrent sets of a fresh name, each observing no row before either writes, leave one row', function () { return __awaiter(void 0, void 0, void 0, function () {
97
+ var held, defaultDriver, rows, _a;
98
+ return __generator(this, function (_b) {
99
+ switch (_b.label) {
100
+ case 0:
101
+ held = new HeldFirstWriteDriver_1.HeldFirstWriteDriver(driver, 2);
102
+ defaultDriver = jest.spyOn(db_1.Db, 'getDefaultDbDriver').mockImplementation(function () { return held; });
103
+ _b.label = 1;
104
+ case 1:
105
+ _b.trys.push([1, , 3, 4]);
106
+ return [4 /*yield*/, Promise.all([settings.set('theme', 'dark'), settings.set('theme', 'light')])];
107
+ case 2:
108
+ _b.sent();
109
+ return [3 /*break*/, 4];
110
+ case 3:
111
+ defaultDriver.mockRestore();
112
+ return [7 /*endfinally*/];
113
+ case 4: return [4 /*yield*/, rowsOf(ada.id, 'theme')];
114
+ case 5:
115
+ rows = _b.sent();
116
+ expect(rows.length).toBe(1);
117
+ expect(['dark', 'light']).toContain(rows[0].value);
118
+ _a = expect;
119
+ return [4 /*yield*/, settings.get('theme')];
120
+ case 6:
121
+ _a.apply(void 0, [_b.sent()]).toBe(rows[0].value);
122
+ return [2 /*return*/];
123
+ }
124
+ });
125
+ }); });
126
+ test('a later set rewrites the one row, and the driver writes no error line for it', function () { return __awaiter(void 0, void 0, void 0, function () {
127
+ var internals, driverLogger, lines, _a, _b;
128
+ return __generator(this, function (_c) {
129
+ switch (_c.label) {
130
+ case 0:
131
+ internals = driver;
132
+ driverLogger = internals.logger;
133
+ lines = [];
134
+ internals.logger = new logger_1.Logger({
135
+ name: 'SpannerDriver',
136
+ logLevel: 'info',
137
+ logWriter: { write: function (line) { return lines.push(line); } },
138
+ });
139
+ _c.label = 1;
140
+ case 1:
141
+ _c.trys.push([1, , 3, 4]);
142
+ return [4 /*yield*/, settings.set('theme', 'blue')];
143
+ case 2:
144
+ _c.sent();
145
+ return [3 /*break*/, 4];
146
+ case 3:
147
+ internals.logger = driverLogger;
148
+ return [7 /*endfinally*/];
149
+ case 4:
150
+ expect(lines.filter(function (line) { return line.logLevel === 'error' || line.logLevel === 'warn'; })).toEqual([]);
151
+ _a = expect;
152
+ return [4 /*yield*/, rowsOf(ada.id, 'theme')];
153
+ case 5:
154
+ _a.apply(void 0, [(_c.sent()).map(function (row) { return row.value; })]).toEqual(['blue']);
155
+ _b = expect;
156
+ return [4 /*yield*/, settings.get('theme')];
157
+ case 6:
158
+ _b.apply(void 0, [_c.sent()]).toBe('blue');
159
+ return [2 /*return*/];
160
+ }
161
+ });
162
+ }); });
163
+ test('the unique index refuses a second row of the name in the scope by its typed error', function () { return __awaiter(void 0, void 0, void 0, function () {
164
+ var refusal, error_1, _a;
165
+ return __generator(this, function (_b) {
166
+ switch (_b.label) {
167
+ case 0:
168
+ _b.trys.push([0, 2, , 3]);
169
+ return [4 /*yield*/, systemDb.insert(tables_1.tables.Setting, { scope: ada.id, name: 'theme', value: 'red' })];
170
+ case 1:
171
+ _b.sent();
172
+ return [3 /*break*/, 3];
173
+ case 2:
174
+ error_1 = _b.sent();
175
+ refusal = error_1;
176
+ return [3 /*break*/, 3];
177
+ case 3:
178
+ expect((0, db_1.isDuplicateKeyError)(refusal)).toBe(true);
179
+ _a = expect;
180
+ return [4 /*yield*/, rowsOf(ada.id, 'theme')];
181
+ case 4:
182
+ _a.apply(void 0, [(_b.sent()).map(function (row) { return row.value; })]).toEqual(['blue']);
183
+ return [2 /*return*/];
184
+ }
185
+ });
186
+ }); });
187
+ test('the same name in another scope is another row', function () { return __awaiter(void 0, void 0, void 0, function () {
188
+ var _a, _b, _c;
189
+ return __generator(this, function (_d) {
190
+ switch (_d.label) {
191
+ case 0:
192
+ TestSession_1.TestSession.as(grace);
193
+ return [4 /*yield*/, settings.set('theme', 'green')];
194
+ case 1:
195
+ _d.sent();
196
+ _a = expect;
197
+ return [4 /*yield*/, settings.get('theme')];
198
+ case 2:
199
+ _a.apply(void 0, [_d.sent()]).toBe('green');
200
+ _b = expect;
201
+ return [4 /*yield*/, rowsOf(grace.id, 'theme')];
202
+ case 3:
203
+ _b.apply(void 0, [(_d.sent()).map(function (row) { return row.value; })]).toEqual(['green']);
204
+ _c = expect;
205
+ return [4 /*yield*/, rowsOf(ada.id, 'theme')];
206
+ case 4:
207
+ _c.apply(void 0, [(_d.sent()).map(function (row) { return row.value; })]).toEqual(['blue']);
208
+ return [2 /*return*/];
209
+ }
210
+ });
211
+ }); });
212
+ });
213
+ //# sourceMappingURL=SettingsOneRowPerName.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsOneRowPerName.test.js","sourceRoot":"","sources":["../../test/SettingsOneRowPerName.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { DbDriver, DbDriverDmlStatementConfig, DbDriverQueryStatementConfig, SerializedRecord, Statement, TableManager } from '@proteinjs/db';
2
+ /**
3
+ * The real driver, with `writers` concurrent writers held in lockstep at their first write: each
4
+ * writer's first write is held until every writer has reached one, then all run; every later
5
+ * write waits until every first write has settled. The interleaving under which concurrent
6
+ * writers of one fresh name each observe no row before any of them writes one — what two requests
7
+ * produce when they write one setting at the same moment — made exact, so the suite does not
8
+ * rest on timing (the emulator serializes concurrent transactions with aborts and retries, which
9
+ * on their own let one writer's second write slip in before the other's first has settled).
10
+ * Reads go straight through. A barrier that never fills fails the held writers with a named
11
+ * error instead of hanging the suite.
12
+ */
13
+ export declare class HeldFirstWriteDriver implements DbDriver {
14
+ private readonly driver;
15
+ private readonly writers;
16
+ private arrivals;
17
+ private release;
18
+ private readonly released;
19
+ private readonly firstWrites;
20
+ constructor(driver: DbDriver, writers: number);
21
+ runDml(generateStatement: (config: DbDriverDmlStatementConfig) => Statement, transaction?: unknown): Promise<number>;
22
+ runQuery(generateStatement: (config: DbDriverQueryStatementConfig) => Statement, transaction?: unknown): Promise<SerializedRecord[]>;
23
+ runTransaction<T>(fn: (transaction: unknown) => Promise<T>): Promise<T>;
24
+ isDuplicateKeyError(error: unknown): boolean;
25
+ getDbName(): string;
26
+ createDbIfNotExists(): Promise<void>;
27
+ createDb(name: string, options?: {
28
+ ddl?: string[];
29
+ }): Promise<void>;
30
+ dropDb(name: string): Promise<void>;
31
+ getTableManager(): TableManager;
32
+ getOperationDeadlineMs(): number;
33
+ /** Forwarded; a driver that declares no bound would leave this undeclared, which a wrapper cannot express. */
34
+ getStatementParameterLimit(): number;
35
+ }
36
+ //# sourceMappingURL=HeldFirstWriteDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeldFirstWriteDriver.d.ts","sourceRoot":"","sources":["../../../test/util/HeldFirstWriteDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,0BAA0B,EAC1B,4BAA4B,EAC5B,gBAAgB,EAChB,SAAS,EACT,YAAY,EACb,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;GAUG;AACH,qBAAa,oBAAqB,YAAW,QAAQ;IAOjD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;gBAGhC,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,MAAM;IAW5B,MAAM,CACV,iBAAiB,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,SAAS,EACpE,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,MAAM,CAAC;IAoBlB,QAAQ,CACN,iBAAiB,EAAE,CAAC,MAAM,EAAE,4BAA4B,KAAK,SAAS,EACtE,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAI9B,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIvE,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAI5C,SAAS,IAAI,MAAM;IAInB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAInE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,eAAe,IAAI,YAAY;IAI/B,sBAAsB,IAAI,MAAM;IAIhC,8GAA8G;IAC9G,0BAA0B,IAAI,MAAM;CAOrC"}
@@ -0,0 +1,130 @@
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
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ 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;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.HeldFirstWriteDriver = void 0;
40
+ /**
41
+ * The real driver, with `writers` concurrent writers held in lockstep at their first write: each
42
+ * writer's first write is held until every writer has reached one, then all run; every later
43
+ * write waits until every first write has settled. The interleaving under which concurrent
44
+ * writers of one fresh name each observe no row before any of them writes one — what two requests
45
+ * produce when they write one setting at the same moment — made exact, so the suite does not
46
+ * rest on timing (the emulator serializes concurrent transactions with aborts and retries, which
47
+ * on their own let one writer's second write slip in before the other's first has settled).
48
+ * Reads go straight through. A barrier that never fills fails the held writers with a named
49
+ * error instead of hanging the suite.
50
+ */
51
+ var HeldFirstWriteDriver = /** @class */ (function () {
52
+ function HeldFirstWriteDriver(driver, writers) {
53
+ var _this = this;
54
+ this.driver = driver;
55
+ this.writers = writers;
56
+ this.arrivals = 0;
57
+ this.firstWrites = [];
58
+ this.released = new Promise(function (resolve, reject) {
59
+ _this.release = resolve;
60
+ setTimeout(function () { return reject(new Error("Only ".concat(_this.arrivals, " of ").concat(writers, " writers reached their first write"))); }, 10000).unref();
61
+ });
62
+ }
63
+ HeldFirstWriteDriver.prototype.runDml = function (generateStatement, transaction) {
64
+ return __awaiter(this, void 0, void 0, function () {
65
+ var write;
66
+ return __generator(this, function (_a) {
67
+ switch (_a.label) {
68
+ case 0:
69
+ if (!(this.arrivals < this.writers)) return [3 /*break*/, 3];
70
+ this.arrivals += 1;
71
+ if (this.arrivals === this.writers) {
72
+ this.release();
73
+ }
74
+ return [4 /*yield*/, this.released];
75
+ case 1:
76
+ _a.sent();
77
+ write = this.driver.runDml(generateStatement, transaction);
78
+ this.firstWrites.push(write.then(function () { return undefined; }, function () { return undefined; }));
79
+ return [4 /*yield*/, write];
80
+ case 2: return [2 /*return*/, _a.sent()];
81
+ case 3: return [4 /*yield*/, Promise.all(this.firstWrites)];
82
+ case 4:
83
+ _a.sent();
84
+ return [4 /*yield*/, this.driver.runDml(generateStatement, transaction)];
85
+ case 5: return [2 /*return*/, _a.sent()];
86
+ }
87
+ });
88
+ });
89
+ };
90
+ HeldFirstWriteDriver.prototype.runQuery = function (generateStatement, transaction) {
91
+ return this.driver.runQuery(generateStatement, transaction);
92
+ };
93
+ HeldFirstWriteDriver.prototype.runTransaction = function (fn) {
94
+ return this.driver.runTransaction(fn);
95
+ };
96
+ HeldFirstWriteDriver.prototype.isDuplicateKeyError = function (error) {
97
+ var _a, _b, _c;
98
+ return (_c = (_b = (_a = this.driver).isDuplicateKeyError) === null || _b === void 0 ? void 0 : _b.call(_a, error)) !== null && _c !== void 0 ? _c : false;
99
+ };
100
+ HeldFirstWriteDriver.prototype.getDbName = function () {
101
+ return this.driver.getDbName();
102
+ };
103
+ HeldFirstWriteDriver.prototype.createDbIfNotExists = function () {
104
+ return this.driver.createDbIfNotExists();
105
+ };
106
+ HeldFirstWriteDriver.prototype.createDb = function (name, options) {
107
+ return this.driver.createDb(name, options);
108
+ };
109
+ HeldFirstWriteDriver.prototype.dropDb = function (name) {
110
+ return this.driver.dropDb(name);
111
+ };
112
+ HeldFirstWriteDriver.prototype.getTableManager = function () {
113
+ return this.driver.getTableManager();
114
+ };
115
+ HeldFirstWriteDriver.prototype.getOperationDeadlineMs = function () {
116
+ return this.driver.getOperationDeadlineMs();
117
+ };
118
+ /** Forwarded; a driver that declares no bound would leave this undeclared, which a wrapper cannot express. */
119
+ HeldFirstWriteDriver.prototype.getStatementParameterLimit = function () {
120
+ var _a, _b;
121
+ var limit = (_b = (_a = this.driver).getStatementParameterLimit) === null || _b === void 0 ? void 0 : _b.call(_a);
122
+ if (limit === undefined) {
123
+ throw new Error('The wrapped driver declares no statement parameter limit');
124
+ }
125
+ return limit;
126
+ };
127
+ return HeldFirstWriteDriver;
128
+ }());
129
+ exports.HeldFirstWriteDriver = HeldFirstWriteDriver;
130
+ //# sourceMappingURL=HeldFirstWriteDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeldFirstWriteDriver.js","sourceRoot":"","sources":["../../../test/util/HeldFirstWriteDriver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { SessionData, SessionDataStorage } from '@proteinjs/server-api';
2
+ /**
3
+ * The one session a suite's calls run in: a process-wide session store (the async-context store a
4
+ * server uses does not survive jest's hook and test boundaries), registered where `Session` looks
5
+ * for one, carrying the user the scoped db reads its scope from. `as(user)` re-seats the session
6
+ * on another user, so a suite can act as two people.
7
+ */
8
+ export declare class TestSession implements SessionDataStorage {
9
+ environment: "node";
10
+ priority: number;
11
+ private static data;
12
+ /** Act as `user` from here on: the session's user, and the scope every scoped write and query carries. */
13
+ static as(user: {
14
+ id: string;
15
+ email: string;
16
+ name: string;
17
+ }): void;
18
+ setData(data: SessionData): void;
19
+ getData(): SessionData;
20
+ private static register;
21
+ }
22
+ //# sourceMappingURL=TestSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestSession.d.ts","sourceRoot":"","sources":["../../../test/util/TestSession.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGjF;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,kBAAkB;IACpD,WAAW,SAAmB;IAC9B,QAAQ,SAAK;IACb,OAAO,CAAC,MAAM,CAAC,IAAI,CAA0E;IAE7F,0GAA0G;IAC1G,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAMlE,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAIhC,OAAO,IAAI,WAAW;IAItB,OAAO,CAAC,MAAM,CAAC,QAAQ;CAIxB"}