@proteinjs/db 1.35.1 → 1.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +7 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/generated/test/index.d.ts.map +1 -1
- package/dist/generated/test/index.js +7 -1
- package/dist/generated/test/index.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/src/Columns.d.ts +5 -1
- package/dist/src/Columns.d.ts.map +1 -1
- package/dist/src/Columns.js +2 -1
- package/dist/src/Columns.js.map +1 -1
- package/dist/src/Db.d.ts +25 -0
- package/dist/src/Db.d.ts.map +1 -1
- package/dist/src/Db.js +219 -22
- package/dist/src/Db.js.map +1 -1
- package/dist/src/Record.d.ts +7 -1
- package/dist/src/Record.d.ts.map +1 -1
- package/dist/src/Record.js +56 -21
- package/dist/src/Record.js.map +1 -1
- package/dist/src/Table.d.ts +82 -1
- package/dist/src/Table.d.ts.map +1 -1
- package/dist/src/Table.js +24 -1
- package/dist/src/Table.js.map +1 -1
- package/dist/src/TableQueryTransformProvider.d.ts +17 -0
- package/dist/src/TableQueryTransformProvider.d.ts.map +1 -0
- package/dist/src/TableQueryTransformProvider.js +34 -0
- package/dist/src/TableQueryTransformProvider.js.map +1 -0
- package/dist/src/encryption/Base64Url.d.ts +12 -0
- package/dist/src/encryption/Base64Url.d.ts.map +1 -0
- package/dist/src/encryption/Base64Url.js +21 -0
- package/dist/src/encryption/Base64Url.js.map +1 -0
- package/dist/src/encryption/DataEncryptionKeyTable.d.ts +33 -0
- package/dist/src/encryption/DataEncryptionKeyTable.d.ts.map +1 -0
- package/dist/src/encryption/DataEncryptionKeyTable.js +45 -0
- package/dist/src/encryption/DataEncryptionKeyTable.js.map +1 -0
- package/dist/src/encryption/DataKeyStore.d.ts +84 -0
- package/dist/src/encryption/DataKeyStore.d.ts.map +1 -0
- package/dist/src/encryption/DataKeyStore.js +481 -0
- package/dist/src/encryption/DataKeyStore.js.map +1 -0
- package/dist/src/encryption/DbEncryptionConfig.d.ts +62 -0
- package/dist/src/encryption/DbEncryptionConfig.d.ts.map +1 -0
- package/dist/src/encryption/DbEncryptionConfig.js +67 -0
- package/dist/src/encryption/DbEncryptionConfig.js.map +1 -0
- package/dist/src/encryption/EncryptedColumnQueryError.d.ts +10 -0
- package/dist/src/encryption/EncryptedColumnQueryError.d.ts.map +1 -0
- package/dist/src/encryption/EncryptedColumnQueryError.js +38 -0
- package/dist/src/encryption/EncryptedColumnQueryError.js.map +1 -0
- package/dist/src/encryption/EncryptedColumnQueryTransform.d.ts +52 -0
- package/dist/src/encryption/EncryptedColumnQueryTransform.d.ts.map +1 -0
- package/dist/src/encryption/EncryptedColumnQueryTransform.js +423 -0
- package/dist/src/encryption/EncryptedColumnQueryTransform.js.map +1 -0
- package/dist/src/encryption/EncryptedColumns.d.ts +69 -0
- package/dist/src/encryption/EncryptedColumns.d.ts.map +1 -0
- package/dist/src/encryption/EncryptedColumns.js +286 -0
- package/dist/src/encryption/EncryptedColumns.js.map +1 -0
- package/dist/src/encryption/EncryptionDerivedTableRegistry.d.ts +20 -0
- package/dist/src/encryption/EncryptionDerivedTableRegistry.d.ts.map +1 -0
- package/dist/src/encryption/EncryptionDerivedTableRegistry.js +36 -0
- package/dist/src/encryption/EncryptionDerivedTableRegistry.js.map +1 -0
- package/dist/src/encryption/EncryptionEnvelope.d.ts +25 -0
- package/dist/src/encryption/EncryptionEnvelope.d.ts.map +1 -0
- package/dist/src/encryption/EncryptionEnvelope.js +59 -0
- package/dist/src/encryption/EncryptionEnvelope.js.map +1 -0
- package/dist/src/encryption/EncryptionLifecycleWalker.d.ts +65 -0
- package/dist/src/encryption/EncryptionLifecycleWalker.d.ts.map +1 -0
- package/dist/src/encryption/EncryptionLifecycleWalker.js +343 -0
- package/dist/src/encryption/EncryptionLifecycleWalker.js.map +1 -0
- package/dist/src/encryption/EncryptionRecordHooks.d.ts +47 -0
- package/dist/src/encryption/EncryptionRecordHooks.d.ts.map +1 -0
- package/dist/src/encryption/EncryptionRecordHooks.js +229 -0
- package/dist/src/encryption/EncryptionRecordHooks.js.map +1 -0
- package/dist/src/encryption/EncryptionTokenMaintenance.d.ts +34 -0
- package/dist/src/encryption/EncryptionTokenMaintenance.d.ts.map +1 -0
- package/dist/src/encryption/EncryptionTokenMaintenance.js +211 -0
- package/dist/src/encryption/EncryptionTokenMaintenance.js.map +1 -0
- package/dist/src/encryption/InMemoryMasterKeyProvider.d.ts +20 -0
- package/dist/src/encryption/InMemoryMasterKeyProvider.d.ts.map +1 -0
- package/dist/src/encryption/InMemoryMasterKeyProvider.js +87 -0
- package/dist/src/encryption/InMemoryMasterKeyProvider.js.map +1 -0
- package/dist/src/encryption/MasterKeyProvider.d.ts +27 -0
- package/dist/src/encryption/MasterKeyProvider.d.ts.map +1 -0
- package/dist/src/encryption/MasterKeyProvider.js +3 -0
- package/dist/src/encryption/MasterKeyProvider.js.map +1 -0
- package/dist/src/encryption/SearchTokenizer.d.ts +49 -0
- package/dist/src/encryption/SearchTokenizer.d.ts.map +1 -0
- package/dist/src/encryption/SearchTokenizer.js +106 -0
- package/dist/src/encryption/SearchTokenizer.js.map +1 -0
- package/dist/src/schema/TableManager.d.ts +10 -0
- package/dist/src/schema/TableManager.d.ts.map +1 -1
- package/dist/src/schema/TableManager.js +62 -5
- package/dist/src/schema/TableManager.js.map +1 -1
- package/dist/src/tables/MigrationTable.d.ts +2 -2
- package/dist/test/EncryptedColumnsSchema.test.d.ts +2 -0
- package/dist/test/EncryptedColumnsSchema.test.d.ts.map +1 -0
- package/dist/test/EncryptedColumnsSchema.test.js +151 -0
- package/dist/test/EncryptedColumnsSchema.test.js.map +1 -0
- package/dist/test/EncryptionEnvelope.test.d.ts +2 -0
- package/dist/test/EncryptionEnvelope.test.d.ts.map +1 -0
- package/dist/test/EncryptionEnvelope.test.js +109 -0
- package/dist/test/EncryptionEnvelope.test.js.map +1 -0
- package/dist/test/EncryptionQueryContract.test.d.ts +2 -0
- package/dist/test/EncryptionQueryContract.test.d.ts.map +1 -0
- package/dist/test/EncryptionQueryContract.test.js +399 -0
- package/dist/test/EncryptionQueryContract.test.js.map +1 -0
- package/dist/test/SearchTokenizer.test.d.ts +2 -0
- package/dist/test/SearchTokenizer.test.d.ts.map +1 -0
- package/dist/test/SearchTokenizer.test.js +64 -0
- package/dist/test/SearchTokenizer.test.js.map +1 -0
- package/dist/test/reusable/CascadeDeleteTests.js +2 -2
- package/dist/test/reusable/CascadeDeleteTests.js.map +1 -1
- package/dist/test/reusable/ColumnTypesTests.js +2 -2
- package/dist/test/reusable/ColumnTypesTests.js.map +1 -1
- package/dist/test/reusable/CrudTests.js +2 -2
- package/dist/test/reusable/CrudTests.js.map +1 -1
- package/dist/test/reusable/DynamicReferenceColumnTests.js +2 -2
- package/dist/test/reusable/DynamicReferenceColumnTests.js.map +1 -1
- package/dist/test/reusable/PreloadReferencesTests.js +2 -2
- package/dist/test/reusable/PreloadReferencesTests.js.map +1 -1
- package/dist/test/reusable/RecordIteratorTests.js +2 -2
- package/dist/test/reusable/RecordIteratorTests.js.map +1 -1
- package/dist/test/reusable/TableManagerTests.js +2 -2
- package/dist/test/reusable/TableManagerTests.js.map +1 -1
- package/dist/test/reusable/TransactionTests.js +2 -2
- package/dist/test/reusable/TransactionTests.js.map +1 -1
- package/generated/index.ts +7 -1
- package/generated/test/index.ts +7 -1
- package/index.ts +16 -0
- package/package.json +3 -3
- package/src/Columns.ts +6 -2
- package/src/Db.ts +137 -12
- package/src/Record.ts +21 -4
- package/src/Table.ts +109 -2
- package/src/TableQueryTransformProvider.ts +31 -0
- package/src/encryption/Base64Url.ts +14 -0
- package/src/encryption/DataEncryptionKeyTable.ts +40 -0
- package/src/encryption/DataKeyStore.ts +305 -0
- package/src/encryption/DbEncryptionConfig.ts +92 -0
- package/src/encryption/EncryptedColumnQueryError.ts +15 -0
- package/src/encryption/EncryptedColumnQueryTransform.ts +383 -0
- package/src/encryption/EncryptedColumns.ts +318 -0
- package/src/encryption/EncryptionDerivedTableRegistry.ts +34 -0
- package/src/encryption/EncryptionEnvelope.ts +60 -0
- package/src/encryption/EncryptionLifecycleWalker.ts +243 -0
- package/src/encryption/EncryptionRecordHooks.ts +167 -0
- package/src/encryption/EncryptionTokenMaintenance.ts +123 -0
- package/src/encryption/InMemoryMasterKeyProvider.ts +42 -0
- package/src/encryption/MasterKeyProvider.ts +26 -0
- package/src/encryption/SearchTokenizer.ts +103 -0
- package/src/schema/TableManager.ts +43 -1
- package/test/EncryptedColumnsSchema.test.ts +172 -0
- package/test/EncryptionEnvelope.test.ts +66 -0
- package/test/EncryptionQueryContract.test.ts +195 -0
- package/test/SearchTokenizer.test.ts +67 -0
- package/test/reusable/CascadeDeleteTests.ts +2 -2
- package/test/reusable/ColumnTypesTests.ts +2 -2
- package/test/reusable/CrudTests.ts +2 -2
- package/test/reusable/DynamicReferenceColumnTests.ts +2 -2
- package/test/reusable/PreloadReferencesTests.ts +2 -2
- package/test/reusable/RecordIteratorTests.ts +2 -2
- package/test/reusable/TableManagerTests.ts +2 -2
- package/test/reusable/TransactionTests.ts +2 -2
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
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;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.EncryptedColumnQueryTransform = void 0;
|
|
51
|
+
var util_1 = require("@proteinjs/util");
|
|
52
|
+
var db_query_1 = require("@proteinjs/db-query");
|
|
53
|
+
var EncryptedColumns_1 = require("./EncryptedColumns");
|
|
54
|
+
var EncryptedColumnQueryError_1 = require("./EncryptedColumnQueryError");
|
|
55
|
+
var DbEncryptionConfig_1 = require("./DbEncryptionConfig");
|
|
56
|
+
var DataKeyStore_1 = require("./DataKeyStore");
|
|
57
|
+
var SearchTokenizer_1 = require("./SearchTokenizer");
|
|
58
|
+
/**
|
|
59
|
+
* An encrypted column's `ColumnQueryTransform` — the query-side half of the column's
|
|
60
|
+
* encryption contract (`EncryptionRecordHooks` is the storage-side half). Attached to the
|
|
61
|
+
* column by `EncryptedColumns.ensureSchema` (derived from `ColumnOptions.encrypted`, never
|
|
62
|
+
* caller-declared) and applied by `QueryBuilder.applyColumnTransforms` on every query, so
|
|
63
|
+
* callers keep writing exactly what they write today:
|
|
64
|
+
*
|
|
65
|
+
* - **Equality** (`=`, `IN`) on a column declared `searchable: 'equality'` rewrites onto the
|
|
66
|
+
* whole-value fingerprint companion — one indexed lookup, exact (fingerprints cover the
|
|
67
|
+
* exact value), fingerprinted once per accessible key owner (shared-scope reads OR the
|
|
68
|
+
* owners' fingerprints).
|
|
69
|
+
* - **Contains / prefix LIKE** on a column declared `searchable: 'contains'` resolves in two
|
|
70
|
+
* index-bounded steps: (1) the token table answers a candidate id set — rows holding ALL
|
|
71
|
+
* of the query's fragment fingerprints under some accessible owner key; (2) candidates
|
|
72
|
+
* are verified against the decrypted value (the pg_trgm recheck semantics — no false
|
|
73
|
+
* positive survives, no true match ≥3-chars-per-word is missed). The condition then
|
|
74
|
+
* rewrites to the VERIFIED id set, so it composes exactly under any boolean structure,
|
|
75
|
+
* ORDER BY, pagination, and COUNT.
|
|
76
|
+
* - **Out-of-contract shapes are REJECTED loudly at query-build time** — undeclared ORDER BY,
|
|
77
|
+
* ranges, arbitrary LIKE patterns, aggregation over the value — each error naming the
|
|
78
|
+
* sanctioned paths. A limitation a developer cannot hit silently is a contract; one they
|
|
79
|
+
* discover in production is a bug.
|
|
80
|
+
*/
|
|
81
|
+
var EncryptedColumnQueryTransform = /** @class */ (function () {
|
|
82
|
+
function EncryptedColumnQueryTransform(table, prop, column, config, runtime) {
|
|
83
|
+
this.table = table;
|
|
84
|
+
this.prop = prop;
|
|
85
|
+
this.column = column;
|
|
86
|
+
this.config = config;
|
|
87
|
+
this.runtime = runtime;
|
|
88
|
+
this.encryptedColumns = new EncryptedColumns_1.EncryptedColumns();
|
|
89
|
+
this.tokenizer = new SearchTokenizer_1.SearchTokenizer();
|
|
90
|
+
}
|
|
91
|
+
EncryptedColumnQueryTransform.prototype.transformCondition = function (condition, context) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
93
|
+
var operator, _a;
|
|
94
|
+
return __generator(this, function (_b) {
|
|
95
|
+
switch (_b.label) {
|
|
96
|
+
case 0:
|
|
97
|
+
operator = condition.operator;
|
|
98
|
+
if (operator === 'IS NULL' || operator === 'IS NOT NULL') {
|
|
99
|
+
return [2 /*return*/, undefined]; // encryption preserves null-ness — native
|
|
100
|
+
}
|
|
101
|
+
if (condition.value === null) {
|
|
102
|
+
return [2 /*return*/, undefined]; // `= null` renders IS NULL; a normalized empty-IN renders 1=0
|
|
103
|
+
}
|
|
104
|
+
if ((0, util_1.isInstanceOf)(condition.value, db_query_1.QueryBuilder)) {
|
|
105
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot compare encrypted column `".concat(this.table.name, ".").concat(this.prop, "` against a subquery. ") +
|
|
106
|
+
"Compare on a metadata column, or resolve the subquery app-side and pass literal values.");
|
|
107
|
+
}
|
|
108
|
+
_a = operator;
|
|
109
|
+
switch (_a) {
|
|
110
|
+
case '=': return [3 /*break*/, 1];
|
|
111
|
+
case 'IN': return [3 /*break*/, 1];
|
|
112
|
+
case 'LIKE': return [3 /*break*/, 3];
|
|
113
|
+
case '<': return [3 /*break*/, 5];
|
|
114
|
+
case '>': return [3 /*break*/, 5];
|
|
115
|
+
case '<=': return [3 /*break*/, 5];
|
|
116
|
+
case '>=': return [3 /*break*/, 5];
|
|
117
|
+
case 'BETWEEN': return [3 /*break*/, 5];
|
|
118
|
+
}
|
|
119
|
+
return [3 /*break*/, 6];
|
|
120
|
+
case 1: return [4 /*yield*/, this.equalityCondition(condition, context)];
|
|
121
|
+
case 2: return [2 /*return*/, _b.sent()];
|
|
122
|
+
case 3: return [4 /*yield*/, this.likeCondition(condition, context)];
|
|
123
|
+
case 4: return [2 /*return*/, _b.sent()];
|
|
124
|
+
case 5: throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot apply range condition (".concat(operator, ") to encrypted column `").concat(this.table.name, ".").concat(this.prop, "`. ") +
|
|
125
|
+
"Ranges over an encrypted value are not supported. Options: filter by a metadata column; " +
|
|
126
|
+
"filter the fetched, decrypted rows app-side (bounded sets); or use " +
|
|
127
|
+
"encrypted: { sortKey: { revealPrefix: N } } ordering with app-side refinement.");
|
|
128
|
+
case 6:
|
|
129
|
+
// <>, !=, NOT IN, NOT, NOT LIKE — negations over lossy/keyed representations cannot
|
|
130
|
+
// be answered exactly DB-side.
|
|
131
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot apply ".concat(operator, " to encrypted column `").concat(this.table.name, ".").concat(this.prop, "`. Options: use ") +
|
|
132
|
+
"equality/contains conditions (searchable declarations) and exclude app-side; or filter " +
|
|
133
|
+
"by a metadata column.");
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
EncryptedColumnQueryTransform.prototype.transformSort = function (criteria) {
|
|
139
|
+
if (criteria.byValues && criteria.byValues.length > 0) {
|
|
140
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot ORDER BY specific values of encrypted column `".concat(this.table.name, ".").concat(this.prop, "`: value-CASE ") +
|
|
141
|
+
"ordering compares the stored value. Options: order by a metadata column; or order the " +
|
|
142
|
+
"fetched rows for display (bounded sets).");
|
|
143
|
+
}
|
|
144
|
+
if (!this.config.sortKey) {
|
|
145
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot ORDER BY encrypted column `".concat(this.table.name, ".").concat(this.prop, "` (no sortKey declared). Options: ") +
|
|
146
|
+
"sort by a metadata column; sort the fetched rows for display (bounded sets); or declare " +
|
|
147
|
+
"encrypted: { sortKey: { revealPrefix: N } } \u2014 a documented, bounded reveal of the first " +
|
|
148
|
+
"N characters' order.");
|
|
149
|
+
}
|
|
150
|
+
return __assign(__assign({}, criteria), { field: this.encryptedColumns.sortCompanionProp(this.table, this.prop) });
|
|
151
|
+
};
|
|
152
|
+
EncryptedColumnQueryTransform.prototype.transformAggregate = function (aggregate) {
|
|
153
|
+
if (aggregate.function === 'COUNT') {
|
|
154
|
+
return undefined; // counting rows never reads the value — native
|
|
155
|
+
}
|
|
156
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot ".concat(aggregate.function, " encrypted column `").concat(this.table.name, ".").concat(this.prop, "`. Aggregation over ") +
|
|
157
|
+
"an encrypted value is not supported. Options: aggregate a metadata column; or fetch the " +
|
|
158
|
+
"rows and aggregate the decrypted values app-side (bounded sets).");
|
|
159
|
+
};
|
|
160
|
+
EncryptedColumnQueryTransform.prototype.transformGroupByField = function () {
|
|
161
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot GROUP BY encrypted column `".concat(this.table.name, ".").concat(this.prop, "`. Grouping over an encrypted ") +
|
|
162
|
+
"value is not supported. Options: group by a metadata column; or group the fetched, " +
|
|
163
|
+
"decrypted rows app-side (bounded sets).");
|
|
164
|
+
};
|
|
165
|
+
EncryptedColumnQueryTransform.prototype.equalityCondition = function (condition, context) {
|
|
166
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
167
|
+
var values, _i, values_1, value, keys, fingerprints, _a, keys_1, key, _b, values_2, value, companionProp;
|
|
168
|
+
return __generator(this, function (_c) {
|
|
169
|
+
switch (_c.label) {
|
|
170
|
+
case 0:
|
|
171
|
+
if (this.config.searchable !== 'equality') {
|
|
172
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot compare encrypted column `".concat(this.table.name, ".").concat(this.prop, "` by value (").concat(condition.operator, "): ") +
|
|
173
|
+
"the column does not declare encrypted: { searchable: 'equality' }. Options: declare it (plus the " +
|
|
174
|
+
"lifecycle backfill) for indexed exact lookups; declare searchable: 'contains' and use LIKE; " +
|
|
175
|
+
"or look the row up by a metadata column.");
|
|
176
|
+
}
|
|
177
|
+
if (!context.caseSensitive) {
|
|
178
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot case-insensitively compare encrypted column `".concat(this.table.name, ".").concat(this.prop, "`: equality ") +
|
|
179
|
+
"fingerprints cover the exact value. Options: compare the exact value; or declare " +
|
|
180
|
+
"searchable: 'contains' and use LIKE for token-normalized matching.");
|
|
181
|
+
}
|
|
182
|
+
values = Array.isArray(condition.value) ? condition.value : [condition.value];
|
|
183
|
+
for (_i = 0, values_1 = values; _i < values_1.length; _i++) {
|
|
184
|
+
value = values_1[_i];
|
|
185
|
+
if (typeof value !== 'string') {
|
|
186
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Encrypted column `".concat(this.table.name, ".").concat(this.prop, "` equality values must be strings; ") +
|
|
187
|
+
"got ".concat(typeof value, "."));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return [4 /*yield*/, this.accessibleIndexKeys()];
|
|
191
|
+
case 1:
|
|
192
|
+
keys = _c.sent();
|
|
193
|
+
fingerprints = [];
|
|
194
|
+
for (_a = 0, keys_1 = keys; _a < keys_1.length; _a++) {
|
|
195
|
+
key = keys_1[_a];
|
|
196
|
+
for (_b = 0, values_2 = values; _b < values_2.length; _b++) {
|
|
197
|
+
value = values_2[_b];
|
|
198
|
+
fingerprints.push(this.tokenizer.equalityFingerprint(value, key.indexKey));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
companionProp = this.encryptedColumns.eqCompanionProp(this.table, this.prop);
|
|
202
|
+
if (fingerprints.length === 1) {
|
|
203
|
+
return [2 /*return*/, { field: companionProp, operator: '=', value: fingerprints[0] }];
|
|
204
|
+
}
|
|
205
|
+
return [2 /*return*/, { field: companionProp, operator: 'IN', value: fingerprints }];
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
EncryptedColumnQueryTransform.prototype.likeCondition = function (condition, context) {
|
|
211
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
212
|
+
var shape, verifiedIds;
|
|
213
|
+
return __generator(this, function (_a) {
|
|
214
|
+
switch (_a.label) {
|
|
215
|
+
case 0:
|
|
216
|
+
if (this.config.searchable !== 'contains') {
|
|
217
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot LIKE-search encrypted column `".concat(this.table.name, ".").concat(this.prop, "`: the column does not declare ") +
|
|
218
|
+
"encrypted: { searchable: 'contains' }. Options: declare it (plus the lifecycle backfill) " +
|
|
219
|
+
"for indexed contains/prefix search; or search a metadata column.");
|
|
220
|
+
}
|
|
221
|
+
shape = this.parseLikePattern(condition.value);
|
|
222
|
+
if (shape.kind === 'match-any') {
|
|
223
|
+
// LIKE '%' / '%%' — matches every non-null value.
|
|
224
|
+
return [2 /*return*/, { field: this.prop, operator: 'IS NOT NULL' }];
|
|
225
|
+
}
|
|
226
|
+
return [4 /*yield*/, this.verifiedCandidateIds(shape, context.caseSensitive)];
|
|
227
|
+
case 1:
|
|
228
|
+
verifiedIds = _a.sent();
|
|
229
|
+
return [2 /*return*/, { field: 'id', operator: 'IN', value: verifiedIds }];
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* The two index-bounded steps of contains search (class doc): token-cover candidates,
|
|
236
|
+
* then decrypt-and-verify. Returns the ids whose values TRULY match the pattern.
|
|
237
|
+
*/
|
|
238
|
+
EncryptedColumnQueryTransform.prototype.verifiedCandidateIds = function (shape, caseSensitive) {
|
|
239
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
240
|
+
var keys, fragments, candidateIds, verifyQb, rows, verified, _i, rows_1, row, value;
|
|
241
|
+
return __generator(this, function (_a) {
|
|
242
|
+
switch (_a.label) {
|
|
243
|
+
case 0: return [4 /*yield*/, this.accessibleIndexKeys()];
|
|
244
|
+
case 1:
|
|
245
|
+
keys = _a.sent();
|
|
246
|
+
if (keys.length === 0) {
|
|
247
|
+
return [2 /*return*/, []];
|
|
248
|
+
}
|
|
249
|
+
fragments = this.tokenizer.fragmentsForQuery(shape.needle);
|
|
250
|
+
if (fragments.length === 0) {
|
|
251
|
+
// A needle with no indexable words (punctuation-only). Verification alone cannot be
|
|
252
|
+
// index-bounded; reject rather than silently scan.
|
|
253
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot search encrypted column `".concat(this.table.name, ".").concat(this.prop, "` for a pattern with no letters or ") +
|
|
254
|
+
"digits (".concat(JSON.stringify(shape.needle), "). Search patterns must contain at least one word character."));
|
|
255
|
+
}
|
|
256
|
+
return [4 /*yield*/, this.tokenCoverCandidates(fragments, keys)];
|
|
257
|
+
case 2:
|
|
258
|
+
candidateIds = _a.sent();
|
|
259
|
+
if (candidateIds.length === 0) {
|
|
260
|
+
return [2 /*return*/, []];
|
|
261
|
+
}
|
|
262
|
+
verifyQb = new db_query_1.QueryBuilder(this.table.name)
|
|
263
|
+
.select({ fields: ['id', this.prop] })
|
|
264
|
+
.condition({ field: 'id', operator: 'IN', value: candidateIds });
|
|
265
|
+
return [4 /*yield*/, this.runtime.query(this.table, verifyQb)];
|
|
266
|
+
case 3:
|
|
267
|
+
rows = _a.sent();
|
|
268
|
+
verified = [];
|
|
269
|
+
for (_i = 0, rows_1 = rows; _i < rows_1.length; _i++) {
|
|
270
|
+
row = rows_1[_i];
|
|
271
|
+
value = row[this.prop];
|
|
272
|
+
if (typeof value === 'string' && this.matches(value, shape, caseSensitive)) {
|
|
273
|
+
verified.push(row.id);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return [2 /*return*/, verified];
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
/** Candidate ids: rows whose token rows cover ALL query fragments under SOME accessible key. */
|
|
282
|
+
EncryptedColumnQueryTransform.prototype.tokenCoverCandidates = function (fragments, keys) {
|
|
283
|
+
var _a;
|
|
284
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
285
|
+
var tokenTable, fingerprintSources, keyIndex, _i, fragments_1, fragment, fingerprint, sources, tokenQb, tokenRows, coverage, _b, tokenRows_1, tokenRow, sources, byKey, _c, sources_1, source, matched, candidates, _d, _e, _f, recordId, byKey, _g, _h, matched;
|
|
286
|
+
return __generator(this, function (_j) {
|
|
287
|
+
switch (_j.label) {
|
|
288
|
+
case 0:
|
|
289
|
+
tokenTable = this.encryptedColumns.tokenTableFor(this.table);
|
|
290
|
+
if (!tokenTable) {
|
|
291
|
+
return [2 /*return*/, []];
|
|
292
|
+
}
|
|
293
|
+
fingerprintSources = new Map();
|
|
294
|
+
for (keyIndex = 0; keyIndex < keys.length; keyIndex++) {
|
|
295
|
+
for (_i = 0, fragments_1 = fragments; _i < fragments_1.length; _i++) {
|
|
296
|
+
fragment = fragments_1[_i];
|
|
297
|
+
fingerprint = this.tokenizer.fingerprint(fragment, keys[keyIndex].indexKey);
|
|
298
|
+
sources = (_a = fingerprintSources.get(fingerprint)) !== null && _a !== void 0 ? _a : [];
|
|
299
|
+
sources.push({ keyIndex: keyIndex, fragment: fragment });
|
|
300
|
+
fingerprintSources.set(fingerprint, sources);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
tokenQb = new db_query_1.QueryBuilder(tokenTable.name)
|
|
304
|
+
.select({ fields: ['recordId', 'token'] })
|
|
305
|
+
.condition({ field: 'columnName', operator: '=', value: this.column.name })
|
|
306
|
+
.condition({ field: 'token', operator: 'IN', value: Array.from(fingerprintSources.keys()) });
|
|
307
|
+
return [4 /*yield*/, this.runtime.systemQuery(tokenTable, tokenQb)];
|
|
308
|
+
case 1:
|
|
309
|
+
tokenRows = (_j.sent());
|
|
310
|
+
coverage = new Map();
|
|
311
|
+
for (_b = 0, tokenRows_1 = tokenRows; _b < tokenRows_1.length; _b++) {
|
|
312
|
+
tokenRow = tokenRows_1[_b];
|
|
313
|
+
sources = fingerprintSources.get(tokenRow.token);
|
|
314
|
+
if (!sources) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
byKey = coverage.get(tokenRow.recordId);
|
|
318
|
+
if (!byKey) {
|
|
319
|
+
byKey = new Map();
|
|
320
|
+
coverage.set(tokenRow.recordId, byKey);
|
|
321
|
+
}
|
|
322
|
+
for (_c = 0, sources_1 = sources; _c < sources_1.length; _c++) {
|
|
323
|
+
source = sources_1[_c];
|
|
324
|
+
matched = byKey.get(source.keyIndex);
|
|
325
|
+
if (!matched) {
|
|
326
|
+
matched = new Set();
|
|
327
|
+
byKey.set(source.keyIndex, matched);
|
|
328
|
+
}
|
|
329
|
+
matched.add(source.fragment);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
candidates = [];
|
|
333
|
+
for (_d = 0, _e = Array.from(coverage.entries()); _d < _e.length; _d++) {
|
|
334
|
+
_f = _e[_d], recordId = _f[0], byKey = _f[1];
|
|
335
|
+
for (_g = 0, _h = Array.from(byKey.values()); _g < _h.length; _g++) {
|
|
336
|
+
matched = _h[_g];
|
|
337
|
+
if (matched.size === fragments.length) {
|
|
338
|
+
candidates.push(recordId);
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return [2 /*return*/, candidates];
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
EncryptedColumnQueryTransform.prototype.matches = function (value, shape, caseSensitive) {
|
|
349
|
+
var haystack = caseSensitive ? value : value.toLowerCase();
|
|
350
|
+
var needle = caseSensitive ? shape.needle : shape.needle.toLowerCase();
|
|
351
|
+
switch (shape.kind) {
|
|
352
|
+
case 'contains':
|
|
353
|
+
return haystack.includes(needle);
|
|
354
|
+
case 'prefix':
|
|
355
|
+
return haystack.startsWith(needle);
|
|
356
|
+
case 'suffix':
|
|
357
|
+
return haystack.endsWith(needle);
|
|
358
|
+
case 'exact':
|
|
359
|
+
return haystack === needle;
|
|
360
|
+
default:
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
EncryptedColumnQueryTransform.prototype.parseLikePattern = function (pattern) {
|
|
365
|
+
var _this = this;
|
|
366
|
+
if (typeof pattern !== 'string') {
|
|
367
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Encrypted column `".concat(this.table.name, ".").concat(this.prop, "` LIKE patterns must be strings; ") +
|
|
368
|
+
"got ".concat(typeof pattern, "."));
|
|
369
|
+
}
|
|
370
|
+
var rejectPattern = function () {
|
|
371
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("Cannot apply LIKE pattern ".concat(JSON.stringify(pattern), " to encrypted column ") +
|
|
372
|
+
"`".concat(_this.table.name, ".").concat(_this.prop, "`. Supported: contains ('%q%'), prefix ('q%'), suffix ('%q'), ") +
|
|
373
|
+
"and exact ('q'). Mid-pattern wildcards, '_' wildcards, and escapes are not supported on " +
|
|
374
|
+
"encrypted columns; search a metadata column for those shapes.");
|
|
375
|
+
};
|
|
376
|
+
var leading = pattern.startsWith('%');
|
|
377
|
+
var trailing = pattern.endsWith('%') && pattern.length > (leading ? 1 : 0);
|
|
378
|
+
var needle = pattern.slice(leading ? 1 : 0, trailing ? -1 : undefined);
|
|
379
|
+
if (needle.length === 0) {
|
|
380
|
+
if (leading || trailing) {
|
|
381
|
+
return { kind: 'match-any', needle: '' };
|
|
382
|
+
}
|
|
383
|
+
return { kind: 'exact', needle: '' };
|
|
384
|
+
}
|
|
385
|
+
if (needle.includes('%') || needle.includes('_') || needle.includes('\\')) {
|
|
386
|
+
rejectPattern();
|
|
387
|
+
}
|
|
388
|
+
if (leading && trailing) {
|
|
389
|
+
return { kind: 'contains', needle: needle };
|
|
390
|
+
}
|
|
391
|
+
if (trailing) {
|
|
392
|
+
return { kind: 'prefix', needle: needle };
|
|
393
|
+
}
|
|
394
|
+
if (leading) {
|
|
395
|
+
return { kind: 'suffix', needle: needle };
|
|
396
|
+
}
|
|
397
|
+
return { kind: 'exact', needle: needle };
|
|
398
|
+
};
|
|
399
|
+
EncryptedColumnQueryTransform.prototype.accessibleIndexKeys = function () {
|
|
400
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
401
|
+
var config, owners;
|
|
402
|
+
return __generator(this, function (_a) {
|
|
403
|
+
switch (_a.label) {
|
|
404
|
+
case 0:
|
|
405
|
+
config = (0, DbEncryptionConfig_1.getDbEncryptionConfig)();
|
|
406
|
+
if (!config.getAccessibleKeyOwners) {
|
|
407
|
+
throw new EncryptedColumnQueryError_1.EncryptedColumnQueryError("A query searches an encrypted column but DbEncryptionConfig.getAccessibleKeyOwners is not " +
|
|
408
|
+
"configured. Search fingerprints are keyed per owner; the config must supply the caller's " +
|
|
409
|
+
"accessible key owners (their own id plus owners sharing with them).");
|
|
410
|
+
}
|
|
411
|
+
return [4 /*yield*/, config.getAccessibleKeyOwners({ runAsSystem: this.runtime.runAsSystem })];
|
|
412
|
+
case 1:
|
|
413
|
+
owners = _a.sent();
|
|
414
|
+
return [4 /*yield*/, new DataKeyStore_1.DataKeyStore().getQueryIndexKeys(owners)];
|
|
415
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
};
|
|
420
|
+
return EncryptedColumnQueryTransform;
|
|
421
|
+
}());
|
|
422
|
+
exports.EncryptedColumnQueryTransform = EncryptedColumnQueryTransform;
|
|
423
|
+
//# sourceMappingURL=EncryptedColumnQueryTransform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EncryptedColumnQueryTransform.js","sourceRoot":"","sources":["../../../src/encryption/EncryptedColumnQueryTransform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Column, EncryptedColumnConfig, Table } from '../Table';
|
|
2
|
+
import { Record } from '../Record';
|
|
3
|
+
/** One search-token row: a keyed fingerprint of one token of one encrypted column value. */
|
|
4
|
+
export interface EncryptionSearchToken extends Record {
|
|
5
|
+
recordId: string;
|
|
6
|
+
columnName: string;
|
|
7
|
+
token: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The schema side of column encryption (`ColumnOptions.encrypted`): validation of the
|
|
11
|
+
* declaration, and derivation of the physical schema an encrypted column needs —
|
|
12
|
+
* automatically, at the framework seam, never by callers:
|
|
13
|
+
*
|
|
14
|
+
* - the ciphertext column itself widens to STRING(MAX) (envelopes outgrow declared widths);
|
|
15
|
+
* - `searchable: 'equality'` derives an indexed whole-value fingerprint companion column
|
|
16
|
+
* (`<column>_enc_eq`); a `unique` declaration on the base column moves onto it (per-owner
|
|
17
|
+
* value uniqueness — fingerprints are keyed per owner);
|
|
18
|
+
* - `searchable: 'contains'` derives a companion search-token TABLE (`<table>_enc_tok`) —
|
|
19
|
+
* a plain indexed inverted index, portable across drivers (Spanner's native search
|
|
20
|
+
* indexes are a named later optimization at the driver layer);
|
|
21
|
+
* - `sortKey: { revealPrefix: N }` derives an ordered-prefix companion column
|
|
22
|
+
* (`<column>_enc_srt`) for native ORDER BY — the declared bounded reveal.
|
|
23
|
+
*
|
|
24
|
+
* `ensureSchema` is idempotent and runs wherever the physical schema is consulted
|
|
25
|
+
* (TableManager, the record serializer, the query translator).
|
|
26
|
+
*/
|
|
27
|
+
export declare class EncryptedColumns {
|
|
28
|
+
private static readonly SCHEMA_APPLIED_MARKER;
|
|
29
|
+
private static readonly INTERNAL_COLUMN_MARKER;
|
|
30
|
+
/** Validate declarations and inject the derived physical schema (idempotent). */
|
|
31
|
+
ensureSchema(table: Table<any>): void;
|
|
32
|
+
/**
|
|
33
|
+
* The mandatory-declaration gate (`DbEncryptionConfig.requireEncryptedDeclarations`):
|
|
34
|
+
* every text-holding column must declare `encrypted` (`false` or a config object), so no
|
|
35
|
+
* column can be added without deciding, and the encrypted set can never silently drift.
|
|
36
|
+
* Identifier-bearing column classes (uuids, references) are metadata by construction and
|
|
37
|
+
* are exempt.
|
|
38
|
+
*/
|
|
39
|
+
validateDeclarations(table: Table<any>): void;
|
|
40
|
+
/** The `encrypted` config object for a column property, or undefined when not encrypted. */
|
|
41
|
+
configFor(table: Table<any>, prop: string): EncryptedColumnConfig | undefined;
|
|
42
|
+
/** Property names of this table's encrypted columns. */
|
|
43
|
+
encryptedProps(table: Table<any>): string[];
|
|
44
|
+
/** Property names declared `searchable: 'contains'`. */
|
|
45
|
+
containsProps(table: Table<any>): string[];
|
|
46
|
+
/** Does this write payload touch any encrypted column? */
|
|
47
|
+
recordTouchesEncryptedColumns(table: Table<any>, record: any): boolean;
|
|
48
|
+
hasEncryptedColumns(table: Table<any>): boolean;
|
|
49
|
+
/** The derived search-token table for `table`, or undefined when nothing is `contains`-searchable. */
|
|
50
|
+
tokenTableFor(table: Table<any>): Table<EncryptionSearchToken> | undefined;
|
|
51
|
+
tokenTableName(table: Table<any>): string;
|
|
52
|
+
/** Companion property names EQUAL their column names (several seams assume prop == name). */
|
|
53
|
+
eqCompanionProp(table: Table<any>, prop: string): string;
|
|
54
|
+
sortCompanionProp(table: Table<any>, prop: string): string;
|
|
55
|
+
/** Framework-derived companion columns are dropped from deserialized records and exempt from declaration checks. */
|
|
56
|
+
isInternalColumn(column: Column<any, any>): boolean;
|
|
57
|
+
private validateDeclaration;
|
|
58
|
+
private injectEqualityCompanion;
|
|
59
|
+
private injectSortCompanion;
|
|
60
|
+
private injectCompanionColumn;
|
|
61
|
+
/**
|
|
62
|
+
* The token table's SHAPE for `table`, independent of registration — the lifecycle
|
|
63
|
+
* walker's decrypt-out sweep needs it after the config stopped declaring any
|
|
64
|
+
* `searchable: 'contains'` column (when `tokenTableFor` no longer resolves).
|
|
65
|
+
*/
|
|
66
|
+
tokenTableShape(table: Table<any>): Table<EncryptionSearchToken>;
|
|
67
|
+
private isTextColumn;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=EncryptedColumns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EncryptedColumns.d.ts","sourceRoot":"","sources":["../../../src/encryption/EncryptedColumns.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,MAAM,EAAqB,MAAM,WAAW,CAAC;AAatD,4FAA4F;AAC5F,MAAM,WAAW,qBAAsB,SAAQ,MAAM;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAwB;IACrE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAEvE,iFAAiF;IACjF,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI;IAkDrC;;;;;;OAMG;IACH,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI;IAqB7C,4FAA4F;IAC5F,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS;IAM7E,wDAAwD;IACxD,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;IAI3C,wDAAwD;IACxD,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;IAI1C,0DAA0D;IAC1D,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO;IAItE,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO;IAI/C,sGAAsG;IACtG,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC,GAAG,SAAS;IAK1E,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM;IAIzC,6FAA6F;IAC7F,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAIxD,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAI1D,oHAAoH;IACpH,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,OAAO;IAInD,OAAO,CAAC,mBAAmB;IA8D3B,OAAO,CAAC,uBAAuB;IAwB/B,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,qBAAqB;IAY7B;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC;IAmBhE,OAAO,CAAC,YAAY;CAarB"}
|