@hot-updater/server 0.36.7 → 1.0.0-rc.1
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/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/insights/bounded/activeOverview.cjs +87 -0
- package/dist/insights/bounded/activeOverview.mjs +85 -0
- package/dist/insights/bounded/installationSearch.cjs +42 -0
- package/dist/insights/bounded/installationSearch.mjs +42 -0
- package/dist/insights/bounded/persistence.cjs +39 -0
- package/dist/insights/bounded/persistence.mjs +38 -0
- package/dist/insights/bounded/provider.cjs +170 -0
- package/dist/insights/bounded/provider.d.cts +7 -0
- package/dist/insights/bounded/provider.d.mts +7 -0
- package/dist/insights/bounded/provider.mjs +170 -0
- package/dist/insights/bounded/scan.cjs +144 -0
- package/dist/insights/bounded/scan.mjs +138 -0
- package/dist/insights/domain.d.cts +121 -0
- package/dist/insights/domain.d.mts +121 -0
- package/dist/insights/errors.cjs +22 -0
- package/dist/insights/errors.mjs +20 -0
- package/dist/insights/eventInput.cjs +111 -0
- package/dist/insights/eventInput.mjs +111 -0
- package/dist/insights/persistence.d.cts +6 -0
- package/dist/insights/persistence.d.mts +6 -0
- package/dist/insights/queryInput.cjs +58 -0
- package/dist/insights/queryInput.mjs +55 -0
- package/dist/insights/routes.cjs +63 -0
- package/dist/insights/routes.mjs +61 -0
- package/dist/insights/types.d.cts +17 -0
- package/dist/insights/types.d.mts +17 -0
- 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/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/insights/bounded/activeOverview.spec.ts +189 -0
- package/src/insights/bounded/activeOverview.ts +148 -0
- package/src/insights/bounded/installationSearch.spec.ts +130 -0
- package/src/insights/bounded/installationSearch.ts +69 -0
- package/src/insights/bounded/persistence.ts +43 -0
- package/src/insights/bounded/provider.spec.ts +219 -0
- package/src/insights/bounded/provider.ts +229 -0
- package/src/insights/bounded/scan.ts +235 -0
- package/src/insights/domain.ts +139 -0
- package/src/insights/errors.ts +18 -0
- package/src/insights/eventInput.ts +168 -0
- package/src/insights/insights.integration.spec.ts +144 -0
- package/src/insights/persistence.ts +10 -0
- package/src/insights/queryInput.ts +102 -0
- package/src/insights/routes.ts +123 -0
- package/src/insights/types.ts +44 -0
- 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
|
@@ -17,6 +17,9 @@ import { getSQLProvider } from "./types";
|
|
|
17
17
|
|
|
18
18
|
const literal = (value: string): string => JSON.stringify(value);
|
|
19
19
|
|
|
20
|
+
const varcharLength = (type: HotUpdaterColumnType): number =>
|
|
21
|
+
Number(type.slice("varchar(".length, -1));
|
|
22
|
+
|
|
20
23
|
const toPascalCase = (value: string): string =>
|
|
21
24
|
value
|
|
22
25
|
.split("_")
|
|
@@ -27,11 +30,16 @@ const toPascalCase = (value: string): string =>
|
|
|
27
30
|
const prismaDb = (type: HotUpdaterColumnType, provider: ORMProvider) => {
|
|
28
31
|
if (provider === "postgresql") {
|
|
29
32
|
if (type === "uuid") return " @db.Uuid";
|
|
30
|
-
if (type.startsWith("varchar"))
|
|
33
|
+
if (type.startsWith("varchar")) {
|
|
34
|
+
return ` @db.VarChar(${varcharLength(type)})`;
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
if (provider === "mysql") {
|
|
33
38
|
if (type === "uuid") return " @db.Char(36)";
|
|
34
|
-
if (type
|
|
39
|
+
if (type === "large-string") return " @db.MediumText";
|
|
40
|
+
if (type.startsWith("varchar")) {
|
|
41
|
+
return ` @db.VarChar(${varcharLength(type)})`;
|
|
42
|
+
}
|
|
35
43
|
}
|
|
36
44
|
return "";
|
|
37
45
|
};
|
|
@@ -42,6 +50,7 @@ const prismaType = (
|
|
|
42
50
|
): string => {
|
|
43
51
|
const base = (() => {
|
|
44
52
|
if (column.type === "bool") return "Boolean";
|
|
53
|
+
if (column.type === "float") return "Float";
|
|
45
54
|
if (column.type === "integer") return "Int";
|
|
46
55
|
if (column.type === "json") return "Json";
|
|
47
56
|
return "String";
|
|
@@ -80,11 +89,14 @@ const prismaField = (
|
|
|
80
89
|
const relationTargetFields = (
|
|
81
90
|
table: HotUpdaterTableSchema,
|
|
82
91
|
schema: HotUpdaterVersionedSchema,
|
|
83
|
-
): readonly
|
|
92
|
+
): readonly {
|
|
93
|
+
readonly relation: HotUpdaterRelationSchema;
|
|
94
|
+
readonly sourceTable: HotUpdaterTableSchema;
|
|
95
|
+
}[] =>
|
|
84
96
|
schema.tables.flatMap((sourceTable) =>
|
|
85
|
-
(sourceTable.relations ?? [])
|
|
86
|
-
(relation) => relation.referencedTable === table.ormName
|
|
87
|
-
|
|
97
|
+
(sourceTable.relations ?? [])
|
|
98
|
+
.filter((relation) => relation.referencedTable === table.ormName)
|
|
99
|
+
.map((relation) => ({ relation, sourceTable })),
|
|
88
100
|
);
|
|
89
101
|
|
|
90
102
|
const prismaRelationFields = (
|
|
@@ -92,14 +104,36 @@ const prismaRelationFields = (
|
|
|
92
104
|
schema: HotUpdaterVersionedSchema,
|
|
93
105
|
): string[] => {
|
|
94
106
|
const lines = relationTargetFields(table, schema).map(
|
|
95
|
-
(relation) =>
|
|
96
|
-
`${relation.fieldName} ${
|
|
107
|
+
({ relation, sourceTable }) =>
|
|
108
|
+
`${relation.fieldName} ${sourceTable.ormName}[] @relation(${literal(relation.relationName)})`,
|
|
97
109
|
);
|
|
98
110
|
|
|
99
111
|
for (const relation of table.relations ?? []) {
|
|
100
112
|
const targetType = toPascalCase(relation.referencedTable);
|
|
113
|
+
const foreignKey = table.foreignKeys?.find(
|
|
114
|
+
(item) =>
|
|
115
|
+
item.referencedTable === relation.referencedTable &&
|
|
116
|
+
item.columns.join("\0") === relation.columns.join("\0") &&
|
|
117
|
+
item.referencedColumns.join("\0") ===
|
|
118
|
+
relation.referencedColumns.join("\0"),
|
|
119
|
+
);
|
|
120
|
+
if (!foreignKey) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Missing foreign key metadata for relation ${table.ormName}.${relation.name}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const optional = relation.columns.some(
|
|
126
|
+
(columnName) =>
|
|
127
|
+
table.columns.find(({ ormName }) => ormName === columnName)?.nullable,
|
|
128
|
+
);
|
|
129
|
+
const onDelete =
|
|
130
|
+
foreignKey.onDelete === "cascade"
|
|
131
|
+
? "Cascade"
|
|
132
|
+
: foreignKey.onDelete === "set null"
|
|
133
|
+
? "SetNull"
|
|
134
|
+
: "Restrict";
|
|
101
135
|
lines.push(
|
|
102
|
-
`${relation.targetFieldName} ${targetType === "Bundles" ? "bundles" : relation.referencedTable} @relation(${literal(relation.relationName)}, fields: [${relation.columns.join(", ")}], references: [${relation.referencedColumns.join(", ")}], onUpdate: Restrict, onDelete:
|
|
136
|
+
`${relation.targetFieldName} ${targetType === "Bundles" ? "bundles" : relation.referencedTable}${optional ? "?" : ""} @relation(${literal(relation.relationName)}, fields: [${relation.columns.join(", ")}], references: [${relation.referencedColumns.join(", ")}], onUpdate: Restrict, onDelete: ${onDelete})`,
|
|
103
137
|
);
|
|
104
138
|
}
|
|
105
139
|
|
|
@@ -114,7 +148,7 @@ const prismaIndexes = (
|
|
|
114
148
|
.filter((index) => schemaIndexAppliesToProvider(index, provider))
|
|
115
149
|
.map(
|
|
116
150
|
(index) =>
|
|
117
|
-
|
|
151
|
+
`@@${index.unique ? "unique" : "index"}([${index.columns.join(", ")}], map: ${literal(index.name)})`,
|
|
118
152
|
);
|
|
119
153
|
|
|
120
154
|
export const generatePrismaSchema = (
|
|
@@ -165,6 +199,12 @@ const drizzleColumnFn = (
|
|
|
165
199
|
imports: ["integer"],
|
|
166
200
|
};
|
|
167
201
|
}
|
|
202
|
+
if (column.type === "float") {
|
|
203
|
+
return {
|
|
204
|
+
code: `real(${literal(column.ormName)})`,
|
|
205
|
+
imports: ["real"],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
168
208
|
if (column.type === "json") {
|
|
169
209
|
return {
|
|
170
210
|
code: `blob(${literal(column.ormName)}, { mode: "json" })`,
|
|
@@ -173,7 +213,7 @@ const drizzleColumnFn = (
|
|
|
173
213
|
}
|
|
174
214
|
if (column.type.startsWith("varchar")) {
|
|
175
215
|
return {
|
|
176
|
-
code: `text(${literal(column.ormName)}, { length:
|
|
216
|
+
code: `text(${literal(column.ormName)}, { length: ${varcharLength(column.type)} })`,
|
|
177
217
|
imports: ["text"],
|
|
178
218
|
};
|
|
179
219
|
}
|
|
@@ -196,12 +236,24 @@ const drizzleColumnFn = (
|
|
|
196
236
|
if (column.type === "integer") {
|
|
197
237
|
return { code: `int(${literal(column.ormName)})`, imports: ["int"] };
|
|
198
238
|
}
|
|
239
|
+
if (column.type === "float") {
|
|
240
|
+
return {
|
|
241
|
+
code: `double(${literal(column.ormName)})`,
|
|
242
|
+
imports: ["double"],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
199
245
|
if (column.type === "json") {
|
|
200
246
|
return { code: `json(${literal(column.ormName)})`, imports: ["json"] };
|
|
201
247
|
}
|
|
248
|
+
if (column.type === "large-string") {
|
|
249
|
+
return {
|
|
250
|
+
code: `mediumtext(${literal(column.ormName)})`,
|
|
251
|
+
imports: ["mediumtext"],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
202
254
|
if (column.type.startsWith("varchar")) {
|
|
203
255
|
return {
|
|
204
|
-
code: `varchar(${literal(column.ormName)}, { length:
|
|
256
|
+
code: `varchar(${literal(column.ormName)}, { length: ${varcharLength(column.type)} })`,
|
|
205
257
|
imports: ["varchar"],
|
|
206
258
|
};
|
|
207
259
|
}
|
|
@@ -223,12 +275,18 @@ const drizzleColumnFn = (
|
|
|
223
275
|
imports: ["integer"],
|
|
224
276
|
};
|
|
225
277
|
}
|
|
278
|
+
if (column.type === "float") {
|
|
279
|
+
return {
|
|
280
|
+
code: `doublePrecision(${literal(column.ormName)})`,
|
|
281
|
+
imports: ["doublePrecision"],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
226
284
|
if (column.type === "json") {
|
|
227
285
|
return { code: `json(${literal(column.ormName)})`, imports: ["json"] };
|
|
228
286
|
}
|
|
229
287
|
if (column.type.startsWith("varchar")) {
|
|
230
288
|
return {
|
|
231
|
-
code: `varchar(${literal(column.ormName)}, { length:
|
|
289
|
+
code: `varchar(${literal(column.ormName)}, { length: ${varcharLength(column.type)} })`,
|
|
232
290
|
imports: ["varchar"],
|
|
233
291
|
};
|
|
234
292
|
}
|
|
@@ -237,7 +295,9 @@ const drizzleColumnFn = (
|
|
|
237
295
|
|
|
238
296
|
const drizzleDefault = (value: HotUpdaterDefault | undefined): string => {
|
|
239
297
|
if (!value) return "";
|
|
240
|
-
if (value.type === "json")
|
|
298
|
+
if (value.type === "json") {
|
|
299
|
+
return `.default(${JSON.stringify(value.value)})`;
|
|
300
|
+
}
|
|
241
301
|
return `.default(${JSON.stringify(value.value)})`;
|
|
242
302
|
};
|
|
243
303
|
|
|
@@ -276,10 +336,14 @@ const drizzleTable = (
|
|
|
276
336
|
const callbacks: string[] = [];
|
|
277
337
|
for (const foreignKey of table.foreignKeys ?? []) {
|
|
278
338
|
imports.add("foreignKey");
|
|
339
|
+
const referencedTable =
|
|
340
|
+
foreignKey.referencedTable === table.ormName
|
|
341
|
+
? "table"
|
|
342
|
+
: foreignKey.referencedTable;
|
|
279
343
|
callbacks.push(`foreignKey({
|
|
280
344
|
columns: [table.${foreignKey.columns.join(", table.")}],
|
|
281
|
-
foreignColumns: [${
|
|
282
|
-
`, ${
|
|
345
|
+
foreignColumns: [${referencedTable}.${foreignKey.referencedColumns.join(
|
|
346
|
+
`, ${referencedTable}.`,
|
|
283
347
|
)}],
|
|
284
348
|
name: ${literal(foreignKey.name)}
|
|
285
349
|
}).onUpdate(${literal(foreignKey.onUpdate)}).onDelete(${literal(
|
|
@@ -289,9 +353,10 @@ const drizzleTable = (
|
|
|
289
353
|
for (const index of (table.indexes ?? []).filter((item) =>
|
|
290
354
|
schemaIndexAppliesToProvider(item, provider),
|
|
291
355
|
)) {
|
|
292
|
-
|
|
356
|
+
const indexFunction = index.unique ? "uniqueIndex" : "index";
|
|
357
|
+
imports.add(indexFunction);
|
|
293
358
|
callbacks.push(
|
|
294
|
-
|
|
359
|
+
`${indexFunction}(${literal(index.name)}).on(${index.columns
|
|
295
360
|
.map((column) => `table.${column}`)
|
|
296
361
|
.join(", ")})`,
|
|
297
362
|
);
|
|
@@ -310,9 +375,16 @@ const drizzleTable = (
|
|
|
310
375
|
)})`;
|
|
311
376
|
};
|
|
312
377
|
|
|
313
|
-
const drizzleRelations = (
|
|
314
|
-
|
|
315
|
-
|
|
378
|
+
const drizzleRelations = (
|
|
379
|
+
table: HotUpdaterTableSchema,
|
|
380
|
+
schema: HotUpdaterVersionedSchema,
|
|
381
|
+
): string | undefined => {
|
|
382
|
+
const sourceRelations = table.relations ?? [];
|
|
383
|
+
const targetRelations = relationTargetFields(table, schema);
|
|
384
|
+
if (sourceRelations.length === 0 && targetRelations.length === 0) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
const lines = sourceRelations.map(
|
|
316
388
|
(
|
|
317
389
|
relation,
|
|
318
390
|
) => ` ${relation.targetFieldName}: one(${relation.referencedTable}, {
|
|
@@ -323,7 +395,19 @@ const drizzleRelations = (table: HotUpdaterTableSchema): string | undefined => {
|
|
|
323
395
|
.join(", ")}]
|
|
324
396
|
})`,
|
|
325
397
|
);
|
|
326
|
-
|
|
398
|
+
lines.push(
|
|
399
|
+
...targetRelations.map(
|
|
400
|
+
({ relation, sourceTable }) =>
|
|
401
|
+
` ${relation.fieldName}: many(${sourceTable.ormName}, {
|
|
402
|
+
relationName: ${literal(relation.relationName)}
|
|
403
|
+
})`,
|
|
404
|
+
),
|
|
405
|
+
);
|
|
406
|
+
const callbacks = [
|
|
407
|
+
...(sourceRelations.length > 0 ? ["one"] : []),
|
|
408
|
+
...(targetRelations.length > 0 ? ["many"] : []),
|
|
409
|
+
];
|
|
410
|
+
return `export const ${table.ormName}Relations = relations(${table.ormName}, ({ ${callbacks.join(", ")} }) => ({
|
|
327
411
|
${lines.join(",\n")}
|
|
328
412
|
}))`;
|
|
329
413
|
};
|
|
@@ -337,7 +421,7 @@ export const generateDrizzleSchema = (
|
|
|
337
421
|
|
|
338
422
|
for (const table of schema.tables) {
|
|
339
423
|
body.push(drizzleTable(table, provider, imports));
|
|
340
|
-
const relations = drizzleRelations(table);
|
|
424
|
+
const relations = drizzleRelations(table, schema);
|
|
341
425
|
if (relations) body.push(relations);
|
|
342
426
|
}
|
|
343
427
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HOT_UPDATER_SCHEMA_VERSION } from "../schema/types";
|
|
2
|
+
import { unsupportedSchemaUpgradeMessage } from "./fixedMigratorShared";
|
|
2
3
|
import type { Migrator } from "./types";
|
|
3
4
|
|
|
4
5
|
export class HotUpdaterSchemaMigrationRequiredError extends Error {
|
|
@@ -9,7 +10,7 @@ export class HotUpdaterSchemaMigrationRequiredError extends Error {
|
|
|
9
10
|
super(
|
|
10
11
|
currentVersion === undefined
|
|
11
12
|
? `Hot Updater database schema is not initialized for ${adapterName}. Run \`hot-updater db migrate\` before using this adapter.`
|
|
12
|
-
:
|
|
13
|
+
: unsupportedSchemaUpgradeMessage(currentVersion),
|
|
13
14
|
);
|
|
14
15
|
this.name = "HotUpdaterSchemaMigrationRequiredError";
|
|
15
16
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DatabaseSync, type SqliteValue } from "node:sqlite";
|
|
2
|
+
|
|
3
|
+
import { Kysely, SqliteDialect } from "kysely";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { executeMigrationStatements } from "./sqlMigrationExecution";
|
|
7
|
+
|
|
8
|
+
const createSqliteKysely = (database: DatabaseSync): Kysely<object> =>
|
|
9
|
+
new Kysely<object>({
|
|
10
|
+
dialect: new SqliteDialect({
|
|
11
|
+
database: {
|
|
12
|
+
close: () => database.close(),
|
|
13
|
+
prepare: (sqlText) => {
|
|
14
|
+
const statement = database.prepare(sqlText);
|
|
15
|
+
return {
|
|
16
|
+
reader: statement.columns().length > 0,
|
|
17
|
+
all: (parameters) =>
|
|
18
|
+
statement.all(...(parameters as SqliteValue[])),
|
|
19
|
+
run: (parameters) => {
|
|
20
|
+
const result = statement.run(...(parameters as SqliteValue[]));
|
|
21
|
+
return {
|
|
22
|
+
changes: result.changes,
|
|
23
|
+
lastInsertRowid: result.lastInsertRowid,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
iterate: (parameters) =>
|
|
27
|
+
statement.iterate(...(parameters as SqliteValue[])),
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("executeMigrationStatements", () => {
|
|
35
|
+
it("rolls back SQLite statements and restores foreign keys when a statement fails", async () => {
|
|
36
|
+
const database = new DatabaseSync(":memory:");
|
|
37
|
+
database.exec("pragma foreign_keys = on");
|
|
38
|
+
const db = createSqliteKysely(database);
|
|
39
|
+
|
|
40
|
+
await expect(
|
|
41
|
+
executeMigrationStatements({
|
|
42
|
+
db,
|
|
43
|
+
provider: "sqlite",
|
|
44
|
+
statements: [
|
|
45
|
+
"create table migration_rows (id integer primary key)",
|
|
46
|
+
"insert into migration_rows (id) values (1)",
|
|
47
|
+
"insert into missing_rows (id) values (1)",
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
).rejects.toThrow("no such table: missing_rows");
|
|
51
|
+
|
|
52
|
+
expect(
|
|
53
|
+
database
|
|
54
|
+
.prepare(
|
|
55
|
+
"select name from sqlite_master where type = 'table' and name = 'migration_rows'",
|
|
56
|
+
)
|
|
57
|
+
.all(),
|
|
58
|
+
).toEqual([]);
|
|
59
|
+
expect(database.prepare("pragma foreign_keys").get()).toEqual({
|
|
60
|
+
foreign_keys: 1,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await db.destroy();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { sql, type QueryExecutorProvider } from "kysely";
|
|
2
|
+
|
|
3
|
+
import type { ORMSQLProvider } from "./types";
|
|
4
|
+
|
|
5
|
+
class SqliteMigrationForeignKeyError extends Error {
|
|
6
|
+
readonly name = "SqliteMigrationForeignKeyError";
|
|
7
|
+
|
|
8
|
+
constructor(readonly violations: number) {
|
|
9
|
+
super(`SQLite migration produced ${violations} foreign key violation(s).`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const executeTransactionalStatements = async (
|
|
14
|
+
db: QueryExecutorProvider,
|
|
15
|
+
statements: readonly string[],
|
|
16
|
+
): Promise<void> => {
|
|
17
|
+
await db.getExecutor().provideConnection(async (connection) => {
|
|
18
|
+
const execute = (statement: string) =>
|
|
19
|
+
connection.executeQuery(sql.raw(statement).compile(db));
|
|
20
|
+
await execute("begin");
|
|
21
|
+
try {
|
|
22
|
+
for (const statement of statements) await execute(statement);
|
|
23
|
+
await execute("commit");
|
|
24
|
+
} catch (error) {
|
|
25
|
+
await execute("rollback");
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const executeSqliteStatements = async (
|
|
32
|
+
db: QueryExecutorProvider,
|
|
33
|
+
statements: readonly string[],
|
|
34
|
+
): Promise<void> => {
|
|
35
|
+
await db.getExecutor().provideConnection(async (connection) => {
|
|
36
|
+
const execute = (statement: string) =>
|
|
37
|
+
connection.executeQuery(sql.raw(statement).compile(db));
|
|
38
|
+
const foreignKeys = await execute("pragma foreign_keys");
|
|
39
|
+
const state = foreignKeys.rows[0];
|
|
40
|
+
const foreignKeysWereEnabled =
|
|
41
|
+
typeof state === "object" &&
|
|
42
|
+
state !== null &&
|
|
43
|
+
"foreign_keys" in state &&
|
|
44
|
+
state.foreign_keys === 1;
|
|
45
|
+
|
|
46
|
+
await execute("pragma foreign_keys = off");
|
|
47
|
+
try {
|
|
48
|
+
await execute("begin");
|
|
49
|
+
try {
|
|
50
|
+
for (const statement of statements) {
|
|
51
|
+
if (
|
|
52
|
+
statement === "pragma foreign_keys = off" ||
|
|
53
|
+
statement === "pragma foreign_keys = on"
|
|
54
|
+
) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (statement === "pragma foreign_key_check") {
|
|
58
|
+
const result = await execute(statement);
|
|
59
|
+
if (result.rows.length > 0) {
|
|
60
|
+
throw new SqliteMigrationForeignKeyError(result.rows.length);
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
await execute(statement);
|
|
65
|
+
}
|
|
66
|
+
await execute("commit");
|
|
67
|
+
} catch (error) {
|
|
68
|
+
await execute("rollback");
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
await execute(
|
|
73
|
+
foreignKeysWereEnabled
|
|
74
|
+
? "pragma foreign_keys = on"
|
|
75
|
+
: "pragma foreign_keys = off",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const executeMysqlStatements = async (
|
|
82
|
+
db: QueryExecutorProvider,
|
|
83
|
+
statements: readonly string[],
|
|
84
|
+
): Promise<void> => {
|
|
85
|
+
for (const statement of statements) {
|
|
86
|
+
await sql.raw(statement).execute(db);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const executeMigrationStatements = async ({
|
|
91
|
+
db,
|
|
92
|
+
provider,
|
|
93
|
+
statements,
|
|
94
|
+
}: {
|
|
95
|
+
readonly db: QueryExecutorProvider;
|
|
96
|
+
readonly provider: ORMSQLProvider;
|
|
97
|
+
readonly statements: readonly string[];
|
|
98
|
+
}): Promise<void> => {
|
|
99
|
+
if (provider === "mysql") {
|
|
100
|
+
await executeMysqlStatements(db, statements);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (provider === "sqlite") {
|
|
104
|
+
await executeSqliteStatements(db, statements);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await executeTransactionalStatements(db, statements);
|
|
108
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { createInMemoryDatabasePlugin } from "../../../test-utils/test/inMemoryDatabasePlugin";
|
|
4
|
+
import { isDatabasePlugin } from "./types";
|
|
5
|
+
|
|
6
|
+
describe("isDatabasePlugin", () => {
|
|
7
|
+
it("accepts a direct fixed-model plugin object", () => {
|
|
8
|
+
const plugin = createInMemoryDatabasePlugin();
|
|
9
|
+
|
|
10
|
+
const result = isDatabasePlugin(plugin);
|
|
11
|
+
|
|
12
|
+
expect(result).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("rejects a v1 factory and malformed required domains", () => {
|
|
16
|
+
const plugin = createInMemoryDatabasePlugin();
|
|
17
|
+
const factory = () => plugin;
|
|
18
|
+
const malformedBundles = {
|
|
19
|
+
...plugin,
|
|
20
|
+
models: {
|
|
21
|
+
...plugin.models,
|
|
22
|
+
bundles: { ...plugin.models.bundles, findMany: null },
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
const missingInsights = {
|
|
26
|
+
...plugin,
|
|
27
|
+
models: { ...plugin.models, insights: undefined },
|
|
28
|
+
};
|
|
29
|
+
const malformedChannels = {
|
|
30
|
+
...plugin,
|
|
31
|
+
models: {
|
|
32
|
+
...plugin.models,
|
|
33
|
+
channels: { ...plugin.models.channels, delete: null },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const malformedApiKeys = {
|
|
37
|
+
...plugin,
|
|
38
|
+
models: {
|
|
39
|
+
...plugin.models,
|
|
40
|
+
apiKeys: {
|
|
41
|
+
...plugin.models.apiKeys,
|
|
42
|
+
revoke: null,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
const malformedDispose = { ...plugin, dispose: null };
|
|
47
|
+
|
|
48
|
+
expect(isDatabasePlugin(factory)).toBe(false);
|
|
49
|
+
expect(isDatabasePlugin(malformedBundles)).toBe(false);
|
|
50
|
+
expect(isDatabasePlugin(missingInsights)).toBe(false);
|
|
51
|
+
expect(isDatabasePlugin(malformedChannels)).toBe(false);
|
|
52
|
+
expect(isDatabasePlugin(malformedApiKeys)).toBe(false);
|
|
53
|
+
expect(isDatabasePlugin(malformedDispose)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|