@hot-updater/server 0.21.5 → 0.21.7
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/db/index.cjs +8 -6
- package/dist/db/index.d.cts +8 -5
- package/dist/db/index.d.ts +8 -5
- package/dist/db/index.js +8 -6
- package/dist/handler.cjs +120 -111
- package/dist/handler.js +119 -111
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/node.cjs +51 -0
- package/dist/node.d.cts +25 -0
- package/dist/node.d.ts +25 -0
- package/dist/node.js +50 -0
- package/dist/schema/{v1.cjs → v0_21_0.cjs} +5 -5
- package/dist/schema/{v1.js → v0_21_0.js} +5 -5
- package/dist/types/index.d.cts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +15 -13
- package/src/db/index.spec.ts +14 -14
- package/src/db/index.ts +14 -5
- package/src/handler-standalone-integration.spec.ts +39 -33
- package/src/handler.ts +168 -153
- package/src/node.ts +104 -0
- package/src/schema/{v1.ts → v0_21_0.ts} +3 -3
- package/src/types/index.ts +1 -0
- package/dist/adapters/typeorm.cjs +0 -9
- package/dist/adapters/typeorm.d.cts +0 -1
- package/dist/adapters/typeorm.d.ts +0 -1
- package/dist/adapters/typeorm.js +0 -3
- package/src/adapters/typeorm.ts +0 -1
package/dist/node.cjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/node.ts
|
|
3
|
+
/**
|
|
4
|
+
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
5
|
+
* Works with Express, Connect, and other frameworks using Node.js req/res
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { toNodeHandler } from "@hot-updater/server/node";
|
|
10
|
+
* import express from "express";
|
|
11
|
+
*
|
|
12
|
+
* const app = express();
|
|
13
|
+
*
|
|
14
|
+
* // Mount middleware
|
|
15
|
+
* app.use(express.json());
|
|
16
|
+
*
|
|
17
|
+
* // Mount hot-updater handler
|
|
18
|
+
* app.all("/hot-updater/*", toNodeHandler(hotUpdater));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function toNodeHandler(hotUpdater) {
|
|
22
|
+
return async (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const url = `${req.protocol || "http"}://${req.get?.("host") || "localhost"}${req.url || "/"}`;
|
|
25
|
+
const headers = new Headers();
|
|
26
|
+
for (const [key, value] of Object.entries(req.headers)) if (value) headers.set(key, Array.isArray(value) ? value.join(", ") : value);
|
|
27
|
+
let body;
|
|
28
|
+
if (req.method && req.method !== "GET" && req.method !== "HEAD" && req.body) body = JSON.stringify(req.body);
|
|
29
|
+
const webRequest = new globalThis.Request(url, {
|
|
30
|
+
method: req.method || "GET",
|
|
31
|
+
headers,
|
|
32
|
+
body
|
|
33
|
+
});
|
|
34
|
+
const response = await hotUpdater.handler(webRequest);
|
|
35
|
+
res.status(response.status);
|
|
36
|
+
response.headers.forEach((value, key) => {
|
|
37
|
+
res.setHeader(key, value);
|
|
38
|
+
});
|
|
39
|
+
const text = await response.text();
|
|
40
|
+
if (text) res.send(text);
|
|
41
|
+
else res.end();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Hot Updater handler error:", error);
|
|
44
|
+
res.status(500);
|
|
45
|
+
res.send("Internal Server Error");
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
exports.toNodeHandler = toNodeHandler;
|
package/dist/node.d.cts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { HotUpdaterAPI } from "./db/index.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/node.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
7
|
+
* Works with Express, Connect, and other frameworks using Node.js req/res
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { toNodeHandler } from "@hot-updater/server/node";
|
|
12
|
+
* import express from "express";
|
|
13
|
+
*
|
|
14
|
+
* const app = express();
|
|
15
|
+
*
|
|
16
|
+
* // Mount middleware
|
|
17
|
+
* app.use(express.json());
|
|
18
|
+
*
|
|
19
|
+
* // Mount hot-updater handler
|
|
20
|
+
* app.all("/hot-updater/*", toNodeHandler(hotUpdater));
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare function toNodeHandler(hotUpdater: HotUpdaterAPI): (req: any, res: any, next?: any) => Promise<void>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { toNodeHandler };
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { HotUpdaterAPI } from "./db/index.js";
|
|
2
|
+
|
|
3
|
+
//#region src/node.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
7
|
+
* Works with Express, Connect, and other frameworks using Node.js req/res
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { toNodeHandler } from "@hot-updater/server/node";
|
|
12
|
+
* import express from "express";
|
|
13
|
+
*
|
|
14
|
+
* const app = express();
|
|
15
|
+
*
|
|
16
|
+
* // Mount middleware
|
|
17
|
+
* app.use(express.json());
|
|
18
|
+
*
|
|
19
|
+
* // Mount hot-updater handler
|
|
20
|
+
* app.all("/hot-updater/*", toNodeHandler(hotUpdater));
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare function toNodeHandler(hotUpdater: HotUpdaterAPI): (req: any, res: any, next?: any) => Promise<void>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { toNodeHandler };
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/node.ts
|
|
2
|
+
/**
|
|
3
|
+
* Converts a Hot Updater handler to a Node.js-compatible middleware
|
|
4
|
+
* Works with Express, Connect, and other frameworks using Node.js req/res
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { toNodeHandler } from "@hot-updater/server/node";
|
|
9
|
+
* import express from "express";
|
|
10
|
+
*
|
|
11
|
+
* const app = express();
|
|
12
|
+
*
|
|
13
|
+
* // Mount middleware
|
|
14
|
+
* app.use(express.json());
|
|
15
|
+
*
|
|
16
|
+
* // Mount hot-updater handler
|
|
17
|
+
* app.all("/hot-updater/*", toNodeHandler(hotUpdater));
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function toNodeHandler(hotUpdater) {
|
|
21
|
+
return async (req, res) => {
|
|
22
|
+
try {
|
|
23
|
+
const url = `${req.protocol || "http"}://${req.get?.("host") || "localhost"}${req.url || "/"}`;
|
|
24
|
+
const headers = new Headers();
|
|
25
|
+
for (const [key, value] of Object.entries(req.headers)) if (value) headers.set(key, Array.isArray(value) ? value.join(", ") : value);
|
|
26
|
+
let body;
|
|
27
|
+
if (req.method && req.method !== "GET" && req.method !== "HEAD" && req.body) body = JSON.stringify(req.body);
|
|
28
|
+
const webRequest = new globalThis.Request(url, {
|
|
29
|
+
method: req.method || "GET",
|
|
30
|
+
headers,
|
|
31
|
+
body
|
|
32
|
+
});
|
|
33
|
+
const response = await hotUpdater.handler(webRequest);
|
|
34
|
+
res.status(response.status);
|
|
35
|
+
response.headers.forEach((value, key) => {
|
|
36
|
+
res.setHeader(key, value);
|
|
37
|
+
});
|
|
38
|
+
const text = await response.text();
|
|
39
|
+
if (text) res.send(text);
|
|
40
|
+
else res.end();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("Hot Updater handler error:", error);
|
|
43
|
+
res.status(500);
|
|
44
|
+
res.send("Internal Server Error");
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { toNodeHandler };
|
|
@@ -2,11 +2,11 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
|
2
2
|
let fumadb_schema = require("fumadb/schema");
|
|
3
3
|
fumadb_schema = require_rolldown_runtime.__toESM(fumadb_schema);
|
|
4
4
|
|
|
5
|
-
//#region src/schema/
|
|
6
|
-
const
|
|
7
|
-
version: "
|
|
5
|
+
//#region src/schema/v0_21_0.ts
|
|
6
|
+
const v0_21_0 = (0, fumadb_schema.schema)({
|
|
7
|
+
version: "0.21.0",
|
|
8
8
|
tables: { bundles: (0, fumadb_schema.table)("bundles", {
|
|
9
|
-
id: (0, fumadb_schema.idColumn)("id", "
|
|
9
|
+
id: (0, fumadb_schema.idColumn)("id", "uuid"),
|
|
10
10
|
platform: (0, fumadb_schema.column)("platform", "string"),
|
|
11
11
|
should_force_update: (0, fumadb_schema.column)("should_force_update", "bool"),
|
|
12
12
|
enabled: (0, fumadb_schema.column)("enabled", "bool"),
|
|
@@ -23,4 +23,4 @@ const v1 = (0, fumadb_schema.schema)({
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
//#endregion
|
|
26
|
-
exports.
|
|
26
|
+
exports.v0_21_0 = v0_21_0;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { column, idColumn, schema, table } from "fumadb/schema";
|
|
2
2
|
|
|
3
|
-
//#region src/schema/
|
|
4
|
-
const
|
|
5
|
-
version: "
|
|
3
|
+
//#region src/schema/v0_21_0.ts
|
|
4
|
+
const v0_21_0 = schema({
|
|
5
|
+
version: "0.21.0",
|
|
6
6
|
tables: { bundles: table("bundles", {
|
|
7
|
-
id: idColumn("id", "
|
|
7
|
+
id: idColumn("id", "uuid"),
|
|
8
8
|
platform: column("platform", "string"),
|
|
9
9
|
should_force_update: column("should_force_update", "bool"),
|
|
10
10
|
enabled: column("enabled", "bool"),
|
|
@@ -21,4 +21,4 @@ const v1 = schema({
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
//#endregion
|
|
24
|
-
export {
|
|
24
|
+
export { v0_21_0 };
|
package/dist/types/index.d.cts
CHANGED
package/dist/types/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/server",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"import": "./dist/adapters/prisma.js",
|
|
29
29
|
"require": "./dist/adapters/prisma.cjs"
|
|
30
30
|
},
|
|
31
|
-
"./
|
|
32
|
-
"import": "./dist/
|
|
33
|
-
"require": "./dist/
|
|
31
|
+
"./node": {
|
|
32
|
+
"import": "./dist/node.js",
|
|
33
|
+
"require": "./dist/node.cjs"
|
|
34
34
|
},
|
|
35
35
|
"./package.json": "./package.json"
|
|
36
36
|
},
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"fumadb": "0.
|
|
49
|
+
"fumadb": "0.2.0",
|
|
50
|
+
"rou3": "0.7.9",
|
|
50
51
|
"semver": "^7.7.2",
|
|
51
|
-
"@hot-updater/core": "0.21.
|
|
52
|
-
"@hot-updater/plugin-core": "0.21.
|
|
52
|
+
"@hot-updater/core": "0.21.7",
|
|
53
|
+
"@hot-updater/plugin-core": "0.21.7"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@electric-sql/pglite": "^0.2.17",
|
|
@@ -59,12 +60,13 @@
|
|
|
59
60
|
"kysely": "^0.28.5",
|
|
60
61
|
"kysely-pglite-dialect": "^1.2.0",
|
|
61
62
|
"msw": "^2.7.0",
|
|
62
|
-
"
|
|
63
|
-
"@hot-updater/
|
|
64
|
-
"@hot-updater/cloudflare": "0.21.
|
|
65
|
-
"@hot-updater/
|
|
66
|
-
"@hot-updater/
|
|
67
|
-
"@hot-updater/test-utils": "0.21.
|
|
63
|
+
"uuidv7": "^1.0.2",
|
|
64
|
+
"@hot-updater/aws": "0.21.7",
|
|
65
|
+
"@hot-updater/cloudflare": "0.21.7",
|
|
66
|
+
"@hot-updater/firebase": "0.21.7",
|
|
67
|
+
"@hot-updater/standalone": "0.21.7",
|
|
68
|
+
"@hot-updater/test-utils": "0.21.7",
|
|
69
|
+
"@hot-updater/supabase": "0.21.7"
|
|
68
70
|
},
|
|
69
71
|
"scripts": {
|
|
70
72
|
"build": "tsdown",
|
package/src/db/index.spec.ts
CHANGED
|
@@ -19,14 +19,14 @@ import {
|
|
|
19
19
|
it,
|
|
20
20
|
vi,
|
|
21
21
|
} from "vitest";
|
|
22
|
-
import {
|
|
22
|
+
import { createHotUpdater } from "./index";
|
|
23
23
|
|
|
24
24
|
describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
25
25
|
const db = new PGlite();
|
|
26
26
|
|
|
27
27
|
const kysely = new Kysely({ dialect: new PGliteDialect(db) });
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const hotUpdater = createHotUpdater({
|
|
30
30
|
database: kyselyAdapter({
|
|
31
31
|
db: kysely,
|
|
32
32
|
provider: "postgresql",
|
|
@@ -58,7 +58,7 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
58
58
|
|
|
59
59
|
beforeAll(async () => {
|
|
60
60
|
// Initialize FumaDB schema to latest (creates tables under the hood)
|
|
61
|
-
const migrator =
|
|
61
|
+
const migrator = hotUpdater.createMigrator();
|
|
62
62
|
const result = await migrator.migrateToLatest({
|
|
63
63
|
mode: "from-schema",
|
|
64
64
|
updateSettings: true,
|
|
@@ -81,9 +81,9 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
81
81
|
): Promise<UpdateInfo | null> => {
|
|
82
82
|
// Insert fixtures via the server API to exercise its types + mapping
|
|
83
83
|
for (const b of bundles) {
|
|
84
|
-
await
|
|
84
|
+
await hotUpdater.insertBundle(b);
|
|
85
85
|
}
|
|
86
|
-
return
|
|
86
|
+
return hotUpdater.getUpdateInfo(options);
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
setupGetUpdateInfoTestSuite({ getUpdateInfo });
|
|
@@ -114,9 +114,9 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
114
114
|
fingerprintHash: null,
|
|
115
115
|
};
|
|
116
116
|
|
|
117
|
-
await
|
|
117
|
+
await hotUpdater.insertBundle(bundle);
|
|
118
118
|
|
|
119
|
-
const updateInfo = await
|
|
119
|
+
const updateInfo = await hotUpdater.getAppUpdateInfo({
|
|
120
120
|
appVersion: "1.0.0",
|
|
121
121
|
bundleId: NIL_UUID,
|
|
122
122
|
platform: "ios",
|
|
@@ -144,9 +144,9 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
144
144
|
fingerprintHash: null,
|
|
145
145
|
};
|
|
146
146
|
|
|
147
|
-
await
|
|
147
|
+
await hotUpdater.insertBundle(bundle);
|
|
148
148
|
|
|
149
|
-
const updateInfo = await
|
|
149
|
+
const updateInfo = await hotUpdater.getAppUpdateInfo({
|
|
150
150
|
appVersion: "1.0.0",
|
|
151
151
|
bundleId: NIL_UUID,
|
|
152
152
|
platform: "ios",
|
|
@@ -174,9 +174,9 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
174
174
|
fingerprintHash: null,
|
|
175
175
|
};
|
|
176
176
|
|
|
177
|
-
await
|
|
177
|
+
await hotUpdater.insertBundle(bundle);
|
|
178
178
|
|
|
179
|
-
const updateInfo = await
|
|
179
|
+
const updateInfo = await hotUpdater.getAppUpdateInfo({
|
|
180
180
|
appVersion: "1.0.0",
|
|
181
181
|
bundleId: NIL_UUID,
|
|
182
182
|
platform: "ios",
|
|
@@ -188,7 +188,7 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
188
188
|
});
|
|
189
189
|
|
|
190
190
|
it("returns null when no update is available", async () => {
|
|
191
|
-
const updateInfo = await
|
|
191
|
+
const updateInfo = await hotUpdater.getAppUpdateInfo({
|
|
192
192
|
appVersion: "99.0.0",
|
|
193
193
|
bundleId: NIL_UUID,
|
|
194
194
|
platform: "ios",
|
|
@@ -213,9 +213,9 @@ describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
|
213
213
|
fingerprintHash: "fingerprint123",
|
|
214
214
|
};
|
|
215
215
|
|
|
216
|
-
await
|
|
216
|
+
await hotUpdater.insertBundle(bundle);
|
|
217
217
|
|
|
218
|
-
const updateInfo = await
|
|
218
|
+
const updateInfo = await hotUpdater.getAppUpdateInfo({
|
|
219
219
|
fingerprintHash: "fingerprint123",
|
|
220
220
|
bundleId: NIL_UUID,
|
|
221
221
|
platform: "ios",
|
package/src/db/index.ts
CHANGED
|
@@ -14,12 +14,12 @@ import type { InferFumaDB } from "fumadb";
|
|
|
14
14
|
import { fumadb } from "fumadb";
|
|
15
15
|
import { calculatePagination } from "../calculatePagination";
|
|
16
16
|
import { createHandler } from "../handler";
|
|
17
|
-
import {
|
|
17
|
+
import { v0_21_0 } from "../schema/v0_21_0";
|
|
18
18
|
import type { PaginationInfo } from "../types";
|
|
19
19
|
|
|
20
20
|
export const HotUpdaterDB = fumadb({
|
|
21
|
-
namespace: "
|
|
22
|
-
schemas: [
|
|
21
|
+
namespace: "hot_updater",
|
|
22
|
+
schemas: [v0_21_0],
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
export type HotUpdaterClient = InferFumaDB<typeof HotUpdaterDB>;
|
|
@@ -43,9 +43,14 @@ export interface DatabaseAPI {
|
|
|
43
43
|
|
|
44
44
|
export type HotUpdaterAPI = DatabaseAPI & {
|
|
45
45
|
handler: (request: Request) => Promise<Response>;
|
|
46
|
-
|
|
46
|
+
|
|
47
|
+
adapterName: string;
|
|
48
|
+
createMigrator: () => Migrator;
|
|
49
|
+
generateSchema: HotUpdaterClient["generateSchema"];
|
|
47
50
|
};
|
|
48
51
|
|
|
52
|
+
export type Migrator = ReturnType<HotUpdaterClient["createMigrator"]>;
|
|
53
|
+
|
|
49
54
|
export type StoragePluginFactory = (args: { cwd: string }) => StoragePlugin;
|
|
50
55
|
|
|
51
56
|
export interface HotUpdaterOptions {
|
|
@@ -55,7 +60,7 @@ export interface HotUpdaterOptions {
|
|
|
55
60
|
cwd?: string;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
export function
|
|
63
|
+
export function createHotUpdater(options: HotUpdaterOptions): HotUpdaterAPI {
|
|
59
64
|
const client = HotUpdaterDB.client(options.database);
|
|
60
65
|
const cwd = options.cwd ?? process.cwd();
|
|
61
66
|
|
|
@@ -485,6 +490,10 @@ export function hotUpdater(options: HotUpdaterOptions): HotUpdaterAPI {
|
|
|
485
490
|
api,
|
|
486
491
|
options?.basePath ? { basePath: options.basePath } : {},
|
|
487
492
|
),
|
|
493
|
+
|
|
494
|
+
// private method
|
|
495
|
+
adapterName: client.adapter.name,
|
|
488
496
|
createMigrator: () => client.createMigrator(),
|
|
497
|
+
generateSchema: client.generateSchema,
|
|
489
498
|
};
|
|
490
499
|
}
|
|
@@ -7,8 +7,9 @@ import { Kysely } from "kysely";
|
|
|
7
7
|
import { PGliteDialect } from "kysely-pglite-dialect";
|
|
8
8
|
import { HttpResponse, http } from "msw";
|
|
9
9
|
import { setupServer } from "msw/node";
|
|
10
|
+
import { uuidv7 } from "uuidv7";
|
|
10
11
|
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
11
|
-
import {
|
|
12
|
+
import { createHotUpdater } from "./db";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Integration tests between @hot-updater/server handler and @hot-updater/standalone repository
|
|
@@ -25,7 +26,7 @@ const db = new PGlite();
|
|
|
25
26
|
const kysely = new Kysely({ dialect: new PGliteDialect(db) });
|
|
26
27
|
|
|
27
28
|
// Create handler API with in-memory DB
|
|
28
|
-
const api =
|
|
29
|
+
const api = createHotUpdater({
|
|
29
30
|
database: kyselyAdapter({
|
|
30
31
|
db: kysely,
|
|
31
32
|
provider: "postgresql",
|
|
@@ -109,8 +110,9 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
109
110
|
baseUrl: `${baseUrl}/hot-updater`,
|
|
110
111
|
})({ cwd: process.cwd() });
|
|
111
112
|
|
|
113
|
+
const bundleId = uuidv7();
|
|
112
114
|
const bundle = createTestBundle({
|
|
113
|
-
id:
|
|
115
|
+
id: bundleId,
|
|
114
116
|
fileHash: "integration-hash-1",
|
|
115
117
|
});
|
|
116
118
|
|
|
@@ -119,25 +121,23 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
119
121
|
await repo.commitBundle(); // Triggers actual commit
|
|
120
122
|
|
|
121
123
|
// Verify via handler that bundle was created
|
|
122
|
-
const request = new Request(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
method: "GET",
|
|
126
|
-
},
|
|
127
|
-
);
|
|
124
|
+
const request = new Request(`${baseUrl}/hot-updater/bundles/${bundleId}`, {
|
|
125
|
+
method: "GET",
|
|
126
|
+
});
|
|
128
127
|
|
|
129
128
|
const response = await api.handler(request);
|
|
130
129
|
expect(response.status).toBe(200);
|
|
131
130
|
|
|
132
131
|
const retrieved = (await response.json()) as Bundle;
|
|
133
|
-
expect(retrieved.id).toBe(
|
|
132
|
+
expect(retrieved.id).toBe(bundleId);
|
|
134
133
|
expect(retrieved.fileHash).toBe("integration-hash-1");
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
it("Real integration: getBundleById → handler GET /bundles/:id", async () => {
|
|
138
137
|
// First, create a bundle directly via handler
|
|
138
|
+
const bundleId = uuidv7();
|
|
139
139
|
const bundle = createTestBundle({
|
|
140
|
-
id:
|
|
140
|
+
id: bundleId,
|
|
141
141
|
fileHash: "get-hash-1",
|
|
142
142
|
});
|
|
143
143
|
|
|
@@ -149,24 +149,25 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
149
149
|
})({ cwd: process.cwd() });
|
|
150
150
|
|
|
151
151
|
// Use standalone repository to retrieve
|
|
152
|
-
const retrieved = await repo.getBundleById(
|
|
152
|
+
const retrieved = await repo.getBundleById(bundleId);
|
|
153
153
|
|
|
154
154
|
expect(retrieved).toBeTruthy();
|
|
155
|
-
expect(retrieved?.id).toBe(
|
|
155
|
+
expect(retrieved?.id).toBe(bundleId);
|
|
156
156
|
expect(retrieved?.fileHash).toBe("get-hash-1");
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
it("Real integration: deleteBundle + commitBundle → handler DELETE /bundles/:id", async () => {
|
|
160
160
|
// Create a bundle via handler
|
|
161
|
+
const bundleId = uuidv7();
|
|
161
162
|
const bundle = createTestBundle({
|
|
162
|
-
id:
|
|
163
|
+
id: bundleId,
|
|
163
164
|
fileHash: "delete-hash-1",
|
|
164
165
|
});
|
|
165
166
|
|
|
166
167
|
await api.insertBundle(bundle);
|
|
167
168
|
|
|
168
169
|
// Verify it exists
|
|
169
|
-
const beforeDelete = await api.getBundleById(
|
|
170
|
+
const beforeDelete = await api.getBundleById(bundleId);
|
|
170
171
|
expect(beforeDelete).toBeTruthy();
|
|
171
172
|
|
|
172
173
|
// Create standalone repository
|
|
@@ -179,20 +180,20 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
179
180
|
await repo.commitBundle();
|
|
180
181
|
|
|
181
182
|
// Verify it was deleted
|
|
182
|
-
const afterDelete = await api.getBundleById(
|
|
183
|
+
const afterDelete = await api.getBundleById(bundleId);
|
|
183
184
|
expect(afterDelete).toBeNull();
|
|
184
185
|
});
|
|
185
186
|
|
|
186
187
|
it("Real integration: getBundles → handler GET /bundles", async () => {
|
|
187
188
|
// Create multiple bundles
|
|
188
189
|
await api.insertBundle(
|
|
189
|
-
createTestBundle({ id:
|
|
190
|
+
createTestBundle({ id: uuidv7(), channel: "production" }),
|
|
190
191
|
);
|
|
191
192
|
await api.insertBundle(
|
|
192
|
-
createTestBundle({ id:
|
|
193
|
+
createTestBundle({ id: uuidv7(), channel: "production" }),
|
|
193
194
|
);
|
|
194
195
|
await api.insertBundle(
|
|
195
|
-
createTestBundle({ id:
|
|
196
|
+
createTestBundle({ id: uuidv7(), channel: "staging" }),
|
|
196
197
|
);
|
|
197
198
|
|
|
198
199
|
// Create standalone repository
|
|
@@ -222,8 +223,9 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
222
223
|
})({ cwd: process.cwd() });
|
|
223
224
|
|
|
224
225
|
// Step 1: Create bundle via standalone
|
|
226
|
+
const bundleId = uuidv7();
|
|
225
227
|
const bundle = createTestBundle({
|
|
226
|
-
id:
|
|
228
|
+
id: bundleId,
|
|
227
229
|
fileHash: "e2e-hash",
|
|
228
230
|
enabled: true,
|
|
229
231
|
});
|
|
@@ -232,16 +234,16 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
232
234
|
await repo.commitBundle();
|
|
233
235
|
|
|
234
236
|
// Step 2: Retrieve via standalone
|
|
235
|
-
const retrieved = await repo.getBundleById(
|
|
237
|
+
const retrieved = await repo.getBundleById(bundleId);
|
|
236
238
|
expect(retrieved).toBeTruthy();
|
|
237
239
|
expect(retrieved?.enabled).toBe(true);
|
|
238
240
|
|
|
239
241
|
// Step 3: Update via standalone
|
|
240
|
-
await repo.updateBundle(
|
|
242
|
+
await repo.updateBundle(bundleId, { enabled: false });
|
|
241
243
|
await repo.commitBundle();
|
|
242
244
|
|
|
243
245
|
// Verify update
|
|
244
|
-
const updated = await repo.getBundleById(
|
|
246
|
+
const updated = await repo.getBundleById(bundleId);
|
|
245
247
|
expect(updated?.enabled).toBe(false);
|
|
246
248
|
|
|
247
249
|
// Step 4: Delete via standalone
|
|
@@ -249,7 +251,7 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
249
251
|
await repo.commitBundle();
|
|
250
252
|
|
|
251
253
|
// Verify deletion
|
|
252
|
-
const deleted = await repo.getBundleById(
|
|
254
|
+
const deleted = await repo.getBundleById(bundleId);
|
|
253
255
|
expect(deleted).toBeNull();
|
|
254
256
|
});
|
|
255
257
|
|
|
@@ -259,17 +261,20 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
259
261
|
})({ cwd: process.cwd() });
|
|
260
262
|
|
|
261
263
|
// Append multiple bundles
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
const bundleId1 = uuidv7();
|
|
265
|
+
const bundleId2 = uuidv7();
|
|
266
|
+
const bundleId3 = uuidv7();
|
|
267
|
+
await repo.appendBundle(createTestBundle({ id: bundleId1 }));
|
|
268
|
+
await repo.appendBundle(createTestBundle({ id: bundleId2 }));
|
|
269
|
+
await repo.appendBundle(createTestBundle({ id: bundleId3 }));
|
|
265
270
|
|
|
266
271
|
// Commit all at once (standalone sends array in POST)
|
|
267
272
|
await repo.commitBundle();
|
|
268
273
|
|
|
269
274
|
// Verify all were created
|
|
270
|
-
const bundle1 = await api.getBundleById(
|
|
271
|
-
const bundle2 = await api.getBundleById(
|
|
272
|
-
const bundle3 = await api.getBundleById(
|
|
275
|
+
const bundle1 = await api.getBundleById(bundleId1);
|
|
276
|
+
const bundle2 = await api.getBundleById(bundleId2);
|
|
277
|
+
const bundle3 = await api.getBundleById(bundleId3);
|
|
273
278
|
|
|
274
279
|
expect(bundle1).toBeTruthy();
|
|
275
280
|
expect(bundle2).toBeTruthy();
|
|
@@ -278,7 +283,7 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
278
283
|
|
|
279
284
|
it("Works with custom basePath configuration", async () => {
|
|
280
285
|
// Create handler with custom basePath
|
|
281
|
-
const customApi =
|
|
286
|
+
const customApi = createHotUpdater({
|
|
282
287
|
database: kyselyAdapter({
|
|
283
288
|
db: kysely,
|
|
284
289
|
provider: "postgresql",
|
|
@@ -314,15 +319,16 @@ describe("Handler <-> Standalone Repository Integration", () => {
|
|
|
314
319
|
})({ cwd: process.cwd() });
|
|
315
320
|
|
|
316
321
|
// Test create and retrieve
|
|
322
|
+
const bundleId = uuidv7();
|
|
317
323
|
const bundle = createTestBundle({
|
|
318
|
-
id:
|
|
324
|
+
id: bundleId,
|
|
319
325
|
fileHash: "custom-hash",
|
|
320
326
|
});
|
|
321
327
|
|
|
322
328
|
await repo.appendBundle(bundle);
|
|
323
329
|
await repo.commitBundle();
|
|
324
330
|
|
|
325
|
-
const retrieved = await repo.getBundleById(
|
|
331
|
+
const retrieved = await repo.getBundleById(bundleId);
|
|
326
332
|
expect(retrieved).toBeTruthy();
|
|
327
333
|
expect(retrieved?.fileHash).toBe("custom-hash");
|
|
328
334
|
});
|