@hot-updater/server 1.0.0-rc.0 → 1.0.0-rc.14
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 +14 -1
- package/dist/adapters/databasePluginUtils.mjs +14 -2
- package/dist/adapters/drizzle.cjs +9 -4
- package/dist/adapters/drizzle.d.cts +0 -1
- package/dist/adapters/drizzle.d.mts +0 -1
- package/dist/adapters/drizzle.mjs +10 -4
- package/dist/adapters/drizzleCrud.cjs +93 -3
- package/dist/adapters/drizzleCrud.mjs +93 -5
- package/dist/adapters/drizzleLazyDB.cjs +2 -0
- package/dist/adapters/drizzleLazyDB.mjs +2 -0
- package/dist/adapters/drizzleQuery.cjs +3 -2
- package/dist/adapters/drizzleQuery.mjs +3 -1
- package/dist/adapters/kysely.cjs +1 -1
- package/dist/adapters/kysely.d.cts +0 -1
- package/dist/adapters/kysely.d.mts +0 -1
- package/dist/adapters/kysely.mjs +2 -1
- package/dist/adapters/kyselyCrud.cjs +44 -5
- package/dist/adapters/kyselyCrud.mjs +45 -6
- package/dist/adapters/kyselyQuery.cjs +2 -2
- package/dist/adapters/kyselyQuery.mjs +2 -1
- package/dist/adapters/mongodb.cjs +36 -1
- package/dist/adapters/mongodb.d.cts +1 -1
- package/dist/adapters/mongodb.d.mts +1 -1
- package/dist/adapters/mongodb.mjs +36 -0
- package/dist/adapters/mongodbCollections.cjs +2 -0
- package/dist/adapters/mongodbCollections.mjs +2 -0
- package/dist/adapters/mongodbQuery.cjs +17 -4
- package/dist/adapters/mongodbQuery.mjs +17 -4
- package/dist/adapters/mongodbReads.cjs +88 -30
- package/dist/adapters/mongodbReads.mjs +88 -29
- package/dist/adapters/mongodbWrites.cjs +1 -2
- package/dist/adapters/mongodbWrites.mjs +1 -1
- package/dist/adapters/prisma.cjs +44 -7
- package/dist/adapters/prisma.d.cts +0 -1
- package/dist/adapters/prisma.d.mts +0 -1
- package/dist/adapters/prisma.mjs +44 -6
- package/dist/adapters/prismaInsights.cjs +99 -0
- package/dist/adapters/prismaInsights.mjs +97 -0
- package/dist/adapters/prismaQuery.cjs +0 -1
- package/dist/adapters/prismaRows.cjs +7 -8
- package/dist/adapters/prismaRows.mjs +8 -8
- 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 +27 -12
- package/dist/createHotUpdaterCore.d.cts +8 -8
- package/dist/createHotUpdaterCore.d.mts +8 -8
- package/dist/createHotUpdaterCore.mjs +26 -12
- 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 +29 -9
- package/dist/db/schemaGenerators.mjs +29 -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.cjs +5 -5
- package/dist/handler.d.cts +1 -2
- package/dist/handler.d.mts +1 -2
- package/dist/handler.mjs +5 -5
- 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 +2 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/insights/domain.d.cts +92 -0
- package/dist/insights/domain.d.mts +92 -0
- package/dist/insights/errors.cjs +15 -0
- package/dist/insights/errors.mjs +14 -0
- package/dist/{analytics → insights}/eventInput.cjs +61 -16
- package/dist/{analytics → insights}/eventInput.mjs +61 -18
- package/dist/insights/provider.cjs +332 -0
- package/dist/insights/provider.d.cts +6 -0
- package/dist/insights/provider.d.mts +6 -0
- package/dist/insights/provider.mjs +332 -0
- package/dist/insights/queryInput.cjs +93 -0
- package/dist/insights/queryInput.mjs +91 -0
- package/dist/insights/routes.cjs +58 -0
- package/dist/insights/routes.mjs +56 -0
- package/dist/insights/types.d.cts +32 -0
- package/dist/insights/types.d.mts +32 -0
- 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 +64 -35
- package/dist/schema/v1_0_0.mjs +64 -36
- 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 +15 -5
- package/src/adapters/databasePluginUtils.ts +24 -0
- package/src/adapters/drizzle.spec.ts +203 -5
- package/src/adapters/drizzle.ts +16 -4
- package/src/adapters/drizzleCrud.ts +200 -9
- package/src/adapters/drizzleLazyDB.ts +33 -6
- package/src/adapters/kysely.spec.ts +62 -0
- package/src/adapters/kysely.ts +7 -1
- package/src/adapters/kyselyCrud.ts +115 -7
- package/src/adapters/kyselyInsights.spec.ts +168 -0
- package/src/adapters/mongodb.integration.spec.ts +483 -0
- package/src/adapters/mongodb.spec.ts +89 -0
- package/src/adapters/mongodb.ts +59 -1
- package/src/adapters/mongodbCollections.ts +16 -0
- package/src/adapters/mongodbQuery.ts +22 -3
- package/src/adapters/mongodbReads.ts +78 -2
- package/src/adapters/mongodbTestClient.ts +107 -6
- package/src/adapters/mongodbTestFilter.ts +5 -1
- package/src/adapters/mongodbWrites.ts +1 -0
- package/src/adapters/prisma.spec.ts +270 -0
- package/src/adapters/prisma.ts +75 -2
- package/src/adapters/prismaInsights.ts +190 -0
- package/src/adapters/prismaRows.ts +17 -10
- package/src/adapters/prismaTestClient.ts +89 -7
- package/src/apiKeys.integration.spec.ts +3 -3
- package/src/createHotUpdaterCore.ts +30 -16
- package/src/db/databasePluginUpdateCheck.bench.ts +10 -0
- package/src/db/fixedMigrator.spec.ts +63 -0
- package/src/db/fixedMigratorMongo.spec.ts +70 -9
- package/src/db/fixedMigratorMongo.ts +10 -10
- package/src/db/index.spec.ts +6 -1
- package/src/db/schema/insightsCollation.ts +21 -0
- package/src/db/schema/sql.ts +8 -5
- package/src/db/schemaGenerators.ts +42 -13
- package/src/db/types.spec.ts +11 -3
- package/src/db/types.ts +13 -7
- package/src/handler-standalone.integration.spec.ts +6 -8
- package/src/handler.ts +12 -12
- package/src/index.ts +3 -3
- package/src/insights/domain.ts +106 -0
- package/src/insights/errors.ts +11 -0
- package/src/{analytics → insights}/eventInput.ts +66 -16
- package/src/insights/insights.integration.spec.ts +475 -0
- package/src/insights/provider.spec.ts +396 -0
- package/src/insights/provider.ts +611 -0
- package/src/insights/queryInput.ts +142 -0
- package/src/insights/routes.ts +104 -0
- package/src/insights/types.ts +55 -0
- package/src/schema/dsl-all-versions.spec.ts +1 -1
- package/src/schema/v1_0_0.spec.ts +77 -3
- package/src/schema/v1_0_0.ts +83 -37
- package/dist/analytics/bounded/activeOverview.cjs +0 -87
- package/dist/analytics/bounded/activeOverview.mjs +0 -85
- package/dist/analytics/bounded/installationSearch.cjs +0 -42
- package/dist/analytics/bounded/installationSearch.mjs +0 -42
- package/dist/analytics/bounded/persistence.cjs +0 -39
- package/dist/analytics/bounded/persistence.mjs +0 -38
- package/dist/analytics/bounded/provider.cjs +0 -170
- package/dist/analytics/bounded/provider.d.cts +0 -7
- package/dist/analytics/bounded/provider.d.mts +0 -7
- package/dist/analytics/bounded/provider.mjs +0 -170
- package/dist/analytics/bounded/scan.cjs +0 -144
- package/dist/analytics/bounded/scan.mjs +0 -138
- package/dist/analytics/domain.d.cts +0 -121
- package/dist/analytics/domain.d.mts +0 -121
- package/dist/analytics/errors.cjs +0 -22
- package/dist/analytics/errors.mjs +0 -20
- package/dist/analytics/persistence.d.cts +0 -6
- package/dist/analytics/persistence.d.mts +0 -6
- package/dist/analytics/queryInput.cjs +0 -58
- package/dist/analytics/queryInput.mjs +0 -55
- package/dist/analytics/routes.cjs +0 -63
- package/dist/analytics/routes.mjs +0 -61
- package/dist/analytics/types.d.cts +0 -17
- package/dist/analytics/types.d.mts +0 -17
- package/src/analytics/analytics.integration.spec.ts +0 -144
- package/src/analytics/bounded/activeOverview.spec.ts +0 -189
- package/src/analytics/bounded/activeOverview.ts +0 -148
- package/src/analytics/bounded/installationSearch.spec.ts +0 -130
- package/src/analytics/bounded/installationSearch.ts +0 -69
- package/src/analytics/bounded/persistence.ts +0 -43
- package/src/analytics/bounded/provider.spec.ts +0 -219
- package/src/analytics/bounded/provider.ts +0 -229
- package/src/analytics/bounded/scan.ts +0 -235
- package/src/analytics/domain.ts +0 -139
- package/src/analytics/errors.ts +0 -18
- package/src/analytics/persistence.ts +0 -10
- package/src/analytics/queryInput.ts +0 -102
- package/src/analytics/routes.ts +0 -123
- package/src/analytics/types.ts +0 -44
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const require_prismaRows = require("./prismaRows.cjs");
|
|
2
|
+
let _hot_updater_plugin_core_internal = require("@hot-updater/plugin-core/internal");
|
|
3
|
+
//#region src/adapters/prismaInsights.ts
|
|
4
|
+
const headFields = [
|
|
5
|
+
"install_id",
|
|
6
|
+
"id",
|
|
7
|
+
"received_at_ms",
|
|
8
|
+
"user_id",
|
|
9
|
+
"platform",
|
|
10
|
+
"channel",
|
|
11
|
+
"type",
|
|
12
|
+
"from_bundle_id",
|
|
13
|
+
"to_bundle_id"
|
|
14
|
+
];
|
|
15
|
+
const parameter = (provider, index, field) => {
|
|
16
|
+
if (provider !== "postgresql" && provider !== "cockroachdb") return "?";
|
|
17
|
+
return `$${index}${field === "metadata" ? "::jsonb" : [
|
|
18
|
+
"id",
|
|
19
|
+
"from_release_id",
|
|
20
|
+
"from_bundle_id",
|
|
21
|
+
"to_release_id",
|
|
22
|
+
"to_bundle_id"
|
|
23
|
+
].includes(field ?? "") ? "::uuid" : ""}`;
|
|
24
|
+
};
|
|
25
|
+
const headConflict = (provider) => {
|
|
26
|
+
const newer = "(excluded.received_at_ms > bundle_event_heads.received_at_ms OR (excluded.received_at_ms = bundle_event_heads.received_at_ms AND excluded.id > bundle_event_heads.id))";
|
|
27
|
+
if (provider === "mysql") {
|
|
28
|
+
const mysqlNewer = newer.replace(/excluded\.(\w+)/g, "VALUES($1)");
|
|
29
|
+
return `ON DUPLICATE KEY UPDATE ${[
|
|
30
|
+
...headFields.filter((field) => ![
|
|
31
|
+
"install_id",
|
|
32
|
+
"id",
|
|
33
|
+
"received_at_ms"
|
|
34
|
+
].includes(field)),
|
|
35
|
+
"id",
|
|
36
|
+
"received_at_ms"
|
|
37
|
+
].map((field) => `${field} = IF(${mysqlNewer}, VALUES(${field}), bundle_event_heads.${field})`).join(", ")}`;
|
|
38
|
+
}
|
|
39
|
+
return `ON CONFLICT (install_id) DO UPDATE SET ${headFields.filter((field) => field !== "install_id").map((field) => `${field} = excluded.${field}`).join(", ")} WHERE ${newer}`;
|
|
40
|
+
};
|
|
41
|
+
const executePrismaInsights = async (client, query, values) => {
|
|
42
|
+
if (!("$executeRawUnsafe" in client) || typeof client.$executeRawUnsafe !== "function") throw new require_prismaRows.PrismaAdapterError("Insights writes require native SQL support");
|
|
43
|
+
await client.$executeRawUnsafe(query, ...values);
|
|
44
|
+
};
|
|
45
|
+
const updatePrismaEventHead = (client, provider, event) => executePrismaInsights(client, `INSERT INTO bundle_event_heads (${headFields.join(", ")}) VALUES (${headFields.map((field, index) => parameter(provider, index + 1, field)).join(", ")}) ${headConflict(provider)}`, headFields.map((field) => event[field]));
|
|
46
|
+
/** PostgreSQL clients without callback transactions can publish both rows atomically. */
|
|
47
|
+
const recordPrismaEventWithCte = (client, provider, event) => {
|
|
48
|
+
if (provider !== "postgresql" && provider !== "cockroachdb") throw new require_prismaRows.PrismaAdapterError("Insights writes require callback transactions");
|
|
49
|
+
const fields = [
|
|
50
|
+
"id",
|
|
51
|
+
"type",
|
|
52
|
+
"install_id",
|
|
53
|
+
"user_id",
|
|
54
|
+
"from_release_id",
|
|
55
|
+
"from_bundle_id",
|
|
56
|
+
"to_release_id",
|
|
57
|
+
"to_bundle_id",
|
|
58
|
+
"platform",
|
|
59
|
+
"app_version",
|
|
60
|
+
"channel",
|
|
61
|
+
"metadata",
|
|
62
|
+
"received_at_ms"
|
|
63
|
+
];
|
|
64
|
+
return executePrismaInsights(client, `WITH inserted AS (INSERT INTO bundle_events (${fields.join(", ")}) VALUES (${fields.map((field, index) => parameter(provider, index + 1, field)).join(", ")}) ON CONFLICT (id) DO NOTHING RETURNING ${headFields.join(", ")}) INSERT INTO bundle_event_heads (${headFields.join(", ")}) SELECT ${headFields.join(", ")} FROM inserted ${headConflict(provider)}`, fields.map((field) => field === "metadata" ? JSON.stringify(event[field]) : event[field]));
|
|
65
|
+
};
|
|
66
|
+
const queryPrismaLatestEvents = async (client, provider, input) => {
|
|
67
|
+
if (!("$queryRawUnsafe" in client) || typeof client.$queryRawUnsafe !== "function") throw new require_prismaRows.PrismaAdapterError("Insights reads require native SQL query support");
|
|
68
|
+
const values = [];
|
|
69
|
+
const bind = (value, field) => {
|
|
70
|
+
values.push(value);
|
|
71
|
+
return parameter(provider, values.length, field);
|
|
72
|
+
};
|
|
73
|
+
const groups = "sinceMs" in input ? (0, _hot_updater_plugin_core_internal.latestInsightsCountGroups)(input) : [(0, _hot_updater_plugin_core_internal.latestInsightsWhere)(input)];
|
|
74
|
+
let firstGroupValues = [];
|
|
75
|
+
const predicates = groups.map((group, index) => {
|
|
76
|
+
const predicate = `(${group.map((where) => where.operator === "in" && Array.isArray(where.value) ? `${where.field} IN (${where.value.map((value) => bind(value, where.field)).join(", ")})` : `${where.field} ${where.operator === "gte" ? ">=" : where.operator === "gt" ? ">" : "="} ${bind(where.value, where.field)}`).join(" AND ")})`;
|
|
77
|
+
if (index === 0) firstGroupValues = values.slice();
|
|
78
|
+
return predicate;
|
|
79
|
+
});
|
|
80
|
+
const counting = "sinceMs" in input;
|
|
81
|
+
const page = counting ? "" : ` ORDER BY install_id ASC LIMIT ${bind("installId" in input ? 1 : input.limit)}`;
|
|
82
|
+
let selection = `SELECT ${counting ? "COUNT(*) AS count" : "id, install_id"} FROM bundle_event_heads WHERE (${predicates.join(" OR ")})${page}`;
|
|
83
|
+
if (counting && provider === "mysql" && predicates.length === 2) {
|
|
84
|
+
selection = `SELECT (SELECT COUNT(*) FROM bundle_event_heads WHERE ${predicates[0]}) + (SELECT COUNT(*) FROM bundle_event_heads WHERE ${predicates[1]} AND ${predicates[0]} IS NOT TRUE) AS count`;
|
|
85
|
+
values.push(...firstGroupValues);
|
|
86
|
+
}
|
|
87
|
+
const rows = await client.$queryRawUnsafe(counting ? selection : `SELECT event.* FROM (${selection}) AS head JOIN bundle_events AS event ON event.id = head.id ORDER BY head.install_id ASC`, ...values);
|
|
88
|
+
if (!Array.isArray(rows)) throw new require_prismaRows.PrismaAdapterError("invalid latest-event query result");
|
|
89
|
+
if (counting) {
|
|
90
|
+
const count = rows[0]?.count;
|
|
91
|
+
if (typeof count !== "number" && typeof count !== "bigint") throw new require_prismaRows.PrismaAdapterError("invalid latest-event count");
|
|
92
|
+
return Number(count);
|
|
93
|
+
}
|
|
94
|
+
return rows.map(require_prismaRows.parsePrismaBundleEventRow);
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
exports.queryPrismaLatestEvents = queryPrismaLatestEvents;
|
|
98
|
+
exports.recordPrismaEventWithCte = recordPrismaEventWithCte;
|
|
99
|
+
exports.updatePrismaEventHead = updatePrismaEventHead;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { PrismaAdapterError, parsePrismaBundleEventRow } from "./prismaRows.mjs";
|
|
2
|
+
import { latestInsightsCountGroups, latestInsightsWhere } from "@hot-updater/plugin-core/internal";
|
|
3
|
+
//#region src/adapters/prismaInsights.ts
|
|
4
|
+
const headFields = [
|
|
5
|
+
"install_id",
|
|
6
|
+
"id",
|
|
7
|
+
"received_at_ms",
|
|
8
|
+
"user_id",
|
|
9
|
+
"platform",
|
|
10
|
+
"channel",
|
|
11
|
+
"type",
|
|
12
|
+
"from_bundle_id",
|
|
13
|
+
"to_bundle_id"
|
|
14
|
+
];
|
|
15
|
+
const parameter = (provider, index, field) => {
|
|
16
|
+
if (provider !== "postgresql" && provider !== "cockroachdb") return "?";
|
|
17
|
+
return `$${index}${field === "metadata" ? "::jsonb" : [
|
|
18
|
+
"id",
|
|
19
|
+
"from_release_id",
|
|
20
|
+
"from_bundle_id",
|
|
21
|
+
"to_release_id",
|
|
22
|
+
"to_bundle_id"
|
|
23
|
+
].includes(field ?? "") ? "::uuid" : ""}`;
|
|
24
|
+
};
|
|
25
|
+
const headConflict = (provider) => {
|
|
26
|
+
const newer = "(excluded.received_at_ms > bundle_event_heads.received_at_ms OR (excluded.received_at_ms = bundle_event_heads.received_at_ms AND excluded.id > bundle_event_heads.id))";
|
|
27
|
+
if (provider === "mysql") {
|
|
28
|
+
const mysqlNewer = newer.replace(/excluded\.(\w+)/g, "VALUES($1)");
|
|
29
|
+
return `ON DUPLICATE KEY UPDATE ${[
|
|
30
|
+
...headFields.filter((field) => ![
|
|
31
|
+
"install_id",
|
|
32
|
+
"id",
|
|
33
|
+
"received_at_ms"
|
|
34
|
+
].includes(field)),
|
|
35
|
+
"id",
|
|
36
|
+
"received_at_ms"
|
|
37
|
+
].map((field) => `${field} = IF(${mysqlNewer}, VALUES(${field}), bundle_event_heads.${field})`).join(", ")}`;
|
|
38
|
+
}
|
|
39
|
+
return `ON CONFLICT (install_id) DO UPDATE SET ${headFields.filter((field) => field !== "install_id").map((field) => `${field} = excluded.${field}`).join(", ")} WHERE ${newer}`;
|
|
40
|
+
};
|
|
41
|
+
const executePrismaInsights = async (client, query, values) => {
|
|
42
|
+
if (!("$executeRawUnsafe" in client) || typeof client.$executeRawUnsafe !== "function") throw new PrismaAdapterError("Insights writes require native SQL support");
|
|
43
|
+
await client.$executeRawUnsafe(query, ...values);
|
|
44
|
+
};
|
|
45
|
+
const updatePrismaEventHead = (client, provider, event) => executePrismaInsights(client, `INSERT INTO bundle_event_heads (${headFields.join(", ")}) VALUES (${headFields.map((field, index) => parameter(provider, index + 1, field)).join(", ")}) ${headConflict(provider)}`, headFields.map((field) => event[field]));
|
|
46
|
+
/** PostgreSQL clients without callback transactions can publish both rows atomically. */
|
|
47
|
+
const recordPrismaEventWithCte = (client, provider, event) => {
|
|
48
|
+
if (provider !== "postgresql" && provider !== "cockroachdb") throw new PrismaAdapterError("Insights writes require callback transactions");
|
|
49
|
+
const fields = [
|
|
50
|
+
"id",
|
|
51
|
+
"type",
|
|
52
|
+
"install_id",
|
|
53
|
+
"user_id",
|
|
54
|
+
"from_release_id",
|
|
55
|
+
"from_bundle_id",
|
|
56
|
+
"to_release_id",
|
|
57
|
+
"to_bundle_id",
|
|
58
|
+
"platform",
|
|
59
|
+
"app_version",
|
|
60
|
+
"channel",
|
|
61
|
+
"metadata",
|
|
62
|
+
"received_at_ms"
|
|
63
|
+
];
|
|
64
|
+
return executePrismaInsights(client, `WITH inserted AS (INSERT INTO bundle_events (${fields.join(", ")}) VALUES (${fields.map((field, index) => parameter(provider, index + 1, field)).join(", ")}) ON CONFLICT (id) DO NOTHING RETURNING ${headFields.join(", ")}) INSERT INTO bundle_event_heads (${headFields.join(", ")}) SELECT ${headFields.join(", ")} FROM inserted ${headConflict(provider)}`, fields.map((field) => field === "metadata" ? JSON.stringify(event[field]) : event[field]));
|
|
65
|
+
};
|
|
66
|
+
const queryPrismaLatestEvents = async (client, provider, input) => {
|
|
67
|
+
if (!("$queryRawUnsafe" in client) || typeof client.$queryRawUnsafe !== "function") throw new PrismaAdapterError("Insights reads require native SQL query support");
|
|
68
|
+
const values = [];
|
|
69
|
+
const bind = (value, field) => {
|
|
70
|
+
values.push(value);
|
|
71
|
+
return parameter(provider, values.length, field);
|
|
72
|
+
};
|
|
73
|
+
const groups = "sinceMs" in input ? latestInsightsCountGroups(input) : [latestInsightsWhere(input)];
|
|
74
|
+
let firstGroupValues = [];
|
|
75
|
+
const predicates = groups.map((group, index) => {
|
|
76
|
+
const predicate = `(${group.map((where) => where.operator === "in" && Array.isArray(where.value) ? `${where.field} IN (${where.value.map((value) => bind(value, where.field)).join(", ")})` : `${where.field} ${where.operator === "gte" ? ">=" : where.operator === "gt" ? ">" : "="} ${bind(where.value, where.field)}`).join(" AND ")})`;
|
|
77
|
+
if (index === 0) firstGroupValues = values.slice();
|
|
78
|
+
return predicate;
|
|
79
|
+
});
|
|
80
|
+
const counting = "sinceMs" in input;
|
|
81
|
+
const page = counting ? "" : ` ORDER BY install_id ASC LIMIT ${bind("installId" in input ? 1 : input.limit)}`;
|
|
82
|
+
let selection = `SELECT ${counting ? "COUNT(*) AS count" : "id, install_id"} FROM bundle_event_heads WHERE (${predicates.join(" OR ")})${page}`;
|
|
83
|
+
if (counting && provider === "mysql" && predicates.length === 2) {
|
|
84
|
+
selection = `SELECT (SELECT COUNT(*) FROM bundle_event_heads WHERE ${predicates[0]}) + (SELECT COUNT(*) FROM bundle_event_heads WHERE ${predicates[1]} AND ${predicates[0]} IS NOT TRUE) AS count`;
|
|
85
|
+
values.push(...firstGroupValues);
|
|
86
|
+
}
|
|
87
|
+
const rows = await client.$queryRawUnsafe(counting ? selection : `SELECT event.* FROM (${selection}) AS head JOIN bundle_events AS event ON event.id = head.id ORDER BY head.install_id ASC`, ...values);
|
|
88
|
+
if (!Array.isArray(rows)) throw new PrismaAdapterError("invalid latest-event query result");
|
|
89
|
+
if (counting) {
|
|
90
|
+
const count = rows[0]?.count;
|
|
91
|
+
if (typeof count !== "number" && typeof count !== "bigint") throw new PrismaAdapterError("invalid latest-event count");
|
|
92
|
+
return Number(count);
|
|
93
|
+
}
|
|
94
|
+
return rows.map(parsePrismaBundleEventRow);
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
export { queryPrismaLatestEvents, recordPrismaEventWithCte, updatePrismaEventHead };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
1
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
3
2
|
//#region src/adapters/prismaRows.ts
|
|
4
3
|
var PrismaAdapterError = class extends Error {
|
|
4
|
+
reason;
|
|
5
5
|
name = "PrismaAdapterError";
|
|
6
6
|
constructor(reason) {
|
|
7
7
|
super(`Invalid Prisma plugin state: ${reason}`);
|
|
@@ -92,14 +92,16 @@ const parsePrismaBundleEventRow = (value) => {
|
|
|
92
92
|
const type = readString(value, "type");
|
|
93
93
|
const platform = readString(value, "platform");
|
|
94
94
|
const fromBundleId = readNullableString(value, "from_bundle_id");
|
|
95
|
-
const
|
|
96
|
-
|
|
95
|
+
const storedMetadata = value["metadata"];
|
|
96
|
+
const metadata = typeof storedMetadata === "string" ? JSON.parse(storedMetadata) : storedMetadata;
|
|
97
|
+
if (!(0, _hot_updater_plugin_core.isDatabaseBundleEventMetadata)(metadata)) throw new PrismaAdapterError("invalid event metadata");
|
|
98
|
+
const updateStrategy = metadata.update_strategy;
|
|
99
|
+
if (platform !== "ios" && platform !== "android" || !((type === "UPDATE_DOWNLOADED" || type === "UPDATE_APPLIED" || type === "RECOVERED") && typeof fromBundleId === "string" && (updateStrategy === "fingerprint" || updateStrategy === "appVersion") || type === "UNCHANGED" && fromBundleId === null && updateStrategy === null)) throw new PrismaAdapterError("invalid event shape");
|
|
97
100
|
return {
|
|
98
101
|
id: readString(value, "id"),
|
|
99
102
|
type,
|
|
100
103
|
install_id: readString(value, "install_id"),
|
|
101
104
|
user_id: readNullableString(value, "user_id"),
|
|
102
|
-
username: readNullableString(value, "username"),
|
|
103
105
|
from_release_id: readNullableString(value, "from_release_id"),
|
|
104
106
|
from_bundle_id: fromBundleId,
|
|
105
107
|
to_release_id: readNullableString(value, "to_release_id"),
|
|
@@ -107,10 +109,7 @@ const parsePrismaBundleEventRow = (value) => {
|
|
|
107
109
|
platform,
|
|
108
110
|
app_version: readString(value, "app_version"),
|
|
109
111
|
channel: readString(value, "channel"),
|
|
110
|
-
|
|
111
|
-
update_strategy: updateStrategy,
|
|
112
|
-
fingerprint_hash: readNullableString(value, "fingerprint_hash"),
|
|
113
|
-
sdk_version: readNullableString(value, "sdk_version"),
|
|
112
|
+
metadata,
|
|
114
113
|
received_at_ms: readNumber(value, "received_at_ms")
|
|
115
114
|
};
|
|
116
115
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { isDatabaseMetadataObject } from "@hot-updater/plugin-core";
|
|
1
|
+
import { isDatabaseBundleEventMetadata, isDatabaseMetadataObject } from "@hot-updater/plugin-core";
|
|
2
2
|
//#region src/adapters/prismaRows.ts
|
|
3
3
|
var PrismaAdapterError = class extends Error {
|
|
4
|
+
reason;
|
|
4
5
|
name = "PrismaAdapterError";
|
|
5
6
|
constructor(reason) {
|
|
6
7
|
super(`Invalid Prisma plugin state: ${reason}`);
|
|
@@ -91,14 +92,16 @@ const parsePrismaBundleEventRow = (value) => {
|
|
|
91
92
|
const type = readString(value, "type");
|
|
92
93
|
const platform = readString(value, "platform");
|
|
93
94
|
const fromBundleId = readNullableString(value, "from_bundle_id");
|
|
94
|
-
const
|
|
95
|
-
|
|
95
|
+
const storedMetadata = value["metadata"];
|
|
96
|
+
const metadata = typeof storedMetadata === "string" ? JSON.parse(storedMetadata) : storedMetadata;
|
|
97
|
+
if (!isDatabaseBundleEventMetadata(metadata)) throw new PrismaAdapterError("invalid event metadata");
|
|
98
|
+
const updateStrategy = metadata.update_strategy;
|
|
99
|
+
if (platform !== "ios" && platform !== "android" || !((type === "UPDATE_DOWNLOADED" || type === "UPDATE_APPLIED" || type === "RECOVERED") && typeof fromBundleId === "string" && (updateStrategy === "fingerprint" || updateStrategy === "appVersion") || type === "UNCHANGED" && fromBundleId === null && updateStrategy === null)) throw new PrismaAdapterError("invalid event shape");
|
|
96
100
|
return {
|
|
97
101
|
id: readString(value, "id"),
|
|
98
102
|
type,
|
|
99
103
|
install_id: readString(value, "install_id"),
|
|
100
104
|
user_id: readNullableString(value, "user_id"),
|
|
101
|
-
username: readNullableString(value, "username"),
|
|
102
105
|
from_release_id: readNullableString(value, "from_release_id"),
|
|
103
106
|
from_bundle_id: fromBundleId,
|
|
104
107
|
to_release_id: readNullableString(value, "to_release_id"),
|
|
@@ -106,10 +109,7 @@ const parsePrismaBundleEventRow = (value) => {
|
|
|
106
109
|
platform,
|
|
107
110
|
app_version: readString(value, "app_version"),
|
|
108
111
|
channel: readString(value, "channel"),
|
|
109
|
-
|
|
110
|
-
update_strategy: updateStrategy,
|
|
111
|
-
fingerprint_hash: readNullableString(value, "fingerprint_hash"),
|
|
112
|
-
sdk_version: readNullableString(value, "sdk_version"),
|
|
112
|
+
metadata,
|
|
113
113
|
received_at_ms: readNumber(value, "received_at_ms")
|
|
114
114
|
};
|
|
115
115
|
};
|
package/dist/apiKeys.cjs
CHANGED
|
@@ -33,7 +33,7 @@ const hashApiKey = async (apiKey) => {
|
|
|
33
33
|
return bytesToBase64Url(new Uint8Array(digest));
|
|
34
34
|
};
|
|
35
35
|
const apiKeyId = () => {
|
|
36
|
-
const bytes = new Uint8Array(16);
|
|
36
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
37
37
|
crypto.getRandomValues(bytes);
|
|
38
38
|
return `api-${bytesToBase64Url(bytes)}`;
|
|
39
39
|
};
|
|
@@ -70,7 +70,7 @@ const registerApiKey = async (input) => {
|
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
72
|
const createApiKey = (input) => {
|
|
73
|
-
const bytes = new Uint8Array(32);
|
|
73
|
+
const bytes = /* @__PURE__ */ new Uint8Array(32);
|
|
74
74
|
crypto.getRandomValues(bytes);
|
|
75
75
|
return registerApiKey({
|
|
76
76
|
apiKey: bytesToBase64Url(bytes),
|
|
@@ -131,9 +131,13 @@ const createApiKeyManagement = (input) => Object.freeze({
|
|
|
131
131
|
});
|
|
132
132
|
//#endregion
|
|
133
133
|
exports.API_KEY_HEADER_NAME = API_KEY_HEADER_NAME;
|
|
134
|
+
exports.apiKeyId = apiKeyId;
|
|
134
135
|
exports.authenticateApiKey = authenticateApiKey;
|
|
135
136
|
exports.createApiKey = createApiKey;
|
|
136
137
|
exports.createApiKeyManagement = createApiKeyManagement;
|
|
138
|
+
exports.hashApiKey = hashApiKey;
|
|
139
|
+
exports.isApiKey = isApiKey;
|
|
137
140
|
exports.normalizeApiKeyHeaderName = normalizeApiKeyHeaderName;
|
|
141
|
+
exports.normalizeApiKeyName = normalizeApiKeyName;
|
|
138
142
|
exports.provisionApiKey = provisionApiKey;
|
|
139
143
|
exports.registerApiKey = registerApiKey;
|
package/dist/apiKeys.d.cts
CHANGED
package/dist/apiKeys.d.mts
CHANGED
package/dist/apiKeys.mjs
CHANGED
|
@@ -33,7 +33,7 @@ const hashApiKey = async (apiKey) => {
|
|
|
33
33
|
return bytesToBase64Url(new Uint8Array(digest));
|
|
34
34
|
};
|
|
35
35
|
const apiKeyId = () => {
|
|
36
|
-
const bytes = new Uint8Array(16);
|
|
36
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
37
37
|
crypto.getRandomValues(bytes);
|
|
38
38
|
return `api-${bytesToBase64Url(bytes)}`;
|
|
39
39
|
};
|
|
@@ -70,7 +70,7 @@ const registerApiKey = async (input) => {
|
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
72
|
const createApiKey = (input) => {
|
|
73
|
-
const bytes = new Uint8Array(32);
|
|
73
|
+
const bytes = /* @__PURE__ */ new Uint8Array(32);
|
|
74
74
|
crypto.getRandomValues(bytes);
|
|
75
75
|
return registerApiKey({
|
|
76
76
|
apiKey: bytesToBase64Url(bytes),
|
|
@@ -130,4 +130,4 @@ const createApiKeyManagement = (input) => Object.freeze({
|
|
|
130
130
|
}
|
|
131
131
|
});
|
|
132
132
|
//#endregion
|
|
133
|
-
export { API_KEY_HEADER_NAME, authenticateApiKey, createApiKey, createApiKeyManagement, normalizeApiKeyHeaderName, provisionApiKey, registerApiKey };
|
|
133
|
+
export { API_KEY_HEADER_NAME, apiKeyId, authenticateApiKey, createApiKey, createApiKeyManagement, hashApiKey, isApiKey, normalizeApiKeyHeaderName, normalizeApiKeyName, provisionApiKey, registerApiKey };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
require("./_virtual/_rolldown/runtime.cjs");
|
|
2
1
|
const require_handler = require("./handler.cjs");
|
|
3
|
-
const require_provider = require("./
|
|
2
|
+
const require_provider = require("./insights/provider.cjs");
|
|
4
3
|
const require_apiKeys = require("./apiKeys.cjs");
|
|
5
4
|
const require_databasePluginCore = require("./db/databasePluginCore.cjs");
|
|
6
5
|
const require_schemaReadiness = require("./db/schemaReadiness.cjs");
|
|
@@ -25,34 +24,48 @@ function getHotUpdaterCoreMetadata(hotUpdater) {
|
|
|
25
24
|
function createHotUpdaterCore(options) {
|
|
26
25
|
for (const key of ["authorityId", "catalogId"]) if (Object.hasOwn(options, key)) throw new TypeError(`Remove ${key} from createHotUpdater options. Catalog identity is managed internally.`);
|
|
27
26
|
const database = options.database;
|
|
28
|
-
const
|
|
27
|
+
const storagePlugins = (options.storage ?? []).map((storage) => {
|
|
29
28
|
(0, _hot_updater_plugin_core.assertStorageOperations)(storage, ["get", "getDownloadUrl"]);
|
|
30
29
|
return storage;
|
|
31
|
-
})
|
|
30
|
+
});
|
|
31
|
+
const { downloadStorageObject, readStorageText, resolveFileUrl } = require_storageAccess.createStorageAccess(storagePlugins);
|
|
32
32
|
const adapterCapabilities = database;
|
|
33
33
|
if (!require_types.isDatabasePlugin(database)) throw new Error("@hot-updater/server only supports database plugins.");
|
|
34
34
|
const plugin = database;
|
|
35
|
-
const
|
|
35
|
+
const adapterName = adapterCapabilities.adapterName ?? plugin.name;
|
|
36
|
+
const assertSchemaReady = require_schemaReadiness.createSchemaReadinessChecker(adapterName, adapterCapabilities.createMigrator);
|
|
36
37
|
const core = require_databasePluginCore.createDatabasePluginCore(plugin, resolveFileUrl, {
|
|
37
38
|
beforeOperation: assertSchemaReady,
|
|
38
39
|
readStorageText
|
|
39
40
|
});
|
|
40
41
|
const clientAccess = normalizeClientAccess(options.clientAccess);
|
|
41
|
-
const
|
|
42
|
-
async
|
|
42
|
+
const insights = require_provider.createInsightsProvider({
|
|
43
|
+
async recordEvent(input) {
|
|
44
|
+
await assertSchemaReady();
|
|
45
|
+
return plugin.models.insights.recordEvent(input);
|
|
46
|
+
},
|
|
47
|
+
async listEvents(input) {
|
|
48
|
+
await assertSchemaReady();
|
|
49
|
+
return plugin.models.insights.listEvents(input);
|
|
50
|
+
},
|
|
51
|
+
async findLatestEvents(input) {
|
|
52
|
+
await assertSchemaReady();
|
|
53
|
+
return plugin.models.insights.findLatestEvents(input);
|
|
54
|
+
},
|
|
55
|
+
async countLatestEvents(input) {
|
|
43
56
|
await assertSchemaReady();
|
|
44
|
-
return plugin.models.
|
|
57
|
+
return plugin.models.insights.countLatestEvents(input);
|
|
45
58
|
},
|
|
46
|
-
async
|
|
59
|
+
async countEvents(input) {
|
|
47
60
|
await assertSchemaReady();
|
|
48
|
-
return plugin.models.
|
|
61
|
+
return plugin.models.insights.countEvents(input);
|
|
49
62
|
}
|
|
50
63
|
});
|
|
51
64
|
const apiKeys = require_apiKeys.createApiKeyManagement({
|
|
52
65
|
apiKeys: plugin.models.apiKeys,
|
|
53
66
|
beforeOperation: assertSchemaReady
|
|
54
67
|
});
|
|
55
|
-
const handlers = require_handler.createHotUpdaterHandlers(core.api,
|
|
68
|
+
const handlers = require_handler.createHotUpdaterHandlers(core.api, insights, clientAccess.type === "api-key" ? {
|
|
56
69
|
authenticate: (request) => require_apiKeys.authenticateApiKey({
|
|
57
70
|
apiKeys: plugin.models.apiKeys,
|
|
58
71
|
beforeLookup: assertSchemaReady,
|
|
@@ -63,7 +76,7 @@ function createHotUpdaterCore(options) {
|
|
|
63
76
|
} : void 0, downloadStorageObject);
|
|
64
77
|
const api = Object.assign({
|
|
65
78
|
adapterName: adapterCapabilities.adapterName ?? core.adapterName,
|
|
66
|
-
|
|
79
|
+
insights,
|
|
67
80
|
apiKeys,
|
|
68
81
|
handlers
|
|
69
82
|
}, core.api);
|
|
@@ -85,4 +98,6 @@ function createHotUpdater(options) {
|
|
|
85
98
|
}
|
|
86
99
|
//#endregion
|
|
87
100
|
exports.createHotUpdater = createHotUpdater;
|
|
101
|
+
exports.createHotUpdaterCore = createHotUpdaterCore;
|
|
88
102
|
exports.getHotUpdaterCoreMetadata = getHotUpdaterCoreMetadata;
|
|
103
|
+
exports.hotUpdaterCoreMetadata = hotUpdaterCoreMetadata;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { AnalyticsProvider } from "./analytics/types.cjs";
|
|
2
1
|
import { ApiKeyManagementAPI } from "./apiKeys.cjs";
|
|
3
2
|
import { HotUpdaterHandlers } from "./handlerTypes.cjs";
|
|
3
|
+
import { InsightsProvider } from "./insights/types.cjs";
|
|
4
|
+
import "./handler.cjs";
|
|
4
5
|
import { DatabaseAPI, DatabasePlugin as DatabasePlugin$1 } from "./db/types.cjs";
|
|
5
6
|
import { StoragePlugin } from "@hot-updater/plugin-core";
|
|
6
|
-
|
|
7
7
|
//#region src/createHotUpdaterCore.d.ts
|
|
8
8
|
type RuntimeHotUpdaterAPI = DatabaseAPI & {
|
|
9
9
|
readonly handlers: HotUpdaterHandlers;
|
|
10
10
|
readonly adapterName: string;
|
|
11
11
|
/**
|
|
12
|
-
* Built-in
|
|
13
|
-
* always mounted; React Native clients
|
|
14
|
-
*
|
|
12
|
+
* Built-in Insights provider. Client ingestion and admin query routes are
|
|
13
|
+
* always mounted; React Native clients report lifecycle events by default
|
|
14
|
+
* and can opt out with `HotUpdater.init({ insights: false })`.
|
|
15
15
|
*/
|
|
16
|
-
readonly
|
|
16
|
+
readonly insights: InsightsProvider;
|
|
17
17
|
/**
|
|
18
18
|
* In-process API key lifecycle operations for trusted server tooling.
|
|
19
19
|
* Creation returns the plaintext once; list and revoke expose only metadata.
|
|
@@ -24,14 +24,14 @@ type RuntimeHotUpdaterAPI = DatabaseAPI & {
|
|
|
24
24
|
type HotUpdaterAPI = RuntimeHotUpdaterAPI;
|
|
25
25
|
type ClientAccessPolicy = {
|
|
26
26
|
/**
|
|
27
|
-
* Leaves Release Catalog, artifact, and
|
|
27
|
+
* Leaves Release Catalog, artifact, and Insights ingestion routes
|
|
28
28
|
* publicly accessible without a client credential.
|
|
29
29
|
*/
|
|
30
30
|
readonly type: "public";
|
|
31
31
|
} | {
|
|
32
32
|
/**
|
|
33
33
|
* Requires a key registered in `database.models.apiKeys` for
|
|
34
|
-
* Release Catalog, artifact, and
|
|
34
|
+
* Release Catalog, artifact, and Insights ingestion requests.
|
|
35
35
|
*/
|
|
36
36
|
readonly type: "api-key";
|
|
37
37
|
/**
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { AnalyticsProvider } from "./analytics/types.mjs";
|
|
2
1
|
import { ApiKeyManagementAPI } from "./apiKeys.mjs";
|
|
3
2
|
import { HotUpdaterHandlers } from "./handlerTypes.mjs";
|
|
3
|
+
import { InsightsProvider } from "./insights/types.mjs";
|
|
4
|
+
import "./handler.mjs";
|
|
4
5
|
import { DatabaseAPI, DatabasePlugin as DatabasePlugin$1 } from "./db/types.mjs";
|
|
5
6
|
import { StoragePlugin } from "@hot-updater/plugin-core";
|
|
6
|
-
|
|
7
7
|
//#region src/createHotUpdaterCore.d.ts
|
|
8
8
|
type RuntimeHotUpdaterAPI = DatabaseAPI & {
|
|
9
9
|
readonly handlers: HotUpdaterHandlers;
|
|
10
10
|
readonly adapterName: string;
|
|
11
11
|
/**
|
|
12
|
-
* Built-in
|
|
13
|
-
* always mounted; React Native clients
|
|
14
|
-
*
|
|
12
|
+
* Built-in Insights provider. Client ingestion and admin query routes are
|
|
13
|
+
* always mounted; React Native clients report lifecycle events by default
|
|
14
|
+
* and can opt out with `HotUpdater.init({ insights: false })`.
|
|
15
15
|
*/
|
|
16
|
-
readonly
|
|
16
|
+
readonly insights: InsightsProvider;
|
|
17
17
|
/**
|
|
18
18
|
* In-process API key lifecycle operations for trusted server tooling.
|
|
19
19
|
* Creation returns the plaintext once; list and revoke expose only metadata.
|
|
@@ -24,14 +24,14 @@ type RuntimeHotUpdaterAPI = DatabaseAPI & {
|
|
|
24
24
|
type HotUpdaterAPI = RuntimeHotUpdaterAPI;
|
|
25
25
|
type ClientAccessPolicy = {
|
|
26
26
|
/**
|
|
27
|
-
* Leaves Release Catalog, artifact, and
|
|
27
|
+
* Leaves Release Catalog, artifact, and Insights ingestion routes
|
|
28
28
|
* publicly accessible without a client credential.
|
|
29
29
|
*/
|
|
30
30
|
readonly type: "public";
|
|
31
31
|
} | {
|
|
32
32
|
/**
|
|
33
33
|
* Requires a key registered in `database.models.apiKeys` for
|
|
34
|
-
* Release Catalog, artifact, and
|
|
34
|
+
* Release Catalog, artifact, and Insights ingestion requests.
|
|
35
35
|
*/
|
|
36
36
|
readonly type: "api-key";
|
|
37
37
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHotUpdaterHandlers } from "./handler.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { createInsightsProvider } from "./insights/provider.mjs";
|
|
3
3
|
import { authenticateApiKey, createApiKeyManagement, normalizeApiKeyHeaderName } from "./apiKeys.mjs";
|
|
4
4
|
import { createDatabasePluginCore } from "./db/databasePluginCore.mjs";
|
|
5
5
|
import { createSchemaReadinessChecker } from "./db/schemaReadiness.mjs";
|
|
@@ -24,34 +24,48 @@ function getHotUpdaterCoreMetadata(hotUpdater) {
|
|
|
24
24
|
function createHotUpdaterCore(options) {
|
|
25
25
|
for (const key of ["authorityId", "catalogId"]) if (Object.hasOwn(options, key)) throw new TypeError(`Remove ${key} from createHotUpdater options. Catalog identity is managed internally.`);
|
|
26
26
|
const database = options.database;
|
|
27
|
-
const
|
|
27
|
+
const storagePlugins = (options.storage ?? []).map((storage) => {
|
|
28
28
|
assertStorageOperations(storage, ["get", "getDownloadUrl"]);
|
|
29
29
|
return storage;
|
|
30
|
-
})
|
|
30
|
+
});
|
|
31
|
+
const { downloadStorageObject, readStorageText, resolveFileUrl } = createStorageAccess(storagePlugins);
|
|
31
32
|
const adapterCapabilities = database;
|
|
32
33
|
if (!isDatabasePlugin(database)) throw new Error("@hot-updater/server only supports database plugins.");
|
|
33
34
|
const plugin = database;
|
|
34
|
-
const
|
|
35
|
+
const adapterName = adapterCapabilities.adapterName ?? plugin.name;
|
|
36
|
+
const assertSchemaReady = createSchemaReadinessChecker(adapterName, adapterCapabilities.createMigrator);
|
|
35
37
|
const core = createDatabasePluginCore(plugin, resolveFileUrl, {
|
|
36
38
|
beforeOperation: assertSchemaReady,
|
|
37
39
|
readStorageText
|
|
38
40
|
});
|
|
39
41
|
const clientAccess = normalizeClientAccess(options.clientAccess);
|
|
40
|
-
const
|
|
41
|
-
async
|
|
42
|
+
const insights = createInsightsProvider({
|
|
43
|
+
async recordEvent(input) {
|
|
44
|
+
await assertSchemaReady();
|
|
45
|
+
return plugin.models.insights.recordEvent(input);
|
|
46
|
+
},
|
|
47
|
+
async listEvents(input) {
|
|
48
|
+
await assertSchemaReady();
|
|
49
|
+
return plugin.models.insights.listEvents(input);
|
|
50
|
+
},
|
|
51
|
+
async findLatestEvents(input) {
|
|
52
|
+
await assertSchemaReady();
|
|
53
|
+
return plugin.models.insights.findLatestEvents(input);
|
|
54
|
+
},
|
|
55
|
+
async countLatestEvents(input) {
|
|
42
56
|
await assertSchemaReady();
|
|
43
|
-
return plugin.models.
|
|
57
|
+
return plugin.models.insights.countLatestEvents(input);
|
|
44
58
|
},
|
|
45
|
-
async
|
|
59
|
+
async countEvents(input) {
|
|
46
60
|
await assertSchemaReady();
|
|
47
|
-
return plugin.models.
|
|
61
|
+
return plugin.models.insights.countEvents(input);
|
|
48
62
|
}
|
|
49
63
|
});
|
|
50
64
|
const apiKeys = createApiKeyManagement({
|
|
51
65
|
apiKeys: plugin.models.apiKeys,
|
|
52
66
|
beforeOperation: assertSchemaReady
|
|
53
67
|
});
|
|
54
|
-
const handlers = createHotUpdaterHandlers(core.api,
|
|
68
|
+
const handlers = createHotUpdaterHandlers(core.api, insights, clientAccess.type === "api-key" ? {
|
|
55
69
|
authenticate: (request) => authenticateApiKey({
|
|
56
70
|
apiKeys: plugin.models.apiKeys,
|
|
57
71
|
beforeLookup: assertSchemaReady,
|
|
@@ -62,7 +76,7 @@ function createHotUpdaterCore(options) {
|
|
|
62
76
|
} : void 0, downloadStorageObject);
|
|
63
77
|
const api = Object.assign({
|
|
64
78
|
adapterName: adapterCapabilities.adapterName ?? core.adapterName,
|
|
65
|
-
|
|
79
|
+
insights,
|
|
66
80
|
apiKeys,
|
|
67
81
|
handlers
|
|
68
82
|
}, core.api);
|
|
@@ -83,4 +97,4 @@ function createHotUpdater(options) {
|
|
|
83
97
|
return createHotUpdaterCore(options).api;
|
|
84
98
|
}
|
|
85
99
|
//#endregion
|
|
86
|
-
export { createHotUpdater, getHotUpdaterCoreMetadata };
|
|
100
|
+
export { createHotUpdater, createHotUpdaterCore, getHotUpdaterCoreMetadata, hotUpdaterCoreMetadata };
|
|
@@ -2,9 +2,9 @@ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
|
2
2
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
3
3
|
let _hot_updater_core = require("@hot-updater/core");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
|
-
node_crypto = require_runtime.__toESM(node_crypto);
|
|
5
|
+
node_crypto = require_runtime.__toESM(node_crypto, 1);
|
|
6
6
|
let node_path = require("node:path");
|
|
7
|
-
node_path = require_runtime.__toESM(node_path);
|
|
7
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
8
8
|
let node_util = require("node:util");
|
|
9
9
|
let node_zlib = require("node:zlib");
|
|
10
10
|
let _hot_updater_bsdiff = require("@hot-updater/bsdiff");
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Bundle, BundleRepository, StoragePluginWith } from "@hot-updater/plugin-core";
|
|
2
|
-
|
|
3
2
|
//#region src/db/createBundleDiff.d.ts
|
|
4
3
|
interface CreateBundleDiffInput {
|
|
5
4
|
baseBundleId: string;
|
|
@@ -12,9 +11,6 @@ interface CreateBundleDiffDependencies {
|
|
|
12
11
|
interface CreateBundleDiffOptions {
|
|
13
12
|
makePrimary?: boolean;
|
|
14
13
|
}
|
|
15
|
-
declare function createBundleDiff({
|
|
16
|
-
baseBundleId,
|
|
17
|
-
bundleId
|
|
18
|
-
}: CreateBundleDiffInput, deps: CreateBundleDiffDependencies, options?: CreateBundleDiffOptions): Promise<Bundle>;
|
|
14
|
+
declare function createBundleDiff({ baseBundleId, bundleId }: CreateBundleDiffInput, deps: CreateBundleDiffDependencies, options?: CreateBundleDiffOptions): Promise<Bundle>;
|
|
19
15
|
//#endregion
|
|
20
16
|
export { CreateBundleDiffDependencies, CreateBundleDiffInput, CreateBundleDiffOptions, createBundleDiff };
|