@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/src/schema/v1_0_0.ts
CHANGED
|
@@ -366,21 +366,37 @@ export const releaseCatalogsV100 = table(
|
|
|
366
366
|
},
|
|
367
367
|
);
|
|
368
368
|
|
|
369
|
+
const insightsTextCollations = {
|
|
370
|
+
postgresql: '"C"',
|
|
371
|
+
mysql: "utf8mb4_0900_bin",
|
|
372
|
+
} as const;
|
|
373
|
+
const insightsProviders = [
|
|
374
|
+
"postgresql",
|
|
375
|
+
"mysql",
|
|
376
|
+
"sqlite",
|
|
377
|
+
"cockroachdb",
|
|
378
|
+
"mongodb",
|
|
379
|
+
] as const;
|
|
380
|
+
|
|
369
381
|
export const bundleEventsV100 = table(
|
|
370
382
|
"bundle_events",
|
|
371
383
|
{
|
|
372
384
|
id: idColumn("id", "uuid"),
|
|
373
|
-
type:
|
|
374
|
-
install_id:
|
|
375
|
-
|
|
385
|
+
type: column("type", varchar(32)),
|
|
386
|
+
install_id: column("install_id", varchar(255)).collate(
|
|
387
|
+
insightsTextCollations,
|
|
388
|
+
),
|
|
389
|
+
user_id: column("user_id", varchar(255))
|
|
390
|
+
.collate(insightsTextCollations)
|
|
391
|
+
.nullable(),
|
|
376
392
|
username: stringColumn("username").nullable(),
|
|
377
393
|
from_release_id: uuid("from_release_id").nullable(),
|
|
378
394
|
from_bundle_id: uuid("from_bundle_id").nullable(),
|
|
379
395
|
to_release_id: uuid("to_release_id").nullable(),
|
|
380
396
|
to_bundle_id: uuid("to_bundle_id"),
|
|
381
|
-
platform: stringColumn("platform"),
|
|
397
|
+
platform: stringColumn("platform").collate(insightsTextCollations),
|
|
382
398
|
app_version: stringColumn("app_version"),
|
|
383
|
-
channel: stringColumn("channel"),
|
|
399
|
+
channel: stringColumn("channel").collate(insightsTextCollations),
|
|
384
400
|
cohort: stringColumn("cohort"),
|
|
385
401
|
update_strategy: stringColumn("update_strategy").nullable(),
|
|
386
402
|
fingerprint_hash: stringColumn("fingerprint_hash").nullable(),
|
|
@@ -392,35 +408,27 @@ export const bundleEventsV100 = table(
|
|
|
392
408
|
index("bundle_events_received_at_idx", ["received_at_ms", "id"]),
|
|
393
409
|
index("bundle_events_install_idx", [
|
|
394
410
|
"install_id",
|
|
395
|
-
"received_at_ms",
|
|
396
|
-
"id",
|
|
397
|
-
]),
|
|
398
|
-
index("bundle_events_user_id_idx", ["user_id", "received_at_ms", "id"]),
|
|
399
|
-
index("bundle_events_username_idx", ["username", "received_at_ms", "id"]),
|
|
400
|
-
index("bundle_events_to_bundle_idx", [
|
|
401
|
-
"type",
|
|
402
|
-
"to_bundle_id",
|
|
403
|
-
"received_at_ms",
|
|
404
|
-
"id",
|
|
405
|
-
]),
|
|
406
|
-
index("bundle_events_from_bundle_idx", [
|
|
407
|
-
"type",
|
|
408
|
-
"from_bundle_id",
|
|
409
|
-
"received_at_ms",
|
|
410
|
-
"id",
|
|
411
|
-
]),
|
|
412
|
-
index("bundle_events_to_release_idx", [
|
|
413
411
|
"type",
|
|
414
|
-
"to_release_id",
|
|
415
|
-
"received_at_ms",
|
|
416
|
-
"id",
|
|
417
|
-
]),
|
|
418
|
-
index("bundle_events_from_release_idx", [
|
|
419
|
-
"type",
|
|
420
|
-
"from_release_id",
|
|
421
412
|
"received_at_ms",
|
|
422
413
|
"id",
|
|
423
414
|
]),
|
|
415
|
+
index(
|
|
416
|
+
"bundle_events_from_bundle_idx",
|
|
417
|
+
[
|
|
418
|
+
"type",
|
|
419
|
+
"platform",
|
|
420
|
+
"channel",
|
|
421
|
+
"from_bundle_id",
|
|
422
|
+
"received_at_ms",
|
|
423
|
+
"id",
|
|
424
|
+
],
|
|
425
|
+
insightsProviders,
|
|
426
|
+
),
|
|
427
|
+
index(
|
|
428
|
+
"bundle_events_to_bundle_idx",
|
|
429
|
+
["type", "platform", "channel", "to_bundle_id", "received_at_ms", "id"],
|
|
430
|
+
insightsProviders,
|
|
431
|
+
),
|
|
424
432
|
],
|
|
425
433
|
checks: [
|
|
426
434
|
check({
|
|
@@ -449,6 +457,61 @@ export const bundleEventsV100 = table(
|
|
|
449
457
|
},
|
|
450
458
|
);
|
|
451
459
|
|
|
460
|
+
export const bundleInstallationsV100 = table(
|
|
461
|
+
"bundle_installations",
|
|
462
|
+
{
|
|
463
|
+
install_id: idColumn("install_id", varchar(255)).collate(
|
|
464
|
+
insightsTextCollations,
|
|
465
|
+
),
|
|
466
|
+
id: uuid("id"),
|
|
467
|
+
user_id: column("user_id", varchar(255))
|
|
468
|
+
.collate(insightsTextCollations)
|
|
469
|
+
.nullable(),
|
|
470
|
+
username: stringColumn("username").nullable(),
|
|
471
|
+
to_bundle_id: uuid("to_bundle_id"),
|
|
472
|
+
type: column("type", varchar(32)),
|
|
473
|
+
platform: stringColumn("platform").collate(insightsTextCollations),
|
|
474
|
+
app_version: stringColumn("app_version"),
|
|
475
|
+
channel: stringColumn("channel").collate(insightsTextCollations),
|
|
476
|
+
cohort: stringColumn("cohort"),
|
|
477
|
+
received_at_ms: float("received_at_ms"),
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
indexes: [
|
|
481
|
+
index("bundle_installations_user_id_idx", ["user_id", "install_id"]),
|
|
482
|
+
index("bundle_installations_received_at_idx", ["received_at_ms"]),
|
|
483
|
+
index(
|
|
484
|
+
"bundle_installations_scope_idx",
|
|
485
|
+
["platform", "channel", "received_at_ms"],
|
|
486
|
+
insightsProviders,
|
|
487
|
+
),
|
|
488
|
+
index(
|
|
489
|
+
"bundle_installations_bundle_idx",
|
|
490
|
+
["platform", "channel", "to_bundle_id", "received_at_ms"],
|
|
491
|
+
insightsProviders,
|
|
492
|
+
),
|
|
493
|
+
],
|
|
494
|
+
checks: [
|
|
495
|
+
check({
|
|
496
|
+
name: "bundle_installations_type_check",
|
|
497
|
+
expression:
|
|
498
|
+
"type in ('UPDATE_APPLIED', 'RECOVERED', 'RELEASE_ADOPTED', 'UNCHANGED')",
|
|
499
|
+
sqliteInline: true,
|
|
500
|
+
}),
|
|
501
|
+
check({
|
|
502
|
+
name: "bundle_installations_platform_check",
|
|
503
|
+
expression: "platform in ('ios', 'android')",
|
|
504
|
+
sqliteInline: true,
|
|
505
|
+
}),
|
|
506
|
+
check({
|
|
507
|
+
name: "bundle_installations_received_at_check",
|
|
508
|
+
expression: "received_at_ms >= 0",
|
|
509
|
+
sqliteInline: true,
|
|
510
|
+
}),
|
|
511
|
+
],
|
|
512
|
+
},
|
|
513
|
+
);
|
|
514
|
+
|
|
452
515
|
export const v1_0_0 = schema({
|
|
453
516
|
version: "1.0.0",
|
|
454
517
|
settingsTable: HOT_UPDATER_SETTINGS_TABLE,
|
|
@@ -459,6 +522,7 @@ export const v1_0_0 = schema({
|
|
|
459
522
|
releasesV100,
|
|
460
523
|
releaseCatalogsV100,
|
|
461
524
|
bundleEventsV100,
|
|
525
|
+
bundleInstallationsV100,
|
|
462
526
|
apiKeysV100,
|
|
463
527
|
createSettingsTable("1.0.0"),
|
|
464
528
|
],
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
//#region src/insights/bounded/activeOverview.ts
|
|
2
|
-
const HOUR_MS = 3600 * 1e3;
|
|
3
|
-
const DAY_MS = 24 * HOUR_MS;
|
|
4
|
-
const ACTIVE_BUNDLE_EVENT_TYPES = [
|
|
5
|
-
"UPDATE_APPLIED",
|
|
6
|
-
"RECOVERED",
|
|
7
|
-
"RELEASE_ADOPTED",
|
|
8
|
-
"UNCHANGED"
|
|
9
|
-
];
|
|
10
|
-
const activeWindowDefinitions = {
|
|
11
|
-
"24h": {
|
|
12
|
-
bucketCount: 24,
|
|
13
|
-
bucketSizeMs: HOUR_MS
|
|
14
|
-
},
|
|
15
|
-
"7d": {
|
|
16
|
-
bucketCount: 7,
|
|
17
|
-
bucketSizeMs: DAY_MS
|
|
18
|
-
},
|
|
19
|
-
"30d": {
|
|
20
|
-
bucketCount: 30,
|
|
21
|
-
bucketSizeMs: DAY_MS
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
function getActiveWindowDefinition(window) {
|
|
25
|
-
return activeWindowDefinitions[window];
|
|
26
|
-
}
|
|
27
|
-
function compareCodePoints(left, right) {
|
|
28
|
-
if (left < right) return -1;
|
|
29
|
-
if (left > right) return 1;
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
function isNewer(candidate, current) {
|
|
33
|
-
return candidate.received_at_ms > current.received_at_ms || candidate.received_at_ms === current.received_at_ms && compareCodePoints(candidate.id, current.id) > 0;
|
|
34
|
-
}
|
|
35
|
-
function collectActiveInstallationOverview(request) {
|
|
36
|
-
const definition = getActiveWindowDefinition(request.window);
|
|
37
|
-
const windowStartMs = request.asOfMs - definition.bucketCount * definition.bucketSizeMs;
|
|
38
|
-
const rows = request.rows.filter((row) => row.received_at_ms >= windowStartMs && row.received_at_ms < request.asOfMs);
|
|
39
|
-
const latestByInstall = /* @__PURE__ */ new Map();
|
|
40
|
-
for (const row of rows) {
|
|
41
|
-
const current = latestByInstall.get(row.install_id);
|
|
42
|
-
if (current === void 0 || isNewer(row, current)) latestByInstall.set(row.install_id, row);
|
|
43
|
-
}
|
|
44
|
-
const selectedRows = [...latestByInstall.values()].filter((row) => request.userId === void 0 || row.user_id === request.userId);
|
|
45
|
-
const selectedInstallIds = new Set(selectedRows.map((row) => row.install_id));
|
|
46
|
-
const bundleCounts = /* @__PURE__ */ new Map();
|
|
47
|
-
for (const row of selectedRows) bundleCounts.set(row.to_bundle_id, (bundleCounts.get(row.to_bundle_id) ?? 0) + 1);
|
|
48
|
-
const latestByBucket = Array.from({ length: definition.bucketCount }, () => /* @__PURE__ */ new Map());
|
|
49
|
-
for (const row of rows) {
|
|
50
|
-
if (!selectedInstallIds.has(row.install_id)) continue;
|
|
51
|
-
const bucket = latestByBucket[Math.floor((row.received_at_ms - windowStartMs) / definition.bucketSizeMs)];
|
|
52
|
-
if (bucket === void 0) continue;
|
|
53
|
-
const current = bucket.get(row.install_id);
|
|
54
|
-
if (current === void 0 || isNewer(row, current)) bucket.set(row.install_id, row);
|
|
55
|
-
}
|
|
56
|
-
const bundleCountsByBucket = latestByBucket.map((bucket) => {
|
|
57
|
-
const counts = /* @__PURE__ */ new Map();
|
|
58
|
-
for (const row of bucket.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
|
|
59
|
-
return counts;
|
|
60
|
-
});
|
|
61
|
-
const bundleObservationTotals = /* @__PURE__ */ new Map();
|
|
62
|
-
for (const counts of bundleCountsByBucket) for (const [bundleId, count] of counts) bundleObservationTotals.set(bundleId, (bundleObservationTotals.get(bundleId) ?? 0) + count);
|
|
63
|
-
return {
|
|
64
|
-
asOfMs: request.asOfMs,
|
|
65
|
-
window: request.window,
|
|
66
|
-
activeInstallations: selectedInstallIds.size,
|
|
67
|
-
series: latestByBucket.map((bucket, index) => ({
|
|
68
|
-
bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
|
|
69
|
-
value: bucket.size
|
|
70
|
-
})),
|
|
71
|
-
bundleSeries: [...bundleObservationTotals].sort(([leftId, leftTotal], [rightId, rightTotal]) => rightTotal - leftTotal || compareCodePoints(leftId, rightId)).map(([bundleId]) => ({
|
|
72
|
-
bundleId,
|
|
73
|
-
series: bundleCountsByBucket.map((counts, index) => ({
|
|
74
|
-
bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
|
|
75
|
-
value: counts.get(bundleId) ?? 0
|
|
76
|
-
}))
|
|
77
|
-
})),
|
|
78
|
-
bundles: [...bundleCounts].map(([bundleId, installations]) => ({
|
|
79
|
-
bundleId,
|
|
80
|
-
installations
|
|
81
|
-
})).sort((left, right) => right.installations - left.installations || compareCodePoints(left.bundleId, right.bundleId))
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
//#endregion
|
|
85
|
-
exports.ACTIVE_BUNDLE_EVENT_TYPES = ACTIVE_BUNDLE_EVENT_TYPES;
|
|
86
|
-
exports.collectActiveInstallationOverview = collectActiveInstallationOverview;
|
|
87
|
-
exports.getActiveWindowDefinition = getActiveWindowDefinition;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
//#region src/insights/bounded/activeOverview.ts
|
|
2
|
-
const HOUR_MS = 3600 * 1e3;
|
|
3
|
-
const DAY_MS = 24 * HOUR_MS;
|
|
4
|
-
const ACTIVE_BUNDLE_EVENT_TYPES = [
|
|
5
|
-
"UPDATE_APPLIED",
|
|
6
|
-
"RECOVERED",
|
|
7
|
-
"RELEASE_ADOPTED",
|
|
8
|
-
"UNCHANGED"
|
|
9
|
-
];
|
|
10
|
-
const activeWindowDefinitions = {
|
|
11
|
-
"24h": {
|
|
12
|
-
bucketCount: 24,
|
|
13
|
-
bucketSizeMs: HOUR_MS
|
|
14
|
-
},
|
|
15
|
-
"7d": {
|
|
16
|
-
bucketCount: 7,
|
|
17
|
-
bucketSizeMs: DAY_MS
|
|
18
|
-
},
|
|
19
|
-
"30d": {
|
|
20
|
-
bucketCount: 30,
|
|
21
|
-
bucketSizeMs: DAY_MS
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
function getActiveWindowDefinition(window) {
|
|
25
|
-
return activeWindowDefinitions[window];
|
|
26
|
-
}
|
|
27
|
-
function compareCodePoints(left, right) {
|
|
28
|
-
if (left < right) return -1;
|
|
29
|
-
if (left > right) return 1;
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
function isNewer(candidate, current) {
|
|
33
|
-
return candidate.received_at_ms > current.received_at_ms || candidate.received_at_ms === current.received_at_ms && compareCodePoints(candidate.id, current.id) > 0;
|
|
34
|
-
}
|
|
35
|
-
function collectActiveInstallationOverview(request) {
|
|
36
|
-
const definition = getActiveWindowDefinition(request.window);
|
|
37
|
-
const windowStartMs = request.asOfMs - definition.bucketCount * definition.bucketSizeMs;
|
|
38
|
-
const rows = request.rows.filter((row) => row.received_at_ms >= windowStartMs && row.received_at_ms < request.asOfMs);
|
|
39
|
-
const latestByInstall = /* @__PURE__ */ new Map();
|
|
40
|
-
for (const row of rows) {
|
|
41
|
-
const current = latestByInstall.get(row.install_id);
|
|
42
|
-
if (current === void 0 || isNewer(row, current)) latestByInstall.set(row.install_id, row);
|
|
43
|
-
}
|
|
44
|
-
const selectedRows = [...latestByInstall.values()].filter((row) => request.userId === void 0 || row.user_id === request.userId);
|
|
45
|
-
const selectedInstallIds = new Set(selectedRows.map((row) => row.install_id));
|
|
46
|
-
const bundleCounts = /* @__PURE__ */ new Map();
|
|
47
|
-
for (const row of selectedRows) bundleCounts.set(row.to_bundle_id, (bundleCounts.get(row.to_bundle_id) ?? 0) + 1);
|
|
48
|
-
const latestByBucket = Array.from({ length: definition.bucketCount }, () => /* @__PURE__ */ new Map());
|
|
49
|
-
for (const row of rows) {
|
|
50
|
-
if (!selectedInstallIds.has(row.install_id)) continue;
|
|
51
|
-
const bucket = latestByBucket[Math.floor((row.received_at_ms - windowStartMs) / definition.bucketSizeMs)];
|
|
52
|
-
if (bucket === void 0) continue;
|
|
53
|
-
const current = bucket.get(row.install_id);
|
|
54
|
-
if (current === void 0 || isNewer(row, current)) bucket.set(row.install_id, row);
|
|
55
|
-
}
|
|
56
|
-
const bundleCountsByBucket = latestByBucket.map((bucket) => {
|
|
57
|
-
const counts = /* @__PURE__ */ new Map();
|
|
58
|
-
for (const row of bucket.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
|
|
59
|
-
return counts;
|
|
60
|
-
});
|
|
61
|
-
const bundleObservationTotals = /* @__PURE__ */ new Map();
|
|
62
|
-
for (const counts of bundleCountsByBucket) for (const [bundleId, count] of counts) bundleObservationTotals.set(bundleId, (bundleObservationTotals.get(bundleId) ?? 0) + count);
|
|
63
|
-
return {
|
|
64
|
-
asOfMs: request.asOfMs,
|
|
65
|
-
window: request.window,
|
|
66
|
-
activeInstallations: selectedInstallIds.size,
|
|
67
|
-
series: latestByBucket.map((bucket, index) => ({
|
|
68
|
-
bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
|
|
69
|
-
value: bucket.size
|
|
70
|
-
})),
|
|
71
|
-
bundleSeries: [...bundleObservationTotals].sort(([leftId, leftTotal], [rightId, rightTotal]) => rightTotal - leftTotal || compareCodePoints(leftId, rightId)).map(([bundleId]) => ({
|
|
72
|
-
bundleId,
|
|
73
|
-
series: bundleCountsByBucket.map((counts, index) => ({
|
|
74
|
-
bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
|
|
75
|
-
value: counts.get(bundleId) ?? 0
|
|
76
|
-
}))
|
|
77
|
-
})),
|
|
78
|
-
bundles: [...bundleCounts].map(([bundleId, installations]) => ({
|
|
79
|
-
bundleId,
|
|
80
|
-
installations
|
|
81
|
-
})).sort((left, right) => right.installations - left.installations || compareCodePoints(left.bundleId, right.bundleId))
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
//#endregion
|
|
85
|
-
export { ACTIVE_BUNDLE_EVENT_TYPES, collectActiveInstallationOverview, getActiveWindowDefinition };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const require_scan = require("./scan.cjs");
|
|
2
|
-
//#region src/insights/bounded/installationSearch.ts
|
|
3
|
-
function toSearchRow(row) {
|
|
4
|
-
return {
|
|
5
|
-
installId: row.install_id,
|
|
6
|
-
username: row.username,
|
|
7
|
-
userId: row.user_id,
|
|
8
|
-
lastKnownBundleId: row.to_bundle_id,
|
|
9
|
-
latestStatus: row.type,
|
|
10
|
-
platform: row.platform,
|
|
11
|
-
appVersion: row.app_version,
|
|
12
|
-
channel: row.channel,
|
|
13
|
-
cohort: row.cohort,
|
|
14
|
-
receivedAtMs: row.received_at_ms
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function matchesIdentity(row, query) {
|
|
18
|
-
return row.install_id.toLowerCase().includes(query) || row.user_id?.toLowerCase().includes(query) === true || row.username?.toLowerCase().includes(query) === true;
|
|
19
|
-
}
|
|
20
|
-
function searchEventInstallations(request) {
|
|
21
|
-
const query = request.query.toLowerCase();
|
|
22
|
-
const matchingInstallIds = /* @__PURE__ */ new Set();
|
|
23
|
-
for (const row of request.rows) if (query.length === 0 || matchesIdentity(row, query)) matchingInstallIds.add(row.install_id);
|
|
24
|
-
const latestByInstall = /* @__PURE__ */ new Map();
|
|
25
|
-
for (const row of request.rows) {
|
|
26
|
-
if (!matchingInstallIds.has(row.install_id)) continue;
|
|
27
|
-
const current = latestByInstall.get(row.install_id);
|
|
28
|
-
if (current === void 0 || require_scan.compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
|
|
29
|
-
}
|
|
30
|
-
const matchingRows = [...latestByInstall.values()].sort((left, right) => require_scan.compareCodePoints(left.install_id, right.install_id));
|
|
31
|
-
const pageSize = Math.min(Math.max(request.limit, 0), 100);
|
|
32
|
-
return {
|
|
33
|
-
data: matchingRows.slice(request.offset, request.offset + pageSize).map(toSearchRow),
|
|
34
|
-
pagination: {
|
|
35
|
-
total: matchingRows.length,
|
|
36
|
-
limit: request.limit,
|
|
37
|
-
offset: request.offset
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
//#endregion
|
|
42
|
-
exports.searchEventInstallations = searchEventInstallations;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { compareCodePoints, compareEventNewest } from "./scan.mjs";
|
|
2
|
-
//#region src/insights/bounded/installationSearch.ts
|
|
3
|
-
function toSearchRow(row) {
|
|
4
|
-
return {
|
|
5
|
-
installId: row.install_id,
|
|
6
|
-
username: row.username,
|
|
7
|
-
userId: row.user_id,
|
|
8
|
-
lastKnownBundleId: row.to_bundle_id,
|
|
9
|
-
latestStatus: row.type,
|
|
10
|
-
platform: row.platform,
|
|
11
|
-
appVersion: row.app_version,
|
|
12
|
-
channel: row.channel,
|
|
13
|
-
cohort: row.cohort,
|
|
14
|
-
receivedAtMs: row.received_at_ms
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function matchesIdentity(row, query) {
|
|
18
|
-
return row.install_id.toLowerCase().includes(query) || row.user_id?.toLowerCase().includes(query) === true || row.username?.toLowerCase().includes(query) === true;
|
|
19
|
-
}
|
|
20
|
-
function searchEventInstallations(request) {
|
|
21
|
-
const query = request.query.toLowerCase();
|
|
22
|
-
const matchingInstallIds = /* @__PURE__ */ new Set();
|
|
23
|
-
for (const row of request.rows) if (query.length === 0 || matchesIdentity(row, query)) matchingInstallIds.add(row.install_id);
|
|
24
|
-
const latestByInstall = /* @__PURE__ */ new Map();
|
|
25
|
-
for (const row of request.rows) {
|
|
26
|
-
if (!matchingInstallIds.has(row.install_id)) continue;
|
|
27
|
-
const current = latestByInstall.get(row.install_id);
|
|
28
|
-
if (current === void 0 || compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
|
|
29
|
-
}
|
|
30
|
-
const matchingRows = [...latestByInstall.values()].sort((left, right) => compareCodePoints(left.install_id, right.install_id));
|
|
31
|
-
const pageSize = Math.min(Math.max(request.limit, 0), 100);
|
|
32
|
-
return {
|
|
33
|
-
data: matchingRows.slice(request.offset, request.offset + pageSize).map(toSearchRow),
|
|
34
|
-
pagination: {
|
|
35
|
-
total: matchingRows.length,
|
|
36
|
-
limit: request.limit,
|
|
37
|
-
offset: request.offset
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
//#endregion
|
|
42
|
-
export { searchEventInstallations };
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
3
|
-
//#region src/insights/bounded/persistence.ts
|
|
4
|
-
function createBundleEventRow(input) {
|
|
5
|
-
const base = {
|
|
6
|
-
id: (0, _hot_updater_plugin_core.createUUIDv7)(),
|
|
7
|
-
install_id: input.installId,
|
|
8
|
-
user_id: input.userId ?? null,
|
|
9
|
-
username: input.username ?? null,
|
|
10
|
-
from_release_id: input.fromReleaseId,
|
|
11
|
-
to_release_id: input.toReleaseId,
|
|
12
|
-
to_bundle_id: input.toBundleId,
|
|
13
|
-
platform: input.platform,
|
|
14
|
-
app_version: input.appVersion,
|
|
15
|
-
channel: input.channel,
|
|
16
|
-
cohort: input.cohort,
|
|
17
|
-
fingerprint_hash: input.fingerprintHash,
|
|
18
|
-
sdk_version: input.sdkVersion ?? null,
|
|
19
|
-
received_at_ms: Date.now()
|
|
20
|
-
};
|
|
21
|
-
switch (input.type) {
|
|
22
|
-
case "UPDATE_APPLIED":
|
|
23
|
-
case "RECOVERED":
|
|
24
|
-
case "RELEASE_ADOPTED": return {
|
|
25
|
-
...base,
|
|
26
|
-
type: input.type,
|
|
27
|
-
from_bundle_id: input.fromBundleId,
|
|
28
|
-
update_strategy: input.updateStrategy
|
|
29
|
-
};
|
|
30
|
-
case "UNCHANGED": return {
|
|
31
|
-
...base,
|
|
32
|
-
type: input.type,
|
|
33
|
-
from_bundle_id: null,
|
|
34
|
-
update_strategy: null
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
exports.createBundleEventRow = createBundleEventRow;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { createUUIDv7 } from "@hot-updater/plugin-core";
|
|
2
|
-
//#region src/insights/bounded/persistence.ts
|
|
3
|
-
function createBundleEventRow(input) {
|
|
4
|
-
const base = {
|
|
5
|
-
id: createUUIDv7(),
|
|
6
|
-
install_id: input.installId,
|
|
7
|
-
user_id: input.userId ?? null,
|
|
8
|
-
username: input.username ?? null,
|
|
9
|
-
from_release_id: input.fromReleaseId,
|
|
10
|
-
to_release_id: input.toReleaseId,
|
|
11
|
-
to_bundle_id: input.toBundleId,
|
|
12
|
-
platform: input.platform,
|
|
13
|
-
app_version: input.appVersion,
|
|
14
|
-
channel: input.channel,
|
|
15
|
-
cohort: input.cohort,
|
|
16
|
-
fingerprint_hash: input.fingerprintHash,
|
|
17
|
-
sdk_version: input.sdkVersion ?? null,
|
|
18
|
-
received_at_ms: Date.now()
|
|
19
|
-
};
|
|
20
|
-
switch (input.type) {
|
|
21
|
-
case "UPDATE_APPLIED":
|
|
22
|
-
case "RECOVERED":
|
|
23
|
-
case "RELEASE_ADOPTED": return {
|
|
24
|
-
...base,
|
|
25
|
-
type: input.type,
|
|
26
|
-
from_bundle_id: input.fromBundleId,
|
|
27
|
-
update_strategy: input.updateStrategy
|
|
28
|
-
};
|
|
29
|
-
case "UNCHANGED": return {
|
|
30
|
-
...base,
|
|
31
|
-
type: input.type,
|
|
32
|
-
from_bundle_id: null,
|
|
33
|
-
update_strategy: null
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
|
-
export { createBundleEventRow };
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
const require_activeOverview = require("./activeOverview.cjs");
|
|
2
|
-
const require_scan = require("./scan.cjs");
|
|
3
|
-
const require_installationSearch = require("./installationSearch.cjs");
|
|
4
|
-
const require_persistence = require("./persistence.cjs");
|
|
5
|
-
//#region src/insights/bounded/provider.ts
|
|
6
|
-
const isTransitionEventRow = (row) => row.type === "UPDATE_APPLIED" || row.type === "RECOVERED";
|
|
7
|
-
const toHistoryRow = (row) => ({
|
|
8
|
-
id: row.id,
|
|
9
|
-
type: row.type,
|
|
10
|
-
fromBundleId: row.from_bundle_id,
|
|
11
|
-
toBundleId: row.to_bundle_id,
|
|
12
|
-
username: row.username,
|
|
13
|
-
userId: row.user_id,
|
|
14
|
-
platform: row.platform,
|
|
15
|
-
appVersion: row.app_version,
|
|
16
|
-
channel: row.channel,
|
|
17
|
-
cohort: row.cohort,
|
|
18
|
-
receivedAtMs: row.received_at_ms
|
|
19
|
-
});
|
|
20
|
-
const isInstalledForBundle = (row, bundleId) => row.type === "UPDATE_APPLIED" && row.to_bundle_id === bundleId;
|
|
21
|
-
const isRecoveredFromBundle = (row, bundleId) => row.type === "RECOVERED" && row.from_bundle_id === bundleId;
|
|
22
|
-
const countDistinctInstallations = (rows) => new Set(rows.map(({ install_id }) => install_id)).size;
|
|
23
|
-
const getInsightsResult = async (persistence, bundleId, window, limit, offset) => {
|
|
24
|
-
const scope = {
|
|
25
|
-
persistence,
|
|
26
|
-
cutoffMs: Date.now()
|
|
27
|
-
};
|
|
28
|
-
const rows = (await require_scan.materializeRowsForWindow(scope, window)).filter(isTransitionEventRow);
|
|
29
|
-
const installedRows = rows.filter((row) => isInstalledForBundle(row, bundleId));
|
|
30
|
-
const recoveredRows = rows.filter((row) => isRecoveredFromBundle(row, bundleId));
|
|
31
|
-
const installed = require_scan.collectEventActivity({
|
|
32
|
-
rows: installedRows,
|
|
33
|
-
window,
|
|
34
|
-
cutoffMs: scope.cutoffMs
|
|
35
|
-
});
|
|
36
|
-
const recovered = require_scan.collectEventActivity({
|
|
37
|
-
rows: recoveredRows,
|
|
38
|
-
window,
|
|
39
|
-
cutoffMs: scope.cutoffMs
|
|
40
|
-
});
|
|
41
|
-
const recentRows = [...installedRows, ...recoveredRows].sort(require_scan.compareEventNewest);
|
|
42
|
-
return {
|
|
43
|
-
summary: {
|
|
44
|
-
installed: installed.summary,
|
|
45
|
-
recovered: recovered.summary
|
|
46
|
-
},
|
|
47
|
-
series: {
|
|
48
|
-
installed: installed.series,
|
|
49
|
-
recovered: recovered.series
|
|
50
|
-
},
|
|
51
|
-
cohorts: {
|
|
52
|
-
installed: installed.cohorts,
|
|
53
|
-
recovered: recovered.cohorts
|
|
54
|
-
},
|
|
55
|
-
recentEvents: {
|
|
56
|
-
data: recentRows.slice(offset, offset + limit).map(toHistoryRow),
|
|
57
|
-
pagination: {
|
|
58
|
-
total: recentRows.length,
|
|
59
|
-
limit,
|
|
60
|
-
offset
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
const getInsightsSummaries = async (persistence, bundleIds, window) => {
|
|
66
|
-
const normalizedBundleIds = [...new Set(bundleIds)];
|
|
67
|
-
if (normalizedBundleIds.length === 0) return [];
|
|
68
|
-
const requestedBundleIds = new Set(normalizedBundleIds);
|
|
69
|
-
const installedByBundleId = /* @__PURE__ */ new Map();
|
|
70
|
-
const recoveredByBundleId = /* @__PURE__ */ new Map();
|
|
71
|
-
const rows = (await require_scan.materializeRowsForWindow({
|
|
72
|
-
persistence,
|
|
73
|
-
cutoffMs: Date.now()
|
|
74
|
-
}, window)).filter(isTransitionEventRow);
|
|
75
|
-
for (const row of rows) {
|
|
76
|
-
const bundleId = row.type === "UPDATE_APPLIED" ? row.to_bundle_id : row.from_bundle_id;
|
|
77
|
-
if (!requestedBundleIds.has(bundleId)) continue;
|
|
78
|
-
const counts = row.type === "UPDATE_APPLIED" ? installedByBundleId : recoveredByBundleId;
|
|
79
|
-
const installIds = counts.get(bundleId) ?? /* @__PURE__ */ new Set();
|
|
80
|
-
installIds.add(row.install_id);
|
|
81
|
-
counts.set(bundleId, installIds);
|
|
82
|
-
}
|
|
83
|
-
return normalizedBundleIds.map((bundleId) => ({
|
|
84
|
-
bundleId,
|
|
85
|
-
installed: installedByBundleId.get(bundleId)?.size ?? 0,
|
|
86
|
-
recovered: recoveredByBundleId.get(bundleId)?.size ?? 0
|
|
87
|
-
}));
|
|
88
|
-
};
|
|
89
|
-
const createInsightsProvider = (persistence) => Object.freeze({
|
|
90
|
-
mode: "bounded",
|
|
91
|
-
maxMatchingRows: require_scan.INSIGHTS_SCAN_MAX_ROWS,
|
|
92
|
-
async appendBundleEvent(input) {
|
|
93
|
-
await persistence.append(require_persistence.createBundleEventRow(input));
|
|
94
|
-
},
|
|
95
|
-
async getBundleEventSummary(bundleId) {
|
|
96
|
-
const rows = (await require_scan.materializeEventRows({
|
|
97
|
-
persistence,
|
|
98
|
-
cutoffMs: Date.now()
|
|
99
|
-
})).filter(isTransitionEventRow);
|
|
100
|
-
return {
|
|
101
|
-
installed: countDistinctInstallations(rows.filter((row) => isInstalledForBundle(row, bundleId))),
|
|
102
|
-
recovered: countDistinctInstallations(rows.filter((row) => isRecoveredFromBundle(row, bundleId)))
|
|
103
|
-
};
|
|
104
|
-
},
|
|
105
|
-
getBundleEventSummaries(bundleIds, window) {
|
|
106
|
-
return getInsightsSummaries(persistence, bundleIds, window);
|
|
107
|
-
},
|
|
108
|
-
getBundleEventInsights(bundleId, window, limit, offset) {
|
|
109
|
-
return getInsightsResult(persistence, bundleId, window, limit, offset);
|
|
110
|
-
},
|
|
111
|
-
async getBundleEventOverview() {
|
|
112
|
-
const rows = await require_scan.materializeEventRows({
|
|
113
|
-
persistence,
|
|
114
|
-
cutoffMs: Date.now()
|
|
115
|
-
});
|
|
116
|
-
const latestByInstall = /* @__PURE__ */ new Map();
|
|
117
|
-
for (const row of rows) {
|
|
118
|
-
const current = latestByInstall.get(row.install_id);
|
|
119
|
-
if (current === void 0 || require_scan.compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
|
|
120
|
-
}
|
|
121
|
-
const counts = /* @__PURE__ */ new Map();
|
|
122
|
-
for (const row of latestByInstall.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
|
|
123
|
-
return {
|
|
124
|
-
trackedInstallations: latestByInstall.size,
|
|
125
|
-
bundles: [...counts].map(([bundleId, installations]) => ({
|
|
126
|
-
bundleId,
|
|
127
|
-
installations
|
|
128
|
-
})).sort((left, right) => right.installations - left.installations || require_scan.compareCodePoints(left.bundleId, right.bundleId))
|
|
129
|
-
};
|
|
130
|
-
},
|
|
131
|
-
async getActiveInstallationOverview(input) {
|
|
132
|
-
const asOfMs = Date.now();
|
|
133
|
-
return require_activeOverview.collectActiveInstallationOverview({
|
|
134
|
-
rows: await require_scan.materializeActiveRows({
|
|
135
|
-
persistence,
|
|
136
|
-
cutoffMs: asOfMs
|
|
137
|
-
}, input.window),
|
|
138
|
-
asOfMs,
|
|
139
|
-
window: input.window,
|
|
140
|
-
...input.userId === void 0 ? {} : { userId: input.userId }
|
|
141
|
-
});
|
|
142
|
-
},
|
|
143
|
-
async searchInstallations(query, limit, offset) {
|
|
144
|
-
return require_installationSearch.searchEventInstallations({
|
|
145
|
-
rows: await require_scan.materializeEventRows({
|
|
146
|
-
persistence,
|
|
147
|
-
cutoffMs: Date.now()
|
|
148
|
-
}),
|
|
149
|
-
query,
|
|
150
|
-
limit,
|
|
151
|
-
offset
|
|
152
|
-
});
|
|
153
|
-
},
|
|
154
|
-
async getInstallationHistory(installId, limit, offset) {
|
|
155
|
-
const ordered = (await require_scan.materializeEventRows({
|
|
156
|
-
persistence,
|
|
157
|
-
cutoffMs: Date.now()
|
|
158
|
-
})).filter((row) => row.install_id === installId && isTransitionEventRow(row)).toSorted(require_scan.compareEventNewest);
|
|
159
|
-
return {
|
|
160
|
-
data: ordered.slice(offset, offset + limit).map(toHistoryRow),
|
|
161
|
-
pagination: {
|
|
162
|
-
total: ordered.length,
|
|
163
|
-
limit,
|
|
164
|
-
offset
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
//#endregion
|
|
170
|
-
exports.createInsightsProvider = createInsightsProvider;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { InsightsProvider } from "../types.cjs";
|
|
2
|
-
import { InsightsPersistence } from "../persistence.cjs";
|
|
3
|
-
|
|
4
|
-
//#region src/insights/bounded/provider.d.ts
|
|
5
|
-
declare const createInsightsProvider: (persistence: InsightsPersistence) => InsightsProvider;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { createInsightsProvider };
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { InsightsProvider } from "../types.mjs";
|
|
2
|
-
import { InsightsPersistence } from "../persistence.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/insights/bounded/provider.d.ts
|
|
5
|
-
declare const createInsightsProvider: (persistence: InsightsPersistence) => InsightsProvider;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { createInsightsProvider };
|