@hot-updater/cloudflare 0.36.7 → 1.0.0-rc.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/dist/iac/index.cjs +988 -10
- package/dist/iac/index.mjs +991 -14
- package/dist/index.cjs +919 -6989
- package/dist/index.d.cts +11 -47
- package/dist/index.d.mts +11 -47
- package/dist/index.mjs +917 -7006
- package/dist/worker/index.cjs +898 -336
- package/dist/worker/index.d.cts +18 -33
- package/dist/worker/index.d.mts +18 -33
- package/dist/worker/index.mjs +899 -331
- package/package.json +7 -10
- package/sql/bundles.sql +183 -35
- package/src/d1Database.spec.ts +143 -392
- package/src/d1Database.ts +55 -650
- package/src/d1Implementation.spec.ts +252 -0
- package/src/d1Implementation.ts +742 -0
- package/src/d1Query.spec.ts +46 -0
- package/src/d1Query.ts +89 -0
- package/src/d1Rows.spec.ts +111 -0
- package/src/d1Rows.ts +326 -0
- package/src/d1Sql.spec.ts +30 -0
- package/src/d1Sql.ts +175 -0
- package/src/r2S3Storage.ts +93 -137
- package/src/r2Storage.spec.ts +120 -218
- package/src/r2Storage.ts +13 -61
- package/src/r2WorkerStorage.spec.ts +161 -50
- package/src/r2WorkerStorage.ts +85 -118
- package/src/worker/d1Database.spec.ts +33 -0
- package/src/worker/d1Database.ts +52 -0
- package/src/worker/index.ts +3 -31
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +5968 -4701
- package/worker/dist/index.js.map +4 -4
- package/worker/migrations/0001_hot-updater_1.0.0.sql +197 -0
- package/worker/migrations/0002_hot-updater_artifact_byte_sizes.sql +11 -0
- package/worker/src/databaseMigration.integration.spec.ts +114 -0
- package/worker/src/databasePlugins.integration.spec.ts +292 -0
- package/worker/src/index.integration.spec.ts +102 -294
- package/worker/src/index.ts +14 -66
- package/worker/src/storagePlugin.integration.spec.ts +59 -0
- package/worker/wrangler.json +5 -2
- package/src/cloudflareWorkerDatabase.spec.ts +0 -260
- package/src/cloudflareWorkerDatabase.ts +0 -651
- package/src/r2WranglerStorage.ts +0 -193
- package/worker/migrations/0001_hot-updater_init.sql +0 -16
- package/worker/migrations/0002_hot-updater_0.13.0.sql +0 -8
- package/worker/migrations/0003_hot-updater_0.18.0.sql +0 -38
- package/worker/migrations/0004_hot-updater_0.29.0.sql +0 -11
- package/worker/migrations/0005_hot-updater_0.31.0.sql +0 -24
package/src/d1Database.ts
CHANGED
|
@@ -1,660 +1,65 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
getAssetBaseStorageUri,
|
|
4
|
-
getBundlePatches,
|
|
5
|
-
getManifestFileHash,
|
|
6
|
-
getManifestStorageUri,
|
|
7
|
-
NIL_UUID,
|
|
8
|
-
stripBundleArtifactMetadata,
|
|
9
|
-
} from "@hot-updater/core";
|
|
10
|
-
import type {
|
|
11
|
-
Bundle,
|
|
12
|
-
DatabaseBundleQueryOrder,
|
|
13
|
-
DatabaseBundleQueryWhere,
|
|
14
|
-
PaginationOptions,
|
|
15
|
-
} from "@hot-updater/plugin-core";
|
|
16
|
-
import {
|
|
17
|
-
calculatePagination,
|
|
18
|
-
createDatabasePlugin,
|
|
19
|
-
filterCompatibleAppVersions,
|
|
20
|
-
resolveUpdateInfoFromBundles,
|
|
21
|
-
} from "@hot-updater/plugin-core";
|
|
1
|
+
import { createDatabasePlugin } from "@hot-updater/plugin-core";
|
|
2
|
+
import { createDatabasePluginAdapter } from "@hot-updater/plugin-core/internal";
|
|
22
3
|
import Cloudflare from "cloudflare";
|
|
23
|
-
import minify from "pg-minify";
|
|
24
4
|
|
|
25
|
-
|
|
26
|
-
databaseId: string;
|
|
27
|
-
accountId: string;
|
|
28
|
-
cloudflareApiToken: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Helper interfaces for clarity
|
|
32
|
-
type QueryConditions = DatabaseBundleQueryWhere;
|
|
33
|
-
|
|
34
|
-
interface BuildQueryResult {
|
|
35
|
-
sql: string;
|
|
36
|
-
params: any[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const buildJsonEachInClause = (
|
|
40
|
-
columnName: string,
|
|
41
|
-
values: string[],
|
|
42
|
-
params: any[],
|
|
43
|
-
) => {
|
|
44
|
-
if (values.length === 0) {
|
|
45
|
-
return "1 = 0";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
params.push(JSON.stringify(values));
|
|
49
|
-
return `${columnName} IN (SELECT value FROM json_each(?))`;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
interface D1BundleRow {
|
|
53
|
-
id: string;
|
|
54
|
-
channel: string;
|
|
55
|
-
enabled: number | boolean;
|
|
56
|
-
should_force_update: number | boolean;
|
|
57
|
-
file_hash: string;
|
|
58
|
-
git_commit_hash: string | null;
|
|
59
|
-
message: string | null;
|
|
60
|
-
platform: "ios" | "android";
|
|
61
|
-
target_app_version: string | null;
|
|
62
|
-
storage_uri: string;
|
|
63
|
-
fingerprint_hash: string | null;
|
|
64
|
-
metadata: unknown;
|
|
65
|
-
manifest_storage_uri?: string | null;
|
|
66
|
-
manifest_file_hash?: string | null;
|
|
67
|
-
asset_base_storage_uri?: string | null;
|
|
68
|
-
rollout_cohort_count: number | null;
|
|
69
|
-
target_cohorts: string | null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
interface D1BundlePatchRow {
|
|
73
|
-
id: string;
|
|
74
|
-
bundle_id: string;
|
|
75
|
-
base_bundle_id: string;
|
|
76
|
-
base_file_hash: string;
|
|
77
|
-
patch_file_hash: string;
|
|
78
|
-
patch_storage_uri: string;
|
|
79
|
-
order_index: number | null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function resolvePage<T>(singlePage: any): Promise<T[]> {
|
|
83
|
-
const results: T[] = [];
|
|
84
|
-
for await (const page of singlePage.iterPages()) {
|
|
85
|
-
const data = page.result.flatMap((r: any) => r.results);
|
|
86
|
-
results.push(...(data as T[]));
|
|
87
|
-
}
|
|
88
|
-
return results;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Helper function to build WHERE clause
|
|
92
|
-
function buildWhereClause(conditions: QueryConditions): BuildQueryResult {
|
|
93
|
-
const clauses: string[] = [];
|
|
94
|
-
const params: any[] = [];
|
|
95
|
-
|
|
96
|
-
if (conditions.channel) {
|
|
97
|
-
clauses.push("channel = ?");
|
|
98
|
-
params.push(conditions.channel);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (conditions.platform) {
|
|
102
|
-
clauses.push("platform = ?");
|
|
103
|
-
params.push(conditions.platform);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (conditions.enabled !== undefined) {
|
|
107
|
-
clauses.push("enabled = ?");
|
|
108
|
-
params.push(conditions.enabled ? 1 : 0);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (conditions.id?.in) {
|
|
112
|
-
clauses.push(buildJsonEachInClause("id", conditions.id.in, params));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (conditions.id?.eq) {
|
|
116
|
-
clauses.push("id = ?");
|
|
117
|
-
params.push(conditions.id.eq);
|
|
118
|
-
}
|
|
5
|
+
import { createD1Implementation, type D1Statement } from "./d1Implementation";
|
|
119
6
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (conditions.id?.gte) {
|
|
126
|
-
clauses.push("id >= ?");
|
|
127
|
-
params.push(conditions.id.gte);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (conditions.id?.lt) {
|
|
131
|
-
clauses.push("id < ?");
|
|
132
|
-
params.push(conditions.id.lt);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (conditions.id?.lte) {
|
|
136
|
-
clauses.push("id <= ?");
|
|
137
|
-
params.push(conditions.id.lte);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (conditions.targetAppVersionNotNull) {
|
|
141
|
-
clauses.push("target_app_version IS NOT NULL");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (conditions.targetAppVersion !== undefined) {
|
|
145
|
-
if (conditions.targetAppVersion === null) {
|
|
146
|
-
clauses.push("target_app_version IS NULL");
|
|
147
|
-
} else {
|
|
148
|
-
clauses.push("target_app_version = ?");
|
|
149
|
-
params.push(conditions.targetAppVersion);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (conditions.targetAppVersionIn) {
|
|
154
|
-
clauses.push(
|
|
155
|
-
buildJsonEachInClause(
|
|
156
|
-
"target_app_version",
|
|
157
|
-
conditions.targetAppVersionIn,
|
|
158
|
-
params,
|
|
159
|
-
),
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (conditions.fingerprintHash !== undefined) {
|
|
164
|
-
if (conditions.fingerprintHash === null) {
|
|
165
|
-
clauses.push("fingerprint_hash IS NULL");
|
|
166
|
-
} else {
|
|
167
|
-
clauses.push("fingerprint_hash = ?");
|
|
168
|
-
params.push(conditions.fingerprintHash);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const whereClause =
|
|
173
|
-
clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
|
|
174
|
-
|
|
175
|
-
return { sql: whereClause, params };
|
|
7
|
+
export interface D1DatabaseConfig {
|
|
8
|
+
readonly databaseId: string;
|
|
9
|
+
readonly accountId: string;
|
|
10
|
+
readonly cloudflareApiToken: string;
|
|
176
11
|
}
|
|
177
12
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
13
|
+
export const d1Database = (config: D1DatabaseConfig) => {
|
|
14
|
+
const cloudflare = new Cloudflare({
|
|
15
|
+
apiToken: config.cloudflareApiToken,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const execute = async (
|
|
19
|
+
statements: readonly D1Statement[],
|
|
20
|
+
): Promise<readonly (readonly unknown[])[]> => {
|
|
21
|
+
const first = statements[0];
|
|
22
|
+
if (first === undefined) return [];
|
|
23
|
+
const body =
|
|
24
|
+
statements.length === 1
|
|
25
|
+
? {
|
|
26
|
+
account_id: config.accountId,
|
|
27
|
+
sql: first.sql,
|
|
28
|
+
params: [...first.params],
|
|
29
|
+
}
|
|
30
|
+
: {
|
|
31
|
+
account_id: config.accountId,
|
|
32
|
+
batch: statements.map(({ sql, params }) => ({
|
|
33
|
+
sql,
|
|
34
|
+
params: [...params],
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
// cloudflare@4 predates the D1 REST API's parameterized batch body type.
|
|
38
|
+
const page = await cloudflare.d1.database.query(config.databaseId, {
|
|
39
|
+
...body,
|
|
40
|
+
} as unknown as Parameters<typeof cloudflare.d1.database.query>[1]);
|
|
41
|
+
const results: unknown[][] = [];
|
|
42
|
+
for await (const resultPage of page.iterPages()) {
|
|
43
|
+
for (const result of resultPage.result) {
|
|
44
|
+
results.push(result.results ?? []);
|
|
188
45
|
}
|
|
189
|
-
} catch {
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const parseMetadata = (value: unknown): Bundle["metadata"] => {
|
|
197
|
-
if (!value) return undefined;
|
|
198
|
-
if (typeof value === "string") {
|
|
199
|
-
try {
|
|
200
|
-
return parseMetadata(JSON.parse(value) as unknown);
|
|
201
|
-
} catch {
|
|
202
|
-
return undefined;
|
|
203
46
|
}
|
|
204
|
-
|
|
205
|
-
return typeof value === "object" && !Array.isArray(value)
|
|
206
|
-
? (value as Bundle["metadata"])
|
|
207
|
-
: undefined;
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const buildBundlePatchId = (bundleId: string, baseBundleId: string) =>
|
|
211
|
-
`${bundleId}:${baseBundleId}`;
|
|
212
|
-
|
|
213
|
-
const bundleToPatchRows = (bundle: Bundle): D1BundlePatchRow[] =>
|
|
214
|
-
getBundlePatches(bundle).map((patch, index) => ({
|
|
215
|
-
id: buildBundlePatchId(bundle.id, patch.baseBundleId),
|
|
216
|
-
bundle_id: bundle.id,
|
|
217
|
-
base_bundle_id: patch.baseBundleId,
|
|
218
|
-
base_file_hash: patch.baseFileHash,
|
|
219
|
-
patch_file_hash: patch.patchFileHash,
|
|
220
|
-
patch_storage_uri: patch.patchStorageUri,
|
|
221
|
-
order_index: index,
|
|
222
|
-
}));
|
|
223
|
-
|
|
224
|
-
function transformRowToBundle(
|
|
225
|
-
row: D1BundleRow,
|
|
226
|
-
patchRows: D1BundlePatchRow[] = [],
|
|
227
|
-
): Bundle {
|
|
228
|
-
const rawMetadata = parseMetadata(row.metadata);
|
|
229
|
-
const patches = patchRows
|
|
230
|
-
.slice()
|
|
231
|
-
.sort(
|
|
232
|
-
(left, right) =>
|
|
233
|
-
(left.order_index ?? 0) - (right.order_index ?? 0) ||
|
|
234
|
-
left.base_bundle_id.localeCompare(right.base_bundle_id),
|
|
235
|
-
)
|
|
236
|
-
.map((patch) => ({
|
|
237
|
-
baseBundleId: patch.base_bundle_id,
|
|
238
|
-
baseFileHash: patch.base_file_hash,
|
|
239
|
-
patchFileHash: patch.patch_file_hash,
|
|
240
|
-
patchStorageUri: patch.patch_storage_uri,
|
|
241
|
-
}));
|
|
242
|
-
const primaryPatch = patches[0] ?? null;
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
id: row.id,
|
|
246
|
-
channel: row.channel,
|
|
247
|
-
enabled: Boolean(row.enabled),
|
|
248
|
-
shouldForceUpdate: Boolean(row.should_force_update),
|
|
249
|
-
fileHash: row.file_hash,
|
|
250
|
-
gitCommitHash: row.git_commit_hash,
|
|
251
|
-
message: row.message,
|
|
252
|
-
platform: row.platform,
|
|
253
|
-
targetAppVersion: row.target_app_version,
|
|
254
|
-
storageUri: row.storage_uri,
|
|
255
|
-
fingerprintHash: row.fingerprint_hash,
|
|
256
|
-
metadata: stripBundleArtifactMetadata(rawMetadata),
|
|
257
|
-
manifestStorageUri: row.manifest_storage_uri ?? null,
|
|
258
|
-
manifestFileHash: row.manifest_file_hash ?? null,
|
|
259
|
-
assetBaseStorageUri: row.asset_base_storage_uri ?? null,
|
|
260
|
-
patches,
|
|
261
|
-
patchBaseBundleId: primaryPatch?.baseBundleId ?? null,
|
|
262
|
-
patchBaseFileHash: primaryPatch?.baseFileHash ?? null,
|
|
263
|
-
patchFileHash: primaryPatch?.patchFileHash ?? null,
|
|
264
|
-
patchStorageUri: primaryPatch?.patchStorageUri ?? null,
|
|
265
|
-
rolloutCohortCount:
|
|
266
|
-
(row.rollout_cohort_count as number | null) ??
|
|
267
|
-
DEFAULT_ROLLOUT_COHORT_COUNT,
|
|
268
|
-
targetCohorts: parseTargetCohorts(row.target_cohorts as unknown),
|
|
47
|
+
return results;
|
|
269
48
|
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export const d1Database = createDatabasePlugin<D1DatabaseConfig>({
|
|
273
|
-
name: "d1Database",
|
|
274
|
-
factory: (config) => {
|
|
275
|
-
const cf = new Cloudflare({
|
|
276
|
-
apiToken: config.cloudflareApiToken,
|
|
277
|
-
});
|
|
278
|
-
const getPatchMap = async (bundleIds: string[]) => {
|
|
279
|
-
const patchMap = new Map<string, D1BundlePatchRow[]>();
|
|
280
|
-
|
|
281
|
-
if (bundleIds.length === 0) {
|
|
282
|
-
return patchMap;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const sql = minify(`
|
|
286
|
-
SELECT *
|
|
287
|
-
FROM bundle_patches
|
|
288
|
-
WHERE bundle_id IN (SELECT value FROM json_each(?))
|
|
289
|
-
ORDER BY order_index ASC, base_bundle_id ASC
|
|
290
|
-
`);
|
|
291
|
-
|
|
292
|
-
const result = await cf.d1.database.query(config.databaseId, {
|
|
293
|
-
account_id: config.accountId,
|
|
294
|
-
sql,
|
|
295
|
-
params: [JSON.stringify(bundleIds)],
|
|
296
|
-
});
|
|
297
|
-
const rows = await resolvePage<D1BundlePatchRow>(result);
|
|
298
|
-
|
|
299
|
-
for (const row of rows) {
|
|
300
|
-
const current = patchMap.get(row.bundle_id) ?? [];
|
|
301
|
-
current.push(row);
|
|
302
|
-
patchMap.set(row.bundle_id, current);
|
|
303
|
-
}
|
|
304
49
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const { sql: whereClause, params } = buildWhereClause(conditions);
|
|
311
|
-
const countSql = minify(
|
|
312
|
-
`SELECT COUNT(*) as total FROM bundles${whereClause}`,
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
const countResult = await cf.d1.database.query(config.databaseId, {
|
|
316
|
-
account_id: config.accountId,
|
|
317
|
-
sql: countSql,
|
|
318
|
-
params,
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
const rows = await resolvePage<{ total: number }>(countResult);
|
|
322
|
-
return rows[0]?.total || 0;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Helper function to get paginated bundles
|
|
326
|
-
async function getPaginatedBundles(
|
|
327
|
-
conditions: QueryConditions,
|
|
328
|
-
limit: number,
|
|
329
|
-
offset: number,
|
|
330
|
-
orderBy?: DatabaseBundleQueryOrder,
|
|
331
|
-
): Promise<Bundle[]> {
|
|
332
|
-
const { sql: whereClause, params } = buildWhereClause(conditions);
|
|
333
|
-
const orderBySql =
|
|
334
|
-
orderBy?.direction === "asc" ? "ORDER BY id ASC" : "ORDER BY id DESC";
|
|
335
|
-
|
|
336
|
-
// Build the complete query
|
|
337
|
-
const sql = minify(`
|
|
338
|
-
SELECT * FROM bundles
|
|
339
|
-
${whereClause}
|
|
340
|
-
${orderBySql}
|
|
341
|
-
LIMIT ?
|
|
342
|
-
OFFSET ?
|
|
343
|
-
`);
|
|
344
|
-
|
|
345
|
-
// Add pagination params
|
|
346
|
-
params.push(limit, offset);
|
|
347
|
-
|
|
348
|
-
const result = await cf.d1.database.query(config.databaseId, {
|
|
349
|
-
account_id: config.accountId,
|
|
350
|
-
sql,
|
|
351
|
-
params,
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
const rows = await resolvePage<D1BundleRow>(result);
|
|
355
|
-
const patchMap = await getPatchMap(rows.map((row) => row.id));
|
|
356
|
-
return rows.map((row) => transformRowToBundle(row, patchMap.get(row.id)));
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
async function queryBundlesForUpdateInfo(
|
|
360
|
-
conditions: QueryConditions,
|
|
361
|
-
): Promise<Bundle[]> {
|
|
362
|
-
const { sql: whereClause, params } = buildWhereClause(conditions);
|
|
363
|
-
const sql = minify(`
|
|
364
|
-
SELECT * FROM bundles
|
|
365
|
-
${whereClause}
|
|
366
|
-
`);
|
|
367
|
-
|
|
368
|
-
const result = await cf.d1.database.query(config.databaseId, {
|
|
369
|
-
account_id: config.accountId,
|
|
370
|
-
sql,
|
|
371
|
-
params,
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
const rows = await resolvePage<D1BundleRow>(result);
|
|
375
|
-
const patchMap = await getPatchMap(rows.map((row) => row.id));
|
|
376
|
-
return rows.map((row) => transformRowToBundle(row, patchMap.get(row.id)));
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
async function getTargetAppVersionsForUpdateInfo({
|
|
380
|
-
platform,
|
|
381
|
-
channel,
|
|
382
|
-
minBundleId,
|
|
383
|
-
}: {
|
|
384
|
-
platform: Bundle["platform"];
|
|
385
|
-
channel: string;
|
|
386
|
-
minBundleId: string;
|
|
387
|
-
}): Promise<string[]> {
|
|
388
|
-
const sql = minify(`
|
|
389
|
-
SELECT target_app_version
|
|
390
|
-
FROM bundles
|
|
391
|
-
WHERE channel = ?
|
|
392
|
-
AND platform = ?
|
|
393
|
-
AND enabled = 1
|
|
394
|
-
AND id >= ?
|
|
395
|
-
AND target_app_version IS NOT NULL
|
|
396
|
-
GROUP BY target_app_version
|
|
397
|
-
`);
|
|
398
|
-
|
|
399
|
-
const result = await cf.d1.database.query(config.databaseId, {
|
|
400
|
-
account_id: config.accountId,
|
|
401
|
-
sql,
|
|
402
|
-
params: [channel, platform, minBundleId],
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
const rows = await resolvePage<{ target_app_version: string }>(result);
|
|
406
|
-
return rows.map((row) => row.target_app_version);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
return {
|
|
410
|
-
async getUpdateInfo(args, context) {
|
|
411
|
-
const channel = args.channel ?? "production";
|
|
412
|
-
const minBundleId = args.minBundleId ?? NIL_UUID;
|
|
413
|
-
|
|
414
|
-
if (args._updateStrategy === "appVersion") {
|
|
415
|
-
const targetAppVersions = await getTargetAppVersionsForUpdateInfo({
|
|
416
|
-
platform: args.platform,
|
|
417
|
-
channel,
|
|
418
|
-
minBundleId,
|
|
419
|
-
});
|
|
420
|
-
const compatibleAppVersions = filterCompatibleAppVersions(
|
|
421
|
-
targetAppVersions,
|
|
422
|
-
args.appVersion,
|
|
423
|
-
);
|
|
424
|
-
const bundles =
|
|
425
|
-
compatibleAppVersions.length > 0
|
|
426
|
-
? await queryBundlesForUpdateInfo({
|
|
427
|
-
enabled: true,
|
|
428
|
-
platform: args.platform,
|
|
429
|
-
channel,
|
|
430
|
-
id: {
|
|
431
|
-
gte: minBundleId,
|
|
432
|
-
},
|
|
433
|
-
targetAppVersionIn: compatibleAppVersions,
|
|
434
|
-
})
|
|
435
|
-
: [];
|
|
436
|
-
|
|
437
|
-
return resolveUpdateInfoFromBundles({
|
|
438
|
-
args: { ...args, channel, minBundleId },
|
|
439
|
-
bundles,
|
|
440
|
-
context,
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const bundles = await queryBundlesForUpdateInfo({
|
|
445
|
-
enabled: true,
|
|
446
|
-
platform: args.platform,
|
|
447
|
-
channel,
|
|
448
|
-
id: {
|
|
449
|
-
gte: minBundleId,
|
|
450
|
-
},
|
|
451
|
-
fingerprintHash: args.fingerprintHash,
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
return resolveUpdateInfoFromBundles({
|
|
455
|
-
args: { ...args, channel, minBundleId },
|
|
456
|
-
bundles,
|
|
457
|
-
context,
|
|
458
|
-
});
|
|
50
|
+
const adapter = createDatabasePluginAdapter(
|
|
51
|
+
"d1Database",
|
|
52
|
+
createD1Implementation({
|
|
53
|
+
async query(sql, params) {
|
|
54
|
+
return (await execute([{ sql, params }])).flat();
|
|
459
55
|
},
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
getPatchMap([bundleId]),
|
|
471
|
-
]);
|
|
472
|
-
|
|
473
|
-
const rows = await resolvePage<D1BundleRow>(singlePage);
|
|
474
|
-
|
|
475
|
-
if (rows.length === 0) {
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
return transformRowToBundle(rows[0], patchMap.get(bundleId));
|
|
480
|
-
},
|
|
481
|
-
|
|
482
|
-
async getBundles(options) {
|
|
483
|
-
const { where = {}, limit, orderBy } = options;
|
|
484
|
-
const offset =
|
|
485
|
-
(("offset" in options ? options.offset : undefined) as
|
|
486
|
-
| number
|
|
487
|
-
| undefined) ?? 0;
|
|
488
|
-
|
|
489
|
-
// 1. Get total count for pagination
|
|
490
|
-
const totalCount = await getTotalCount(where);
|
|
491
|
-
|
|
492
|
-
// 2. Get paginated bundles
|
|
493
|
-
const bundles = await getPaginatedBundles(
|
|
494
|
-
where,
|
|
495
|
-
limit,
|
|
496
|
-
offset,
|
|
497
|
-
orderBy,
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
// 3. Calculate pagination metadata
|
|
501
|
-
const paginationOptions: PaginationOptions = { limit, offset };
|
|
502
|
-
const pagination = calculatePagination(totalCount, paginationOptions);
|
|
503
|
-
|
|
504
|
-
return {
|
|
505
|
-
data: bundles,
|
|
506
|
-
pagination,
|
|
507
|
-
};
|
|
508
|
-
},
|
|
509
|
-
|
|
510
|
-
async getChannels() {
|
|
511
|
-
const sql = minify(/* sql */ `
|
|
512
|
-
SELECT channel FROM bundles GROUP BY channel
|
|
513
|
-
`);
|
|
514
|
-
const singlePage = await cf.d1.database.query(config.databaseId, {
|
|
515
|
-
account_id: config.accountId,
|
|
516
|
-
sql,
|
|
517
|
-
params: [],
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
const rows = await resolvePage<{ channel: string }>(singlePage);
|
|
521
|
-
return rows.map((row) => row.channel);
|
|
522
|
-
},
|
|
523
|
-
|
|
524
|
-
async commitBundle({ changedSets }) {
|
|
525
|
-
if (changedSets.length === 0) {
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Process each operation sequentially
|
|
530
|
-
for (const op of changedSets) {
|
|
531
|
-
if (op.operation === "delete") {
|
|
532
|
-
// Handle delete operation
|
|
533
|
-
const deleteSql = minify(/* sql */ `
|
|
534
|
-
DELETE FROM bundles WHERE id = ?
|
|
535
|
-
`);
|
|
536
|
-
|
|
537
|
-
const deletePatchSql = minify(/* sql */ `
|
|
538
|
-
DELETE FROM bundle_patches WHERE bundle_id = ?
|
|
539
|
-
`);
|
|
540
|
-
await cf.d1.database.query(config.databaseId, {
|
|
541
|
-
account_id: config.accountId,
|
|
542
|
-
sql: deletePatchSql,
|
|
543
|
-
params: [op.data.id],
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
const deleteBasePatchSql = minify(/* sql */ `
|
|
547
|
-
DELETE FROM bundle_patches WHERE base_bundle_id = ?
|
|
548
|
-
`);
|
|
549
|
-
await cf.d1.database.query(config.databaseId, {
|
|
550
|
-
account_id: config.accountId,
|
|
551
|
-
sql: deleteBasePatchSql,
|
|
552
|
-
params: [op.data.id],
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
await cf.d1.database.query(config.databaseId, {
|
|
556
|
-
account_id: config.accountId,
|
|
557
|
-
sql: deleteSql,
|
|
558
|
-
params: [op.data.id],
|
|
559
|
-
});
|
|
560
|
-
} else if (op.operation === "insert" || op.operation === "update") {
|
|
561
|
-
// Handle insert and update operations
|
|
562
|
-
const bundle = op.data;
|
|
563
|
-
const upsertSql = minify(/* sql */ `
|
|
564
|
-
INSERT OR REPLACE INTO bundles (
|
|
565
|
-
id,
|
|
566
|
-
channel,
|
|
567
|
-
enabled,
|
|
568
|
-
should_force_update,
|
|
569
|
-
file_hash,
|
|
570
|
-
git_commit_hash,
|
|
571
|
-
message,
|
|
572
|
-
platform,
|
|
573
|
-
target_app_version,
|
|
574
|
-
storage_uri,
|
|
575
|
-
fingerprint_hash,
|
|
576
|
-
metadata,
|
|
577
|
-
manifest_storage_uri,
|
|
578
|
-
manifest_file_hash,
|
|
579
|
-
asset_base_storage_uri,
|
|
580
|
-
rollout_cohort_count,
|
|
581
|
-
target_cohorts
|
|
582
|
-
)
|
|
583
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
584
|
-
`);
|
|
585
|
-
|
|
586
|
-
const params = [
|
|
587
|
-
bundle.id,
|
|
588
|
-
bundle.channel,
|
|
589
|
-
bundle.enabled ? 1 : 0,
|
|
590
|
-
bundle.shouldForceUpdate ? 1 : 0,
|
|
591
|
-
bundle.fileHash,
|
|
592
|
-
bundle.gitCommitHash || null,
|
|
593
|
-
bundle.message || null,
|
|
594
|
-
bundle.platform,
|
|
595
|
-
bundle.targetAppVersion,
|
|
596
|
-
bundle.storageUri,
|
|
597
|
-
bundle.fingerprintHash,
|
|
598
|
-
JSON.stringify(
|
|
599
|
-
stripBundleArtifactMetadata(bundle.metadata) ?? {},
|
|
600
|
-
),
|
|
601
|
-
getManifestStorageUri(bundle),
|
|
602
|
-
getManifestFileHash(bundle),
|
|
603
|
-
getAssetBaseStorageUri(bundle),
|
|
604
|
-
bundle.rolloutCohortCount ?? DEFAULT_ROLLOUT_COHORT_COUNT,
|
|
605
|
-
bundle.targetCohorts
|
|
606
|
-
? JSON.stringify(bundle.targetCohorts)
|
|
607
|
-
: null,
|
|
608
|
-
];
|
|
609
|
-
|
|
610
|
-
await cf.d1.database.query(config.databaseId, {
|
|
611
|
-
account_id: config.accountId,
|
|
612
|
-
sql: upsertSql,
|
|
613
|
-
params: params as string[],
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
await cf.d1.database.query(config.databaseId, {
|
|
617
|
-
account_id: config.accountId,
|
|
618
|
-
sql: minify(`
|
|
619
|
-
DELETE FROM bundle_patches WHERE bundle_id = ?
|
|
620
|
-
`),
|
|
621
|
-
params: [bundle.id],
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
const patchRows = bundleToPatchRows(bundle);
|
|
625
|
-
if (patchRows.length > 0) {
|
|
626
|
-
const patchInsertSql = minify(`
|
|
627
|
-
INSERT OR REPLACE INTO bundle_patches (
|
|
628
|
-
id,
|
|
629
|
-
bundle_id,
|
|
630
|
-
base_bundle_id,
|
|
631
|
-
base_file_hash,
|
|
632
|
-
patch_file_hash,
|
|
633
|
-
patch_storage_uri,
|
|
634
|
-
order_index
|
|
635
|
-
)
|
|
636
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
637
|
-
`);
|
|
638
|
-
|
|
639
|
-
for (const patchRow of patchRows) {
|
|
640
|
-
await cf.d1.database.query(config.databaseId, {
|
|
641
|
-
account_id: config.accountId,
|
|
642
|
-
sql: patchInsertSql,
|
|
643
|
-
params: [
|
|
644
|
-
patchRow.id,
|
|
645
|
-
patchRow.bundle_id,
|
|
646
|
-
patchRow.base_bundle_id,
|
|
647
|
-
patchRow.base_file_hash,
|
|
648
|
-
patchRow.patch_file_hash,
|
|
649
|
-
patchRow.patch_storage_uri,
|
|
650
|
-
String(patchRow.order_index ?? 0),
|
|
651
|
-
],
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
},
|
|
658
|
-
};
|
|
659
|
-
},
|
|
660
|
-
});
|
|
56
|
+
batch: execute,
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
return createDatabasePlugin({
|
|
60
|
+
name: "d1Database",
|
|
61
|
+
models: adapter.models,
|
|
62
|
+
commit: adapter.commit,
|
|
63
|
+
...(adapter.dispose ? { dispose: adapter.dispose } : {}),
|
|
64
|
+
});
|
|
65
|
+
};
|