@hot-updater/server 1.0.0-rc.1 → 1.0.0-rc.2
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/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/adapters/databaseConstraintErrors.cjs +0 -1
- package/dist/adapters/databasePluginUtils.cjs +0 -1
- package/dist/adapters/drizzle.cjs +10 -3
- package/dist/adapters/drizzle.d.cts +0 -1
- package/dist/adapters/drizzle.d.mts +0 -1
- package/dist/adapters/drizzle.mjs +11 -3
- package/dist/adapters/drizzleCrud.cjs +62 -1
- package/dist/adapters/drizzleCrud.mjs +61 -2
- package/dist/adapters/drizzleLazyDB.cjs +6 -1
- package/dist/adapters/drizzleLazyDB.mjs +6 -1
- package/dist/adapters/drizzleQuery.cjs +3 -2
- package/dist/adapters/drizzleQuery.mjs +3 -1
- package/dist/adapters/insightsTransaction.cjs +22 -0
- package/dist/adapters/insightsTransaction.mjs +22 -0
- package/dist/adapters/kysely.cjs +2 -1
- package/dist/adapters/kysely.d.cts +0 -1
- package/dist/adapters/kysely.d.mts +0 -1
- package/dist/adapters/kysely.mjs +3 -1
- package/dist/adapters/kyselyCrud.cjs +54 -3
- package/dist/adapters/kyselyCrud.mjs +54 -3
- package/dist/adapters/kyselyQuery.cjs +2 -2
- package/dist/adapters/kyselyQuery.mjs +2 -1
- package/dist/adapters/mongodb.cjs +27 -1
- package/dist/adapters/mongodb.d.cts +1 -1
- package/dist/adapters/mongodb.d.mts +1 -1
- package/dist/adapters/mongodb.mjs +28 -1
- package/dist/adapters/mongodbCollections.cjs +2 -0
- package/dist/adapters/mongodbCollections.mjs +2 -0
- package/dist/adapters/mongodbQuery.cjs +21 -4
- package/dist/adapters/mongodbQuery.mjs +21 -5
- package/dist/adapters/mongodbReads.cjs +91 -29
- package/dist/adapters/mongodbReads.mjs +92 -29
- package/dist/adapters/mongodbWrites.cjs +16 -2
- package/dist/adapters/mongodbWrites.mjs +18 -3
- package/dist/adapters/prisma.cjs +55 -7
- package/dist/adapters/prisma.d.cts +0 -1
- package/dist/adapters/prisma.d.mts +0 -1
- package/dist/adapters/prisma.mjs +56 -7
- package/dist/adapters/prismaQuery.cjs +0 -1
- package/dist/adapters/prismaRows.cjs +22 -1
- package/dist/adapters/prismaRows.mjs +22 -1
- package/dist/apiKeys.cjs +6 -2
- package/dist/apiKeys.d.cts +0 -1
- package/dist/apiKeys.d.mts +0 -1
- package/dist/apiKeys.mjs +3 -3
- package/dist/createHotUpdaterCore.cjs +24 -9
- package/dist/createHotUpdaterCore.d.cts +1 -1
- package/dist/createHotUpdaterCore.d.mts +1 -1
- package/dist/createHotUpdaterCore.mjs +23 -9
- package/dist/db/createBundleDiff.cjs +2 -2
- package/dist/db/createBundleDiff.d.cts +1 -5
- package/dist/db/createBundleDiff.d.mts +1 -5
- package/dist/db/databasePluginCore.cjs +0 -1
- package/dist/db/fixedMigrator.cjs +2 -2
- package/dist/db/fixedMigrator.mjs +2 -2
- package/dist/db/fixedMigratorMongo.cjs +6 -4
- package/dist/db/fixedMigratorMongo.mjs +6 -4
- package/dist/db/index.d.cts +0 -1
- package/dist/db/index.d.mts +0 -1
- package/dist/db/releaseCatalog.cjs +0 -1
- package/dist/db/releaseCatalog.d.cts +0 -1
- package/dist/db/releaseCatalog.d.mts +0 -1
- package/dist/db/schema/insightsCollation.cjs +14 -0
- package/dist/db/schema/insightsCollation.mjs +14 -0
- package/dist/db/schema/sql.cjs +11 -7
- package/dist/db/schema/sql.mjs +8 -8
- package/dist/db/schemaGenerators.cjs +28 -9
- package/dist/db/schemaGenerators.mjs +28 -9
- package/dist/db/schemaReadiness.cjs +2 -0
- package/dist/db/schemaReadiness.d.cts +1 -0
- package/dist/db/schemaReadiness.d.mts +1 -0
- package/dist/db/schemaReadiness.mjs +2 -0
- package/dist/db/sqlMigrationExecution.cjs +1 -1
- package/dist/db/sqlMigrationExecution.mjs +1 -0
- package/dist/db/types.cjs +3 -1
- package/dist/db/types.d.cts +3 -6
- package/dist/db/types.d.mts +3 -6
- package/dist/db/types.mjs +2 -2
- package/dist/db/updateArtifacts.cjs +0 -1
- package/dist/handler.d.cts +1 -2
- package/dist/handler.d.mts +1 -2
- package/dist/handlerReleaseCatalogRoutes.cjs +0 -1
- package/dist/handlerReleaseManagementRoutes.cjs +0 -1
- package/dist/handlerTypes.d.cts +0 -1
- package/dist/handlerTypes.d.mts +0 -1
- package/dist/handlerVersionRoutes.d.cts +1 -0
- package/dist/handlerVersionRoutes.d.mts +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/insights/domain.d.cts +37 -69
- package/dist/insights/domain.d.mts +37 -69
- package/dist/insights/errors.cjs +1 -8
- package/dist/insights/errors.mjs +2 -8
- package/dist/insights/eventInput.cjs +49 -5
- package/dist/insights/eventInput.mjs +48 -6
- package/dist/insights/provider.cjs +314 -0
- package/dist/insights/provider.d.cts +6 -0
- package/dist/insights/provider.d.mts +6 -0
- package/dist/insights/provider.mjs +314 -0
- package/dist/insights/queryInput.cjs +78 -43
- package/dist/insights/queryInput.mjs +76 -40
- package/dist/insights/routes.cjs +20 -25
- package/dist/insights/routes.mjs +22 -27
- package/dist/insights/types.d.cts +27 -12
- package/dist/insights/types.d.mts +27 -12
- package/dist/node.d.cts +0 -1
- package/dist/node.d.mts +0 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/schema/index.cjs +17 -3
- package/dist/schema/index.mjs +4 -4
- package/dist/schema/v1_0_0.cjs +72 -30
- package/dist/schema/v1_0_0.mjs +72 -31
- package/dist/storageAccess.cjs +0 -1
- package/dist/types/index.d.cts +0 -1
- package/dist/types/index.d.mts +0 -1
- package/package.json +7 -7
- package/src/adapters/databasePluginTestDatabase.ts +14 -0
- package/src/adapters/drizzle.spec.ts +99 -0
- package/src/adapters/drizzle.ts +21 -3
- package/src/adapters/drizzleCrud.ts +154 -2
- package/src/adapters/drizzleLazyDB.ts +17 -3
- package/src/adapters/insightsTransaction.ts +32 -0
- package/src/adapters/kysely.spec.ts +29 -0
- package/src/adapters/kysely.ts +10 -1
- package/src/adapters/kyselyCrud.ts +121 -0
- package/src/adapters/mongodb.integration.spec.ts +210 -0
- package/src/adapters/mongodb.spec.ts +69 -1
- package/src/adapters/mongodb.ts +66 -2
- package/src/adapters/mongodbCollections.ts +5 -0
- package/src/adapters/mongodbQuery.ts +32 -3
- package/src/adapters/mongodbReads.ts +69 -0
- package/src/adapters/mongodbTestClient.ts +41 -6
- package/src/adapters/mongodbTestFilter.ts +4 -1
- package/src/adapters/mongodbWrites.ts +27 -0
- package/src/adapters/prisma.spec.ts +97 -0
- package/src/adapters/prisma.ts +103 -3
- package/src/adapters/prismaRows.ts +37 -1
- package/src/adapters/prismaTestClient.ts +22 -5
- package/src/apiKeys.integration.spec.ts +1 -1
- package/src/createHotUpdaterCore.ts +21 -7
- package/src/db/databasePluginUpdateCheck.bench.ts +3 -0
- package/src/db/fixedMigrator.spec.ts +65 -0
- package/src/db/fixedMigratorMongo.spec.ts +56 -9
- package/src/db/fixedMigratorMongo.ts +10 -10
- package/src/db/index.spec.ts +8 -1
- package/src/db/schema/insightsCollation.ts +21 -0
- package/src/db/schema/sql.ts +8 -5
- package/src/db/schemaGenerators.ts +41 -13
- package/src/db/types.spec.ts +8 -0
- package/src/db/types.ts +10 -4
- package/src/handler-standalone.integration.spec.ts +5 -7
- package/src/index.ts +2 -2
- package/src/insights/domain.ts +39 -75
- package/src/insights/errors.ts +0 -7
- package/src/insights/eventInput.ts +60 -3
- package/src/insights/insights.integration.spec.ts +254 -17
- package/src/insights/provider.spec.ts +385 -0
- package/src/insights/provider.ts +591 -0
- package/src/insights/queryInput.ts +112 -73
- package/src/insights/routes.ts +37 -56
- package/src/insights/types.ts +47 -36
- package/src/schema/dsl-all-versions.spec.ts +3 -1
- package/src/schema/v1_0_0.spec.ts +77 -3
- package/src/schema/v1_0_0.ts +94 -30
- package/dist/insights/bounded/activeOverview.cjs +0 -87
- package/dist/insights/bounded/activeOverview.mjs +0 -85
- package/dist/insights/bounded/installationSearch.cjs +0 -42
- package/dist/insights/bounded/installationSearch.mjs +0 -42
- package/dist/insights/bounded/persistence.cjs +0 -39
- package/dist/insights/bounded/persistence.mjs +0 -38
- package/dist/insights/bounded/provider.cjs +0 -170
- package/dist/insights/bounded/provider.d.cts +0 -7
- package/dist/insights/bounded/provider.d.mts +0 -7
- package/dist/insights/bounded/provider.mjs +0 -170
- package/dist/insights/bounded/scan.cjs +0 -144
- package/dist/insights/bounded/scan.mjs +0 -138
- package/dist/insights/persistence.d.cts +0 -6
- package/dist/insights/persistence.d.mts +0 -6
- package/src/insights/bounded/activeOverview.spec.ts +0 -189
- package/src/insights/bounded/activeOverview.ts +0 -148
- package/src/insights/bounded/installationSearch.spec.ts +0 -130
- package/src/insights/bounded/installationSearch.ts +0 -69
- package/src/insights/bounded/persistence.ts +0 -43
- package/src/insights/bounded/provider.spec.ts +0 -219
- package/src/insights/bounded/provider.ts +0 -229
- package/src/insights/bounded/scan.ts +0 -235
- package/src/insights/persistence.ts +0 -10
package/dist/schema/v1_0_0.cjs
CHANGED
|
@@ -255,19 +255,30 @@ const releaseCatalogsV100 = require_dsl.table("release_catalogs", {
|
|
|
255
255
|
referencedColumns: ["id"]
|
|
256
256
|
})]
|
|
257
257
|
});
|
|
258
|
+
const insightsTextCollations = {
|
|
259
|
+
postgresql: "\"C\"",
|
|
260
|
+
mysql: "utf8mb4_0900_bin"
|
|
261
|
+
};
|
|
262
|
+
const insightsProviders = [
|
|
263
|
+
"postgresql",
|
|
264
|
+
"mysql",
|
|
265
|
+
"sqlite",
|
|
266
|
+
"cockroachdb",
|
|
267
|
+
"mongodb"
|
|
268
|
+
];
|
|
258
269
|
const bundleEventsV100 = require_dsl.table("bundle_events", {
|
|
259
270
|
id: require_dsl.idColumn("id", "uuid"),
|
|
260
|
-
type: require_dsl.
|
|
261
|
-
install_id: require_dsl.
|
|
262
|
-
user_id: require_dsl.
|
|
271
|
+
type: require_dsl.column("type", require_dsl.varchar(32)),
|
|
272
|
+
install_id: require_dsl.column("install_id", require_dsl.varchar(255)).collate(insightsTextCollations),
|
|
273
|
+
user_id: require_dsl.column("user_id", require_dsl.varchar(255)).collate(insightsTextCollations).nullable(),
|
|
263
274
|
username: require_dsl.stringColumn("username").nullable(),
|
|
264
275
|
from_release_id: require_dsl.uuid("from_release_id").nullable(),
|
|
265
276
|
from_bundle_id: require_dsl.uuid("from_bundle_id").nullable(),
|
|
266
277
|
to_release_id: require_dsl.uuid("to_release_id").nullable(),
|
|
267
278
|
to_bundle_id: require_dsl.uuid("to_bundle_id"),
|
|
268
|
-
platform: require_dsl.stringColumn("platform"),
|
|
279
|
+
platform: require_dsl.stringColumn("platform").collate(insightsTextCollations),
|
|
269
280
|
app_version: require_dsl.stringColumn("app_version"),
|
|
270
|
-
channel: require_dsl.stringColumn("channel"),
|
|
281
|
+
channel: require_dsl.stringColumn("channel").collate(insightsTextCollations),
|
|
271
282
|
cohort: require_dsl.stringColumn("cohort"),
|
|
272
283
|
update_strategy: require_dsl.stringColumn("update_strategy").nullable(),
|
|
273
284
|
fingerprint_hash: require_dsl.stringColumn("fingerprint_hash").nullable(),
|
|
@@ -278,43 +289,26 @@ const bundleEventsV100 = require_dsl.table("bundle_events", {
|
|
|
278
289
|
require_dsl.index("bundle_events_received_at_idx", ["received_at_ms", "id"]),
|
|
279
290
|
require_dsl.index("bundle_events_install_idx", [
|
|
280
291
|
"install_id",
|
|
281
|
-
"received_at_ms",
|
|
282
|
-
"id"
|
|
283
|
-
]),
|
|
284
|
-
require_dsl.index("bundle_events_user_id_idx", [
|
|
285
|
-
"user_id",
|
|
286
|
-
"received_at_ms",
|
|
287
|
-
"id"
|
|
288
|
-
]),
|
|
289
|
-
require_dsl.index("bundle_events_username_idx", [
|
|
290
|
-
"username",
|
|
291
|
-
"received_at_ms",
|
|
292
|
-
"id"
|
|
293
|
-
]),
|
|
294
|
-
require_dsl.index("bundle_events_to_bundle_idx", [
|
|
295
292
|
"type",
|
|
296
|
-
"to_bundle_id",
|
|
297
293
|
"received_at_ms",
|
|
298
294
|
"id"
|
|
299
295
|
]),
|
|
300
296
|
require_dsl.index("bundle_events_from_bundle_idx", [
|
|
301
297
|
"type",
|
|
298
|
+
"platform",
|
|
299
|
+
"channel",
|
|
302
300
|
"from_bundle_id",
|
|
303
301
|
"received_at_ms",
|
|
304
302
|
"id"
|
|
305
|
-
]),
|
|
306
|
-
require_dsl.index("
|
|
307
|
-
"type",
|
|
308
|
-
"to_release_id",
|
|
309
|
-
"received_at_ms",
|
|
310
|
-
"id"
|
|
311
|
-
]),
|
|
312
|
-
require_dsl.index("bundle_events_from_release_idx", [
|
|
303
|
+
], insightsProviders),
|
|
304
|
+
require_dsl.index("bundle_events_to_bundle_idx", [
|
|
313
305
|
"type",
|
|
314
|
-
"
|
|
306
|
+
"platform",
|
|
307
|
+
"channel",
|
|
308
|
+
"to_bundle_id",
|
|
315
309
|
"received_at_ms",
|
|
316
310
|
"id"
|
|
317
|
-
])
|
|
311
|
+
], insightsProviders)
|
|
318
312
|
],
|
|
319
313
|
checks: [
|
|
320
314
|
require_dsl.check({
|
|
@@ -339,6 +333,52 @@ const bundleEventsV100 = require_dsl.table("bundle_events", {
|
|
|
339
333
|
})
|
|
340
334
|
]
|
|
341
335
|
});
|
|
336
|
+
const bundleInstallationsV100 = require_dsl.table("bundle_installations", {
|
|
337
|
+
install_id: require_dsl.idColumn("install_id", require_dsl.varchar(255)).collate(insightsTextCollations),
|
|
338
|
+
id: require_dsl.uuid("id"),
|
|
339
|
+
user_id: require_dsl.column("user_id", require_dsl.varchar(255)).collate(insightsTextCollations).nullable(),
|
|
340
|
+
username: require_dsl.stringColumn("username").nullable(),
|
|
341
|
+
to_bundle_id: require_dsl.uuid("to_bundle_id"),
|
|
342
|
+
type: require_dsl.column("type", require_dsl.varchar(32)),
|
|
343
|
+
platform: require_dsl.stringColumn("platform").collate(insightsTextCollations),
|
|
344
|
+
app_version: require_dsl.stringColumn("app_version"),
|
|
345
|
+
channel: require_dsl.stringColumn("channel").collate(insightsTextCollations),
|
|
346
|
+
cohort: require_dsl.stringColumn("cohort"),
|
|
347
|
+
received_at_ms: require_dsl.float("received_at_ms")
|
|
348
|
+
}, {
|
|
349
|
+
indexes: [
|
|
350
|
+
require_dsl.index("bundle_installations_user_id_idx", ["user_id", "install_id"]),
|
|
351
|
+
require_dsl.index("bundle_installations_received_at_idx", ["received_at_ms"]),
|
|
352
|
+
require_dsl.index("bundle_installations_scope_idx", [
|
|
353
|
+
"platform",
|
|
354
|
+
"channel",
|
|
355
|
+
"received_at_ms"
|
|
356
|
+
], insightsProviders),
|
|
357
|
+
require_dsl.index("bundle_installations_bundle_idx", [
|
|
358
|
+
"platform",
|
|
359
|
+
"channel",
|
|
360
|
+
"to_bundle_id",
|
|
361
|
+
"received_at_ms"
|
|
362
|
+
], insightsProviders)
|
|
363
|
+
],
|
|
364
|
+
checks: [
|
|
365
|
+
require_dsl.check({
|
|
366
|
+
name: "bundle_installations_type_check",
|
|
367
|
+
expression: "type in ('UPDATE_APPLIED', 'RECOVERED', 'RELEASE_ADOPTED', 'UNCHANGED')",
|
|
368
|
+
sqliteInline: true
|
|
369
|
+
}),
|
|
370
|
+
require_dsl.check({
|
|
371
|
+
name: "bundle_installations_platform_check",
|
|
372
|
+
expression: "platform in ('ios', 'android')",
|
|
373
|
+
sqliteInline: true
|
|
374
|
+
}),
|
|
375
|
+
require_dsl.check({
|
|
376
|
+
name: "bundle_installations_received_at_check",
|
|
377
|
+
expression: "received_at_ms >= 0",
|
|
378
|
+
sqliteInline: true
|
|
379
|
+
})
|
|
380
|
+
]
|
|
381
|
+
});
|
|
342
382
|
const v1_0_0 = require_dsl.schema({
|
|
343
383
|
version: "1.0.0",
|
|
344
384
|
settingsTable: require_types.HOT_UPDATER_SETTINGS_TABLE,
|
|
@@ -349,6 +389,7 @@ const v1_0_0 = require_dsl.schema({
|
|
|
349
389
|
releasesV100,
|
|
350
390
|
releaseCatalogsV100,
|
|
351
391
|
bundleEventsV100,
|
|
392
|
+
bundleInstallationsV100,
|
|
352
393
|
apiKeysV100,
|
|
353
394
|
require_settings.createSettingsTable("1.0.0")
|
|
354
395
|
]
|
|
@@ -356,6 +397,7 @@ const v1_0_0 = require_dsl.schema({
|
|
|
356
397
|
//#endregion
|
|
357
398
|
exports.apiKeysV100 = apiKeysV100;
|
|
358
399
|
exports.bundleEventsV100 = bundleEventsV100;
|
|
400
|
+
exports.bundleInstallationsV100 = bundleInstallationsV100;
|
|
359
401
|
exports.bundlePatchesV100 = bundlePatchesV100;
|
|
360
402
|
exports.bundlesV100 = bundlesV100;
|
|
361
403
|
exports.channelsV100 = channelsV100;
|
package/dist/schema/v1_0_0.mjs
CHANGED
|
@@ -255,19 +255,30 @@ const releaseCatalogsV100 = table("release_catalogs", {
|
|
|
255
255
|
referencedColumns: ["id"]
|
|
256
256
|
})]
|
|
257
257
|
});
|
|
258
|
+
const insightsTextCollations = {
|
|
259
|
+
postgresql: "\"C\"",
|
|
260
|
+
mysql: "utf8mb4_0900_bin"
|
|
261
|
+
};
|
|
262
|
+
const insightsProviders = [
|
|
263
|
+
"postgresql",
|
|
264
|
+
"mysql",
|
|
265
|
+
"sqlite",
|
|
266
|
+
"cockroachdb",
|
|
267
|
+
"mongodb"
|
|
268
|
+
];
|
|
258
269
|
const bundleEventsV100 = table("bundle_events", {
|
|
259
270
|
id: idColumn("id", "uuid"),
|
|
260
|
-
type:
|
|
261
|
-
install_id:
|
|
262
|
-
user_id:
|
|
271
|
+
type: column("type", varchar(32)),
|
|
272
|
+
install_id: column("install_id", varchar(255)).collate(insightsTextCollations),
|
|
273
|
+
user_id: column("user_id", varchar(255)).collate(insightsTextCollations).nullable(),
|
|
263
274
|
username: stringColumn("username").nullable(),
|
|
264
275
|
from_release_id: uuid("from_release_id").nullable(),
|
|
265
276
|
from_bundle_id: uuid("from_bundle_id").nullable(),
|
|
266
277
|
to_release_id: uuid("to_release_id").nullable(),
|
|
267
278
|
to_bundle_id: uuid("to_bundle_id"),
|
|
268
|
-
platform: stringColumn("platform"),
|
|
279
|
+
platform: stringColumn("platform").collate(insightsTextCollations),
|
|
269
280
|
app_version: stringColumn("app_version"),
|
|
270
|
-
channel: stringColumn("channel"),
|
|
281
|
+
channel: stringColumn("channel").collate(insightsTextCollations),
|
|
271
282
|
cohort: stringColumn("cohort"),
|
|
272
283
|
update_strategy: stringColumn("update_strategy").nullable(),
|
|
273
284
|
fingerprint_hash: stringColumn("fingerprint_hash").nullable(),
|
|
@@ -278,43 +289,26 @@ const bundleEventsV100 = table("bundle_events", {
|
|
|
278
289
|
index("bundle_events_received_at_idx", ["received_at_ms", "id"]),
|
|
279
290
|
index("bundle_events_install_idx", [
|
|
280
291
|
"install_id",
|
|
281
|
-
"received_at_ms",
|
|
282
|
-
"id"
|
|
283
|
-
]),
|
|
284
|
-
index("bundle_events_user_id_idx", [
|
|
285
|
-
"user_id",
|
|
286
|
-
"received_at_ms",
|
|
287
|
-
"id"
|
|
288
|
-
]),
|
|
289
|
-
index("bundle_events_username_idx", [
|
|
290
|
-
"username",
|
|
291
|
-
"received_at_ms",
|
|
292
|
-
"id"
|
|
293
|
-
]),
|
|
294
|
-
index("bundle_events_to_bundle_idx", [
|
|
295
292
|
"type",
|
|
296
|
-
"to_bundle_id",
|
|
297
293
|
"received_at_ms",
|
|
298
294
|
"id"
|
|
299
295
|
]),
|
|
300
296
|
index("bundle_events_from_bundle_idx", [
|
|
301
297
|
"type",
|
|
298
|
+
"platform",
|
|
299
|
+
"channel",
|
|
302
300
|
"from_bundle_id",
|
|
303
301
|
"received_at_ms",
|
|
304
302
|
"id"
|
|
305
|
-
]),
|
|
306
|
-
index("
|
|
307
|
-
"type",
|
|
308
|
-
"to_release_id",
|
|
309
|
-
"received_at_ms",
|
|
310
|
-
"id"
|
|
311
|
-
]),
|
|
312
|
-
index("bundle_events_from_release_idx", [
|
|
303
|
+
], insightsProviders),
|
|
304
|
+
index("bundle_events_to_bundle_idx", [
|
|
313
305
|
"type",
|
|
314
|
-
"
|
|
306
|
+
"platform",
|
|
307
|
+
"channel",
|
|
308
|
+
"to_bundle_id",
|
|
315
309
|
"received_at_ms",
|
|
316
310
|
"id"
|
|
317
|
-
])
|
|
311
|
+
], insightsProviders)
|
|
318
312
|
],
|
|
319
313
|
checks: [
|
|
320
314
|
check({
|
|
@@ -339,6 +333,52 @@ const bundleEventsV100 = table("bundle_events", {
|
|
|
339
333
|
})
|
|
340
334
|
]
|
|
341
335
|
});
|
|
336
|
+
const bundleInstallationsV100 = table("bundle_installations", {
|
|
337
|
+
install_id: idColumn("install_id", varchar(255)).collate(insightsTextCollations),
|
|
338
|
+
id: uuid("id"),
|
|
339
|
+
user_id: column("user_id", varchar(255)).collate(insightsTextCollations).nullable(),
|
|
340
|
+
username: stringColumn("username").nullable(),
|
|
341
|
+
to_bundle_id: uuid("to_bundle_id"),
|
|
342
|
+
type: column("type", varchar(32)),
|
|
343
|
+
platform: stringColumn("platform").collate(insightsTextCollations),
|
|
344
|
+
app_version: stringColumn("app_version"),
|
|
345
|
+
channel: stringColumn("channel").collate(insightsTextCollations),
|
|
346
|
+
cohort: stringColumn("cohort"),
|
|
347
|
+
received_at_ms: float("received_at_ms")
|
|
348
|
+
}, {
|
|
349
|
+
indexes: [
|
|
350
|
+
index("bundle_installations_user_id_idx", ["user_id", "install_id"]),
|
|
351
|
+
index("bundle_installations_received_at_idx", ["received_at_ms"]),
|
|
352
|
+
index("bundle_installations_scope_idx", [
|
|
353
|
+
"platform",
|
|
354
|
+
"channel",
|
|
355
|
+
"received_at_ms"
|
|
356
|
+
], insightsProviders),
|
|
357
|
+
index("bundle_installations_bundle_idx", [
|
|
358
|
+
"platform",
|
|
359
|
+
"channel",
|
|
360
|
+
"to_bundle_id",
|
|
361
|
+
"received_at_ms"
|
|
362
|
+
], insightsProviders)
|
|
363
|
+
],
|
|
364
|
+
checks: [
|
|
365
|
+
check({
|
|
366
|
+
name: "bundle_installations_type_check",
|
|
367
|
+
expression: "type in ('UPDATE_APPLIED', 'RECOVERED', 'RELEASE_ADOPTED', 'UNCHANGED')",
|
|
368
|
+
sqliteInline: true
|
|
369
|
+
}),
|
|
370
|
+
check({
|
|
371
|
+
name: "bundle_installations_platform_check",
|
|
372
|
+
expression: "platform in ('ios', 'android')",
|
|
373
|
+
sqliteInline: true
|
|
374
|
+
}),
|
|
375
|
+
check({
|
|
376
|
+
name: "bundle_installations_received_at_check",
|
|
377
|
+
expression: "received_at_ms >= 0",
|
|
378
|
+
sqliteInline: true
|
|
379
|
+
})
|
|
380
|
+
]
|
|
381
|
+
});
|
|
342
382
|
const v1_0_0 = schema({
|
|
343
383
|
version: "1.0.0",
|
|
344
384
|
settingsTable: HOT_UPDATER_SETTINGS_TABLE,
|
|
@@ -349,9 +389,10 @@ const v1_0_0 = schema({
|
|
|
349
389
|
releasesV100,
|
|
350
390
|
releaseCatalogsV100,
|
|
351
391
|
bundleEventsV100,
|
|
392
|
+
bundleInstallationsV100,
|
|
352
393
|
apiKeysV100,
|
|
353
394
|
createSettingsTable("1.0.0")
|
|
354
395
|
]
|
|
355
396
|
});
|
|
356
397
|
//#endregion
|
|
357
|
-
export { apiKeysV100, bundleEventsV100, bundlePatchesV100, bundlesV100, channelsV100, releaseCatalogsV100, releasesV100, v1_0_0 };
|
|
398
|
+
export { apiKeysV100, bundleEventsV100, bundleInstallationsV100, bundlePatchesV100, bundlesV100, channelsV100, releaseCatalogsV100, releasesV100, v1_0_0 };
|
package/dist/storageAccess.cjs
CHANGED
package/dist/types/index.d.cts
CHANGED
package/dist/types/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { HotUpdaterAPI } from "../createHotUpdaterCore.mjs";
|
|
2
2
|
import { ChannelRow } from "@hot-updater/plugin-core";
|
|
3
3
|
import { Bundle as Bundle$1, Bundle as Bundle$2 } from "@hot-updater/core";
|
|
4
|
-
|
|
5
4
|
//#region src/types/index.d.ts
|
|
6
5
|
interface PaginationInfo {
|
|
7
6
|
total: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/server",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"rou3": "0.7.9",
|
|
57
|
-
"verkit": "0.
|
|
58
|
-
"@hot-updater/
|
|
59
|
-
"@hot-updater/
|
|
60
|
-
"@hot-updater/
|
|
57
|
+
"verkit": "0.4.0",
|
|
58
|
+
"@hot-updater/core": "1.0.0-rc.0",
|
|
59
|
+
"@hot-updater/plugin-core": "1.0.0-rc.2",
|
|
60
|
+
"@hot-updater/bsdiff": "1.0.0-rc.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"@prisma/client": "*",
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"mongodb": "6.20.0",
|
|
92
92
|
"msw": "^2.7.0",
|
|
93
93
|
"uuidv7": "^1.0.2",
|
|
94
|
-
"@hot-updater/standalone": "1.0.0-rc.
|
|
95
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
94
|
+
"@hot-updater/standalone": "1.0.0-rc.2",
|
|
95
|
+
"@hot-updater/test-utils": "1.0.0-rc.2"
|
|
96
96
|
},
|
|
97
97
|
"scripts": {
|
|
98
98
|
"build": "tsdown",
|
|
@@ -84,6 +84,19 @@ export const DATABASE_PLUGIN_TEST_SCHEMA_SQL = `
|
|
|
84
84
|
sdk_version text,
|
|
85
85
|
received_at_ms integer not null
|
|
86
86
|
);
|
|
87
|
+
create table bundle_installations (
|
|
88
|
+
install_id varchar(255) primary key,
|
|
89
|
+
id text not null,
|
|
90
|
+
user_id text,
|
|
91
|
+
username text,
|
|
92
|
+
to_bundle_id text not null,
|
|
93
|
+
type text not null,
|
|
94
|
+
platform text not null,
|
|
95
|
+
app_version text not null,
|
|
96
|
+
channel text not null,
|
|
97
|
+
cohort text not null,
|
|
98
|
+
received_at_ms integer not null
|
|
99
|
+
);
|
|
87
100
|
create table api_keys (
|
|
88
101
|
id text primary key,
|
|
89
102
|
hash text not null unique,
|
|
@@ -97,6 +110,7 @@ export const DATABASE_PLUGIN_TEST_SCHEMA_SQL = `
|
|
|
97
110
|
|
|
98
111
|
export const DATABASE_PLUGIN_TEST_RESET_SQL = `
|
|
99
112
|
delete from bundle_events;
|
|
113
|
+
delete from bundle_installations;
|
|
100
114
|
delete from api_keys;
|
|
101
115
|
delete from bundle_patches;
|
|
102
116
|
delete from release_catalogs;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { toInsightsInstallationRow } from "@hot-updater/plugin-core";
|
|
2
3
|
import {
|
|
3
4
|
boolean,
|
|
4
5
|
doublePrecision,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
import { drizzle } from "drizzle-orm/pglite";
|
|
12
13
|
import { describe, expect, it, vi } from "vitest";
|
|
13
14
|
|
|
15
|
+
import { createBundleEventRowFixture } from "../../../test-utils/src/databaseTestFixtures";
|
|
14
16
|
import { setupDatabasePluginTestSuite } from "../../../test-utils/src/setupDatabasePluginTestSuite";
|
|
15
17
|
import type { DatabaseAdapterWithCapabilities } from "../db/types";
|
|
16
18
|
import {
|
|
@@ -100,6 +102,19 @@ const bundleEvents = pgTable("bundle_events", {
|
|
|
100
102
|
sdk_version: text("sdk_version"),
|
|
101
103
|
received_at_ms: integer("received_at_ms").notNull(),
|
|
102
104
|
});
|
|
105
|
+
const bundleInstallations = pgTable("bundle_installations", {
|
|
106
|
+
install_id: varchar("install_id", { length: 255 }).primaryKey(),
|
|
107
|
+
id: text("id").notNull(),
|
|
108
|
+
user_id: text("user_id"),
|
|
109
|
+
username: text("username"),
|
|
110
|
+
to_bundle_id: text("to_bundle_id").notNull(),
|
|
111
|
+
type: text("type").notNull(),
|
|
112
|
+
platform: text("platform").notNull(),
|
|
113
|
+
app_version: text("app_version").notNull(),
|
|
114
|
+
channel: text("channel").notNull(),
|
|
115
|
+
cohort: text("cohort").notNull(),
|
|
116
|
+
received_at_ms: integer("received_at_ms").notNull(),
|
|
117
|
+
});
|
|
103
118
|
const apiKeys = pgTable("api_keys", {
|
|
104
119
|
id: text("id").primaryKey(),
|
|
105
120
|
hash: text("hash").notNull().unique(),
|
|
@@ -111,6 +126,7 @@ const apiKeys = pgTable("api_keys", {
|
|
|
111
126
|
});
|
|
112
127
|
const schema = {
|
|
113
128
|
bundle_events: bundleEvents,
|
|
129
|
+
bundle_installations: bundleInstallations,
|
|
114
130
|
bundle_patches: bundlePatches,
|
|
115
131
|
bundles,
|
|
116
132
|
channels,
|
|
@@ -156,6 +172,87 @@ setupDatabasePluginTestSuite({
|
|
|
156
172
|
});
|
|
157
173
|
|
|
158
174
|
describe("drizzleAdapter schema requirements", () => {
|
|
175
|
+
it("keeps Bun SQLite event and snapshot writes inside its synchronous transaction", async () => {
|
|
176
|
+
const { stdout } = await promisify(execFile)(
|
|
177
|
+
new URL("../../../../node_modules/.bin/bun", import.meta.url).pathname,
|
|
178
|
+
[
|
|
179
|
+
"-e",
|
|
180
|
+
`
|
|
181
|
+
import { Database } from "bun:sqlite";
|
|
182
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
183
|
+
import { toInsightsInstallationRow } from "@hot-updater/plugin-core";
|
|
184
|
+
import { drizzleAdapter } from "./src/adapters/drizzle.ts";
|
|
185
|
+
import { createTableSql } from "./src/db/schema/sql.ts";
|
|
186
|
+
import { createBundleEventRowFixture } from "../test-utils/src/databaseTestFixtures.ts";
|
|
187
|
+
import * as schema from "../../examples-server/elysia-drizzle-libsql/hot-updater-schema.ts";
|
|
188
|
+
const db = new Database(":memory:");
|
|
189
|
+
db.exec(createTableSql("sqlite").join(";"));
|
|
190
|
+
db.exec("create trigger reject_snapshot before insert on bundle_installations begin select raise(abort, 'injected snapshot failure'); end;");
|
|
191
|
+
const plugin = drizzleAdapter({ db: drizzle(db, { schema }), provider: "sqlite" });
|
|
192
|
+
const event = createBundleEventRowFixture("708", 100);
|
|
193
|
+
const input = { event, installation: toInsightsInstallationRow(event) };
|
|
194
|
+
let rejected = false;
|
|
195
|
+
try { await plugin.models.insights.record(input); } catch { rejected = true; }
|
|
196
|
+
if (!rejected || db.query("select count(*) as total from bundle_events").get().total !== 0) throw new Error("transaction failed to roll back");
|
|
197
|
+
db.exec("drop trigger reject_snapshot");
|
|
198
|
+
await plugin.models.insights.record(input);
|
|
199
|
+
await plugin.models.insights.record(input);
|
|
200
|
+
if (db.query("select count(*) as total from bundle_events").get().total !== 1 || db.query("select count(*) as total from bundle_installations").get().total !== 1) throw new Error("retry did not commit exactly one report");
|
|
201
|
+
db.close();
|
|
202
|
+
console.log("atomic retry verified");
|
|
203
|
+
`,
|
|
204
|
+
],
|
|
205
|
+
{ cwd: new URL("../..", import.meta.url).pathname },
|
|
206
|
+
);
|
|
207
|
+
expect(stdout).toContain("atomic retry verified");
|
|
208
|
+
});
|
|
209
|
+
it("rolls back the event when the installation write fails and permits retry", async () => {
|
|
210
|
+
const db = new PGlite();
|
|
211
|
+
await db.exec(DATABASE_PLUGIN_TEST_SCHEMA_SQL);
|
|
212
|
+
await db.exec(
|
|
213
|
+
"alter table bundle_installations add constraint reject_snapshot check (install_id <> 'install-701')",
|
|
214
|
+
);
|
|
215
|
+
const plugin = drizzleAdapter({
|
|
216
|
+
db: drizzle(db, { schema }),
|
|
217
|
+
provider: "postgresql",
|
|
218
|
+
});
|
|
219
|
+
const event = createBundleEventRowFixture("701", 100);
|
|
220
|
+
const input = { event, installation: toInsightsInstallationRow(event) };
|
|
221
|
+
try {
|
|
222
|
+
await expect(plugin.models.insights.record(input)).rejects.toThrow();
|
|
223
|
+
expect((await db.query("select id from bundle_events")).rows).toEqual([]);
|
|
224
|
+
await db.exec(
|
|
225
|
+
"alter table bundle_installations drop constraint reject_snapshot",
|
|
226
|
+
);
|
|
227
|
+
await plugin.models.insights.record(input);
|
|
228
|
+
await expect(
|
|
229
|
+
plugin.models.insights.findInstallations({
|
|
230
|
+
installId: event.install_id,
|
|
231
|
+
}),
|
|
232
|
+
).resolves.toEqual([input.installation]);
|
|
233
|
+
} finally {
|
|
234
|
+
await db.close();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("requires transaction support before recording on a lazy database", async () => {
|
|
239
|
+
const getDB = vi.fn(() => {
|
|
240
|
+
throw new DrizzleTestStateError();
|
|
241
|
+
});
|
|
242
|
+
const plugin = drizzleAdapter({
|
|
243
|
+
db: getDB,
|
|
244
|
+
provider: "postgresql",
|
|
245
|
+
schema,
|
|
246
|
+
});
|
|
247
|
+
const event = createBundleEventRowFixture("702", 100);
|
|
248
|
+
await expect(
|
|
249
|
+
plugin.models.insights.record({
|
|
250
|
+
event,
|
|
251
|
+
installation: toInsightsInstallationRow(event),
|
|
252
|
+
}),
|
|
253
|
+
).rejects.toThrow("transaction support");
|
|
254
|
+
expect(getDB).not.toHaveBeenCalled();
|
|
255
|
+
});
|
|
159
256
|
it("does not resolve a lazy database while generating a schema", () => {
|
|
160
257
|
const getDB = vi.fn(() => {
|
|
161
258
|
throw new DrizzleTestStateError();
|
|
@@ -235,3 +332,5 @@ describe("drizzleAdapter schema requirements", () => {
|
|
|
235
332
|
);
|
|
236
333
|
});
|
|
237
334
|
});
|
|
335
|
+
import { execFile } from "node:child_process";
|
|
336
|
+
import { promisify } from "node:util";
|
package/src/adapters/drizzle.ts
CHANGED
|
@@ -15,8 +15,9 @@ import type {
|
|
|
15
15
|
ORMProvider,
|
|
16
16
|
SchemaGenerator,
|
|
17
17
|
} from "../db/types";
|
|
18
|
-
import { createDrizzleCrud } from "./drizzleCrud";
|
|
18
|
+
import { createDrizzleCrud, recordDrizzleInsights } from "./drizzleCrud";
|
|
19
19
|
import { createLazyDB } from "./drizzleLazyDB";
|
|
20
|
+
import { runInsightsTransaction } from "./insightsTransaction";
|
|
20
21
|
|
|
21
22
|
export type DrizzleProvider = Exclude<
|
|
22
23
|
ORMProvider,
|
|
@@ -38,6 +39,18 @@ const createImplementation = (
|
|
|
38
39
|
const transaction = db.transaction?.bind(db);
|
|
39
40
|
return {
|
|
40
41
|
...crud,
|
|
42
|
+
recordInsights: (input) => {
|
|
43
|
+
if (transaction === undefined) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"Drizzle Insights recording requires transaction support.",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return runInsightsTransaction(() =>
|
|
49
|
+
transaction((transactionDatabase) =>
|
|
50
|
+
recordDrizzleInsights(transactionDatabase, config.provider, input),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
},
|
|
41
54
|
deleteChannel: (input) => {
|
|
42
55
|
if (transaction === undefined) {
|
|
43
56
|
throw new Error(
|
|
@@ -116,8 +129,13 @@ export const drizzleAdapter = (
|
|
|
116
129
|
delete: (input) => getAdapter().models.channels.delete(input),
|
|
117
130
|
},
|
|
118
131
|
insights: {
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
record: (input) => getAdapter().models.insights.record(input),
|
|
133
|
+
listEvents: (input) => getAdapter().models.insights.listEvents(input),
|
|
134
|
+
findInstallations: (input) =>
|
|
135
|
+
getAdapter().models.insights.findInstallations(input),
|
|
136
|
+
countInstallations: (input) =>
|
|
137
|
+
getAdapter().models.insights.countInstallations(input),
|
|
138
|
+
countEvents: (input) => getAdapter().models.insights.countEvents(input),
|
|
121
139
|
},
|
|
122
140
|
apiKeys: {
|
|
123
141
|
create: (row) => getAdapter().models.apiKeys.create(row),
|