@hot-updater/cloudflare 0.36.1 → 0.36.2
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/worker/index.cjs +1 -1
- package/dist/worker/index.mjs +2 -2
- package/package.json +7 -7
- package/src/r2Storage.spec.ts +45 -0
- package/src/r2WorkerStorage.spec.ts +23 -0
- package/src/r2WorkerStorage.ts +2 -1
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +2489 -2480
- package/worker/dist/index.js.map +4 -4
- package/worker/src/index.integration.spec.ts +19 -0
- package/worker/src/index.ts +3 -2
package/dist/worker/index.cjs
CHANGED
|
@@ -323,7 +323,7 @@ const resolveR2BucketFromContext = (context) => {
|
|
|
323
323
|
return bucket;
|
|
324
324
|
};
|
|
325
325
|
const createPublicObjectPath = (storageUrl) => `${storageUrl.host}${storageUrl.pathname}`;
|
|
326
|
-
const createR2ObjectKey = (storageUrl) => storageUrl.pathname.replace(/^\/+/, "");
|
|
326
|
+
const createR2ObjectKey = (storageUrl) => (0, _hot_updater_plugin_core.decodeStorageObjectKey)(storageUrl.pathname.replace(/^\/+/, ""));
|
|
327
327
|
const r2WorkerStorage = (config) => {
|
|
328
328
|
return (0, _hot_updater_plugin_core.createRuntimeStoragePlugin)({
|
|
329
329
|
name: "r2WorkerStorage",
|
package/dist/worker/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signToken, verifyJwtSignedUrl } from "@hot-updater/js";
|
|
2
2
|
import { DEFAULT_ROLLOUT_COHORT_COUNT, NIL_UUID, getAssetBaseStorageUri, getBundlePatches, getManifestFileHash, getManifestStorageUri, stripBundleArtifactMetadata } from "@hot-updater/core";
|
|
3
|
-
import { calculatePagination, createDatabasePlugin, createRuntimeStoragePlugin, filterCompatibleAppVersions, resolveUpdateInfoFromBundles } from "@hot-updater/plugin-core";
|
|
3
|
+
import { calculatePagination, createDatabasePlugin, createRuntimeStoragePlugin, decodeStorageObjectKey, filterCompatibleAppVersions, resolveUpdateInfoFromBundles } from "@hot-updater/plugin-core";
|
|
4
4
|
//#region src/cloudflareWorkerDatabase.ts
|
|
5
5
|
const buildJsonEachInClause = (columnName, values, params) => {
|
|
6
6
|
if (values.length === 0) return "1 = 0";
|
|
@@ -322,7 +322,7 @@ const resolveR2BucketFromContext = (context) => {
|
|
|
322
322
|
return bucket;
|
|
323
323
|
};
|
|
324
324
|
const createPublicObjectPath = (storageUrl) => `${storageUrl.host}${storageUrl.pathname}`;
|
|
325
|
-
const createR2ObjectKey = (storageUrl) => storageUrl.pathname.replace(/^\/+/, "");
|
|
325
|
+
const createR2ObjectKey = (storageUrl) => decodeStorageObjectKey(storageUrl.pathname.replace(/^\/+/, ""));
|
|
326
326
|
const r2WorkerStorage = (config) => {
|
|
327
327
|
return createRuntimeStoragePlugin({
|
|
328
328
|
name: "r2WorkerStorage",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/cloudflare",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.36.
|
|
4
|
+
"version": "0.36.2",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"cloudflare": "4.2.0",
|
|
62
62
|
"hono": "4.12.9",
|
|
63
63
|
"uuidv7": "^1.0.2",
|
|
64
|
-
"@hot-updater/
|
|
65
|
-
"@hot-updater/
|
|
66
|
-
"@hot-updater/
|
|
67
|
-
"@hot-updater/core": "0.36.
|
|
68
|
-
"@hot-updater/
|
|
64
|
+
"@hot-updater/core": "0.36.2",
|
|
65
|
+
"@hot-updater/cli-tools": "0.36.2",
|
|
66
|
+
"@hot-updater/js": "0.36.2",
|
|
67
|
+
"@hot-updater/plugin-core": "0.36.2",
|
|
68
|
+
"@hot-updater/server": "0.36.2"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@cloudflare/vitest-pool-workers": "0.13.0",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"vitest": "4.1.4",
|
|
83
83
|
"wrangler": "^4.5.0",
|
|
84
84
|
"xdg-app-paths": "^8.3.0",
|
|
85
|
-
"@hot-updater/test-utils": "0.36.
|
|
85
|
+
"@hot-updater/test-utils": "0.36.2"
|
|
86
86
|
},
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "tsdown && pnpm build:worker",
|
package/src/r2Storage.spec.ts
CHANGED
|
@@ -279,6 +279,28 @@ describe("r2Storage", () => {
|
|
|
279
279
|
);
|
|
280
280
|
});
|
|
281
281
|
|
|
282
|
+
it("decodes percent-encoded keys before signing through the S3 API", async () => {
|
|
283
|
+
mockS3Client();
|
|
284
|
+
const storage = r2Storage({
|
|
285
|
+
accountId: "account-id",
|
|
286
|
+
bucketName: "test-bucket",
|
|
287
|
+
credentials: {
|
|
288
|
+
accessKeyId: "access-key-id",
|
|
289
|
+
secretAccessKey: "secret-access-key",
|
|
290
|
+
},
|
|
291
|
+
})();
|
|
292
|
+
|
|
293
|
+
await storage.profiles.runtime.getDownloadUrl(
|
|
294
|
+
"r2://test-bucket/releases/logo%402x.png",
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const command = vi.mocked(getSignedUrl).mock.calls.at(-1)?.[1];
|
|
298
|
+
expect(command).toBeInstanceOf(GetObjectCommand);
|
|
299
|
+
expect((command as GetObjectCommand).input.Key).toBe(
|
|
300
|
+
"releases/logo@2x.png",
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
282
304
|
it("falls back to wrangler without S3 credentials", async () => {
|
|
283
305
|
wrangler.mockImplementation(async (...args: string[]) => {
|
|
284
306
|
const fileIndex = args.indexOf("--file");
|
|
@@ -382,6 +404,29 @@ describe("r2Storage", () => {
|
|
|
382
404
|
);
|
|
383
405
|
});
|
|
384
406
|
|
|
407
|
+
it("decodes percent-encoded keys before using Wrangler", async () => {
|
|
408
|
+
wrangler.mockResolvedValueOnce({ exitCode: 0, stderr: "" });
|
|
409
|
+
const storage = r2Storage({
|
|
410
|
+
accountId: "account-id",
|
|
411
|
+
bucketName: "test-bucket",
|
|
412
|
+
cloudflareApiToken: "api-token",
|
|
413
|
+
})();
|
|
414
|
+
|
|
415
|
+
await storage.profiles.node.exists(
|
|
416
|
+
"r2://test-bucket/releases/logo%402x.png",
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
expect(wrangler).toHaveBeenCalledWith(
|
|
420
|
+
"r2",
|
|
421
|
+
"object",
|
|
422
|
+
"get",
|
|
423
|
+
"test-bucket/releases/logo@2x.png",
|
|
424
|
+
"--file",
|
|
425
|
+
expect.any(String),
|
|
426
|
+
"--remote",
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
385
430
|
it("rethrows non-missing R2 existence errors", async () => {
|
|
386
431
|
const error = createExecaError("Authentication failed");
|
|
387
432
|
wrangler.mockRejectedValueOnce(error);
|
|
@@ -30,6 +30,29 @@ describe("r2WorkerStorage", () => {
|
|
|
30
30
|
expect(get).toHaveBeenCalledWith("app/manifest.json");
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
it("decodes percent-encoded object keys before reading from R2", async () => {
|
|
34
|
+
const get = vi.fn(async (key: string) => ({
|
|
35
|
+
text: async () => `text:${key}`,
|
|
36
|
+
}));
|
|
37
|
+
const storage = r2WorkerStorage({
|
|
38
|
+
jwtSecret: "secret",
|
|
39
|
+
publicBaseUrl: "https://assets.example.com",
|
|
40
|
+
})();
|
|
41
|
+
|
|
42
|
+
await storage.profiles.runtime.readText(
|
|
43
|
+
"r2://bundles/assets/bootsplash/logo-ios%402x.png",
|
|
44
|
+
{
|
|
45
|
+
env: {
|
|
46
|
+
BUCKET: { get },
|
|
47
|
+
JWT_SECRET: "secret",
|
|
48
|
+
},
|
|
49
|
+
request: new Request("https://updates.example.com"),
|
|
50
|
+
} satisfies TestContext,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(get).toHaveBeenCalledWith("assets/bootsplash/logo-ios@2x.png");
|
|
54
|
+
});
|
|
55
|
+
|
|
33
56
|
it("fails fast when the R2 binding is missing", async () => {
|
|
34
57
|
const storage = r2WorkerStorage({
|
|
35
58
|
jwtSecret: "secret",
|
package/src/r2WorkerStorage.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { signToken } from "@hot-updater/js";
|
|
2
2
|
import {
|
|
3
3
|
createRuntimeStoragePlugin,
|
|
4
|
+
decodeStorageObjectKey,
|
|
4
5
|
type HotUpdaterContext,
|
|
5
6
|
type RequestEnvContext,
|
|
6
7
|
} from "@hot-updater/plugin-core";
|
|
@@ -64,7 +65,7 @@ const createPublicObjectPath = (storageUrl: URL) =>
|
|
|
64
65
|
`${storageUrl.host}${storageUrl.pathname}`;
|
|
65
66
|
|
|
66
67
|
const createR2ObjectKey = (storageUrl: URL) =>
|
|
67
|
-
storageUrl.pathname.replace(/^\/+/, "");
|
|
68
|
+
decodeStorageObjectKey(storageUrl.pathname.replace(/^\/+/, ""));
|
|
68
69
|
|
|
69
70
|
export const r2WorkerStorage = <
|
|
70
71
|
TContext extends RequestEnvContext<CloudflareWorkerStorageEnv> =
|
package/worker/dist/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
This folder contains the built output assets for the worker "hot-updater" generated at 2026-08-
|
|
1
|
+
This folder contains the built output assets for the worker "hot-updater" generated at 2026-08-20T12:36:25.676Z.
|