hide-a-bed 7.1.0 → 7.1.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.
@@ -1,6 +1,6 @@
1
- import { getCouchError, getReason } from "./response.mts";
2
- import type { StandardSchemaV1 } from "../../types/standard-schema.ts";
3
- import { isObject } from "../../types/types.utils.ts";
1
+ import { getCouchError, getReason } from './response.mts'
2
+ import type { StandardSchemaV1 } from '../../types/standard-schema.ts'
3
+ import { isObject } from '../../types/types.utils.ts'
4
4
 
5
5
  /**
6
6
  * Represents a network-level error emitted by Node.js or HTTP client libraries.
@@ -11,42 +11,42 @@ export interface NetworkError {
11
11
  /**
12
12
  * Machine-readable error code describing the network failure.
13
13
  */
14
- code: string;
14
+ code: string
15
15
 
16
16
  /**
17
17
  * Optional human-readable message supplied by the underlying library.
18
18
  */
19
- message?: string;
19
+ message?: string
20
20
  }
21
21
 
22
22
  type ErrorWithCause = {
23
- cause?: unknown;
24
- };
23
+ cause?: unknown
24
+ }
25
25
 
26
26
  export type ErrorCategory =
27
- | "conflict"
28
- | "network"
29
- | "not_found"
30
- | "operation"
31
- | "retryable"
32
- | "validation"
33
- | "transaction";
27
+ | 'conflict'
28
+ | 'network'
29
+ | 'not_found'
30
+ | 'operation'
31
+ | 'retryable'
32
+ | 'validation'
33
+ | 'transaction'
34
34
 
35
35
  export type ErrorOperation =
36
- | "get"
37
- | "getAtRev"
38
- | "getDBInfo"
39
- | "headDB"
40
- | "patch"
41
- | "patchDangerously"
42
- | "put"
43
- | "query"
44
- | "queryStream"
45
- | "remove"
46
- | "request"
47
- | "watchDocs";
48
-
49
- const RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);
36
+ | 'get'
37
+ | 'getAtRev'
38
+ | 'getDBInfo'
39
+ | 'headDB'
40
+ | 'patch'
41
+ | 'patchDangerously'
42
+ | 'put'
43
+ | 'query'
44
+ | 'queryStream'
45
+ | 'remove'
46
+ | 'request'
47
+ | 'watchDocs'
48
+
49
+ const RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504])
50
50
 
51
51
  const NETWORK_ERROR_STATUS_MAP = {
52
52
  ECONNREFUSED: 503,
@@ -56,53 +56,42 @@ const NETWORK_ERROR_STATUS_MAP = {
56
56
  ENOTFOUND: 503,
57
57
  EPIPE: 503,
58
58
  EHOSTUNREACH: 503,
59
- ESOCKETTIMEDOUT: 503,
60
- } as const satisfies Record<string, number>;
61
-
62
- type NetworkErrorCode = keyof typeof NETWORK_ERROR_STATUS_MAP;
63
-
64
- const isNetworkError = (
65
- value: unknown,
66
- ): value is NetworkError & { code: NetworkErrorCode } => {
67
- if (typeof value !== "object" || value === null) return false;
68
- const candidate = value as { code?: unknown };
69
- return (
70
- typeof candidate.code === "string" &&
71
- candidate.code in NETWORK_ERROR_STATUS_MAP
72
- );
73
- };
59
+ ESOCKETTIMEDOUT: 503
60
+ } as const satisfies Record<string, number>
61
+
62
+ type NetworkErrorCode = keyof typeof NETWORK_ERROR_STATUS_MAP
63
+
64
+ const isNetworkError = (value: unknown): value is NetworkError & { code: NetworkErrorCode } => {
65
+ if (typeof value !== 'object' || value === null) return false
66
+ const candidate = value as { code?: unknown }
67
+ return typeof candidate.code === 'string' && candidate.code in NETWORK_ERROR_STATUS_MAP
68
+ }
74
69
 
75
70
  const getNestedNetworkError = (
76
- value: unknown,
71
+ value: unknown
77
72
  ): (NetworkError & { code: NetworkErrorCode }) | null => {
78
73
  if (isNetworkError(value)) {
79
- return value;
74
+ return value
80
75
  }
81
76
 
82
- if (typeof value !== "object" || value === null) {
83
- return null;
77
+ if (typeof value !== 'object' || value === null) {
78
+ return null
84
79
  }
85
80
 
86
- const candidate = value as ErrorWithCause;
87
- return isNetworkError(candidate.cause) ? candidate.cause : null;
88
- };
81
+ const candidate = value as ErrorWithCause
82
+ return isNetworkError(candidate.cause) ? candidate.cause : null
83
+ }
89
84
 
90
- export const hasStatusCode = (
91
- error: unknown,
92
- ): error is { statusCode: number } => {
93
- return (
94
- isObject(error) &&
95
- "statusCode" in error &&
96
- typeof error.statusCode === "number"
97
- );
98
- };
85
+ export const hasStatusCode = (error: unknown): error is { statusCode: number } => {
86
+ return isObject(error) && 'statusCode' in error && typeof error.statusCode === 'number'
87
+ }
99
88
 
100
89
  export const isTransientAuthError = (error: unknown, attempt: number) => {
101
- if (!hasStatusCode(error)) return false;
102
- if (attempt > 0) return false;
90
+ if (!hasStatusCode(error)) return false
91
+ if (attempt > 0) return false
103
92
 
104
- return error.statusCode === 401 || error.statusCode === 403;
105
- };
93
+ return error.statusCode === 401 || error.statusCode === 403
94
+ }
106
95
 
107
96
  /**
108
97
  * Shared structured fields available on hide-a-bed operational errors.
@@ -110,15 +99,15 @@ export const isTransientAuthError = (error: unknown, attempt: number) => {
110
99
  * @public
111
100
  */
112
101
  export type HideABedErrorOptions = {
113
- category: ErrorCategory;
114
- cause?: unknown;
115
- couchError?: string;
116
- couchReason?: string;
117
- docId?: string;
118
- operation?: ErrorOperation;
119
- retryable: boolean;
120
- statusCode?: number;
121
- };
102
+ category: ErrorCategory
103
+ cause?: unknown
104
+ couchError?: string
105
+ couchReason?: string
106
+ docId?: string
107
+ operation?: ErrorOperation
108
+ retryable: boolean
109
+ statusCode?: number
110
+ }
122
111
 
123
112
  /**
124
113
  * Shared base class for operational errors thrown by hide-a-bed.
@@ -126,37 +115,34 @@ export type HideABedErrorOptions = {
126
115
  * @public
127
116
  */
128
117
  export class HideABedError extends Error {
129
- readonly category: ErrorCategory;
130
- readonly couchError?: string;
131
- readonly couchReason?: string;
132
- readonly docId?: string;
133
- readonly operation?: ErrorOperation;
134
- readonly retryable: boolean;
135
- readonly statusCode?: number;
118
+ readonly category: ErrorCategory
119
+ readonly couchError?: string
120
+ readonly couchReason?: string
121
+ readonly docId?: string
122
+ readonly operation?: ErrorOperation
123
+ readonly retryable: boolean
124
+ readonly statusCode?: number
136
125
 
137
126
  constructor(message: string, options: HideABedErrorOptions) {
138
- super(
139
- message,
140
- options.cause === undefined ? undefined : { cause: options.cause },
141
- );
142
- this.name = "HideABedError";
143
- this.category = options.category;
144
- this.couchError = options.couchError;
145
- this.couchReason = options.couchReason;
146
- this.docId = options.docId;
147
- this.operation = options.operation;
148
- this.retryable = options.retryable;
149
- this.statusCode = options.statusCode;
127
+ super(message, options.cause === undefined ? undefined : { cause: options.cause })
128
+ this.name = 'HideABedError'
129
+ this.category = options.category
130
+ this.couchError = options.couchError
131
+ this.couchReason = options.couchReason
132
+ this.docId = options.docId
133
+ this.operation = options.operation
134
+ this.retryable = options.retryable
135
+ this.statusCode = options.statusCode
150
136
  }
151
137
  }
152
138
 
153
139
  export type ValidationErrorOptions = Omit<
154
140
  Partial<HideABedErrorOptions>,
155
- "category" | "retryable"
141
+ 'category' | 'retryable'
156
142
  > & {
157
- issues: ReadonlyArray<StandardSchemaV1.Issue>;
158
- message?: string;
159
- };
143
+ issues: ReadonlyArray<StandardSchemaV1.Issue>
144
+ message?: string
145
+ }
160
146
 
161
147
  /**
162
148
  * Error thrown when a requested CouchDB document cannot be found.
@@ -170,24 +156,21 @@ export type ValidationErrorOptions = Omit<
170
156
  export class NotFoundError extends HideABedError {
171
157
  constructor(
172
158
  docId: string,
173
- options: Omit<
174
- Partial<HideABedErrorOptions>,
175
- "category" | "docId" | "retryable"
176
- > & {
177
- message?: string;
178
- } = {},
159
+ options: Omit<Partial<HideABedErrorOptions>, 'category' | 'docId' | 'retryable'> & {
160
+ message?: string
161
+ } = {}
179
162
  ) {
180
- super(options.message ?? "Document not found", {
181
- category: "not_found",
182
- couchError: options.couchError ?? "not_found",
163
+ super(options.message ?? 'Document not found', {
164
+ category: 'not_found',
165
+ couchError: options.couchError ?? 'not_found',
183
166
  couchReason: options.couchReason,
184
167
  cause: options.cause,
185
168
  docId,
186
169
  operation: options.operation,
187
170
  retryable: false,
188
- statusCode: options.statusCode ?? 404,
189
- });
190
- this.name = "NotFoundError";
171
+ statusCode: options.statusCode ?? 404
172
+ })
173
+ this.name = 'NotFoundError'
191
174
  }
192
175
  }
193
176
 
@@ -199,24 +182,21 @@ export class NotFoundError extends HideABedError {
199
182
  export class ConflictError extends HideABedError {
200
183
  constructor(
201
184
  docId: string,
202
- options: Omit<
203
- Partial<HideABedErrorOptions>,
204
- "category" | "docId" | "retryable"
205
- > & {
206
- message?: string;
207
- } = {},
185
+ options: Omit<Partial<HideABedErrorOptions>, 'category' | 'docId' | 'retryable'> & {
186
+ message?: string
187
+ } = {}
208
188
  ) {
209
- super(options.message ?? "Document update conflict", {
210
- category: "conflict",
211
- couchError: options.couchError ?? "conflict",
189
+ super(options.message ?? 'Document update conflict', {
190
+ category: 'conflict',
191
+ couchError: options.couchError ?? 'conflict',
212
192
  couchReason: options.couchReason,
213
193
  cause: options.cause,
214
194
  docId,
215
195
  operation: options.operation,
216
196
  retryable: false,
217
- statusCode: options.statusCode ?? 409,
218
- });
219
- this.name = "ConflictError";
197
+ statusCode: options.statusCode ?? 409
198
+ })
199
+ this.name = 'ConflictError'
220
200
  }
221
201
  }
222
202
 
@@ -228,21 +208,21 @@ export class ConflictError extends HideABedError {
228
208
  export class OperationError extends HideABedError {
229
209
  constructor(
230
210
  message: string,
231
- options: Omit<Partial<HideABedErrorOptions>, "category" | "retryable"> & {
232
- category?: Extract<ErrorCategory, "operation" | "transaction">;
233
- } = {},
211
+ options: Omit<Partial<HideABedErrorOptions>, 'category' | 'retryable'> & {
212
+ category?: Extract<ErrorCategory, 'operation' | 'transaction'>
213
+ } = {}
234
214
  ) {
235
215
  super(message, {
236
- category: options.category ?? "operation",
216
+ category: options.category ?? 'operation',
237
217
  cause: options.cause,
238
218
  couchError: options.couchError,
239
219
  couchReason: options.couchReason,
240
220
  docId: options.docId,
241
221
  operation: options.operation,
242
222
  retryable: false,
243
- statusCode: options.statusCode,
244
- });
245
- this.name = "OperationError";
223
+ statusCode: options.statusCode
224
+ })
225
+ this.name = 'OperationError'
246
226
  }
247
227
  }
248
228
 
@@ -252,21 +232,21 @@ export class OperationError extends HideABedError {
252
232
  * @public
253
233
  */
254
234
  export class ValidationError extends HideABedError {
255
- readonly issues: ValidationErrorOptions["issues"];
235
+ readonly issues: ValidationErrorOptions['issues']
256
236
 
257
237
  constructor(options: ValidationErrorOptions) {
258
- super(options.message ?? "Validation failed", {
259
- category: "validation",
238
+ super(options.message ?? 'Validation failed', {
239
+ category: 'validation',
260
240
  cause: options.cause,
261
241
  couchError: options.couchError,
262
242
  couchReason: options.couchReason,
263
243
  docId: options.docId,
264
244
  operation: options.operation,
265
245
  retryable: false,
266
- statusCode: options.statusCode,
267
- });
268
- this.name = "ValidationError";
269
- this.issues = options.issues;
246
+ statusCode: options.statusCode
247
+ })
248
+ this.name = 'ValidationError'
249
+ this.issues = options.issues
270
250
  }
271
251
  }
272
252
 
@@ -283,24 +263,21 @@ export class RetryableError extends HideABedError {
283
263
  constructor(
284
264
  message: string,
285
265
  statusCode?: number,
286
- options: Omit<
287
- Partial<HideABedErrorOptions>,
288
- "category" | "retryable" | "statusCode"
289
- > & {
290
- category?: Extract<ErrorCategory, "network" | "retryable">;
291
- } = {},
266
+ options: Omit<Partial<HideABedErrorOptions>, 'category' | 'retryable' | 'statusCode'> & {
267
+ category?: Extract<ErrorCategory, 'network' | 'retryable'>
268
+ } = {}
292
269
  ) {
293
270
  super(message, {
294
- category: options.category ?? "retryable",
271
+ category: options.category ?? 'retryable',
295
272
  cause: options.cause,
296
273
  couchError: options.couchError,
297
274
  couchReason: options.couchReason,
298
275
  docId: options.docId,
299
276
  operation: options.operation,
300
277
  retryable: true,
301
- statusCode,
302
- });
303
- this.name = "RetryableError";
278
+ statusCode
279
+ })
280
+ this.name = 'RetryableError'
304
281
  }
305
282
 
306
283
  /**
@@ -310,11 +287,9 @@ export class RetryableError extends HideABedError {
310
287
  *
311
288
  * @returns `true` if the status code is considered retryable; otherwise `false`.
312
289
  */
313
- static isRetryableStatusCode(
314
- statusCode: number | undefined,
315
- ): statusCode is number {
316
- if (typeof statusCode !== "number") return false;
317
- return RETRYABLE_STATUS_CODES.has(statusCode);
290
+ static isRetryableStatusCode(statusCode: number | undefined): statusCode is number {
291
+ if (typeof statusCode !== 'number') return false
292
+ return RETRYABLE_STATUS_CODES.has(statusCode)
318
293
  }
319
294
 
320
295
  /**
@@ -325,45 +300,42 @@ export class RetryableError extends HideABedError {
325
300
  * @throws {@link RetryableError} When the error maps to a retryable network condition.
326
301
  * @throws {*} Re-throws the original error when it cannot be mapped.
327
302
  */
328
- static handleNetworkError(
329
- err: unknown,
330
- operation: ErrorOperation = "request",
331
- ): never {
332
- const networkError = getNestedNetworkError(err);
303
+ static handleNetworkError(err: unknown, operation: ErrorOperation = 'request'): never {
304
+ const networkError = getNestedNetworkError(err)
333
305
 
334
306
  if (networkError) {
335
- const statusCode = NETWORK_ERROR_STATUS_MAP[networkError.code];
307
+ const statusCode = NETWORK_ERROR_STATUS_MAP[networkError.code]
336
308
  if (statusCode) {
337
- throw new RetryableError("Network request failed", statusCode, {
338
- category: "network",
309
+ throw new RetryableError('Network request failed', statusCode, {
310
+ category: 'network',
339
311
  cause: err,
340
- operation,
341
- });
312
+ operation
313
+ })
342
314
  }
343
315
  }
344
316
 
345
- throw err;
317
+ throw err
346
318
  }
347
319
  }
348
320
 
349
321
  type ResponseErrorOptions = {
350
- body?: unknown;
351
- defaultMessage: string;
352
- docId?: string;
353
- notFoundMessage?: string;
354
- operation: ErrorOperation;
355
- statusCode?: number;
356
- };
322
+ body?: unknown
323
+ defaultMessage: string
324
+ docId?: string
325
+ notFoundMessage?: string
326
+ operation: ErrorOperation
327
+ statusCode?: number
328
+ }
357
329
 
358
330
  const getResponseErrorMessage = (body: unknown, defaultMessage: string) => {
359
- const reason = getReason(body, "").trim();
331
+ const reason = getReason(body, '').trim()
360
332
 
361
333
  if (!reason || reason === defaultMessage) {
362
- return defaultMessage;
334
+ return defaultMessage
363
335
  }
364
336
 
365
- return `${defaultMessage}: ${reason}`;
366
- };
337
+ return `${defaultMessage}: ${reason}`
338
+ }
367
339
 
368
340
  export function createResponseError({
369
341
  body,
@@ -371,22 +343,20 @@ export function createResponseError({
371
343
  docId,
372
344
  notFoundMessage,
373
345
  operation,
374
- statusCode,
346
+ statusCode
375
347
  }: ResponseErrorOptions): HideABedError {
376
- const couchError = getCouchError(body);
377
- const couchReason = getReason(body, "").trim() || undefined;
378
- const message = getResponseErrorMessage(body, defaultMessage);
348
+ const couchError = getCouchError(body)
349
+ const couchReason = getReason(body, '').trim() || undefined
350
+ const message = getResponseErrorMessage(body, defaultMessage)
379
351
 
380
352
  if (statusCode === 404 && docId) {
381
353
  return new NotFoundError(docId, {
382
354
  couchError,
383
355
  couchReason,
384
- message: notFoundMessage
385
- ? getResponseErrorMessage(body, notFoundMessage)
386
- : undefined,
356
+ message: notFoundMessage ? getResponseErrorMessage(body, notFoundMessage) : undefined,
387
357
  operation,
388
- statusCode,
389
- });
358
+ statusCode
359
+ })
390
360
  }
391
361
 
392
362
  if (statusCode === 409 && docId) {
@@ -395,16 +365,16 @@ export function createResponseError({
395
365
  couchReason,
396
366
  message,
397
367
  operation,
398
- statusCode,
399
- });
368
+ statusCode
369
+ })
400
370
  }
401
371
 
402
372
  if (RetryableError.isRetryableStatusCode(statusCode)) {
403
373
  return new RetryableError(message, statusCode, {
404
374
  couchError,
405
375
  couchReason,
406
- operation,
407
- });
376
+ operation
377
+ })
408
378
  }
409
379
 
410
380
  return new OperationError(message, {
@@ -412,13 +382,13 @@ export function createResponseError({
412
382
  couchReason,
413
383
  docId,
414
384
  operation,
415
- statusCode,
416
- });
385
+ statusCode
386
+ })
417
387
  }
418
388
 
419
389
  export function isConflictError(err: unknown): boolean {
420
- if (err instanceof ConflictError) return true;
421
- if (typeof err !== "object" || err === null) return false;
422
- const candidate = err as { statusCode?: unknown };
423
- return candidate.statusCode === 409;
390
+ if (err instanceof ConflictError) return true
391
+ if (typeof err !== 'object' || err === null) return false
392
+ const candidate = err as { statusCode?: unknown }
393
+ return candidate.statusCode === 409
424
394
  }