@hot-updater/server 1.0.0-rc.2 → 1.0.0-rc.21
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/LICENSE +12 -14
- package/dist/adapters/drizzle.cjs +32 -75
- package/dist/adapters/drizzle.d.cts +13 -5
- package/dist/adapters/drizzle.d.mts +13 -5
- package/dist/adapters/drizzle.mjs +32 -77
- package/dist/adapters/drizzleExecutor.cjs +114 -0
- package/dist/adapters/drizzleExecutor.d.cts +9 -0
- package/dist/adapters/drizzleExecutor.d.mts +9 -0
- package/dist/adapters/drizzleExecutor.mjs +112 -0
- package/dist/adapters/kysely.cjs +22 -31
- package/dist/adapters/kysely.d.cts +10 -6
- package/dist/adapters/kysely.d.mts +10 -6
- package/dist/adapters/kysely.mjs +23 -33
- package/dist/adapters/kyselyExecutor.cjs +32 -0
- package/dist/adapters/kyselyExecutor.d.cts +11 -0
- package/dist/adapters/kyselyExecutor.d.mts +11 -0
- package/dist/adapters/kyselyExecutor.mjs +32 -0
- package/dist/adapters/mongodb.cjs +18 -54
- package/dist/adapters/mongodb.d.cts +10 -5
- package/dist/adapters/mongodb.d.mts +10 -5
- package/dist/adapters/mongodb.mjs +19 -56
- package/dist/adapters/mongodbAdapter.cjs +192 -0
- package/dist/adapters/mongodbAdapter.d.cts +9 -0
- package/dist/adapters/mongodbAdapter.d.mts +9 -0
- package/dist/adapters/mongodbAdapter.mjs +191 -0
- package/dist/adapters/prisma.cjs +35 -267
- package/dist/adapters/prisma.d.cts +12 -7
- package/dist/adapters/prisma.d.mts +12 -7
- package/dist/adapters/prisma.mjs +36 -268
- package/dist/adapters/prismaExecutor.cjs +63 -0
- package/dist/adapters/prismaExecutor.mjs +63 -0
- package/dist/adapters/sqlProviders.cjs +14 -0
- package/dist/adapters/sqlProviders.mjs +14 -0
- package/dist/assembly/assemblePlugins.cjs +144 -0
- package/dist/assembly/assemblePlugins.d.cts +5 -0
- package/dist/assembly/assemblePlugins.d.mts +5 -0
- package/dist/assembly/assemblePlugins.mjs +139 -0
- package/dist/assembly/clientPlugins.cjs +88 -0
- package/dist/assembly/clientPlugins.mjs +88 -0
- package/dist/assembly/configError.d.cts +2 -0
- package/dist/assembly/configError.d.mts +2 -0
- package/dist/assembly/configError.mjs +2 -0
- package/dist/core/api.cjs +51 -0
- package/dist/core/api.d.cts +54 -0
- package/dist/core/api.d.mts +54 -0
- package/dist/core/api.mjs +51 -0
- package/dist/core/baseCandidates.cjs +28 -0
- package/dist/core/baseCandidates.mjs +28 -0
- package/dist/core/operations.cjs +351 -0
- package/dist/core/operations.mjs +351 -0
- package/dist/core/reads.cjs +273 -0
- package/dist/core/reads.d.cts +9 -0
- package/dist/core/reads.d.mts +9 -0
- package/dist/core/reads.mjs +267 -0
- package/dist/core/releases.cjs +193 -0
- package/dist/core/releases.mjs +190 -0
- package/dist/core/writes.cjs +96 -0
- package/dist/core/writes.mjs +91 -0
- package/dist/createHotUpdaterCore.cjs +72 -91
- package/dist/createHotUpdaterCore.d.cts +106 -38
- package/dist/createHotUpdaterCore.d.mts +106 -38
- package/dist/createHotUpdaterCore.mjs +74 -89
- package/dist/db/engineDrizzleSchema.cjs +53 -0
- package/dist/db/engineDrizzleSchema.mjs +53 -0
- package/dist/db/enginePrismaSchema.cjs +84 -0
- package/dist/db/enginePrismaSchema.d.cts +5 -0
- package/dist/db/enginePrismaSchema.d.mts +5 -0
- package/dist/db/enginePrismaSchema.mjs +83 -0
- package/dist/db/releaseCatalog.cjs +24 -47
- package/dist/db/releaseCatalog.mjs +20 -44
- package/dist/db/updateArtifacts.cjs +43 -72
- package/dist/db/updateArtifacts.mjs +40 -69
- package/dist/handler.cjs +59 -43
- package/dist/handler.d.cts +3 -5
- package/dist/handler.d.mts +3 -5
- package/dist/handler.mjs +61 -43
- package/dist/handlerAdminRoutes.cjs +374 -0
- package/dist/handlerAdminRoutes.mjs +372 -0
- package/dist/handlerParameters.cjs +6 -11
- package/dist/handlerParameters.mjs +6 -10
- package/dist/handlerReleaseCatalogRoutes.cjs +32 -15
- package/dist/handlerReleaseCatalogRoutes.mjs +32 -15
- package/dist/handlerTypes.d.cts +2 -55
- package/dist/handlerTypes.d.mts +2 -55
- package/dist/handlerVersionRoutes.cjs +20 -4
- package/dist/handlerVersionRoutes.mjs +20 -4
- package/dist/index.cjs +9 -9
- package/dist/index.d.cts +8 -9
- package/dist/index.d.mts +8 -9
- package/dist/index.mjs +3 -4
- package/dist/node.cjs +3 -6
- package/dist/node.d.cts +2 -2
- package/dist/node.d.mts +2 -2
- package/dist/node.mjs +4 -4
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugins/api-keys/index.cjs +9 -0
- package/dist/plugins/api-keys/index.d.cts +1 -0
- package/dist/plugins/api-keys/index.d.mts +1 -0
- package/dist/plugins/api-keys/index.mjs +2 -0
- package/dist/plugins/definePlugin.d.cts +2 -0
- package/dist/plugins/definePlugin.d.mts +2 -0
- package/dist/plugins/insights/index.cjs +9 -0
- package/dist/plugins/insights/index.d.cts +1 -0
- package/dist/plugins/insights/index.d.mts +1 -0
- package/dist/plugins/insights/index.mjs +2 -0
- package/dist/plugins/official.cjs +30 -0
- package/dist/plugins/official.mjs +29 -0
- package/dist/storageAccess.cjs +13 -10
- package/dist/storageAccess.mjs +13 -10
- package/dist/types/index.d.cts +2 -28
- package/dist/types/index.d.mts +2 -28
- package/package.json +26 -14
- package/dist/_virtual/_rolldown/runtime.cjs +0 -23
- package/dist/adapters/databaseConstraintErrors.cjs +0 -22
- package/dist/adapters/databaseConstraintErrors.mjs +0 -21
- package/dist/adapters/databasePluginUtils.cjs +0 -116
- package/dist/adapters/databasePluginUtils.mjs +0 -106
- package/dist/adapters/drizzleCrud.cjs +0 -311
- package/dist/adapters/drizzleCrud.mjs +0 -308
- package/dist/adapters/drizzleLazyDB.cjs +0 -106
- package/dist/adapters/drizzleLazyDB.mjs +0 -106
- package/dist/adapters/drizzleQuery.cjs +0 -69
- package/dist/adapters/drizzleQuery.mjs +0 -69
- package/dist/adapters/insightsTransaction.cjs +0 -22
- package/dist/adapters/insightsTransaction.mjs +0 -22
- package/dist/adapters/kyselyCrud.cjs +0 -305
- package/dist/adapters/kyselyCrud.mjs +0 -304
- package/dist/adapters/kyselyQuery.cjs +0 -56
- package/dist/adapters/kyselyQuery.mjs +0 -56
- package/dist/adapters/mongodbCollections.cjs +0 -38
- package/dist/adapters/mongodbCollections.mjs +0 -32
- package/dist/adapters/mongodbQuery.cjs +0 -94
- package/dist/adapters/mongodbQuery.mjs +0 -86
- package/dist/adapters/mongodbReads.cjs +0 -210
- package/dist/adapters/mongodbReads.mjs +0 -210
- package/dist/adapters/mongodbWrites.cjs +0 -148
- package/dist/adapters/mongodbWrites.mjs +0 -148
- package/dist/adapters/prismaQuery.cjs +0 -57
- package/dist/adapters/prismaQuery.mjs +0 -56
- package/dist/adapters/prismaRows.cjs +0 -219
- package/dist/adapters/prismaRows.mjs +0 -209
- package/dist/apiKeys.cjs +0 -143
- package/dist/apiKeys.d.cts +0 -49
- package/dist/apiKeys.d.mts +0 -49
- package/dist/apiKeys.mjs +0 -133
- package/dist/db/createBundleDiff.cjs +0 -144
- package/dist/db/createBundleDiff.d.cts +0 -16
- package/dist/db/createBundleDiff.d.mts +0 -16
- package/dist/db/createBundleDiff.mjs +0 -141
- package/dist/db/databasePluginCore.cjs +0 -137
- package/dist/db/databasePluginCore.mjs +0 -137
- package/dist/db/fixedMigrator.cjs +0 -79
- package/dist/db/fixedMigrator.mjs +0 -78
- package/dist/db/fixedMigratorMongo.cjs +0 -321
- package/dist/db/fixedMigratorMongo.mjs +0 -321
- package/dist/db/fixedMigratorShared.cjs +0 -23
- package/dist/db/fixedMigratorShared.mjs +0 -18
- package/dist/db/index.cjs +0 -27
- package/dist/db/index.d.cts +0 -12
- package/dist/db/index.d.mts +0 -12
- package/dist/db/index.mjs +0 -22
- package/dist/db/mongoMigrationExecution.cjs +0 -11
- package/dist/db/mongoMigrationExecution.mjs +0 -11
- package/dist/db/releaseCatalog.d.cts +0 -16
- package/dist/db/releaseCatalog.d.mts +0 -16
- package/dist/db/schema/insightsCollation.cjs +0 -14
- package/dist/db/schema/insightsCollation.mjs +0 -14
- package/dist/db/schema/mongodb.cjs +0 -25
- package/dist/db/schema/mongodb.mjs +0 -25
- package/dist/db/schema/registry.cjs +0 -21
- package/dist/db/schema/registry.mjs +0 -17
- package/dist/db/schema/sql.cjs +0 -105
- package/dist/db/schema/sql.mjs +0 -97
- package/dist/db/schema/sqlOperations.cjs +0 -27
- package/dist/db/schema/sqlOperations.mjs +0 -26
- package/dist/db/schemaGenerators.cjs +0 -275
- package/dist/db/schemaGenerators.mjs +0 -273
- package/dist/db/schemaReadiness.cjs +0 -26
- package/dist/db/schemaReadiness.d.cts +0 -9
- package/dist/db/schemaReadiness.d.mts +0 -9
- package/dist/db/schemaReadiness.mjs +0 -25
- package/dist/db/sqlMigrationExecution.cjs +0 -67
- package/dist/db/sqlMigrationExecution.mjs +0 -67
- package/dist/db/types.cjs +0 -22
- package/dist/db/types.d.cts +0 -118
- package/dist/db/types.d.mts +0 -118
- package/dist/db/types.mjs +0 -19
- package/dist/handlerBundleRoutes.cjs +0 -155
- package/dist/handlerBundleRoutes.mjs +0 -155
- package/dist/handlerReleaseManagementRoutes.cjs +0 -124
- package/dist/handlerReleaseManagementRoutes.mjs +0 -124
- package/dist/insights/domain.d.cts +0 -89
- package/dist/insights/domain.d.mts +0 -89
- package/dist/insights/errors.cjs +0 -15
- package/dist/insights/errors.mjs +0 -14
- package/dist/insights/eventInput.cjs +0 -155
- package/dist/insights/eventInput.mjs +0 -153
- package/dist/insights/provider.cjs +0 -314
- package/dist/insights/provider.d.cts +0 -6
- package/dist/insights/provider.d.mts +0 -6
- package/dist/insights/provider.mjs +0 -314
- package/dist/insights/queryInput.cjs +0 -93
- package/dist/insights/queryInput.mjs +0 -91
- package/dist/insights/routes.cjs +0 -58
- package/dist/insights/routes.mjs +0 -56
- package/dist/insights/types.d.cts +0 -32
- package/dist/insights/types.d.mts +0 -32
- package/dist/schema/dsl.cjs +0 -103
- package/dist/schema/dsl.mjs +0 -87
- package/dist/schema/index.cjs +0 -20
- package/dist/schema/index.mjs +0 -7
- package/dist/schema/settings.cjs +0 -9
- package/dist/schema/settings.mjs +0 -9
- package/dist/schema/types.cjs +0 -8
- package/dist/schema/types.mjs +0 -6
- package/dist/schema/v1_0_0.cjs +0 -406
- package/dist/schema/v1_0_0.mjs +0 -398
- package/src/adapters/databaseConstraintErrors.ts +0 -25
- package/src/adapters/databasePluginTestDatabase.ts +0 -120
- package/src/adapters/databasePluginUtils.spec.ts +0 -29
- package/src/adapters/databasePluginUtils.ts +0 -214
- package/src/adapters/drizzle.spec.ts +0 -336
- package/src/adapters/drizzle.ts +0 -162
- package/src/adapters/drizzleCrud.ts +0 -571
- package/src/adapters/drizzleLazyDB.ts +0 -271
- package/src/adapters/drizzleQuery.ts +0 -164
- package/src/adapters/insightsTransaction.ts +0 -32
- package/src/adapters/kysely.spec.ts +0 -174
- package/src/adapters/kysely.ts +0 -110
- package/src/adapters/kyselyCrud.ts +0 -709
- package/src/adapters/kyselyQuery.ts +0 -128
- package/src/adapters/mongodb.integration.spec.ts +0 -210
- package/src/adapters/mongodb.spec.ts +0 -209
- package/src/adapters/mongodb.ts +0 -138
- package/src/adapters/mongodbCollections.ts +0 -67
- package/src/adapters/mongodbQuery.ts +0 -255
- package/src/adapters/mongodbReads.ts +0 -380
- package/src/adapters/mongodbRows.spec.ts +0 -64
- package/src/adapters/mongodbRows.ts +0 -118
- package/src/adapters/mongodbTestClient.ts +0 -377
- package/src/adapters/mongodbTestFilter.ts +0 -169
- package/src/adapters/mongodbWrites.ts +0 -287
- package/src/adapters/prisma.spec.ts +0 -293
- package/src/adapters/prisma.ts +0 -565
- package/src/adapters/prismaQuery.ts +0 -129
- package/src/adapters/prismaRows.spec.ts +0 -98
- package/src/adapters/prismaRows.ts +0 -337
- package/src/adapters/prismaTestClient.ts +0 -413
- package/src/apiKeys.integration.spec.ts +0 -192
- package/src/apiKeys.spec.ts +0 -162
- package/src/apiKeys.ts +0 -220
- package/src/calculatePagination.ts +0 -34
- package/src/createHotUpdaterCore.ts +0 -246
- package/src/db/bundleRows.spec.ts +0 -164
- package/src/db/bundleRows.ts +0 -10
- package/src/db/createBundleDiff.spec.ts +0 -450
- package/src/db/createBundleDiff.ts +0 -364
- package/src/db/databasePluginCore.spec.ts +0 -62
- package/src/db/databasePluginCore.testFixtures.ts +0 -76
- package/src/db/databasePluginCore.ts +0 -185
- package/src/db/databasePluginUpdateCheck.bench.ts +0 -110
- package/src/db/fixedMigrator.spec.ts +0 -296
- package/src/db/fixedMigrator.ts +0 -147
- package/src/db/fixedMigratorMongo.spec.ts +0 -239
- package/src/db/fixedMigratorMongo.ts +0 -425
- package/src/db/fixedMigratorShared.ts +0 -36
- package/src/db/hotUpdaterSchema.ts +0 -5
- package/src/db/index.spec.ts +0 -1269
- package/src/db/index.ts +0 -47
- package/src/db/mongoMigrationExecution.ts +0 -23
- package/src/db/node-sqlite.d.ts +0 -25
- package/src/db/releaseCatalog.ts +0 -148
- package/src/db/schema/definitions.ts +0 -1
- package/src/db/schema/insightsCollation.ts +0 -21
- package/src/db/schema/mongodb.ts +0 -40
- package/src/db/schema/registry.ts +0 -55
- package/src/db/schema/sql.ts +0 -244
- package/src/db/schema/sqlOperations.ts +0 -63
- package/src/db/schema/types.ts +0 -1
- package/src/db/schemaGenerators.ts +0 -494
- package/src/db/schemaReadiness.ts +0 -34
- package/src/db/sqlMigrationExecution.spec.ts +0 -65
- package/src/db/sqlMigrationExecution.ts +0 -108
- package/src/db/types.spec.ts +0 -63
- package/src/db/types.ts +0 -228
- package/src/db/updateArtifacts.spec.ts +0 -359
- package/src/db/updateArtifacts.ts +0 -541
- package/src/handler-standalone.integration.spec.ts +0 -528
- package/src/handler.spec.ts +0 -190
- package/src/handler.testFixtures.ts +0 -30
- package/src/handler.ts +0 -245
- package/src/handlerAuth.integration.spec.ts +0 -33
- package/src/handlerBundleRoutes.ts +0 -259
- package/src/handlerErrors.ts +0 -7
- package/src/handlerManagement.spec.ts +0 -262
- package/src/handlerParameters.ts +0 -105
- package/src/handlerReleaseCatalogRoutes.ts +0 -154
- package/src/handlerReleaseManagementRoutes.ts +0 -213
- package/src/handlerTypes.ts +0 -96
- package/src/handlerVersionRoutes.ts +0 -12
- package/src/index.ts +0 -30
- package/src/insights/domain.ts +0 -103
- package/src/insights/errors.ts +0 -11
- package/src/insights/eventInput.ts +0 -225
- package/src/insights/insights.integration.spec.ts +0 -381
- package/src/insights/provider.spec.ts +0 -385
- package/src/insights/provider.ts +0 -591
- package/src/insights/queryInput.ts +0 -141
- package/src/insights/routes.ts +0 -104
- package/src/insights/types.ts +0 -55
- package/src/internalRouter.ts +0 -104
- package/src/node.spec.ts +0 -119
- package/src/node.ts +0 -111
- package/src/releaseCatalogRoutes.integration.spec.ts +0 -238
- package/src/runtime.spec.ts +0 -194
- package/src/runtime.testFixtures.ts +0 -71
- package/src/schema/dsl-all-versions.spec.ts +0 -37
- package/src/schema/dsl.ts +0 -187
- package/src/schema/index.ts +0 -10
- package/src/schema/settings.ts +0 -15
- package/src/schema/types.ts +0 -79
- package/src/schema/v1_0_0.spec.ts +0 -220
- package/src/schema/v1_0_0.ts +0 -529
- package/src/storageAccess.spec.ts +0 -152
- package/src/storageAccess.ts +0 -141
- package/src/types/index.ts +0 -34
- package/src/version.ts +0 -3
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ActiveInstallationWindow,
|
|
3
|
-
InsightsBundleSelection,
|
|
4
|
-
InsightsScope,
|
|
5
|
-
} from "./domain";
|
|
6
|
-
import { InsightsBadRequestError } from "./errors";
|
|
7
|
-
import type {
|
|
8
|
-
InsightsEventPageInput,
|
|
9
|
-
InsightsUserInstallationPageInput,
|
|
10
|
-
} from "./types";
|
|
11
|
-
|
|
12
|
-
const MAX_PAGE_LIMIT = 100;
|
|
13
|
-
const MAX_IDENTITY_LENGTH = 255;
|
|
14
|
-
const MAX_CURSOR_LENGTH = 8 * 1_024;
|
|
15
|
-
|
|
16
|
-
const readSingle = (url: URL, key: string): string | undefined => {
|
|
17
|
-
const values = url.searchParams.getAll(key);
|
|
18
|
-
if (values.length > 1) {
|
|
19
|
-
throw new InsightsBadRequestError(`Duplicate '${key}' query parameter.`);
|
|
20
|
-
}
|
|
21
|
-
return values[0];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const readPageLimit = (url: URL): number | undefined => {
|
|
25
|
-
const value = readSingle(url, "limit");
|
|
26
|
-
if (value === undefined) return undefined;
|
|
27
|
-
const limit = Number(value);
|
|
28
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_PAGE_LIMIT) {
|
|
29
|
-
throw new InsightsBadRequestError("Invalid 'limit' query parameter.");
|
|
30
|
-
}
|
|
31
|
-
return limit;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const readCursor = (url: URL): string | undefined => {
|
|
35
|
-
const cursor = readSingle(url, "cursor");
|
|
36
|
-
if (
|
|
37
|
-
cursor !== undefined &&
|
|
38
|
-
(cursor.length === 0 || cursor.length > MAX_CURSOR_LENGTH)
|
|
39
|
-
) {
|
|
40
|
-
throw new InsightsBadRequestError("Invalid 'cursor' query parameter.");
|
|
41
|
-
}
|
|
42
|
-
return cursor;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const readId = (
|
|
46
|
-
url: URL,
|
|
47
|
-
key: string,
|
|
48
|
-
maximumLength = MAX_IDENTITY_LENGTH,
|
|
49
|
-
): string => {
|
|
50
|
-
const value = readSingle(url, key);
|
|
51
|
-
if (
|
|
52
|
-
value === undefined ||
|
|
53
|
-
value.length === 0 ||
|
|
54
|
-
value.length > maximumLength
|
|
55
|
-
) {
|
|
56
|
-
throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
|
|
57
|
-
}
|
|
58
|
-
return value;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const readTimestamp = (url: URL, key: string): number | undefined => {
|
|
62
|
-
const raw = readSingle(url, key);
|
|
63
|
-
if (raw === undefined) return undefined;
|
|
64
|
-
const value = Number(raw);
|
|
65
|
-
if (!raw.length || !Number.isSafeInteger(value) || value < 0) {
|
|
66
|
-
throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
|
|
67
|
-
}
|
|
68
|
-
return value;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const readScope = (url: URL): InsightsScope => {
|
|
72
|
-
const platform = readSingle(url, "platform");
|
|
73
|
-
if (platform !== "ios" && platform !== "android") {
|
|
74
|
-
throw new InsightsBadRequestError("Invalid 'platform' query parameter.");
|
|
75
|
-
}
|
|
76
|
-
return { platform, channel: readId(url, "channel", 1_024) };
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const parseEventPageInput = (
|
|
80
|
-
request: Request,
|
|
81
|
-
): InsightsEventPageInput => {
|
|
82
|
-
const url = new URL(request.url);
|
|
83
|
-
const bundleFields = ["bundleId", "outcome", "platform", "channel"];
|
|
84
|
-
let bundle: InsightsBundleSelection | undefined;
|
|
85
|
-
if (bundleFields.some((key) => url.searchParams.has(key))) {
|
|
86
|
-
const outcome = readSingle(url, "outcome");
|
|
87
|
-
if (
|
|
88
|
-
outcome !== "applied" &&
|
|
89
|
-
outcome !== "recovered" &&
|
|
90
|
-
outcome !== "adopted"
|
|
91
|
-
) {
|
|
92
|
-
throw new InsightsBadRequestError("Invalid 'outcome' query parameter.");
|
|
93
|
-
}
|
|
94
|
-
bundle = {
|
|
95
|
-
...readScope(url),
|
|
96
|
-
bundleId: readId(url, "bundleId", 1_024),
|
|
97
|
-
outcome,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
beforeReceivedAtMs: readTimestamp(url, "beforeReceivedAtMs"),
|
|
102
|
-
sinceMs: readTimestamp(url, "sinceMs"),
|
|
103
|
-
cursor: readCursor(url),
|
|
104
|
-
limit: readPageLimit(url),
|
|
105
|
-
...(bundle === undefined ? {} : { bundle }),
|
|
106
|
-
};
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export const parseUserInstallationPageInput = (
|
|
110
|
-
request: Request,
|
|
111
|
-
): InsightsUserInstallationPageInput => {
|
|
112
|
-
const url = new URL(request.url);
|
|
113
|
-
const cursor = readCursor(url);
|
|
114
|
-
const limit = readPageLimit(url);
|
|
115
|
-
return {
|
|
116
|
-
userId: readId(url, "userId"),
|
|
117
|
-
...(cursor === undefined ? {} : { cursor }),
|
|
118
|
-
...(limit === undefined ? {} : { limit }),
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export const parseReportingOverviewInput = (
|
|
123
|
-
request: Request,
|
|
124
|
-
): InsightsScope & {
|
|
125
|
-
readonly window: ActiveInstallationWindow;
|
|
126
|
-
readonly bundleId?: string;
|
|
127
|
-
} => {
|
|
128
|
-
const url = new URL(request.url);
|
|
129
|
-
const window = readSingle(url, "window") ?? "30d";
|
|
130
|
-
if (window !== "24h" && window !== "7d" && window !== "30d") {
|
|
131
|
-
throw new InsightsBadRequestError("Invalid 'window' query parameter.");
|
|
132
|
-
}
|
|
133
|
-
const bundleId = readSingle(url, "bundleId");
|
|
134
|
-
return {
|
|
135
|
-
...readScope(url),
|
|
136
|
-
window,
|
|
137
|
-
...(bundleId === undefined
|
|
138
|
-
? {}
|
|
139
|
-
: { bundleId: readId(url, "bundleId", 1_024) }),
|
|
140
|
-
};
|
|
141
|
-
};
|
package/src/insights/routes.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import type { RouteHandler } from "../handlerTypes";
|
|
2
|
-
import {
|
|
3
|
-
InsightsBadRequestError,
|
|
4
|
-
InsightsPayloadTooLargeError,
|
|
5
|
-
} from "./errors";
|
|
6
|
-
import { parseBundleEventRequest } from "./eventInput";
|
|
7
|
-
import {
|
|
8
|
-
parseReportingOverviewInput,
|
|
9
|
-
parseEventPageInput,
|
|
10
|
-
parseUserInstallationPageInput,
|
|
11
|
-
} from "./queryInput";
|
|
12
|
-
import type { InsightsProvider } from "./types";
|
|
13
|
-
|
|
14
|
-
const json = (body: unknown, status: number): Response =>
|
|
15
|
-
Response.json(body, {
|
|
16
|
-
headers: { "cache-control": "private, no-store" },
|
|
17
|
-
status,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const requireParam = (
|
|
21
|
-
params: Readonly<Record<string, string>>,
|
|
22
|
-
key: string,
|
|
23
|
-
): string => {
|
|
24
|
-
const value = params[key];
|
|
25
|
-
if (value === undefined || value.length === 0) {
|
|
26
|
-
throw new InsightsBadRequestError(`Missing route parameter: ${key}`);
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
return decodeURIComponent(value);
|
|
30
|
-
} catch {
|
|
31
|
-
throw new InsightsBadRequestError(`Invalid route parameter: ${key}`);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const run = async (operation: () => Promise<Response>): Promise<Response> => {
|
|
36
|
-
try {
|
|
37
|
-
return await operation();
|
|
38
|
-
} catch (error) {
|
|
39
|
-
if (error instanceof InsightsBadRequestError) {
|
|
40
|
-
return json({ error: error.message }, 400);
|
|
41
|
-
}
|
|
42
|
-
if (error instanceof InsightsPayloadTooLargeError) {
|
|
43
|
-
return json({ error: error.message }, 413);
|
|
44
|
-
}
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const query = (operation: () => Promise<unknown>): Promise<Response> =>
|
|
50
|
-
run(async () => json(await operation(), 200));
|
|
51
|
-
|
|
52
|
-
export const createInsightsRouteHandlers = (
|
|
53
|
-
provider: InsightsProvider,
|
|
54
|
-
): Record<string, RouteHandler> => ({
|
|
55
|
-
appendBundleEvent: async (_params, request) =>
|
|
56
|
-
run(async () => {
|
|
57
|
-
await provider.appendBundleEvent(await parseBundleEventRequest(request));
|
|
58
|
-
return new Response(null, { status: 204 });
|
|
59
|
-
}),
|
|
60
|
-
getReportingOverview: (_params, request) =>
|
|
61
|
-
query(() =>
|
|
62
|
-
provider.getReportingOverview(parseReportingOverviewInput(request)),
|
|
63
|
-
),
|
|
64
|
-
getInstallation: (params) =>
|
|
65
|
-
run(async () => {
|
|
66
|
-
const installation = await provider.getInstallation({
|
|
67
|
-
installId: requireParam(params, "installId"),
|
|
68
|
-
});
|
|
69
|
-
return installation === null
|
|
70
|
-
? json({ error: "Installation not found" }, 404)
|
|
71
|
-
: json(installation, 200);
|
|
72
|
-
}),
|
|
73
|
-
listEvents: (_params, request) =>
|
|
74
|
-
query(() => provider.listEvents(parseEventPageInput(request))),
|
|
75
|
-
listInstallationEvents: (params, request) =>
|
|
76
|
-
query(() =>
|
|
77
|
-
provider.listInstallationEvents({
|
|
78
|
-
...parseEventPageInput(request),
|
|
79
|
-
installId: requireParam(params, "installId"),
|
|
80
|
-
}),
|
|
81
|
-
),
|
|
82
|
-
pageInstallationsByCurrentUserId: (_params, request) =>
|
|
83
|
-
query(() =>
|
|
84
|
-
provider.pageInstallationsByCurrentUserId(
|
|
85
|
-
parseUserInstallationPageInput(request),
|
|
86
|
-
),
|
|
87
|
-
),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
export const registerInsightsClientRoutes = (
|
|
91
|
-
add: (method: string, path: string, handler: string) => void,
|
|
92
|
-
): void => {
|
|
93
|
-
add("POST", "/events", "appendBundleEvent");
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export const registerInsightsAdminRoutes = (
|
|
97
|
-
add: (method: string, path: string, handler: string) => void,
|
|
98
|
-
): void => {
|
|
99
|
-
add("GET", "/events", "listEvents");
|
|
100
|
-
add("GET", "/overview", "getReportingOverview");
|
|
101
|
-
add("GET", "/installations", "pageInstallationsByCurrentUserId");
|
|
102
|
-
add("GET", "/installations/:installId/events", "listInstallationEvents");
|
|
103
|
-
add("GET", "/installations/:installId", "getInstallation");
|
|
104
|
-
};
|
package/src/insights/types.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ReportingOverview,
|
|
3
|
-
InsightsBundleSelection,
|
|
4
|
-
InsightsScope,
|
|
5
|
-
ActiveInstallationWindow,
|
|
6
|
-
CreateBundleEventRequest,
|
|
7
|
-
CursorPage,
|
|
8
|
-
EventCursorPage,
|
|
9
|
-
EventHistoryRow,
|
|
10
|
-
InstallationHistoryRow,
|
|
11
|
-
InstallationRow,
|
|
12
|
-
} from "./domain";
|
|
13
|
-
|
|
14
|
-
export type InsightsEventPageInput = {
|
|
15
|
-
readonly beforeReceivedAtMs?: number;
|
|
16
|
-
readonly sinceMs?: number;
|
|
17
|
-
readonly bundle?: InsightsBundleSelection;
|
|
18
|
-
readonly cursor?: string;
|
|
19
|
-
readonly limit?: number;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type InsightsInstallationEventPageInput = Omit<
|
|
23
|
-
InsightsEventPageInput,
|
|
24
|
-
"bundle"
|
|
25
|
-
> & {
|
|
26
|
-
readonly installId: string;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export type InsightsUserInstallationPageInput = {
|
|
30
|
-
readonly userId: string;
|
|
31
|
-
readonly cursor?: string;
|
|
32
|
-
readonly limit?: number;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type InsightsProvider = {
|
|
36
|
-
appendBundleEvent(input: CreateBundleEventRequest): Promise<void>;
|
|
37
|
-
listEvents(
|
|
38
|
-
input: InsightsEventPageInput,
|
|
39
|
-
): Promise<EventCursorPage<EventHistoryRow>>;
|
|
40
|
-
listInstallationEvents(
|
|
41
|
-
input: InsightsInstallationEventPageInput,
|
|
42
|
-
): Promise<EventCursorPage<InstallationHistoryRow>>;
|
|
43
|
-
getInstallation(input: {
|
|
44
|
-
readonly installId: string;
|
|
45
|
-
}): Promise<InstallationRow | null>;
|
|
46
|
-
pageInstallationsByCurrentUserId(
|
|
47
|
-
input: InsightsUserInstallationPageInput,
|
|
48
|
-
): Promise<CursorPage<InstallationRow>>;
|
|
49
|
-
getReportingOverview(
|
|
50
|
-
input: InsightsScope & {
|
|
51
|
-
readonly window: ActiveInstallationWindow;
|
|
52
|
-
readonly bundleId?: string;
|
|
53
|
-
},
|
|
54
|
-
): Promise<ReportingOverview>;
|
|
55
|
-
};
|
package/src/internalRouter.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
interface RouteRecord<T> {
|
|
2
|
-
data: T;
|
|
3
|
-
method: string;
|
|
4
|
-
paramNames: string[];
|
|
5
|
-
segments: string[];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface RouteMatch<T> {
|
|
9
|
-
data: T;
|
|
10
|
-
params: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface Router<T> {
|
|
14
|
-
routes: RouteRecord<T>[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const normalizePath = (path: string) => {
|
|
18
|
-
if (!path) {
|
|
19
|
-
return "/";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (path === "/") {
|
|
23
|
-
return path;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const withLeadingSlash = path.startsWith("/") ? path : `/${path}`;
|
|
27
|
-
return withLeadingSlash.endsWith("/")
|
|
28
|
-
? withLeadingSlash.slice(0, -1)
|
|
29
|
-
: withLeadingSlash;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const toSegments = (path: string) => {
|
|
33
|
-
const normalized = normalizePath(path);
|
|
34
|
-
return normalized === "/" ? [] : normalized.slice(1).split("/");
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export function createRouter<T>(): Router<T> {
|
|
38
|
-
return { routes: [] };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function addRoute<T>(
|
|
42
|
-
router: Router<T>,
|
|
43
|
-
method: string,
|
|
44
|
-
path: string,
|
|
45
|
-
data: T,
|
|
46
|
-
) {
|
|
47
|
-
const segments = toSegments(path);
|
|
48
|
-
const paramNames = segments
|
|
49
|
-
.filter((segment) => segment.startsWith(":"))
|
|
50
|
-
.map((segment) => segment.slice(1));
|
|
51
|
-
|
|
52
|
-
router.routes.push({
|
|
53
|
-
data,
|
|
54
|
-
method: method.toUpperCase(),
|
|
55
|
-
paramNames,
|
|
56
|
-
segments,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function findRoute<T>(
|
|
61
|
-
router: Router<T>,
|
|
62
|
-
method: string,
|
|
63
|
-
path: string,
|
|
64
|
-
): RouteMatch<T> | undefined {
|
|
65
|
-
const normalizedMethod = method.toUpperCase();
|
|
66
|
-
const pathSegments = toSegments(path);
|
|
67
|
-
|
|
68
|
-
for (const route of router.routes) {
|
|
69
|
-
if (route.method !== normalizedMethod) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (route.segments.length !== pathSegments.length) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const params: Record<string, string> = {};
|
|
78
|
-
let matched = true;
|
|
79
|
-
|
|
80
|
-
for (let index = 0; index < route.segments.length; index += 1) {
|
|
81
|
-
const routeSegment = route.segments[index];
|
|
82
|
-
const pathSegment = pathSegments[index];
|
|
83
|
-
|
|
84
|
-
if (routeSegment.startsWith(":")) {
|
|
85
|
-
params[routeSegment.slice(1)] = pathSegment;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (routeSegment !== pathSegment) {
|
|
90
|
-
matched = false;
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (matched) {
|
|
96
|
-
return {
|
|
97
|
-
data: route.data,
|
|
98
|
-
params,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
package/src/node.spec.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { toNodeHandler } from "./node";
|
|
4
|
-
|
|
5
|
-
describe("server node entry", () => {
|
|
6
|
-
it("converts a Web Request handler to Node middleware", async () => {
|
|
7
|
-
const middleware = toNodeHandler(async (request: Request) =>
|
|
8
|
-
Response.json({
|
|
9
|
-
method: request.method,
|
|
10
|
-
pathname: new URL(request.url).pathname,
|
|
11
|
-
}),
|
|
12
|
-
);
|
|
13
|
-
const headers = new Map<string, string | string[]>();
|
|
14
|
-
const response = {
|
|
15
|
-
body: "",
|
|
16
|
-
statusCode: 0,
|
|
17
|
-
status(code: number) {
|
|
18
|
-
this.statusCode = code;
|
|
19
|
-
return this;
|
|
20
|
-
},
|
|
21
|
-
setHeader(name: string, value: string | string[]) {
|
|
22
|
-
headers.set(name, value);
|
|
23
|
-
},
|
|
24
|
-
send(body: Uint8Array) {
|
|
25
|
-
this.body = new TextDecoder().decode(body);
|
|
26
|
-
},
|
|
27
|
-
end() {},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
await middleware(
|
|
31
|
-
{
|
|
32
|
-
method: "GET",
|
|
33
|
-
url: "/api/check",
|
|
34
|
-
headers: { host: "example.com" },
|
|
35
|
-
protocol: "https",
|
|
36
|
-
get: (name: string) => (name === "host" ? "example.com" : undefined),
|
|
37
|
-
},
|
|
38
|
-
response,
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
expect(response.statusCode).toBe(200);
|
|
42
|
-
expect(headers.get("content-type")).toContain("application/json");
|
|
43
|
-
expect(JSON.parse(response.body)).toEqual({
|
|
44
|
-
method: "GET",
|
|
45
|
-
pathname: "/api/check",
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("uses the mount-relative URL supplied by Express", async () => {
|
|
50
|
-
const middleware = toNodeHandler(async (request: Request) =>
|
|
51
|
-
Response.json({ pathname: new URL(request.url).pathname }),
|
|
52
|
-
);
|
|
53
|
-
const response = {
|
|
54
|
-
body: "",
|
|
55
|
-
statusCode: 0,
|
|
56
|
-
status(code: number) {
|
|
57
|
-
this.statusCode = code;
|
|
58
|
-
return this;
|
|
59
|
-
},
|
|
60
|
-
setHeader() {},
|
|
61
|
-
send(body: Uint8Array) {
|
|
62
|
-
this.body = new TextDecoder().decode(body);
|
|
63
|
-
},
|
|
64
|
-
end() {},
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
await middleware(
|
|
68
|
-
{
|
|
69
|
-
method: "GET",
|
|
70
|
-
url: "/channels",
|
|
71
|
-
headers: { host: "example.com" },
|
|
72
|
-
protocol: "https",
|
|
73
|
-
get: (name: string) => (name === "host" ? "example.com" : undefined),
|
|
74
|
-
},
|
|
75
|
-
response,
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
expect(JSON.parse(response.body)).toEqual({
|
|
79
|
-
pathname: "/channels",
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("preserves binary response bytes", async () => {
|
|
84
|
-
const bytes = new Uint8Array([0x50, 0x4b, 0x03, 0x04, 0xff, 0xfe, 0x00]);
|
|
85
|
-
const middleware = toNodeHandler(
|
|
86
|
-
async () =>
|
|
87
|
-
new Response(bytes, {
|
|
88
|
-
headers: { "content-type": "application/zip" },
|
|
89
|
-
}),
|
|
90
|
-
);
|
|
91
|
-
const response = {
|
|
92
|
-
body: new Uint8Array(),
|
|
93
|
-
statusCode: 0,
|
|
94
|
-
status(code: number) {
|
|
95
|
-
this.statusCode = code;
|
|
96
|
-
return this;
|
|
97
|
-
},
|
|
98
|
-
setHeader() {},
|
|
99
|
-
send(body: Uint8Array) {
|
|
100
|
-
this.body = new Uint8Array(body);
|
|
101
|
-
},
|
|
102
|
-
end() {},
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
await middleware(
|
|
106
|
-
{
|
|
107
|
-
method: "GET",
|
|
108
|
-
url: "/hot-updater/storage/token/signature",
|
|
109
|
-
headers: { host: "example.com" },
|
|
110
|
-
protocol: "https",
|
|
111
|
-
get: (name: string) => (name === "host" ? "example.com" : undefined),
|
|
112
|
-
},
|
|
113
|
-
response,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
expect(response.statusCode).toBe(200);
|
|
117
|
-
expect([...response.body]).toEqual([...bytes]);
|
|
118
|
-
});
|
|
119
|
-
});
|
package/src/node.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Node.js request/response types (compatible with Express, Connect, etc.)
|
|
3
|
-
*/
|
|
4
|
-
interface NodeRequest {
|
|
5
|
-
method?: string;
|
|
6
|
-
url?: string;
|
|
7
|
-
headers: Record<string, string | string[] | undefined>;
|
|
8
|
-
body?: unknown;
|
|
9
|
-
protocol?: string;
|
|
10
|
-
get?(name: string): string | undefined;
|
|
11
|
-
[key: string]: unknown;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface NodeResponse {
|
|
15
|
-
status(code: number): NodeResponse;
|
|
16
|
-
setHeader(name: string, value: string | string[]): void;
|
|
17
|
-
send(body: string | Uint8Array): void;
|
|
18
|
-
end(): void;
|
|
19
|
-
[key: string]: unknown;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export { HOT_UPDATER_SERVER_VERSION } from "./version";
|
|
23
|
-
export { HOT_UPDATER_INFRASTRUCTURE_GENERATION } from "./handlerVersionRoutes";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
27
|
-
* Works with Express, Connect, and other frameworks using Node.js req/res
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```typescript
|
|
31
|
-
* import { toNodeHandler } from "@hot-updater/server/node";
|
|
32
|
-
* import express from "express";
|
|
33
|
-
*
|
|
34
|
-
* const app = express();
|
|
35
|
-
*
|
|
36
|
-
* app.use(
|
|
37
|
-
* "/hot-updater/admin",
|
|
38
|
-
* bearerMiddleware,
|
|
39
|
-
* express.json({ limit: "1mb" }),
|
|
40
|
-
* toNodeHandler(hotUpdater.handlers.admin),
|
|
41
|
-
* );
|
|
42
|
-
* app.use(
|
|
43
|
-
* "/hot-updater",
|
|
44
|
-
* express.json({ limit: "1mb" }),
|
|
45
|
-
* toNodeHandler(hotUpdater.handlers.client),
|
|
46
|
-
* );
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
export function toNodeHandler(
|
|
50
|
-
handler: HotUpdaterHandler,
|
|
51
|
-
): (req: any, res: any, next?: any) => Promise<void> {
|
|
52
|
-
return async (req: NodeRequest, res: NodeResponse) => {
|
|
53
|
-
try {
|
|
54
|
-
// Build full URL
|
|
55
|
-
const protocol = req.protocol || "http";
|
|
56
|
-
const host = req.get?.("host") || "localhost";
|
|
57
|
-
const url = `${protocol}://${host}${req.url ?? "/"}`;
|
|
58
|
-
|
|
59
|
-
// Convert headers to Web Headers
|
|
60
|
-
const headers = new Headers();
|
|
61
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
62
|
-
if (value) {
|
|
63
|
-
headers.set(key, Array.isArray(value) ? value.join(", ") : value);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Handle request body
|
|
68
|
-
let body: string | undefined;
|
|
69
|
-
if (
|
|
70
|
-
req.method &&
|
|
71
|
-
req.method !== "GET" &&
|
|
72
|
-
req.method !== "HEAD" &&
|
|
73
|
-
req.body
|
|
74
|
-
) {
|
|
75
|
-
// If body is already parsed (by express.json()), stringify it
|
|
76
|
-
body = JSON.stringify(req.body);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Create Web Request
|
|
80
|
-
const webRequest = new globalThis.Request(url, {
|
|
81
|
-
method: req.method || "GET",
|
|
82
|
-
headers,
|
|
83
|
-
body,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const response = await handler(webRequest);
|
|
87
|
-
|
|
88
|
-
// Set status code
|
|
89
|
-
res.status(response.status);
|
|
90
|
-
|
|
91
|
-
// Set headers
|
|
92
|
-
response.headers.forEach((value, key) => {
|
|
93
|
-
res.setHeader(key, value);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Send response body
|
|
97
|
-
const responseBody = Buffer.from(await response.arrayBuffer());
|
|
98
|
-
if (responseBody.length > 0) {
|
|
99
|
-
res.send(responseBody);
|
|
100
|
-
} else {
|
|
101
|
-
res.end();
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
// Handle errors gracefully
|
|
105
|
-
console.error("Hot Updater handler error:", error);
|
|
106
|
-
res.status(500);
|
|
107
|
-
res.send("Internal Server Error");
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
import type { HotUpdaterHandler } from "./handlerTypes";
|