@chronos.sh/sdk 0.0.2-canary.fa8e22c → 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.
- package/dist/index.cjs +116 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -14
- package/dist/index.d.ts +59 -14
- package/dist/index.js +115 -51
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.cts
CHANGED
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
type ChronosHandlerResult = Record<string, unknown> | void;
|
|
7
7
|
/**
|
|
8
8
|
* Custom fetch implementation. Compatible with `globalThis.fetch`. Pass via
|
|
9
|
-
* {@link ChronosOptions.fetch} to inject middleware (logging, tracing
|
|
10
|
-
*
|
|
9
|
+
* {@link ChronosOptions.fetch} to inject middleware (logging, tracing) or run
|
|
10
|
+
* in environments without a global `fetch`. Implementations must forward
|
|
11
|
+
* `init.signal` to the underlying transport — dropping it disables the SDK's
|
|
12
|
+
* transport timeout and graceful abort.
|
|
11
13
|
*/
|
|
12
14
|
type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
13
15
|
/**
|
|
@@ -39,6 +41,15 @@ type ChronosOptions = {
|
|
|
39
41
|
* 20 inclusive. Defaults to 20 (the API maximum).
|
|
40
42
|
*/
|
|
41
43
|
pollWaitTimeSeconds?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Per-request transport timeout in milliseconds, applied to every HTTP
|
|
46
|
+
* request the SDK makes. Requests exceeding it fail with
|
|
47
|
+
* `ChronosTimeoutError`. Long-poll claim requests get
|
|
48
|
+
* `pollWaitTimeSeconds * 1000` of extra headroom on top, so the
|
|
49
|
+
* intentional poll wait never counts against this bound. Must be a
|
|
50
|
+
* positive integer no greater than 2147483647. Defaults to 30000.
|
|
51
|
+
*/
|
|
52
|
+
timeoutMs?: number;
|
|
42
53
|
/**
|
|
43
54
|
* Worker retry delay in milliseconds. Used between failed result-report
|
|
44
55
|
* attempts and between poll-loop iterations after a claim error. Defaults
|
|
@@ -100,14 +111,21 @@ type WebhookOptions = {
|
|
|
100
111
|
//#region src/client.d.ts
|
|
101
112
|
/** Default Chronos API base URL. */
|
|
102
113
|
declare const DEFAULT_BASE_URL = "https://api.chronos.sh";
|
|
114
|
+
/** Default per-request transport timeout in milliseconds. */
|
|
115
|
+
declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
116
|
+
type RequestOptions = {
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
timeoutMs?: number;
|
|
119
|
+
};
|
|
103
120
|
declare class BaseClient {
|
|
104
121
|
readonly apiKey: string;
|
|
105
122
|
readonly baseUrl: string;
|
|
106
123
|
readonly fetch: FetchLike;
|
|
107
124
|
readonly logger: ChronosLogger;
|
|
125
|
+
readonly timeoutMs: number;
|
|
108
126
|
private readonly headers;
|
|
109
127
|
constructor(options: ChronosOptions);
|
|
110
|
-
request<T>(path: string, body: Record<string, unknown>,
|
|
128
|
+
request<T>(path: string, body: Record<string, unknown>, options?: RequestOptions): Promise<T>;
|
|
111
129
|
}
|
|
112
130
|
//#endregion
|
|
113
131
|
//#region src/worker.d.ts
|
|
@@ -162,8 +180,11 @@ declare class Worker {
|
|
|
162
180
|
* in-flight handler that has not exceeded its timeout, along with the
|
|
163
181
|
* subsequent result-report, is allowed to complete. Timed-out handlers
|
|
164
182
|
* are abandoned (their {@link ChronosContext.signal} fires, but the SDK
|
|
165
|
-
* does not wait for them).
|
|
166
|
-
*
|
|
183
|
+
* does not wait for them). Every HTTP request is bounded by the `timeoutMs`
|
|
184
|
+
* transport timeout and retry waits are skipped once stop is requested, so
|
|
185
|
+
* the returned promise resolves in bounded time. Returns the same promise
|
|
186
|
+
* as the active {@link Worker.start}, or a resolved promise if the worker
|
|
187
|
+
* isn't running.
|
|
167
188
|
*/
|
|
168
189
|
stop(): Promise<void>;
|
|
169
190
|
private get isStopped();
|
|
@@ -238,7 +259,7 @@ type ChronosApiErrorOptions = {
|
|
|
238
259
|
/** HTTP status code returned by the Chronos API. */status: number; /** Application-level error code from the response envelope, when present. */
|
|
239
260
|
code?: string; /** Full parsed response payload (envelope + data, or whatever the server returned). */
|
|
240
261
|
body?: unknown; /** Value of the `X-Request-Id` response header, when present. Pair with server logs. */
|
|
241
|
-
requestId?: string; /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */
|
|
262
|
+
requestId?: string; /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 and 5xx responses that include the header. */
|
|
242
263
|
retryAfterSeconds?: number; /** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */
|
|
243
264
|
rateLimit?: RateLimitInfo;
|
|
244
265
|
};
|
|
@@ -247,7 +268,7 @@ type ChronosApiErrorOptions = {
|
|
|
247
268
|
* `success: false` envelope. Carries HTTP `status`, the application
|
|
248
269
|
* `code`, parsed `body`, the API's `X-Request-Id`, and — when the
|
|
249
270
|
* relevant headers are present — rate-limit metadata
|
|
250
|
-
* (`retryAfterSeconds` on 429, `rateLimit` on
|
|
271
|
+
* (`retryAfterSeconds` on 429 and 5xx, `rateLimit` on non-429 errors).
|
|
251
272
|
*
|
|
252
273
|
* `instanceof ChronosApiError` means the server replied;
|
|
253
274
|
* network/transport failures throw {@link ChronosNetworkError} instead.
|
|
@@ -274,7 +295,7 @@ declare class ChronosApiError extends ChronosError {
|
|
|
274
295
|
readonly body?: unknown;
|
|
275
296
|
/** Value of the `X-Request-Id` response header, when present. */
|
|
276
297
|
readonly requestId?: string;
|
|
277
|
-
/** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */
|
|
298
|
+
/** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 and 5xx responses that include the header. */
|
|
278
299
|
readonly retryAfterSeconds?: number;
|
|
279
300
|
/** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */
|
|
280
301
|
readonly rateLimit?: RateLimitInfo;
|
|
@@ -301,12 +322,14 @@ declare class ChronosRateLimitError extends ChronosApiError {
|
|
|
301
322
|
constructor(message: string, options: ChronosApiErrorOptions);
|
|
302
323
|
}
|
|
303
324
|
/**
|
|
304
|
-
* Thrown when
|
|
305
|
-
*
|
|
306
|
-
* available on `.cause`.
|
|
325
|
+
* Thrown when a request fails at the transport layer — DNS failure, TCP
|
|
326
|
+
* reset, connection refused, or a connection dropped mid-response. The
|
|
327
|
+
* original error is available on `.cause`.
|
|
307
328
|
*
|
|
308
|
-
*
|
|
309
|
-
* always means a
|
|
329
|
+
* Caller-supplied abort signals propagate unwrapped — `instanceof
|
|
330
|
+
* ChronosNetworkError` always means a transport failure, not a graceful
|
|
331
|
+
* shutdown. The SDK's own transport timeout throws the
|
|
332
|
+
* {@link ChronosTimeoutError} subclass.
|
|
310
333
|
*
|
|
311
334
|
* @example
|
|
312
335
|
* ```ts
|
|
@@ -326,6 +349,28 @@ declare class ChronosNetworkError extends ChronosError {
|
|
|
326
349
|
cause: unknown;
|
|
327
350
|
});
|
|
328
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Thrown when an HTTP request exceeds the configured `timeoutMs` before the
|
|
354
|
+
* response completes. Subclass of {@link ChronosNetworkError}, so generic
|
|
355
|
+
* transport-failure handling catches it; the underlying rejection is on
|
|
356
|
+
* `.cause`.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* import { ChronosTimeoutError } from '@chronos.sh/sdk';
|
|
361
|
+
*
|
|
362
|
+
* function handleSdkError(err: unknown) {
|
|
363
|
+
* if (err instanceof ChronosTimeoutError) {
|
|
364
|
+
* console.warn('Chronos request timed out', err.message);
|
|
365
|
+
* }
|
|
366
|
+
* }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
declare class ChronosTimeoutError extends ChronosNetworkError {
|
|
370
|
+
constructor(message: string, options: {
|
|
371
|
+
cause: unknown;
|
|
372
|
+
});
|
|
373
|
+
}
|
|
329
374
|
/**
|
|
330
375
|
* Wraps an exception thrown by a user-supplied {@link ChronosHandler}. The
|
|
331
376
|
* original error is on `.cause`; `.message` is copied from the original so
|
|
@@ -443,5 +488,5 @@ declare class Chronos {
|
|
|
443
488
|
constructor(options: ChronosOptions);
|
|
444
489
|
}
|
|
445
490
|
//#endregion
|
|
446
|
-
export { Chronos, ChronosApiError, type ChronosApiErrorOptions, ChronosConfigError, type ChronosContext, ChronosError, type ChronosHandler, ChronosHandlerError, type ChronosHandlerResult, type ChronosLogger, ChronosNetworkError, type ChronosOptions, ChronosRateLimitError, type ChronosSchedule, ChronosWebhookVerificationError, DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, type FetchLike, type RateLimitInfo, SDK_VERSION, Webhook, type WebhookOptions };
|
|
491
|
+
export { Chronos, ChronosApiError, type ChronosApiErrorOptions, ChronosConfigError, type ChronosContext, ChronosError, type ChronosHandler, ChronosHandlerError, type ChronosHandlerResult, type ChronosLogger, ChronosNetworkError, type ChronosOptions, ChronosRateLimitError, type ChronosSchedule, ChronosTimeoutError, ChronosWebhookVerificationError, DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS, DEFAULT_TIMEOUT_MS, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, type FetchLike, type RateLimitInfo, SDK_VERSION, Webhook, type WebhookOptions };
|
|
447
492
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
type ChronosHandlerResult = Record<string, unknown> | void;
|
|
7
7
|
/**
|
|
8
8
|
* Custom fetch implementation. Compatible with `globalThis.fetch`. Pass via
|
|
9
|
-
* {@link ChronosOptions.fetch} to inject middleware (logging, tracing
|
|
10
|
-
*
|
|
9
|
+
* {@link ChronosOptions.fetch} to inject middleware (logging, tracing) or run
|
|
10
|
+
* in environments without a global `fetch`. Implementations must forward
|
|
11
|
+
* `init.signal` to the underlying transport — dropping it disables the SDK's
|
|
12
|
+
* transport timeout and graceful abort.
|
|
11
13
|
*/
|
|
12
14
|
type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
13
15
|
/**
|
|
@@ -39,6 +41,15 @@ type ChronosOptions = {
|
|
|
39
41
|
* 20 inclusive. Defaults to 20 (the API maximum).
|
|
40
42
|
*/
|
|
41
43
|
pollWaitTimeSeconds?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Per-request transport timeout in milliseconds, applied to every HTTP
|
|
46
|
+
* request the SDK makes. Requests exceeding it fail with
|
|
47
|
+
* `ChronosTimeoutError`. Long-poll claim requests get
|
|
48
|
+
* `pollWaitTimeSeconds * 1000` of extra headroom on top, so the
|
|
49
|
+
* intentional poll wait never counts against this bound. Must be a
|
|
50
|
+
* positive integer no greater than 2147483647. Defaults to 30000.
|
|
51
|
+
*/
|
|
52
|
+
timeoutMs?: number;
|
|
42
53
|
/**
|
|
43
54
|
* Worker retry delay in milliseconds. Used between failed result-report
|
|
44
55
|
* attempts and between poll-loop iterations after a claim error. Defaults
|
|
@@ -100,14 +111,21 @@ type WebhookOptions = {
|
|
|
100
111
|
//#region src/client.d.ts
|
|
101
112
|
/** Default Chronos API base URL. */
|
|
102
113
|
declare const DEFAULT_BASE_URL = "https://api.chronos.sh";
|
|
114
|
+
/** Default per-request transport timeout in milliseconds. */
|
|
115
|
+
declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
116
|
+
type RequestOptions = {
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
timeoutMs?: number;
|
|
119
|
+
};
|
|
103
120
|
declare class BaseClient {
|
|
104
121
|
readonly apiKey: string;
|
|
105
122
|
readonly baseUrl: string;
|
|
106
123
|
readonly fetch: FetchLike;
|
|
107
124
|
readonly logger: ChronosLogger;
|
|
125
|
+
readonly timeoutMs: number;
|
|
108
126
|
private readonly headers;
|
|
109
127
|
constructor(options: ChronosOptions);
|
|
110
|
-
request<T>(path: string, body: Record<string, unknown>,
|
|
128
|
+
request<T>(path: string, body: Record<string, unknown>, options?: RequestOptions): Promise<T>;
|
|
111
129
|
}
|
|
112
130
|
//#endregion
|
|
113
131
|
//#region src/worker.d.ts
|
|
@@ -162,8 +180,11 @@ declare class Worker {
|
|
|
162
180
|
* in-flight handler that has not exceeded its timeout, along with the
|
|
163
181
|
* subsequent result-report, is allowed to complete. Timed-out handlers
|
|
164
182
|
* are abandoned (their {@link ChronosContext.signal} fires, but the SDK
|
|
165
|
-
* does not wait for them).
|
|
166
|
-
*
|
|
183
|
+
* does not wait for them). Every HTTP request is bounded by the `timeoutMs`
|
|
184
|
+
* transport timeout and retry waits are skipped once stop is requested, so
|
|
185
|
+
* the returned promise resolves in bounded time. Returns the same promise
|
|
186
|
+
* as the active {@link Worker.start}, or a resolved promise if the worker
|
|
187
|
+
* isn't running.
|
|
167
188
|
*/
|
|
168
189
|
stop(): Promise<void>;
|
|
169
190
|
private get isStopped();
|
|
@@ -238,7 +259,7 @@ type ChronosApiErrorOptions = {
|
|
|
238
259
|
/** HTTP status code returned by the Chronos API. */status: number; /** Application-level error code from the response envelope, when present. */
|
|
239
260
|
code?: string; /** Full parsed response payload (envelope + data, or whatever the server returned). */
|
|
240
261
|
body?: unknown; /** Value of the `X-Request-Id` response header, when present. Pair with server logs. */
|
|
241
|
-
requestId?: string; /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */
|
|
262
|
+
requestId?: string; /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 and 5xx responses that include the header. */
|
|
242
263
|
retryAfterSeconds?: number; /** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */
|
|
243
264
|
rateLimit?: RateLimitInfo;
|
|
244
265
|
};
|
|
@@ -247,7 +268,7 @@ type ChronosApiErrorOptions = {
|
|
|
247
268
|
* `success: false` envelope. Carries HTTP `status`, the application
|
|
248
269
|
* `code`, parsed `body`, the API's `X-Request-Id`, and — when the
|
|
249
270
|
* relevant headers are present — rate-limit metadata
|
|
250
|
-
* (`retryAfterSeconds` on 429, `rateLimit` on
|
|
271
|
+
* (`retryAfterSeconds` on 429 and 5xx, `rateLimit` on non-429 errors).
|
|
251
272
|
*
|
|
252
273
|
* `instanceof ChronosApiError` means the server replied;
|
|
253
274
|
* network/transport failures throw {@link ChronosNetworkError} instead.
|
|
@@ -274,7 +295,7 @@ declare class ChronosApiError extends ChronosError {
|
|
|
274
295
|
readonly body?: unknown;
|
|
275
296
|
/** Value of the `X-Request-Id` response header, when present. */
|
|
276
297
|
readonly requestId?: string;
|
|
277
|
-
/** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */
|
|
298
|
+
/** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 and 5xx responses that include the header. */
|
|
278
299
|
readonly retryAfterSeconds?: number;
|
|
279
300
|
/** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */
|
|
280
301
|
readonly rateLimit?: RateLimitInfo;
|
|
@@ -301,12 +322,14 @@ declare class ChronosRateLimitError extends ChronosApiError {
|
|
|
301
322
|
constructor(message: string, options: ChronosApiErrorOptions);
|
|
302
323
|
}
|
|
303
324
|
/**
|
|
304
|
-
* Thrown when
|
|
305
|
-
*
|
|
306
|
-
* available on `.cause`.
|
|
325
|
+
* Thrown when a request fails at the transport layer — DNS failure, TCP
|
|
326
|
+
* reset, connection refused, or a connection dropped mid-response. The
|
|
327
|
+
* original error is available on `.cause`.
|
|
307
328
|
*
|
|
308
|
-
*
|
|
309
|
-
* always means a
|
|
329
|
+
* Caller-supplied abort signals propagate unwrapped — `instanceof
|
|
330
|
+
* ChronosNetworkError` always means a transport failure, not a graceful
|
|
331
|
+
* shutdown. The SDK's own transport timeout throws the
|
|
332
|
+
* {@link ChronosTimeoutError} subclass.
|
|
310
333
|
*
|
|
311
334
|
* @example
|
|
312
335
|
* ```ts
|
|
@@ -326,6 +349,28 @@ declare class ChronosNetworkError extends ChronosError {
|
|
|
326
349
|
cause: unknown;
|
|
327
350
|
});
|
|
328
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Thrown when an HTTP request exceeds the configured `timeoutMs` before the
|
|
354
|
+
* response completes. Subclass of {@link ChronosNetworkError}, so generic
|
|
355
|
+
* transport-failure handling catches it; the underlying rejection is on
|
|
356
|
+
* `.cause`.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* import { ChronosTimeoutError } from '@chronos.sh/sdk';
|
|
361
|
+
*
|
|
362
|
+
* function handleSdkError(err: unknown) {
|
|
363
|
+
* if (err instanceof ChronosTimeoutError) {
|
|
364
|
+
* console.warn('Chronos request timed out', err.message);
|
|
365
|
+
* }
|
|
366
|
+
* }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
declare class ChronosTimeoutError extends ChronosNetworkError {
|
|
370
|
+
constructor(message: string, options: {
|
|
371
|
+
cause: unknown;
|
|
372
|
+
});
|
|
373
|
+
}
|
|
329
374
|
/**
|
|
330
375
|
* Wraps an exception thrown by a user-supplied {@link ChronosHandler}. The
|
|
331
376
|
* original error is on `.cause`; `.message` is copied from the original so
|
|
@@ -443,5 +488,5 @@ declare class Chronos {
|
|
|
443
488
|
constructor(options: ChronosOptions);
|
|
444
489
|
}
|
|
445
490
|
//#endregion
|
|
446
|
-
export { Chronos, ChronosApiError, type ChronosApiErrorOptions, ChronosConfigError, type ChronosContext, ChronosError, type ChronosHandler, ChronosHandlerError, type ChronosHandlerResult, type ChronosLogger, ChronosNetworkError, type ChronosOptions, ChronosRateLimitError, type ChronosSchedule, ChronosWebhookVerificationError, DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, type FetchLike, type RateLimitInfo, SDK_VERSION, Webhook, type WebhookOptions };
|
|
491
|
+
export { Chronos, ChronosApiError, type ChronosApiErrorOptions, ChronosConfigError, type ChronosContext, ChronosError, type ChronosHandler, ChronosHandlerError, type ChronosHandlerResult, type ChronosLogger, ChronosNetworkError, type ChronosOptions, ChronosRateLimitError, type ChronosSchedule, ChronosTimeoutError, ChronosWebhookVerificationError, DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS, DEFAULT_TIMEOUT_MS, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, type FetchLike, type RateLimitInfo, SDK_VERSION, Webhook, type WebhookOptions };
|
|
447
492
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ var ChronosConfigError = class extends ChronosError {
|
|
|
57
57
|
* `success: false` envelope. Carries HTTP `status`, the application
|
|
58
58
|
* `code`, parsed `body`, the API's `X-Request-Id`, and — when the
|
|
59
59
|
* relevant headers are present — rate-limit metadata
|
|
60
|
-
* (`retryAfterSeconds` on 429, `rateLimit` on
|
|
60
|
+
* (`retryAfterSeconds` on 429 and 5xx, `rateLimit` on non-429 errors).
|
|
61
61
|
*
|
|
62
62
|
* `instanceof ChronosApiError` means the server replied;
|
|
63
63
|
* network/transport failures throw {@link ChronosNetworkError} instead.
|
|
@@ -84,7 +84,7 @@ var ChronosApiError = class extends ChronosError {
|
|
|
84
84
|
body;
|
|
85
85
|
/** Value of the `X-Request-Id` response header, when present. */
|
|
86
86
|
requestId;
|
|
87
|
-
/** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */
|
|
87
|
+
/** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 and 5xx responses that include the header. */
|
|
88
88
|
retryAfterSeconds;
|
|
89
89
|
/** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */
|
|
90
90
|
rateLimit;
|
|
@@ -123,12 +123,14 @@ var ChronosRateLimitError = class extends ChronosApiError {
|
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
125
|
/**
|
|
126
|
-
* Thrown when
|
|
127
|
-
*
|
|
128
|
-
* available on `.cause`.
|
|
126
|
+
* Thrown when a request fails at the transport layer — DNS failure, TCP
|
|
127
|
+
* reset, connection refused, or a connection dropped mid-response. The
|
|
128
|
+
* original error is available on `.cause`.
|
|
129
129
|
*
|
|
130
|
-
*
|
|
131
|
-
* always means a
|
|
130
|
+
* Caller-supplied abort signals propagate unwrapped — `instanceof
|
|
131
|
+
* ChronosNetworkError` always means a transport failure, not a graceful
|
|
132
|
+
* shutdown. The SDK's own transport timeout throws the
|
|
133
|
+
* {@link ChronosTimeoutError} subclass.
|
|
132
134
|
*
|
|
133
135
|
* @example
|
|
134
136
|
* ```ts
|
|
@@ -148,6 +150,29 @@ var ChronosNetworkError = class extends ChronosError {
|
|
|
148
150
|
}
|
|
149
151
|
};
|
|
150
152
|
/**
|
|
153
|
+
* Thrown when an HTTP request exceeds the configured `timeoutMs` before the
|
|
154
|
+
* response completes. Subclass of {@link ChronosNetworkError}, so generic
|
|
155
|
+
* transport-failure handling catches it; the underlying rejection is on
|
|
156
|
+
* `.cause`.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* import { ChronosTimeoutError } from '@chronos.sh/sdk';
|
|
161
|
+
*
|
|
162
|
+
* function handleSdkError(err: unknown) {
|
|
163
|
+
* if (err instanceof ChronosTimeoutError) {
|
|
164
|
+
* console.warn('Chronos request timed out', err.message);
|
|
165
|
+
* }
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
var ChronosTimeoutError = class extends ChronosNetworkError {
|
|
170
|
+
constructor(message, options) {
|
|
171
|
+
super(message, options);
|
|
172
|
+
this.name = "ChronosTimeoutError";
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
151
176
|
* Wraps an exception thrown by a user-supplied {@link ChronosHandler}. The
|
|
152
177
|
* original error is on `.cause`; `.message` is copied from the original so
|
|
153
178
|
* the SDK reports it to the API as the failure reason.
|
|
@@ -215,6 +240,10 @@ function validatePollWaitTime(seconds) {
|
|
|
215
240
|
function validateRetryDelayMs(ms) {
|
|
216
241
|
if (!Number.isFinite(ms) || ms < 0) throw new ChronosConfigError("retryDelayMs must be a non-negative number");
|
|
217
242
|
}
|
|
243
|
+
const MAX_TIMEOUT_MS = 2147483647;
|
|
244
|
+
function validateTimeoutMs(ms) {
|
|
245
|
+
if (!Number.isInteger(ms) || ms <= 0 || ms > 2147483647) throw new ChronosConfigError(`timeoutMs must be a positive integer no greater than ${MAX_TIMEOUT_MS}`);
|
|
246
|
+
}
|
|
218
247
|
function validateWorkerId(id) {
|
|
219
248
|
const trimmed = id.trim();
|
|
220
249
|
if (trimmed.length === 0 || trimmed.length > 255) throw new ChronosConfigError("workerId must be 1-255 characters");
|
|
@@ -230,64 +259,92 @@ function normalizeHandlerName(name) {
|
|
|
230
259
|
//#region src/client.ts
|
|
231
260
|
/** Default Chronos API base URL. */
|
|
232
261
|
const DEFAULT_BASE_URL = "https://api.chronos.sh";
|
|
262
|
+
/** Default per-request transport timeout in milliseconds. */
|
|
263
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
233
264
|
var BaseClient = class {
|
|
234
265
|
apiKey;
|
|
235
266
|
baseUrl;
|
|
236
267
|
fetch;
|
|
237
268
|
logger;
|
|
269
|
+
timeoutMs;
|
|
238
270
|
headers;
|
|
239
271
|
constructor(options) {
|
|
240
272
|
this.apiKey = validateApiKey(options.apiKey);
|
|
241
273
|
this.baseUrl = normalizeBaseUrl(options.baseUrl ?? "https://api.chronos.sh");
|
|
242
274
|
this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
243
275
|
this.logger = options.logger ?? defaultLogger;
|
|
276
|
+
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
277
|
+
validateTimeoutMs(this.timeoutMs);
|
|
244
278
|
this.headers = {
|
|
245
279
|
"content-type": "application/json",
|
|
246
280
|
authorization: `Bearer ${this.apiKey}`
|
|
247
281
|
};
|
|
248
282
|
}
|
|
249
|
-
async request(path, body,
|
|
250
|
-
|
|
283
|
+
async request(path, body, options = {}) {
|
|
284
|
+
const { signal } = options;
|
|
285
|
+
const timeoutMs = options.timeoutMs ?? this.timeoutMs;
|
|
286
|
+
const controller = new AbortController();
|
|
287
|
+
let timedOut = false;
|
|
288
|
+
const timer = setTimeout(() => {
|
|
289
|
+
if (controller.signal.aborted) return;
|
|
290
|
+
timedOut = true;
|
|
291
|
+
controller.abort(new DOMException(`Chronos API request timed out after ${timeoutMs}ms`, "TimeoutError"));
|
|
292
|
+
}, timeoutMs);
|
|
293
|
+
const onCallerAbort = () => controller.abort(signal?.reason);
|
|
294
|
+
if (signal?.aborted) onCallerAbort();
|
|
295
|
+
else signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
251
296
|
try {
|
|
252
|
-
response
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
297
|
+
let response;
|
|
298
|
+
try {
|
|
299
|
+
response = await this.fetch(`${this.baseUrl}${path}`, {
|
|
300
|
+
method: "POST",
|
|
301
|
+
headers: this.headers,
|
|
302
|
+
body: JSON.stringify(body),
|
|
303
|
+
signal: controller.signal
|
|
304
|
+
});
|
|
305
|
+
} catch (err) {
|
|
306
|
+
if (signal?.aborted && !timedOut) throw err;
|
|
307
|
+
if (timedOut) throw timeoutError(timeoutMs, err);
|
|
308
|
+
throw new ChronosNetworkError(`Chronos API request failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
309
|
+
}
|
|
310
|
+
let payload;
|
|
311
|
+
try {
|
|
312
|
+
payload = await response.json();
|
|
313
|
+
} catch (err) {
|
|
314
|
+
if (signal?.aborted && !timedOut) throw err;
|
|
315
|
+
if (timedOut) throw timeoutError(timeoutMs, err);
|
|
316
|
+
if (err instanceof SyntaxError) payload = null;
|
|
317
|
+
else throw new ChronosNetworkError(`Chronos API response failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
318
|
+
}
|
|
319
|
+
const envelope = isEnvelope(payload) ? payload : void 0;
|
|
320
|
+
if (!response.ok || envelope && !envelope.success) {
|
|
321
|
+
const is429 = response.status === 429;
|
|
322
|
+
const retryAfterApplies = is429 || response.status >= 500;
|
|
323
|
+
throw new (is429 ? ChronosRateLimitError : ChronosApiError)(apiErrorMessage(response, envelope), {
|
|
324
|
+
status: response.status,
|
|
325
|
+
code: typeof envelope?.code === "string" ? envelope.code : void 0,
|
|
326
|
+
body: payload,
|
|
327
|
+
requestId: response.headers.get("x-request-id") ?? void 0,
|
|
328
|
+
retryAfterSeconds: retryAfterApplies ? parseIntHeader(response.headers, "retry-after") : void 0,
|
|
329
|
+
rateLimit: is429 ? void 0 : parseRateLimitHeaders(response.headers)
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
if (!envelope) throw new ChronosError("Chronos API returned an invalid response");
|
|
333
|
+
return envelope.data;
|
|
334
|
+
} finally {
|
|
335
|
+
clearTimeout(timer);
|
|
336
|
+
signal?.removeEventListener("abort", onCallerAbort);
|
|
274
337
|
}
|
|
275
|
-
if (!envelope) throw new ChronosError("Chronos API returned an invalid response");
|
|
276
|
-
return envelope.data;
|
|
277
338
|
}
|
|
278
339
|
};
|
|
340
|
+
function timeoutError(timeoutMs, cause) {
|
|
341
|
+
return new ChronosTimeoutError(`Chronos API request timed out after ${timeoutMs}ms`, { cause });
|
|
342
|
+
}
|
|
279
343
|
function normalizeBaseUrl(baseUrl) {
|
|
280
344
|
const normalized = baseUrl.trim().replace(/\/+$/, "");
|
|
281
345
|
if (!normalized) throw new ChronosConfigError("Chronos baseUrl is required");
|
|
282
346
|
return normalized;
|
|
283
347
|
}
|
|
284
|
-
async function parseJsonResponse(response) {
|
|
285
|
-
try {
|
|
286
|
-
return await response.json();
|
|
287
|
-
} catch {
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
348
|
function apiErrorMessage(response, envelope) {
|
|
292
349
|
if (typeof envelope?.message === "string" && envelope.message.trim()) return envelope.message;
|
|
293
350
|
return response.ok ? "Chronos API returned an invalid response" : `Chronos API request failed with status ${response.status}`;
|
|
@@ -315,7 +372,7 @@ function isEnvelope(value) {
|
|
|
315
372
|
//#endregion
|
|
316
373
|
//#region src/internal/id.ts
|
|
317
374
|
function generateWorkerId() {
|
|
318
|
-
const bytes = crypto.getRandomValues(new Uint8Array(8));
|
|
375
|
+
const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8));
|
|
319
376
|
let hex = "";
|
|
320
377
|
for (const b of bytes) hex += b.toString(16).padStart(2, "0");
|
|
321
378
|
return `w_${hex}`;
|
|
@@ -331,12 +388,13 @@ function detectRuntime() {
|
|
|
331
388
|
}
|
|
332
389
|
//#endregion
|
|
333
390
|
//#region src/internal/version.ts
|
|
334
|
-
const SDK_VERSION = "0.0
|
|
391
|
+
const SDK_VERSION = "0.1.0";
|
|
335
392
|
//#endregion
|
|
336
393
|
//#region src/worker.ts
|
|
337
394
|
/** Default worker long-poll wait time in seconds. Equal to the API maximum. */
|
|
338
395
|
const DEFAULT_POLL_WAIT_TIME_SECONDS = 20;
|
|
339
396
|
const DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
397
|
+
const MAX_RETRY_AFTER_MS = 6e4;
|
|
340
398
|
const RESULT_REPORT_MAX_ATTEMPTS = 3;
|
|
341
399
|
const MAX_REPORTED_ERROR_LENGTH = 4096;
|
|
342
400
|
const DEFAULT_HANDLER_ERROR = "Chronos handler failed";
|
|
@@ -414,8 +472,11 @@ var Worker = class {
|
|
|
414
472
|
* in-flight handler that has not exceeded its timeout, along with the
|
|
415
473
|
* subsequent result-report, is allowed to complete. Timed-out handlers
|
|
416
474
|
* are abandoned (their {@link ChronosContext.signal} fires, but the SDK
|
|
417
|
-
* does not wait for them).
|
|
418
|
-
*
|
|
475
|
+
* does not wait for them). Every HTTP request is bounded by the `timeoutMs`
|
|
476
|
+
* transport timeout and retry waits are skipped once stop is requested, so
|
|
477
|
+
* the returned promise resolves in bounded time. Returns the same promise
|
|
478
|
+
* as the active {@link Worker.start}, or a resolved promise if the worker
|
|
479
|
+
* isn't running.
|
|
419
480
|
*/
|
|
420
481
|
stop() {
|
|
421
482
|
this.pollController?.abort();
|
|
@@ -436,7 +497,7 @@ var Worker = class {
|
|
|
436
497
|
}
|
|
437
498
|
async claimJob() {
|
|
438
499
|
if (this.handlerNames.length === 0) throw new ChronosError("Cannot claim jobs without registered handlers");
|
|
439
|
-
return this.client.request("/v1/
|
|
500
|
+
return this.client.request("/v1/workers/jobs/claim", {
|
|
440
501
|
wait_time_seconds: this.pollWaitTimeSeconds,
|
|
441
502
|
handlers: this.handlerNames,
|
|
442
503
|
worker: {
|
|
@@ -445,7 +506,10 @@ var Worker = class {
|
|
|
445
506
|
runtime: this.runtime,
|
|
446
507
|
uptime_ms: Math.round(performance.now() - this.startedAt)
|
|
447
508
|
}
|
|
448
|
-
},
|
|
509
|
+
}, {
|
|
510
|
+
signal: this.pollController?.signal,
|
|
511
|
+
timeoutMs: Math.min(this.pollWaitTimeSeconds * 1e3 + this.client.timeoutMs, MAX_TIMEOUT_MS)
|
|
512
|
+
});
|
|
449
513
|
}
|
|
450
514
|
async processJob(job) {
|
|
451
515
|
const handler = this.handlers.get(job.handler);
|
|
@@ -540,7 +604,7 @@ var Worker = class {
|
|
|
540
604
|
async reportResultWithRetry(executionId, body) {
|
|
541
605
|
let lastErr;
|
|
542
606
|
for (let attempt = 1; attempt <= RESULT_REPORT_MAX_ATTEMPTS; attempt++) try {
|
|
543
|
-
await this.client.request(`/v1/
|
|
607
|
+
await this.client.request(`/v1/workers/executions/${encodeURIComponent(executionId)}/result`, body);
|
|
544
608
|
return;
|
|
545
609
|
} catch (err) {
|
|
546
610
|
const apiErr = err instanceof ChronosApiError ? err : void 0;
|
|
@@ -562,7 +626,7 @@ var Worker = class {
|
|
|
562
626
|
attempt,
|
|
563
627
|
maxAttempts: RESULT_REPORT_MAX_ATTEMPTS
|
|
564
628
|
});
|
|
565
|
-
await sleep(retryDelayFor(err, this.retryDelayMs));
|
|
629
|
+
await sleep(retryDelayFor(err, this.retryDelayMs), this.pollController?.signal);
|
|
566
630
|
}
|
|
567
631
|
throw lastErr;
|
|
568
632
|
}
|
|
@@ -638,10 +702,10 @@ function truncate(value, maxLength) {
|
|
|
638
702
|
return value.length <= maxLength ? value : value.slice(0, maxLength);
|
|
639
703
|
}
|
|
640
704
|
function retryDelayFor(err, fallbackMs) {
|
|
641
|
-
if (err instanceof ChronosApiError && err.retryAfterSeconds !== void 0) return err.retryAfterSeconds * 1e3;
|
|
705
|
+
if (err instanceof ChronosApiError && err.retryAfterSeconds !== void 0) return Math.max(Math.min(err.retryAfterSeconds * 1e3, MAX_RETRY_AFTER_MS), fallbackMs);
|
|
642
706
|
return fallbackMs;
|
|
643
707
|
}
|
|
644
|
-
const NON_TERMINAL_4XX = new Set([
|
|
708
|
+
const NON_TERMINAL_4XX = /* @__PURE__ */ new Set([
|
|
645
709
|
408,
|
|
646
710
|
409,
|
|
647
711
|
429
|
|
@@ -814,6 +878,6 @@ var Chronos = class {
|
|
|
814
878
|
}
|
|
815
879
|
};
|
|
816
880
|
//#endregion
|
|
817
|
-
export { Chronos, ChronosApiError, ChronosConfigError, ChronosError, ChronosHandlerError, ChronosNetworkError, ChronosRateLimitError, ChronosWebhookVerificationError, DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, SDK_VERSION, Webhook };
|
|
881
|
+
export { Chronos, ChronosApiError, ChronosConfigError, ChronosError, ChronosHandlerError, ChronosNetworkError, ChronosRateLimitError, ChronosTimeoutError, ChronosWebhookVerificationError, DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS, DEFAULT_TIMEOUT_MS, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, SDK_VERSION, Webhook };
|
|
818
882
|
|
|
819
883
|
//# sourceMappingURL=index.js.map
|