@hot-updater/server 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/adapters/databaseConstraintErrors.cjs +23 -0
- package/dist/adapters/databaseConstraintErrors.mjs +21 -0
- package/dist/adapters/databasePluginUtils.cjs +117 -0
- package/dist/adapters/databasePluginUtils.mjs +106 -0
- package/dist/adapters/drizzle.cjs +53 -176
- package/dist/adapters/drizzle.d.cts +6 -4
- package/dist/adapters/drizzle.d.mts +6 -4
- package/dist/adapters/drizzle.mjs +54 -177
- package/dist/adapters/drizzleCrud.cjs +250 -0
- package/dist/adapters/drizzleCrud.mjs +249 -0
- package/dist/adapters/drizzleLazyDB.cjs +84 -36
- package/dist/adapters/drizzleLazyDB.mjs +84 -36
- package/dist/adapters/drizzleQuery.cjs +68 -0
- package/dist/adapters/drizzleQuery.mjs +67 -0
- package/dist/adapters/kysely.cjs +18 -138
- package/dist/adapters/kysely.d.cts +3 -12
- package/dist/adapters/kysely.d.mts +3 -12
- package/dist/adapters/kysely.mjs +19 -139
- package/dist/adapters/kyselyCrud.cjs +254 -0
- package/dist/adapters/kyselyCrud.mjs +253 -0
- package/dist/adapters/kyselyQuery.cjs +56 -0
- package/dist/adapters/kyselyQuery.mjs +55 -0
- package/dist/adapters/mongodb.cjs +21 -174
- package/dist/adapters/mongodb.d.cts +3 -2
- package/dist/adapters/mongodb.d.mts +3 -2
- package/dist/adapters/mongodb.mjs +21 -174
- package/dist/adapters/mongodbCollections.cjs +36 -0
- package/dist/adapters/mongodbCollections.mjs +30 -0
- package/dist/adapters/mongodbQuery.cjs +77 -0
- package/dist/adapters/mongodbQuery.mjs +70 -0
- package/dist/adapters/mongodbReads.cjs +148 -0
- package/dist/adapters/mongodbReads.mjs +147 -0
- package/dist/adapters/mongodbWrites.cjs +134 -0
- package/dist/adapters/mongodbWrites.mjs +133 -0
- package/dist/adapters/prisma.cjs +205 -180
- package/dist/adapters/prisma.d.cts +3 -3
- package/dist/adapters/prisma.d.mts +3 -3
- package/dist/adapters/prisma.mjs +206 -181
- package/dist/adapters/prismaQuery.cjs +58 -0
- package/dist/adapters/prismaQuery.mjs +56 -0
- package/dist/adapters/prismaRows.cjs +198 -0
- package/dist/adapters/prismaRows.mjs +188 -0
- package/dist/analytics/bounded/activeOverview.cjs +87 -0
- package/dist/analytics/bounded/activeOverview.mjs +85 -0
- package/dist/analytics/bounded/installationSearch.cjs +42 -0
- package/dist/analytics/bounded/installationSearch.mjs +42 -0
- package/dist/analytics/bounded/persistence.cjs +39 -0
- package/dist/analytics/bounded/persistence.mjs +38 -0
- package/dist/analytics/bounded/provider.cjs +170 -0
- package/dist/analytics/bounded/provider.d.cts +7 -0
- package/dist/analytics/bounded/provider.d.mts +7 -0
- package/dist/analytics/bounded/provider.mjs +170 -0
- package/dist/analytics/bounded/scan.cjs +144 -0
- package/dist/analytics/bounded/scan.mjs +138 -0
- package/dist/analytics/domain.d.cts +121 -0
- package/dist/analytics/domain.d.mts +121 -0
- package/dist/analytics/errors.cjs +22 -0
- package/dist/analytics/errors.mjs +20 -0
- package/dist/analytics/eventInput.cjs +111 -0
- package/dist/analytics/eventInput.mjs +111 -0
- package/dist/analytics/persistence.d.cts +6 -0
- package/dist/analytics/persistence.d.mts +6 -0
- package/dist/analytics/queryInput.cjs +58 -0
- package/dist/analytics/queryInput.mjs +55 -0
- package/dist/analytics/routes.cjs +63 -0
- package/dist/analytics/routes.mjs +61 -0
- package/dist/analytics/types.d.cts +17 -0
- package/dist/analytics/types.d.mts +17 -0
- package/dist/apiKeys.cjs +139 -0
- package/dist/apiKeys.d.cts +50 -0
- package/dist/apiKeys.d.mts +50 -0
- package/dist/apiKeys.mjs +133 -0
- package/dist/createHotUpdaterCore.cjs +48 -26
- package/dist/createHotUpdaterCore.d.cts +49 -17
- package/dist/createHotUpdaterCore.d.mts +49 -17
- package/dist/createHotUpdaterCore.mjs +51 -29
- package/dist/db/createBundleDiff.cjs +45 -68
- package/dist/db/createBundleDiff.d.cts +3 -3
- package/dist/db/createBundleDiff.d.mts +3 -3
- package/dist/db/createBundleDiff.mjs +47 -68
- package/dist/db/databasePluginCore.cjs +138 -0
- package/dist/db/databasePluginCore.mjs +137 -0
- package/dist/db/fixedMigrator.cjs +36 -93
- package/dist/db/fixedMigrator.mjs +36 -93
- package/dist/db/fixedMigratorMongo.cjs +319 -0
- package/dist/db/fixedMigratorMongo.mjs +319 -0
- package/dist/db/fixedMigratorShared.cjs +23 -0
- package/dist/db/fixedMigratorShared.mjs +18 -0
- package/dist/db/mongoMigrationExecution.cjs +11 -0
- package/dist/db/mongoMigrationExecution.mjs +11 -0
- package/dist/db/releaseCatalog.cjs +66 -0
- package/dist/db/releaseCatalog.d.cts +17 -0
- package/dist/db/releaseCatalog.d.mts +17 -0
- package/dist/db/releaseCatalog.mjs +64 -0
- package/dist/db/schema/mongodb.cjs +14 -6
- package/dist/db/schema/mongodb.mjs +14 -6
- package/dist/db/schema/registry.cjs +0 -2
- package/dist/db/schema/registry.mjs +1 -2
- package/dist/db/schema/sql.cjs +31 -14
- package/dist/db/schema/sql.mjs +32 -13
- package/dist/db/schema/sqlOperations.cjs +4 -4
- package/dist/db/schema/sqlOperations.mjs +5 -5
- package/dist/db/schemaGenerators.cjs +52 -18
- package/dist/db/schemaGenerators.mjs +52 -18
- package/dist/db/schemaReadiness.cjs +4 -3
- package/dist/db/schemaReadiness.mjs +4 -3
- package/dist/db/sqlMigrationExecution.cjs +67 -0
- package/dist/db/sqlMigrationExecution.mjs +66 -0
- package/dist/db/types.cjs +2 -6
- package/dist/db/types.d.cts +60 -16
- package/dist/db/types.d.mts +60 -16
- package/dist/db/types.mjs +3 -6
- package/dist/db/updateArtifacts.cjs +114 -60
- package/dist/db/updateArtifacts.mjs +117 -62
- package/dist/handler.cjs +108 -282
- package/dist/handler.d.cts +3 -46
- package/dist/handler.d.mts +3 -46
- package/dist/handler.mjs +107 -281
- package/dist/handlerBundleRoutes.cjs +155 -0
- package/dist/handlerBundleRoutes.mjs +155 -0
- package/dist/handlerErrors.cjs +9 -0
- package/dist/handlerErrors.mjs +9 -0
- package/dist/handlerParameters.cjs +39 -0
- package/dist/handlerParameters.mjs +34 -0
- package/dist/handlerReleaseCatalogRoutes.cjs +111 -0
- package/dist/handlerReleaseCatalogRoutes.mjs +110 -0
- package/dist/handlerReleaseManagementRoutes.cjs +125 -0
- package/dist/handlerReleaseManagementRoutes.mjs +124 -0
- package/dist/handlerTypes.d.cts +63 -0
- package/dist/handlerTypes.d.mts +63 -0
- package/dist/handlerVersionRoutes.cjs +10 -0
- package/dist/handlerVersionRoutes.d.cts +4 -0
- package/dist/handlerVersionRoutes.d.mts +4 -0
- package/dist/handlerVersionRoutes.mjs +9 -0
- package/dist/index.cjs +10 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.mts +9 -3
- package/dist/index.mjs +5 -2
- package/dist/node.cjs +19 -12
- package/dist/node.d.cts +15 -10
- package/dist/node.d.mts +15 -10
- package/dist/node.mjs +19 -13
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/schema/dsl.cjs +19 -2
- package/dist/schema/dsl.mjs +17 -3
- package/dist/schema/index.cjs +1 -8
- package/dist/schema/index.mjs +2 -8
- package/dist/schema/types.cjs +3 -1
- package/dist/schema/types.mjs +3 -2
- package/dist/schema/v1_0_0.cjs +364 -0
- package/dist/schema/v1_0_0.mjs +357 -0
- package/dist/storageAccess.cjs +69 -25
- package/dist/storageAccess.mjs +68 -25
- package/dist/types/index.d.cts +4 -3
- package/dist/types/index.d.mts +5 -4
- package/package.json +8 -7
- package/src/adapters/databaseConstraintErrors.ts +25 -0
- package/src/adapters/databasePluginTestDatabase.ts +106 -0
- package/src/adapters/databasePluginUtils.spec.ts +29 -0
- package/src/adapters/databasePluginUtils.ts +214 -0
- package/src/adapters/drizzle.spec.ts +208 -47
- package/src/adapters/drizzle.ts +106 -293
- package/src/adapters/drizzleCrud.ts +419 -0
- package/src/adapters/drizzleLazyDB.ts +206 -100
- package/src/adapters/drizzleQuery.ts +164 -0
- package/src/adapters/kysely.spec.ts +126 -88
- package/src/adapters/kysely.ts +76 -334
- package/src/adapters/kyselyCrud.ts +588 -0
- package/src/adapters/kyselyQuery.ts +128 -0
- package/src/adapters/mongodb.spec.ts +141 -0
- package/src/adapters/mongodb.ts +60 -288
- package/src/adapters/mongodbCollections.ts +62 -0
- package/src/adapters/mongodbQuery.ts +226 -0
- package/src/adapters/mongodbReads.ts +311 -0
- package/src/adapters/mongodbRows.spec.ts +64 -0
- package/src/adapters/mongodbRows.ts +118 -0
- package/src/adapters/mongodbTestClient.ts +342 -0
- package/src/adapters/mongodbTestFilter.ts +166 -0
- package/src/adapters/mongodbWrites.ts +260 -0
- package/src/adapters/prisma.spec.ts +196 -0
- package/src/adapters/prisma.ts +417 -292
- package/src/adapters/prismaQuery.ts +129 -0
- package/src/adapters/prismaRows.spec.ts +98 -0
- package/src/adapters/prismaRows.ts +301 -0
- package/src/adapters/prismaTestClient.ts +396 -0
- package/src/analytics/analytics.integration.spec.ts +144 -0
- package/src/analytics/bounded/activeOverview.spec.ts +189 -0
- package/src/analytics/bounded/activeOverview.ts +148 -0
- package/src/analytics/bounded/installationSearch.spec.ts +130 -0
- package/src/analytics/bounded/installationSearch.ts +69 -0
- package/src/analytics/bounded/persistence.ts +43 -0
- package/src/analytics/bounded/provider.spec.ts +219 -0
- package/src/analytics/bounded/provider.ts +229 -0
- package/src/analytics/bounded/scan.ts +235 -0
- package/src/analytics/domain.ts +139 -0
- package/src/analytics/errors.ts +18 -0
- package/src/analytics/eventInput.ts +168 -0
- package/src/analytics/persistence.ts +10 -0
- package/src/analytics/queryInput.ts +102 -0
- package/src/analytics/routes.ts +123 -0
- package/src/analytics/types.ts +44 -0
- package/src/apiKeys.integration.spec.ts +192 -0
- package/src/apiKeys.spec.ts +162 -0
- package/src/apiKeys.ts +220 -0
- package/src/createHotUpdaterCore.ts +164 -100
- package/src/db/bundleRows.spec.ts +164 -0
- package/src/db/bundleRows.ts +10 -140
- package/src/db/createBundleDiff.spec.ts +172 -127
- package/src/db/createBundleDiff.ts +91 -110
- package/src/db/databasePluginCore.spec.ts +62 -0
- package/src/db/databasePluginCore.testFixtures.ts +76 -0
- package/src/db/databasePluginCore.ts +185 -0
- package/src/db/databasePluginUpdateCheck.bench.ts +107 -0
- package/src/db/fixedMigrator.spec.ts +184 -42
- package/src/db/fixedMigrator.ts +64 -205
- package/src/db/fixedMigratorMongo.spec.ts +192 -0
- package/src/db/fixedMigratorMongo.ts +425 -0
- package/src/db/fixedMigratorShared.ts +36 -0
- package/src/db/hotUpdaterSchema.ts +0 -1
- package/src/db/index.spec.ts +333 -1025
- package/src/db/mongoMigrationExecution.ts +23 -0
- package/src/db/node-sqlite.d.ts +25 -0
- package/src/db/releaseCatalog.ts +148 -0
- package/src/db/schema/mongodb.ts +20 -6
- package/src/db/schema/sql.ts +52 -11
- package/src/db/schema/sqlOperations.ts +5 -4
- package/src/db/schemaGenerators.ts +107 -23
- package/src/db/schemaReadiness.ts +2 -1
- package/src/db/sqlMigrationExecution.spec.ts +65 -0
- package/src/db/sqlMigrationExecution.ts +108 -0
- package/src/db/types.spec.ts +55 -0
- package/src/db/types.ts +129 -65
- package/src/db/updateArtifacts.spec.ts +359 -0
- package/src/db/updateArtifacts.ts +290 -126
- package/src/handler-standalone.integration.spec.ts +401 -364
- package/src/handler.spec.ts +137 -582
- package/src/handler.testFixtures.ts +30 -0
- package/src/handler.ts +218 -616
- package/src/handlerAuth.integration.spec.ts +33 -0
- package/src/handlerBundleRoutes.ts +259 -0
- package/src/handlerErrors.ts +7 -0
- package/src/handlerManagement.spec.ts +262 -0
- package/src/handlerParameters.ts +105 -0
- package/src/handlerReleaseCatalogRoutes.ts +154 -0
- package/src/handlerReleaseManagementRoutes.ts +213 -0
- package/src/handlerTypes.ts +96 -0
- package/src/handlerVersionRoutes.ts +12 -0
- package/src/index.ts +22 -2
- package/src/node.spec.ts +79 -10
- package/src/node.ts +20 -17
- package/src/releaseCatalogRoutes.integration.spec.ts +238 -0
- package/src/runtime.spec.ts +126 -959
- package/src/runtime.testFixtures.ts +71 -0
- package/src/schema/dsl-all-versions.spec.ts +26 -17
- package/src/schema/dsl.ts +41 -2
- package/src/schema/index.ts +3 -9
- package/src/schema/types.ts +9 -3
- package/src/schema/v1_0_0.spec.ts +146 -0
- package/src/schema/v1_0_0.ts +465 -0
- package/src/storageAccess.spec.ts +127 -32
- package/src/storageAccess.ts +106 -55
- package/src/types/index.ts +2 -1
- package/dist/db/bundleRows.cjs +0 -82
- package/dist/db/bundleRows.d.cts +0 -33
- package/dist/db/bundleRows.d.mts +0 -33
- package/dist/db/bundleRows.mjs +0 -79
- package/dist/db/pluginCore.cjs +0 -242
- package/dist/db/pluginCore.mjs +0 -241
- package/dist/db/schema/sqlMigrations.cjs +0 -74
- package/dist/db/schema/sqlMigrations.mjs +0 -73
- package/dist/db/schemaEnhancements.cjs +0 -20
- package/dist/db/schemaEnhancements.mjs +0 -19
- package/dist/route.cjs +0 -7
- package/dist/route.mjs +0 -7
- package/dist/schema/v0_21_0.cjs +0 -38
- package/dist/schema/v0_21_0.mjs +0 -37
- package/dist/schema/v0_29_0.cjs +0 -45
- package/dist/schema/v0_29_0.mjs +0 -44
- package/dist/schema/v0_31_0.cjs +0 -82
- package/dist/schema/v0_31_0.mjs +0 -80
- package/src/db/pluginCore.spec.ts +0 -1095
- package/src/db/pluginCore.ts +0 -475
- package/src/db/pluginUpdateCheck.bench.ts +0 -254
- package/src/db/schema/sqlMigrations.ts +0 -219
- package/src/db/schemaEnhancements.ts +0 -55
- package/src/route.ts +0 -7
- package/src/schema/v0_21_0.ts +0 -52
- package/src/schema/v0_29_0.ts +0 -62
- package/src/schema/v0_31_0.spec.ts +0 -73
- package/src/schema/v0_31_0.ts +0 -120
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DatabaseModel,
|
|
3
|
+
DatabaseWhere,
|
|
4
|
+
} from "@hot-updater/plugin-core/internal";
|
|
5
|
+
import { sql, type RawBuilder } from "kysely";
|
|
6
|
+
|
|
7
|
+
import type { ORMSQLProvider } from "../db/types";
|
|
8
|
+
import { escapeGlobPattern, escapeLikePattern } from "./databasePluginUtils";
|
|
9
|
+
|
|
10
|
+
class InvalidDatabasePredicateError extends Error {
|
|
11
|
+
readonly name = "InvalidDatabasePredicateError";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const stringPredicate = (
|
|
15
|
+
provider: ORMSQLProvider,
|
|
16
|
+
field: string,
|
|
17
|
+
operator: "contains" | "ends_with" | "starts_with",
|
|
18
|
+
value: string,
|
|
19
|
+
insensitive: boolean,
|
|
20
|
+
): RawBuilder<boolean> => {
|
|
21
|
+
const column = sql.ref(field);
|
|
22
|
+
if (provider === "sqlite" && !insensitive) {
|
|
23
|
+
const literal = escapeGlobPattern(value);
|
|
24
|
+
const pattern =
|
|
25
|
+
operator === "contains"
|
|
26
|
+
? `*${literal}*`
|
|
27
|
+
: operator === "starts_with"
|
|
28
|
+
? `${literal}*`
|
|
29
|
+
: `*${literal}`;
|
|
30
|
+
return sql<boolean>`${column} glob ${pattern}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const literal = escapeLikePattern(value);
|
|
34
|
+
const pattern =
|
|
35
|
+
operator === "contains"
|
|
36
|
+
? `%${literal}%`
|
|
37
|
+
: operator === "starts_with"
|
|
38
|
+
? `${literal}%`
|
|
39
|
+
: `%${literal}`;
|
|
40
|
+
if (insensitive) {
|
|
41
|
+
return sql<boolean>`lower(${column}) like lower(${pattern}) escape '\\'`;
|
|
42
|
+
}
|
|
43
|
+
if (provider === "mysql") {
|
|
44
|
+
return sql<boolean>`binary ${column} like binary ${pattern} escape '\\'`;
|
|
45
|
+
}
|
|
46
|
+
return sql<boolean>`${column} like ${pattern} escape '\\'`;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const predicate = <TModel extends DatabaseModel>(
|
|
50
|
+
provider: ORMSQLProvider,
|
|
51
|
+
condition: DatabaseWhere<TModel>,
|
|
52
|
+
): RawBuilder<boolean> => {
|
|
53
|
+
const column = sql.ref(condition.field);
|
|
54
|
+
const operator = condition.operator ?? "eq";
|
|
55
|
+
switch (operator) {
|
|
56
|
+
case "eq":
|
|
57
|
+
case "ne": {
|
|
58
|
+
if (condition.value === null) {
|
|
59
|
+
return operator === "eq"
|
|
60
|
+
? sql<boolean>`${column} is null`
|
|
61
|
+
: sql<boolean>`${column} is not null`;
|
|
62
|
+
}
|
|
63
|
+
const insensitive =
|
|
64
|
+
"mode" in condition && condition.mode === "insensitive";
|
|
65
|
+
if (insensitive) {
|
|
66
|
+
return operator === "eq"
|
|
67
|
+
? sql<boolean>`lower(${column}) = lower(${condition.value})`
|
|
68
|
+
: sql<boolean>`lower(${column}) <> lower(${condition.value})`;
|
|
69
|
+
}
|
|
70
|
+
return operator === "eq"
|
|
71
|
+
? sql<boolean>`${column} = ${condition.value}`
|
|
72
|
+
: sql<boolean>`${column} <> ${condition.value}`;
|
|
73
|
+
}
|
|
74
|
+
case "gt":
|
|
75
|
+
return sql<boolean>`${column} > ${condition.value}`;
|
|
76
|
+
case "gte":
|
|
77
|
+
return sql<boolean>`${column} >= ${condition.value}`;
|
|
78
|
+
case "lt":
|
|
79
|
+
return sql<boolean>`${column} < ${condition.value}`;
|
|
80
|
+
case "lte":
|
|
81
|
+
return sql<boolean>`${column} <= ${condition.value}`;
|
|
82
|
+
case "in":
|
|
83
|
+
case "not_in": {
|
|
84
|
+
if (!Array.isArray(condition.value)) {
|
|
85
|
+
throw new InvalidDatabasePredicateError();
|
|
86
|
+
}
|
|
87
|
+
if (operator === "in") {
|
|
88
|
+
return condition.value.length === 0
|
|
89
|
+
? sql<boolean>`false`
|
|
90
|
+
: sql<boolean>`${column} in (${sql.join(condition.value)})`;
|
|
91
|
+
}
|
|
92
|
+
return condition.value.length === 0
|
|
93
|
+
? sql<boolean>`true`
|
|
94
|
+
: sql<boolean>`${column} not in (${sql.join(condition.value)})`;
|
|
95
|
+
}
|
|
96
|
+
case "contains":
|
|
97
|
+
case "starts_with":
|
|
98
|
+
case "ends_with": {
|
|
99
|
+
if (typeof condition.value !== "string") {
|
|
100
|
+
throw new InvalidDatabasePredicateError();
|
|
101
|
+
}
|
|
102
|
+
return stringPredicate(
|
|
103
|
+
provider,
|
|
104
|
+
condition.field,
|
|
105
|
+
operator,
|
|
106
|
+
condition.value,
|
|
107
|
+
"mode" in condition && condition.mode === "insensitive",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const buildKyselyWhere = <TModel extends DatabaseModel>(
|
|
114
|
+
provider: ORMSQLProvider,
|
|
115
|
+
where: readonly DatabaseWhere<TModel>[] | undefined,
|
|
116
|
+
): RawBuilder<boolean> | undefined => {
|
|
117
|
+
const [first, ...rest] = where ?? [];
|
|
118
|
+
if (first === undefined) return undefined;
|
|
119
|
+
let expression = predicate(provider, first);
|
|
120
|
+
for (const condition of rest) {
|
|
121
|
+
const next = predicate(provider, condition);
|
|
122
|
+
expression =
|
|
123
|
+
condition.connector === "OR"
|
|
124
|
+
? sql<boolean>`(${expression} or ${next})`
|
|
125
|
+
: sql<boolean>`(${expression} and ${next})`;
|
|
126
|
+
}
|
|
127
|
+
return expression;
|
|
128
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { createDatabaseClient } from "@hot-updater/plugin-core";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
createBundlePatchRowFixture,
|
|
6
|
+
createBundleRowFixture,
|
|
7
|
+
} from "../../../test-utils/src/databaseTestFixtures";
|
|
8
|
+
import { setupDatabasePluginTestSuite } from "../../../test-utils/src/setupDatabasePluginTestSuite";
|
|
9
|
+
import { mongoAdapter } from "./mongodb";
|
|
10
|
+
import { createMongoBundleWhere } from "./mongodbQuery";
|
|
11
|
+
import { createMongoTestHarness } from "./mongodbTestClient";
|
|
12
|
+
|
|
13
|
+
const harness = createMongoTestHarness();
|
|
14
|
+
|
|
15
|
+
setupDatabasePluginTestSuite({
|
|
16
|
+
name: "mongoAdapter v2",
|
|
17
|
+
migrate: () => undefined,
|
|
18
|
+
createPlugin: () =>
|
|
19
|
+
mongoAdapter({ client: harness.client, transactions: true }),
|
|
20
|
+
reset: () => harness.reset(),
|
|
21
|
+
dispose: () => harness.close(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("mongoAdapter capabilities", () => {
|
|
25
|
+
it("returns an adapter without an unsafe atomic-batch fallback", () => {
|
|
26
|
+
const plugin = mongoAdapter({ client: harness.client });
|
|
27
|
+
expect(plugin.name).toBe("mongodb");
|
|
28
|
+
expect(plugin.adapterName).toBe("mongodb");
|
|
29
|
+
expect(plugin.provider).toBe("mongodb");
|
|
30
|
+
expect(Reflect.has(plugin, "transaction")).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("rejects a cross-table commit when transactions are disabled", async () => {
|
|
34
|
+
const plugin = mongoAdapter({ client: harness.client });
|
|
35
|
+
const owner = createBundleRowFixture("970");
|
|
36
|
+
const patch = createBundlePatchRowFixture(
|
|
37
|
+
"971",
|
|
38
|
+
owner.id,
|
|
39
|
+
createBundleRowFixture("972").id,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
await expect(
|
|
43
|
+
plugin.commit({
|
|
44
|
+
changes: [
|
|
45
|
+
{
|
|
46
|
+
model: "bundles",
|
|
47
|
+
operation: "insert",
|
|
48
|
+
row: owner,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
model: "bundlePatches",
|
|
52
|
+
operation: "insert",
|
|
53
|
+
row: patch,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
}),
|
|
57
|
+
).rejects.toMatchObject({ name: "DatabaseAtomicCommitUnsupportedError" });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("recovers a tombstoned bundle when an aggregate delete is retried", async () => {
|
|
61
|
+
harness.reset();
|
|
62
|
+
const client = createDatabaseClient(
|
|
63
|
+
mongoAdapter({ client: harness.client }),
|
|
64
|
+
);
|
|
65
|
+
const bundle = {
|
|
66
|
+
id: "bundle-retry",
|
|
67
|
+
platform: "ios" as const,
|
|
68
|
+
fileHash: "bundle-retry-hash",
|
|
69
|
+
gitCommitHash: null,
|
|
70
|
+
storageUri: "storage://bundle-retry",
|
|
71
|
+
archiveByteSize: 3_000_000_001,
|
|
72
|
+
};
|
|
73
|
+
await client.insertBundle(bundle);
|
|
74
|
+
|
|
75
|
+
harness.failNextBundleTombstone();
|
|
76
|
+
await expect(client.deleteBundleById(bundle.id)).rejects.toThrow(
|
|
77
|
+
"injected tombstone failure",
|
|
78
|
+
);
|
|
79
|
+
await expect(client.deleteBundleById(bundle.id)).resolves.toBeUndefined();
|
|
80
|
+
await expect(client.insertBundle(bundle)).resolves.toBeUndefined();
|
|
81
|
+
await expect(client.getBundleById(bundle.id)).resolves.toMatchObject({
|
|
82
|
+
id: bundle.id,
|
|
83
|
+
fileHash: bundle.fileHash,
|
|
84
|
+
storageUri: bundle.storageUri,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("MongoDB low-level predicate translation", () => {
|
|
90
|
+
it("composes connectors left to right", () => {
|
|
91
|
+
expect(
|
|
92
|
+
createMongoBundleWhere([
|
|
93
|
+
{ field: "id", value: "first" },
|
|
94
|
+
{ field: "id", value: "second", connector: "OR" },
|
|
95
|
+
{ field: "platform", value: "ios", connector: "AND" },
|
|
96
|
+
]),
|
|
97
|
+
).toEqual({
|
|
98
|
+
$and: [
|
|
99
|
+
{
|
|
100
|
+
$or: [
|
|
101
|
+
{ $expr: { $eq: ["$id", { $literal: "first" }] } },
|
|
102
|
+
{ $expr: { $eq: ["$id", { $literal: "second" }] } },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{ $expr: { $eq: ["$platform", { $literal: "ios" }] } },
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("escapes insensitive string patterns", () => {
|
|
111
|
+
expect(
|
|
112
|
+
createMongoBundleWhere([
|
|
113
|
+
{
|
|
114
|
+
field: "storage_uri",
|
|
115
|
+
operator: "contains",
|
|
116
|
+
value: "release.*",
|
|
117
|
+
mode: "insensitive",
|
|
118
|
+
},
|
|
119
|
+
]),
|
|
120
|
+
).toEqual({
|
|
121
|
+
$expr: {
|
|
122
|
+
$regexMatch: {
|
|
123
|
+
input: { $ifNull: ["$storage_uri", ""] },
|
|
124
|
+
regex: { $literal: "release\\.\\*" },
|
|
125
|
+
options: "i",
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("preserves empty set semantics", () => {
|
|
132
|
+
expect(
|
|
133
|
+
createMongoBundleWhere([{ field: "id", operator: "in", value: [] }]),
|
|
134
|
+
).toEqual({ $expr: { $in: ["$id", { $literal: [] }] } });
|
|
135
|
+
expect(
|
|
136
|
+
createMongoBundleWhere([{ field: "id", operator: "not_in", value: [] }]),
|
|
137
|
+
).toEqual({
|
|
138
|
+
$expr: { $not: [{ $in: ["$id", { $literal: [] }] }] },
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
package/src/adapters/mongodb.ts
CHANGED
|
@@ -1,302 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
Bundle,
|
|
4
|
-
DatabaseBundleQueryOptions,
|
|
5
|
-
DatabaseBundleQueryWhere,
|
|
6
|
-
} from "@hot-updater/plugin-core";
|
|
1
|
+
import { createDatabasePlugin } from "@hot-updater/plugin-core";
|
|
7
2
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "
|
|
13
|
-
import type { ClientSession, Collection, Filter, MongoClient } from "mongodb";
|
|
3
|
+
createDatabasePluginAdapter,
|
|
4
|
+
type DatabasePluginImplementation,
|
|
5
|
+
type TransactionDatabasePluginImplementation,
|
|
6
|
+
} from "@hot-updater/plugin-core/internal";
|
|
7
|
+
import type { ClientSession, MongoClient } from "mongodb";
|
|
14
8
|
|
|
15
|
-
import {
|
|
16
|
-
bundleToPatchRows,
|
|
17
|
-
bundleToRow,
|
|
18
|
-
type BundlePatchRow,
|
|
19
|
-
type BundleRow,
|
|
20
|
-
rowToBundle,
|
|
21
|
-
} from "../db/bundleRows";
|
|
22
9
|
import { createMongoMigrator } from "../db/fixedMigrator";
|
|
23
|
-
import type {
|
|
10
|
+
import type { DatabaseAdapterWithCapabilities } from "../db/types";
|
|
11
|
+
import { createMongoCollections } from "./mongodbCollections";
|
|
12
|
+
import { createMongoReads } from "./mongodbReads";
|
|
13
|
+
import { createMongoWrites } from "./mongodbWrites";
|
|
24
14
|
|
|
25
15
|
export interface MongoDBConfig {
|
|
26
16
|
readonly client: MongoClient;
|
|
17
|
+
readonly transactions?: boolean;
|
|
27
18
|
}
|
|
28
19
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
...(
|
|
36
|
-
...(
|
|
37
|
-
? where.fingerprintHash === null
|
|
38
|
-
? { fingerprint_hash: { $in: [null, ""] } }
|
|
39
|
-
: { fingerprint_hash: where.fingerprintHash }
|
|
40
|
-
: {}),
|
|
41
|
-
...(where?.id
|
|
42
|
-
? {
|
|
43
|
-
id: {
|
|
44
|
-
...(where.id.eq !== undefined ? { $eq: where.id.eq } : {}),
|
|
45
|
-
...(where.id.gt !== undefined ? { $gt: where.id.gt } : {}),
|
|
46
|
-
...(where.id.gte !== undefined ? { $gte: where.id.gte } : {}),
|
|
47
|
-
...(where.id.lt !== undefined ? { $lt: where.id.lt } : {}),
|
|
48
|
-
...(where.id.lte !== undefined ? { $lte: where.id.lte } : {}),
|
|
49
|
-
...(where.id.in !== undefined ? { $in: where.id.in } : {}),
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
: {}),
|
|
20
|
+
const createMongoImplementation = (
|
|
21
|
+
client: MongoClient,
|
|
22
|
+
session?: ClientSession,
|
|
23
|
+
): DatabasePluginImplementation => {
|
|
24
|
+
const collections = createMongoCollections(client);
|
|
25
|
+
return {
|
|
26
|
+
...createMongoWrites(collections, session),
|
|
27
|
+
...createMongoReads(collections, session),
|
|
53
28
|
};
|
|
54
|
-
const targetAppVersionFilters: Filter<BundleRow>[] = [];
|
|
55
|
-
if (where?.targetAppVersion !== undefined) {
|
|
56
|
-
targetAppVersionFilters.push(
|
|
57
|
-
where.targetAppVersion === null
|
|
58
|
-
? { target_app_version: { $in: [null, ""] } }
|
|
59
|
-
: { target_app_version: where.targetAppVersion },
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
if (where?.targetAppVersionIn) {
|
|
63
|
-
targetAppVersionFilters.push({
|
|
64
|
-
target_app_version: { $in: where.targetAppVersionIn },
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
if (where?.targetAppVersionNotNull) {
|
|
68
|
-
targetAppVersionFilters.push({
|
|
69
|
-
target_app_version: { $exists: true, $nin: [null, ""] },
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const filters = [
|
|
74
|
-
...(Object.keys(baseFilter).length > 0 ? [baseFilter] : []),
|
|
75
|
-
...targetAppVersionFilters,
|
|
76
|
-
];
|
|
77
|
-
if (filters.length === 0) return {};
|
|
78
|
-
if (filters.length === 1) return filters[0] ?? {};
|
|
79
|
-
return { $and: filters };
|
|
80
29
|
};
|
|
81
30
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const mapRowsToBundles = async (
|
|
103
|
-
rows: readonly BundleRow[],
|
|
104
|
-
): Promise<Bundle[]> => {
|
|
105
|
-
const patchMap = await fetchPatchMap(rows.map((row) => row.id));
|
|
106
|
-
return rows.map((row) => rowToBundle(row, patchMap.get(row.id) ?? []));
|
|
107
|
-
};
|
|
108
|
-
const isTransactionUnsupported = (error: unknown): boolean =>
|
|
109
|
-
error instanceof Error &&
|
|
110
|
-
/Transaction numbers are only allowed|replica set member or mongos|Transaction API error/i.test(
|
|
111
|
-
error.message,
|
|
112
|
-
);
|
|
113
|
-
const runInTransaction = async <T>(
|
|
114
|
-
operation: (session: ClientSession | undefined) => Promise<T>,
|
|
115
|
-
) => {
|
|
116
|
-
if (typeof client.startSession !== "function") {
|
|
117
|
-
return operation(undefined);
|
|
118
|
-
}
|
|
119
|
-
const session = client.startSession();
|
|
120
|
-
try {
|
|
121
|
-
if (typeof session.withTransaction !== "function") {
|
|
122
|
-
return await operation(session);
|
|
123
|
-
}
|
|
124
|
-
let result: T | undefined;
|
|
125
|
-
await session.withTransaction(async () => {
|
|
126
|
-
result = await operation(session);
|
|
127
|
-
});
|
|
128
|
-
return result as T;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
if (isTransactionUnsupported(error)) {
|
|
131
|
-
return operation(undefined);
|
|
132
|
-
}
|
|
133
|
-
throw error;
|
|
134
|
-
} finally {
|
|
135
|
-
await session.endSession();
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
const replaceBundle = async (
|
|
139
|
-
bundle: Bundle,
|
|
140
|
-
session: ClientSession | undefined,
|
|
141
|
-
) => {
|
|
142
|
-
const row = bundleToRow(bundle);
|
|
143
|
-
await bundles.updateOne(
|
|
144
|
-
{ id: bundle.id },
|
|
145
|
-
{ $set: row },
|
|
146
|
-
{ session, upsert: true },
|
|
147
|
-
);
|
|
148
|
-
await patches.deleteMany({ bundle_id: bundle.id }, { session });
|
|
149
|
-
const patchRows = bundleToPatchRows(bundle);
|
|
150
|
-
if (patchRows.length > 0)
|
|
151
|
-
await patches.insertMany(patchRows, { session });
|
|
152
|
-
};
|
|
153
|
-
const deleteByBundleId = async (
|
|
154
|
-
collection: Collection<BundlePatchRow>,
|
|
155
|
-
field: "bundle_id" | "base_bundle_id",
|
|
156
|
-
bundleId: string,
|
|
157
|
-
session: ClientSession | undefined,
|
|
158
|
-
) => {
|
|
159
|
-
await collection.deleteMany({ [field]: bundleId }, { session });
|
|
160
|
-
};
|
|
161
|
-
return {
|
|
162
|
-
async getBundleById(bundleId) {
|
|
163
|
-
const row = await bundles.findOne({ id: bundleId });
|
|
164
|
-
if (!row) return null;
|
|
165
|
-
const patchMap = await fetchPatchMap([bundleId]);
|
|
166
|
-
return rowToBundle(row, patchMap.get(bundleId) ?? []);
|
|
167
|
-
},
|
|
168
|
-
async getBundles(
|
|
169
|
-
options: DatabaseBundleQueryOptions & { offset?: number },
|
|
170
|
-
) {
|
|
171
|
-
const offset = options.offset ?? 0;
|
|
172
|
-
const orderBy = options.orderBy ?? { field: "id", direction: "desc" };
|
|
173
|
-
const where = mongoWhere(options.where);
|
|
174
|
-
const [total, rows] = await Promise.all([
|
|
175
|
-
bundles.countDocuments(where),
|
|
176
|
-
bundles
|
|
177
|
-
.find(where)
|
|
178
|
-
.sort({ id: orderBy.direction === "asc" ? 1 : -1 })
|
|
179
|
-
.skip(offset)
|
|
180
|
-
.limit(options.limit)
|
|
181
|
-
.toArray(),
|
|
182
|
-
]);
|
|
183
|
-
const patchMap = await fetchPatchMap(rows.map((row) => row.id));
|
|
184
|
-
return {
|
|
185
|
-
data: rows.map((row) => rowToBundle(row, patchMap.get(row.id) ?? [])),
|
|
186
|
-
pagination: calculatePagination(total, {
|
|
187
|
-
limit: options.limit,
|
|
188
|
-
offset,
|
|
189
|
-
}),
|
|
190
|
-
};
|
|
191
|
-
},
|
|
192
|
-
async getUpdateInfo(args, context) {
|
|
193
|
-
if (args._updateStrategy === "appVersion") {
|
|
194
|
-
const channel = args.channel ?? "production";
|
|
195
|
-
const minBundleId = args.minBundleId ?? NIL_UUID;
|
|
196
|
-
const rows = await bundles
|
|
197
|
-
.find({
|
|
198
|
-
enabled: true,
|
|
199
|
-
platform: args.platform,
|
|
200
|
-
channel,
|
|
201
|
-
id: { $gte: minBundleId },
|
|
202
|
-
target_app_version: { $exists: true, $nin: [null, ""] },
|
|
203
|
-
})
|
|
204
|
-
.project<{ target_app_version?: string | null }>({
|
|
205
|
-
target_app_version: 1,
|
|
206
|
-
})
|
|
207
|
-
.toArray();
|
|
208
|
-
|
|
209
|
-
const targetAppVersions = Array.from(
|
|
210
|
-
new Set(
|
|
211
|
-
rows
|
|
212
|
-
.map((row) => row.target_app_version)
|
|
213
|
-
.filter(
|
|
214
|
-
(value): value is string =>
|
|
215
|
-
typeof value === "string" && value.length > 0,
|
|
216
|
-
),
|
|
217
|
-
),
|
|
218
|
-
);
|
|
219
|
-
const compatibleAppVersions = filterCompatibleAppVersions(
|
|
220
|
-
targetAppVersions,
|
|
221
|
-
args.appVersion,
|
|
222
|
-
);
|
|
223
|
-
const updateRows =
|
|
224
|
-
compatibleAppVersions.length > 0
|
|
225
|
-
? await bundles
|
|
226
|
-
.find({
|
|
227
|
-
enabled: true,
|
|
228
|
-
platform: args.platform,
|
|
229
|
-
channel,
|
|
230
|
-
id: { $gte: minBundleId },
|
|
231
|
-
target_app_version: { $in: compatibleAppVersions },
|
|
232
|
-
})
|
|
233
|
-
.sort({ id: -1 })
|
|
234
|
-
.toArray()
|
|
235
|
-
: [];
|
|
236
|
-
|
|
237
|
-
return resolveUpdateInfoFromBundles({
|
|
238
|
-
args: { ...args, channel, minBundleId },
|
|
239
|
-
bundles: await mapRowsToBundles(updateRows),
|
|
240
|
-
context,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const channel = args.channel ?? "production";
|
|
245
|
-
const minBundleId = args.minBundleId ?? NIL_UUID;
|
|
246
|
-
const rows = await bundles
|
|
247
|
-
.find({
|
|
248
|
-
enabled: true,
|
|
249
|
-
platform: args.platform,
|
|
250
|
-
channel,
|
|
251
|
-
id: { $gte: minBundleId },
|
|
252
|
-
fingerprint_hash: args.fingerprintHash,
|
|
253
|
-
})
|
|
254
|
-
.sort({ id: -1 })
|
|
255
|
-
.toArray();
|
|
256
|
-
|
|
257
|
-
return resolveUpdateInfoFromBundles({
|
|
258
|
-
args: { ...args, channel, minBundleId },
|
|
259
|
-
bundles: await mapRowsToBundles(rows),
|
|
260
|
-
context,
|
|
261
|
-
});
|
|
262
|
-
},
|
|
263
|
-
async getChannels() {
|
|
264
|
-
const channels = await bundles.distinct("channel");
|
|
265
|
-
return channels
|
|
266
|
-
.filter((channel): channel is string => typeof channel === "string")
|
|
267
|
-
.sort((left, right) => left.localeCompare(right));
|
|
268
|
-
},
|
|
269
|
-
async commitBundle({ changedSets }) {
|
|
270
|
-
await runInTransaction(async (session) => {
|
|
271
|
-
for (const change of changedSets) {
|
|
272
|
-
if (change.operation === "delete") {
|
|
273
|
-
await deleteByBundleId(
|
|
274
|
-
patches,
|
|
275
|
-
"bundle_id",
|
|
276
|
-
change.data.id,
|
|
277
|
-
session,
|
|
278
|
-
);
|
|
279
|
-
await deleteByBundleId(
|
|
280
|
-
patches,
|
|
281
|
-
"base_bundle_id",
|
|
282
|
-
change.data.id,
|
|
283
|
-
session,
|
|
284
|
-
);
|
|
285
|
-
await bundles.deleteMany({ id: change.data.id }, { session });
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
await replaceBundle(change.data, session);
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
},
|
|
31
|
+
const createTransactionalMongoImplementation = (
|
|
32
|
+
client: MongoClient,
|
|
33
|
+
): DatabasePluginImplementation => ({
|
|
34
|
+
...createMongoImplementation(client),
|
|
35
|
+
deleteChannel: (input) =>
|
|
36
|
+
client.withSession((session) =>
|
|
37
|
+
session.withTransaction(() =>
|
|
38
|
+
createMongoImplementation(client, session).deleteChannel(input),
|
|
39
|
+
),
|
|
40
|
+
),
|
|
41
|
+
transaction: <TResult>(
|
|
42
|
+
callback: (
|
|
43
|
+
transaction: TransactionDatabasePluginImplementation,
|
|
44
|
+
) => Promise<TResult>,
|
|
45
|
+
): Promise<TResult> =>
|
|
46
|
+
client.withSession((session) =>
|
|
47
|
+
session.withTransaction(() =>
|
|
48
|
+
callback(createMongoImplementation(client, session)),
|
|
49
|
+
),
|
|
50
|
+
),
|
|
294
51
|
});
|
|
295
52
|
|
|
296
|
-
export const mongoAdapter = (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
53
|
+
export const mongoAdapter = (
|
|
54
|
+
config: MongoDBConfig,
|
|
55
|
+
): DatabaseAdapterWithCapabilities => {
|
|
56
|
+
const adapter = createDatabasePluginAdapter(
|
|
57
|
+
"mongodb",
|
|
58
|
+
config.transactions === true
|
|
59
|
+
? createTransactionalMongoImplementation(config.client)
|
|
60
|
+
: createMongoImplementation(config.client),
|
|
61
|
+
);
|
|
62
|
+
return Object.assign(
|
|
63
|
+
createDatabasePlugin({
|
|
64
|
+
name: "mongodb",
|
|
65
|
+
models: adapter.models,
|
|
66
|
+
commit: adapter.commit,
|
|
67
|
+
}),
|
|
68
|
+
{
|
|
69
|
+
adapterName: "mongodb",
|
|
70
|
+
provider: "mongodb" as const,
|
|
71
|
+
createMigrator: () => createMongoMigrator(config.client),
|
|
72
|
+
},
|
|
73
|
+
);
|
|
302
74
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BundleEventRow,
|
|
3
|
+
BundlePatchRow,
|
|
4
|
+
BundleRow,
|
|
5
|
+
ChannelRow,
|
|
6
|
+
ApiKeyRow,
|
|
7
|
+
ReleaseCatalogRow,
|
|
8
|
+
ReleaseRow,
|
|
9
|
+
} from "@hot-updater/plugin-core";
|
|
10
|
+
import type { ClientSession, Collection, MongoClient } from "mongodb";
|
|
11
|
+
|
|
12
|
+
export class MongoAdapterConstraintError extends Error {
|
|
13
|
+
readonly name = "MongoAdapterConstraintError";
|
|
14
|
+
|
|
15
|
+
constructor(readonly reason: string) {
|
|
16
|
+
super(`MongoDB adapter constraint failed: ${reason}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const WITHOUT_MONGO_ID = { _id: 0 } as const;
|
|
21
|
+
export const DELETION_TOKEN_FIELD = "_hot_updater_deletion_token" as const;
|
|
22
|
+
|
|
23
|
+
export type MongoBundleDocument = BundleRow & {
|
|
24
|
+
readonly [DELETION_TOKEN_FIELD]?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const WITHOUT_INTERNAL_FIELDS = {
|
|
28
|
+
...WITHOUT_MONGO_ID,
|
|
29
|
+
[DELETION_TOKEN_FIELD]: 0,
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
export const activeBundleFilter = (where: object) => ({
|
|
33
|
+
$and: [where, { [DELETION_TOKEN_FIELD]: { $exists: false } }],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type MongoCollections = {
|
|
37
|
+
readonly bundles: Collection<MongoBundleDocument>;
|
|
38
|
+
readonly bundlePatches: Collection<BundlePatchRow>;
|
|
39
|
+
readonly bundleEvents: Collection<BundleEventRow>;
|
|
40
|
+
readonly channels: Collection<ChannelRow>;
|
|
41
|
+
readonly apiKeys: Collection<ApiKeyRow>;
|
|
42
|
+
readonly releases: Collection<ReleaseRow>;
|
|
43
|
+
readonly releaseCatalogs: Collection<ReleaseCatalogRow>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const createMongoCollections = (
|
|
47
|
+
client: MongoClient,
|
|
48
|
+
): MongoCollections => {
|
|
49
|
+
const database = client.db();
|
|
50
|
+
return {
|
|
51
|
+
bundles: database.collection<MongoBundleDocument>("bundles"),
|
|
52
|
+
bundlePatches: database.collection<BundlePatchRow>("bundle_patches"),
|
|
53
|
+
bundleEvents: database.collection<BundleEventRow>("bundle_events"),
|
|
54
|
+
channels: database.collection<ChannelRow>("channels"),
|
|
55
|
+
apiKeys: database.collection<ApiKeyRow>("api_keys"),
|
|
56
|
+
releases: database.collection<ReleaseRow>("releases"),
|
|
57
|
+
releaseCatalogs: database.collection<ReleaseCatalogRow>("release_catalogs"),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const mongoSessionOptions = (session?: ClientSession) =>
|
|
62
|
+
session === undefined ? {} : { session };
|