@hot-updater/server 0.21.10 → 0.21.11
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 +16 -276
- package/dist/db/index.d.cts +3 -48
- package/dist/db/index.d.ts +3 -48
- package/dist/db/index.js +17 -272
- package/dist/db/ormCore.cjs +278 -0
- package/dist/db/ormCore.d.cts +26 -0
- package/dist/db/ormCore.d.ts +26 -0
- package/dist/db/ormCore.js +273 -0
- package/dist/db/pluginCore.cjs +71 -0
- package/dist/db/pluginCore.js +69 -0
- package/dist/db/types.cjs +12 -0
- package/dist/db/types.d.cts +31 -0
- package/dist/db/types.d.ts +31 -0
- package/dist/db/types.js +10 -0
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/package.json +10 -9
- package/src/db/index.spec.ts +99 -1
- package/src/db/index.ts +38 -446
- package/src/db/ormCore.ts +441 -0
- package/src/db/pluginCore.ts +119 -0
- package/src/db/types.ts +57 -0
- package/src/handler-standalone-integration.spec.ts +9 -9
package/dist/db/index.js
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import { calculatePagination } from "../calculatePagination.js";
|
|
2
1
|
import { createHandler } from "../handler.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { fumadb } from "fumadb";
|
|
2
|
+
import { HotUpdaterDB, createOrmDatabaseCore } from "./ormCore.js";
|
|
3
|
+
import { createPluginDatabaseCore } from "./pluginCore.js";
|
|
4
|
+
import { isDatabasePlugin, isDatabasePluginFactory } from "./types.js";
|
|
7
5
|
|
|
8
6
|
//#region src/db/index.ts
|
|
9
|
-
const HotUpdaterDB = fumadb({
|
|
10
|
-
namespace: "hot_updater",
|
|
11
|
-
schemas: [v0_21_0]
|
|
12
|
-
});
|
|
13
7
|
function createHotUpdater(options) {
|
|
14
|
-
const
|
|
15
|
-
const cwd = options.cwd ?? process.cwd();
|
|
16
|
-
const storagePlugins = (options?.storagePlugins ?? []).map((plugin) => typeof plugin === "function" ? plugin({ cwd }) : plugin);
|
|
8
|
+
const storagePlugins = (options?.storagePlugins ?? []).map((plugin) => typeof plugin === "function" ? plugin() : plugin);
|
|
17
9
|
const resolveFileUrl = async (storageUri) => {
|
|
18
10
|
if (!storageUri) return null;
|
|
19
11
|
const protocol = new URL(storageUri).protocol.replace(":", "");
|
|
@@ -24,268 +16,21 @@ function createHotUpdater(options) {
|
|
|
24
16
|
if (!fileUrl) throw new Error("Storage plugin returned empty fileUrl");
|
|
25
17
|
return fileUrl;
|
|
26
18
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"should_force_update",
|
|
35
|
-
"enabled",
|
|
36
|
-
"file_hash",
|
|
37
|
-
"git_commit_hash",
|
|
38
|
-
"message",
|
|
39
|
-
"channel",
|
|
40
|
-
"storage_uri",
|
|
41
|
-
"target_app_version",
|
|
42
|
-
"fingerprint_hash",
|
|
43
|
-
"metadata"
|
|
44
|
-
],
|
|
45
|
-
where: (b) => b.and(b.isNotNull("id"), b("id", "=", id))
|
|
46
|
-
});
|
|
47
|
-
if (!result) return null;
|
|
48
|
-
return {
|
|
49
|
-
id: result.id,
|
|
50
|
-
platform: result.platform,
|
|
51
|
-
shouldForceUpdate: Boolean(result.should_force_update),
|
|
52
|
-
enabled: Boolean(result.enabled),
|
|
53
|
-
fileHash: result.file_hash,
|
|
54
|
-
gitCommitHash: result.git_commit_hash ?? null,
|
|
55
|
-
message: result.message ?? null,
|
|
56
|
-
channel: result.channel,
|
|
57
|
-
storageUri: result.storage_uri,
|
|
58
|
-
targetAppVersion: result.target_app_version ?? null,
|
|
59
|
-
fingerprintHash: result.fingerprint_hash ?? null
|
|
60
|
-
};
|
|
61
|
-
},
|
|
62
|
-
async getUpdateInfo(args) {
|
|
63
|
-
const version = await client.version();
|
|
64
|
-
const orm = client.orm(version);
|
|
65
|
-
const toUpdateInfo = (row, status) => ({
|
|
66
|
-
id: row.id,
|
|
67
|
-
shouldForceUpdate: status === "ROLLBACK" ? true : Boolean(row.should_force_update),
|
|
68
|
-
message: row.message ?? null,
|
|
69
|
-
status,
|
|
70
|
-
storageUri: row.storage_uri ?? null,
|
|
71
|
-
fileHash: row.file_hash ?? null
|
|
72
|
-
});
|
|
73
|
-
const INIT_BUNDLE_ROLLBACK_UPDATE_INFO = {
|
|
74
|
-
id: NIL_UUID,
|
|
75
|
-
message: null,
|
|
76
|
-
shouldForceUpdate: true,
|
|
77
|
-
status: "ROLLBACK",
|
|
78
|
-
storageUri: null,
|
|
79
|
-
fileHash: null
|
|
80
|
-
};
|
|
81
|
-
const appVersionStrategy = async ({ platform, appVersion, bundleId, minBundleId = NIL_UUID, channel = "production" }) => {
|
|
82
|
-
const versionRows = await orm.findMany("bundles", {
|
|
83
|
-
select: ["target_app_version"],
|
|
84
|
-
where: (b) => b.and(b("platform", "=", platform))
|
|
85
|
-
});
|
|
86
|
-
const compatibleVersions = filterCompatibleAppVersions(Array.from(new Set((versionRows ?? []).map((r) => r.target_app_version).filter((v) => Boolean(v)))), appVersion);
|
|
87
|
-
const candidates = ((compatibleVersions.length === 0 ? [] : await orm.findMany("bundles", {
|
|
88
|
-
select: [
|
|
89
|
-
"id",
|
|
90
|
-
"should_force_update",
|
|
91
|
-
"message",
|
|
92
|
-
"storage_uri",
|
|
93
|
-
"file_hash",
|
|
94
|
-
"channel",
|
|
95
|
-
"target_app_version",
|
|
96
|
-
"enabled"
|
|
97
|
-
],
|
|
98
|
-
where: (b) => b.and(b("enabled", "=", true), b("platform", "=", platform), b("id", ">=", minBundleId ?? NIL_UUID), b("channel", "=", channel), b.isNotNull("target_app_version"))
|
|
99
|
-
})) ?? []).filter((r) => r.target_app_version ? compatibleVersions.includes(r.target_app_version) : false);
|
|
100
|
-
const byIdDesc = (a, b) => b.id.localeCompare(a.id);
|
|
101
|
-
const sorted = (candidates ?? []).slice().sort(byIdDesc);
|
|
102
|
-
const latestCandidate = sorted[0] ?? null;
|
|
103
|
-
const currentBundle = sorted.find((b) => b.id === bundleId);
|
|
104
|
-
const updateCandidate = sorted.find((b) => b.id.localeCompare(bundleId) > 0) ?? null;
|
|
105
|
-
const rollbackCandidate = sorted.find((b) => b.id.localeCompare(bundleId) < 0) ?? null;
|
|
106
|
-
if (bundleId === NIL_UUID) {
|
|
107
|
-
if (latestCandidate && latestCandidate.id !== bundleId) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
if (currentBundle) {
|
|
111
|
-
if (latestCandidate && latestCandidate.id.localeCompare(currentBundle.id) > 0) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
if (updateCandidate) return toUpdateInfo(updateCandidate, "UPDATE");
|
|
115
|
-
if (rollbackCandidate) return toUpdateInfo(rollbackCandidate, "ROLLBACK");
|
|
116
|
-
if (minBundleId && bundleId.localeCompare(minBundleId) <= 0) return null;
|
|
117
|
-
return INIT_BUNDLE_ROLLBACK_UPDATE_INFO;
|
|
118
|
-
};
|
|
119
|
-
const fingerprintStrategy = async ({ platform, fingerprintHash, bundleId, minBundleId = NIL_UUID, channel = "production" }) => {
|
|
120
|
-
const candidates = await orm.findMany("bundles", {
|
|
121
|
-
select: [
|
|
122
|
-
"id",
|
|
123
|
-
"should_force_update",
|
|
124
|
-
"message",
|
|
125
|
-
"storage_uri",
|
|
126
|
-
"file_hash",
|
|
127
|
-
"channel",
|
|
128
|
-
"fingerprint_hash",
|
|
129
|
-
"enabled"
|
|
130
|
-
],
|
|
131
|
-
where: (b) => b.and(b("enabled", "=", true), b("platform", "=", platform), b("id", ">=", minBundleId ?? NIL_UUID), b("channel", "=", channel), b("fingerprint_hash", "=", fingerprintHash))
|
|
132
|
-
});
|
|
133
|
-
const byIdDesc = (a, b) => b.id.localeCompare(a.id);
|
|
134
|
-
const sorted = (candidates ?? []).slice().sort(byIdDesc);
|
|
135
|
-
const latestCandidate = sorted[0] ?? null;
|
|
136
|
-
const currentBundle = sorted.find((b) => b.id === bundleId);
|
|
137
|
-
const updateCandidate = sorted.find((b) => b.id.localeCompare(bundleId) > 0) ?? null;
|
|
138
|
-
const rollbackCandidate = sorted.find((b) => b.id.localeCompare(bundleId) < 0) ?? null;
|
|
139
|
-
if (bundleId === NIL_UUID) {
|
|
140
|
-
if (latestCandidate && latestCandidate.id !== bundleId) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
if (currentBundle) {
|
|
144
|
-
if (latestCandidate && latestCandidate.id.localeCompare(currentBundle.id) > 0) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
if (updateCandidate) return toUpdateInfo(updateCandidate, "UPDATE");
|
|
148
|
-
if (rollbackCandidate) return toUpdateInfo(rollbackCandidate, "ROLLBACK");
|
|
149
|
-
if (minBundleId && bundleId.localeCompare(minBundleId) <= 0) return null;
|
|
150
|
-
return INIT_BUNDLE_ROLLBACK_UPDATE_INFO;
|
|
151
|
-
};
|
|
152
|
-
if (args._updateStrategy === "appVersion") return appVersionStrategy(args);
|
|
153
|
-
if (args._updateStrategy === "fingerprint") return fingerprintStrategy(args);
|
|
154
|
-
return null;
|
|
155
|
-
},
|
|
156
|
-
async getAppUpdateInfo(args) {
|
|
157
|
-
const info = await this.getUpdateInfo(args);
|
|
158
|
-
if (!info) return null;
|
|
159
|
-
const { storageUri,...rest } = info;
|
|
160
|
-
const fileUrl = await resolveFileUrl(storageUri ?? null);
|
|
161
|
-
return {
|
|
162
|
-
...rest,
|
|
163
|
-
fileUrl
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
async getChannels() {
|
|
167
|
-
const version = await client.version();
|
|
168
|
-
const rows = await client.orm(version).findMany("bundles", {
|
|
169
|
-
select: ["channel"],
|
|
170
|
-
where: (b) => b.isNotNull("channel")
|
|
171
|
-
});
|
|
172
|
-
const set = new Set(rows?.map((r) => r.channel) ?? []);
|
|
173
|
-
return Array.from(set);
|
|
174
|
-
},
|
|
175
|
-
async getBundles(options$1) {
|
|
176
|
-
const version = await client.version();
|
|
177
|
-
const orm = client.orm(version);
|
|
178
|
-
const { where, limit, offset } = options$1;
|
|
179
|
-
const all = (await orm.findMany("bundles", {
|
|
180
|
-
select: [
|
|
181
|
-
"id",
|
|
182
|
-
"platform",
|
|
183
|
-
"should_force_update",
|
|
184
|
-
"enabled",
|
|
185
|
-
"file_hash",
|
|
186
|
-
"git_commit_hash",
|
|
187
|
-
"message",
|
|
188
|
-
"channel",
|
|
189
|
-
"storage_uri",
|
|
190
|
-
"target_app_version",
|
|
191
|
-
"fingerprint_hash",
|
|
192
|
-
"metadata"
|
|
193
|
-
],
|
|
194
|
-
where: (b) => {
|
|
195
|
-
const conditions = [];
|
|
196
|
-
if (where?.channel) conditions.push(b("channel", "=", where.channel));
|
|
197
|
-
if (where?.platform) conditions.push(b("platform", "=", where.platform));
|
|
198
|
-
return conditions.length > 0 ? b.and(...conditions) : true;
|
|
199
|
-
}
|
|
200
|
-
})).map((r) => ({
|
|
201
|
-
id: r.id,
|
|
202
|
-
platform: r.platform,
|
|
203
|
-
shouldForceUpdate: Boolean(r.should_force_update),
|
|
204
|
-
enabled: Boolean(r.enabled),
|
|
205
|
-
fileHash: r.file_hash,
|
|
206
|
-
gitCommitHash: r.git_commit_hash ?? null,
|
|
207
|
-
message: r.message ?? null,
|
|
208
|
-
channel: r.channel,
|
|
209
|
-
storageUri: r.storage_uri,
|
|
210
|
-
targetAppVersion: r.target_app_version ?? null,
|
|
211
|
-
fingerprintHash: r.fingerprint_hash ?? null
|
|
212
|
-
})).sort((a, b) => b.id.localeCompare(a.id));
|
|
213
|
-
const total = all.length;
|
|
214
|
-
return {
|
|
215
|
-
data: all.slice(offset, offset + limit),
|
|
216
|
-
pagination: calculatePagination(total, {
|
|
217
|
-
limit,
|
|
218
|
-
offset
|
|
219
|
-
})
|
|
220
|
-
};
|
|
221
|
-
},
|
|
222
|
-
async insertBundle(bundle) {
|
|
223
|
-
const version = await client.version();
|
|
224
|
-
const orm = client.orm(version);
|
|
225
|
-
const values = {
|
|
226
|
-
id: bundle.id,
|
|
227
|
-
platform: bundle.platform,
|
|
228
|
-
should_force_update: bundle.shouldForceUpdate,
|
|
229
|
-
enabled: bundle.enabled,
|
|
230
|
-
file_hash: bundle.fileHash,
|
|
231
|
-
git_commit_hash: bundle.gitCommitHash,
|
|
232
|
-
message: bundle.message,
|
|
233
|
-
channel: bundle.channel,
|
|
234
|
-
storage_uri: bundle.storageUri,
|
|
235
|
-
target_app_version: bundle.targetAppVersion,
|
|
236
|
-
fingerprint_hash: bundle.fingerprintHash,
|
|
237
|
-
metadata: bundle.metadata ?? {}
|
|
238
|
-
};
|
|
239
|
-
const { id,...updateValues } = values;
|
|
240
|
-
await orm.upsert("bundles", {
|
|
241
|
-
where: (b) => b("id", "=", id),
|
|
242
|
-
create: values,
|
|
243
|
-
update: updateValues
|
|
244
|
-
});
|
|
245
|
-
},
|
|
246
|
-
async updateBundleById(bundleId, newBundle) {
|
|
247
|
-
const version = await client.version();
|
|
248
|
-
const orm = client.orm(version);
|
|
249
|
-
const current = await this.getBundleById(bundleId);
|
|
250
|
-
if (!current) throw new Error("targetBundleId not found");
|
|
251
|
-
const merged = {
|
|
252
|
-
...current,
|
|
253
|
-
...newBundle
|
|
254
|
-
};
|
|
255
|
-
const values = {
|
|
256
|
-
id: merged.id,
|
|
257
|
-
platform: merged.platform,
|
|
258
|
-
should_force_update: merged.shouldForceUpdate,
|
|
259
|
-
enabled: merged.enabled,
|
|
260
|
-
file_hash: merged.fileHash,
|
|
261
|
-
git_commit_hash: merged.gitCommitHash,
|
|
262
|
-
message: merged.message,
|
|
263
|
-
channel: merged.channel,
|
|
264
|
-
storage_uri: merged.storageUri,
|
|
265
|
-
target_app_version: merged.targetAppVersion,
|
|
266
|
-
fingerprint_hash: merged.fingerprintHash,
|
|
267
|
-
metadata: merged.metadata ?? {}
|
|
268
|
-
};
|
|
269
|
-
const { id: id2,...updateValues2 } = values;
|
|
270
|
-
await orm.upsert("bundles", {
|
|
271
|
-
where: (b) => b("id", "=", id2),
|
|
272
|
-
create: values,
|
|
273
|
-
update: updateValues2
|
|
274
|
-
});
|
|
275
|
-
},
|
|
276
|
-
async deleteBundleById(bundleId) {
|
|
277
|
-
const version = await client.version();
|
|
278
|
-
await client.orm(version).deleteMany("bundles", { where: (b) => b("id", "=", bundleId) });
|
|
279
|
-
}
|
|
280
|
-
};
|
|
19
|
+
let core;
|
|
20
|
+
const database = options.database;
|
|
21
|
+
if (isDatabasePluginFactory(database) || isDatabasePlugin(database)) core = createPluginDatabaseCore(isDatabasePluginFactory(database) ? database() : database, resolveFileUrl);
|
|
22
|
+
else core = createOrmDatabaseCore({
|
|
23
|
+
database,
|
|
24
|
+
resolveFileUrl
|
|
25
|
+
});
|
|
281
26
|
return {
|
|
282
|
-
...api,
|
|
283
|
-
handler: createHandler(api, options?.basePath ? { basePath: options.basePath } : {}),
|
|
284
|
-
adapterName:
|
|
285
|
-
createMigrator:
|
|
286
|
-
generateSchema:
|
|
27
|
+
...core.api,
|
|
28
|
+
handler: createHandler(core.api, options?.basePath ? { basePath: options.basePath } : {}),
|
|
29
|
+
adapterName: core.adapterName,
|
|
30
|
+
createMigrator: core.createMigrator,
|
|
31
|
+
generateSchema: core.generateSchema
|
|
287
32
|
};
|
|
288
33
|
}
|
|
289
34
|
|
|
290
35
|
//#endregion
|
|
291
|
-
export {
|
|
36
|
+
export { createHotUpdater };
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_calculatePagination = require('../calculatePagination.cjs');
|
|
3
|
+
const require_v0_21_0 = require('../schema/v0_21_0.cjs');
|
|
4
|
+
let __hot_updater_core = require("@hot-updater/core");
|
|
5
|
+
__hot_updater_core = require_rolldown_runtime.__toESM(__hot_updater_core);
|
|
6
|
+
let __hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
7
|
+
__hot_updater_plugin_core = require_rolldown_runtime.__toESM(__hot_updater_plugin_core);
|
|
8
|
+
let fumadb = require("fumadb");
|
|
9
|
+
fumadb = require_rolldown_runtime.__toESM(fumadb);
|
|
10
|
+
|
|
11
|
+
//#region src/db/ormCore.ts
|
|
12
|
+
const HotUpdaterDB = (0, fumadb.fumadb)({
|
|
13
|
+
namespace: "hot_updater",
|
|
14
|
+
schemas: [require_v0_21_0.v0_21_0]
|
|
15
|
+
});
|
|
16
|
+
function createOrmDatabaseCore({ database, resolveFileUrl }) {
|
|
17
|
+
const client = HotUpdaterDB.client(database);
|
|
18
|
+
return {
|
|
19
|
+
api: {
|
|
20
|
+
async getBundleById(id) {
|
|
21
|
+
const version = await client.version();
|
|
22
|
+
const result = await client.orm(version).findFirst("bundles", {
|
|
23
|
+
select: [
|
|
24
|
+
"id",
|
|
25
|
+
"platform",
|
|
26
|
+
"should_force_update",
|
|
27
|
+
"enabled",
|
|
28
|
+
"file_hash",
|
|
29
|
+
"git_commit_hash",
|
|
30
|
+
"message",
|
|
31
|
+
"channel",
|
|
32
|
+
"storage_uri",
|
|
33
|
+
"target_app_version",
|
|
34
|
+
"fingerprint_hash",
|
|
35
|
+
"metadata"
|
|
36
|
+
],
|
|
37
|
+
where: (b) => b("id", "=", id)
|
|
38
|
+
});
|
|
39
|
+
if (!result) return null;
|
|
40
|
+
return {
|
|
41
|
+
id: result.id,
|
|
42
|
+
platform: result.platform,
|
|
43
|
+
shouldForceUpdate: Boolean(result.should_force_update),
|
|
44
|
+
enabled: Boolean(result.enabled),
|
|
45
|
+
fileHash: result.file_hash,
|
|
46
|
+
gitCommitHash: result.git_commit_hash ?? null,
|
|
47
|
+
message: result.message ?? null,
|
|
48
|
+
channel: result.channel,
|
|
49
|
+
storageUri: result.storage_uri,
|
|
50
|
+
targetAppVersion: result.target_app_version ?? null,
|
|
51
|
+
fingerprintHash: result.fingerprint_hash ?? null
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
async getUpdateInfo(args) {
|
|
55
|
+
const version = await client.version();
|
|
56
|
+
const orm = client.orm(version);
|
|
57
|
+
const toUpdateInfo = (row, status) => ({
|
|
58
|
+
id: row.id,
|
|
59
|
+
shouldForceUpdate: status === "ROLLBACK" ? true : Boolean(row.should_force_update),
|
|
60
|
+
message: row.message ?? null,
|
|
61
|
+
status,
|
|
62
|
+
storageUri: row.storage_uri ?? null,
|
|
63
|
+
fileHash: row.file_hash ?? null
|
|
64
|
+
});
|
|
65
|
+
const INIT_BUNDLE_ROLLBACK_UPDATE_INFO = {
|
|
66
|
+
id: __hot_updater_core.NIL_UUID,
|
|
67
|
+
message: null,
|
|
68
|
+
shouldForceUpdate: true,
|
|
69
|
+
status: "ROLLBACK",
|
|
70
|
+
storageUri: null,
|
|
71
|
+
fileHash: null
|
|
72
|
+
};
|
|
73
|
+
const appVersionStrategy = async ({ platform, appVersion, bundleId, minBundleId = __hot_updater_core.NIL_UUID, channel = "production" }) => {
|
|
74
|
+
const versionRows = await orm.findMany("bundles", {
|
|
75
|
+
select: ["target_app_version"],
|
|
76
|
+
where: (b) => b.and(b("platform", "=", platform))
|
|
77
|
+
});
|
|
78
|
+
const compatibleVersions = (0, __hot_updater_plugin_core.filterCompatibleAppVersions)(Array.from(new Set((versionRows ?? []).map((r) => r.target_app_version).filter((v) => Boolean(v)))), appVersion);
|
|
79
|
+
const candidates = ((compatibleVersions.length === 0 ? [] : await orm.findMany("bundles", {
|
|
80
|
+
select: [
|
|
81
|
+
"id",
|
|
82
|
+
"should_force_update",
|
|
83
|
+
"message",
|
|
84
|
+
"storage_uri",
|
|
85
|
+
"file_hash",
|
|
86
|
+
"channel",
|
|
87
|
+
"target_app_version",
|
|
88
|
+
"enabled"
|
|
89
|
+
],
|
|
90
|
+
where: (b) => b.and(b("enabled", "=", true), b("platform", "=", platform), b("id", ">=", minBundleId ?? __hot_updater_core.NIL_UUID), b("channel", "=", channel), b.isNotNull("target_app_version"))
|
|
91
|
+
})) ?? []).filter((r) => r.target_app_version ? compatibleVersions.includes(r.target_app_version) : false);
|
|
92
|
+
const byIdDesc = (a, b) => b.id.localeCompare(a.id);
|
|
93
|
+
const sorted = (candidates ?? []).slice().sort(byIdDesc);
|
|
94
|
+
const latestCandidate = sorted[0] ?? null;
|
|
95
|
+
const currentBundle = sorted.find((b) => b.id === bundleId);
|
|
96
|
+
const updateCandidate = sorted.find((b) => b.id.localeCompare(bundleId) > 0) ?? null;
|
|
97
|
+
const rollbackCandidate = sorted.find((b) => b.id.localeCompare(bundleId) < 0) ?? null;
|
|
98
|
+
if (bundleId === __hot_updater_core.NIL_UUID) {
|
|
99
|
+
if (latestCandidate && latestCandidate.id !== bundleId) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
if (currentBundle) {
|
|
103
|
+
if (latestCandidate && latestCandidate.id.localeCompare(currentBundle.id) > 0) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (updateCandidate) return toUpdateInfo(updateCandidate, "UPDATE");
|
|
107
|
+
if (rollbackCandidate) return toUpdateInfo(rollbackCandidate, "ROLLBACK");
|
|
108
|
+
if (minBundleId && bundleId.localeCompare(minBundleId) <= 0) return null;
|
|
109
|
+
return INIT_BUNDLE_ROLLBACK_UPDATE_INFO;
|
|
110
|
+
};
|
|
111
|
+
const fingerprintStrategy = async ({ platform, fingerprintHash, bundleId, minBundleId = __hot_updater_core.NIL_UUID, channel = "production" }) => {
|
|
112
|
+
const candidates = await orm.findMany("bundles", {
|
|
113
|
+
select: [
|
|
114
|
+
"id",
|
|
115
|
+
"should_force_update",
|
|
116
|
+
"message",
|
|
117
|
+
"storage_uri",
|
|
118
|
+
"file_hash",
|
|
119
|
+
"channel",
|
|
120
|
+
"fingerprint_hash",
|
|
121
|
+
"enabled"
|
|
122
|
+
],
|
|
123
|
+
where: (b) => b.and(b("enabled", "=", true), b("platform", "=", platform), b("id", ">=", minBundleId ?? __hot_updater_core.NIL_UUID), b("channel", "=", channel), b("fingerprint_hash", "=", fingerprintHash))
|
|
124
|
+
});
|
|
125
|
+
const byIdDesc = (a, b) => b.id.localeCompare(a.id);
|
|
126
|
+
const sorted = (candidates ?? []).slice().sort(byIdDesc);
|
|
127
|
+
const latestCandidate = sorted[0] ?? null;
|
|
128
|
+
const currentBundle = sorted.find((b) => b.id === bundleId);
|
|
129
|
+
const updateCandidate = sorted.find((b) => b.id.localeCompare(bundleId) > 0) ?? null;
|
|
130
|
+
const rollbackCandidate = sorted.find((b) => b.id.localeCompare(bundleId) < 0) ?? null;
|
|
131
|
+
if (bundleId === __hot_updater_core.NIL_UUID) {
|
|
132
|
+
if (latestCandidate && latestCandidate.id !== bundleId) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (currentBundle) {
|
|
136
|
+
if (latestCandidate && latestCandidate.id.localeCompare(currentBundle.id) > 0) return toUpdateInfo(latestCandidate, "UPDATE");
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (updateCandidate) return toUpdateInfo(updateCandidate, "UPDATE");
|
|
140
|
+
if (rollbackCandidate) return toUpdateInfo(rollbackCandidate, "ROLLBACK");
|
|
141
|
+
if (minBundleId && bundleId.localeCompare(minBundleId) <= 0) return null;
|
|
142
|
+
return INIT_BUNDLE_ROLLBACK_UPDATE_INFO;
|
|
143
|
+
};
|
|
144
|
+
if (args._updateStrategy === "appVersion") return appVersionStrategy(args);
|
|
145
|
+
if (args._updateStrategy === "fingerprint") return fingerprintStrategy(args);
|
|
146
|
+
return null;
|
|
147
|
+
},
|
|
148
|
+
async getAppUpdateInfo(args) {
|
|
149
|
+
const info = await this.getUpdateInfo(args);
|
|
150
|
+
if (!info) return null;
|
|
151
|
+
const { storageUri,...rest } = info;
|
|
152
|
+
const fileUrl = await resolveFileUrl(storageUri ?? null);
|
|
153
|
+
return {
|
|
154
|
+
...rest,
|
|
155
|
+
fileUrl
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
async getChannels() {
|
|
159
|
+
const version = await client.version();
|
|
160
|
+
const rows = await client.orm(version).findMany("bundles", { select: ["channel"] });
|
|
161
|
+
const set = new Set(rows?.map((r) => r.channel) ?? []);
|
|
162
|
+
return Array.from(set);
|
|
163
|
+
},
|
|
164
|
+
async getBundles(options) {
|
|
165
|
+
const version = await client.version();
|
|
166
|
+
const orm = client.orm(version);
|
|
167
|
+
const { where, limit, offset } = options;
|
|
168
|
+
const all = (await orm.findMany("bundles", {
|
|
169
|
+
select: [
|
|
170
|
+
"id",
|
|
171
|
+
"platform",
|
|
172
|
+
"should_force_update",
|
|
173
|
+
"enabled",
|
|
174
|
+
"file_hash",
|
|
175
|
+
"git_commit_hash",
|
|
176
|
+
"message",
|
|
177
|
+
"channel",
|
|
178
|
+
"storage_uri",
|
|
179
|
+
"target_app_version",
|
|
180
|
+
"fingerprint_hash",
|
|
181
|
+
"metadata"
|
|
182
|
+
],
|
|
183
|
+
where: (b) => {
|
|
184
|
+
const conditions = [];
|
|
185
|
+
if (where?.channel) conditions.push(b("channel", "=", where.channel));
|
|
186
|
+
if (where?.platform) conditions.push(b("platform", "=", where.platform));
|
|
187
|
+
return conditions.length > 0 ? b.and(...conditions) : true;
|
|
188
|
+
}
|
|
189
|
+
})).map((r) => ({
|
|
190
|
+
id: r.id,
|
|
191
|
+
platform: r.platform,
|
|
192
|
+
shouldForceUpdate: Boolean(r.should_force_update),
|
|
193
|
+
enabled: Boolean(r.enabled),
|
|
194
|
+
fileHash: r.file_hash,
|
|
195
|
+
gitCommitHash: r.git_commit_hash ?? null,
|
|
196
|
+
message: r.message ?? null,
|
|
197
|
+
channel: r.channel,
|
|
198
|
+
storageUri: r.storage_uri,
|
|
199
|
+
targetAppVersion: r.target_app_version ?? null,
|
|
200
|
+
fingerprintHash: r.fingerprint_hash ?? null
|
|
201
|
+
})).sort((a, b) => b.id.localeCompare(a.id));
|
|
202
|
+
const total = all.length;
|
|
203
|
+
return {
|
|
204
|
+
data: all.slice(offset, offset + limit),
|
|
205
|
+
pagination: require_calculatePagination.calculatePagination(total, {
|
|
206
|
+
limit,
|
|
207
|
+
offset
|
|
208
|
+
})
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
async insertBundle(bundle) {
|
|
212
|
+
const version = await client.version();
|
|
213
|
+
const orm = client.orm(version);
|
|
214
|
+
const values = {
|
|
215
|
+
id: bundle.id,
|
|
216
|
+
platform: bundle.platform,
|
|
217
|
+
should_force_update: bundle.shouldForceUpdate,
|
|
218
|
+
enabled: bundle.enabled,
|
|
219
|
+
file_hash: bundle.fileHash,
|
|
220
|
+
git_commit_hash: bundle.gitCommitHash,
|
|
221
|
+
message: bundle.message,
|
|
222
|
+
channel: bundle.channel,
|
|
223
|
+
storage_uri: bundle.storageUri,
|
|
224
|
+
target_app_version: bundle.targetAppVersion,
|
|
225
|
+
fingerprint_hash: bundle.fingerprintHash,
|
|
226
|
+
metadata: bundle.metadata ?? {}
|
|
227
|
+
};
|
|
228
|
+
const { id,...updateValues } = values;
|
|
229
|
+
await orm.upsert("bundles", {
|
|
230
|
+
where: (b) => b("id", "=", id),
|
|
231
|
+
create: values,
|
|
232
|
+
update: updateValues
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
async updateBundleById(bundleId, newBundle) {
|
|
236
|
+
const version = await client.version();
|
|
237
|
+
const orm = client.orm(version);
|
|
238
|
+
const current = await this.getBundleById(bundleId);
|
|
239
|
+
if (!current) throw new Error("targetBundleId not found");
|
|
240
|
+
const merged = {
|
|
241
|
+
...current,
|
|
242
|
+
...newBundle
|
|
243
|
+
};
|
|
244
|
+
const values = {
|
|
245
|
+
id: merged.id,
|
|
246
|
+
platform: merged.platform,
|
|
247
|
+
should_force_update: merged.shouldForceUpdate,
|
|
248
|
+
enabled: merged.enabled,
|
|
249
|
+
file_hash: merged.fileHash,
|
|
250
|
+
git_commit_hash: merged.gitCommitHash,
|
|
251
|
+
message: merged.message,
|
|
252
|
+
channel: merged.channel,
|
|
253
|
+
storage_uri: merged.storageUri,
|
|
254
|
+
target_app_version: merged.targetAppVersion,
|
|
255
|
+
fingerprint_hash: merged.fingerprintHash,
|
|
256
|
+
metadata: merged.metadata ?? {}
|
|
257
|
+
};
|
|
258
|
+
const { id: id2,...updateValues2 } = values;
|
|
259
|
+
await orm.upsert("bundles", {
|
|
260
|
+
where: (b) => b("id", "=", id2),
|
|
261
|
+
create: values,
|
|
262
|
+
update: updateValues2
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
async deleteBundleById(bundleId) {
|
|
266
|
+
const version = await client.version();
|
|
267
|
+
await client.orm(version).deleteMany("bundles", { where: (b) => b("id", "=", bundleId) });
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
adapterName: client.adapter.name,
|
|
271
|
+
createMigrator: () => client.createMigrator(),
|
|
272
|
+
generateSchema: client.generateSchema
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
//#endregion
|
|
277
|
+
exports.HotUpdaterDB = HotUpdaterDB;
|
|
278
|
+
exports.createOrmDatabaseCore = createOrmDatabaseCore;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as fumadb_schema0 from "fumadb/schema";
|
|
2
|
+
import * as fumadb0 from "fumadb";
|
|
3
|
+
import { InferFumaDB } from "fumadb";
|
|
4
|
+
import { FumaDBAdapter } from "fumadb/adapters";
|
|
5
|
+
|
|
6
|
+
//#region src/db/ormCore.d.ts
|
|
7
|
+
declare const HotUpdaterDB: fumadb0.FumaDBFactory<fumadb_schema0.Schema<"0.21.0", {
|
|
8
|
+
bundles: fumadb_schema0.Table<{
|
|
9
|
+
id: fumadb_schema0.IdColumn<"uuid", string, string>;
|
|
10
|
+
platform: fumadb_schema0.Column<"string", string, string>;
|
|
11
|
+
should_force_update: fumadb_schema0.Column<"bool", boolean, boolean>;
|
|
12
|
+
enabled: fumadb_schema0.Column<"bool", boolean, boolean>;
|
|
13
|
+
file_hash: fumadb_schema0.Column<"string", string, string>;
|
|
14
|
+
git_commit_hash: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
15
|
+
message: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
16
|
+
channel: fumadb_schema0.Column<"string", string, string>;
|
|
17
|
+
storage_uri: fumadb_schema0.Column<"string", string, string>;
|
|
18
|
+
target_app_version: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
19
|
+
fingerprint_hash: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
20
|
+
metadata: fumadb_schema0.Column<"json", unknown, unknown>;
|
|
21
|
+
}, {}>;
|
|
22
|
+
}>[]>;
|
|
23
|
+
type HotUpdaterClient = InferFumaDB<typeof HotUpdaterDB>;
|
|
24
|
+
type Migrator = ReturnType<HotUpdaterClient["createMigrator"]>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { HotUpdaterClient, HotUpdaterDB, Migrator };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as fumadb0 from "fumadb";
|
|
2
|
+
import { InferFumaDB } from "fumadb";
|
|
3
|
+
import * as fumadb_schema0 from "fumadb/schema";
|
|
4
|
+
import { FumaDBAdapter } from "fumadb/adapters";
|
|
5
|
+
|
|
6
|
+
//#region src/db/ormCore.d.ts
|
|
7
|
+
declare const HotUpdaterDB: fumadb0.FumaDBFactory<fumadb_schema0.Schema<"0.21.0", {
|
|
8
|
+
bundles: fumadb_schema0.Table<{
|
|
9
|
+
id: fumadb_schema0.IdColumn<"uuid", string, string>;
|
|
10
|
+
platform: fumadb_schema0.Column<"string", string, string>;
|
|
11
|
+
should_force_update: fumadb_schema0.Column<"bool", boolean, boolean>;
|
|
12
|
+
enabled: fumadb_schema0.Column<"bool", boolean, boolean>;
|
|
13
|
+
file_hash: fumadb_schema0.Column<"string", string, string>;
|
|
14
|
+
git_commit_hash: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
15
|
+
message: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
16
|
+
channel: fumadb_schema0.Column<"string", string, string>;
|
|
17
|
+
storage_uri: fumadb_schema0.Column<"string", string, string>;
|
|
18
|
+
target_app_version: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
19
|
+
fingerprint_hash: fumadb_schema0.Column<"string", string | null, string | null>;
|
|
20
|
+
metadata: fumadb_schema0.Column<"json", unknown, unknown>;
|
|
21
|
+
}, {}>;
|
|
22
|
+
}>[]>;
|
|
23
|
+
type HotUpdaterClient = InferFumaDB<typeof HotUpdaterDB>;
|
|
24
|
+
type Migrator = ReturnType<HotUpdaterClient["createMigrator"]>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { HotUpdaterClient, HotUpdaterDB, Migrator };
|