@hot-updater/plugin-core 0.36.7 → 1.0.0-rc.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/dist/assetStorageLayout.cjs +36 -18
- package/dist/assetStorageLayout.d.cts +21 -6
- package/dist/assetStorageLayout.d.mts +21 -6
- package/dist/assetStorageLayout.mjs +36 -18
- package/dist/bundleStorageLayout.cjs +2 -3
- package/dist/bundleStorageLayout.mjs +2 -3
- package/dist/contentAddressedAssets.cjs +5 -0
- package/dist/contentAddressedAssets.d.cts +2 -1
- package/dist/contentAddressedAssets.d.mts +2 -1
- package/dist/contentAddressedAssets.mjs +5 -1
- package/dist/createDatabasePlugin.cjs +775 -284
- package/dist/createDatabasePlugin.d.cts +20 -64
- package/dist/createDatabasePlugin.d.mts +20 -64
- package/dist/createDatabasePlugin.mjs +773 -284
- package/dist/createStorageKeyBuilder.cjs +9 -2
- package/dist/createStorageKeyBuilder.mjs +9 -2
- package/dist/createStoragePlugin.cjs +6 -145
- package/dist/createStoragePlugin.d.cts +7 -52
- package/dist/createStoragePlugin.d.mts +7 -52
- package/dist/createStoragePlugin.mjs +5 -143
- package/dist/databaseClient.cjs +112 -0
- package/dist/databaseClient.d.cts +33 -0
- package/dist/databaseClient.d.mts +33 -0
- package/dist/databaseClient.mjs +110 -0
- package/dist/databaseClientReads.cjs +113 -0
- package/dist/databaseClientReads.mjs +112 -0
- package/dist/databaseClientUpdates.cjs +39 -0
- package/dist/databaseClientUpdates.d.cts +11 -0
- package/dist/databaseClientUpdates.d.mts +11 -0
- package/dist/databaseClientUpdates.mjs +36 -0
- package/dist/databaseJsonValue.cjs +37 -0
- package/dist/databaseJsonValue.d.cts +5 -0
- package/dist/databaseJsonValue.d.mts +5 -0
- package/dist/databaseJsonValue.mjs +37 -0
- package/dist/databaseMetadata.cjs +13 -0
- package/dist/databaseMetadata.mjs +12 -0
- package/dist/databasePluginCrud.cjs +86 -0
- package/dist/databasePluginCrud.d.cts +1 -0
- package/dist/databasePluginCrud.d.mts +1 -0
- package/dist/databasePluginCrud.mjs +86 -0
- package/dist/databasePluginCrudValidation.d.cts +1 -0
- package/dist/databasePluginCrudValidation.d.mts +1 -0
- package/dist/databasePluginCrudValidationErrors.cjs +10 -0
- package/dist/databasePluginCrudValidationErrors.d.cts +9 -0
- package/dist/databasePluginCrudValidationErrors.d.mts +9 -0
- package/dist/databasePluginCrudValidationErrors.mjs +10 -0
- package/dist/databasePluginCrudValidationFields.cjs +291 -0
- package/dist/databasePluginCrudValidationFields.d.cts +1 -0
- package/dist/databasePluginCrudValidationFields.d.mts +1 -0
- package/dist/databasePluginCrudValidationFields.mjs +281 -0
- package/dist/databasePluginCrudValidationMutations.cjs +88 -0
- package/dist/databasePluginCrudValidationMutations.d.cts +1 -0
- package/dist/databasePluginCrudValidationMutations.d.mts +1 -0
- package/dist/databasePluginCrudValidationMutations.mjs +82 -0
- package/dist/databasePluginCrudValidationQueries.cjs +103 -0
- package/dist/databasePluginCrudValidationQueries.d.cts +1 -0
- package/dist/databasePluginCrudValidationQueries.d.mts +1 -0
- package/dist/databasePluginCrudValidationQueries.mjs +97 -0
- package/dist/databasePluginCrudValidationRows.cjs +55 -0
- package/dist/databasePluginCrudValidationRows.d.cts +1 -0
- package/dist/databasePluginCrudValidationRows.d.mts +1 -0
- package/dist/databasePluginCrudValidationRows.mjs +53 -0
- package/dist/databasePluginTransaction.cjs +7 -0
- package/dist/databasePluginTransaction.mjs +7 -0
- package/dist/databaseRows.cjs +149 -0
- package/dist/databaseRows.d.cts +34 -0
- package/dist/databaseRows.d.mts +34 -0
- package/dist/databaseRows.mjs +141 -0
- package/dist/index.cjs +76 -24
- package/dist/index.d.cts +22 -12
- package/dist/index.d.mts +22 -12
- package/dist/index.mjs +21 -14
- package/dist/internal.cjs +6 -0
- package/dist/internal.d.cts +9 -0
- package/dist/internal.d.mts +9 -0
- package/dist/internal.mjs +3 -0
- package/dist/node_modules/.pnpm/verkit@0.3.2/node_modules/verkit/dist/index.cjs +345 -0
- package/dist/node_modules/.pnpm/verkit@0.3.2/node_modules/verkit/dist/index.mjs +341 -0
- package/dist/paginateBundles.d.cts +6 -4
- package/dist/paginateBundles.d.mts +6 -4
- package/dist/parseStorageUri.cjs +46 -30
- package/dist/parseStorageUri.d.cts +20 -17
- package/dist/parseStorageUri.d.mts +20 -17
- package/dist/parseStorageUri.mjs +46 -30
- package/dist/queryBundles.cjs +1 -7
- package/dist/queryBundles.d.cts +3 -1
- package/dist/queryBundles.d.mts +3 -1
- package/dist/queryBundles.mjs +1 -7
- package/dist/releaseCatalogCompiler.cjs +391 -0
- package/dist/releaseCatalogCompiler.d.cts +55 -0
- package/dist/releaseCatalogCompiler.d.mts +55 -0
- package/dist/releaseCatalogCompiler.mjs +386 -0
- package/dist/releaseCatalogMutation.cjs +303 -0
- package/dist/releaseCatalogMutation.d.cts +86 -0
- package/dist/releaseCatalogMutation.d.mts +86 -0
- package/dist/releaseCatalogMutation.mjs +297 -0
- package/dist/releaseManagement.cjs +231 -0
- package/dist/releaseManagement.d.cts +48 -0
- package/dist/releaseManagement.d.mts +48 -0
- package/dist/releaseManagement.mjs +224 -0
- package/dist/remoteBundleSigning.cjs +296 -0
- package/dist/remoteBundleSigning.d.cts +43 -0
- package/dist/remoteBundleSigning.d.mts +43 -0
- package/dist/remoteBundleSigning.mjs +291 -0
- package/dist/requestBundleCache.cjs +22 -0
- package/dist/requestBundleCache.d.cts +9 -0
- package/dist/requestBundleCache.d.mts +9 -0
- package/dist/requestBundleCache.mjs +22 -0
- package/dist/semverSatisfies.cjs +3 -4
- package/dist/semverSatisfies.mjs +1 -1
- package/dist/storageDownloadPath.cjs +48 -0
- package/dist/storageDownloadPath.d.cts +15 -0
- package/dist/storageDownloadPath.d.mts +15 -0
- package/dist/storageDownloadPath.mjs +46 -0
- package/dist/types/database.d.cts +4 -0
- package/dist/types/database.d.mts +4 -0
- package/dist/types/databaseFields.cjs +92 -0
- package/dist/types/databaseFields.d.cts +12 -0
- package/dist/types/databaseFields.d.mts +12 -0
- package/dist/types/databaseFields.mjs +92 -0
- package/dist/types/databaseOperations.d.cts +173 -0
- package/dist/types/databaseOperations.d.mts +173 -0
- package/dist/types/databasePlugin.d.cts +240 -0
- package/dist/types/databasePlugin.d.mts +240 -0
- package/dist/types/databaseQuery.d.cts +47 -0
- package/dist/types/databaseQuery.d.mts +47 -0
- package/dist/types/databaseRows.d.cts +120 -0
- package/dist/types/databaseRows.d.mts +120 -0
- package/dist/types/index.d.cts +120 -162
- package/dist/types/index.d.mts +120 -162
- package/dist/types/internal.d.cts +7 -0
- package/dist/types/internal.d.mts +7 -0
- package/dist/types/public.d.cts +3 -0
- package/dist/types/public.d.mts +3 -0
- package/dist/uuidv7.cjs +9 -0
- package/dist/uuidv7.d.cts +4 -1
- package/dist/uuidv7.d.mts +4 -1
- package/dist/uuidv7.mjs +8 -1
- package/package.json +10 -5
- package/dist/bundleUnitOfWork.cjs +0 -158
- package/dist/bundleUnitOfWork.mjs +0 -158
- package/dist/bundleUnitOfWorkStore.cjs +0 -15
- package/dist/bundleUnitOfWorkStore.mjs +0 -15
- package/dist/createBlobDatabasePlugin.cjs +0 -382
- package/dist/createBlobDatabasePlugin.d.cts +0 -29
- package/dist/createBlobDatabasePlugin.d.mts +0 -29
- package/dist/createBlobDatabasePlugin.mjs +0 -381
- package/dist/legacyAssetStorageLayout.cjs +0 -12
- package/dist/legacyAssetStorageLayout.mjs +0 -12
- package/dist/requestUpdateBundleState.cjs +0 -26
- package/dist/requestUpdateBundleState.d.cts +0 -12
- package/dist/requestUpdateBundleState.d.mts +0 -12
- package/dist/requestUpdateBundleState.mjs +0 -24
- package/dist/resolveUpdateInfoFromBundles.cjs +0 -14
- package/dist/resolveUpdateInfoFromBundles.d.cts +0 -16
- package/dist/resolveUpdateInfoFromBundles.d.mts +0 -16
- package/dist/resolveUpdateInfoFromBundles.mjs +0 -13
- package/dist/storageProfile.cjs +0 -15
- package/dist/storageProfile.d.cts +0 -9
- package/dist/storageProfile.d.mts +0 -9
- package/dist/storageProfile.mjs +0 -12
|
@@ -1,307 +1,796 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { DatabasePluginInputError } from "./databasePluginCrudValidationErrors.mjs";
|
|
2
|
+
import { isChannelText, isRecord } from "./databasePluginCrudValidationFields.mjs";
|
|
3
|
+
import { validateCreateData, validateResult } from "./databasePluginCrudValidationRows.mjs";
|
|
4
|
+
import { validateApiKeyUpdateData, validateBundleUpdateData, validateReleaseUpdateData } from "./databasePluginCrudValidationMutations.mjs";
|
|
5
|
+
import { createDatabasePluginCrud } from "./databasePluginCrud.mjs";
|
|
6
|
+
import { createTransactionDatabasePlugin } from "./databasePluginTransaction.mjs";
|
|
5
7
|
//#region src/createDatabasePlugin.ts
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const PAGE_SIZE = 100;
|
|
9
|
+
const compareChannelRows = (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
|
|
10
|
+
var DatabaseAtomicCommitUnsupportedError = class extends Error {
|
|
11
|
+
name = "DatabaseAtomicCommitUnsupportedError";
|
|
12
|
+
constructor(pluginName) {
|
|
13
|
+
super(`Database plugin "${pluginName}" cannot atomically commit changes across models.`);
|
|
14
|
+
this.pluginName = pluginName;
|
|
15
|
+
}
|
|
10
16
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Internal provider signal for a delete rejected by a live reference.
|
|
19
|
+
*
|
|
20
|
+
* Provider implementations throw this after translating their native
|
|
21
|
+
* foreign-key or reference-condition error. The public database contract
|
|
22
|
+
* observes only the indexed `referenced` commit conflict.
|
|
23
|
+
*/
|
|
24
|
+
var DatabaseRowReferencedError = class extends Error {
|
|
25
|
+
name = "DatabaseRowReferencedError";
|
|
26
|
+
constructor() {
|
|
27
|
+
super("The database row is still referenced.");
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var DatabaseCommitConflictError = class extends Error {
|
|
31
|
+
name = "DatabaseCommitConflictError";
|
|
32
|
+
constructor(result) {
|
|
33
|
+
super("Database commit precondition failed.");
|
|
34
|
+
this.result = result;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const toBundleWhere = (where) => {
|
|
38
|
+
if (!where) return [];
|
|
39
|
+
const filters = [];
|
|
40
|
+
if (where.platform !== void 0) filters.push({
|
|
41
|
+
field: "platform",
|
|
42
|
+
value: where.platform
|
|
18
43
|
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
if (where.id?.eq !== void 0) filters.push({
|
|
45
|
+
field: "id",
|
|
46
|
+
value: where.id.eq
|
|
47
|
+
});
|
|
48
|
+
if (where.id?.gt !== void 0) filters.push({
|
|
49
|
+
field: "id",
|
|
50
|
+
operator: "gt",
|
|
51
|
+
value: where.id.gt
|
|
52
|
+
});
|
|
53
|
+
if (where.id?.gte !== void 0) filters.push({
|
|
54
|
+
field: "id",
|
|
55
|
+
operator: "gte",
|
|
56
|
+
value: where.id.gte
|
|
57
|
+
});
|
|
58
|
+
if (where.id?.lt !== void 0) filters.push({
|
|
59
|
+
field: "id",
|
|
60
|
+
operator: "lt",
|
|
61
|
+
value: where.id.lt
|
|
62
|
+
});
|
|
63
|
+
if (where.id?.lte !== void 0) filters.push({
|
|
64
|
+
field: "id",
|
|
65
|
+
operator: "lte",
|
|
66
|
+
value: where.id.lte
|
|
67
|
+
});
|
|
68
|
+
if (where.id?.in !== void 0) filters.push({
|
|
69
|
+
field: "id",
|
|
70
|
+
operator: "in",
|
|
71
|
+
value: where.id.in
|
|
72
|
+
});
|
|
73
|
+
return filters;
|
|
74
|
+
};
|
|
75
|
+
const assertReleaseReferences = async (database, row) => {
|
|
76
|
+
if (await database.findOne({
|
|
77
|
+
model: "channels",
|
|
78
|
+
where: [{
|
|
79
|
+
field: "id",
|
|
80
|
+
value: row.channel_id
|
|
81
|
+
}]
|
|
82
|
+
}) === null) throw new DatabasePluginInputError("invalid-data");
|
|
83
|
+
if (row.bundle_id !== null) {
|
|
84
|
+
const bundle = await database.findOne({
|
|
85
|
+
model: "bundles",
|
|
86
|
+
where: [{
|
|
87
|
+
field: "id",
|
|
88
|
+
value: row.bundle_id
|
|
89
|
+
}]
|
|
90
|
+
});
|
|
91
|
+
if (bundle === null || bundle.platform !== row.platform) throw new DatabasePluginInputError("invalid-data");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const applyChange = async (database, change, changeIndex) => {
|
|
95
|
+
switch (change.model) {
|
|
96
|
+
case "bundles": switch (change.operation) {
|
|
97
|
+
case "insert":
|
|
98
|
+
await database.create({
|
|
99
|
+
model: "bundles",
|
|
100
|
+
data: change.row
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
case "update":
|
|
104
|
+
if (await database.update({
|
|
105
|
+
model: "bundles",
|
|
106
|
+
where: [{
|
|
107
|
+
field: "id",
|
|
108
|
+
value: change.where.id
|
|
109
|
+
}],
|
|
110
|
+
update: change.update
|
|
111
|
+
}) === null) throw new DatabaseCommitConflictError({
|
|
112
|
+
committed: false,
|
|
113
|
+
conflict: {
|
|
114
|
+
changeIndex,
|
|
115
|
+
reason: "not_found"
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return;
|
|
119
|
+
case "delete":
|
|
120
|
+
if (await database.count({
|
|
121
|
+
model: "releases",
|
|
122
|
+
where: [{
|
|
123
|
+
field: "bundle_id",
|
|
124
|
+
value: change.where.id
|
|
125
|
+
}]
|
|
126
|
+
}) > 0) throw new DatabaseCommitConflictError({
|
|
127
|
+
committed: false,
|
|
128
|
+
conflict: {
|
|
129
|
+
changeIndex,
|
|
130
|
+
reason: "referenced"
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
await database.delete({
|
|
134
|
+
model: "bundles",
|
|
135
|
+
where: [{
|
|
136
|
+
field: "id",
|
|
137
|
+
value: change.where.id
|
|
138
|
+
}]
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
45
141
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
142
|
+
case "releases": switch (change.operation) {
|
|
143
|
+
case "insert":
|
|
144
|
+
await assertReleaseReferences(database, change.row);
|
|
145
|
+
await database.create({
|
|
146
|
+
model: "releases",
|
|
147
|
+
data: change.row
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
case "update":
|
|
151
|
+
if (await database.update({
|
|
152
|
+
model: "releases",
|
|
153
|
+
where: [{
|
|
154
|
+
field: "id",
|
|
155
|
+
value: change.where.id
|
|
156
|
+
}],
|
|
157
|
+
update: change.update
|
|
158
|
+
}) === null) throw new DatabaseCommitConflictError({
|
|
159
|
+
committed: false,
|
|
160
|
+
conflict: {
|
|
161
|
+
changeIndex,
|
|
162
|
+
reason: "not_found"
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
case "delete":
|
|
167
|
+
await database.delete({
|
|
168
|
+
model: "releases",
|
|
169
|
+
where: [{
|
|
170
|
+
field: "id",
|
|
171
|
+
value: change.where.id
|
|
172
|
+
}]
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
case "releaseCatalogs":
|
|
177
|
+
if (await database.findOne({
|
|
178
|
+
model: "release_catalogs",
|
|
179
|
+
where: [{
|
|
180
|
+
field: "scope_key",
|
|
181
|
+
value: change.row.scope_key
|
|
182
|
+
}]
|
|
183
|
+
}) === null) await database.create({
|
|
184
|
+
model: "release_catalogs",
|
|
185
|
+
data: change.row
|
|
186
|
+
});
|
|
187
|
+
else {
|
|
188
|
+
const { scope_key: _scopeKey, ...update } = change.row;
|
|
189
|
+
await database.update({
|
|
190
|
+
model: "release_catalogs",
|
|
191
|
+
where: [{
|
|
192
|
+
field: "scope_key",
|
|
193
|
+
value: change.row.scope_key
|
|
194
|
+
}],
|
|
195
|
+
update
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
case "bundlePatches": switch (change.operation) {
|
|
200
|
+
case "insert":
|
|
201
|
+
await database.create({
|
|
202
|
+
model: "bundle_patches",
|
|
203
|
+
data: change.row
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
case "delete":
|
|
207
|
+
await database.delete({
|
|
208
|
+
model: "bundle_patches",
|
|
209
|
+
where: [{
|
|
210
|
+
field: "bundle_id",
|
|
211
|
+
value: change.where.bundleId
|
|
212
|
+
}]
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
case "channels": switch (change.operation) {
|
|
217
|
+
case "insert":
|
|
218
|
+
await database.create({
|
|
219
|
+
model: "channels",
|
|
220
|
+
data: change.row,
|
|
221
|
+
onConflict: change.onConflict
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
case "delete":
|
|
225
|
+
if (await database.count({
|
|
226
|
+
model: "releases",
|
|
227
|
+
where: [{
|
|
228
|
+
field: "channel_id",
|
|
229
|
+
value: change.where.id
|
|
230
|
+
}]
|
|
231
|
+
}) > 0) throw new DatabaseCommitConflictError({
|
|
232
|
+
committed: false,
|
|
233
|
+
conflict: {
|
|
234
|
+
changeIndex,
|
|
235
|
+
reason: "referenced"
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
try {
|
|
239
|
+
await database.delete({
|
|
240
|
+
model: "channels",
|
|
241
|
+
where: [{
|
|
242
|
+
field: "id",
|
|
243
|
+
value: change.where.id
|
|
244
|
+
}]
|
|
245
|
+
});
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof DatabaseRowReferencedError) throw new DatabaseCommitConflictError({
|
|
248
|
+
committed: false,
|
|
249
|
+
conflict: {
|
|
250
|
+
changeIndex,
|
|
251
|
+
reason: "referenced"
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
case "analytics":
|
|
259
|
+
await database.create({
|
|
260
|
+
model: "bundle_events",
|
|
261
|
+
data: change.row
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
case "apiKeys": switch (change.operation) {
|
|
265
|
+
case "insert":
|
|
266
|
+
await database.create({
|
|
267
|
+
model: "api_keys",
|
|
268
|
+
data: change.row,
|
|
269
|
+
onConflict: change.onConflict
|
|
270
|
+
});
|
|
271
|
+
return;
|
|
272
|
+
case "update":
|
|
273
|
+
if (await database.update({
|
|
274
|
+
model: "api_keys",
|
|
275
|
+
where: [{
|
|
276
|
+
field: "id",
|
|
277
|
+
value: change.where.id
|
|
278
|
+
}],
|
|
279
|
+
update: { revoked_at_ms: change.update.revokedAtMs }
|
|
280
|
+
}) === null) throw new DatabaseCommitConflictError({
|
|
281
|
+
committed: false,
|
|
282
|
+
conflict: {
|
|
283
|
+
changeIndex,
|
|
284
|
+
reason: "not_found"
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
const expectationVersion = async (database, expectation) => {
|
|
292
|
+
if (expectation.model === "releases") return (await database.findOne({
|
|
293
|
+
model: "releases",
|
|
294
|
+
where: [{
|
|
295
|
+
field: "id",
|
|
296
|
+
value: expectation.id
|
|
297
|
+
}],
|
|
298
|
+
select: ["revision"]
|
|
299
|
+
}))?.revision ?? null;
|
|
300
|
+
return (await database.findOne({
|
|
301
|
+
model: "release_catalogs",
|
|
302
|
+
where: [{
|
|
303
|
+
field: "scope_key",
|
|
304
|
+
value: expectation.scopeKey
|
|
305
|
+
}],
|
|
306
|
+
select: ["generation"]
|
|
307
|
+
}))?.generation ?? null;
|
|
308
|
+
};
|
|
309
|
+
const applyExpectations = async (database, expectations) => {
|
|
310
|
+
for (const expectation of expectations) {
|
|
311
|
+
const expectedVersion = expectation.model === "releases" ? expectation.revision : expectation.generation;
|
|
312
|
+
const actualVersion = await expectationVersion(database, expectation);
|
|
313
|
+
if (actualVersion !== expectedVersion) throw new DatabaseCommitConflictError({
|
|
314
|
+
committed: false,
|
|
315
|
+
conflict: {
|
|
316
|
+
actualVersion,
|
|
317
|
+
changeIndex: -1,
|
|
318
|
+
expectedVersion,
|
|
319
|
+
key: expectation.model === "releases" ? expectation.id : expectation.scopeKey,
|
|
320
|
+
model: expectation.model,
|
|
321
|
+
reason: "version_conflict"
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
const applyChanges = async (database, input) => {
|
|
327
|
+
await applyExpectations(database, input.expectations ?? []);
|
|
328
|
+
for (const [changeIndex, change] of input.changes.entries()) await applyChange(database, change, changeIndex);
|
|
329
|
+
return { committed: true };
|
|
330
|
+
};
|
|
331
|
+
const hasOnlyKeys = (value, keys) => {
|
|
332
|
+
return Reflect.ownKeys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
333
|
+
};
|
|
334
|
+
const validateWhere = (where, field, validateValue = (value) => typeof value === "string") => {
|
|
335
|
+
if (!isRecord(where) || !hasOnlyKeys(where, [field]) || !validateValue(Reflect.get(where, field))) throw new DatabasePluginInputError("invalid-data");
|
|
336
|
+
};
|
|
337
|
+
const validateDatabaseChange = (change) => {
|
|
338
|
+
if (!isRecord(change)) throw new DatabasePluginInputError("invalid-data");
|
|
339
|
+
switch (change.model) {
|
|
340
|
+
case "bundles": switch (change.operation) {
|
|
341
|
+
case "insert":
|
|
342
|
+
if (!hasOnlyKeys(change, [
|
|
343
|
+
"model",
|
|
344
|
+
"operation",
|
|
345
|
+
"row"
|
|
346
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
347
|
+
validateCreateData("bundles", change.row);
|
|
348
|
+
return;
|
|
349
|
+
case "update":
|
|
350
|
+
if (!hasOnlyKeys(change, [
|
|
351
|
+
"model",
|
|
352
|
+
"operation",
|
|
353
|
+
"where",
|
|
354
|
+
"update"
|
|
355
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
356
|
+
validateWhere(change.where, "id");
|
|
357
|
+
validateBundleUpdateData(change.update);
|
|
358
|
+
return;
|
|
359
|
+
case "delete":
|
|
360
|
+
if (!hasOnlyKeys(change, [
|
|
361
|
+
"model",
|
|
362
|
+
"operation",
|
|
363
|
+
"where"
|
|
364
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
365
|
+
validateWhere(change.where, "id");
|
|
366
|
+
return;
|
|
367
|
+
default: throw new DatabasePluginInputError("invalid-operation");
|
|
368
|
+
}
|
|
369
|
+
case "bundlePatches": switch (change.operation) {
|
|
370
|
+
case "insert":
|
|
371
|
+
if (!hasOnlyKeys(change, [
|
|
372
|
+
"model",
|
|
373
|
+
"operation",
|
|
374
|
+
"row"
|
|
375
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
376
|
+
validateCreateData("bundle_patches", change.row);
|
|
377
|
+
return;
|
|
378
|
+
case "delete":
|
|
379
|
+
if (!hasOnlyKeys(change, [
|
|
380
|
+
"model",
|
|
381
|
+
"operation",
|
|
382
|
+
"where"
|
|
383
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
384
|
+
validateWhere(change.where, "bundleId");
|
|
385
|
+
return;
|
|
386
|
+
default: throw new DatabasePluginInputError("invalid-operation");
|
|
387
|
+
}
|
|
388
|
+
case "releases": switch (change.operation) {
|
|
389
|
+
case "insert":
|
|
390
|
+
if (!hasOnlyKeys(change, [
|
|
391
|
+
"model",
|
|
392
|
+
"operation",
|
|
393
|
+
"row"
|
|
394
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
395
|
+
validateCreateData("releases", change.row);
|
|
396
|
+
return;
|
|
397
|
+
case "update":
|
|
398
|
+
if (!hasOnlyKeys(change, [
|
|
399
|
+
"model",
|
|
400
|
+
"operation",
|
|
401
|
+
"where",
|
|
402
|
+
"update"
|
|
403
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
404
|
+
validateWhere(change.where, "id");
|
|
405
|
+
validateReleaseUpdateData(change.update);
|
|
406
|
+
return;
|
|
407
|
+
case "delete":
|
|
408
|
+
if (!hasOnlyKeys(change, [
|
|
409
|
+
"model",
|
|
410
|
+
"operation",
|
|
411
|
+
"where"
|
|
412
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
413
|
+
validateWhere(change.where, "id");
|
|
414
|
+
return;
|
|
415
|
+
default: throw new DatabasePluginInputError("invalid-operation");
|
|
416
|
+
}
|
|
417
|
+
case "releaseCatalogs":
|
|
418
|
+
if (change.operation !== "put" || !hasOnlyKeys(change, [
|
|
419
|
+
"model",
|
|
420
|
+
"operation",
|
|
421
|
+
"row"
|
|
422
|
+
])) throw new DatabasePluginInputError("invalid-operation");
|
|
423
|
+
validateCreateData("release_catalogs", change.row);
|
|
424
|
+
return;
|
|
425
|
+
case "channels": switch (change.operation) {
|
|
426
|
+
case "insert":
|
|
427
|
+
if (!hasOnlyKeys(change, [
|
|
428
|
+
"model",
|
|
429
|
+
"operation",
|
|
430
|
+
"row",
|
|
431
|
+
"onConflict"
|
|
432
|
+
]) || change.onConflict !== "ignore") throw new DatabasePluginInputError("invalid-operation");
|
|
433
|
+
validateCreateData("channels", change.row);
|
|
434
|
+
return;
|
|
435
|
+
case "delete":
|
|
436
|
+
if (!hasOnlyKeys(change, [
|
|
437
|
+
"model",
|
|
438
|
+
"operation",
|
|
439
|
+
"where"
|
|
440
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
441
|
+
validateWhere(change.where, "id", isChannelText);
|
|
442
|
+
return;
|
|
443
|
+
default: throw new DatabasePluginInputError("invalid-operation");
|
|
444
|
+
}
|
|
445
|
+
case "analytics":
|
|
446
|
+
if (change.operation !== "insert" || !hasOnlyKeys(change, [
|
|
447
|
+
"model",
|
|
448
|
+
"operation",
|
|
449
|
+
"row"
|
|
450
|
+
])) throw new DatabasePluginInputError("invalid-operation");
|
|
451
|
+
validateCreateData("bundle_events", change.row);
|
|
452
|
+
return;
|
|
453
|
+
case "apiKeys": switch (change.operation) {
|
|
454
|
+
case "insert":
|
|
455
|
+
if (!hasOnlyKeys(change, [
|
|
456
|
+
"model",
|
|
457
|
+
"operation",
|
|
458
|
+
"row",
|
|
459
|
+
"onConflict"
|
|
460
|
+
]) || change.onConflict !== "ignore") throw new DatabasePluginInputError("invalid-operation");
|
|
461
|
+
validateCreateData("api_keys", change.row);
|
|
462
|
+
return;
|
|
463
|
+
case "update":
|
|
464
|
+
if (!hasOnlyKeys(change, [
|
|
465
|
+
"model",
|
|
466
|
+
"operation",
|
|
467
|
+
"where",
|
|
468
|
+
"update"
|
|
469
|
+
])) throw new DatabasePluginInputError("invalid-data");
|
|
470
|
+
validateWhere(change.where, "id");
|
|
471
|
+
if (!isRecord(change.update)) throw new DatabasePluginInputError("invalid-data");
|
|
472
|
+
if (!hasOnlyKeys(change.update, ["revokedAtMs"])) throw new DatabasePluginInputError("invalid-data");
|
|
473
|
+
validateApiKeyUpdateData({ revoked_at_ms: change.update.revokedAtMs });
|
|
474
|
+
return;
|
|
475
|
+
default: throw new DatabasePluginInputError("invalid-operation");
|
|
476
|
+
}
|
|
477
|
+
default: throw new DatabasePluginInputError("invalid-model");
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
const validateDatabaseCommitExpectation = (expectation) => {
|
|
481
|
+
if (!isRecord(expectation)) throw new DatabasePluginInputError("invalid-data");
|
|
482
|
+
if (expectation.model === "releases") {
|
|
483
|
+
if (!hasOnlyKeys(expectation, [
|
|
484
|
+
"model",
|
|
485
|
+
"id",
|
|
486
|
+
"revision"
|
|
487
|
+
]) || typeof expectation.id !== "string" || !(expectation.revision === null || typeof expectation.revision === "number" && Number.isSafeInteger(expectation.revision) && expectation.revision >= 1)) throw new DatabasePluginInputError("invalid-data");
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (expectation.model === "releaseCatalogs") {
|
|
491
|
+
if (!hasOnlyKeys(expectation, [
|
|
492
|
+
"model",
|
|
493
|
+
"scopeKey",
|
|
494
|
+
"generation"
|
|
495
|
+
]) || typeof expectation.scopeKey !== "string" || !(expectation.generation === null || typeof expectation.generation === "number" && Number.isSafeInteger(expectation.generation) && expectation.generation >= 1)) throw new DatabasePluginInputError("invalid-data");
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
throw new DatabasePluginInputError("invalid-model");
|
|
499
|
+
};
|
|
500
|
+
function validateDatabaseCommit(input) {
|
|
501
|
+
if (!isRecord(input) || !Array.isArray(input.changes) || (Object.hasOwn(input, "expectations") ? !hasOnlyKeys(input, ["changes", "expectations"]) || !Array.isArray(input.expectations) : !hasOnlyKeys(input, ["changes"]))) throw new DatabasePluginInputError("invalid-data");
|
|
502
|
+
input.changes.forEach(validateDatabaseChange);
|
|
503
|
+
if (Array.isArray(input.expectations)) input.expectations.forEach(validateDatabaseCommitExpectation);
|
|
55
504
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
505
|
+
const validateChannelInsertResult = (input, result) => {
|
|
506
|
+
validateResult("channels", result.row, void 0);
|
|
507
|
+
if (typeof result.inserted !== "boolean" || result.row.name !== input.row.name || result.inserted && result.row.id !== input.row.id) throw new DatabasePluginInputError("invalid-result");
|
|
508
|
+
};
|
|
509
|
+
const validateChannelDeleteResult = (result) => {
|
|
510
|
+
if (result.deleted) return;
|
|
511
|
+
if (result.reason !== "not_found" && result.reason !== "not_empty") throw new DatabasePluginInputError("invalid-result");
|
|
512
|
+
};
|
|
513
|
+
const createDatabasePluginAdapter = (name, implementation) => {
|
|
514
|
+
const crud = createDatabasePluginCrud(implementation);
|
|
515
|
+
const transaction = implementation.transaction;
|
|
516
|
+
const executeCommit = implementation.commit ? implementation.commit : async (input) => {
|
|
517
|
+
if (transaction) try {
|
|
518
|
+
return await transaction((database) => applyChanges(createTransactionDatabasePlugin(database), input));
|
|
519
|
+
} catch (error) {
|
|
520
|
+
if (error instanceof DatabaseCommitConflictError) return error.result;
|
|
521
|
+
throw error;
|
|
522
|
+
}
|
|
523
|
+
if ((input.expectations?.length ?? 0) > 0 || input.changes.length > 1 || input.changes.some((change) => change.model === "channels" && change.operation === "delete")) throw new DatabaseAtomicCommitUnsupportedError(name);
|
|
524
|
+
try {
|
|
525
|
+
return await applyChanges(crud, input);
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (error instanceof DatabaseCommitConflictError) return error.result;
|
|
528
|
+
throw error;
|
|
69
529
|
}
|
|
70
530
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (extraLimit === 0) return options;
|
|
75
|
-
return {
|
|
76
|
-
...options,
|
|
77
|
-
limit: options.limit + extraLimit
|
|
531
|
+
const commit = async (input) => {
|
|
532
|
+
validateDatabaseCommit(input);
|
|
533
|
+
return executeCommit(input);
|
|
78
534
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
535
|
+
const findApiKeyByHash = (database, hash) => database.findOne({
|
|
536
|
+
model: "api_keys",
|
|
537
|
+
where: [{
|
|
538
|
+
field: "hash",
|
|
539
|
+
value: hash
|
|
540
|
+
}]
|
|
541
|
+
});
|
|
85
542
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
543
|
+
models: {
|
|
544
|
+
bundles: {
|
|
545
|
+
findById: (id) => crud.findOne({
|
|
546
|
+
model: "bundles",
|
|
547
|
+
where: [{
|
|
548
|
+
field: "id",
|
|
549
|
+
value: id
|
|
550
|
+
}]
|
|
551
|
+
}),
|
|
552
|
+
findMany: (query) => crud.findMany({
|
|
553
|
+
model: "bundles",
|
|
554
|
+
where: toBundleWhere(query.where),
|
|
555
|
+
limit: query.limit,
|
|
556
|
+
offset: query.offset,
|
|
557
|
+
orderBy: [query.orderBy]
|
|
558
|
+
}),
|
|
559
|
+
count: (where) => crud.count({
|
|
560
|
+
model: "bundles",
|
|
561
|
+
where: toBundleWhere(where)
|
|
562
|
+
})
|
|
563
|
+
},
|
|
564
|
+
bundlePatches: { async findByBundleIds(bundleIds) {
|
|
565
|
+
if (bundleIds.length === 0) return [];
|
|
566
|
+
const rows = [];
|
|
567
|
+
for (let offset = 0;; offset += PAGE_SIZE) {
|
|
568
|
+
const page = await crud.findMany({
|
|
569
|
+
model: "bundle_patches",
|
|
570
|
+
where: [{
|
|
571
|
+
field: "bundle_id",
|
|
572
|
+
operator: "in",
|
|
573
|
+
value: bundleIds
|
|
574
|
+
}],
|
|
575
|
+
limit: PAGE_SIZE,
|
|
576
|
+
offset,
|
|
577
|
+
orderBy: [{
|
|
578
|
+
field: "id",
|
|
579
|
+
direction: "asc"
|
|
580
|
+
}]
|
|
581
|
+
});
|
|
582
|
+
rows.push(...page);
|
|
583
|
+
if (page.length < PAGE_SIZE) return rows;
|
|
584
|
+
}
|
|
585
|
+
} },
|
|
586
|
+
releases: {
|
|
587
|
+
findById: (id) => crud.findOne({
|
|
588
|
+
model: "releases",
|
|
589
|
+
where: [{
|
|
590
|
+
field: "id",
|
|
591
|
+
value: id
|
|
592
|
+
}]
|
|
593
|
+
}),
|
|
594
|
+
findMany(input) {
|
|
595
|
+
if (!Number.isSafeInteger(input.limit) || input.limit <= 0 || input.afterReleaseId !== void 0 && input.beforeReleaseId !== void 0) throw new DatabasePluginInputError("invalid-query");
|
|
596
|
+
const query = crud.findMany({
|
|
597
|
+
model: "releases",
|
|
598
|
+
where: [
|
|
599
|
+
...input.afterReleaseId === void 0 ? [] : [{
|
|
600
|
+
field: "id",
|
|
601
|
+
operator: "gt",
|
|
602
|
+
value: input.afterReleaseId
|
|
603
|
+
}],
|
|
604
|
+
...input.beforeReleaseId === void 0 ? [] : [{
|
|
605
|
+
field: "id",
|
|
606
|
+
operator: "lt",
|
|
607
|
+
value: input.beforeReleaseId
|
|
608
|
+
}],
|
|
609
|
+
...input.bundleId === void 0 ? [] : [{
|
|
610
|
+
field: "bundle_id",
|
|
611
|
+
value: input.bundleId
|
|
612
|
+
}],
|
|
613
|
+
...input.channelId === void 0 ? [] : [{
|
|
614
|
+
field: "channel_id",
|
|
615
|
+
value: input.channelId
|
|
616
|
+
}],
|
|
617
|
+
...input.enabled === void 0 ? [] : [{
|
|
618
|
+
field: "enabled",
|
|
619
|
+
value: input.enabled
|
|
620
|
+
}],
|
|
621
|
+
...input.platform === void 0 ? [] : [{
|
|
622
|
+
field: "platform",
|
|
623
|
+
value: input.platform
|
|
624
|
+
}],
|
|
625
|
+
...input.targetAppVersion === void 0 ? [] : [{
|
|
626
|
+
field: "target_app_version",
|
|
627
|
+
value: input.targetAppVersion
|
|
628
|
+
}]
|
|
629
|
+
],
|
|
630
|
+
limit: input.limit,
|
|
151
631
|
offset: 0,
|
|
152
|
-
orderBy
|
|
153
|
-
|
|
154
|
-
|
|
632
|
+
orderBy: [{
|
|
633
|
+
field: "id",
|
|
634
|
+
direction: input.afterReleaseId === void 0 ? "desc" : "asc"
|
|
635
|
+
}]
|
|
636
|
+
});
|
|
637
|
+
return input.afterReleaseId === void 0 ? query : query.then((rows) => [...rows].reverse());
|
|
638
|
+
},
|
|
639
|
+
findManyByScope(input) {
|
|
640
|
+
if (input.consistency !== "strong" || !Number.isSafeInteger(input.limit) || input.limit <= 0) throw new DatabasePluginInputError("invalid-query");
|
|
641
|
+
return crud.findMany({
|
|
642
|
+
model: "releases",
|
|
643
|
+
where: [{
|
|
644
|
+
field: "scope_key",
|
|
645
|
+
value: input.scopeKey
|
|
646
|
+
}, ...input.afterReleaseId === void 0 ? [] : [{
|
|
647
|
+
field: "id",
|
|
648
|
+
operator: "gt",
|
|
649
|
+
value: input.afterReleaseId
|
|
650
|
+
}]],
|
|
651
|
+
limit: input.limit,
|
|
652
|
+
offset: 0,
|
|
653
|
+
orderBy: [{
|
|
654
|
+
field: "id",
|
|
655
|
+
direction: "asc"
|
|
656
|
+
}]
|
|
657
|
+
});
|
|
155
658
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
659
|
+
},
|
|
660
|
+
releaseCatalogs: {
|
|
661
|
+
findByScopeKey: (scopeKey) => crud.findOne({
|
|
662
|
+
model: "release_catalogs",
|
|
663
|
+
where: [{
|
|
664
|
+
field: "scope_key",
|
|
665
|
+
value: scopeKey
|
|
666
|
+
}]
|
|
667
|
+
}),
|
|
668
|
+
findMany(input) {
|
|
669
|
+
if (!Number.isSafeInteger(input.limit) || input.limit <= 0) throw new DatabasePluginInputError("invalid-query");
|
|
670
|
+
return crud.findMany({
|
|
671
|
+
model: "release_catalogs",
|
|
672
|
+
where: input.afterScopeKey === void 0 ? [] : [{
|
|
673
|
+
field: "scope_key",
|
|
674
|
+
operator: "gt",
|
|
675
|
+
value: input.afterScopeKey
|
|
676
|
+
}],
|
|
677
|
+
limit: input.limit,
|
|
678
|
+
offset: 0,
|
|
679
|
+
orderBy: [{
|
|
680
|
+
field: "scope_key",
|
|
681
|
+
direction: "asc"
|
|
682
|
+
}]
|
|
683
|
+
});
|
|
177
684
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
187
|
-
const plugin = {
|
|
188
|
-
name: options.name,
|
|
189
|
-
async getBundleById(bundleId, context) {
|
|
190
|
-
const requestUnitOfWork = getRequestUnitOfWork(context);
|
|
191
|
-
if (requestUnitOfWork) return requestUnitOfWork.getById(bundleId, () => getMethods().getBundleById(bundleId, context));
|
|
192
|
-
const pendingMutation = instanceMutationUnitOfWork.peekChanged(bundleId);
|
|
193
|
-
if (pendingMutation.found) return pendingMutation.value;
|
|
194
|
-
return getMethods().getBundleById(bundleId);
|
|
685
|
+
},
|
|
686
|
+
channels: {
|
|
687
|
+
async insert(input) {
|
|
688
|
+
if (input.onConflict !== "returnExisting") throw new DatabasePluginInputError("invalid-operation");
|
|
689
|
+
validateCreateData("channels", input.row);
|
|
690
|
+
const result = await implementation.insertChannel(input);
|
|
691
|
+
validateChannelInsertResult(input, result);
|
|
692
|
+
return result;
|
|
195
693
|
},
|
|
196
|
-
async
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
...
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}),
|
|
214
|
-
pagination: adjustPaginationTotal(result.pagination, {
|
|
215
|
-
limit: normalizedOptions.limit,
|
|
216
|
-
totalDelta: unitOfWork.totalDelta(normalizedOptions.where)
|
|
217
|
-
})
|
|
218
|
-
});
|
|
219
|
-
if (normalizedOptions.page !== void 0) {
|
|
220
|
-
const { page, ...pageOptions } = normalizedOptions;
|
|
221
|
-
const requestedOffset = (page - 1) * normalizedOptions.limit;
|
|
222
|
-
const fetchPageOptions = expandLimitForUnitOfWork(pageOptions, unitOfWork);
|
|
223
|
-
let pageResult = await runGetBundles({
|
|
224
|
-
...fetchPageOptions,
|
|
225
|
-
offset: requestedOffset
|
|
226
|
-
}, context);
|
|
227
|
-
const total = pageResult.pagination.total;
|
|
228
|
-
const totalPages = total === 0 ? 0 : Math.ceil(total / normalizedOptions.limit);
|
|
229
|
-
const maxOffset = totalPages === 0 ? 0 : (Math.max(1, totalPages) - 1) * normalizedOptions.limit;
|
|
230
|
-
const resolvedOffset = Math.min(requestedOffset, maxOffset);
|
|
231
|
-
if (resolvedOffset !== requestedOffset) pageResult = await runGetBundles({
|
|
232
|
-
...fetchPageOptions,
|
|
233
|
-
offset: resolvedOffset
|
|
234
|
-
}, context);
|
|
235
|
-
const result = { ...createPaginatedResult(total, normalizedOptions.limit, resolvedOffset, pageResult.data) };
|
|
236
|
-
return shouldOverlay ? overlayResult(result) : result;
|
|
237
|
-
}
|
|
238
|
-
if (methods.supportsCursorPagination) {
|
|
239
|
-
const fetchOptions = expandLimitForUnitOfWork(normalizedOptions, unitOfWork);
|
|
240
|
-
const result = context === void 0 ? await methods.getBundles(fetchOptions) : await methods.getBundles(fetchOptions, context);
|
|
241
|
-
return shouldOverlay ? overlayResult(result) : result;
|
|
694
|
+
async list(_input) {
|
|
695
|
+
const channels = [];
|
|
696
|
+
for (let offset = 0;; offset += PAGE_SIZE) {
|
|
697
|
+
const page = await crud.findMany({
|
|
698
|
+
model: "channels",
|
|
699
|
+
orderBy: [{
|
|
700
|
+
field: "name",
|
|
701
|
+
direction: "asc"
|
|
702
|
+
}],
|
|
703
|
+
limit: PAGE_SIZE,
|
|
704
|
+
offset
|
|
705
|
+
});
|
|
706
|
+
channels.push(...page);
|
|
707
|
+
if (page.length < PAGE_SIZE) {
|
|
708
|
+
channels.sort(compareChannelRows);
|
|
709
|
+
return { channels };
|
|
710
|
+
}
|
|
242
711
|
}
|
|
243
|
-
const result = await getBundlesWithLegacyCursorFallback(shouldOverlay ? expandLimitForUnitOfWork(normalizedOptions, unitOfWork) : normalizedOptions, context);
|
|
244
|
-
return shouldOverlay ? overlayResult(result) : result;
|
|
245
|
-
},
|
|
246
|
-
async getChannels(context) {
|
|
247
|
-
if (context === void 0) return getMethods().getChannels();
|
|
248
|
-
return getMethods().getChannels(context);
|
|
249
712
|
},
|
|
250
|
-
async
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const methods = getMethods();
|
|
256
|
-
const unitOfWork = getMutationUnitOfWork(context);
|
|
257
|
-
const params = { changedSets: unitOfWork.changedSets() };
|
|
258
|
-
if (context === void 0) await methods.commitBundle(params);
|
|
259
|
-
else await methods.commitBundle(params, context);
|
|
260
|
-
unitOfWork.clear();
|
|
261
|
-
await hooks?.onDatabaseUpdated?.();
|
|
262
|
-
},
|
|
263
|
-
async updateBundle(targetBundleId, newBundle, context) {
|
|
264
|
-
const unitOfWork = getMutationUnitOfWork(context);
|
|
265
|
-
const currentBundle = await unitOfWork.getById(targetBundleId, () => context === void 0 ? getMethods().getBundleById(targetBundleId) : getMethods().getBundleById(targetBundleId, context));
|
|
266
|
-
if (!currentBundle) throw new Error("targetBundleId not found");
|
|
267
|
-
const updatedBundle = mergeBundleUpdate(currentBundle, newBundle);
|
|
268
|
-
unitOfWork.markUpdate(updatedBundle);
|
|
269
|
-
},
|
|
270
|
-
async appendBundle(inputBundle, context) {
|
|
271
|
-
getMutationUnitOfWork(context).markInsert(inputBundle);
|
|
272
|
-
},
|
|
273
|
-
async deleteBundle(deleteBundle, context) {
|
|
274
|
-
getMutationUnitOfWork(context).markDelete(deleteBundle);
|
|
713
|
+
async delete(input) {
|
|
714
|
+
if (!isChannelText(input.id)) throw new DatabasePluginInputError("invalid-data");
|
|
715
|
+
const result = await implementation.deleteChannel(input);
|
|
716
|
+
validateChannelDeleteResult(result);
|
|
717
|
+
return result;
|
|
275
718
|
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
719
|
+
},
|
|
720
|
+
analytics: {
|
|
721
|
+
async append(row) {
|
|
722
|
+
await crud.create({
|
|
723
|
+
model: "bundle_events",
|
|
724
|
+
data: row
|
|
725
|
+
});
|
|
726
|
+
},
|
|
727
|
+
async scan(input) {
|
|
728
|
+
const rows = [];
|
|
729
|
+
for (let offset = 0; rows.length < input.limit; offset += PAGE_SIZE) {
|
|
730
|
+
const page = await crud.findMany({
|
|
731
|
+
model: "bundle_events",
|
|
732
|
+
where: [{
|
|
733
|
+
field: "received_at_ms",
|
|
734
|
+
operator: "lt",
|
|
735
|
+
value: input.beforeReceivedAtMs
|
|
736
|
+
}],
|
|
737
|
+
orderBy: [{
|
|
738
|
+
field: "received_at_ms",
|
|
739
|
+
direction: "asc"
|
|
740
|
+
}, {
|
|
741
|
+
field: "id",
|
|
742
|
+
direction: "asc"
|
|
743
|
+
}],
|
|
744
|
+
limit: PAGE_SIZE,
|
|
745
|
+
offset
|
|
287
746
|
});
|
|
288
|
-
|
|
747
|
+
rows.push(...page.filter((row) => input.after === void 0 || row.received_at_ms > input.after.receivedAtMs || row.received_at_ms === input.after.receivedAtMs && row.id > input.after.id));
|
|
748
|
+
if (page.length < PAGE_SIZE) break;
|
|
289
749
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
750
|
+
return rows.slice(0, input.limit);
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
apiKeys: {
|
|
754
|
+
async create(row) {
|
|
755
|
+
const run = async (database) => {
|
|
756
|
+
if (await findApiKeyByHash(database, row.hash) !== null) return "existing";
|
|
757
|
+
await database.create({
|
|
758
|
+
model: "api_keys",
|
|
759
|
+
data: row
|
|
760
|
+
});
|
|
761
|
+
return "created";
|
|
293
762
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
763
|
+
return transaction ? transaction((database) => run(createTransactionDatabasePlugin(database))) : run(crud);
|
|
764
|
+
},
|
|
765
|
+
findByHash: (hash) => findApiKeyByHash(crud, hash),
|
|
766
|
+
list: () => crud.findMany({
|
|
767
|
+
model: "api_keys",
|
|
768
|
+
orderBy: [{
|
|
769
|
+
field: "created_at_ms",
|
|
770
|
+
direction: "desc"
|
|
771
|
+
}, {
|
|
772
|
+
field: "id",
|
|
773
|
+
direction: "asc"
|
|
774
|
+
}],
|
|
775
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
776
|
+
offset: 0
|
|
777
|
+
}),
|
|
778
|
+
async revoke({ id, revokedAtMs }) {
|
|
779
|
+
return crud.update({
|
|
780
|
+
model: "api_keys",
|
|
781
|
+
where: [{
|
|
782
|
+
field: "id",
|
|
783
|
+
value: id
|
|
784
|
+
}],
|
|
785
|
+
update: { revoked_at_ms: revokedAtMs }
|
|
298
786
|
});
|
|
299
|
-
return wrappedGetUpdateInfo;
|
|
300
787
|
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
commit,
|
|
791
|
+
...implementation.dispose ? { dispose: implementation.dispose } : {}
|
|
304
792
|
};
|
|
305
|
-
}
|
|
793
|
+
};
|
|
794
|
+
const createDatabasePlugin = (options) => ({ ...options });
|
|
306
795
|
//#endregion
|
|
307
|
-
export { createDatabasePlugin };
|
|
796
|
+
export { DatabaseAtomicCommitUnsupportedError, DatabaseRowReferencedError, createDatabasePlugin, createDatabasePluginAdapter };
|