@hot-updater/cloudflare 0.36.6 → 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.
Files changed (49) hide show
  1. package/dist/iac/index.cjs +989 -11
  2. package/dist/iac/index.mjs +992 -15
  3. package/dist/index.cjs +919 -6989
  4. package/dist/index.d.cts +11 -47
  5. package/dist/index.d.mts +11 -47
  6. package/dist/index.mjs +917 -7006
  7. package/dist/worker/index.cjs +898 -336
  8. package/dist/worker/index.d.cts +18 -33
  9. package/dist/worker/index.d.mts +18 -33
  10. package/dist/worker/index.mjs +899 -331
  11. package/package.json +7 -10
  12. package/sql/bundles.sql +183 -35
  13. package/src/d1Database.spec.ts +143 -392
  14. package/src/d1Database.ts +55 -650
  15. package/src/d1Implementation.spec.ts +252 -0
  16. package/src/d1Implementation.ts +742 -0
  17. package/src/d1Query.spec.ts +46 -0
  18. package/src/d1Query.ts +89 -0
  19. package/src/d1Rows.spec.ts +111 -0
  20. package/src/d1Rows.ts +326 -0
  21. package/src/d1Sql.spec.ts +30 -0
  22. package/src/d1Sql.ts +175 -0
  23. package/src/r2S3Storage.ts +93 -137
  24. package/src/r2Storage.spec.ts +120 -218
  25. package/src/r2Storage.ts +13 -61
  26. package/src/r2WorkerStorage.spec.ts +161 -50
  27. package/src/r2WorkerStorage.ts +85 -118
  28. package/src/worker/d1Database.spec.ts +33 -0
  29. package/src/worker/d1Database.ts +52 -0
  30. package/src/worker/index.ts +3 -31
  31. package/worker/dist/README.md +1 -1
  32. package/worker/dist/index.js +5968 -4701
  33. package/worker/dist/index.js.map +4 -4
  34. package/worker/migrations/0001_hot-updater_1.0.0.sql +197 -0
  35. package/worker/migrations/0002_hot-updater_artifact_byte_sizes.sql +11 -0
  36. package/worker/src/databaseMigration.integration.spec.ts +114 -0
  37. package/worker/src/databasePlugins.integration.spec.ts +292 -0
  38. package/worker/src/index.integration.spec.ts +102 -294
  39. package/worker/src/index.ts +14 -66
  40. package/worker/src/storagePlugin.integration.spec.ts +59 -0
  41. package/worker/wrangler.json +5 -2
  42. package/src/cloudflareWorkerDatabase.spec.ts +0 -260
  43. package/src/cloudflareWorkerDatabase.ts +0 -651
  44. package/src/r2WranglerStorage.ts +0 -193
  45. package/worker/migrations/0001_hot-updater_init.sql +0 -16
  46. package/worker/migrations/0002_hot-updater_0.13.0.sql +0 -8
  47. package/worker/migrations/0003_hot-updater_0.18.0.sql +0 -38
  48. package/worker/migrations/0004_hot-updater_0.29.0.sql +0 -11
  49. package/worker/migrations/0005_hot-updater_0.31.0.sql +0 -24
package/src/d1Database.ts CHANGED
@@ -1,660 +1,65 @@
1
- import {
2
- DEFAULT_ROLLOUT_COHORT_COUNT,
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
- export interface D1DatabaseConfig {
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
- if (conditions.id?.gt) {
121
- clauses.push("id > ?");
122
- params.push(conditions.id.gt);
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
- function parseTargetCohorts(value: unknown): string[] | null {
179
- if (!value) return null;
180
- if (Array.isArray(value)) {
181
- return value.filter((v): v is string => typeof v === "string");
182
- }
183
- if (typeof value === "string") {
184
- try {
185
- const parsed = JSON.parse(value) as unknown;
186
- if (Array.isArray(parsed)) {
187
- return parsed.filter((v): v is string => typeof v === "string");
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
- return patchMap;
306
- };
307
-
308
- // Helper function to get total count
309
- async function getTotalCount(conditions: QueryConditions): Promise<number> {
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
- async getBundleById(bundleId) {
462
- const sql = minify(/* sql */ `
463
- SELECT * FROM bundles WHERE id = ? LIMIT 1`);
464
- const [singlePage, patchMap] = await Promise.all([
465
- cf.d1.database.query(config.databaseId, {
466
- account_id: config.accountId,
467
- sql,
468
- params: [bundleId],
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
+ };