@voyant-travel/storage 0.105.0 → 0.106.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/routes.d.ts +5 -9
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +5 -12
- package/dist/routes.test.js +7 -7
- package/package.json +3 -3
package/dist/routes.d.ts
CHANGED
|
@@ -6,16 +6,12 @@
|
|
|
6
6
|
* shares the same content-type safety + key-parsing helpers; splitting it would
|
|
7
7
|
* scatter a single storage-backed contract.
|
|
8
8
|
*
|
|
9
|
-
* POST /v1/uploads
|
|
10
|
-
* POST /v1/admin/uploads
|
|
11
|
-
*
|
|
12
|
-
* POST /v1/admin/uploads/video — (admin alias)
|
|
13
|
-
* GET /v1/media/* — serve stored bytes (hardened)
|
|
14
|
-
* GET /v1/admin/media/* — (admin alias)
|
|
9
|
+
* POST /v1/admin/uploads — multipart file upload → storage ticket
|
|
10
|
+
* POST /v1/admin/uploads/video — video upload ticket (deployment signer)
|
|
11
|
+
* GET /v1/admin/media/* — serve stored bytes (hardened)
|
|
15
12
|
*
|
|
16
|
-
* These routes register ABSOLUTE paths
|
|
17
|
-
*
|
|
18
|
-
* app root rather than under a module prefix.
|
|
13
|
+
* These routes register ABSOLUTE admin paths, so a deployment mounts the
|
|
14
|
+
* returned `Hono` at the app root rather than under a module prefix.
|
|
19
15
|
*
|
|
20
16
|
* The deployment supplies the storage-backed specifics via `options`:
|
|
21
17
|
* - `resolveStorage(c)` — the R2-backed `StorageProvider` for this request
|
package/dist/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAyBjD,8DAA8D;AAC9D,eAAO,MAAM,2BAA2B;;;;;;;;iBAQtC,CAAA;AAEF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAMlF;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAAA;IAClD;;;OAGG;IACH,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACpF;;;OAGG;IACH,mBAAmB,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1C;AA4BD,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD;AAiGD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAkGnE"}
|
package/dist/routes.js
CHANGED
|
@@ -6,16 +6,12 @@
|
|
|
6
6
|
* shares the same content-type safety + key-parsing helpers; splitting it would
|
|
7
7
|
* scatter a single storage-backed contract.
|
|
8
8
|
*
|
|
9
|
-
* POST /v1/uploads
|
|
10
|
-
* POST /v1/admin/uploads
|
|
11
|
-
*
|
|
12
|
-
* POST /v1/admin/uploads/video — (admin alias)
|
|
13
|
-
* GET /v1/media/* — serve stored bytes (hardened)
|
|
14
|
-
* GET /v1/admin/media/* — (admin alias)
|
|
9
|
+
* POST /v1/admin/uploads — multipart file upload → storage ticket
|
|
10
|
+
* POST /v1/admin/uploads/video — video upload ticket (deployment signer)
|
|
11
|
+
* GET /v1/admin/media/* — serve stored bytes (hardened)
|
|
15
12
|
*
|
|
16
|
-
* These routes register ABSOLUTE paths
|
|
17
|
-
*
|
|
18
|
-
* app root rather than under a module prefix.
|
|
13
|
+
* These routes register ABSOLUTE admin paths, so a deployment mounts the
|
|
14
|
+
* returned `Hono` at the app root rather than under a module prefix.
|
|
19
15
|
*
|
|
20
16
|
* The deployment supplies the storage-backed specifics via `options`:
|
|
21
17
|
* - `resolveStorage(c)` — the R2-backed `StorageProvider` for this request
|
|
@@ -217,7 +213,6 @@ export function createMediaRoutes(options) {
|
|
|
217
213
|
size: file.size,
|
|
218
214
|
});
|
|
219
215
|
};
|
|
220
|
-
hono.post("/v1/uploads", handleUpload);
|
|
221
216
|
hono.post("/v1/admin/uploads", handleUpload);
|
|
222
217
|
const handleVideoUploadTicket = async (c) => {
|
|
223
218
|
let raw;
|
|
@@ -234,7 +229,6 @@ export function createMediaRoutes(options) {
|
|
|
234
229
|
const ticket = await options.signVideoUploadTicket(c, parsed.data);
|
|
235
230
|
return c.json(ticket);
|
|
236
231
|
};
|
|
237
|
-
hono.post("/v1/uploads/video", handleVideoUploadTicket);
|
|
238
232
|
hono.post("/v1/admin/uploads/video", handleVideoUploadTicket);
|
|
239
233
|
const handleMediaServe = async (c) => {
|
|
240
234
|
const storage = options.resolveStorage(c);
|
|
@@ -257,7 +251,6 @@ export function createMediaRoutes(options) {
|
|
|
257
251
|
headers.set("Content-Length", String(buffer.byteLength));
|
|
258
252
|
return new Response(buffer, { headers });
|
|
259
253
|
};
|
|
260
|
-
hono.get("/v1/media/*", handleMediaServe);
|
|
261
254
|
hono.get("/v1/admin/media/*", handleMediaServe);
|
|
262
255
|
return hono;
|
|
263
256
|
}
|
package/dist/routes.test.js
CHANGED
|
@@ -37,7 +37,7 @@ describe("media routes", () => {
|
|
|
37
37
|
it("streams stored media by allowed key as an attachment", async () => {
|
|
38
38
|
const storage = makeStorage({ get: vi.fn(async () => new TextEncoder().encode("pdf").buffer) });
|
|
39
39
|
const app = mountApp({ resolveStorage: () => storage });
|
|
40
|
-
const response = await app.request("/v1/media/brochures/products/example.pdf");
|
|
40
|
+
const response = await app.request("/v1/admin/media/brochures/products/example.pdf");
|
|
41
41
|
expect(response.status).toBe(200);
|
|
42
42
|
expect(response.headers.get("content-type")).toBe("application/pdf");
|
|
43
43
|
expect(response.headers.get("x-content-type-options")).toBe("nosniff");
|
|
@@ -47,7 +47,7 @@ describe("media routes", () => {
|
|
|
47
47
|
it("rejects media keys outside allowed upload prefixes", async () => {
|
|
48
48
|
const get = vi.fn(async () => new TextEncoder().encode("private").buffer);
|
|
49
49
|
const app = mountApp({ resolveStorage: () => makeStorage({ get }) });
|
|
50
|
-
const response = await app.request("/v1/media/private/example.pdf");
|
|
50
|
+
const response = await app.request("/v1/admin/media/private/example.pdf");
|
|
51
51
|
expect(response.status).toBe(400);
|
|
52
52
|
expect(get).not.toHaveBeenCalled();
|
|
53
53
|
});
|
|
@@ -56,7 +56,7 @@ describe("media routes", () => {
|
|
|
56
56
|
get: vi.fn(async () => new TextEncoder().encode("<svg />").buffer),
|
|
57
57
|
});
|
|
58
58
|
const app = mountApp({ resolveStorage: () => storage });
|
|
59
|
-
const response = await app.request("/v1/media/uploads/evil.svg");
|
|
59
|
+
const response = await app.request("/v1/admin/media/uploads/evil.svg");
|
|
60
60
|
expect(response.status).toBe(200);
|
|
61
61
|
expect(response.headers.get("content-type")).toBe("application/octet-stream");
|
|
62
62
|
expect(response.headers.get("content-disposition")).toBe('attachment; filename="evil.svg"');
|
|
@@ -72,14 +72,14 @@ describe("media routes", () => {
|
|
|
72
72
|
});
|
|
73
73
|
it("responds 503 when storage is unconfigured", async () => {
|
|
74
74
|
const app = mountApp({ resolveStorage: () => null });
|
|
75
|
-
const response = await app.request("/v1/media/uploads/example.pdf");
|
|
75
|
+
const response = await app.request("/v1/admin/media/uploads/example.pdf");
|
|
76
76
|
expect(response.status).toBe(503);
|
|
77
77
|
});
|
|
78
78
|
it("rejects unsafe upload types", async () => {
|
|
79
79
|
const upload = vi.fn();
|
|
80
80
|
const app = mountApp({ resolveStorage: () => makeStorage({ upload }) });
|
|
81
81
|
const boundary = "----voyant-test-boundary";
|
|
82
|
-
const response = await app.request("/v1/uploads", {
|
|
82
|
+
const response = await app.request("/v1/admin/uploads", {
|
|
83
83
|
method: "POST",
|
|
84
84
|
headers: { "content-type": `multipart/form-data; boundary=${boundary}` },
|
|
85
85
|
body: multipartFileBody({
|
|
@@ -95,7 +95,7 @@ describe("media routes", () => {
|
|
|
95
95
|
it("accepts bounded uploads on the admin surface", async () => {
|
|
96
96
|
const upload = vi.fn(async (_body, options) => ({
|
|
97
97
|
key: options.key,
|
|
98
|
-
url: `/api/v1/media/${options.key}`,
|
|
98
|
+
url: `/api/v1/admin/media/${options.key}`,
|
|
99
99
|
}));
|
|
100
100
|
const app = mountApp({ resolveStorage: () => makeStorage({ upload }) });
|
|
101
101
|
const boundary = "----voyant-test-boundary";
|
|
@@ -133,7 +133,7 @@ describe("media routes", () => {
|
|
|
133
133
|
it("rejects an invalid video upload ticket body", async () => {
|
|
134
134
|
const signVideoUploadTicket = vi.fn();
|
|
135
135
|
const app = mountApp({ signVideoUploadTicket });
|
|
136
|
-
const response = await app.request("/v1/uploads/video", {
|
|
136
|
+
const response = await app.request("/v1/admin/uploads/video", {
|
|
137
137
|
method: "POST",
|
|
138
138
|
headers: { "content-type": "application/json" },
|
|
139
139
|
body: JSON.stringify({ fileSize: -1, maxDurationSeconds: 60 }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyant-travel/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.106.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"directory": "packages/storage"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
|
-
"typecheck": "tsc
|
|
72
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
73
73
|
"lint": "biome check src/",
|
|
74
74
|
"test": "vitest run",
|
|
75
|
-
"build": "tsc -p tsconfig.json",
|
|
75
|
+
"build": "tsc -p tsconfig.build.json",
|
|
76
76
|
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
77
77
|
},
|
|
78
78
|
"main": "./dist/index.js",
|