zerobucket 0.8.1 → 0.9.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/README.md CHANGED
@@ -123,6 +123,14 @@ await images.get(id, { context: { userId } }); // throws AccessDeniedError if th
123
123
  **A hook that throws fails closed**: the original exception propagates; it is never treated as
124
124
  "allow". Denied calls never reach the database. `exists()` is deliberately not gated.
125
125
 
126
+ **Your hook only ever sees a well-formed UUID.** A malformed id (such as one taken straight from a URL) can never
127
+ exist, so it is "not found" (`ImageNotFoundError`, or `error: "not found"` for that item in `getMany`) and your hook is
128
+ never called with it. You can use the id in SQL (`WHERE id = $1` on a `uuid` column) without validating it first. _(Before
129
+ 0.9.0 the hook received the raw string, so on 0.8.x guard against a malformed id yourself: a request for
130
+ `/images/garbage` otherwise became a database error and a `500`.)_ The
131
+ [example app](https://github.com/KedarGhadyalji/ZeroBucket/tree/main/examples/node-integration) shows a working hook, plus
132
+ transactional uploads and serving.
133
+
126
134
  ## Streaming and ranges
127
135
 
128
136
  ```ts
@@ -452,6 +460,7 @@ differ).
452
460
  | -------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
453
461
  | API | sync + async clients | one async client |
454
462
  | Malformed id (`"abc"`) | `StorageError` from Postgres | treated as not found (`ImageNotFoundError`, no DB round trip), friendlier for `/images/:id` routes |
463
+ | Malformed id and the `beforeGet` hook | the hook **is** called with the raw string (validate it yourself) | never reaches the hook: not found, so the hook only sees well-formed UUIDs (0.9.0+) |
455
464
  | `optimize` | Pillow; EXIF stripped, not applied | sharp; EXIF **applied then stripped**; transparency → **white** for JPEG |
456
465
  | `putMany` inserts | pipelined | one transaction, statements run sequentially |
457
466
  | Formats | JPEG, PNG, WebP, HEIC (`[heic]` extra) | JPEG, PNG, WebP, HEIC (optional `libheif-js`) |
@@ -392,7 +392,9 @@ interface ZeroBucketOptions<C = unknown> {
392
392
  * Authorization hook for get / getMany (per id) / getStream / streamTo / metadata.
393
393
  * Return false to deny. A hook that THROWS fails closed: the exception
394
394
  * propagates (or is captured per item in getMany) and is never treated as allow.
395
- * NOT called for exists().
395
+ * NOT called for exists(). Only ever called with a well-formed UUID: a malformed id is
396
+ * "not found" (ImageNotFoundError, or `error: "not found"` per item in getMany) and never
397
+ * reaches the hook.
396
398
  */
397
399
  beforeGet?: BeforeGetHook<C>;
398
400
  /** Authorization hook for put / putMany (evaluated once per call). Same fail-closed rule. */
@@ -392,7 +392,9 @@ interface ZeroBucketOptions<C = unknown> {
392
392
  * Authorization hook for get / getMany (per id) / getStream / streamTo / metadata.
393
393
  * Return false to deny. A hook that THROWS fails closed: the exception
394
394
  * propagates (or is captured per item in getMany) and is never treated as allow.
395
- * NOT called for exists().
395
+ * NOT called for exists(). Only ever called with a well-formed UUID: a malformed id is
396
+ * "not found" (ImageNotFoundError, or `error: "not found"` per item in getMany) and never
397
+ * reaches the hook.
396
398
  */
397
399
  beforeGet?: BeforeGetHook<C>;
398
400
  /** Authorization hook for put / putMany (evaluated once per call). Same fail-closed rule. */
package/dist/http.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { Z as ZeroBucket } from './client-l4jPIFJI.cjs';
2
+ import { Z as ZeroBucket } from './client-7ZNu3xMJ.cjs';
3
3
  import 'pg';
4
4
 
5
5
  /**
package/dist/http.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { Z as ZeroBucket } from './client-l4jPIFJI.js';
2
+ import { Z as ZeroBucket } from './client-7ZNu3xMJ.js';
3
3
  import 'pg';
4
4
 
5
5
  /**
package/dist/index.cjs CHANGED
@@ -3407,6 +3407,7 @@ var ZeroBucket = class _ZeroBucket {
3407
3407
  }
3408
3408
  // ---- hooks -----------------------------------------------------------
3409
3409
  async checkBeforeGet(imageId, context) {
3410
+ if (!isValidId(imageId)) throw new ImageNotFoundError(imageId);
3410
3411
  if (!this.beforeGet) return;
3411
3412
  if (!await this.beforeGet(imageId, context))
3412
3413
  throw new AccessDeniedError("get", imageId);
@@ -3570,6 +3571,10 @@ var ZeroBucket = class _ZeroBucket {
3570
3571
  const allowedIdx = [];
3571
3572
  for (let i = 0; i < imageIds.length; i++) {
3572
3573
  const imageId = imageIds[i];
3574
+ if (!isValidId(imageId)) {
3575
+ results[i] = { imageId, image: null, error: "not found", success: false };
3576
+ continue;
3577
+ }
3573
3578
  if (this.beforeGet) {
3574
3579
  try {
3575
3580
  if (!await this.beforeGet(imageId, options.context)) {