@pencroff-lab/kore 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cjs/index.d.ts +3 -0
  3. package/dist/cjs/index.d.ts.map +1 -0
  4. package/dist/cjs/index.js +19 -0
  5. package/dist/cjs/index.js.map +1 -0
  6. package/dist/cjs/package.json +3 -0
  7. package/dist/cjs/src/types/err.d.ts +1116 -0
  8. package/dist/cjs/src/types/err.d.ts.map +1 -0
  9. package/dist/cjs/src/types/err.js +1324 -0
  10. package/dist/cjs/src/types/err.js.map +1 -0
  11. package/dist/cjs/src/types/index.d.ts +3 -0
  12. package/dist/cjs/src/types/index.d.ts.map +1 -0
  13. package/dist/cjs/src/types/index.js +19 -0
  14. package/dist/cjs/src/types/index.js.map +1 -0
  15. package/dist/cjs/src/types/outcome.d.ts +1002 -0
  16. package/dist/cjs/src/types/outcome.d.ts.map +1 -0
  17. package/dist/cjs/src/types/outcome.js +958 -0
  18. package/dist/cjs/src/types/outcome.js.map +1 -0
  19. package/dist/cjs/src/utils/format_dt.d.ts +9 -0
  20. package/dist/cjs/src/utils/format_dt.d.ts.map +1 -0
  21. package/dist/cjs/src/utils/format_dt.js +29 -0
  22. package/dist/cjs/src/utils/format_dt.js.map +1 -0
  23. package/dist/cjs/src/utils/index.d.ts +2 -0
  24. package/dist/cjs/src/utils/index.d.ts.map +1 -0
  25. package/dist/cjs/src/utils/index.js +18 -0
  26. package/dist/cjs/src/utils/index.js.map +1 -0
  27. package/dist/esm/index.d.ts +3 -0
  28. package/dist/esm/index.d.ts.map +1 -0
  29. package/dist/esm/index.js +3 -0
  30. package/dist/esm/index.js.map +1 -0
  31. package/dist/esm/src/types/err.d.ts +1116 -0
  32. package/dist/esm/src/types/err.d.ts.map +1 -0
  33. package/dist/esm/src/types/err.js +1320 -0
  34. package/dist/esm/src/types/err.js.map +1 -0
  35. package/dist/esm/src/types/index.d.ts +3 -0
  36. package/dist/esm/src/types/index.d.ts.map +1 -0
  37. package/dist/esm/src/types/index.js +3 -0
  38. package/dist/esm/src/types/index.js.map +1 -0
  39. package/dist/esm/src/types/outcome.d.ts +1002 -0
  40. package/dist/esm/src/types/outcome.d.ts.map +1 -0
  41. package/dist/esm/src/types/outcome.js +954 -0
  42. package/dist/esm/src/types/outcome.js.map +1 -0
  43. package/dist/esm/src/utils/format_dt.d.ts +9 -0
  44. package/dist/esm/src/utils/format_dt.d.ts.map +1 -0
  45. package/dist/esm/src/utils/format_dt.js +26 -0
  46. package/dist/esm/src/utils/format_dt.js.map +1 -0
  47. package/dist/esm/src/utils/index.d.ts +2 -0
  48. package/dist/esm/src/utils/index.d.ts.map +1 -0
  49. package/dist/esm/src/utils/index.js +2 -0
  50. package/dist/esm/src/utils/index.js.map +1 -0
  51. package/package.json +56 -0
@@ -0,0 +1,1116 @@
1
+ /**
2
+ * @fileoverview Error-as-value implementation for TypeScript applications.
3
+ *
4
+ * This module provides a Go-style error handling approach where errors are
5
+ * passed as values rather than thrown as exceptions. The `Err` class supports
6
+ * both single error wrapping with context and error aggregation.
7
+ *
8
+ * ## Immutability Contract
9
+ *
10
+ * All `Err` instances are immutable. Methods that appear to modify an error
11
+ * (like `wrap`, `withCode`, `withMetadata`, `add`) return new instances.
12
+ * The original error is never mutated. This means:
13
+ *
14
+ * - Safe to pass errors across boundaries without defensive copying
15
+ * - Method chaining always produces new instances
16
+ * - No "spooky action at a distance" bugs
17
+ *
18
+ * @example Basic usage with tuple pattern
19
+ * ```typescript
20
+ * import { Err } from './err';
21
+ *
22
+ * function divide(a: number, b: number): [number, null] | [null, Err] {
23
+ * if (b === 0) {
24
+ * return [null, Err.from('Division by zero', 'MATH_ERROR')];
25
+ * }
26
+ * return [a / b, null];
27
+ * }
28
+ *
29
+ * const [result, err] = divide(10, 0);
30
+ * if (err) {
31
+ * console.error(err.toString());
32
+ * return;
33
+ * }
34
+ * console.log(result); // result is number here
35
+ * ```
36
+ *
37
+ * @example Error wrapping with context
38
+ * ```typescript
39
+ * function readConfig(path: string): [Config, null] | [null, Err] {
40
+ * const [content, readErr] = readFile(path);
41
+ * if (readErr) {
42
+ * return [null, readErr.wrap(`Failed to read config from ${path}`)];
43
+ * }
44
+ *
45
+ * const [parsed, parseErr] = parseJSON(content);
46
+ * if (parseErr) {
47
+ * return [null, parseErr
48
+ * .wrap('Invalid config format')
49
+ * .withCode('CONFIG_ERROR')
50
+ * .withMetadata({ path })];
51
+ * }
52
+ *
53
+ * return [parsed as Config, null];
54
+ * }
55
+ * ```
56
+ *
57
+ * @example Static wrap for catching native errors
58
+ * ```typescript
59
+ * function parseData(raw: string): [Data, null] | [null, Err] {
60
+ * try {
61
+ * return [JSON.parse(raw), null];
62
+ * } catch (e) {
63
+ * return [null, Err.wrap('Failed to parse data', e as Error)];
64
+ * }
65
+ * }
66
+ * ```
67
+ *
68
+ * @example Aggregating multiple errors
69
+ * ```typescript
70
+ * function validateUser(input: UserInput): [User, null] | [null, Err] {
71
+ * let errors = Err.aggregate('Validation failed');
72
+ *
73
+ * if (!input.name?.trim()) {
74
+ * errors = errors.add('Name is required');
75
+ * }
76
+ * if (!input.email?.includes('@')) {
77
+ * errors = errors.add(Err.from('Invalid email', 'INVALID_EMAIL'));
78
+ * }
79
+ * if (input.age !== undefined && input.age < 0) {
80
+ * errors = errors.add('Age cannot be negative');
81
+ * }
82
+ *
83
+ * if (errors.count > 0) {
84
+ * return [null, errors.withCode('VALIDATION_ERROR')];
85
+ * }
86
+ *
87
+ * return [input as User, null];
88
+ * }
89
+ * ```
90
+ *
91
+ * @example Serialization for service-to-service communication
92
+ * ```typescript
93
+ * // Backend: serialize error for API response
94
+ * const err = Err.from('User not found', 'NOT_FOUND');
95
+ * res.status(404).json({ error: err.toJSON() });
96
+ *
97
+ * // Frontend: deserialize error from API response
98
+ * const response = await fetch('/api/user/123');
99
+ * if (!response.ok) {
100
+ * const { error } = await response.json();
101
+ * const err = Err.fromJSON(error);
102
+ * console.log(err.code); // 'NOT_FOUND'
103
+ * }
104
+ *
105
+ * // Public API: omit stack traces
106
+ * res.json({ error: err.toJSON({ stack: false }) });
107
+ * ```
108
+ *
109
+ * @module err
110
+ */
111
+ /**
112
+ * Error code type - typically uppercase snake_case identifiers.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const codes: ErrCode[] = [
117
+ * 'NOT_FOUND',
118
+ * 'VALIDATION_ERROR',
119
+ * 'DB_CONNECTION_FAILED',
120
+ * 'AUTH_EXPIRED',
121
+ * ];
122
+ * ```
123
+ */
124
+ export type ErrCode = string;
125
+ /**
126
+ * Options for creating or modifying an Err instance.
127
+ */
128
+ export interface ErrOptions {
129
+ /** Error code for programmatic error handling */
130
+ code?: ErrCode;
131
+ /** Human-readable error message */
132
+ message?: string;
133
+ /** Additional contextual data */
134
+ metadata?: Record<string, unknown>;
135
+ }
136
+ /**
137
+ * Options for JSON serialization.
138
+ */
139
+ export interface ErrJSONOptions {
140
+ /**
141
+ * Include stack trace in output.
142
+ * Set to `false` for public API responses.
143
+ * @default true
144
+ */
145
+ stack?: boolean;
146
+ /**
147
+ * Include metadata in output.
148
+ * Set to `false` to omit potentially sensitive data.
149
+ * @default true
150
+ */
151
+ metadata?: boolean;
152
+ }
153
+ /**
154
+ * Options for toString() output formatting.
155
+ */
156
+ export interface ToStringOptions {
157
+ /**
158
+ * Include stack trace in output.
159
+ * - `true`: Include full stack trace
160
+ * - `number`: Include only top N frames (default: 3 when number)
161
+ * @default undefined (no stack)
162
+ */
163
+ stack?: boolean | number;
164
+ /**
165
+ * Include timestamp in output (ISO 8601 format).
166
+ * @default false
167
+ */
168
+ date?: boolean;
169
+ /**
170
+ * Include metadata object in output.
171
+ * @default false
172
+ */
173
+ metadata?: boolean;
174
+ /**
175
+ * Maximum depth for cause chain traversal.
176
+ * When exceeded, shows "... (N more causes)".
177
+ * @default undefined (unlimited)
178
+ */
179
+ maxDepth?: number;
180
+ /**
181
+ * Indentation string for nested output.
182
+ * @default " " (two spaces)
183
+ */
184
+ indent?: string;
185
+ }
186
+ /**
187
+ * JSON representation of an Err for serialization.
188
+ */
189
+ export interface ErrJSON {
190
+ message: string;
191
+ kind?: "Err";
192
+ isErr?: boolean;
193
+ code?: ErrCode;
194
+ metadata?: Record<string, unknown>;
195
+ timestamp: string;
196
+ stack?: string;
197
+ cause?: ErrJSON;
198
+ errors: ErrJSON[];
199
+ }
200
+ /**
201
+ * A value-based error type that supports wrapping and aggregation.
202
+ *
203
+ * `Err` is designed to be returned from functions instead of throwing exceptions,
204
+ * following the Go-style error handling pattern. It supports:
205
+ *
206
+ * - **Single errors**: Created via `Err.from()` with optional code and metadata
207
+ * - **Error wrapping**: Adding context to errors as they propagate up the call stack
208
+ * - **Error aggregation**: Collecting multiple errors under a single parent (e.g., validation)
209
+ * - **Serialization**: Convert to/from JSON for service-to-service communication
210
+ *
211
+ * All instances are immutable - methods return new instances rather than mutating.
212
+ *
213
+ * @example Creating errors
214
+ * ```typescript
215
+ * // From string with code (most common)
216
+ * const err1 = Err.from('User not found', 'NOT_FOUND');
217
+ *
218
+ * // From string with full options
219
+ * const err2 = Err.from('Connection timeout', {
220
+ * code: 'TIMEOUT',
221
+ * metadata: { host: 'api.example.com' }
222
+ * });
223
+ *
224
+ * // From native Error (preserves original stack and cause chain)
225
+ * try {
226
+ * riskyOperation();
227
+ * } catch (e) {
228
+ * const err = Err.from(e).withCode('OPERATION_FAILED');
229
+ * return [null, err];
230
+ * }
231
+ *
232
+ * // From unknown (safe for catch blocks)
233
+ * const err3 = Err.from(unknownValue);
234
+ * ```
235
+ *
236
+ * @example Wrapping errors with static method
237
+ * ```typescript
238
+ * try {
239
+ * await db.query(sql);
240
+ * } catch (e) {
241
+ * return [null, Err.wrap('Database query failed', e as Error)];
242
+ * }
243
+ * ```
244
+ *
245
+ * @example Aggregating errors
246
+ * ```typescript
247
+ * let errors = Err.aggregate('Multiple operations failed')
248
+ * .add(Err.from('Database write failed'))
249
+ * .add(Err.from('Cache invalidation failed'))
250
+ * .add('Notification send failed'); // strings are auto-wrapped
251
+ *
252
+ * console.log(errors.count); // 3
253
+ * console.log(errors.flatten()); // Array of all individual errors
254
+ * ```
255
+ */
256
+ export declare class Err {
257
+ /**
258
+ * Discriminator property for type narrowing.
259
+ * Always "Err" for Err instances.
260
+ */
261
+ readonly kind: "Err";
262
+ /**
263
+ * Discriminator property for type narrowing.
264
+ * Always `true` for Err instances.
265
+ *
266
+ * Useful when checking values from external sources (API responses,
267
+ * message queues) where `instanceof` may not work.
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * // Checking unknown values from API
272
+ * const data = await response.json();
273
+ * if (data.error?.isErr) {
274
+ * // Likely an Err-like object
275
+ * }
276
+ *
277
+ * // For type narrowing, prefer Err.isErr()
278
+ * if (Err.isErr(value)) {
279
+ * console.error(value.message);
280
+ * }
281
+ * ```
282
+ */
283
+ readonly isErr: true;
284
+ /** Human-readable error message */
285
+ readonly message: string;
286
+ /** Error code for programmatic handling */
287
+ readonly code?: ErrCode;
288
+ /** Additional contextual data */
289
+ readonly metadata?: Record<string, unknown>;
290
+ /**
291
+ * Timestamp when the error was created (ISO 8601 string).
292
+ *
293
+ * Stored as string for easy serialization and comparison.
294
+ */
295
+ readonly timestamp: string;
296
+ /** The wrapped/caused error (for error chains) */
297
+ private readonly _cause?;
298
+ /** List of aggregated errors */
299
+ private readonly _errors;
300
+ /**
301
+ * Stack trace - either from original Error or captured at creation.
302
+ *
303
+ * When wrapping a native Error, this preserves the original stack
304
+ * for better debugging (points to actual error location).
305
+ */
306
+ private readonly _stack?;
307
+ /**
308
+ * Private constructor - use static factory methods instead.
309
+ * @internal
310
+ */
311
+ private constructor();
312
+ /**
313
+ * Create an Err from a string message with optional code.
314
+ *
315
+ * @param message - Error message
316
+ * @param code - Optional error code
317
+ * @returns New Err instance
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * const err = Err.from('User not found', 'NOT_FOUND');
322
+ * ```
323
+ */
324
+ static from(message: string, code?: ErrCode): Err;
325
+ /**
326
+ * Create an Err from a string message with full options.
327
+ *
328
+ * @param message - Error message
329
+ * @param options - Code and metadata options
330
+ * @returns New Err instance
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * const err = Err.from('Connection timeout', {
335
+ * code: 'TIMEOUT',
336
+ * metadata: { host: 'api.example.com', timeoutMs: 5000 }
337
+ * });
338
+ * ```
339
+ */
340
+ static from(message: string, options: ErrOptions): Err;
341
+ /**
342
+ * Create an Err from a native Error.
343
+ *
344
+ * Preserves the original error's:
345
+ * - Stack trace (as primary stack for debugging)
346
+ * - Cause chain (if `error.cause` is Error or string)
347
+ * - Name (in metadata as `originalName`)
348
+ *
349
+ * @param error - Native Error instance
350
+ * @param options - Optional overrides for message, code, and metadata
351
+ * @returns New Err instance
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * try {
356
+ * JSON.parse(invalidJson);
357
+ * } catch (e) {
358
+ * return Err.from(e as Error, { code: 'PARSE_ERROR' });
359
+ * }
360
+ * ```
361
+ */
362
+ static from(error: Error, options?: ErrOptions): Err;
363
+ /**
364
+ * Create an Err from another Err instance (clone with optional overrides).
365
+ *
366
+ * @param error - Existing Err instance
367
+ * @param options - Optional overrides
368
+ * @returns New Err instance with merged properties
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * const original = Err.from('Original error');
373
+ * const modified = Err.from(original, { code: 'NEW_CODE' });
374
+ * ```
375
+ */
376
+ static from(error: Err, options?: ErrOptions): Err;
377
+ /**
378
+ * Create an Err from an unknown value (safe for catch blocks).
379
+ *
380
+ * Handles any value that might be thrown, including non-Error objects,
381
+ * strings, numbers, null, and undefined.
382
+ *
383
+ * @param error - Any value
384
+ * @param options - Optional code and metadata
385
+ * @returns New Err instance
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * try {
390
+ * await riskyAsyncOperation();
391
+ * } catch (e) {
392
+ * // Safe - handles any thrown value
393
+ * return Err.from(e).wrap('Operation failed');
394
+ * }
395
+ * ```
396
+ */
397
+ static from(error: unknown, options?: ErrOptions): Err;
398
+ /**
399
+ * Static convenience method to wrap an error with a context message.
400
+ *
401
+ * Creates a new Err with the provided message, having the original
402
+ * error as its cause. This is the recommended pattern for catch blocks.
403
+ *
404
+ * @param message - Context message explaining what operation failed
405
+ * @param error - The original error (Err, Error, or string)
406
+ * @param options - Optional code and metadata for the wrapper
407
+ * @returns New Err instance with the original as cause
408
+ *
409
+ * @see {@link Err.prototype.wrap} for the instance method
410
+ *
411
+ * @example Basic usage in catch block
412
+ * ```typescript
413
+ * try {
414
+ * await db.query(sql);
415
+ * } catch (e) {
416
+ * return Err.wrap('Database query failed', e as Error);
417
+ * }
418
+ * ```
419
+ *
420
+ * @example With code and metadata
421
+ * ```typescript
422
+ * try {
423
+ * const user = await fetchUser(id);
424
+ * } catch (e) {
425
+ * return Err.wrap('Failed to fetch user', e as Error, {
426
+ * code: 'USER_FETCH_ERROR',
427
+ * metadata: { userId: id }
428
+ * });
429
+ * }
430
+ * ```
431
+ */
432
+ static wrap(message: string, error: Err | Error | string, options?: ErrOptions): Err;
433
+ /**
434
+ * Create an aggregate error for collecting multiple errors.
435
+ *
436
+ * Useful for validation, batch operations, or any scenario where
437
+ * multiple errors should be collected and reported together.
438
+ *
439
+ * @param message - Parent error message describing the aggregate
440
+ * @param errors - Optional initial list of errors
441
+ * @param options - Optional code and metadata for the aggregate
442
+ * @returns New aggregate Err instance
443
+ *
444
+ * @example Validation
445
+ * ```typescript
446
+ * function validate(data: Input): [Valid, null] | [null, Err] {
447
+ * let errors = Err.aggregate('Validation failed');
448
+ *
449
+ * if (!data.email) errors = errors.add('Email is required');
450
+ * if (!data.name) errors = errors.add('Name is required');
451
+ *
452
+ * if (errors.count > 0) {
453
+ * return [null, errors.withCode('VALIDATION_ERROR')];
454
+ * }
455
+ * return [data as Valid, null];
456
+ * }
457
+ * ```
458
+ *
459
+ * @example Batch operations
460
+ * ```typescript
461
+ * async function processAll(items: Item[]): [null, Err] | [void, null] {
462
+ * let errors = Err.aggregate('Batch processing failed');
463
+ *
464
+ * for (const item of items) {
465
+ * const [, err] = await processItem(item);
466
+ * if (err) {
467
+ * errors = errors.add(err.withMetadata({ itemId: item.id }));
468
+ * }
469
+ * }
470
+ *
471
+ * if (errors.count > 0) return [null, errors];
472
+ * return [undefined, null];
473
+ * }
474
+ * ```
475
+ */
476
+ static aggregate(message: string, errors?: Array<Err | Error | string>, options?: ErrOptions): Err;
477
+ /**
478
+ * Deserialize an Err from JSON representation.
479
+ *
480
+ * Reconstructs an Err instance from its JSON form, including
481
+ * cause chains and aggregated errors. Validates the input structure.
482
+ *
483
+ * @param json - JSON object matching ErrJSON structure
484
+ * @returns Reconstructed Err instance
485
+ * @throws Error if json is invalid or missing required fields
486
+ *
487
+ * @see {@link toJSON} for serializing an Err to JSON
488
+ *
489
+ * @example API response handling
490
+ * ```typescript
491
+ * const response = await fetch('/api/users/123');
492
+ * if (!response.ok) {
493
+ * const body = await response.json();
494
+ * if (body.error) {
495
+ * const err = Err.fromJSON(body.error);
496
+ * if (err.hasCode('NOT_FOUND')) {
497
+ * return showNotFound();
498
+ * }
499
+ * return showError(err);
500
+ * }
501
+ * }
502
+ * ```
503
+ *
504
+ * @example Message queue processing
505
+ * ```typescript
506
+ * queue.on('error', (message) => {
507
+ * const err = Err.fromJSON(message.payload);
508
+ * logger.error('Task failed', { error: err.toJSON() });
509
+ * });
510
+ * ```
511
+ */
512
+ static fromJSON(json: unknown): Err;
513
+ /**
514
+ * Type guard to check if a value is an Err instance.
515
+ *
516
+ * Useful for checking values from external sources where
517
+ * `instanceof` may not work (different realms, serialization).
518
+ *
519
+ * @param value - Any value to check
520
+ * @returns `true` if value is an Err instance
521
+ *
522
+ * @example Checking external/unknown values
523
+ * ```typescript
524
+ * // Useful for values from external sources
525
+ * function handleApiResponse(data: unknown): void {
526
+ * if (Err.isErr(data)) {
527
+ * console.error('Received error:', data.message);
528
+ * return;
529
+ * }
530
+ * // Process data...
531
+ * }
532
+ * ```
533
+ *
534
+ * @example With tuple pattern (preferred for known types)
535
+ * ```typescript
536
+ * function getUser(id: string): [User, null] | [null, Err] {
537
+ * // ...
538
+ * }
539
+ *
540
+ * const [user, err] = getUser('123');
541
+ * if (err) {
542
+ * console.error(err.message);
543
+ * return;
544
+ * }
545
+ * console.log(user.name);
546
+ * ```
547
+ */
548
+ static isErr(value: unknown): value is Err;
549
+ /**
550
+ * Wrap this error with additional context.
551
+ *
552
+ * Creates a new error that has this error as its cause. The original error
553
+ * is preserved and accessible via `unwrap()` or `chain()`.
554
+ *
555
+ * ## Stack Trace Behavior
556
+ *
557
+ * The new wrapper captures a fresh stack trace pointing to where `wrap()`
558
+ * was called. This is intentional - it shows the propagation path. The
559
+ * original error's stack is preserved and accessible via:
560
+ * - `err.unwrap()?.stack` - immediate cause's stack
561
+ * - `err.root.stack` - original error's stack
562
+ *
563
+ * @param context - Either a message string or full options object
564
+ * @returns New Err instance with this error as cause
565
+ *
566
+ * @see {@link Err.wrap} for the static version (useful in catch blocks)
567
+ *
568
+ * @example Simple wrapping
569
+ * ```typescript
570
+ * const dbErr = queryDatabase();
571
+ * if (Err.isErr(dbErr)) {
572
+ * return dbErr.wrap('Failed to fetch user');
573
+ * }
574
+ * ```
575
+ *
576
+ * @example Wrapping with full options
577
+ * ```typescript
578
+ * return originalErr.wrap({
579
+ * message: 'Service unavailable',
580
+ * code: 'SERVICE_ERROR',
581
+ * metadata: { service: 'user-service', retryAfter: 30 }
582
+ * });
583
+ * ```
584
+ *
585
+ * @example Accessing original stack
586
+ * ```typescript
587
+ * const wrapped = original.wrap('Context 1').wrap('Context 2');
588
+ * console.log(wrapped.stack); // Points to second wrap() call
589
+ * console.log(wrapped.root.stack); // Points to original error location
590
+ * ```
591
+ */
592
+ wrap(context: string | ErrOptions): Err;
593
+ /**
594
+ * Create a new Err with a different or added error code.
595
+ *
596
+ * Preserves the original stack trace and timestamp.
597
+ *
598
+ * @param code - The error code to set
599
+ * @returns New Err instance with the specified code
600
+ *
601
+ * @example
602
+ * ```typescript
603
+ * const err = Err.from('Record not found').withCode('NOT_FOUND');
604
+ *
605
+ * if (err.code === 'NOT_FOUND') {
606
+ * return res.status(404).json(err.toJSON());
607
+ * }
608
+ * ```
609
+ */
610
+ withCode(code: ErrCode): Err;
611
+ /**
612
+ * Create a new Err with additional metadata.
613
+ *
614
+ * New metadata is merged with existing metadata. Preserves the original
615
+ * stack trace and timestamp.
616
+ *
617
+ * @param metadata - Key-value pairs to add to metadata
618
+ * @returns New Err instance with merged metadata
619
+ *
620
+ * @example
621
+ * ```typescript
622
+ * const err = Err.from('Request failed')
623
+ * .withMetadata({ url: '/api/users' })
624
+ * .withMetadata({ statusCode: 500, retryable: true });
625
+ *
626
+ * console.log(err.metadata);
627
+ * // { url: '/api/users', statusCode: 500, retryable: true }
628
+ * ```
629
+ */
630
+ withMetadata(metadata: Record<string, unknown>): Err;
631
+ /**
632
+ * Add an error to this aggregate.
633
+ *
634
+ * Returns a new Err with the error added to the list (immutable).
635
+ * If this is not an aggregate error, it will be treated as one with
636
+ * the added error as the first child.
637
+ *
638
+ * @param error - Error to add (Err, Error, or string)
639
+ * @returns New Err instance with the error added
640
+ *
641
+ * @example
642
+ * ```typescript
643
+ * let errors = Err.aggregate('Form validation failed');
644
+ *
645
+ * if (!email) {
646
+ * errors = errors.add('Email is required');
647
+ * }
648
+ * if (!password) {
649
+ * errors = errors.add(Err.from('Password is required').withCode('MISSING_PASSWORD'));
650
+ * }
651
+ * ```
652
+ */
653
+ add(error: Err | Error | string): Err;
654
+ /**
655
+ * Add multiple errors to this aggregate at once.
656
+ *
657
+ * Returns a new Err with all errors added (immutable).
658
+ *
659
+ * @param errors - Array of errors to add
660
+ * @returns New Err instance with all errors added
661
+ *
662
+ * @example
663
+ * ```typescript
664
+ * const validationErrors = [
665
+ * 'Name too short',
666
+ * Err.from('Invalid email format').withCode('INVALID_EMAIL'),
667
+ * new Error('Age must be positive'),
668
+ * ];
669
+ *
670
+ * const aggregate = Err.aggregate('Validation failed').addAll(validationErrors);
671
+ * ```
672
+ */
673
+ addAll(errors: Array<Err | Error | string>): Err;
674
+ /**
675
+ * Whether this error is an aggregate containing multiple errors.
676
+ *
677
+ * @example
678
+ * ```typescript
679
+ * const single = Err.from('Single error');
680
+ * const multi = Err.aggregate('Multiple').add('One').add('Two');
681
+ *
682
+ * console.log(single.isAggregate); // false
683
+ * console.log(multi.isAggregate); // true
684
+ * ```
685
+ */
686
+ get isAggregate(): boolean;
687
+ /**
688
+ * Total count of errors (including nested aggregates).
689
+ *
690
+ * For single errors, returns 1.
691
+ * For aggregates, recursively counts all child errors.
692
+ *
693
+ * @example
694
+ * ```typescript
695
+ * const single = Err.from('One error');
696
+ * console.log(single.count); // 1
697
+ *
698
+ * const nested = Err.aggregate('Parent')
699
+ * .add('Error 1')
700
+ * .add(Err.aggregate('Child').add('Error 2').add('Error 3'));
701
+ *
702
+ * console.log(nested.count); // 3
703
+ * ```
704
+ */
705
+ get count(): number;
706
+ /**
707
+ * Direct child errors (for aggregates).
708
+ *
709
+ * Returns an empty array for non-aggregate errors.
710
+ *
711
+ * @example
712
+ * ```typescript
713
+ * const aggregate = Err.aggregate('Batch failed')
714
+ * .add('Task 1 failed')
715
+ * .add('Task 2 failed');
716
+ *
717
+ * for (const err of aggregate.errors) {
718
+ * console.log(err.message);
719
+ * }
720
+ * // "Task 1 failed"
721
+ * // "Task 2 failed"
722
+ * ```
723
+ */
724
+ get errors(): ReadonlyArray<Err>;
725
+ /**
726
+ * The root/original error in a wrapped error chain.
727
+ *
728
+ * Follows the cause chain to find the deepest error.
729
+ * Returns `this` if there is no cause.
730
+ *
731
+ * @example
732
+ * ```typescript
733
+ * const root = Err.from('Original error');
734
+ * const wrapped = root
735
+ * .wrap('Added context')
736
+ * .wrap('More context');
737
+ *
738
+ * console.log(wrapped.message); // "More context"
739
+ * console.log(wrapped.root.message); // "Original error"
740
+ * console.log(wrapped.root.stack); // Stack pointing to original error
741
+ * ```
742
+ */
743
+ get root(): Err;
744
+ /**
745
+ * Get the directly wrapped error (one level up).
746
+ *
747
+ * Returns `undefined` if this error has no cause.
748
+ *
749
+ * @returns The wrapped Err or undefined
750
+ *
751
+ * @example
752
+ * ```typescript
753
+ * const inner = Err.from('DB connection failed');
754
+ * const outer = inner.wrap('Could not save user');
755
+ *
756
+ * const unwrapped = outer.unwrap();
757
+ * console.log(unwrapped?.message); // "DB connection failed"
758
+ * console.log(inner.unwrap()); // undefined
759
+ * ```
760
+ */
761
+ unwrap(): Err | undefined;
762
+ /**
763
+ * Get the full chain of wrapped errors from root to current.
764
+ *
765
+ * The first element is the root/original error, the last is `this`.
766
+ *
767
+ * @returns Array of Err instances in causal order
768
+ *
769
+ * @remarks
770
+ * Time complexity: O(n) where n is the depth of the cause chain.
771
+ *
772
+ * @example
773
+ * ```typescript
774
+ * const chain = Err.from('Network timeout')
775
+ * .wrap('API request failed')
776
+ * .wrap('Could not refresh token')
777
+ * .wrap('Authentication failed')
778
+ * .chain();
779
+ *
780
+ * console.log(chain.map(e => e.message));
781
+ * // [
782
+ * // "Network timeout",
783
+ * // "API request failed",
784
+ * // "Could not refresh token",
785
+ * // "Authentication failed"
786
+ * // ]
787
+ * ```
788
+ */
789
+ chain(): Err[];
790
+ /**
791
+ * Flatten all errors into a single array.
792
+ *
793
+ * For aggregates, recursively collects all leaf errors.
794
+ * For single errors, returns an array containing just this error.
795
+ *
796
+ * @returns Flattened array of all individual errors
797
+ *
798
+ * @remarks
799
+ * Time complexity: O(n) where n is the total number of errors in all nested aggregates.
800
+ * Recursively traverses the error tree.
801
+ *
802
+ * @example
803
+ * ```typescript
804
+ * const nested = Err.aggregate('All errors')
805
+ * .add('Error A')
806
+ * .add(Err.aggregate('Group B')
807
+ * .add('Error B1')
808
+ * .add('Error B2'))
809
+ * .add('Error C');
810
+ *
811
+ * const flat = nested.flatten();
812
+ * console.log(flat.map(e => e.message));
813
+ * // ["Error A", "Error B1", "Error B2", "Error C"]
814
+ * ```
815
+ */
816
+ flatten(): Err[];
817
+ /**
818
+ * Check if this error or any error in its chain/aggregate has a specific code.
819
+ *
820
+ * Searches the cause chain and all aggregated errors.
821
+ *
822
+ * @param code - The error code to search for
823
+ * @returns `true` if the code is found anywhere in the error tree
824
+ *
825
+ * @example
826
+ * ```typescript
827
+ * const err = Err.from('DB error', 'DB_ERROR')
828
+ * .wrap('Repository failed')
829
+ * .wrap('Service unavailable');
830
+ *
831
+ * console.log(err.hasCode('DB_ERROR')); // true
832
+ * console.log(err.hasCode('NETWORK_ERROR')); // false
833
+ * ```
834
+ */
835
+ hasCode(code: ErrCode): boolean;
836
+ /**
837
+ * Check if this error or any error in its chain/aggregate has a code
838
+ * matching the given prefix with boundary awareness.
839
+ *
840
+ * This enables hierarchical error code patterns like `AUTH:TOKEN:EXPIRED`
841
+ * where libraries define base codes and consumers extend with subcodes.
842
+ *
843
+ * Matches if:
844
+ * - Code equals prefix exactly (e.g., `"AUTH"` matches `"AUTH"`)
845
+ * - Code starts with prefix + boundary (e.g., `"AUTH"` matches `"AUTH:EXPIRED"`)
846
+ *
847
+ * Does NOT match partial strings (e.g., `"AUTH"` does NOT match `"AUTHORIZATION"`).
848
+ *
849
+ * @param prefix - The code prefix to search for
850
+ * @param boundary - Separator character/string between code segments (default: ":")
851
+ * @returns `true` if a matching code is found anywhere in the error tree
852
+ *
853
+ * @example Basic hierarchical codes
854
+ * ```typescript
855
+ * const err = Err.from('Token expired', { code: 'AUTH:TOKEN:EXPIRED' });
856
+ *
857
+ * err.hasCodePrefix('AUTH'); // true (matches AUTH:*)
858
+ * err.hasCodePrefix('AUTH:TOKEN'); // true (matches AUTH:TOKEN:*)
859
+ * err.hasCodePrefix('AUTHORIZATION'); // false (no boundary match)
860
+ * ```
861
+ *
862
+ * @example Custom boundary
863
+ * ```typescript
864
+ * const err = Err.from('Not found', { code: 'HTTP.404.NOT_FOUND' });
865
+ *
866
+ * err.hasCodePrefix('HTTP', '.'); // true
867
+ * err.hasCodePrefix('HTTP.404', '.'); // true
868
+ * err.hasCodePrefix('HTTP', ':'); // false (wrong boundary)
869
+ * ```
870
+ *
871
+ * @example Search in error tree
872
+ * ```typescript
873
+ * const err = Err.from('DB error', { code: 'DB:CONNECTION' })
874
+ * .wrap('Service failed', { code: 'SERVICE:UNAVAILABLE' });
875
+ *
876
+ * err.hasCodePrefix('DB'); // true (found in cause)
877
+ * err.hasCodePrefix('SERVICE'); // true (found in current)
878
+ * ```
879
+ */
880
+ hasCodePrefix(prefix: string, boundary?: string): boolean;
881
+ /**
882
+ * Find the first error matching a predicate.
883
+ *
884
+ * Searches this error, its cause chain, and all aggregated errors.
885
+ *
886
+ * @param predicate - Function to test each error
887
+ * @returns The first matching Err or undefined
888
+ *
889
+ * @example
890
+ * ```typescript
891
+ * const err = Err.aggregate('Multiple failures')
892
+ * .add(Err.from('Not found', 'NOT_FOUND'))
893
+ * .add(Err.from('Timeout', 'TIMEOUT'));
894
+ *
895
+ * const timeout = err.find(e => e.code === 'TIMEOUT');
896
+ * console.log(timeout?.message); // "Timeout"
897
+ * ```
898
+ */
899
+ find(predicate: (e: Err) => boolean): Err | undefined;
900
+ /**
901
+ * Find all errors matching a predicate.
902
+ *
903
+ * Searches this error, its cause chain, and all aggregated errors.
904
+ *
905
+ * @param predicate - Function to test each error
906
+ * @returns Array of all matching Err instances
907
+ *
908
+ * @example
909
+ * ```typescript
910
+ * const err = Err.aggregate('Validation failed')
911
+ * .add(Err.from('Name required', 'REQUIRED'))
912
+ * .add(Err.from('Invalid email', 'INVALID'))
913
+ * .add(Err.from('Age required', 'REQUIRED'));
914
+ *
915
+ * const required = err.filter(e => e.code === 'REQUIRED');
916
+ * console.log(required.length); // 2
917
+ * ```
918
+ */
919
+ filter(predicate: (e: Err) => boolean): Err[];
920
+ /**
921
+ * Convert to a JSON-serializable object.
922
+ *
923
+ * Useful for logging, API responses, and serialization.
924
+ * Use options to control what's included (e.g., omit stack for public APIs).
925
+ *
926
+ * @param options - Control what fields are included
927
+ * @returns Plain object representation
928
+ *
929
+ * @see {@link fromJSON} for deserializing an Err from JSON
930
+ *
931
+ * @example Full serialization (default)
932
+ * ```typescript
933
+ * const err = Err.from('Not found', {
934
+ * code: 'NOT_FOUND',
935
+ * metadata: { userId: '123' }
936
+ * });
937
+ *
938
+ * console.log(JSON.stringify(err.toJSON(), null, 2));
939
+ * // {
940
+ * // "message": "Not found",
941
+ * // "code": "NOT_FOUND",
942
+ * // "metadata": { "userId": "123" },
943
+ * // "timestamp": "2024-01-15T10:30:00.000Z",
944
+ * // "stack": "Error: ...",
945
+ * // "errors": []
946
+ * // }
947
+ * ```
948
+ *
949
+ * @example Public API response (no stack)
950
+ * ```typescript
951
+ * app.get('/user/:id', (req, res) => {
952
+ * const result = getUser(req.params.id);
953
+ * if (Err.isErr(result)) {
954
+ * const status = result.code === 'NOT_FOUND' ? 404 : 500;
955
+ * return res.status(status).json({
956
+ * error: result.toJSON({ stack: false })
957
+ * });
958
+ * }
959
+ * res.json(result);
960
+ * });
961
+ * ```
962
+ *
963
+ * @example Minimal payload
964
+ * ```typescript
965
+ * err.toJSON({ stack: false, metadata: false });
966
+ * // Only includes: message, code, timestamp, cause, errors
967
+ * ```
968
+ */
969
+ toJSON(options?: ErrJSONOptions): ErrJSON;
970
+ /**
971
+ * Recursive code search helper.
972
+ * @param matcher
973
+ * @private
974
+ */
975
+ private _searchCode;
976
+ /**
977
+ * Pattern to identify internal Err class frames to filter out.
978
+ * Matches frames from err.ts file (handles both "at Err.from" and "at from" patterns).
979
+ * @internal
980
+ */
981
+ private static readonly INTERNAL_FRAME_PATTERN;
982
+ /**
983
+ * Filter out internal Err class frames from stack trace.
984
+ * This makes stack traces more useful by starting at user code.
985
+ *
986
+ * @param stack - Raw stack trace string
987
+ * @returns Stack with internal frames removed
988
+ * @internal
989
+ */
990
+ private static _filterInternalFrames;
991
+ /**
992
+ * Parse and extract stack frames from the stack trace.
993
+ *
994
+ * @param limit - Maximum number of frames to return (undefined = all)
995
+ * @returns Array of stack frame strings
996
+ * @internal
997
+ */
998
+ private _getStackFrames;
999
+ /**
1000
+ * Count remaining causes in the chain from a given error.
1001
+ *
1002
+ * @param err - Starting error
1003
+ * @returns Number of causes remaining
1004
+ * @internal
1005
+ */
1006
+ private _countRemainingCauses;
1007
+ /**
1008
+ * Convert to a formatted string for logging/display.
1009
+ *
1010
+ * Includes cause chain and aggregated errors with indentation.
1011
+ * When called with options, can include additional details like
1012
+ * stack traces, timestamps, and metadata.
1013
+ *
1014
+ * @param options - Formatting options (optional)
1015
+ * @returns Formatted error string
1016
+ *
1017
+ * @example Basic usage (no options - backward compatible)
1018
+ * ```typescript
1019
+ * const err = Err.from('DB error')
1020
+ * .wrap('Repository failed')
1021
+ * .wrap('Service unavailable');
1022
+ *
1023
+ * console.log(err.toString());
1024
+ * // [ERROR] Service unavailable
1025
+ * // Caused by: [ERROR] Repository failed
1026
+ * // Caused by: [ERROR] DB error
1027
+ * ```
1028
+ *
1029
+ * @example With options
1030
+ * ```typescript
1031
+ * const err = Err.from('Connection failed', {
1032
+ * code: 'DB:CONNECTION',
1033
+ * metadata: { host: 'localhost', port: 5432 }
1034
+ * });
1035
+ *
1036
+ * console.log(err.toString({ date: true, metadata: true, stack: 3 }));
1037
+ * // [2024-01-15T10:30:00.000Z] [DB:CONNECTION] Connection failed
1038
+ * // metadata: {"host":"localhost","port":5432}
1039
+ * // stack:
1040
+ * // at Database.connect (src/db.ts:45)
1041
+ * // at Repository.init (src/repo.ts:23)
1042
+ * // at Service.start (src/service.ts:12)
1043
+ * ```
1044
+ *
1045
+ * @example Aggregate
1046
+ * ```typescript
1047
+ * const err = Err.aggregate('Validation failed', [], { code: 'VALIDATION' })
1048
+ * .add('Name required')
1049
+ * .add('Email invalid');
1050
+ *
1051
+ * console.log(err.toString());
1052
+ * // [VALIDATION] Validation failed
1053
+ * // Errors (2):
1054
+ * // - [ERROR] Name required
1055
+ * // - [ERROR] Email invalid
1056
+ * ```
1057
+ *
1058
+ * @example With maxDepth limit
1059
+ * ```typescript
1060
+ * const deep = Err.from('Root')
1061
+ * .wrap('Level 1')
1062
+ * .wrap('Level 2')
1063
+ * .wrap('Level 3');
1064
+ *
1065
+ * console.log(deep.toString({ maxDepth: 2 }));
1066
+ * // [ERROR] Level 3
1067
+ * // Caused by: [ERROR] Level 2
1068
+ * // ... (2 more causes)
1069
+ * ```
1070
+ */
1071
+ toString(options?: ToStringOptions): string;
1072
+ /**
1073
+ * Internal toString implementation with depth tracking.
1074
+ * @internal
1075
+ */
1076
+ private _toStringInternal;
1077
+ /**
1078
+ * Convert to a native Error for interop with throw-based APIs.
1079
+ *
1080
+ * Creates an Error with:
1081
+ * - `message`: This error's message
1082
+ * - `name`: This error's code (or "Err")
1083
+ * - `stack`: This error's original stack trace
1084
+ * - `cause`: Converted cause chain (native Error)
1085
+ *
1086
+ * Note: Metadata is not included on the native Error.
1087
+ *
1088
+ * @returns Native Error instance
1089
+ *
1090
+ * @example
1091
+ * ```typescript
1092
+ * const err = Err.from('Something failed', 'MY_ERROR');
1093
+ *
1094
+ * // If you need to throw for some API
1095
+ * throw err.toError();
1096
+ *
1097
+ * // The thrown error will have:
1098
+ * // - error.message === "Something failed"
1099
+ * // - error.name === "MY_ERROR"
1100
+ * // - error.stack === (original stack trace)
1101
+ * // - error.cause === (if wrapped)
1102
+ * ```
1103
+ */
1104
+ toError(): Error;
1105
+ /**
1106
+ * Get the captured stack trace.
1107
+ *
1108
+ * For errors created from native Errors, this is the original stack.
1109
+ * For errors created via `Err.from(string)`, this is the stack at creation.
1110
+ * For wrapped errors, use `.root.stack` to get the original location.
1111
+ *
1112
+ * @returns Stack trace string or undefined
1113
+ */
1114
+ get stack(): string | undefined;
1115
+ }
1116
+ //# sourceMappingURL=err.d.ts.map