@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.
Files changed (49) hide show
  1. package/dist/iac/index.cjs +988 -10
  2. package/dist/iac/index.mjs +991 -14
  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
@@ -1,193 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
-
5
- import {
6
- createStorageKeyBuilder,
7
- getContentType,
8
- type NodeStorageProfile,
9
- parseStorageUri,
10
- type RuntimeStorageProfile,
11
- } from "@hot-updater/plugin-core";
12
- import { ExecaError } from "execa";
13
-
14
- import { createWrangler } from "./utils/createWrangler";
15
-
16
- /**
17
- * @deprecated `cloudflareApiToken` uses the Wrangler CLI for R2 operations,
18
- * which is slower than direct S3-compatible API access. Create R2
19
- * S3-compatible credentials in the Cloudflare dashboard and pass them with
20
- * `r2Storage({ credentials })` instead.
21
- */
22
- export interface R2WranglerStorageConfig {
23
- accountId: string;
24
- bucketName: string;
25
- /**
26
- * @deprecated This token keeps R2 access on the slower Wrangler CLI path.
27
- * Create R2 S3-compatible credentials in the Cloudflare dashboard and use
28
- * `credentials` instead.
29
- */
30
- cloudflareApiToken: string;
31
- /**
32
- * Base path where bundles will be stored in the bucket
33
- */
34
- basePath?: string;
35
- credentials?: never;
36
- }
37
-
38
- const ensureExpectedR2Bucket = (bucket: string, bucketName: string) => {
39
- if (bucket !== bucketName) {
40
- throw new Error(
41
- `Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`,
42
- );
43
- }
44
- };
45
-
46
- const isR2ObjectNotFoundError = (error: ExecaError) => {
47
- const output = [error.stderr, error.stdout, error.shortMessage, error.message]
48
- .filter(Boolean)
49
- .join("\n")
50
- .toLowerCase();
51
-
52
- return (
53
- output.includes("not found") ||
54
- output.includes("no such object") ||
55
- output.includes("does not exist")
56
- );
57
- };
58
-
59
- export const createWranglerStorageProfile = (
60
- config: R2WranglerStorageConfig,
61
- ): NodeStorageProfile => {
62
- const { bucketName, cloudflareApiToken, accountId } = config;
63
- const wrangler = createWrangler({
64
- accountId,
65
- cloudflareApiToken,
66
- cwd: process.cwd(),
67
- });
68
- const getStorageKey = createStorageKeyBuilder(config.basePath);
69
-
70
- return {
71
- async delete(storageUri) {
72
- const { bucket, key } = parseStorageUri(storageUri, "r2");
73
- ensureExpectedR2Bucket(bucket, bucketName);
74
-
75
- try {
76
- await wrangler(
77
- "r2",
78
- "object",
79
- "delete",
80
- [bucketName, key].join("/"),
81
- "--remote",
82
- );
83
- } catch {
84
- throw new Error("Can not delete bundle");
85
- }
86
- },
87
- async upload(key, filePath) {
88
- const contentType = getContentType(filePath);
89
-
90
- const filename = path.basename(filePath);
91
-
92
- const Key = getStorageKey(key, filename);
93
- try {
94
- const { stderr, exitCode } = await wrangler(
95
- "r2",
96
- "object",
97
- "put",
98
- [bucketName, Key].join("/"),
99
- "--file",
100
- filePath,
101
- "--content-type",
102
- contentType,
103
- "--remote",
104
- );
105
- if (exitCode !== 0 && stderr) {
106
- throw new Error(stderr);
107
- }
108
- } catch (error) {
109
- if (error instanceof ExecaError) {
110
- throw new Error(error.stderr || error.stdout);
111
- }
112
-
113
- throw error;
114
- }
115
-
116
- return {
117
- storageUri: `r2://${bucketName}/${Key}`,
118
- };
119
- },
120
- async exists(storageUri: string) {
121
- const { bucket, key } = parseStorageUri(storageUri, "r2");
122
- ensureExpectedR2Bucket(bucket, bucketName);
123
-
124
- const tempDir = await fs.mkdtemp(
125
- path.join(os.tmpdir(), "hot-updater-r2-exists-"),
126
- );
127
- const tempFilePath = path.join(tempDir, "object");
128
-
129
- try {
130
- await wrangler(
131
- "r2",
132
- "object",
133
- "get",
134
- [bucketName, key].join("/"),
135
- "--file",
136
- tempFilePath,
137
- "--remote",
138
- );
139
- return true;
140
- } catch (error) {
141
- if (error instanceof ExecaError && isR2ObjectNotFoundError(error)) {
142
- return false;
143
- }
144
-
145
- throw error;
146
- } finally {
147
- await fs.rm(tempDir, { force: true, recursive: true });
148
- }
149
- },
150
- async downloadFile(storageUri, filePath) {
151
- const { bucket, key } = parseStorageUri(storageUri, "r2");
152
- ensureExpectedR2Bucket(bucket, bucketName);
153
-
154
- try {
155
- await fs.mkdir(path.dirname(filePath), { recursive: true });
156
- const { stderr, exitCode } = await wrangler(
157
- "r2",
158
- "object",
159
- "get",
160
- [bucketName, key].join("/"),
161
- "--file",
162
- filePath,
163
- "--remote",
164
- );
165
- if (exitCode !== 0 && stderr) {
166
- throw new Error(stderr);
167
- }
168
- } catch (error) {
169
- if (error instanceof ExecaError) {
170
- throw new Error(error.stderr || error.stdout);
171
- }
172
-
173
- throw error;
174
- }
175
- },
176
- };
177
- };
178
-
179
- export const createWranglerRuntimeStorageProfile =
180
- (): RuntimeStorageProfile => {
181
- const error = new Error(
182
- "r2Storage runtime profile requires R2 S3 credentials. Wrangler-based R2 access is only supported by the node profile.",
183
- );
184
-
185
- return {
186
- async readText() {
187
- throw error;
188
- },
189
- async getDownloadUrl() {
190
- throw error;
191
- },
192
- };
193
- };
@@ -1,16 +0,0 @@
1
- -- Migration number: 0001 2025-01-27T08:37:50.374Z
2
- -- HotUpdater.bundles
3
-
4
- CREATE TABLE bundles (
5
- id TEXT PRIMARY KEY,
6
- platform TEXT NOT NULL CHECK (platform IN ('ios', 'android')),
7
- target_app_version TEXT NOT NULL,
8
- should_force_update INTEGER NOT NULL CHECK (should_force_update IN (0, 1)),
9
- enabled INTEGER NOT NULL CHECK (enabled IN (0, 1)),
10
- file_url TEXT NOT NULL,
11
- file_hash TEXT NOT NULL,
12
- git_commit_hash TEXT,
13
- message TEXT
14
- );
15
-
16
- CREATE INDEX bundles_target_app_version_idx ON bundles(target_app_version);
@@ -1,8 +0,0 @@
1
- -- Migration number: 0002 2025-03-07T16:25:12.486Z
2
- -- HotUpdater.bundles
3
-
4
- ALTER TABLE bundles
5
- ADD COLUMN channel TEXT NOT NULL DEFAULT 'production';
6
-
7
- ALTER TABLE bundles
8
- DROP COLUMN file_url;
@@ -1,38 +0,0 @@
1
- -- Migration number: 0003 2025-05-18T16:25:12.486Z
2
- -- HotUpdater.bundles
3
-
4
- ALTER TABLE bundles ADD COLUMN fingerprint_hash TEXT;
5
- ALTER TABLE bundles ADD COLUMN storage_uri TEXT;
6
- ALTER TABLE bundles ADD COLUMN metadata JSONB DEFAULT '{}';
7
-
8
- UPDATE bundles
9
- SET storage_uri = 'r2://%%BUCKET_NAME%%/' || id || '/bundle.zip'
10
- WHERE storage_uri IS NULL;
11
-
12
- CREATE TABLE bundles_temp (
13
- id TEXT PRIMARY KEY,
14
- platform TEXT NOT NULL,
15
- should_force_update INTEGER NOT NULL,
16
- enabled INTEGER NOT NULL,
17
- file_hash TEXT NOT NULL,
18
- git_commit_hash TEXT,
19
- message TEXT,
20
- channel TEXT NOT NULL DEFAULT 'production',
21
- storage_uri TEXT NOT NULL,
22
- target_app_version TEXT,
23
- fingerprint_hash TEXT,
24
- metadata JSONB DEFAULT '{}',
25
- CHECK ((target_app_version IS NOT NULL) OR (fingerprint_hash IS NOT NULL))
26
- );
27
-
28
- INSERT INTO bundles_temp
29
- SELECT id, platform, should_force_update, enabled, file_hash, git_commit_hash, message, channel, storage_uri, target_app_version, fingerprint_hash, metadata
30
- FROM bundles;
31
-
32
- DROP TABLE bundles;
33
-
34
- ALTER TABLE bundles_temp RENAME TO bundles;
35
-
36
- CREATE INDEX bundles_target_app_version_idx ON bundles(target_app_version);
37
- CREATE INDEX bundles_fingerprint_hash_idx ON bundles(fingerprint_hash);
38
- CREATE INDEX bundles_channel_idx ON bundles(channel);
@@ -1,11 +0,0 @@
1
- -- Migration number: 0004 2025-12-21T00:00:00.000Z
2
-
3
- -- HotUpdater.bundles
4
-
5
- ALTER TABLE bundles ADD COLUMN rollout_cohort_count INTEGER DEFAULT 1000
6
- CHECK (rollout_cohort_count >= 0 AND rollout_cohort_count <= 1000);
7
-
8
- CREATE INDEX IF NOT EXISTS bundles_rollout_idx ON bundles(rollout_cohort_count);
9
-
10
- -- SQLite doesn't have array type, store as JSON text
11
- ALTER TABLE bundles ADD COLUMN target_cohorts TEXT;
@@ -1,24 +0,0 @@
1
- -- Migration number: 0005 2026-04-22T00:00:00.000Z
2
-
3
- -- HotUpdater.bundle_artifact_columns
4
-
5
- ALTER TABLE bundles ADD COLUMN manifest_storage_uri TEXT;
6
- ALTER TABLE bundles ADD COLUMN manifest_file_hash TEXT;
7
- ALTER TABLE bundles ADD COLUMN asset_base_storage_uri TEXT;
8
-
9
- CREATE TABLE bundle_patches (
10
- id TEXT PRIMARY KEY,
11
- bundle_id TEXT NOT NULL,
12
- base_bundle_id TEXT NOT NULL,
13
- base_file_hash TEXT NOT NULL,
14
- patch_file_hash TEXT NOT NULL,
15
- patch_storage_uri TEXT NOT NULL,
16
- order_index INTEGER NOT NULL DEFAULT 0,
17
- FOREIGN KEY (bundle_id) REFERENCES bundles(id) ON DELETE CASCADE,
18
- FOREIGN KEY (base_bundle_id) REFERENCES bundles(id) ON DELETE CASCADE
19
- );
20
-
21
- CREATE INDEX IF NOT EXISTS bundle_patches_bundle_id_idx
22
- ON bundle_patches(bundle_id);
23
- CREATE INDEX IF NOT EXISTS bundle_patches_base_bundle_id_idx
24
- ON bundle_patches(base_bundle_id);