@hot-updater/server 0.28.0 → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/drizzle.cjs +7 -7
- package/dist/adapters/drizzle.mjs +2 -0
- package/dist/adapters/kysely.cjs +7 -7
- package/dist/adapters/kysely.mjs +2 -0
- package/dist/adapters/mongodb.cjs +7 -7
- package/dist/adapters/mongodb.mjs +2 -0
- package/dist/adapters/prisma.cjs +7 -7
- package/dist/adapters/prisma.mjs +2 -0
- package/dist/calculatePagination.cjs +1 -3
- package/dist/{calculatePagination.js → calculatePagination.mjs} +1 -2
- package/dist/db/index.cjs +24 -15
- package/dist/db/index.d.cts +12 -9
- package/dist/db/index.d.mts +30 -0
- package/dist/db/index.mjs +45 -0
- package/dist/db/ormCore.cjs +247 -138
- package/dist/db/ormCore.d.cts +35 -17
- package/dist/db/ormCore.d.mts +44 -0
- package/dist/db/ormCore.mjs +386 -0
- package/dist/db/pluginCore.cjs +145 -40
- package/dist/db/pluginCore.mjs +176 -0
- package/dist/db/types.cjs +1 -3
- package/dist/db/types.d.cts +14 -21
- package/dist/db/types.d.mts +24 -0
- package/dist/db/{types.js → types.mjs} +1 -2
- package/dist/handler.cjs +117 -48
- package/dist/handler.d.cts +28 -18
- package/dist/handler.d.mts +47 -0
- package/dist/handler.mjs +217 -0
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +4 -0
- package/dist/internalRouter.cjs +54 -0
- package/dist/internalRouter.mjs +52 -0
- package/dist/node.cjs +2 -3
- package/dist/node.d.cts +0 -1
- package/dist/{node.d.ts → node.d.mts} +1 -2
- package/dist/{node.js → node.mjs} +1 -2
- package/dist/route.cjs +7 -0
- package/dist/route.mjs +7 -0
- package/dist/runtime.cjs +42 -0
- package/dist/runtime.d.cts +21 -0
- package/dist/runtime.d.mts +21 -0
- package/dist/runtime.mjs +40 -0
- package/dist/schema/v0_21_0.cjs +1 -5
- package/dist/schema/{v0_21_0.js → v0_21_0.mjs} +1 -3
- package/dist/schema/v0_29_0.cjs +24 -0
- package/dist/schema/v0_29_0.mjs +24 -0
- package/dist/types/{index.d.ts → index.d.mts} +1 -1
- package/package.json +18 -18
- package/src/db/index.spec.ts +64 -29
- package/src/db/index.ts +55 -35
- package/src/db/ormCore.ts +438 -210
- package/src/db/ormUpdateCheck.bench.ts +261 -0
- package/src/db/pluginCore.ts +298 -49
- package/src/db/pluginUpdateCheck.bench.ts +250 -0
- package/src/db/types.ts +52 -27
- package/src/{handler-standalone-integration.spec.ts → handler-standalone.integration.spec.ts} +106 -0
- package/src/handler.spec.ts +156 -0
- package/src/handler.ts +296 -77
- package/src/internalRouter.ts +104 -0
- package/src/route.ts +7 -0
- package/src/runtime.spec.ts +277 -0
- package/src/runtime.ts +121 -0
- package/src/schema/v0_29_0.ts +26 -0
- package/dist/_virtual/rolldown_runtime.cjs +0 -25
- package/dist/adapters/drizzle.js +0 -3
- package/dist/adapters/kysely.js +0 -3
- package/dist/adapters/mongodb.js +0 -3
- package/dist/adapters/prisma.js +0 -3
- package/dist/db/index.d.ts +0 -27
- package/dist/db/index.js +0 -36
- package/dist/db/ormCore.d.ts +0 -26
- package/dist/db/ormCore.js +0 -273
- package/dist/db/pluginCore.js +0 -69
- package/dist/db/types.d.ts +0 -31
- package/dist/handler.d.ts +0 -37
- package/dist/handler.js +0 -146
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -5
- /package/dist/adapters/{drizzle.d.ts → drizzle.d.mts} +0 -0
- /package/dist/adapters/{kysely.d.ts → kysely.d.mts} +0 -0
- /package/dist/adapters/{mongodb.d.ts → mongodb.d.mts} +0 -0
- /package/dist/adapters/{prisma.d.ts → prisma.d.mts} +0 -0
package/dist/handler.mjs
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { addRoute, createRouter, findRoute } from "./internalRouter.mjs";
|
|
2
|
+
//#region src/handler.ts
|
|
3
|
+
var HandlerBadRequestError = class extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "HandlerBadRequestError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
const handleVersion = async () => {
|
|
10
|
+
return new Response(JSON.stringify({ version: "0.29.1" }), {
|
|
11
|
+
status: 200,
|
|
12
|
+
headers: { "Content-Type": "application/json" }
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const decodeMaybe = (value) => {
|
|
16
|
+
if (value === void 0) return void 0;
|
|
17
|
+
try {
|
|
18
|
+
return decodeURIComponent(value);
|
|
19
|
+
} catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const isPlatform = (value) => {
|
|
24
|
+
return value === "ios" || value === "android";
|
|
25
|
+
};
|
|
26
|
+
const requireRouteParam = (params, key) => {
|
|
27
|
+
const value = params[key];
|
|
28
|
+
if (!value) throw new HandlerBadRequestError(`Missing route parameter: ${key}`);
|
|
29
|
+
return value;
|
|
30
|
+
};
|
|
31
|
+
const requirePlatformParam = (params) => {
|
|
32
|
+
const platform = requireRouteParam(params, "platform");
|
|
33
|
+
if (!isPlatform(platform)) throw new HandlerBadRequestError(`Invalid platform: ${platform}. Expected 'ios' or 'android'.`);
|
|
34
|
+
return platform;
|
|
35
|
+
};
|
|
36
|
+
const requireBundlePatchPayload = (payload, bundleId) => {
|
|
37
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new HandlerBadRequestError("Invalid bundle payload");
|
|
38
|
+
const bundlePatch = payload;
|
|
39
|
+
if (bundlePatch.id !== void 0 && bundlePatch.id !== bundleId) throw new HandlerBadRequestError("Bundle id mismatch");
|
|
40
|
+
const { id: _ignoredId, ...rest } = bundlePatch;
|
|
41
|
+
return rest;
|
|
42
|
+
};
|
|
43
|
+
const handleFingerprintUpdateWithCohort = async (params, _request, api, context) => {
|
|
44
|
+
const platform = requirePlatformParam(params);
|
|
45
|
+
const fingerprintHash = requireRouteParam(params, "fingerprintHash");
|
|
46
|
+
const channel = requireRouteParam(params, "channel");
|
|
47
|
+
const minBundleId = requireRouteParam(params, "minBundleId");
|
|
48
|
+
const bundleId = requireRouteParam(params, "bundleId");
|
|
49
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
50
|
+
_updateStrategy: "fingerprint",
|
|
51
|
+
platform,
|
|
52
|
+
fingerprintHash,
|
|
53
|
+
channel,
|
|
54
|
+
minBundleId,
|
|
55
|
+
bundleId,
|
|
56
|
+
cohort: decodeMaybe(params.cohort)
|
|
57
|
+
}, context);
|
|
58
|
+
return new Response(JSON.stringify(updateInfo), {
|
|
59
|
+
status: 200,
|
|
60
|
+
headers: { "Content-Type": "application/json" }
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const handleAppVersionUpdateWithCohort = async (params, _request, api, context) => {
|
|
64
|
+
const platform = requirePlatformParam(params);
|
|
65
|
+
const appVersion = requireRouteParam(params, "appVersion");
|
|
66
|
+
const channel = requireRouteParam(params, "channel");
|
|
67
|
+
const minBundleId = requireRouteParam(params, "minBundleId");
|
|
68
|
+
const bundleId = requireRouteParam(params, "bundleId");
|
|
69
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
70
|
+
_updateStrategy: "appVersion",
|
|
71
|
+
platform,
|
|
72
|
+
appVersion,
|
|
73
|
+
channel,
|
|
74
|
+
minBundleId,
|
|
75
|
+
bundleId,
|
|
76
|
+
cohort: decodeMaybe(params.cohort)
|
|
77
|
+
}, context);
|
|
78
|
+
return new Response(JSON.stringify(updateInfo), {
|
|
79
|
+
status: 200,
|
|
80
|
+
headers: { "Content-Type": "application/json" }
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const handleGetBundle = async (params, _request, api, context) => {
|
|
84
|
+
const bundleId = requireRouteParam(params, "id");
|
|
85
|
+
const bundle = await api.getBundleById(bundleId, context);
|
|
86
|
+
if (!bundle) return new Response(JSON.stringify({ error: "Bundle not found" }), {
|
|
87
|
+
status: 404,
|
|
88
|
+
headers: { "Content-Type": "application/json" }
|
|
89
|
+
});
|
|
90
|
+
return new Response(JSON.stringify(bundle), {
|
|
91
|
+
status: 200,
|
|
92
|
+
headers: { "Content-Type": "application/json" }
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
const handleGetBundles = async (_params, request, api, context) => {
|
|
96
|
+
const url = new URL(request.url);
|
|
97
|
+
const channel = url.searchParams.get("channel") ?? void 0;
|
|
98
|
+
const platform = url.searchParams.get("platform");
|
|
99
|
+
const limit = Number(url.searchParams.get("limit")) || 50;
|
|
100
|
+
const offset = Number(url.searchParams.get("offset")) || 0;
|
|
101
|
+
if (platform !== null && !isPlatform(platform)) throw new HandlerBadRequestError(`Invalid platform: ${platform}. Expected 'ios' or 'android'.`);
|
|
102
|
+
const result = await api.getBundles({
|
|
103
|
+
where: {
|
|
104
|
+
...channel && { channel },
|
|
105
|
+
...platform && { platform }
|
|
106
|
+
},
|
|
107
|
+
limit,
|
|
108
|
+
offset
|
|
109
|
+
}, context);
|
|
110
|
+
return new Response(JSON.stringify(result.data), {
|
|
111
|
+
status: 200,
|
|
112
|
+
headers: { "Content-Type": "application/json" }
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
const handleCreateBundles = async (_params, request, api, context) => {
|
|
116
|
+
const body = await request.json();
|
|
117
|
+
const bundles = Array.isArray(body) ? body : [body];
|
|
118
|
+
for (const bundle of bundles) await api.insertBundle(bundle, context);
|
|
119
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
120
|
+
status: 201,
|
|
121
|
+
headers: { "Content-Type": "application/json" }
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
const handleUpdateBundle = async (params, request, api, context) => {
|
|
125
|
+
const bundleId = requireRouteParam(params, "id");
|
|
126
|
+
const body = await request.json();
|
|
127
|
+
const bundlePatch = requireBundlePatchPayload(Array.isArray(body) ? body[0] : body, bundleId);
|
|
128
|
+
await api.updateBundleById(bundleId, bundlePatch, context);
|
|
129
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
130
|
+
status: 200,
|
|
131
|
+
headers: { "Content-Type": "application/json" }
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
const handleDeleteBundle = async (params, _request, api, context) => {
|
|
135
|
+
const bundleId = requireRouteParam(params, "id");
|
|
136
|
+
await api.deleteBundleById(bundleId, context);
|
|
137
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
138
|
+
status: 200,
|
|
139
|
+
headers: { "Content-Type": "application/json" }
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const handleGetChannels = async (_params, _request, api, context) => {
|
|
143
|
+
const channels = await api.getChannels(context);
|
|
144
|
+
return new Response(JSON.stringify({ channels }), {
|
|
145
|
+
status: 200,
|
|
146
|
+
headers: { "Content-Type": "application/json" }
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const routes = {
|
|
150
|
+
version: handleVersion,
|
|
151
|
+
fingerprintUpdateWithCohort: handleFingerprintUpdateWithCohort,
|
|
152
|
+
appVersionUpdateWithCohort: handleAppVersionUpdateWithCohort,
|
|
153
|
+
getBundle: handleGetBundle,
|
|
154
|
+
getBundles: handleGetBundles,
|
|
155
|
+
createBundles: handleCreateBundles,
|
|
156
|
+
updateBundle: handleUpdateBundle,
|
|
157
|
+
deleteBundle: handleDeleteBundle,
|
|
158
|
+
getChannels: handleGetChannels
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Creates a Web Standard Request handler for Hot Updater API
|
|
162
|
+
* This handler is framework-agnostic and works with any runtime that
|
|
163
|
+
* supports standard Request/Response objects.
|
|
164
|
+
*/
|
|
165
|
+
function createHandler(api, options = {}) {
|
|
166
|
+
const basePath = options.basePath ?? "/api";
|
|
167
|
+
const updateCheckEnabled = options.routes?.updateCheck ?? true;
|
|
168
|
+
const bundlesEnabled = options.routes?.bundles ?? true;
|
|
169
|
+
const router = createRouter();
|
|
170
|
+
if (updateCheckEnabled) {
|
|
171
|
+
addRoute(router, "GET", "/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId", "fingerprintUpdateWithCohort");
|
|
172
|
+
addRoute(router, "GET", "/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId/:cohort", "fingerprintUpdateWithCohort");
|
|
173
|
+
addRoute(router, "GET", "/app-version/:platform/:appVersion/:channel/:minBundleId/:bundleId", "appVersionUpdateWithCohort");
|
|
174
|
+
addRoute(router, "GET", "/app-version/:platform/:appVersion/:channel/:minBundleId/:bundleId/:cohort", "appVersionUpdateWithCohort");
|
|
175
|
+
}
|
|
176
|
+
if (bundlesEnabled) {
|
|
177
|
+
addRoute(router, "GET", "/version", "version");
|
|
178
|
+
addRoute(router, "GET", "/api/bundles/channels", "getChannels");
|
|
179
|
+
addRoute(router, "GET", "/api/bundles/:id", "getBundle");
|
|
180
|
+
addRoute(router, "GET", "/api/bundles", "getBundles");
|
|
181
|
+
addRoute(router, "POST", "/api/bundles", "createBundles");
|
|
182
|
+
addRoute(router, "PATCH", "/api/bundles/:id", "updateBundle");
|
|
183
|
+
addRoute(router, "DELETE", "/api/bundles/:id", "deleteBundle");
|
|
184
|
+
}
|
|
185
|
+
return async (request, context) => {
|
|
186
|
+
try {
|
|
187
|
+
const path = new URL(request.url).pathname;
|
|
188
|
+
const method = request.method;
|
|
189
|
+
const match = findRoute(router, method, path.startsWith(basePath) ? path.slice(basePath.length) : path);
|
|
190
|
+
if (!match) return new Response(JSON.stringify({ error: "Not found" }), {
|
|
191
|
+
status: 404,
|
|
192
|
+
headers: { "Content-Type": "application/json" }
|
|
193
|
+
});
|
|
194
|
+
const handler = routes[match.data];
|
|
195
|
+
if (!handler) return new Response(JSON.stringify({ error: "Handler not found" }), {
|
|
196
|
+
status: 500,
|
|
197
|
+
headers: { "Content-Type": "application/json" }
|
|
198
|
+
});
|
|
199
|
+
return await handler(match.params || {}, request, api, context);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
if (error instanceof HandlerBadRequestError) return new Response(JSON.stringify({ error: error.message }), {
|
|
202
|
+
status: 400,
|
|
203
|
+
headers: { "Content-Type": "application/json" }
|
|
204
|
+
});
|
|
205
|
+
console.error("Hot Updater handler error:", error);
|
|
206
|
+
return new Response(JSON.stringify({
|
|
207
|
+
error: "Internal server error",
|
|
208
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
209
|
+
}), {
|
|
210
|
+
status: 500,
|
|
211
|
+
headers: { "Content-Type": "application/json" }
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { createHandler };
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_handler = require("./handler.cjs");
|
|
3
|
+
const require_ormCore = require("./db/ormCore.cjs");
|
|
4
|
+
const require_index = require("./db/index.cjs");
|
|
5
5
|
exports.HotUpdaterDB = require_ormCore.HotUpdaterDB;
|
|
6
6
|
exports.createHandler = require_handler.createHandler;
|
|
7
|
-
exports.createHotUpdater = require_index.createHotUpdater;
|
|
7
|
+
exports.createHotUpdater = require_index.createHotUpdater;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Bundle, PaginatedResult, PaginationInfo, PaginationOptions } from "./types/index.cjs";
|
|
2
|
+
import { HandlerAPI, HandlerOptions, HandlerRoutes, createHandler } from "./handler.cjs";
|
|
2
3
|
import { HotUpdaterClient, HotUpdaterDB, Migrator } from "./db/ormCore.cjs";
|
|
3
|
-
import { HotUpdaterAPI, createHotUpdater } from "./db/index.cjs";
|
|
4
|
-
|
|
5
|
-
export { Bundle, HandlerAPI, HandlerOptions, HotUpdaterAPI, HotUpdaterClient, HotUpdaterDB, Migrator, PaginatedResult, PaginationInfo, PaginationOptions, createHandler, createHotUpdater };
|
|
4
|
+
import { CreateHotUpdaterOptions, HotUpdaterAPI, createHotUpdater } from "./db/index.cjs";
|
|
5
|
+
export { Bundle, CreateHotUpdaterOptions, HandlerAPI, HandlerOptions, HandlerRoutes, HotUpdaterAPI, HotUpdaterClient, HotUpdaterDB, Migrator, PaginatedResult, PaginationInfo, PaginationOptions, createHandler, createHotUpdater };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Bundle, PaginatedResult, PaginationInfo, PaginationOptions } from "./types/index.mjs";
|
|
2
|
+
import { HandlerAPI, HandlerOptions, HandlerRoutes, createHandler } from "./handler.mjs";
|
|
3
|
+
import { HotUpdaterClient, HotUpdaterDB, Migrator } from "./db/ormCore.mjs";
|
|
4
|
+
import { CreateHotUpdaterOptions, HotUpdaterAPI, createHotUpdater } from "./db/index.mjs";
|
|
5
|
+
export { Bundle, CreateHotUpdaterOptions, HandlerAPI, HandlerOptions, HandlerRoutes, HotUpdaterAPI, HotUpdaterClient, HotUpdaterDB, Migrator, PaginatedResult, PaginationInfo, PaginationOptions, createHandler, createHotUpdater };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//#region src/internalRouter.ts
|
|
2
|
+
const normalizePath = (path) => {
|
|
3
|
+
if (!path) return "/";
|
|
4
|
+
if (path === "/") return path;
|
|
5
|
+
const withLeadingSlash = path.startsWith("/") ? path : `/${path}`;
|
|
6
|
+
return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
7
|
+
};
|
|
8
|
+
const toSegments = (path) => {
|
|
9
|
+
const normalized = normalizePath(path);
|
|
10
|
+
return normalized === "/" ? [] : normalized.slice(1).split("/");
|
|
11
|
+
};
|
|
12
|
+
function createRouter() {
|
|
13
|
+
return { routes: [] };
|
|
14
|
+
}
|
|
15
|
+
function addRoute(router, method, path, data) {
|
|
16
|
+
const segments = toSegments(path);
|
|
17
|
+
const paramNames = segments.filter((segment) => segment.startsWith(":")).map((segment) => segment.slice(1));
|
|
18
|
+
router.routes.push({
|
|
19
|
+
data,
|
|
20
|
+
method: method.toUpperCase(),
|
|
21
|
+
paramNames,
|
|
22
|
+
segments
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function findRoute(router, method, path) {
|
|
26
|
+
const normalizedMethod = method.toUpperCase();
|
|
27
|
+
const pathSegments = toSegments(path);
|
|
28
|
+
for (const route of router.routes) {
|
|
29
|
+
if (route.method !== normalizedMethod) continue;
|
|
30
|
+
if (route.segments.length !== pathSegments.length) continue;
|
|
31
|
+
const params = {};
|
|
32
|
+
let matched = true;
|
|
33
|
+
for (let index = 0; index < route.segments.length; index += 1) {
|
|
34
|
+
const routeSegment = route.segments[index];
|
|
35
|
+
const pathSegment = pathSegments[index];
|
|
36
|
+
if (routeSegment.startsWith(":")) {
|
|
37
|
+
params[routeSegment.slice(1)] = pathSegment;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (routeSegment !== pathSegment) {
|
|
41
|
+
matched = false;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (matched) return {
|
|
46
|
+
data: route.data,
|
|
47
|
+
params
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
exports.addRoute = addRoute;
|
|
53
|
+
exports.createRouter = createRouter;
|
|
54
|
+
exports.findRoute = findRoute;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/internalRouter.ts
|
|
2
|
+
const normalizePath = (path) => {
|
|
3
|
+
if (!path) return "/";
|
|
4
|
+
if (path === "/") return path;
|
|
5
|
+
const withLeadingSlash = path.startsWith("/") ? path : `/${path}`;
|
|
6
|
+
return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
7
|
+
};
|
|
8
|
+
const toSegments = (path) => {
|
|
9
|
+
const normalized = normalizePath(path);
|
|
10
|
+
return normalized === "/" ? [] : normalized.slice(1).split("/");
|
|
11
|
+
};
|
|
12
|
+
function createRouter() {
|
|
13
|
+
return { routes: [] };
|
|
14
|
+
}
|
|
15
|
+
function addRoute(router, method, path, data) {
|
|
16
|
+
const segments = toSegments(path);
|
|
17
|
+
const paramNames = segments.filter((segment) => segment.startsWith(":")).map((segment) => segment.slice(1));
|
|
18
|
+
router.routes.push({
|
|
19
|
+
data,
|
|
20
|
+
method: method.toUpperCase(),
|
|
21
|
+
paramNames,
|
|
22
|
+
segments
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function findRoute(router, method, path) {
|
|
26
|
+
const normalizedMethod = method.toUpperCase();
|
|
27
|
+
const pathSegments = toSegments(path);
|
|
28
|
+
for (const route of router.routes) {
|
|
29
|
+
if (route.method !== normalizedMethod) continue;
|
|
30
|
+
if (route.segments.length !== pathSegments.length) continue;
|
|
31
|
+
const params = {};
|
|
32
|
+
let matched = true;
|
|
33
|
+
for (let index = 0; index < route.segments.length; index += 1) {
|
|
34
|
+
const routeSegment = route.segments[index];
|
|
35
|
+
const pathSegment = pathSegments[index];
|
|
36
|
+
if (routeSegment.startsWith(":")) {
|
|
37
|
+
params[routeSegment.slice(1)] = pathSegment;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (routeSegment !== pathSegment) {
|
|
41
|
+
matched = false;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (matched) return {
|
|
46
|
+
data: route.data,
|
|
47
|
+
params
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { addRoute, createRouter, findRoute };
|
package/dist/node.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
//#region src/node.ts
|
|
3
3
|
/**
|
|
4
4
|
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
@@ -46,6 +46,5 @@ function toNodeHandler(hotUpdater) {
|
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
|
|
50
49
|
//#endregion
|
|
51
|
-
exports.toNodeHandler = toNodeHandler;
|
|
50
|
+
exports.toNodeHandler = toNodeHandler;
|
package/dist/node.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { HotUpdaterAPI } from "./db/index.
|
|
1
|
+
import { HotUpdaterAPI } from "./db/index.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/node.d.ts
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
7
6
|
* Works with Express, Connect, and other frameworks using Node.js req/res
|
package/dist/route.cjs
ADDED
package/dist/route.mjs
ADDED
package/dist/runtime.cjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_handler = require("./handler.cjs");
|
|
3
|
+
const require_route = require("./route.cjs");
|
|
4
|
+
const require_pluginCore = require("./db/pluginCore.cjs");
|
|
5
|
+
const require_types = require("./db/types.cjs");
|
|
6
|
+
//#region src/runtime.ts
|
|
7
|
+
function createHotUpdater(options) {
|
|
8
|
+
const basePath = require_route.normalizeBasePath(options.basePath ?? "/api");
|
|
9
|
+
const storagePlugins = (options.storages ?? options.storagePlugins ?? []).map((plugin) => typeof plugin === "function" ? plugin() : plugin);
|
|
10
|
+
const resolveStoragePluginUrl = async (storageUri, context) => {
|
|
11
|
+
if (!storageUri) return null;
|
|
12
|
+
const protocol = new URL(storageUri).protocol.replace(":", "");
|
|
13
|
+
if (protocol === "http" || protocol === "https") return storageUri;
|
|
14
|
+
const plugin = storagePlugins.find((item) => item.supportedProtocol === protocol);
|
|
15
|
+
if (!plugin) throw new Error(`No storage plugin for protocol: ${protocol}`);
|
|
16
|
+
const { fileUrl } = await plugin.getDownloadUrl(storageUri, context);
|
|
17
|
+
if (!fileUrl) throw new Error("Storage plugin returned empty fileUrl");
|
|
18
|
+
return fileUrl;
|
|
19
|
+
};
|
|
20
|
+
if (!require_types.isDatabasePluginFactory(options.database) && !require_types.isDatabasePlugin(options.database)) throw new Error("@hot-updater/server/runtime only supports database plugins.");
|
|
21
|
+
const core = require_pluginCore.createPluginDatabaseCore(require_types.isDatabasePluginFactory(options.database) ? options.database() : options.database, resolveStoragePluginUrl);
|
|
22
|
+
const api = {
|
|
23
|
+
...core.api,
|
|
24
|
+
handler: require_handler.createHandler(core.api, {
|
|
25
|
+
basePath,
|
|
26
|
+
routes: options.routes
|
|
27
|
+
}),
|
|
28
|
+
adapterName: core.adapterName
|
|
29
|
+
};
|
|
30
|
+
const handler = (request, context, ...extraArgs) => {
|
|
31
|
+
if (extraArgs.length > 0) return api.handler(request);
|
|
32
|
+
return api.handler(request, context);
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
...api,
|
|
36
|
+
basePath,
|
|
37
|
+
handler
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
exports.createHandler = require_handler.createHandler;
|
|
42
|
+
exports.createHotUpdater = createHotUpdater;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HandlerRoutes, createHandler } from "./handler.cjs";
|
|
2
|
+
import { DatabaseAPI, DatabaseAdapter, StoragePluginFactory } from "./db/types.cjs";
|
|
3
|
+
import { HotUpdaterContext, StoragePlugin } from "@hot-updater/plugin-core";
|
|
4
|
+
|
|
5
|
+
//#region src/runtime.d.ts
|
|
6
|
+
type HotUpdaterAPI<TContext = unknown> = DatabaseAPI<TContext> & {
|
|
7
|
+
basePath: string;
|
|
8
|
+
handler: (request: Request, context?: HotUpdaterContext<TContext>) => Promise<Response>;
|
|
9
|
+
adapterName: string;
|
|
10
|
+
};
|
|
11
|
+
interface CreateHotUpdaterOptions<TContext = unknown> {
|
|
12
|
+
database: DatabaseAdapter<TContext>;
|
|
13
|
+
storages?: (StoragePlugin<TContext> | StoragePluginFactory<TContext>)[];
|
|
14
|
+
storagePlugins?: (StoragePlugin<TContext> | StoragePluginFactory<TContext>)[];
|
|
15
|
+
basePath?: string;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
routes?: HandlerRoutes;
|
|
18
|
+
}
|
|
19
|
+
declare function createHotUpdater<TContext = unknown>(options: CreateHotUpdaterOptions<TContext>): HotUpdaterAPI<TContext>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { CreateHotUpdaterOptions, HotUpdaterAPI, createHandler, createHotUpdater };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HandlerRoutes, createHandler } from "./handler.mjs";
|
|
2
|
+
import { DatabaseAPI, DatabaseAdapter, StoragePluginFactory } from "./db/types.mjs";
|
|
3
|
+
import { HotUpdaterContext, StoragePlugin } from "@hot-updater/plugin-core";
|
|
4
|
+
|
|
5
|
+
//#region src/runtime.d.ts
|
|
6
|
+
type HotUpdaterAPI<TContext = unknown> = DatabaseAPI<TContext> & {
|
|
7
|
+
basePath: string;
|
|
8
|
+
handler: (request: Request, context?: HotUpdaterContext<TContext>) => Promise<Response>;
|
|
9
|
+
adapterName: string;
|
|
10
|
+
};
|
|
11
|
+
interface CreateHotUpdaterOptions<TContext = unknown> {
|
|
12
|
+
database: DatabaseAdapter<TContext>;
|
|
13
|
+
storages?: (StoragePlugin<TContext> | StoragePluginFactory<TContext>)[];
|
|
14
|
+
storagePlugins?: (StoragePlugin<TContext> | StoragePluginFactory<TContext>)[];
|
|
15
|
+
basePath?: string;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
routes?: HandlerRoutes;
|
|
18
|
+
}
|
|
19
|
+
declare function createHotUpdater<TContext = unknown>(options: CreateHotUpdaterOptions<TContext>): HotUpdaterAPI<TContext>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { CreateHotUpdaterOptions, HotUpdaterAPI, createHandler, createHotUpdater };
|
package/dist/runtime.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createHandler } from "./handler.mjs";
|
|
2
|
+
import { normalizeBasePath } from "./route.mjs";
|
|
3
|
+
import { createPluginDatabaseCore } from "./db/pluginCore.mjs";
|
|
4
|
+
import { isDatabasePlugin, isDatabasePluginFactory } from "./db/types.mjs";
|
|
5
|
+
//#region src/runtime.ts
|
|
6
|
+
function createHotUpdater(options) {
|
|
7
|
+
const basePath = normalizeBasePath(options.basePath ?? "/api");
|
|
8
|
+
const storagePlugins = (options.storages ?? options.storagePlugins ?? []).map((plugin) => typeof plugin === "function" ? plugin() : plugin);
|
|
9
|
+
const resolveStoragePluginUrl = async (storageUri, context) => {
|
|
10
|
+
if (!storageUri) return null;
|
|
11
|
+
const protocol = new URL(storageUri).protocol.replace(":", "");
|
|
12
|
+
if (protocol === "http" || protocol === "https") return storageUri;
|
|
13
|
+
const plugin = storagePlugins.find((item) => item.supportedProtocol === protocol);
|
|
14
|
+
if (!plugin) throw new Error(`No storage plugin for protocol: ${protocol}`);
|
|
15
|
+
const { fileUrl } = await plugin.getDownloadUrl(storageUri, context);
|
|
16
|
+
if (!fileUrl) throw new Error("Storage plugin returned empty fileUrl");
|
|
17
|
+
return fileUrl;
|
|
18
|
+
};
|
|
19
|
+
if (!isDatabasePluginFactory(options.database) && !isDatabasePlugin(options.database)) throw new Error("@hot-updater/server/runtime only supports database plugins.");
|
|
20
|
+
const core = createPluginDatabaseCore(isDatabasePluginFactory(options.database) ? options.database() : options.database, resolveStoragePluginUrl);
|
|
21
|
+
const api = {
|
|
22
|
+
...core.api,
|
|
23
|
+
handler: createHandler(core.api, {
|
|
24
|
+
basePath,
|
|
25
|
+
routes: options.routes
|
|
26
|
+
}),
|
|
27
|
+
adapterName: core.adapterName
|
|
28
|
+
};
|
|
29
|
+
const handler = (request, context, ...extraArgs) => {
|
|
30
|
+
if (extraArgs.length > 0) return api.handler(request);
|
|
31
|
+
return api.handler(request, context);
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
...api,
|
|
35
|
+
basePath,
|
|
36
|
+
handler
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { createHandler, createHotUpdater };
|
package/dist/schema/v0_21_0.cjs
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
1
|
let fumadb_schema = require("fumadb/schema");
|
|
3
|
-
fumadb_schema = require_rolldown_runtime.__toESM(fumadb_schema);
|
|
4
|
-
|
|
5
2
|
//#region src/schema/v0_21_0.ts
|
|
6
3
|
const v0_21_0 = (0, fumadb_schema.schema)({
|
|
7
4
|
version: "0.21.0",
|
|
@@ -21,6 +18,5 @@ const v0_21_0 = (0, fumadb_schema.schema)({
|
|
|
21
18
|
}) },
|
|
22
19
|
relations: {}
|
|
23
20
|
});
|
|
24
|
-
|
|
25
21
|
//#endregion
|
|
26
|
-
exports.v0_21_0 = v0_21_0;
|
|
22
|
+
exports.v0_21_0 = v0_21_0;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { column, idColumn, schema, table } from "fumadb/schema";
|
|
2
|
-
|
|
3
2
|
//#region src/schema/v0_21_0.ts
|
|
4
3
|
const v0_21_0 = schema({
|
|
5
4
|
version: "0.21.0",
|
|
@@ -19,6 +18,5 @@ const v0_21_0 = schema({
|
|
|
19
18
|
}) },
|
|
20
19
|
relations: {}
|
|
21
20
|
});
|
|
22
|
-
|
|
23
21
|
//#endregion
|
|
24
|
-
export { v0_21_0 };
|
|
22
|
+
export { v0_21_0 };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
let fumadb_schema = require("fumadb/schema");
|
|
2
|
+
//#region src/schema/v0_29_0.ts
|
|
3
|
+
const v0_29_0 = (0, fumadb_schema.schema)({
|
|
4
|
+
version: "0.29.0",
|
|
5
|
+
tables: { bundles: (0, fumadb_schema.table)("bundles", {
|
|
6
|
+
id: (0, fumadb_schema.idColumn)("id", "uuid"),
|
|
7
|
+
platform: (0, fumadb_schema.column)("platform", "string"),
|
|
8
|
+
should_force_update: (0, fumadb_schema.column)("should_force_update", "bool"),
|
|
9
|
+
enabled: (0, fumadb_schema.column)("enabled", "bool"),
|
|
10
|
+
file_hash: (0, fumadb_schema.column)("file_hash", "string"),
|
|
11
|
+
git_commit_hash: (0, fumadb_schema.column)("git_commit_hash", "string").nullable(),
|
|
12
|
+
message: (0, fumadb_schema.column)("message", "string").nullable(),
|
|
13
|
+
channel: (0, fumadb_schema.column)("channel", "string"),
|
|
14
|
+
storage_uri: (0, fumadb_schema.column)("storage_uri", "string"),
|
|
15
|
+
target_app_version: (0, fumadb_schema.column)("target_app_version", "string").nullable(),
|
|
16
|
+
fingerprint_hash: (0, fumadb_schema.column)("fingerprint_hash", "string").nullable(),
|
|
17
|
+
metadata: (0, fumadb_schema.column)("metadata", "json"),
|
|
18
|
+
rollout_cohort_count: (0, fumadb_schema.column)("rollout_cohort_count", "integer").defaultTo(1e3),
|
|
19
|
+
target_cohorts: (0, fumadb_schema.column)("target_cohorts", "json").nullable()
|
|
20
|
+
}) },
|
|
21
|
+
relations: {}
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.v0_29_0 = v0_29_0;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { column, idColumn, schema, table } from "fumadb/schema";
|
|
2
|
+
//#region src/schema/v0_29_0.ts
|
|
3
|
+
const v0_29_0 = schema({
|
|
4
|
+
version: "0.29.0",
|
|
5
|
+
tables: { bundles: table("bundles", {
|
|
6
|
+
id: idColumn("id", "uuid"),
|
|
7
|
+
platform: column("platform", "string"),
|
|
8
|
+
should_force_update: column("should_force_update", "bool"),
|
|
9
|
+
enabled: column("enabled", "bool"),
|
|
10
|
+
file_hash: column("file_hash", "string"),
|
|
11
|
+
git_commit_hash: column("git_commit_hash", "string").nullable(),
|
|
12
|
+
message: column("message", "string").nullable(),
|
|
13
|
+
channel: column("channel", "string"),
|
|
14
|
+
storage_uri: column("storage_uri", "string"),
|
|
15
|
+
target_app_version: column("target_app_version", "string").nullable(),
|
|
16
|
+
fingerprint_hash: column("fingerprint_hash", "string").nullable(),
|
|
17
|
+
metadata: column("metadata", "json"),
|
|
18
|
+
rollout_cohort_count: column("rollout_cohort_count", "integer").defaultTo(1e3),
|
|
19
|
+
target_cohorts: column("target_cohorts", "json").nullable()
|
|
20
|
+
}) },
|
|
21
|
+
relations: {}
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { v0_29_0 };
|