@outfitter/contracts 0.1.0-rc.1
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 +50 -0
- package/dist/actions.d.ts +388 -0
- package/dist/actions.js +32 -0
- package/dist/adapters.d.ts +182 -0
- package/dist/adapters.js +1 -0
- package/dist/assert/index.d.ts +63 -0
- package/dist/assert/index.js +36 -0
- package/dist/capabilities.d.ts +19 -0
- package/dist/capabilities.js +66 -0
- package/dist/context.d.ts +125 -0
- package/dist/context.js +36 -0
- package/dist/envelope.d.ts +328 -0
- package/dist/envelope.js +56 -0
- package/dist/errors.d.ts +261 -0
- package/dist/errors.js +171 -0
- package/dist/handler.d.ts +318 -0
- package/dist/handler.js +1 -0
- package/dist/index.d.ts +1354 -0
- package/dist/index.js +137 -0
- package/dist/recovery.d.ts +150 -0
- package/dist/recovery.js +56 -0
- package/dist/redactor.d.ts +100 -0
- package/dist/redactor.js +111 -0
- package/dist/resilience.d.ts +299 -0
- package/dist/resilience.js +82 -0
- package/dist/result/index.d.ts +103 -0
- package/dist/result/index.js +13 -0
- package/dist/result/utilities.d.ts +103 -0
- package/dist/result/utilities.js +31 -0
- package/dist/serialization.d.ts +313 -0
- package/dist/serialization.js +270 -0
- package/dist/validation.d.ts +59 -0
- package/dist/validation.js +42 -0
- package/package.json +143 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
import { TaggedErrorClass } from "better-result";
|
|
2
|
+
/**
|
|
3
|
+
* Error categories for classification, exit codes, and HTTP status mapping.
|
|
4
|
+
*
|
|
5
|
+
* Used for:
|
|
6
|
+
* - CLI exit code determination
|
|
7
|
+
* - HTTP status code mapping
|
|
8
|
+
* - Error grouping in logs and metrics
|
|
9
|
+
* - Client retry decisions (transient vs permanent)
|
|
10
|
+
*/
|
|
11
|
+
type ErrorCategory = "validation" | "not_found" | "conflict" | "permission" | "timeout" | "rate_limit" | "network" | "internal" | "auth" | "cancelled";
|
|
12
|
+
/**
|
|
13
|
+
* Maps error category to CLI exit code.
|
|
14
|
+
* Non-zero exit indicates error; specific values for script automation.
|
|
15
|
+
*/
|
|
16
|
+
declare const exitCodeMap: Record<ErrorCategory, number>;
|
|
17
|
+
/**
|
|
18
|
+
* Maps error category to HTTP status code.
|
|
19
|
+
* Used by MCP servers and API responses.
|
|
20
|
+
*/
|
|
21
|
+
declare const statusCodeMap: Record<ErrorCategory, number>;
|
|
22
|
+
/**
|
|
23
|
+
* Serialized error format for JSON transport.
|
|
24
|
+
*/
|
|
25
|
+
interface SerializedError {
|
|
26
|
+
_tag: string;
|
|
27
|
+
category: ErrorCategory;
|
|
28
|
+
message: string;
|
|
29
|
+
context?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Base interface for OutfitterError properties.
|
|
33
|
+
* All concrete error classes must include these fields.
|
|
34
|
+
*/
|
|
35
|
+
interface KitErrorProps {
|
|
36
|
+
message: string;
|
|
37
|
+
category: ErrorCategory;
|
|
38
|
+
context?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get CLI exit code for an error category.
|
|
42
|
+
*/
|
|
43
|
+
declare function getExitCode(category: ErrorCategory): number;
|
|
44
|
+
/**
|
|
45
|
+
* Get HTTP status code for an error category.
|
|
46
|
+
*/
|
|
47
|
+
declare function getStatusCode(category: ErrorCategory): number;
|
|
48
|
+
declare const ValidationErrorBase: TaggedErrorClass<"ValidationError", {
|
|
49
|
+
message: string;
|
|
50
|
+
field?: string;
|
|
51
|
+
}>;
|
|
52
|
+
declare const AssertionErrorBase: TaggedErrorClass<"AssertionError", {
|
|
53
|
+
message: string;
|
|
54
|
+
}>;
|
|
55
|
+
declare const NotFoundErrorBase: TaggedErrorClass<"NotFoundError", {
|
|
56
|
+
message: string;
|
|
57
|
+
resourceType: string;
|
|
58
|
+
resourceId: string;
|
|
59
|
+
}>;
|
|
60
|
+
declare const ConflictErrorBase: TaggedErrorClass<"ConflictError", {
|
|
61
|
+
message: string;
|
|
62
|
+
context?: Record<string, unknown>;
|
|
63
|
+
}>;
|
|
64
|
+
declare const PermissionErrorBase: TaggedErrorClass<"PermissionError", {
|
|
65
|
+
message: string;
|
|
66
|
+
context?: Record<string, unknown>;
|
|
67
|
+
}>;
|
|
68
|
+
declare const TimeoutErrorBase: TaggedErrorClass<"TimeoutError", {
|
|
69
|
+
message: string;
|
|
70
|
+
operation: string;
|
|
71
|
+
timeoutMs: number;
|
|
72
|
+
}>;
|
|
73
|
+
declare const RateLimitErrorBase: TaggedErrorClass<"RateLimitError", {
|
|
74
|
+
message: string;
|
|
75
|
+
retryAfterSeconds?: number;
|
|
76
|
+
}>;
|
|
77
|
+
declare const NetworkErrorBase: TaggedErrorClass<"NetworkError", {
|
|
78
|
+
message: string;
|
|
79
|
+
context?: Record<string, unknown>;
|
|
80
|
+
}>;
|
|
81
|
+
declare const InternalErrorBase: TaggedErrorClass<"InternalError", {
|
|
82
|
+
message: string;
|
|
83
|
+
context?: Record<string, unknown>;
|
|
84
|
+
}>;
|
|
85
|
+
declare const AuthErrorBase: TaggedErrorClass<"AuthError", {
|
|
86
|
+
message: string;
|
|
87
|
+
reason?: "missing" | "invalid" | "expired";
|
|
88
|
+
}>;
|
|
89
|
+
declare const CancelledErrorBase: TaggedErrorClass<"CancelledError", {
|
|
90
|
+
message: string;
|
|
91
|
+
}>;
|
|
92
|
+
/**
|
|
93
|
+
* Input validation failed.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* new ValidationError({ message: "Email format invalid", field: "email" });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare class ValidationError extends ValidationErrorBase {
|
|
101
|
+
readonly category: "validation";
|
|
102
|
+
exitCode(): number;
|
|
103
|
+
statusCode(): number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Assertion failed (invariant violation).
|
|
107
|
+
*
|
|
108
|
+
* Used by assertion utilities that return Result types instead of throwing.
|
|
109
|
+
* AssertionError indicates a programming bug — an invariant that should
|
|
110
|
+
* never be violated was broken. These are internal errors, not user input
|
|
111
|
+
* validation failures.
|
|
112
|
+
*
|
|
113
|
+
* **Category rationale**: Uses `internal` (not `validation`) because:
|
|
114
|
+
* - Assertions check **invariants** (programmer assumptions), not user input
|
|
115
|
+
* - A failed assertion means "this should be impossible if the code is correct"
|
|
116
|
+
* - User-facing validation uses {@link ValidationError} with helpful field info
|
|
117
|
+
* - HTTP 500 is correct: this is a server bug, not a client mistake
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // In domain logic after validation has passed
|
|
122
|
+
* const result = assertDefined(cachedValue, "Cache should always have value after init");
|
|
123
|
+
* if (result.isErr()) {
|
|
124
|
+
* return result; // Propagate as internal error
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @see ValidationError - For user input validation failures (HTTP 400)
|
|
129
|
+
*/
|
|
130
|
+
declare class AssertionError extends AssertionErrorBase {
|
|
131
|
+
readonly category: "internal";
|
|
132
|
+
exitCode(): number;
|
|
133
|
+
statusCode(): number;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Requested resource not found.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* new NotFoundError({ message: "note not found: abc123", resourceType: "note", resourceId: "abc123" });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare class NotFoundError extends NotFoundErrorBase {
|
|
144
|
+
readonly category: "not_found";
|
|
145
|
+
exitCode(): number;
|
|
146
|
+
statusCode(): number;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* State conflict (optimistic locking, concurrent modification).
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* new ConflictError({ message: "Resource was modified by another process" });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare class ConflictError extends ConflictErrorBase {
|
|
157
|
+
readonly category: "conflict";
|
|
158
|
+
exitCode(): number;
|
|
159
|
+
statusCode(): number;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Authorization denied.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* new PermissionError({ message: "Cannot delete read-only resource" });
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
declare class PermissionError extends PermissionErrorBase {
|
|
170
|
+
readonly category: "permission";
|
|
171
|
+
exitCode(): number;
|
|
172
|
+
statusCode(): number;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Operation timed out.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* new TimeoutError({ message: "Database query timed out after 5000ms", operation: "Database query", timeoutMs: 5000 });
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare class TimeoutError extends TimeoutErrorBase {
|
|
183
|
+
readonly category: "timeout";
|
|
184
|
+
exitCode(): number;
|
|
185
|
+
statusCode(): number;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Rate limit exceeded.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* new RateLimitError({ message: "Rate limit exceeded, retry after 60s", retryAfterSeconds: 60 });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare class RateLimitError extends RateLimitErrorBase {
|
|
196
|
+
readonly category: "rate_limit";
|
|
197
|
+
exitCode(): number;
|
|
198
|
+
statusCode(): number;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Network/transport failure.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* new NetworkError({ message: "Connection refused to api.example.com" });
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare class NetworkError extends NetworkErrorBase {
|
|
209
|
+
readonly category: "network";
|
|
210
|
+
exitCode(): number;
|
|
211
|
+
statusCode(): number;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Unexpected internal error.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* new InternalError({ message: "Unexpected state in processor" });
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
declare class InternalError extends InternalErrorBase {
|
|
222
|
+
readonly category: "internal";
|
|
223
|
+
exitCode(): number;
|
|
224
|
+
statusCode(): number;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Authentication failed (missing or invalid credentials).
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* new AuthError({ message: "Invalid API key", reason: "invalid" });
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
declare class AuthError extends AuthErrorBase {
|
|
235
|
+
readonly category: "auth";
|
|
236
|
+
exitCode(): number;
|
|
237
|
+
statusCode(): number;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Operation cancelled by user or signal.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* new CancelledError({ message: "Operation cancelled by user" });
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
declare class CancelledError extends CancelledErrorBase {
|
|
248
|
+
readonly category: "cancelled";
|
|
249
|
+
exitCode(): number;
|
|
250
|
+
statusCode(): number;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Union type of all concrete error class instances.
|
|
254
|
+
*/
|
|
255
|
+
type AnyKitError = InstanceType<typeof ValidationError> | InstanceType<typeof AssertionError> | InstanceType<typeof NotFoundError> | InstanceType<typeof ConflictError> | InstanceType<typeof PermissionError> | InstanceType<typeof TimeoutError> | InstanceType<typeof RateLimitError> | InstanceType<typeof NetworkError> | InstanceType<typeof InternalError> | InstanceType<typeof AuthError> | InstanceType<typeof CancelledError>;
|
|
256
|
+
/**
|
|
257
|
+
* Type alias for backwards compatibility with handler signatures.
|
|
258
|
+
* Use AnyKitError for the union type.
|
|
259
|
+
*/
|
|
260
|
+
type OutfitterError = AnyKitError;
|
|
261
|
+
import { Result } from "better-result";
|
|
262
|
+
import { z } from "zod";
|
|
263
|
+
/**
|
|
264
|
+
* Create a validator function from a Zod schema.
|
|
265
|
+
*
|
|
266
|
+
* @typeParam T - The validated output type
|
|
267
|
+
* @param schema - Zod schema to validate against
|
|
268
|
+
* @returns A function that validates input and returns Result
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const NoteSchema = z.object({
|
|
273
|
+
* id: z.string().uuid(),
|
|
274
|
+
* title: z.string().min(1),
|
|
275
|
+
* });
|
|
276
|
+
*
|
|
277
|
+
* const validateNote = createValidator(NoteSchema);
|
|
278
|
+
* const result = validateNote(input); // Result<Note, ValidationError>
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
declare function createValidator<T>(schema: z.ZodType<T>): (input: unknown) => Result<T, ValidationError>;
|
|
282
|
+
/**
|
|
283
|
+
* Validate input against a Zod schema.
|
|
284
|
+
*
|
|
285
|
+
* Standardized wrapper for Zod schemas that returns Result instead of throwing.
|
|
286
|
+
*
|
|
287
|
+
* @typeParam T - The validated output type
|
|
288
|
+
* @param schema - Zod schema to validate against
|
|
289
|
+
* @param input - Unknown input to validate
|
|
290
|
+
* @returns Result with validated data or ValidationError
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* const result = validateInput(NoteSchema, userInput);
|
|
295
|
+
* if (result.isErr()) {
|
|
296
|
+
* console.error(result.unwrapErr().message);
|
|
297
|
+
* }
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare function validateInput<T>(schema: z.ZodType<T>, input: unknown): Result<T, ValidationError>;
|
|
301
|
+
import { Result as Result2 } from "better-result";
|
|
302
|
+
/**
|
|
303
|
+
* Error types for adapter operations.
|
|
304
|
+
* These extend the base error taxonomy for adapter-specific failures.
|
|
305
|
+
*/
|
|
306
|
+
/** Error during indexing operations */
|
|
307
|
+
interface IndexError {
|
|
308
|
+
readonly _tag: "IndexError";
|
|
309
|
+
readonly message: string;
|
|
310
|
+
readonly cause?: unknown;
|
|
311
|
+
}
|
|
312
|
+
/** Error during cache operations */
|
|
313
|
+
interface CacheError {
|
|
314
|
+
readonly _tag: "CacheError";
|
|
315
|
+
readonly message: string;
|
|
316
|
+
readonly cause?: unknown;
|
|
317
|
+
}
|
|
318
|
+
/** Error during auth/credential operations */
|
|
319
|
+
interface AdapterAuthError {
|
|
320
|
+
readonly _tag: "AdapterAuthError";
|
|
321
|
+
readonly message: string;
|
|
322
|
+
readonly cause?: unknown;
|
|
323
|
+
}
|
|
324
|
+
/** Error during storage operations */
|
|
325
|
+
interface StorageError {
|
|
326
|
+
readonly _tag: "StorageError";
|
|
327
|
+
readonly message: string;
|
|
328
|
+
readonly cause?: unknown;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Search options for index adapter.
|
|
332
|
+
*/
|
|
333
|
+
interface SearchOptions {
|
|
334
|
+
/** Maximum results to return */
|
|
335
|
+
limit?: number;
|
|
336
|
+
/** Offset for pagination */
|
|
337
|
+
offset?: number;
|
|
338
|
+
/** Field-specific filters */
|
|
339
|
+
filters?: Record<string, unknown>;
|
|
340
|
+
/** Fields to boost in relevance scoring */
|
|
341
|
+
boostFields?: string[];
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Search result from index adapter.
|
|
345
|
+
*/
|
|
346
|
+
interface SearchResult<T> {
|
|
347
|
+
/** Matched items with relevance scores */
|
|
348
|
+
hits: Array<{
|
|
349
|
+
item: T;
|
|
350
|
+
score: number;
|
|
351
|
+
highlights?: Record<string, string[]>;
|
|
352
|
+
}>;
|
|
353
|
+
/** Total number of matches (for pagination) */
|
|
354
|
+
total: number;
|
|
355
|
+
/** Search execution time in milliseconds */
|
|
356
|
+
took: number;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Index statistics.
|
|
360
|
+
*/
|
|
361
|
+
interface IndexStats {
|
|
362
|
+
/** Total documents indexed */
|
|
363
|
+
documentCount: number;
|
|
364
|
+
/** Index size in bytes (if available) */
|
|
365
|
+
sizeBytes?: number;
|
|
366
|
+
/** Last update timestamp */
|
|
367
|
+
lastUpdated: Date | null;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Index adapter - pluggable full-text search backends.
|
|
371
|
+
*
|
|
372
|
+
* Implementations: SQLite FTS5, future: Tantivy, Meilisearch
|
|
373
|
+
*
|
|
374
|
+
* @typeParam T - The indexed document type
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```typescript
|
|
378
|
+
* const sqliteIndex = new SqliteFts5Adapter<Note>({
|
|
379
|
+
* db: database,
|
|
380
|
+
* table: "notes_fts",
|
|
381
|
+
* fields: ["title", "content", "tags"],
|
|
382
|
+
* });
|
|
383
|
+
*
|
|
384
|
+
* await sqliteIndex.index(notes);
|
|
385
|
+
* const results = await sqliteIndex.search("authentication patterns");
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
interface IndexAdapter<T> {
|
|
389
|
+
/** Add or update documents in the index */
|
|
390
|
+
index(items: T[]): Promise<Result2<void, IndexError>>;
|
|
391
|
+
/** Full-text search with optional filters */
|
|
392
|
+
search(query: string, options?: SearchOptions): Promise<Result2<SearchResult<T>, IndexError>>;
|
|
393
|
+
/** Remove documents by ID */
|
|
394
|
+
remove(ids: string[]): Promise<Result2<void, IndexError>>;
|
|
395
|
+
/** Clear all indexed documents */
|
|
396
|
+
clear(): Promise<Result2<void, IndexError>>;
|
|
397
|
+
/** Get index statistics */
|
|
398
|
+
stats(): Promise<Result2<IndexStats, IndexError>>;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Cache adapter - pluggable caching backends.
|
|
402
|
+
*
|
|
403
|
+
* Implementations: SQLite, in-memory LRU, future: Redis via Bun.RedisClient
|
|
404
|
+
*
|
|
405
|
+
* @typeParam T - The cached value type
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```typescript
|
|
409
|
+
* const cache = new SqliteCacheAdapter<User>({ db, table: "user_cache" });
|
|
410
|
+
*
|
|
411
|
+
* await cache.set("user:123", user, 3600); // 1 hour TTL
|
|
412
|
+
* const cached = await cache.get("user:123");
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
interface CacheAdapter<T> {
|
|
416
|
+
/** Get cached value, null if not found or expired */
|
|
417
|
+
get(key: string): Promise<Result2<T | null, CacheError>>;
|
|
418
|
+
/** Set value with optional TTL in seconds */
|
|
419
|
+
set(key: string, value: T, ttlSeconds?: number): Promise<Result2<void, CacheError>>;
|
|
420
|
+
/** Delete cached value, returns true if existed */
|
|
421
|
+
delete(key: string): Promise<Result2<boolean, CacheError>>;
|
|
422
|
+
/** Clear all cached values */
|
|
423
|
+
clear(): Promise<Result2<void, CacheError>>;
|
|
424
|
+
/** Check if key exists (without retrieving value) */
|
|
425
|
+
has(key: string): Promise<Result2<boolean, CacheError>>;
|
|
426
|
+
/** Get multiple values at once */
|
|
427
|
+
getMany(keys: string[]): Promise<Result2<Map<string, T>, CacheError>>;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Auth adapter - pluggable credential storage.
|
|
431
|
+
*
|
|
432
|
+
* Implementations: Environment, OS Keychain (via Bun.secrets), file-based
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```typescript
|
|
436
|
+
* const auth = new KeychainAuthAdapter({ service: "outfitter" });
|
|
437
|
+
*
|
|
438
|
+
* await auth.set("github_token", token);
|
|
439
|
+
* const stored = await auth.get("github_token");
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
interface AuthAdapter {
|
|
443
|
+
/** Retrieve credential by key */
|
|
444
|
+
get(key: string): Promise<Result2<string | null, AdapterAuthError>>;
|
|
445
|
+
/** Store credential */
|
|
446
|
+
set(key: string, value: string): Promise<Result2<void, AdapterAuthError>>;
|
|
447
|
+
/** Remove credential */
|
|
448
|
+
delete(key: string): Promise<Result2<boolean, AdapterAuthError>>;
|
|
449
|
+
/** List available credential keys (not values) */
|
|
450
|
+
list(): Promise<Result2<string[], AdapterAuthError>>;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Storage adapter - pluggable blob/file storage.
|
|
454
|
+
*
|
|
455
|
+
* Implementations: Local filesystem, S3 via Bun.S3Client, R2
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* const storage = new LocalStorageAdapter({ basePath: "/data" });
|
|
460
|
+
*
|
|
461
|
+
* await storage.write("notes/abc.md", content);
|
|
462
|
+
* const data = await storage.read("notes/abc.md");
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
interface StorageAdapter {
|
|
466
|
+
/** Read file contents */
|
|
467
|
+
read(path: string): Promise<Result2<Uint8Array, StorageError>>;
|
|
468
|
+
/** Write file contents */
|
|
469
|
+
write(path: string, data: Uint8Array): Promise<Result2<void, StorageError>>;
|
|
470
|
+
/** Delete file */
|
|
471
|
+
delete(path: string): Promise<Result2<boolean, StorageError>>;
|
|
472
|
+
/** Check if file exists */
|
|
473
|
+
exists(path: string): Promise<Result2<boolean, StorageError>>;
|
|
474
|
+
/** List files in directory */
|
|
475
|
+
list(prefix: string): Promise<Result2<string[], StorageError>>;
|
|
476
|
+
/** Get file metadata (size, modified time) */
|
|
477
|
+
stat(path: string): Promise<Result2<{
|
|
478
|
+
size: number;
|
|
479
|
+
modifiedAt: Date;
|
|
480
|
+
} | null, StorageError>>;
|
|
481
|
+
}
|
|
482
|
+
import { Result as Result3 } from "better-result";
|
|
483
|
+
/**
|
|
484
|
+
* Metadata attached to every response envelope.
|
|
485
|
+
*/
|
|
486
|
+
interface EnvelopeMeta {
|
|
487
|
+
/** Unique request identifier for tracing */
|
|
488
|
+
requestId: string;
|
|
489
|
+
/** ISO timestamp of response generation */
|
|
490
|
+
timestamp: string;
|
|
491
|
+
/** Operation duration in milliseconds */
|
|
492
|
+
durationMs?: number;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Pagination metadata for list responses.
|
|
496
|
+
*/
|
|
497
|
+
interface PaginationMeta {
|
|
498
|
+
/** Total number of items (if known) */
|
|
499
|
+
total?: number;
|
|
500
|
+
/** Number of items returned */
|
|
501
|
+
count: number;
|
|
502
|
+
/** Cursor for next page (null if no more pages) */
|
|
503
|
+
nextCursor: string | null;
|
|
504
|
+
/** Whether more pages exist */
|
|
505
|
+
hasMore: boolean;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Success envelope structure.
|
|
509
|
+
*/
|
|
510
|
+
interface SuccessEnvelope<T> {
|
|
511
|
+
ok: true;
|
|
512
|
+
data: T;
|
|
513
|
+
meta: EnvelopeMeta;
|
|
514
|
+
pagination?: PaginationMeta;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Error envelope structure.
|
|
518
|
+
*/
|
|
519
|
+
interface ErrorEnvelope {
|
|
520
|
+
ok: false;
|
|
521
|
+
error: SerializedError;
|
|
522
|
+
meta: EnvelopeMeta;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Response envelope - consistent wrapper for all handler responses.
|
|
526
|
+
*/
|
|
527
|
+
type Envelope<T> = SuccessEnvelope<T> | ErrorEnvelope;
|
|
528
|
+
/**
|
|
529
|
+
* HTTP-style response with status code.
|
|
530
|
+
*/
|
|
531
|
+
interface HttpResponse<T> {
|
|
532
|
+
status: number;
|
|
533
|
+
body: Envelope<T>;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Convert a Result to a response envelope.
|
|
537
|
+
*
|
|
538
|
+
* @typeParam T - Success data type
|
|
539
|
+
* @typeParam E - Error type (extends OutfitterError)
|
|
540
|
+
* @param result - Handler result to wrap
|
|
541
|
+
* @param meta - Optional metadata overrides
|
|
542
|
+
* @returns Envelope with success data or serialized error
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```typescript
|
|
546
|
+
* const result = await getNote({ id: "abc123" }, ctx);
|
|
547
|
+
* const envelope = toEnvelope(result, { requestId: ctx.requestId });
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
declare function toEnvelope<
|
|
551
|
+
T,
|
|
552
|
+
E extends OutfitterError
|
|
553
|
+
>(result: Result3<T, E>, meta?: Partial<EnvelopeMeta>): Envelope<T>;
|
|
554
|
+
/**
|
|
555
|
+
* Convert a Result to HTTP-style response (for MCP over HTTP).
|
|
556
|
+
*
|
|
557
|
+
* Maps error category to appropriate HTTP status code.
|
|
558
|
+
*
|
|
559
|
+
* @typeParam T - Success data type
|
|
560
|
+
* @typeParam E - Error type (extends OutfitterError)
|
|
561
|
+
* @param result - Handler result to convert
|
|
562
|
+
* @returns HTTP response with status code and envelope body
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```typescript
|
|
566
|
+
* const result = await getNote({ id: "abc123" }, ctx);
|
|
567
|
+
* const response = toHttpResponse(result);
|
|
568
|
+
* // { status: 200, body: { ok: true, data: note, meta: {...} } }
|
|
569
|
+
* // or { status: 404, body: { ok: false, error: {...}, meta: {...} } }
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
declare function toHttpResponse<
|
|
573
|
+
T,
|
|
574
|
+
E extends OutfitterError
|
|
575
|
+
>(result: Result3<T, E>): HttpResponse<T>;
|
|
576
|
+
import { Result as Result4 } from "better-result";
|
|
577
|
+
/**
|
|
578
|
+
* Options for retry behavior.
|
|
579
|
+
*/
|
|
580
|
+
interface RetryOptions {
|
|
581
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
582
|
+
maxAttempts?: number;
|
|
583
|
+
/** Initial delay in milliseconds (default: 1000) */
|
|
584
|
+
initialDelayMs?: number;
|
|
585
|
+
/** Maximum delay in milliseconds (default: 30000) */
|
|
586
|
+
maxDelayMs?: number;
|
|
587
|
+
/** Exponential backoff multiplier (default: 2) */
|
|
588
|
+
backoffMultiplier?: number;
|
|
589
|
+
/** Whether to add jitter to delays (default: true) */
|
|
590
|
+
jitter?: boolean;
|
|
591
|
+
/** Predicate to determine if error is retryable */
|
|
592
|
+
isRetryable?: (error: OutfitterError) => boolean;
|
|
593
|
+
/** Abort signal for cancellation */
|
|
594
|
+
signal?: AbortSignal;
|
|
595
|
+
/** Callback invoked before each retry */
|
|
596
|
+
onRetry?: (attempt: number, error: OutfitterError, delayMs: number) => void;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Options for timeout behavior.
|
|
600
|
+
*/
|
|
601
|
+
interface TimeoutOptions {
|
|
602
|
+
/** Timeout duration in milliseconds */
|
|
603
|
+
timeoutMs: number;
|
|
604
|
+
/** Operation name for error context */
|
|
605
|
+
operation?: string;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Retry an async operation with exponential backoff.
|
|
609
|
+
*
|
|
610
|
+
* Automatically retries transient errors (network, timeout, rate_limit)
|
|
611
|
+
* unless overridden with `isRetryable`.
|
|
612
|
+
*
|
|
613
|
+
* @typeParam T - Success type
|
|
614
|
+
* @param fn - Async function returning Result
|
|
615
|
+
* @param options - Retry configuration
|
|
616
|
+
* @returns Result from final attempt
|
|
617
|
+
*
|
|
618
|
+
* @example
|
|
619
|
+
* ```typescript
|
|
620
|
+
* const result = await retry(
|
|
621
|
+
* () => fetchData(url),
|
|
622
|
+
* {
|
|
623
|
+
* maxAttempts: 5,
|
|
624
|
+
* initialDelayMs: 500,
|
|
625
|
+
* onRetry: (attempt, error) => {
|
|
626
|
+
* logger.warn(`Retry ${attempt}`, { error: error._tag });
|
|
627
|
+
* },
|
|
628
|
+
* }
|
|
629
|
+
* );
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
declare function retry<T>(fn: () => Promise<Result4<T, OutfitterError>>, options?: RetryOptions): Promise<Result4<T, OutfitterError>>;
|
|
633
|
+
/**
|
|
634
|
+
* Wrap an async operation with a timeout.
|
|
635
|
+
*
|
|
636
|
+
* Returns TimeoutError if operation doesn't complete within the specified duration.
|
|
637
|
+
*
|
|
638
|
+
* @typeParam T - Success type
|
|
639
|
+
* @typeParam E - Error type
|
|
640
|
+
* @param fn - Async function returning Result
|
|
641
|
+
* @param options - Timeout configuration
|
|
642
|
+
* @returns Result from operation or TimeoutError
|
|
643
|
+
*
|
|
644
|
+
* @example
|
|
645
|
+
* ```typescript
|
|
646
|
+
* const result = await withTimeout(
|
|
647
|
+
* () => slowOperation(),
|
|
648
|
+
* { timeoutMs: 5000, operation: "database query" }
|
|
649
|
+
* );
|
|
650
|
+
*
|
|
651
|
+
* if (result.isErr() && result.error._tag === "TimeoutError") {
|
|
652
|
+
* // Handle timeout
|
|
653
|
+
* }
|
|
654
|
+
* ```
|
|
655
|
+
*/
|
|
656
|
+
declare function withTimeout<
|
|
657
|
+
T,
|
|
658
|
+
E extends OutfitterError
|
|
659
|
+
>(fn: () => Promise<Result4<T, E>>, options: TimeoutOptions): Promise<Result4<T, E | TimeoutError>>;
|
|
660
|
+
import { Result as Result5 } from "better-result";
|
|
661
|
+
import { z as z2 } from "zod";
|
|
662
|
+
/**
|
|
663
|
+
* Options for error serialization.
|
|
664
|
+
*/
|
|
665
|
+
interface SerializeErrorOptions {
|
|
666
|
+
/** Include stack trace (default: false in production, true in development) */
|
|
667
|
+
includeStack?: boolean;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Serialize a OutfitterError to JSON-safe format.
|
|
671
|
+
*
|
|
672
|
+
* Strips stack traces in production, preserves in development.
|
|
673
|
+
* Automatically redacts sensitive values from context.
|
|
674
|
+
*
|
|
675
|
+
* @param error - The error to serialize
|
|
676
|
+
* @param options - Serialization options
|
|
677
|
+
* @returns JSON-safe serialized error
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```typescript
|
|
681
|
+
* const serialized = serializeError(new NotFoundError("note", "abc123"));
|
|
682
|
+
* // { _tag: "NotFoundError", category: "not_found", message: "note not found: abc123", context: { resourceType: "note", resourceId: "abc123" } }
|
|
683
|
+
* ```
|
|
684
|
+
*/
|
|
685
|
+
declare function serializeError(error: OutfitterError, options?: SerializeErrorOptions): SerializedError;
|
|
686
|
+
/**
|
|
687
|
+
* Deserialize error from JSON (e.g., from MCP response).
|
|
688
|
+
*
|
|
689
|
+
* Returns a typed OutfitterError subclass based on _tag.
|
|
690
|
+
*
|
|
691
|
+
* @param data - Serialized error data
|
|
692
|
+
* @returns Reconstructed OutfitterError instance
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```typescript
|
|
696
|
+
* const error = deserializeError(jsonData);
|
|
697
|
+
* if (error._tag === "NotFoundError") {
|
|
698
|
+
* // TypeScript knows error.resourceType exists
|
|
699
|
+
* }
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
declare function deserializeError(data: SerializedError): OutfitterError;
|
|
703
|
+
/**
|
|
704
|
+
* Safely stringify any value to JSON.
|
|
705
|
+
*
|
|
706
|
+
* Handles circular references, BigInt, and other non-JSON-safe values.
|
|
707
|
+
* Applies redaction to sensitive values.
|
|
708
|
+
*
|
|
709
|
+
* @param value - Value to stringify
|
|
710
|
+
* @param space - Indentation (default: undefined for compact)
|
|
711
|
+
* @returns JSON string
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```typescript
|
|
715
|
+
* const json = safeStringify({ apiKey: "sk-secret", data: "safe" });
|
|
716
|
+
* // '{"apiKey":"[REDACTED]","data":"safe"}'
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
declare function safeStringify(value: unknown, space?: number): string;
|
|
720
|
+
/**
|
|
721
|
+
* Safely parse JSON string with optional schema validation.
|
|
722
|
+
*
|
|
723
|
+
* Returns Result instead of throwing on invalid JSON.
|
|
724
|
+
*
|
|
725
|
+
* @typeParam T - Expected parsed type (or unknown if no schema)
|
|
726
|
+
* @param json - JSON string to parse
|
|
727
|
+
* @param schema - Optional Zod schema for validation
|
|
728
|
+
* @returns Result with parsed value or ValidationError
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```typescript
|
|
732
|
+
* const result = safeParse<Config>('{"port": 3000}', ConfigSchema);
|
|
733
|
+
* if (result.isOk()) {
|
|
734
|
+
* const config = result.unwrap();
|
|
735
|
+
* }
|
|
736
|
+
* ```
|
|
737
|
+
*/
|
|
738
|
+
declare function safeParse<T = unknown>(json: string, schema?: z2.ZodType<T>): Result5<T, ValidationError>;
|
|
739
|
+
import { Result as Result6 } from "better-result";
|
|
740
|
+
/**
|
|
741
|
+
* Logger interface for handler context.
|
|
742
|
+
* Implementations provided by @outfitter/logging.
|
|
743
|
+
*
|
|
744
|
+
* All log methods accept an optional context object that will be merged
|
|
745
|
+
* with any context inherited from parent loggers created via `child()`.
|
|
746
|
+
*/
|
|
747
|
+
interface Logger {
|
|
748
|
+
trace(message: string, metadata?: Record<string, unknown>): void;
|
|
749
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
750
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
751
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
752
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
753
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
754
|
+
/**
|
|
755
|
+
* Creates a child logger with additional context.
|
|
756
|
+
*
|
|
757
|
+
* Context from the child is merged with the parent's context,
|
|
758
|
+
* with child context taking precedence for duplicate keys.
|
|
759
|
+
* Child loggers are composable (can create nested children).
|
|
760
|
+
*
|
|
761
|
+
* @param context - Additional context to include in all log messages
|
|
762
|
+
* @returns A new Logger instance with the merged context
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* ```typescript
|
|
766
|
+
* const requestLogger = ctx.logger.child({ requestId: ctx.requestId });
|
|
767
|
+
* requestLogger.info("Processing request"); // includes requestId
|
|
768
|
+
*
|
|
769
|
+
* const opLogger = requestLogger.child({ operation: "create" });
|
|
770
|
+
* opLogger.debug("Starting"); // includes requestId + operation
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
child(context: Record<string, unknown>): Logger;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Resolved configuration interface.
|
|
777
|
+
* Implementations provided by @outfitter/config.
|
|
778
|
+
*/
|
|
779
|
+
interface ResolvedConfig {
|
|
780
|
+
get<T>(key: string): T | undefined;
|
|
781
|
+
getRequired<T>(key: string): T;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Handler context - provides cross-cutting concerns without polluting handler signatures.
|
|
785
|
+
*
|
|
786
|
+
* @example
|
|
787
|
+
* ```typescript
|
|
788
|
+
* const handler: Handler<Input, Output, NotFoundError> = async (input, ctx) => {
|
|
789
|
+
* ctx.logger.debug("Processing request", { requestId: ctx.requestId });
|
|
790
|
+
* // ... handler logic
|
|
791
|
+
* };
|
|
792
|
+
* ```
|
|
793
|
+
*/
|
|
794
|
+
interface HandlerContext {
|
|
795
|
+
/** Abort signal for cancellation propagation */
|
|
796
|
+
signal?: AbortSignal;
|
|
797
|
+
/** Unique request identifier for tracing (UUIDv7) */
|
|
798
|
+
requestId: string;
|
|
799
|
+
/** Structured logger with automatic redaction */
|
|
800
|
+
logger: Logger;
|
|
801
|
+
/** Resolved configuration values */
|
|
802
|
+
config?: ResolvedConfig;
|
|
803
|
+
/** Workspace root path, if detected */
|
|
804
|
+
workspaceRoot?: string;
|
|
805
|
+
/** Current working directory */
|
|
806
|
+
cwd: string;
|
|
807
|
+
/** Environment variables (filtered, redacted) */
|
|
808
|
+
env: Record<string, string | undefined>;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Handler - transport-agnostic domain logic unit.
|
|
812
|
+
*
|
|
813
|
+
* Handlers receive typed input, return Results, and know nothing about
|
|
814
|
+
* transport or output format. CLI and MCP are thin adapters over handlers.
|
|
815
|
+
*
|
|
816
|
+
* @typeParam TInput - Validated input parameters
|
|
817
|
+
* @typeParam TOutput - Success return type
|
|
818
|
+
* @typeParam TError - Error type (must extend OutfitterError)
|
|
819
|
+
*
|
|
820
|
+
* @example
|
|
821
|
+
* ```typescript
|
|
822
|
+
* const getNote: Handler<{ id: string }, Note, NotFoundError> = async (input, ctx) => {
|
|
823
|
+
* const note = await ctx.db.notes.find(input.id);
|
|
824
|
+
* if (!note) return Result.err(new NotFoundError("note", input.id));
|
|
825
|
+
* return Result.ok(note);
|
|
826
|
+
* };
|
|
827
|
+
* ```
|
|
828
|
+
*/
|
|
829
|
+
type Handler<
|
|
830
|
+
TInput,
|
|
831
|
+
TOutput,
|
|
832
|
+
TError extends OutfitterError = OutfitterError
|
|
833
|
+
> = (input: TInput, ctx: HandlerContext) => Promise<Result6<TOutput, TError>>;
|
|
834
|
+
/**
|
|
835
|
+
* Synchronous handler variant for operations that don't need async.
|
|
836
|
+
*/
|
|
837
|
+
type SyncHandler<
|
|
838
|
+
TInput,
|
|
839
|
+
TOutput,
|
|
840
|
+
TError extends OutfitterError = OutfitterError
|
|
841
|
+
> = (input: TInput, ctx: HandlerContext) => Result6<TOutput, TError>;
|
|
842
|
+
/**
|
|
843
|
+
* Options for creating a handler context.
|
|
844
|
+
*/
|
|
845
|
+
interface CreateContextOptions {
|
|
846
|
+
/** Logger instance (uses no-op logger if not provided) */
|
|
847
|
+
logger?: Logger;
|
|
848
|
+
/** Resolved configuration */
|
|
849
|
+
config?: ResolvedConfig;
|
|
850
|
+
/** Abort signal for cancellation */
|
|
851
|
+
signal?: AbortSignal;
|
|
852
|
+
/** Explicit request ID (generates UUIDv7 if not provided) */
|
|
853
|
+
requestId?: string;
|
|
854
|
+
/** Workspace root path */
|
|
855
|
+
workspaceRoot?: string;
|
|
856
|
+
/** Current working directory (defaults to process.cwd()) */
|
|
857
|
+
cwd?: string;
|
|
858
|
+
/** Environment variables to include */
|
|
859
|
+
env?: Record<string, string | undefined>;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Create a HandlerContext for a new request.
|
|
863
|
+
*
|
|
864
|
+
* Auto-generates requestId using Bun.randomUUIDv7() if not provided.
|
|
865
|
+
*
|
|
866
|
+
* @param options - Context configuration options
|
|
867
|
+
* @returns Fully populated HandlerContext
|
|
868
|
+
*
|
|
869
|
+
* @example
|
|
870
|
+
* ```typescript
|
|
871
|
+
* const ctx = createContext({
|
|
872
|
+
* logger: createLogger(),
|
|
873
|
+
* config: resolvedConfig,
|
|
874
|
+
* signal: controller.signal,
|
|
875
|
+
* });
|
|
876
|
+
*
|
|
877
|
+
* const result = await handler(input, ctx);
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
declare function createContext(options: CreateContextOptions): HandlerContext;
|
|
881
|
+
/**
|
|
882
|
+
* Generate a sortable request ID (UUIDv7).
|
|
883
|
+
*
|
|
884
|
+
* UUIDv7 is time-ordered, making it ideal for request tracing
|
|
885
|
+
* as IDs sort chronologically.
|
|
886
|
+
*
|
|
887
|
+
* @returns UUIDv7 string
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```typescript
|
|
891
|
+
* const requestId = generateRequestId();
|
|
892
|
+
* // "018e4f3c-1a2b-7000-8000-000000000001"
|
|
893
|
+
* ```
|
|
894
|
+
*/
|
|
895
|
+
declare function generateRequestId(): string;
|
|
896
|
+
/**
|
|
897
|
+
* @outfitter/contracts - Capability manifest
|
|
898
|
+
*
|
|
899
|
+
* Shared action capability manifest for CLI/MCP/API/server parity.
|
|
900
|
+
*
|
|
901
|
+
* @packageDocumentation
|
|
902
|
+
*/
|
|
903
|
+
declare const CAPABILITY_SURFACES: readonly ["cli", "mcp", "api", "server"];
|
|
904
|
+
type CapabilitySurface = (typeof CAPABILITY_SURFACES)[number];
|
|
905
|
+
interface ActionCapability {
|
|
906
|
+
surfaces: readonly CapabilitySurface[];
|
|
907
|
+
notes?: string;
|
|
908
|
+
}
|
|
909
|
+
declare const DEFAULT_ACTION_SURFACES: readonly ["cli", "mcp"];
|
|
910
|
+
declare function capability(surfaces?: readonly CapabilitySurface[], notes?: string): ActionCapability;
|
|
911
|
+
declare function capabilityAll(notes?: string): ActionCapability;
|
|
912
|
+
declare const ACTION_CAPABILITIES: Record<string, ActionCapability>;
|
|
913
|
+
declare function getActionsForSurface(surface: CapabilitySurface): string[];
|
|
914
|
+
import { z as z3 } from "zod";
|
|
915
|
+
declare const ACTION_SURFACES: readonly ["cli", "mcp", "api", "server"];
|
|
916
|
+
type ActionSurface = (typeof ACTION_SURFACES)[number];
|
|
917
|
+
declare const DEFAULT_REGISTRY_SURFACES: readonly ActionSurface[];
|
|
918
|
+
interface ActionCliOption {
|
|
919
|
+
readonly flags: string;
|
|
920
|
+
readonly description: string;
|
|
921
|
+
readonly defaultValue?: string | boolean | string[];
|
|
922
|
+
readonly required?: boolean;
|
|
923
|
+
}
|
|
924
|
+
interface ActionCliInputContext {
|
|
925
|
+
readonly args: readonly string[];
|
|
926
|
+
readonly flags: Record<string, unknown>;
|
|
927
|
+
}
|
|
928
|
+
interface ActionCliSpec<TInput = unknown> {
|
|
929
|
+
readonly group?: string;
|
|
930
|
+
readonly command?: string;
|
|
931
|
+
readonly description?: string;
|
|
932
|
+
readonly aliases?: readonly string[];
|
|
933
|
+
readonly options?: readonly ActionCliOption[];
|
|
934
|
+
readonly mapInput?: (context: ActionCliInputContext) => TInput;
|
|
935
|
+
}
|
|
936
|
+
interface ActionMcpSpec<TInput = unknown> {
|
|
937
|
+
readonly tool?: string;
|
|
938
|
+
readonly description?: string;
|
|
939
|
+
readonly deferLoading?: boolean;
|
|
940
|
+
readonly mapInput?: (input: unknown) => TInput;
|
|
941
|
+
}
|
|
942
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
943
|
+
interface ActionApiSpec {
|
|
944
|
+
readonly method?: HttpMethod;
|
|
945
|
+
readonly path?: string;
|
|
946
|
+
readonly tags?: readonly string[];
|
|
947
|
+
}
|
|
948
|
+
interface ActionTrpcSpec {
|
|
949
|
+
readonly path?: string;
|
|
950
|
+
}
|
|
951
|
+
interface ActionSpec<
|
|
952
|
+
TInput,
|
|
953
|
+
TOutput,
|
|
954
|
+
TError extends OutfitterError = OutfitterError
|
|
955
|
+
> {
|
|
956
|
+
readonly id: string;
|
|
957
|
+
readonly description?: string;
|
|
958
|
+
readonly surfaces?: readonly ActionSurface[];
|
|
959
|
+
readonly input: z3.ZodType<TInput>;
|
|
960
|
+
readonly output?: z3.ZodType<TOutput>;
|
|
961
|
+
readonly handler: Handler<TInput, TOutput, TError> | SyncHandler<TInput, TOutput, TError>;
|
|
962
|
+
readonly cli?: ActionCliSpec<TInput>;
|
|
963
|
+
readonly mcp?: ActionMcpSpec<TInput>;
|
|
964
|
+
readonly api?: ActionApiSpec;
|
|
965
|
+
readonly trpc?: ActionTrpcSpec;
|
|
966
|
+
}
|
|
967
|
+
type AnyActionSpec = ActionSpec<unknown, unknown, OutfitterError>;
|
|
968
|
+
interface ActionRegistry {
|
|
969
|
+
add<
|
|
970
|
+
TInput,
|
|
971
|
+
TOutput,
|
|
972
|
+
TError extends OutfitterError = OutfitterError
|
|
973
|
+
>(action: ActionSpec<TInput, TOutput, TError>): ActionRegistry;
|
|
974
|
+
list(): AnyActionSpec[];
|
|
975
|
+
get(id: string): AnyActionSpec | undefined;
|
|
976
|
+
forSurface(surface: ActionSurface): AnyActionSpec[];
|
|
977
|
+
}
|
|
978
|
+
declare function defineAction<
|
|
979
|
+
TInput,
|
|
980
|
+
TOutput,
|
|
981
|
+
TError extends OutfitterError = OutfitterError
|
|
982
|
+
>(action: ActionSpec<TInput, TOutput, TError>): ActionSpec<TInput, TOutput, TError>;
|
|
983
|
+
declare function createActionRegistry(): ActionRegistry;
|
|
984
|
+
/**
|
|
985
|
+
* Configuration for creating a redactor.
|
|
986
|
+
*/
|
|
987
|
+
interface RedactorConfig {
|
|
988
|
+
/** Regex patterns to match and redact */
|
|
989
|
+
patterns: RegExp[];
|
|
990
|
+
/** Object keys whose values should always be redacted */
|
|
991
|
+
keys: string[];
|
|
992
|
+
/** Replacement string (default: "[REDACTED]") */
|
|
993
|
+
replacement?: string;
|
|
994
|
+
/** Whether to redact recursively in nested objects (default: true) */
|
|
995
|
+
deep?: boolean;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Redaction event for audit logging.
|
|
999
|
+
*/
|
|
1000
|
+
interface RedactionEvent {
|
|
1001
|
+
/** Type of redaction applied */
|
|
1002
|
+
redactedBy: "pattern" | "key";
|
|
1003
|
+
/** Identifier of the pattern/key that matched */
|
|
1004
|
+
matcher: string;
|
|
1005
|
+
/** Location in the object path (e.g., "config.auth.apiKey") */
|
|
1006
|
+
path: string;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Callback for redaction events.
|
|
1010
|
+
*/
|
|
1011
|
+
type RedactionCallback = (event: RedactionEvent) => void;
|
|
1012
|
+
/**
|
|
1013
|
+
* Redactor - sensitive data scrubbing for logs, errors, and output.
|
|
1014
|
+
*
|
|
1015
|
+
* Applied automatically by @outfitter/logging. Manual application
|
|
1016
|
+
* required when building custom output or error context.
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```typescript
|
|
1020
|
+
* const redactor = createRedactor({
|
|
1021
|
+
* patterns: [
|
|
1022
|
+
* /Bearer [A-Za-z0-9-_]+/g, // Auth headers
|
|
1023
|
+
* /sk-[A-Za-z0-9]{48}/g, // OpenAI keys
|
|
1024
|
+
* /ghp_[A-Za-z0-9]{36}/g, // GitHub PATs
|
|
1025
|
+
* /password[:=]\s*["']?[^"'\s]+/gi, // Password fields
|
|
1026
|
+
* ],
|
|
1027
|
+
* keys: ["apiKey", "secret", "token", "password", "credential"],
|
|
1028
|
+
* replacement: "[REDACTED]",
|
|
1029
|
+
* });
|
|
1030
|
+
*
|
|
1031
|
+
* const safeLog = redactor.redact(sensitiveObject);
|
|
1032
|
+
* ```
|
|
1033
|
+
*/
|
|
1034
|
+
interface Redactor {
|
|
1035
|
+
/** Redact sensitive values from an object (deep) */
|
|
1036
|
+
redact<T>(value: T): T;
|
|
1037
|
+
/** Redact sensitive values from a string */
|
|
1038
|
+
redactString(value: string): string;
|
|
1039
|
+
/** Check if a key name is sensitive */
|
|
1040
|
+
isSensitiveKey(key: string): boolean;
|
|
1041
|
+
/** Add a pattern at runtime */
|
|
1042
|
+
addPattern(pattern: RegExp): void;
|
|
1043
|
+
/** Add a sensitive key at runtime */
|
|
1044
|
+
addSensitiveKey(key: string): void;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Default patterns for common secrets.
|
|
1048
|
+
*
|
|
1049
|
+
* Covers:
|
|
1050
|
+
* - API keys (OpenAI, Anthropic, GitHub, etc.)
|
|
1051
|
+
* - Auth headers (Bearer tokens)
|
|
1052
|
+
* - Connection strings (database URLs)
|
|
1053
|
+
* - Password fields in various formats
|
|
1054
|
+
*/
|
|
1055
|
+
declare const DEFAULT_PATTERNS: RegExp[];
|
|
1056
|
+
/**
|
|
1057
|
+
* Default sensitive key names.
|
|
1058
|
+
*
|
|
1059
|
+
* Object keys matching these (case-insensitive) will have their values redacted.
|
|
1060
|
+
*/
|
|
1061
|
+
declare const DEFAULT_SENSITIVE_KEYS: string[];
|
|
1062
|
+
/**
|
|
1063
|
+
* Create a redactor instance with the given configuration.
|
|
1064
|
+
*
|
|
1065
|
+
* @param config - Redactor configuration
|
|
1066
|
+
* @returns Configured Redactor instance
|
|
1067
|
+
*
|
|
1068
|
+
* @example
|
|
1069
|
+
* ```typescript
|
|
1070
|
+
* const redactor = createRedactor({
|
|
1071
|
+
* patterns: [...DEFAULT_PATTERNS],
|
|
1072
|
+
* keys: [...DEFAULT_SENSITIVE_KEYS],
|
|
1073
|
+
* });
|
|
1074
|
+
*
|
|
1075
|
+
* const safe = redactor.redact({
|
|
1076
|
+
* user: "alice",
|
|
1077
|
+
* apiKey: "sk-abc123...",
|
|
1078
|
+
* });
|
|
1079
|
+
* // { user: "alice", apiKey: "[REDACTED]" }
|
|
1080
|
+
* ```
|
|
1081
|
+
*/
|
|
1082
|
+
declare function createRedactor(config: RedactorConfig): Redactor;
|
|
1083
|
+
import { Result as Result7 } from "better-result";
|
|
1084
|
+
/**
|
|
1085
|
+
* Array type guaranteed to have at least one element.
|
|
1086
|
+
*/
|
|
1087
|
+
type NonEmptyArray<T> = [T, ...T[]];
|
|
1088
|
+
/**
|
|
1089
|
+
* Type guard for NonEmptyArray.
|
|
1090
|
+
*/
|
|
1091
|
+
declare const isNonEmptyArray: <T>(arr: readonly T[]) => arr is NonEmptyArray<T>;
|
|
1092
|
+
/**
|
|
1093
|
+
* Assert a value is defined (not null or undefined).
|
|
1094
|
+
* Returns Result instead of throwing.
|
|
1095
|
+
*/
|
|
1096
|
+
declare const assertDefined: <T>(value: T | null | undefined, message?: string) => Result7<T, InstanceType<typeof AssertionError>>;
|
|
1097
|
+
/**
|
|
1098
|
+
* Assert array has at least one element.
|
|
1099
|
+
* Returns NonEmptyArray on success.
|
|
1100
|
+
*/
|
|
1101
|
+
declare const assertNonEmpty: <T>(arr: readonly T[], message?: string) => Result7<NonEmptyArray<T>, InstanceType<typeof AssertionError>>;
|
|
1102
|
+
/**
|
|
1103
|
+
* Assert value matches a predicate.
|
|
1104
|
+
* Supports type guard predicates for narrowing.
|
|
1105
|
+
*/
|
|
1106
|
+
declare function assertMatches<
|
|
1107
|
+
T,
|
|
1108
|
+
U extends T
|
|
1109
|
+
>(value: T, predicate: (v: T) => v is U, message?: string): Result7<U, InstanceType<typeof AssertionError>>;
|
|
1110
|
+
declare function assertMatches<T>(value: T, predicate: (v: T) => boolean, message?: string): Result7<T, InstanceType<typeof AssertionError>>;
|
|
1111
|
+
/**
|
|
1112
|
+
* Backoff strategy configuration options
|
|
1113
|
+
*/
|
|
1114
|
+
interface BackoffOptions {
|
|
1115
|
+
/** Base delay in milliseconds (default: 100) */
|
|
1116
|
+
baseDelayMs?: number;
|
|
1117
|
+
/** Maximum delay cap in milliseconds (default: 30000) */
|
|
1118
|
+
maxDelayMs?: number;
|
|
1119
|
+
/** Backoff strategy (default: "exponential") */
|
|
1120
|
+
strategy?: "linear" | "exponential" | "constant";
|
|
1121
|
+
/** Whether to add jitter to prevent thundering herd (default: true) */
|
|
1122
|
+
useJitter?: boolean;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Determines if an error is potentially recoverable.
|
|
1126
|
+
*
|
|
1127
|
+
* Recoverable errors might succeed on retry or with user intervention.
|
|
1128
|
+
* This includes transient failures (network, timeout), rate limiting,
|
|
1129
|
+
* and optimistic lock conflicts.
|
|
1130
|
+
*
|
|
1131
|
+
* @param error - Error object with category property
|
|
1132
|
+
* @returns True if the error might be recoverable
|
|
1133
|
+
*
|
|
1134
|
+
* @example
|
|
1135
|
+
* ```typescript
|
|
1136
|
+
* import { isRecoverable, NetworkError } from '@outfitter/contracts';
|
|
1137
|
+
*
|
|
1138
|
+
* const networkError = new NetworkError({ message: "Connection refused" });
|
|
1139
|
+
* console.log(isRecoverable(networkError)); // true
|
|
1140
|
+
*
|
|
1141
|
+
* const validationError = new ValidationError({ message: "Invalid input" });
|
|
1142
|
+
* console.log(isRecoverable(validationError)); // false
|
|
1143
|
+
* ```
|
|
1144
|
+
*/
|
|
1145
|
+
declare const isRecoverable: (error: {
|
|
1146
|
+
readonly category: ErrorCategory;
|
|
1147
|
+
}) => boolean;
|
|
1148
|
+
/**
|
|
1149
|
+
* Determines if an error should trigger automatic retry.
|
|
1150
|
+
*
|
|
1151
|
+
* More restrictive than isRecoverable - only transient failures that
|
|
1152
|
+
* are good candidates for immediate retry without user intervention.
|
|
1153
|
+
*
|
|
1154
|
+
* Retryable categories:
|
|
1155
|
+
* - network: Connection issues may be temporary
|
|
1156
|
+
* - timeout: May succeed with another attempt
|
|
1157
|
+
*
|
|
1158
|
+
* NOT retryable (even though recoverable):
|
|
1159
|
+
* - rate_limit: Should respect retryAfterSeconds header
|
|
1160
|
+
* - conflict: May need user to resolve the conflict
|
|
1161
|
+
*
|
|
1162
|
+
* @param error - Error object with category property
|
|
1163
|
+
* @returns True if the operation should be automatically retried
|
|
1164
|
+
*
|
|
1165
|
+
* @example
|
|
1166
|
+
* ```typescript
|
|
1167
|
+
* import { isRetryable, TimeoutError } from '@outfitter/contracts';
|
|
1168
|
+
*
|
|
1169
|
+
* const timeout = new TimeoutError({
|
|
1170
|
+
* message: "Operation timed out",
|
|
1171
|
+
* operation: "fetch",
|
|
1172
|
+
* timeoutMs: 5000,
|
|
1173
|
+
* });
|
|
1174
|
+
* console.log(isRetryable(timeout)); // true
|
|
1175
|
+
*
|
|
1176
|
+
* const rateLimitError = new RateLimitError({ message: "Rate limit exceeded" });
|
|
1177
|
+
* console.log(isRetryable(rateLimitError)); // false (use retryAfterSeconds)
|
|
1178
|
+
* ```
|
|
1179
|
+
*/
|
|
1180
|
+
declare const isRetryable2: (error: {
|
|
1181
|
+
readonly category: ErrorCategory;
|
|
1182
|
+
}) => boolean;
|
|
1183
|
+
/**
|
|
1184
|
+
* Calculate appropriate backoff delay for retry.
|
|
1185
|
+
*
|
|
1186
|
+
* Supports three strategies:
|
|
1187
|
+
* - exponential (default): delay = baseDelayMs * 2^attempt
|
|
1188
|
+
* - linear: delay = baseDelayMs * (attempt + 1)
|
|
1189
|
+
* - constant: delay = baseDelayMs
|
|
1190
|
+
*
|
|
1191
|
+
* By default, adds jitter (+/-10%) to prevent thundering herd problems
|
|
1192
|
+
* when multiple clients retry simultaneously.
|
|
1193
|
+
*
|
|
1194
|
+
* @param attempt - The attempt number (0-indexed)
|
|
1195
|
+
* @param options - Backoff configuration options
|
|
1196
|
+
* @returns Delay in milliseconds before next retry
|
|
1197
|
+
*
|
|
1198
|
+
* @example
|
|
1199
|
+
* ```typescript
|
|
1200
|
+
* import { getBackoffDelay } from '@outfitter/contracts';
|
|
1201
|
+
*
|
|
1202
|
+
* // Exponential backoff (default): 100ms, 200ms, 400ms, 800ms...
|
|
1203
|
+
* const delay = getBackoffDelay(2); // ~400ms (with jitter)
|
|
1204
|
+
*
|
|
1205
|
+
* // Linear backoff: 100ms, 200ms, 300ms...
|
|
1206
|
+
* const linearDelay = getBackoffDelay(2, { strategy: "linear" }); // ~300ms
|
|
1207
|
+
*
|
|
1208
|
+
* // Constant delay: 500ms, 500ms, 500ms...
|
|
1209
|
+
* const constantDelay = getBackoffDelay(2, {
|
|
1210
|
+
* strategy: "constant",
|
|
1211
|
+
* baseDelayMs: 500,
|
|
1212
|
+
* }); // ~500ms
|
|
1213
|
+
*
|
|
1214
|
+
* // No jitter for deterministic timing
|
|
1215
|
+
* const exactDelay = getBackoffDelay(2, { useJitter: false }); // exactly 400ms
|
|
1216
|
+
* ```
|
|
1217
|
+
*/
|
|
1218
|
+
declare const getBackoffDelay: (attempt: number, options?: BackoffOptions) => number;
|
|
1219
|
+
/**
|
|
1220
|
+
* Convenience function combining retryability check with attempt limit.
|
|
1221
|
+
*
|
|
1222
|
+
* Returns true only if:
|
|
1223
|
+
* 1. The error is retryable (network or timeout)
|
|
1224
|
+
* 2. We haven't exceeded maxAttempts
|
|
1225
|
+
*
|
|
1226
|
+
* @param error - Error object with category property
|
|
1227
|
+
* @param attempt - Current attempt number (0-indexed)
|
|
1228
|
+
* @param maxAttempts - Maximum number of retry attempts (default: 3)
|
|
1229
|
+
* @returns True if the operation should be retried
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* ```typescript
|
|
1233
|
+
* import { shouldRetry, NetworkError } from '@outfitter/contracts';
|
|
1234
|
+
*
|
|
1235
|
+
* const error = new NetworkError({ message: "Connection timeout" });
|
|
1236
|
+
*
|
|
1237
|
+
* // First attempt failed
|
|
1238
|
+
* console.log(shouldRetry(error, 0)); // true (will retry)
|
|
1239
|
+
*
|
|
1240
|
+
* // Fourth attempt failed (exceeded default max of 3)
|
|
1241
|
+
* console.log(shouldRetry(error, 3)); // false (no more retries)
|
|
1242
|
+
*
|
|
1243
|
+
* // Custom max attempts
|
|
1244
|
+
* console.log(shouldRetry(error, 4, 5)); // true (under custom limit)
|
|
1245
|
+
* ```
|
|
1246
|
+
*/
|
|
1247
|
+
declare const shouldRetry: (error: {
|
|
1248
|
+
readonly category: ErrorCategory;
|
|
1249
|
+
}, attempt: number, maxAttempts?: number) => boolean;
|
|
1250
|
+
import { Result as Result8 } from "better-result";
|
|
1251
|
+
/**
|
|
1252
|
+
* Extract value from Ok, or compute default from error.
|
|
1253
|
+
*
|
|
1254
|
+
* Unlike `unwrapOr`, the default is computed lazily only on Err.
|
|
1255
|
+
* This is useful when the default value is expensive to compute
|
|
1256
|
+
* and should only be evaluated when needed.
|
|
1257
|
+
*
|
|
1258
|
+
* @param result - The Result to unwrap
|
|
1259
|
+
* @param defaultFn - Function to compute default value from error
|
|
1260
|
+
* @returns The success value or computed default
|
|
1261
|
+
*
|
|
1262
|
+
* @example
|
|
1263
|
+
* ```typescript
|
|
1264
|
+
* const result = Result.err("not found");
|
|
1265
|
+
* const value = unwrapOrElse(result, (error) => {
|
|
1266
|
+
* console.log("Computing expensive default due to:", error);
|
|
1267
|
+
* return expensiveComputation();
|
|
1268
|
+
* });
|
|
1269
|
+
* ```
|
|
1270
|
+
*/
|
|
1271
|
+
declare const unwrapOrElse: <
|
|
1272
|
+
T,
|
|
1273
|
+
E
|
|
1274
|
+
>(result: Result8<T, E>, defaultFn: (error: E) => T) => T;
|
|
1275
|
+
/**
|
|
1276
|
+
* Return first Ok, or fallback if first is Err.
|
|
1277
|
+
*
|
|
1278
|
+
* Useful for trying alternative operations - if the first fails,
|
|
1279
|
+
* fall back to an alternative Result.
|
|
1280
|
+
*
|
|
1281
|
+
* @param result - The primary Result to try
|
|
1282
|
+
* @param fallback - The fallback Result if primary is Err
|
|
1283
|
+
* @returns First Ok result, or fallback
|
|
1284
|
+
*
|
|
1285
|
+
* @example
|
|
1286
|
+
* ```typescript
|
|
1287
|
+
* const primary = parseFromCache(key);
|
|
1288
|
+
* const fallback = parseFromNetwork(key);
|
|
1289
|
+
* const result = orElse(primary, fallback);
|
|
1290
|
+
* ```
|
|
1291
|
+
*/
|
|
1292
|
+
declare const orElse: <
|
|
1293
|
+
T,
|
|
1294
|
+
E,
|
|
1295
|
+
F
|
|
1296
|
+
>(result: Result8<T, E>, fallback: Result8<T, F>) => Result8<T, F>;
|
|
1297
|
+
/**
|
|
1298
|
+
* Combine two Results into a tuple Result.
|
|
1299
|
+
*
|
|
1300
|
+
* Returns first error if either fails, evaluated left-to-right.
|
|
1301
|
+
* Useful for combining independent operations that must all succeed.
|
|
1302
|
+
*
|
|
1303
|
+
* @param r1 - First Result
|
|
1304
|
+
* @param r2 - Second Result
|
|
1305
|
+
* @returns Result containing tuple of both values, or first error
|
|
1306
|
+
*
|
|
1307
|
+
* @example
|
|
1308
|
+
* ```typescript
|
|
1309
|
+
* const user = fetchUser(id);
|
|
1310
|
+
* const settings = fetchSettings(id);
|
|
1311
|
+
* const combined = combine2(user, settings);
|
|
1312
|
+
*
|
|
1313
|
+
* if (combined.isOk()) {
|
|
1314
|
+
* const [userData, userSettings] = combined.value;
|
|
1315
|
+
* }
|
|
1316
|
+
* ```
|
|
1317
|
+
*/
|
|
1318
|
+
declare const combine2: <
|
|
1319
|
+
T1,
|
|
1320
|
+
T2,
|
|
1321
|
+
E
|
|
1322
|
+
>(r1: Result8<T1, E>, r2: Result8<T2, E>) => Result8<[T1, T2], E>;
|
|
1323
|
+
/**
|
|
1324
|
+
* Combine three Results into a tuple Result.
|
|
1325
|
+
*
|
|
1326
|
+
* Returns first error if any fails, evaluated left-to-right.
|
|
1327
|
+
* Useful for combining independent operations that must all succeed.
|
|
1328
|
+
*
|
|
1329
|
+
* @param r1 - First Result
|
|
1330
|
+
* @param r2 - Second Result
|
|
1331
|
+
* @param r3 - Third Result
|
|
1332
|
+
* @returns Result containing tuple of all values, or first error
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* const user = fetchUser(id);
|
|
1337
|
+
* const settings = fetchSettings(id);
|
|
1338
|
+
* const permissions = fetchPermissions(id);
|
|
1339
|
+
* const combined = combine3(user, settings, permissions);
|
|
1340
|
+
*
|
|
1341
|
+
* if (combined.isOk()) {
|
|
1342
|
+
* const [userData, userSettings, userPermissions] = combined.value;
|
|
1343
|
+
* }
|
|
1344
|
+
* ```
|
|
1345
|
+
*/
|
|
1346
|
+
declare const combine3: <
|
|
1347
|
+
T1,
|
|
1348
|
+
T2,
|
|
1349
|
+
T3,
|
|
1350
|
+
E
|
|
1351
|
+
>(r1: Result8<T1, E>, r2: Result8<T2, E>, r3: Result8<T3, E>) => Result8<[T1, T2, T3], E>;
|
|
1352
|
+
import { TaggedErrorClass as TaggedErrorClass2 } from "better-result";
|
|
1353
|
+
import { Result as Result9, TaggedError } from "better-result";
|
|
1354
|
+
export { withTimeout, validateInput, unwrapOrElse, toHttpResponse, toEnvelope, statusCodeMap, shouldRetry, serializeError, safeStringify, safeParse, retry, orElse, isRetryable2 as isRetryable, isRecoverable, isNonEmptyArray, getStatusCode, getExitCode, getBackoffDelay, getActionsForSurface, generateRequestId, exitCodeMap, deserializeError, defineAction, createValidator, createRedactor, createContext, createActionRegistry, combine3, combine2, capabilityAll, capability, assertNonEmpty, assertMatches, assertDefined, ValidationError, TimeoutOptions, TimeoutError, TaggedErrorClass2 as TaggedErrorClass, TaggedError, SyncHandler, SuccessEnvelope, StorageError, StorageAdapter, SerializedError, SerializeErrorOptions, SearchResult, SearchOptions, RetryOptions, Result9 as Result, ResolvedConfig, RedactorConfig, Redactor, RedactionEvent, RedactionCallback, RateLimitError, PermissionError, PaginationMeta, OutfitterError, NotFoundError, NonEmptyArray, NetworkError, Logger, KitErrorProps, InternalError, IndexStats, IndexError, IndexAdapter, HttpResponse, HttpMethod, HandlerContext, Handler, ErrorEnvelope, ErrorCategory, EnvelopeMeta, Envelope, DEFAULT_SENSITIVE_KEYS, DEFAULT_REGISTRY_SURFACES, DEFAULT_PATTERNS, DEFAULT_ACTION_SURFACES, CreateContextOptions, ConflictError, CapabilitySurface, CancelledError, CacheError, CacheAdapter, CAPABILITY_SURFACES, BackoffOptions, AuthError, AuthAdapter, AssertionError, AnyKitError, AnyActionSpec, AdapterAuthError, ActionTrpcSpec, ActionSurface, ActionSpec, ActionRegistry, ActionMcpSpec, ActionCliSpec, ActionCliOption, ActionCliInputContext, ActionCapability, ActionApiSpec, ACTION_SURFACES, ACTION_CAPABILITIES };
|