@@ -7,37 +7,55 @@ const OBJECT_NOT_FOUND_LABELS = new Set(['NotFound', 'NoSuchKey', 'BlobNotFound'
77 */
88const CONTAINER_NOT_FOUND_LABELS = new Set ( [ 'NoSuchBucket' , 'ContainerNotFound' ] )
99
10+ function readLabels ( error : unknown ) : string [ ] | null {
11+ if ( ! error || typeof error !== 'object' ) return null
12+ const { name, code } = error as { name ?: unknown ; code ?: unknown }
13+ /**
14+ * `name` and `code` are both consulted: Azure raises a `RestError` whose `name`
15+ * carries the class and whose `code` carries the reason, while the AWS SDK puts
16+ * the reason in `name`.
17+ */
18+ return [ name , code ] . filter ( ( value ) : value is string => typeof value === 'string' )
19+ }
20+
21+ /**
22+ * True when a provider names the missing resource as the object itself.
23+ *
24+ * The strict form. Use it wherever the caller cannot vouch for what was requested,
25+ * because an unlabelled 404 is ambiguous: GCS answers a missing object and a
26+ * missing bucket identically (`code: 404`, `errors[].reason: 'notFound'`), so only
27+ * the human-readable message separates them. Treating that as absence would let a
28+ * bucket misconfiguration read as "no metadata" and fail every read closed with
29+ * nothing left to alert on, so it stays an error here.
30+ */
31+ export function hasObjectNotFoundLabel ( error : unknown ) : boolean {
32+ const labels = readLabels ( error )
33+ if ( ! labels ) return false
34+ if ( labels . some ( ( label ) => CONTAINER_NOT_FOUND_LABELS . has ( label ) ) ) return false
35+ return labels . some ( ( label ) => OBJECT_NOT_FOUND_LABELS . has ( label ) )
36+ }
37+
1038/**
11- * True when a storage provider reports that an object simply does not exist.
39+ * True when a storage provider reports that an object does not exist.
1240 *
13- * Absence is an expected outcome of a lookup, not a failure, so callers turn this
14- * into an empty result rather than propagating it. Every provider spells it
15- * differently — S3 throws `NotFound` (HeadObject) or `NoSuchKey` (GetObject),
16- * Azure Blob throws `BlobNotFound`, and GCS throws a numeric `code: 404` — and the
17- * status may arrive as `$metadata.httpStatusCode`, `statusCode`, or `code` .
41+ * The lenient form, for a caller that has just performed an object-level operation
42+ * and can therefore attribute a bare 404 to that object — which is what every
43+ * provider client here does, and how each spelled this check before it was shared.
44+ * It additionally accepts a bare 404 ( `code`, `statusCode`, or
45+ * `$metadata.httpStatusCode`), which GCS relies on since it carries no label .
1846 *
19- * Only genuine absence matches. A network failure, a permission denial, or a
20- * provider 5xx still propagates so it surfaces as the error it is.
47+ * A network failure, a permission denial, or a provider 5xx still propagates.
2148 */
2249export function isObjectNotFoundError ( error : unknown ) : boolean {
23- if ( ! error || typeof error !== 'object' ) return false
50+ const labels = readLabels ( error )
51+ if ( ! labels ) return false
52+ if ( labels . some ( ( label ) => CONTAINER_NOT_FOUND_LABELS . has ( label ) ) ) return false
53+ if ( labels . some ( ( label ) => OBJECT_NOT_FOUND_LABELS . has ( label ) ) ) return true
2454
25- const { name, code, statusCode, $metadata } = error as {
26- name ?: unknown
55+ const { code, statusCode, $metadata } = error as {
2756 code ?: unknown
2857 statusCode ?: unknown
2958 $metadata ?: { httpStatusCode ?: unknown }
3059 }
31-
32- /**
33- * `name` and `code` are both consulted: Azure raises a `RestError` whose `name`
34- * carries the class and whose `code` carries the reason, while the AWS SDK puts
35- * the reason in `name`.
36- */
37- const labels = [ name , code ] . filter ( ( value ) : value is string => typeof value === 'string' )
38-
39- if ( labels . some ( ( label ) => CONTAINER_NOT_FOUND_LABELS . has ( label ) ) ) return false
40- if ( labels . some ( ( label ) => OBJECT_NOT_FOUND_LABELS . has ( label ) ) ) return true
41-
4260 return code === 404 || statusCode === 404 || $metadata ?. httpStatusCode === 404
4361}
0 commit comments