@usequark/quark-core 2.5.7 → 2.5.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usequark/quark-core",
3
- "version": "2.5.7",
3
+ "version": "2.5.8",
4
4
  "description": "Core utilities for Quark apps: auth, BullMQ queues, Redis, email, storage, rate limiting, logging, and error handling",
5
5
  "keywords": [
6
6
  "quark",
package/src/storage.js CHANGED
@@ -187,11 +187,16 @@ export function generateStorageKey(originalFilename, options = {}) {
187
187
  *
188
188
  * Checks `ASSET_CDN_URL` first - if set, prepends it to the key.
189
189
  * This is provider-agnostic: works with any CDN (CloudFront, Cloudflare,
190
- * Bunny, Fastly, etc.) as long as the CDN is pointed at the same bucket.
190
+ * Bunny, Fastly, etc.) provided the CDN is rooted at the bucket, so that a
191
+ * request for `<cdn>/<key>` maps to the object stored at `<key>`. That is the
192
+ * standard arrangement for a CDN in front of a bucket and needs no rewrite
193
+ * rules. If your CDN is not bucket-rooted, set `S3_PUBLIC_URL` instead and use
194
+ * the S3 adapter's `getPublicUrl()`.
191
195
  *
192
196
  * Falls back to the local API route (`/api/files/<key>`) when no CDN is
193
197
  * configured - covers local development and any environment where
194
- * `STORAGE_PROVIDER=local` is used.
198
+ * `STORAGE_PROVIDER=local` is used. The scaffolded `/api/files/[id]` route
199
+ * accepts a storage key as well as a database id, so this resolves.
195
200
  *
196
201
  * @param {string} key - Storage key (e.g. "uploads/2026/02/abc-photo.jpg")
197
202
  * @returns {string} Full CDN URL or local API route
@@ -351,6 +351,55 @@ test("getAssetUrl - percent-encodes key in fallback URL", () => {
351
351
  assert.strictEqual(url, "/api/files/uploads%2Ffile%20with%20spaces.jpg");
352
352
  });
353
353
 
354
+ // ─── getAssetUrl / route contract ────────────────────────────────────────────
355
+ //
356
+ // The scaffolded GET /api/files/[id] route used to look up by database id only,
357
+ // so every URL this function produced resolved to a 404. It now also resolves a
358
+ // storage key. These tests pin the two halves of that contract from each side:
359
+ // the shape produced here, and the lookup performed by the route (in
360
+ // apps/web/src/app/api/files/[id]/route.test.js).
361
+ test("getAssetUrl - fallback URL carries the key in a single path segment", () => {
362
+ delete process.env.ASSET_CDN_URL;
363
+ const url = getAssetUrl("uploads/2026/02/abc-photo.jpg");
364
+
365
+ // A key with slashes must stay inside ONE path segment, or the request never
366
+ // reaches the [id] route at all — it 404s as an unrouted path. `URL` keeps the
367
+ // percent-encoding in `pathname`, so it is the encoded slashes that hold the
368
+ // segment together.
369
+ const segments = new URL(url, "http://localhost").pathname.split("/");
370
+ assert.strictEqual(
371
+ segments.length,
372
+ 4,
373
+ `key leaked into extra segments: ${url}`,
374
+ );
375
+ assert.strictEqual(segments[3], "uploads%2F2026%2F02%2Fabc-photo.jpg");
376
+ // And it must decode back to exactly the key, which is what the route looks up.
377
+ assert.strictEqual(
378
+ decodeURIComponent(segments[3]),
379
+ "uploads/2026/02/abc-photo.jpg",
380
+ );
381
+ });
382
+
383
+ test("getAssetUrl - CDN URL leaves the key path unencoded", () => {
384
+ // The CDN branch is a real URL space, not a path segment: encoding the slashes
385
+ // would ask the CDN for a single object literally named "uploads/2026/02/...".
386
+ // A bucket-rooted CDN maps the path straight onto the key.
387
+ process.env.ASSET_CDN_URL = "https://assets.example.com";
388
+ try {
389
+ const url = getAssetUrl("uploads/2026/02/abc-photo.jpg");
390
+ assert.strictEqual(
391
+ url,
392
+ "https://assets.example.com/uploads/2026/02/abc-photo.jpg",
393
+ );
394
+ assert.ok(
395
+ !url.includes("%2F"),
396
+ "CDN URL must not percent-encode the key path",
397
+ );
398
+ } finally {
399
+ delete process.env.ASSET_CDN_URL;
400
+ }
401
+ });
402
+
354
403
  // ---------------------------------------------------------------------------
355
404
  // getSignedUploadUrl
356
405
  // ---------------------------------------------------------------------------