@outfitter/contracts 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +18 -15
  2. package/dist/actions.d.ts +4 -4
  3. package/dist/actions.js +25 -6
  4. package/dist/adapters.d.ts +1 -1
  5. package/dist/assert/index.d.ts +2 -2
  6. package/dist/assert/index.js +28 -6
  7. package/dist/capabilities.d.ts +1 -1
  8. package/dist/capabilities.js +57 -8
  9. package/dist/context.d.ts +4 -4
  10. package/dist/envelope.d.ts +2 -2
  11. package/dist/envelope.js +49 -6
  12. package/dist/errors.d.ts +1 -1
  13. package/dist/handler.d.ts +3 -3
  14. package/dist/index.d.ts +17 -17
  15. package/dist/index.js +15 -153
  16. package/dist/logging.d.ts +1 -1
  17. package/dist/logging.js +11 -3
  18. package/dist/recovery.d.ts +2 -2
  19. package/dist/recovery.js +49 -6
  20. package/dist/redactor.d.ts +1 -1
  21. package/dist/resilience.d.ts +2 -2
  22. package/dist/resilience.js +76 -4
  23. package/dist/result/index.d.ts +2 -2
  24. package/dist/result/index.js +1 -16
  25. package/dist/result/utilities.d.ts +1 -1
  26. package/dist/result/utilities.js +29 -7
  27. package/dist/schema.d.ts +1 -1
  28. package/dist/schema.js +338 -3
  29. package/dist/serialization.d.ts +2 -2
  30. package/dist/shared/@outfitter/{contracts-ss9vjjft.d.ts → contracts-0akf2sm6.d.ts} +10 -10
  31. package/dist/shared/@outfitter/{contracts-25bkj17f.d.ts → contracts-1waabxbk.d.ts} +10 -10
  32. package/dist/shared/@outfitter/{contracts-j08e95jw.d.ts → contracts-31penhwa.d.ts} +1 -1
  33. package/dist/shared/@outfitter/{contracts-sf1z80yc.d.ts → contracts-56pcsavx.d.ts} +17 -17
  34. package/dist/shared/@outfitter/{contracts-bdwg55c5.d.ts → contracts-95cc3y06.d.ts} +13 -13
  35. package/dist/shared/@outfitter/{contracts-6j6z9dsd.d.ts → contracts-9wtm5nsw.d.ts} +1 -1
  36. package/dist/shared/@outfitter/{contracts-jggbn5tn.d.ts → contracts-e4m948m7.d.ts} +10 -10
  37. package/dist/shared/@outfitter/{contracts-evxky148.d.ts → contracts-k71jqd1m.d.ts} +1 -1
  38. package/dist/shared/@outfitter/{contracts-18vcxecr.d.ts → contracts-mmg0npfk.d.ts} +1 -1
  39. package/dist/validation.d.ts +2 -2
  40. package/dist/validation.js +36 -4
  41. package/package.json +26 -25
  42. package/dist/shared/@outfitter/contracts-0snpmkdt.js +0 -40
  43. package/dist/shared/@outfitter/contracts-37gpc56f.js +0 -1
  44. package/dist/shared/@outfitter/contracts-4zaj7ejb.js +0 -52
  45. package/dist/shared/@outfitter/contracts-btg89x4h.js +0 -53
  46. package/dist/shared/@outfitter/contracts-cp5c6dws.js +0 -32
  47. package/dist/shared/@outfitter/contracts-d0tq2adf.js +0 -60
  48. package/dist/shared/@outfitter/contracts-q0v44kef.js +0 -28
  49. package/dist/shared/@outfitter/contracts-r21yet6j.js +0 -80
  50. package/dist/shared/@outfitter/contracts-sm6vak1a.js +0 -14
  51. package/dist/shared/@outfitter/contracts-wfht4q2b.js +0 -341
  52. package/dist/shared/@outfitter/contracts-zx72gyh1.js +0 -32
  53. package/dist/shared/@outfitter/{contracts-r35bn9p6.d.ts → contracts-3gswmhb1.d.ts} +2 -2
  54. package/dist/shared/@outfitter/{contracts-e70qdasg.d.ts → contracts-9yd4vrjg.d.ts} +12 -12
  55. package/dist/shared/@outfitter/{contracts-93dx53mt.d.ts → contracts-c2cfj172.d.ts} +32 -32
  56. package/dist/shared/@outfitter/{contracts-2g8r01zf.d.ts → contracts-rwzqy9rn.d.ts} +10 -10
  57. package/dist/shared/@outfitter/{contracts-hbbxbwkt.d.ts → contracts-t79engf9.d.ts} +21 -21
  58. package/dist/shared/@outfitter/{contracts-bb4hjt8g.d.ts → contracts-y8f0m1ze.d.ts} +1 -1
@@ -1,34 +1,34 @@
1
- import { OutfitterError, TimeoutError } from "./contracts-r35bn9p6";
1
+ import { OutfitterError, TimeoutError } from "./contracts-3gswmhb1.js";
2
2
  import { Result } from "better-result";
3
3
  /**
4
4
  * Options for retry behavior.
5
5
  */
6
6
  interface RetryOptions {
7
- /** Maximum number of retry attempts (default: 3) */
8
- maxAttempts?: number;
9
- /** Initial delay in milliseconds (default: 1000) */
10
- initialDelayMs?: number;
11
- /** Maximum delay in milliseconds (default: 30000) */
12
- maxDelayMs?: number;
13
7
  /** Exponential backoff multiplier (default: 2) */
14
8
  backoffMultiplier?: number;
15
- /** Whether to add jitter to delays (default: true) */
16
- jitter?: boolean;
9
+ /** Initial delay in milliseconds (default: 1000) */
10
+ initialDelayMs?: number;
17
11
  /** Predicate to determine if error is retryable */
18
12
  isRetryable?: (error: OutfitterError) => boolean;
19
- /** Abort signal for cancellation */
20
- signal?: AbortSignal;
13
+ /** Whether to add jitter to delays (default: true) */
14
+ jitter?: boolean;
15
+ /** Maximum number of retry attempts (default: 3) */
16
+ maxAttempts?: number;
17
+ /** Maximum delay in milliseconds (default: 30000) */
18
+ maxDelayMs?: number;
21
19
  /** Callback invoked before each retry */
22
20
  onRetry?: (attempt: number, error: OutfitterError, delayMs: number) => void;
21
+ /** Abort signal for cancellation */
22
+ signal?: AbortSignal;
23
23
  }
24
24
  /**
25
25
  * Options for timeout behavior.
26
26
  */
27
27
  interface TimeoutOptions {
28
- /** Timeout duration in milliseconds */
29
- timeoutMs: number;
30
28
  /** Operation name for error context */
31
29
  operation?: string;
30
+ /** Timeout duration in milliseconds */
31
+ timeoutMs: number;
32
32
  }
33
33
  /**
34
34
  * Retry an async operation with exponential backoff.
@@ -1,4 +1,4 @@
1
- import { ValidationError } from "./contracts-r35bn9p6";
1
+ import { ValidationError } from "./contracts-3gswmhb1.js";
2
2
  import { Result } from "better-result";
3
3
  import { z } from "zod";
4
4
  /**
@@ -1,45 +1,45 @@
1
- import { OutfitterError, SerializedError } from "./contracts-r35bn9p6";
1
+ import { OutfitterError, SerializedError } from "./contracts-3gswmhb1.js";
2
2
  import { Result } from "better-result";
3
3
  /**
4
4
  * Metadata attached to every response envelope.
5
5
  */
6
6
  interface EnvelopeMeta {
7
+ /** Operation duration in milliseconds */
8
+ durationMs?: number;
7
9
  /** Unique request identifier for tracing */
8
10
  requestId: string;
9
11
  /** ISO timestamp of response generation */
10
12
  timestamp: string;
11
- /** Operation duration in milliseconds */
12
- durationMs?: number;
13
13
  }
14
14
  /**
15
15
  * Pagination metadata for list responses.
16
16
  */
17
17
  interface PaginationMeta {
18
- /** Total number of items (if known) */
19
- total?: number;
20
18
  /** Number of items returned */
21
19
  count: number;
22
- /** Cursor for next page (null if no more pages) */
23
- nextCursor: string | null;
24
20
  /** Whether more pages exist */
25
21
  hasMore: boolean;
22
+ /** Cursor for next page (null if no more pages) */
23
+ nextCursor: string | null;
24
+ /** Total number of items (if known) */
25
+ total?: number;
26
26
  }
27
27
  /**
28
28
  * Success envelope structure.
29
29
  */
30
30
  interface SuccessEnvelope<T> {
31
- ok: true;
32
31
  data: T;
33
32
  meta: EnvelopeMeta;
33
+ ok: true;
34
34
  pagination?: PaginationMeta;
35
35
  }
36
36
  /**
37
37
  * Error envelope structure.
38
38
  */
39
39
  interface ErrorEnvelope {
40
- ok: false;
41
40
  error: SerializedError;
42
41
  meta: EnvelopeMeta;
42
+ ok: false;
43
43
  }
44
44
  /**
45
45
  * Response envelope - consistent wrapper for all handler responses.
@@ -49,8 +49,8 @@ type Envelope<T> = SuccessEnvelope<T> | ErrorEnvelope;
49
49
  * HTTP-style response with status code.
50
50
  */
51
51
  interface HttpResponse<T> {
52
- status: number;
53
52
  body: Envelope<T>;
53
+ status: number;
54
54
  }
55
55
  /**
56
56
  * Convert a Result to a response envelope.
@@ -1,4 +1,4 @@
1
- import { ErrorCategory } from "./contracts-r35bn9p6";
1
+ import { ErrorCategory } from "./contracts-3gswmhb1.js";
2
2
  /**
3
3
  * Backoff strategy configuration options
4
4
  */
@@ -1,4 +1,4 @@
1
- import { AssertionError } from "./contracts-r35bn9p6";
1
+ import { AssertionError } from "./contracts-3gswmhb1.js";
2
2
  import { Result } from "better-result";
3
3
  /**
4
4
  * Array type guaranteed to have at least one element.
@@ -1,3 +1,3 @@
1
- import { createValidator, validateInput } from "./shared/@outfitter/contracts-6j6z9dsd";
2
- import "./shared/@outfitter/contracts-r35bn9p6";
1
+ import { createValidator, validateInput } from "./shared/@outfitter/contracts-9wtm5nsw.js";
2
+ import "./shared/@outfitter/contracts-3gswmhb1.js";
3
3
  export { validateInput, createValidator };
@@ -1,9 +1,41 @@
1
1
  // @bun
2
2
  import {
3
- createValidator,
4
- validateInput
5
- } from "./shared/@outfitter/contracts-0snpmkdt.js";
6
- import"./shared/@outfitter/contracts-phjhz5q3.js";
3
+ ValidationError
4
+ } from "./shared/@outfitter/contracts-phjhz5q3.js";
5
+
6
+ // packages/contracts/src/validation.ts
7
+ import { Result } from "better-result";
8
+ function formatZodIssues(issues) {
9
+ return issues.map((issue) => {
10
+ const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
11
+ return `${path}: ${issue.message}`;
12
+ }).join("; ");
13
+ }
14
+ function extractField(issues) {
15
+ const firstIssue = issues[0];
16
+ if (firstIssue && firstIssue.path.length > 0) {
17
+ return firstIssue.path.join(".");
18
+ }
19
+ return;
20
+ }
21
+ function createValidator(schema) {
22
+ return (input) => {
23
+ return validateInput(schema, input);
24
+ };
25
+ }
26
+ function validateInput(schema, input) {
27
+ const parseResult = schema.safeParse(input);
28
+ if (parseResult.success) {
29
+ return Result.ok(parseResult.data);
30
+ }
31
+ const message = formatZodIssues(parseResult.error.issues);
32
+ const field = extractField(parseResult.error.issues);
33
+ const errorProps = { message };
34
+ if (field !== undefined) {
35
+ errorProps.field = field;
36
+ }
37
+ return Result.err(new ValidationError(errorProps));
38
+ }
7
39
  export {
8
40
  validateInput,
9
41
  createValidator
package/package.json CHANGED
@@ -1,11 +1,25 @@
1
1
  {
2
2
  "name": "@outfitter/contracts",
3
+ "version": "0.4.2",
3
4
  "description": "Result/Error patterns, error taxonomy, and handler contracts for Outfitter",
4
- "version": "0.4.0",
5
- "type": "module",
5
+ "keywords": [
6
+ "contracts",
7
+ "errors",
8
+ "outfitter",
9
+ "result",
10
+ "typescript"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/outfitter-dev/outfitter.git",
16
+ "directory": "packages/contracts"
17
+ },
6
18
  "files": [
7
19
  "dist"
8
20
  ],
21
+ "type": "module",
22
+ "sideEffects": false,
9
23
  "module": "./dist/index.js",
10
24
  "types": "./dist/index.d.ts",
11
25
  "exports": {
@@ -119,37 +133,24 @@
119
133
  }
120
134
  }
121
135
  },
122
- "sideEffects": false,
136
+ "publishConfig": {
137
+ "access": "public"
138
+ },
123
139
  "scripts": {
124
140
  "build": "cd ../.. && bunup --filter @outfitter/contracts",
125
- "lint": "biome lint ./src",
126
- "lint:fix": "biome lint --write ./src",
141
+ "lint": "oxlint ./src",
142
+ "lint:fix": "oxlint --fix ./src",
127
143
  "test": "bun test",
128
144
  "typecheck": "tsc --noEmit",
129
- "clean": "rm -rf dist"
145
+ "clean": "rm -rf dist",
146
+ "prepublishOnly": "bun ../../scripts/check-publish-manifest.ts"
130
147
  },
131
148
  "dependencies": {
132
- "better-result": "^2.5.0",
149
+ "better-result": "^2.5.1",
133
150
  "zod": "^4.3.5"
134
151
  },
135
152
  "devDependencies": {
136
- "@types/bun": "latest",
137
- "typescript": "^5.8.0"
138
- },
139
- "keywords": [
140
- "outfitter",
141
- "contracts",
142
- "errors",
143
- "result",
144
- "typescript"
145
- ],
146
- "license": "MIT",
147
- "repository": {
148
- "type": "git",
149
- "url": "https://github.com/outfitter-dev/outfitter.git",
150
- "directory": "packages/contracts"
151
- },
152
- "publishConfig": {
153
- "access": "public"
153
+ "@types/bun": "^1.3.9",
154
+ "typescript": "^5.9.3"
154
155
  }
155
156
  }
@@ -1,40 +0,0 @@
1
- // @bun
2
- import {
3
- ValidationError
4
- } from "./contracts-phjhz5q3.js";
5
-
6
- // packages/contracts/src/validation.ts
7
- import { Result } from "better-result";
8
- function formatZodIssues(issues) {
9
- return issues.map((issue) => {
10
- const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
11
- return `${path}: ${issue.message}`;
12
- }).join("; ");
13
- }
14
- function extractField(issues) {
15
- const firstIssue = issues[0];
16
- if (firstIssue && firstIssue.path.length > 0) {
17
- return firstIssue.path.join(".");
18
- }
19
- return;
20
- }
21
- function createValidator(schema) {
22
- return (input) => {
23
- return validateInput(schema, input);
24
- };
25
- }
26
- function validateInput(schema, input) {
27
- const parseResult = schema.safeParse(input);
28
- if (parseResult.success) {
29
- return Result.ok(parseResult.data);
30
- }
31
- const message = formatZodIssues(parseResult.error.issues);
32
- const field = extractField(parseResult.error.issues);
33
- const errorProps = { message };
34
- if (field !== undefined) {
35
- errorProps.field = field;
36
- }
37
- return Result.err(new ValidationError(errorProps));
38
- }
39
-
40
- export { createValidator, validateInput };
@@ -1 +0,0 @@
1
- // @bun
@@ -1,52 +0,0 @@
1
- // @bun
2
- // packages/contracts/src/recovery.ts
3
- var RECOVERABLE_CATEGORIES = [
4
- "network",
5
- "timeout",
6
- "rate_limit",
7
- "conflict"
8
- ];
9
- var RETRYABLE_CATEGORIES = ["network", "timeout"];
10
- var isRecoverable = (error) => {
11
- return RECOVERABLE_CATEGORIES.includes(error.category);
12
- };
13
- var isRetryable = (error) => {
14
- return RETRYABLE_CATEGORIES.includes(error.category);
15
- };
16
- var getBackoffDelay = (attempt, options = {}) => {
17
- const {
18
- baseDelayMs = 100,
19
- maxDelayMs = 30000,
20
- strategy = "exponential",
21
- useJitter = true
22
- } = options;
23
- let delay;
24
- switch (strategy) {
25
- case "constant": {
26
- delay = baseDelayMs;
27
- break;
28
- }
29
- case "linear": {
30
- delay = baseDelayMs * (attempt + 1);
31
- break;
32
- }
33
- default: {
34
- delay = baseDelayMs * 2 ** attempt;
35
- }
36
- }
37
- delay = Math.min(delay, maxDelayMs);
38
- if (useJitter) {
39
- const jitterFactor = 0.1;
40
- const jitter = delay * jitterFactor * (Math.random() * 2 - 1);
41
- delay = Math.round(delay + jitter);
42
- }
43
- return Math.min(delay, maxDelayMs);
44
- };
45
- var shouldRetry = (error, attempt, maxAttempts = 3) => {
46
- if (attempt >= maxAttempts) {
47
- return false;
48
- }
49
- return isRetryable(error);
50
- };
51
-
52
- export { isRecoverable, isRetryable, getBackoffDelay, shouldRetry };
@@ -1,53 +0,0 @@
1
- // @bun
2
- import {
3
- serializeError
4
- } from "./contracts-5k6q4n48.js";
5
- import {
6
- generateRequestId
7
- } from "./contracts-agmt8915.js";
8
- import {
9
- statusCodeMap
10
- } from "./contracts-phjhz5q3.js";
11
-
12
- // packages/contracts/src/envelope.ts
13
- function buildMeta(overrides) {
14
- const meta = {
15
- requestId: overrides?.requestId ?? generateRequestId(),
16
- timestamp: new Date().toISOString()
17
- };
18
- if (overrides?.durationMs !== undefined) {
19
- meta.durationMs = overrides.durationMs;
20
- }
21
- return meta;
22
- }
23
- function toEnvelope(result, meta) {
24
- const envelopeMeta = buildMeta(meta);
25
- if (result.isOk()) {
26
- return {
27
- ok: true,
28
- data: result.value,
29
- meta: envelopeMeta
30
- };
31
- }
32
- return {
33
- ok: false,
34
- error: serializeError(result.error),
35
- meta: envelopeMeta
36
- };
37
- }
38
- function toHttpResponse(result) {
39
- const envelope = toEnvelope(result);
40
- if (envelope.ok) {
41
- return {
42
- status: 200,
43
- body: envelope
44
- };
45
- }
46
- const status = statusCodeMap[envelope.error.category];
47
- return {
48
- status,
49
- body: envelope
50
- };
51
- }
52
-
53
- export { toEnvelope, toHttpResponse };
@@ -1,32 +0,0 @@
1
- // @bun
2
- import {
3
- AssertionError
4
- } from "./contracts-phjhz5q3.js";
5
-
6
- // packages/contracts/src/assert/index.ts
7
- import { Result } from "better-result";
8
- var isNonEmptyArray = (arr) => {
9
- return arr.length > 0;
10
- };
11
- var assertDefined = (value, message) => {
12
- if (value === null || value === undefined) {
13
- return Result.err(new AssertionError({ message: message ?? "Value is null or undefined" }));
14
- }
15
- return Result.ok(value);
16
- };
17
- var assertNonEmpty = (arr, message) => {
18
- if (arr.length === 0) {
19
- return Result.err(new AssertionError({ message: message ?? "Array is empty" }));
20
- }
21
- return Result.ok(arr);
22
- };
23
- function assertMatches(value, predicate, message) {
24
- if (!predicate(value)) {
25
- return Result.err(new AssertionError({
26
- message: message ?? "Value does not match predicate"
27
- }));
28
- }
29
- return Result.ok(value);
30
- }
31
-
32
- export { isNonEmptyArray, assertDefined, assertNonEmpty, assertMatches };
@@ -1,60 +0,0 @@
1
- // @bun
2
- // packages/contracts/src/capabilities.ts
3
- var CAPABILITY_SURFACES = ["cli", "mcp", "api", "server"];
4
- var DEFAULT_ACTION_SURFACES = ["cli", "mcp"];
5
- function capability(surfaces = DEFAULT_ACTION_SURFACES, notes) {
6
- return notes ? { surfaces, notes } : { surfaces };
7
- }
8
- function capabilityAll(notes) {
9
- return capability(CAPABILITY_SURFACES, notes);
10
- }
11
- var ACTION_CAPABILITIES = {
12
- navigate: capability(),
13
- back: capability(),
14
- forward: capability(),
15
- reload: capability(),
16
- tab: capability(),
17
- tabs: capability(),
18
- newTab: capability(),
19
- closeTab: capability(),
20
- click: capability(),
21
- type: capability(),
22
- select: capability(),
23
- hover: capability(),
24
- focus: capability(["mcp"]),
25
- scroll: capability(),
26
- press: capability(),
27
- fill: capability(),
28
- find: capability(),
29
- check: capability(),
30
- uncheck: capability(),
31
- upload: capability(),
32
- download: capability(["server"], "Server-only for now"),
33
- dialog: capability(),
34
- waitFor: capability(["mcp"]),
35
- waitForNavigation: capability(["mcp"]),
36
- wait: capability(["mcp"]),
37
- snap: capability(),
38
- screenshot: capability(),
39
- html: capability(["mcp"]),
40
- text: capability(["mcp"]),
41
- marker: capability(),
42
- markers: capability(),
43
- markerGet: capability(),
44
- markerRead: capability(["mcp"]),
45
- markerCompare: capability(),
46
- markerDelete: capability(),
47
- markerResolve: capability(["cli"], "CLI-only for now"),
48
- viewport: capability(),
49
- colorScheme: capability(),
50
- mode: capability(["mcp"]),
51
- evaluate: capability(["mcp"]),
52
- session: capability(),
53
- sessions: capability(["mcp"]),
54
- steps: capability()
55
- };
56
- function getActionsForSurface(surface) {
57
- return Object.entries(ACTION_CAPABILITIES).filter(([, capability2]) => capability2.surfaces.includes(surface)).map(([action]) => action);
58
- }
59
-
60
- export { CAPABILITY_SURFACES, DEFAULT_ACTION_SURFACES, capability, capabilityAll, ACTION_CAPABILITIES, getActionsForSurface };
@@ -1,28 +0,0 @@
1
- // @bun
2
- // packages/contracts/src/actions.ts
3
- var ACTION_SURFACES = ["cli", "mcp", "api", "server"];
4
- var DEFAULT_REGISTRY_SURFACES = ACTION_SURFACES;
5
- function defineAction(action) {
6
- return action;
7
- }
8
- function createActionRegistry() {
9
- const actions = new Map;
10
- return {
11
- add(action) {
12
- actions.set(action.id, action);
13
- return this;
14
- },
15
- list() {
16
- return Array.from(actions.values());
17
- },
18
- get(id) {
19
- return actions.get(id);
20
- },
21
- forSurface(surface) {
22
- const defaults = DEFAULT_REGISTRY_SURFACES;
23
- return Array.from(actions.values()).filter((action) => (action.surfaces ?? defaults).includes(surface));
24
- }
25
- };
26
- }
27
-
28
- export { ACTION_SURFACES, DEFAULT_REGISTRY_SURFACES, defineAction, createActionRegistry };
@@ -1,80 +0,0 @@
1
- // @bun
2
- import {
3
- TimeoutError
4
- } from "./contracts-phjhz5q3.js";
5
-
6
- // packages/contracts/src/resilience.ts
7
- import { Result } from "better-result";
8
- function defaultIsRetryable(error) {
9
- return error.category === "network" || error.category === "timeout" || error.category === "rate_limit";
10
- }
11
- function calculateDelay(attempt, initialDelayMs, maxDelayMs, backoffMultiplier, jitter) {
12
- const baseDelay = initialDelayMs * backoffMultiplier ** (attempt - 1);
13
- const cappedDelay = Math.min(baseDelay, maxDelayMs);
14
- if (jitter) {
15
- const jitterFactor = 0.5 + Math.random();
16
- return Math.floor(cappedDelay * jitterFactor);
17
- }
18
- return cappedDelay;
19
- }
20
- function sleep(ms) {
21
- return new Promise((resolve) => setTimeout(resolve, ms));
22
- }
23
- async function retry(fn, options) {
24
- const maxAttempts = options?.maxAttempts ?? 3;
25
- const initialDelayMs = options?.initialDelayMs ?? 1000;
26
- const maxDelayMs = options?.maxDelayMs ?? 30000;
27
- const backoffMultiplier = options?.backoffMultiplier ?? 2;
28
- const jitter = options?.jitter ?? true;
29
- const isRetryable = options?.isRetryable ?? defaultIsRetryable;
30
- const onRetry = options?.onRetry;
31
- const signal = options?.signal;
32
- let lastError;
33
- let attempt = 0;
34
- while (attempt < maxAttempts) {
35
- attempt++;
36
- if (signal?.aborted) {
37
- return Result.err(lastError ?? new TimeoutError({
38
- message: "Operation cancelled",
39
- operation: "retry",
40
- timeoutMs: 0
41
- }));
42
- }
43
- const result = await fn();
44
- if (result.isOk()) {
45
- return result;
46
- }
47
- lastError = result.error;
48
- if (attempt >= maxAttempts || !isRetryable(lastError)) {
49
- return result;
50
- }
51
- const delayMs = calculateDelay(attempt, initialDelayMs, maxDelayMs, backoffMultiplier, jitter);
52
- if (onRetry) {
53
- onRetry(attempt, lastError, delayMs);
54
- }
55
- await sleep(delayMs);
56
- }
57
- throw new Error("Unexpected: retry loop completed without returning a result");
58
- }
59
- async function withTimeout(fn, options) {
60
- const { timeoutMs, operation = "operation" } = options;
61
- let timeoutId;
62
- const timeoutPromise = new Promise((resolve) => {
63
- timeoutId = setTimeout(() => {
64
- resolve(Result.err(new TimeoutError({
65
- message: `${operation} timed out after ${timeoutMs}ms`,
66
- operation,
67
- timeoutMs
68
- })));
69
- }, timeoutMs);
70
- });
71
- try {
72
- return await Promise.race([fn(), timeoutPromise]);
73
- } finally {
74
- if (timeoutId !== undefined) {
75
- clearTimeout(timeoutId);
76
- }
77
- }
78
- }
79
-
80
- export { retry, withTimeout };
@@ -1,14 +0,0 @@
1
- // @bun
2
- // packages/contracts/src/logging.ts
3
- function createLoggerFactory(adapter) {
4
- return {
5
- createLogger(config) {
6
- return adapter.createLogger(config);
7
- },
8
- async flush() {
9
- await adapter.flush?.();
10
- }
11
- };
12
- }
13
-
14
- export { createLoggerFactory };