@hot-updater/cloudflare 0.36.7 → 1.0.0-rc.1
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 +205 -0
- package/worker/src/databaseMigration.integration.spec.ts +130 -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.spec.ts
CHANGED
|
@@ -1,427 +1,178 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
setupBundleMethodsTestSuite,
|
|
4
|
-
setupGetUpdateInfoTestSuite,
|
|
5
|
-
} from "@hot-updater/test-utils";
|
|
6
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
1
|
+
import { beforeEach, expect, it, vi } from "vitest";
|
|
7
2
|
|
|
8
3
|
import { d1Database } from "./d1Database";
|
|
9
4
|
|
|
10
|
-
type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
enabled: number | boolean;
|
|
14
|
-
should_force_update: number | boolean;
|
|
15
|
-
file_hash: string;
|
|
16
|
-
git_commit_hash: string | null;
|
|
17
|
-
message: string | null;
|
|
18
|
-
platform: "ios" | "android";
|
|
19
|
-
target_app_version: string | null;
|
|
20
|
-
storage_uri: string;
|
|
21
|
-
fingerprint_hash: string | null;
|
|
22
|
-
metadata: string;
|
|
23
|
-
manifest_storage_uri?: string | null;
|
|
24
|
-
manifest_file_hash?: string | null;
|
|
25
|
-
asset_base_storage_uri?: string | null;
|
|
26
|
-
rollout_cohort_count: number | null;
|
|
27
|
-
target_cohorts: string | null;
|
|
5
|
+
type RecordedQuery = {
|
|
6
|
+
readonly sql: string;
|
|
7
|
+
readonly params: readonly string[];
|
|
28
8
|
};
|
|
29
9
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const createPage = <T>(results: T[]) => ({
|
|
50
|
-
async *iterPages() {
|
|
51
|
-
yield {
|
|
52
|
-
result: [{ results }],
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const getFilteredRows = (sql: string, params: any[]) => {
|
|
58
|
-
let filteredRows = Array.from(rows.values());
|
|
59
|
-
let index = 0;
|
|
60
|
-
|
|
61
|
-
const consumeInValues = (pattern: RegExp) => {
|
|
62
|
-
const match = sql.match(pattern);
|
|
63
|
-
if (!match) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const body = match[1] ?? "";
|
|
68
|
-
if (body.includes("json_each(")) {
|
|
69
|
-
const values = JSON.parse(String(params[index++])) as unknown;
|
|
70
|
-
return Array.isArray(values) ? values : [];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const count = (body.match(/\?/g) ?? []).length;
|
|
74
|
-
const values = params.slice(index, index + count);
|
|
75
|
-
index += count;
|
|
76
|
-
return values;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
if (sql.includes("channel = ?")) {
|
|
80
|
-
const channel = params[index++];
|
|
81
|
-
filteredRows = filteredRows.filter((row) => row.channel === channel);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (sql.includes("platform = ?")) {
|
|
85
|
-
const platform = params[index++];
|
|
86
|
-
filteredRows = filteredRows.filter((row) => row.platform === platform);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (sql.includes("enabled = ?")) {
|
|
90
|
-
const enabled = Number(params[index++]);
|
|
91
|
-
filteredRows = filteredRows.filter(
|
|
92
|
-
(row) => Number(row.enabled) === enabled,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const idInValues = consumeInValues(/id IN \(([^)]+)\)/);
|
|
97
|
-
if (idInValues) {
|
|
98
|
-
filteredRows = filteredRows.filter((row) => idInValues.includes(row.id));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (sql.includes("id = ?")) {
|
|
102
|
-
const id = params[index++];
|
|
103
|
-
filteredRows = filteredRows.filter((row) => row.id === id);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (sql.includes("id > ?")) {
|
|
107
|
-
const id = params[index++];
|
|
108
|
-
filteredRows = filteredRows.filter((row) => row.id.localeCompare(id) > 0);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (sql.includes("id >= ?")) {
|
|
112
|
-
const id = params[index++];
|
|
113
|
-
filteredRows = filteredRows.filter((row) => row.id.localeCompare(id) >= 0);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (sql.includes("id < ?")) {
|
|
117
|
-
const id = params[index++];
|
|
118
|
-
filteredRows = filteredRows.filter((row) => row.id.localeCompare(id) < 0);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (sql.includes("id <= ?")) {
|
|
122
|
-
const id = params[index++];
|
|
123
|
-
filteredRows = filteredRows.filter((row) => row.id.localeCompare(id) <= 0);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (sql.includes("target_app_version IS NOT NULL")) {
|
|
127
|
-
filteredRows = filteredRows.filter(
|
|
128
|
-
(row) => row.target_app_version !== null,
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (sql.includes("target_app_version IS NULL")) {
|
|
133
|
-
filteredRows = filteredRows.filter(
|
|
134
|
-
(row) => row.target_app_version === null,
|
|
135
|
-
);
|
|
136
|
-
} else if (sql.includes("target_app_version = ?")) {
|
|
137
|
-
const targetAppVersion = params[index++];
|
|
138
|
-
filteredRows = filteredRows.filter(
|
|
139
|
-
(row) => row.target_app_version === targetAppVersion,
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const targetAppVersionInValues = consumeInValues(
|
|
144
|
-
/target_app_version IN \(([^)]+)\)/,
|
|
145
|
-
);
|
|
146
|
-
if (targetAppVersionInValues) {
|
|
147
|
-
filteredRows = filteredRows.filter((row) =>
|
|
148
|
-
targetAppVersionInValues.includes(row.target_app_version),
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (sql.includes("fingerprint_hash IS NULL")) {
|
|
153
|
-
filteredRows = filteredRows.filter((row) => row.fingerprint_hash === null);
|
|
154
|
-
} else if (sql.includes("fingerprint_hash = ?")) {
|
|
155
|
-
const fingerprintHash = params[index++];
|
|
156
|
-
filteredRows = filteredRows.filter(
|
|
157
|
-
(row) => row.fingerprint_hash === fingerprintHash,
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return { filteredRows, index };
|
|
162
|
-
};
|
|
10
|
+
const state = vi.hoisted<{
|
|
11
|
+
batches: RecordedQuery[][];
|
|
12
|
+
queries: RecordedQuery[];
|
|
13
|
+
results: unknown[];
|
|
14
|
+
}>(() => ({ batches: [], queries: [], results: [] }));
|
|
15
|
+
|
|
16
|
+
const bundleD1Row = {
|
|
17
|
+
id: "bundle-1",
|
|
18
|
+
platform: "ios",
|
|
19
|
+
file_hash: "hash",
|
|
20
|
+
git_commit_hash: null,
|
|
21
|
+
storage_uri: "storage://bundle",
|
|
22
|
+
archive_byte_size: 3_000_000_001,
|
|
23
|
+
metadata: '{"version":1}',
|
|
24
|
+
manifest_storage_uri: null,
|
|
25
|
+
manifest_file_hash: null,
|
|
26
|
+
asset_base_storage_uri: null,
|
|
27
|
+
} as const;
|
|
163
28
|
|
|
164
29
|
vi.mock("cloudflare", () => ({
|
|
165
30
|
default: class MockCloudflare {
|
|
166
|
-
d1 = {
|
|
31
|
+
readonly d1 = {
|
|
167
32
|
database: {
|
|
168
33
|
query: async (
|
|
169
34
|
_databaseId: string,
|
|
170
|
-
|
|
171
|
-
sql
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
35
|
+
input:
|
|
36
|
+
| { readonly sql: string; readonly params?: readonly string[] }
|
|
37
|
+
| {
|
|
38
|
+
readonly batch: readonly {
|
|
39
|
+
readonly sql: string;
|
|
40
|
+
readonly params?: readonly string[];
|
|
41
|
+
}[];
|
|
42
|
+
},
|
|
177
43
|
) => {
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
44
|
+
if ("batch" in input) {
|
|
45
|
+
state.batches.push(
|
|
46
|
+
input.batch.map(({ sql, params }) => ({
|
|
47
|
+
sql,
|
|
48
|
+
params: params ?? [],
|
|
49
|
+
})),
|
|
181
50
|
);
|
|
51
|
+
} else {
|
|
52
|
+
state.queries.push({
|
|
53
|
+
sql: input.sql,
|
|
54
|
+
params: input.params ?? [],
|
|
55
|
+
});
|
|
182
56
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (normalizedSql.startsWith("select * from bundles where id = ?")) {
|
|
194
|
-
const bundleId = params[0];
|
|
195
|
-
const row = rows.get(bundleId);
|
|
196
|
-
return createPage(row ? [row] : []);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
normalizedSql.startsWith(
|
|
201
|
-
"select * from bundle_patches where bundle_id in",
|
|
202
|
-
)
|
|
203
|
-
) {
|
|
204
|
-
const selectedBundleIds = new Set(
|
|
205
|
-
normalizedSql.includes("json_each")
|
|
206
|
-
? (JSON.parse(String(params[0])) as unknown[]).map(String)
|
|
207
|
-
: params.map((value) => String(value)),
|
|
208
|
-
);
|
|
209
|
-
const result = Array.from(patchRows.values())
|
|
210
|
-
.filter((row) => selectedBundleIds.has(row.bundle_id))
|
|
211
|
-
.sort(
|
|
212
|
-
(a, b) =>
|
|
213
|
-
Number(a.order_index ?? 0) - Number(b.order_index ?? 0) ||
|
|
214
|
-
a.base_bundle_id.localeCompare(b.base_bundle_id),
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
return createPage(result);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (normalizedSql.startsWith("select * from bundles")) {
|
|
221
|
-
const { filteredRows, index } = getFilteredRows(sql, params);
|
|
222
|
-
const limit = Number(params[index] ?? filteredRows.length);
|
|
223
|
-
const offset = Number(params[index + 1] ?? 0);
|
|
224
|
-
const result = filteredRows
|
|
225
|
-
.sort((a, b) => b.id.localeCompare(a.id))
|
|
226
|
-
.slice(offset, offset + limit);
|
|
227
|
-
|
|
228
|
-
return createPage(result);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
normalizedSql.startsWith("select target_app_version from bundles")
|
|
233
|
-
) {
|
|
234
|
-
const { filteredRows } = getFilteredRows(sql, params);
|
|
235
|
-
const result = Array.from(
|
|
236
|
-
new Set(
|
|
237
|
-
filteredRows
|
|
238
|
-
.map((row) => row.target_app_version)
|
|
239
|
-
.filter((version): version is string => Boolean(version)),
|
|
240
|
-
),
|
|
241
|
-
).map((targetAppVersion) => ({
|
|
242
|
-
target_app_version: targetAppVersion,
|
|
243
|
-
}));
|
|
244
|
-
|
|
245
|
-
return createPage(result);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (
|
|
249
|
-
normalizedSql.startsWith(
|
|
250
|
-
"select channel from bundles group by channel",
|
|
251
|
-
)
|
|
252
|
-
) {
|
|
253
|
-
const channels = Array.from(
|
|
254
|
-
new Set(Array.from(rows.values()).map((row) => row.channel)),
|
|
255
|
-
).map((channel) => ({ channel }));
|
|
256
|
-
return createPage(channels);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (normalizedSql.startsWith("delete from bundles where id = ?")) {
|
|
260
|
-
rows.delete(params[0]);
|
|
261
|
-
return createPage([]);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (
|
|
265
|
-
normalizedSql.startsWith(
|
|
266
|
-
"delete from bundle_patches where bundle_id = ?",
|
|
267
|
-
)
|
|
268
|
-
) {
|
|
269
|
-
const bundleId = String(params[0]);
|
|
270
|
-
for (const [id, row] of patchRows.entries()) {
|
|
271
|
-
if (row.bundle_id === bundleId) {
|
|
272
|
-
patchRows.delete(id);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return createPage([]);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
normalizedSql.startsWith(
|
|
280
|
-
"delete from bundle_patches where base_bundle_id = ?",
|
|
281
|
-
)
|
|
282
|
-
) {
|
|
283
|
-
const baseBundleId = String(params[0]);
|
|
284
|
-
for (const [id, row] of patchRows.entries()) {
|
|
285
|
-
if (row.base_bundle_id === baseBundleId) {
|
|
286
|
-
patchRows.delete(id);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return createPage([]);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (normalizedSql.startsWith("insert or replace into bundles")) {
|
|
293
|
-
const row: D1Row = {
|
|
294
|
-
id: params[0],
|
|
295
|
-
channel: params[1],
|
|
296
|
-
enabled: params[2],
|
|
297
|
-
should_force_update: params[3],
|
|
298
|
-
file_hash: params[4],
|
|
299
|
-
git_commit_hash: params[5],
|
|
300
|
-
message: params[6],
|
|
301
|
-
platform: params[7],
|
|
302
|
-
target_app_version: params[8],
|
|
303
|
-
storage_uri: params[9],
|
|
304
|
-
fingerprint_hash: params[10],
|
|
305
|
-
metadata: params[11],
|
|
306
|
-
manifest_storage_uri: params[12],
|
|
307
|
-
manifest_file_hash: params[13],
|
|
308
|
-
asset_base_storage_uri: params[14],
|
|
309
|
-
rollout_cohort_count: params[15],
|
|
310
|
-
target_cohorts: params[16],
|
|
311
|
-
};
|
|
312
|
-
rows.set(row.id, row);
|
|
313
|
-
return createPage([]);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (
|
|
317
|
-
normalizedSql.startsWith("insert or replace into bundle_patches")
|
|
318
|
-
) {
|
|
319
|
-
const row: D1PatchRow = {
|
|
320
|
-
id: params[0],
|
|
321
|
-
bundle_id: params[1],
|
|
322
|
-
base_bundle_id: params[2],
|
|
323
|
-
base_file_hash: params[3],
|
|
324
|
-
patch_file_hash: params[4],
|
|
325
|
-
patch_storage_uri: params[5],
|
|
326
|
-
order_index: Number(params[6] ?? 0),
|
|
327
|
-
};
|
|
328
|
-
patchRows.set(row.id, row);
|
|
329
|
-
return createPage([]);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
throw new Error(`Unsupported SQL in D1 mock: ${sql}`);
|
|
57
|
+
return {
|
|
58
|
+
async *iterPages() {
|
|
59
|
+
yield {
|
|
60
|
+
result:
|
|
61
|
+
"batch" in input
|
|
62
|
+
? input.batch.map(() => ({ results: [] }))
|
|
63
|
+
: [{ results: state.results }],
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
};
|
|
333
67
|
},
|
|
334
68
|
},
|
|
335
69
|
};
|
|
336
70
|
},
|
|
337
71
|
}));
|
|
338
72
|
|
|
339
|
-
|
|
340
|
-
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
state.batches.length = 0;
|
|
75
|
+
state.queries.length = 0;
|
|
76
|
+
state.results.length = 0;
|
|
77
|
+
});
|
|
341
78
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
cloudflareApiToken: "test-token",
|
|
349
|
-
})();
|
|
79
|
+
it("queries bundles through domain filters", async () => {
|
|
80
|
+
state.results.push(bundleD1Row);
|
|
81
|
+
const plugin = d1Database({
|
|
82
|
+
accountId: "account",
|
|
83
|
+
cloudflareApiToken: "token",
|
|
84
|
+
databaseId: "database",
|
|
350
85
|
});
|
|
351
86
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
await plugin.commitBundle();
|
|
358
|
-
},
|
|
359
|
-
getBundles: (options) => plugin.getBundles(options),
|
|
360
|
-
updateBundleById: async (bundleId, newBundle) => {
|
|
361
|
-
await plugin.updateBundle(bundleId, newBundle);
|
|
362
|
-
await plugin.commitBundle();
|
|
363
|
-
},
|
|
364
|
-
deleteBundleById: async (bundleId) => {
|
|
365
|
-
const bundle = await plugin.getBundleById(bundleId);
|
|
366
|
-
if (!bundle) {
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
await plugin.deleteBundle(bundle);
|
|
370
|
-
await plugin.commitBundle();
|
|
371
|
-
},
|
|
87
|
+
const rows = await plugin.models.bundles.findMany({
|
|
88
|
+
where: { platform: "ios", id: { gte: "bundle-1" } },
|
|
89
|
+
orderBy: { field: "id", direction: "desc" },
|
|
90
|
+
limit: 10,
|
|
91
|
+
offset: 0,
|
|
372
92
|
});
|
|
373
93
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
94
|
+
expect(rows).toEqual([
|
|
95
|
+
{
|
|
96
|
+
...bundleD1Row,
|
|
97
|
+
metadata: { version: 1 },
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
100
|
+
expect(state.queries[0]?.sql).toContain("platform = json_extract(?, '$')");
|
|
101
|
+
expect(state.queries[0]?.sql).toContain("id >= json_extract(?, '$')");
|
|
102
|
+
expect(state.queries[0]?.sql).toContain("ORDER BY id DESC");
|
|
103
|
+
});
|
|
378
104
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
105
|
+
it("lists normalized channels without scanning bundles", async () => {
|
|
106
|
+
state.results.push({ id: "channel-production", name: "production" });
|
|
107
|
+
const plugin = d1Database({
|
|
108
|
+
accountId: "account",
|
|
109
|
+
cloudflareApiToken: "token",
|
|
110
|
+
databaseId: "database",
|
|
111
|
+
});
|
|
383
112
|
|
|
384
|
-
|
|
385
|
-
},
|
|
113
|
+
await expect(plugin.models.channels.list({})).resolves.toEqual({
|
|
114
|
+
channels: [{ id: "channel-production", name: "production" }],
|
|
386
115
|
});
|
|
387
116
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
should_force_update: 0,
|
|
395
|
-
file_hash: "hash-1",
|
|
396
|
-
git_commit_hash: "commit-1",
|
|
397
|
-
message: "stale message",
|
|
398
|
-
platform: "ios",
|
|
399
|
-
target_app_version: "1.0.0",
|
|
400
|
-
storage_uri: "s3://bucket/stale.zip",
|
|
401
|
-
fingerprint_hash: null,
|
|
402
|
-
metadata: JSON.stringify({ source: "initial" }),
|
|
403
|
-
rollout_cohort_count: 1000,
|
|
404
|
-
target_cohorts: null,
|
|
405
|
-
};
|
|
406
|
-
rows.set(bundleId, initialRow);
|
|
117
|
+
expect(state.queries[0]?.params).toEqual(["100", "0"]);
|
|
118
|
+
expect(state.queries[0]?.sql).toBe(
|
|
119
|
+
"SELECT * FROM channels ORDER BY name ASC LIMIT json_extract(?, '$') OFFSET json_extract(?, '$')",
|
|
120
|
+
);
|
|
121
|
+
expect(state.queries[0]?.sql).not.toContain("bundles");
|
|
122
|
+
});
|
|
407
123
|
|
|
408
|
-
|
|
124
|
+
it("counts domain-filtered bundle rows in SQL", async () => {
|
|
125
|
+
state.results.push({ count: 3 });
|
|
126
|
+
const plugin = d1Database({
|
|
127
|
+
accountId: "account",
|
|
128
|
+
cloudflareApiToken: "token",
|
|
129
|
+
databaseId: "database",
|
|
130
|
+
});
|
|
409
131
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
metadata: JSON.stringify({ source: "fresh" }),
|
|
414
|
-
});
|
|
132
|
+
await plugin.models.bundles.count({
|
|
133
|
+
platform: "ios",
|
|
134
|
+
});
|
|
415
135
|
|
|
416
|
-
|
|
417
|
-
|
|
136
|
+
expect(state.queries[0]?.sql).toContain(
|
|
137
|
+
"SELECT COUNT(*) AS count FROM bundles",
|
|
138
|
+
);
|
|
139
|
+
expect(state.queries[0]?.sql).toContain("platform = json_extract(?, '$')");
|
|
140
|
+
});
|
|
418
141
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}),
|
|
425
|
-
);
|
|
142
|
+
it("sends parameterized commits through the D1 batch body", async () => {
|
|
143
|
+
const plugin = d1Database({
|
|
144
|
+
accountId: "account",
|
|
145
|
+
cloudflareApiToken: "token",
|
|
146
|
+
databaseId: "database",
|
|
426
147
|
});
|
|
148
|
+
|
|
149
|
+
await expect(
|
|
150
|
+
plugin.commit({
|
|
151
|
+
changes: [
|
|
152
|
+
{
|
|
153
|
+
model: "channels",
|
|
154
|
+
operation: "insert",
|
|
155
|
+
row: {
|
|
156
|
+
id: "00000000-0000-0000-0000-000000000001",
|
|
157
|
+
name: "production",
|
|
158
|
+
},
|
|
159
|
+
onConflict: "ignore",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
model: "channels",
|
|
163
|
+
operation: "insert",
|
|
164
|
+
row: {
|
|
165
|
+
id: "00000000-0000-0000-0000-000000000002",
|
|
166
|
+
name: "beta",
|
|
167
|
+
},
|
|
168
|
+
onConflict: "ignore",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
}),
|
|
172
|
+
).resolves.toEqual({ committed: true });
|
|
173
|
+
|
|
174
|
+
expect(state.batches).toHaveLength(1);
|
|
175
|
+
expect(state.batches[0]).toHaveLength(2);
|
|
176
|
+
expect(state.batches[0]?.[0]?.params.length).toBeGreaterThan(0);
|
|
177
|
+
expect(state.batches[0]?.[1]?.params.length).toBeGreaterThan(0);
|
|
427
178
|
});
|