@hot-updater/server 0.21.0
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 +21 -0
- package/dist/_virtual/rolldown_runtime.cjs +25 -0
- package/dist/adapters/drizzle.cjs +9 -0
- package/dist/adapters/drizzle.d.cts +1 -0
- package/dist/adapters/drizzle.d.ts +1 -0
- package/dist/adapters/drizzle.js +3 -0
- package/dist/adapters/kysely.cjs +9 -0
- package/dist/adapters/kysely.d.cts +1 -0
- package/dist/adapters/kysely.d.ts +1 -0
- package/dist/adapters/kysely.js +3 -0
- package/dist/adapters/mongodb.cjs +9 -0
- package/dist/adapters/mongodb.d.cts +1 -0
- package/dist/adapters/mongodb.d.ts +1 -0
- package/dist/adapters/mongodb.js +3 -0
- package/dist/adapters/prisma.cjs +9 -0
- package/dist/adapters/prisma.d.cts +1 -0
- package/dist/adapters/prisma.d.ts +1 -0
- package/dist/adapters/prisma.js +3 -0
- package/dist/adapters/typeorm.cjs +9 -0
- package/dist/adapters/typeorm.d.cts +1 -0
- package/dist/adapters/typeorm.d.ts +1 -0
- package/dist/adapters/typeorm.js +3 -0
- package/dist/calculatePagination.cjs +27 -0
- package/dist/calculatePagination.js +26 -0
- package/dist/db/index.cjs +289 -0
- package/dist/db/index.d.cts +62 -0
- package/dist/db/index.d.ts +62 -0
- package/dist/db/index.js +284 -0
- package/dist/handler.cjs +141 -0
- package/dist/handler.d.cts +37 -0
- package/dist/handler.d.ts +37 -0
- package/dist/handler.js +140 -0
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/schema/v1.cjs +26 -0
- package/dist/schema/v1.js +24 -0
- package/dist/types/index.d.cts +20 -0
- package/dist/types/index.d.ts +20 -0
- package/package.json +73 -0
- package/src/adapters/drizzle.ts +1 -0
- package/src/adapters/kysely.ts +1 -0
- package/src/adapters/mongodb.ts +1 -0
- package/src/adapters/prisma.ts +1 -0
- package/src/adapters/typeorm.ts +1 -0
- package/src/calculatePagination.ts +34 -0
- package/src/db/index.spec.ts +231 -0
- package/src/db/index.ts +490 -0
- package/src/handler-standalone-integration.spec.ts +341 -0
- package/src/handler.ts +231 -0
- package/src/index.ts +3 -0
- package/src/schema/v1.ts +22 -0
- package/src/types/index.ts +21 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let fumadb_schema = require("fumadb/schema");
|
|
3
|
+
fumadb_schema = require_rolldown_runtime.__toESM(fumadb_schema);
|
|
4
|
+
|
|
5
|
+
//#region src/schema/v1.ts
|
|
6
|
+
const v1 = (0, fumadb_schema.schema)({
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
tables: { bundles: (0, fumadb_schema.table)("bundles", {
|
|
9
|
+
id: (0, fumadb_schema.idColumn)("id", "varchar(255)").defaultTo$("auto"),
|
|
10
|
+
platform: (0, fumadb_schema.column)("platform", "string"),
|
|
11
|
+
should_force_update: (0, fumadb_schema.column)("should_force_update", "bool"),
|
|
12
|
+
enabled: (0, fumadb_schema.column)("enabled", "bool"),
|
|
13
|
+
file_hash: (0, fumadb_schema.column)("file_hash", "string"),
|
|
14
|
+
git_commit_hash: (0, fumadb_schema.column)("git_commit_hash", "string").nullable(),
|
|
15
|
+
message: (0, fumadb_schema.column)("message", "string").nullable(),
|
|
16
|
+
channel: (0, fumadb_schema.column)("channel", "string"),
|
|
17
|
+
storage_uri: (0, fumadb_schema.column)("storage_uri", "string"),
|
|
18
|
+
target_app_version: (0, fumadb_schema.column)("target_app_version", "string").nullable(),
|
|
19
|
+
fingerprint_hash: (0, fumadb_schema.column)("fingerprint_hash", "string").nullable(),
|
|
20
|
+
metadata: (0, fumadb_schema.column)("metadata", "json")
|
|
21
|
+
}) },
|
|
22
|
+
relations: {}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
exports.v1 = v1;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { column, idColumn, schema, table } from "fumadb/schema";
|
|
2
|
+
|
|
3
|
+
//#region src/schema/v1.ts
|
|
4
|
+
const v1 = schema({
|
|
5
|
+
version: "1.0.0",
|
|
6
|
+
tables: { bundles: table("bundles", {
|
|
7
|
+
id: idColumn("id", "varchar(255)").defaultTo$("auto"),
|
|
8
|
+
platform: column("platform", "string"),
|
|
9
|
+
should_force_update: column("should_force_update", "bool"),
|
|
10
|
+
enabled: column("enabled", "bool"),
|
|
11
|
+
file_hash: column("file_hash", "string"),
|
|
12
|
+
git_commit_hash: column("git_commit_hash", "string").nullable(),
|
|
13
|
+
message: column("message", "string").nullable(),
|
|
14
|
+
channel: column("channel", "string"),
|
|
15
|
+
storage_uri: column("storage_uri", "string"),
|
|
16
|
+
target_app_version: column("target_app_version", "string").nullable(),
|
|
17
|
+
fingerprint_hash: column("fingerprint_hash", "string").nullable(),
|
|
18
|
+
metadata: column("metadata", "json")
|
|
19
|
+
}) },
|
|
20
|
+
relations: {}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { v1 };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Bundle, Bundle as Bundle$1 } from "@hot-updater/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types/index.d.ts
|
|
4
|
+
interface PaginationInfo {
|
|
5
|
+
total: number;
|
|
6
|
+
hasNextPage: boolean;
|
|
7
|
+
hasPreviousPage: boolean;
|
|
8
|
+
currentPage: number;
|
|
9
|
+
totalPages: number;
|
|
10
|
+
}
|
|
11
|
+
interface PaginationOptions {
|
|
12
|
+
limit: number;
|
|
13
|
+
offset: number;
|
|
14
|
+
}
|
|
15
|
+
interface PaginatedResult {
|
|
16
|
+
data: Bundle[];
|
|
17
|
+
pagination: PaginationInfo;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { type Bundle$1 as Bundle, PaginatedResult, PaginationInfo, PaginationOptions };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Bundle, Bundle as Bundle$1 } from "@hot-updater/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types/index.d.ts
|
|
4
|
+
interface PaginationInfo {
|
|
5
|
+
total: number;
|
|
6
|
+
hasNextPage: boolean;
|
|
7
|
+
hasPreviousPage: boolean;
|
|
8
|
+
currentPage: number;
|
|
9
|
+
totalPages: number;
|
|
10
|
+
}
|
|
11
|
+
interface PaginationOptions {
|
|
12
|
+
limit: number;
|
|
13
|
+
offset: number;
|
|
14
|
+
}
|
|
15
|
+
interface PaginatedResult {
|
|
16
|
+
data: Bundle[];
|
|
17
|
+
pagination: PaginationInfo;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { type Bundle$1 as Bundle, PaginatedResult, PaginationInfo, PaginationOptions };
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hot-updater/server",
|
|
3
|
+
"version": "0.21.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "React Native OTA solution for self-hosted",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.cts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./adapters/drizzle": {
|
|
16
|
+
"import": "./dist/adapters/drizzle.js",
|
|
17
|
+
"require": "./dist/adapters/drizzle.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./adapters/kysely": {
|
|
20
|
+
"import": "./dist/adapters/kysely.js",
|
|
21
|
+
"require": "./dist/adapters/kysely.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./adapters/mongodb": {
|
|
24
|
+
"import": "./dist/adapters/mongodb.js",
|
|
25
|
+
"require": "./dist/adapters/mongodb.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./adapters/prisma": {
|
|
28
|
+
"import": "./dist/adapters/prisma.js",
|
|
29
|
+
"require": "./dist/adapters/prisma.cjs"
|
|
30
|
+
},
|
|
31
|
+
"./adapters/typeorm": {
|
|
32
|
+
"import": "./dist/adapters/typeorm.js",
|
|
33
|
+
"require": "./dist/adapters/typeorm.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"src",
|
|
40
|
+
"package.json"
|
|
41
|
+
],
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": "https://github.com/gronxb/hot-updater",
|
|
44
|
+
"author": "gronxb <gron1gh1@gmail.com> (https://github.com/gronxb)",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"fumadb": "0.1.1",
|
|
50
|
+
"semver": "^7.7.2",
|
|
51
|
+
"@hot-updater/core": "0.21.0",
|
|
52
|
+
"@hot-updater/plugin-core": "0.21.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@electric-sql/pglite": "^0.2.17",
|
|
56
|
+
"@types/node": "^20",
|
|
57
|
+
"@types/semver": "^7.5.8",
|
|
58
|
+
"execa": "^9.5.2",
|
|
59
|
+
"kysely": "^0.28.5",
|
|
60
|
+
"kysely-pglite-dialect": "^1.2.0",
|
|
61
|
+
"msw": "^2.7.0",
|
|
62
|
+
"@hot-updater/aws": "0.21.0",
|
|
63
|
+
"@hot-updater/cloudflare": "0.21.0",
|
|
64
|
+
"@hot-updater/firebase": "0.21.0",
|
|
65
|
+
"@hot-updater/standalone": "0.21.0",
|
|
66
|
+
"@hot-updater/supabase": "0.21.0",
|
|
67
|
+
"@hot-updater/test-utils": "0.21.0"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsdown",
|
|
71
|
+
"test:type": "tsc --noEmit"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "fumadb/adapters/drizzle";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "fumadb/adapters/kysely";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "fumadb/adapters/mongodb";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "fumadb/adapters/prisma";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "fumadb/adapters/typeorm";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { PaginationInfo, PaginationOptions } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculate pagination information based on total count, limit, and offset
|
|
5
|
+
*/
|
|
6
|
+
export function calculatePagination(
|
|
7
|
+
total: number,
|
|
8
|
+
options: PaginationOptions,
|
|
9
|
+
): PaginationInfo {
|
|
10
|
+
const { limit, offset } = options;
|
|
11
|
+
|
|
12
|
+
if (total === 0) {
|
|
13
|
+
return {
|
|
14
|
+
total: 0,
|
|
15
|
+
hasNextPage: false,
|
|
16
|
+
hasPreviousPage: false,
|
|
17
|
+
currentPage: 1,
|
|
18
|
+
totalPages: 0,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const currentPage = Math.floor(offset / limit) + 1;
|
|
23
|
+
const totalPages = Math.ceil(total / limit);
|
|
24
|
+
const hasNextPage = offset + limit < total;
|
|
25
|
+
const hasPreviousPage = offset > 0;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
total,
|
|
29
|
+
hasNextPage,
|
|
30
|
+
hasPreviousPage,
|
|
31
|
+
currentPage,
|
|
32
|
+
totalPages,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { s3Storage } from "@hot-updater/aws";
|
|
3
|
+
import { r2Storage } from "@hot-updater/cloudflare";
|
|
4
|
+
import type { Bundle, GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
|
|
5
|
+
import { NIL_UUID } from "@hot-updater/core";
|
|
6
|
+
import { firebaseStorage } from "@hot-updater/firebase";
|
|
7
|
+
import { kyselyAdapter } from "@hot-updater/server/adapters/kysely";
|
|
8
|
+
import { supabaseStorage } from "@hot-updater/supabase";
|
|
9
|
+
import { setupGetUpdateInfoTestSuite } from "@hot-updater/test-utils";
|
|
10
|
+
import { Kysely } from "kysely";
|
|
11
|
+
import { PGliteDialect } from "kysely-pglite-dialect";
|
|
12
|
+
import {
|
|
13
|
+
afterAll,
|
|
14
|
+
afterEach,
|
|
15
|
+
beforeAll,
|
|
16
|
+
beforeEach,
|
|
17
|
+
describe,
|
|
18
|
+
expect,
|
|
19
|
+
it,
|
|
20
|
+
vi,
|
|
21
|
+
} from "vitest";
|
|
22
|
+
import { hotUpdater } from "./index";
|
|
23
|
+
|
|
24
|
+
describe("server/db hotUpdater getUpdateInfo (PGlite + Kysely)", async () => {
|
|
25
|
+
const db = new PGlite();
|
|
26
|
+
|
|
27
|
+
const kysely = new Kysely({ dialect: new PGliteDialect(db) });
|
|
28
|
+
|
|
29
|
+
const api = hotUpdater({
|
|
30
|
+
database: kyselyAdapter({
|
|
31
|
+
db: kysely,
|
|
32
|
+
provider: "postgresql",
|
|
33
|
+
}),
|
|
34
|
+
storagePlugins: [
|
|
35
|
+
s3Storage({
|
|
36
|
+
region: "us-east-1",
|
|
37
|
+
credentials: {
|
|
38
|
+
accessKeyId: "test-access-key",
|
|
39
|
+
secretAccessKey: "test-secret-key",
|
|
40
|
+
},
|
|
41
|
+
bucketName: "test-bucket",
|
|
42
|
+
}),
|
|
43
|
+
r2Storage({
|
|
44
|
+
cloudflareApiToken: "test-token",
|
|
45
|
+
accountId: "test-account-id",
|
|
46
|
+
bucketName: "test-bucket",
|
|
47
|
+
}),
|
|
48
|
+
supabaseStorage({
|
|
49
|
+
supabaseUrl: "https://test.supabase.co",
|
|
50
|
+
supabaseAnonKey: "test-anon-key",
|
|
51
|
+
bucketName: "test-bucket",
|
|
52
|
+
}),
|
|
53
|
+
firebaseStorage({
|
|
54
|
+
storageBucket: "test-bucket.appspot.com",
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
beforeAll(async () => {
|
|
60
|
+
// Initialize FumaDB schema to latest (creates tables under the hood)
|
|
61
|
+
const migrator = api.createMigrator();
|
|
62
|
+
const result = await migrator.migrateToLatest({
|
|
63
|
+
mode: "from-schema",
|
|
64
|
+
updateSettings: true,
|
|
65
|
+
});
|
|
66
|
+
await result.execute();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
beforeEach(async () => {
|
|
70
|
+
await db.exec("DELETE FROM bundles");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterAll(async () => {
|
|
74
|
+
await kysely.destroy();
|
|
75
|
+
await db.close();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const getUpdateInfo = async (
|
|
79
|
+
bundles: Bundle[],
|
|
80
|
+
options: GetBundlesArgs,
|
|
81
|
+
): Promise<UpdateInfo | null> => {
|
|
82
|
+
// Insert fixtures via the server API to exercise its types + mapping
|
|
83
|
+
for (const b of bundles) {
|
|
84
|
+
await api.insertBundle(b);
|
|
85
|
+
}
|
|
86
|
+
return api.getUpdateInfo(options);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
setupGetUpdateInfoTestSuite({ getUpdateInfo });
|
|
90
|
+
|
|
91
|
+
describe("getAppUpdateInfo with storage plugins", () => {
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
// Fix time for deterministic signed URLs
|
|
94
|
+
vi.useFakeTimers();
|
|
95
|
+
vi.setSystemTime(new Date("2025-10-15T12:21:00Z"));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
afterEach(() => {
|
|
99
|
+
vi.useRealTimers();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("resolves s3:// storage URI to signed URL via s3StoragePlugin", async () => {
|
|
103
|
+
const bundle: Bundle = {
|
|
104
|
+
id: "00000000-0000-0000-0000-000000000001",
|
|
105
|
+
platform: "ios",
|
|
106
|
+
shouldForceUpdate: false,
|
|
107
|
+
enabled: true,
|
|
108
|
+
fileHash: "hash123",
|
|
109
|
+
gitCommitHash: null,
|
|
110
|
+
message: "Test bundle",
|
|
111
|
+
channel: "production",
|
|
112
|
+
storageUri: "s3://test-bucket/bundles/bundle.zip",
|
|
113
|
+
targetAppVersion: "1.0.0",
|
|
114
|
+
fingerprintHash: null,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
await api.insertBundle(bundle);
|
|
118
|
+
|
|
119
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
120
|
+
appVersion: "1.0.0",
|
|
121
|
+
bundleId: NIL_UUID,
|
|
122
|
+
platform: "ios",
|
|
123
|
+
_updateStrategy: "appVersion",
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(updateInfo).not.toBeNull();
|
|
127
|
+
expect(updateInfo?.fileUrl).toBe(
|
|
128
|
+
"https://test-bucket.s3.us-east-1.amazonaws.com/bundles/bundle.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=test-access-key%2F20251015%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251015T122100Z&X-Amz-Expires=3600&X-Amz-Signature=4fa782e86a842ce2eacbfa6534d1f5d5145d733092959cf6ad755cc306bbe98e&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("passes through http:// URLs without plugin resolution", async () => {
|
|
133
|
+
const bundle: Bundle = {
|
|
134
|
+
id: "00000000-0000-0000-0000-000000000004",
|
|
135
|
+
platform: "ios",
|
|
136
|
+
shouldForceUpdate: false,
|
|
137
|
+
enabled: true,
|
|
138
|
+
fileHash: "hashhttp",
|
|
139
|
+
gitCommitHash: null,
|
|
140
|
+
message: "HTTP bundle",
|
|
141
|
+
channel: "production",
|
|
142
|
+
storageUri: "s3://bundle/bundle.zip",
|
|
143
|
+
targetAppVersion: "1.0.0",
|
|
144
|
+
fingerprintHash: null,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
await api.insertBundle(bundle);
|
|
148
|
+
|
|
149
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
150
|
+
appVersion: "1.0.0",
|
|
151
|
+
bundleId: NIL_UUID,
|
|
152
|
+
platform: "ios",
|
|
153
|
+
_updateStrategy: "appVersion",
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(updateInfo).not.toBeNull();
|
|
157
|
+
expect(updateInfo?.fileUrl).toBe(
|
|
158
|
+
"https://bundle.s3.us-east-1.amazonaws.com/bundle.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=test-access-key%2F20251015%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251015T122100Z&X-Amz-Expires=3600&X-Amz-Signature=b83d9cfc9bd23275e5eb3baf792776fd7b49730f3aa2f5172d067c9dfb10cd94&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("passes through https:// URLs without plugin resolution", async () => {
|
|
163
|
+
const bundle: Bundle = {
|
|
164
|
+
id: "00000000-0000-0000-0000-000000000005",
|
|
165
|
+
platform: "ios",
|
|
166
|
+
shouldForceUpdate: false,
|
|
167
|
+
enabled: true,
|
|
168
|
+
fileHash: "hashhttps",
|
|
169
|
+
gitCommitHash: null,
|
|
170
|
+
message: "HTTPS bundle",
|
|
171
|
+
channel: "production",
|
|
172
|
+
storageUri: "https://cdn.example.com/bundle.zip",
|
|
173
|
+
targetAppVersion: "1.0.0",
|
|
174
|
+
fingerprintHash: null,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
await api.insertBundle(bundle);
|
|
178
|
+
|
|
179
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
180
|
+
appVersion: "1.0.0",
|
|
181
|
+
bundleId: NIL_UUID,
|
|
182
|
+
platform: "ios",
|
|
183
|
+
_updateStrategy: "appVersion",
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(updateInfo).not.toBeNull();
|
|
187
|
+
expect(updateInfo?.fileUrl).toBe("https://cdn.example.com/bundle.zip");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("returns null when no update is available", async () => {
|
|
191
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
192
|
+
appVersion: "99.0.0",
|
|
193
|
+
bundleId: NIL_UUID,
|
|
194
|
+
platform: "ios",
|
|
195
|
+
_updateStrategy: "appVersion",
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
expect(updateInfo).toBeNull();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("works with fingerprint strategy", async () => {
|
|
202
|
+
const bundle: Bundle = {
|
|
203
|
+
id: "00000000-0000-0000-0000-000000000008",
|
|
204
|
+
platform: "ios",
|
|
205
|
+
shouldForceUpdate: false,
|
|
206
|
+
enabled: true,
|
|
207
|
+
fileHash: "hashfp",
|
|
208
|
+
gitCommitHash: null,
|
|
209
|
+
message: "Fingerprint bundle",
|
|
210
|
+
channel: "production",
|
|
211
|
+
storageUri: "s3://test-bucket/fp-bundle.zip",
|
|
212
|
+
targetAppVersion: null,
|
|
213
|
+
fingerprintHash: "fingerprint123",
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
await api.insertBundle(bundle);
|
|
217
|
+
|
|
218
|
+
const updateInfo = await api.getAppUpdateInfo({
|
|
219
|
+
fingerprintHash: "fingerprint123",
|
|
220
|
+
bundleId: NIL_UUID,
|
|
221
|
+
platform: "ios",
|
|
222
|
+
_updateStrategy: "fingerprint",
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(updateInfo).not.toBeNull();
|
|
226
|
+
expect(updateInfo?.fileUrl).toBe(
|
|
227
|
+
"https://test-bucket.s3.us-east-1.amazonaws.com/fp-bundle.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=test-access-key%2F20251015%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251015T122100Z&X-Amz-Expires=3600&X-Amz-Signature=d70e9b699dccbb51cf32f3e5b7912f2567d38f7e508b1f30091a8fee0d0abb65&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|