@chronos.sh/sdk 0.0.2-canary.0e28802 → 0.0.2-canary.464b2bb

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 CHANGED
@@ -56,10 +56,13 @@ var ChronosConfigError = class extends ChronosError {
56
56
  /**
57
57
  * Thrown when the Chronos API responds with a non-2xx status or a
58
58
  * `success: false` envelope. Carries HTTP `status`, the application
59
- * `code`, parsed `body`, and the API's `X-Request-Id`.
59
+ * `code`, parsed `body`, the API's `X-Request-Id`, and — when the
60
+ * relevant headers are present — rate-limit metadata
61
+ * (`retryAfterSeconds` on 429, `rateLimit` on other errors).
60
62
  *
61
63
  * `instanceof ChronosApiError` means the server replied;
62
64
  * network/transport failures throw {@link ChronosNetworkError} instead.
65
+ * For 429 specifically, see {@link ChronosRateLimitError}.
63
66
  *
64
67
  * @example
65
68
  * ```ts
@@ -82,6 +85,10 @@ var ChronosApiError = class extends ChronosError {
82
85
  body;
83
86
  /** Value of the `X-Request-Id` response header, when present. */
84
87
  requestId;
88
+ /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */
89
+ retryAfterSeconds;
90
+ /** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */
91
+ rateLimit;
85
92
  constructor(message, options) {
86
93
  super(message);
87
94
  this.name = "ChronosApiError";
@@ -89,6 +96,31 @@ var ChronosApiError = class extends ChronosError {
89
96
  this.code = options.code;
90
97
  this.body = options.body;
91
98
  this.requestId = options.requestId;
99
+ this.retryAfterSeconds = options.retryAfterSeconds;
100
+ this.rateLimit = options.rateLimit;
101
+ }
102
+ };
103
+ /**
104
+ * Thrown when the Chronos API responds with `429 Too Many Requests`.
105
+ * Subclass of {@link ChronosApiError} — `instanceof ChronosRateLimitError`
106
+ * narrows to a rate-limited response. `retryAfterSeconds` is set when the
107
+ * response includes a `Retry-After` header; otherwise it is `undefined`.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * import { ChronosRateLimitError } from '@chronos.sh/sdk';
112
+ *
113
+ * function handleSdkError(err: unknown) {
114
+ * if (err instanceof ChronosRateLimitError && err.retryAfterSeconds) {
115
+ * console.warn(`Rate limited — retry after ${err.retryAfterSeconds}s`);
116
+ * }
117
+ * }
118
+ * ```
119
+ */
120
+ var ChronosRateLimitError = class extends ChronosApiError {
121
+ constructor(message, options) {
122
+ super(message, options);
123
+ this.name = "ChronosRateLimitError";
92
124
  }
93
125
  };
94
126
  /**
@@ -136,6 +168,31 @@ var ChronosHandlerError = class extends ChronosError {
136
168
  this.name = "ChronosHandlerError";
137
169
  }
138
170
  };
171
+ /**
172
+ * Thrown by {@link Webhook.verify} when signature verification fails.
173
+ * Covers missing or malformed headers, expired timestamps, and signature
174
+ * mismatches.
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * import { Webhook, ChronosWebhookVerificationError } from '@chronos.sh/sdk';
179
+ *
180
+ * const wh = new Webhook(process.env.CHRONOS_SIGNING_SECRET!);
181
+ * try {
182
+ * const payload = await wh.verify(rawBody, headers);
183
+ * } catch (err) {
184
+ * if (err instanceof ChronosWebhookVerificationError) {
185
+ * console.error('Webhook verification failed:', err.message);
186
+ * }
187
+ * }
188
+ * ```
189
+ */
190
+ var ChronosWebhookVerificationError = class extends ChronosError {
191
+ constructor(message) {
192
+ super(message);
193
+ this.name = "ChronosWebhookVerificationError";
194
+ }
195
+ };
139
196
  //#endregion
140
197
  //#region src/internal/logger.ts
141
198
  const defaultLogger = {
@@ -200,12 +257,17 @@ var BaseClient = class {
200
257
  }
201
258
  const payload = await parseJsonResponse(response);
202
259
  const envelope = isEnvelope(payload) ? payload : void 0;
203
- if (!response.ok || envelope && !envelope.success) throw new ChronosApiError(apiErrorMessage(response, envelope), {
204
- status: response.status,
205
- code: typeof envelope?.code === "string" ? envelope.code : void 0,
206
- body: payload,
207
- requestId: response.headers.get("x-request-id") ?? void 0
208
- });
260
+ if (!response.ok || envelope && !envelope.success) {
261
+ const is429 = response.status === 429;
262
+ throw new (is429 ? ChronosRateLimitError : ChronosApiError)(apiErrorMessage(response, envelope), {
263
+ status: response.status,
264
+ code: typeof envelope?.code === "string" ? envelope.code : void 0,
265
+ body: payload,
266
+ requestId: response.headers.get("x-request-id") ?? void 0,
267
+ retryAfterSeconds: is429 ? parseIntHeader(response.headers, "retry-after") : void 0,
268
+ rateLimit: is429 ? void 0 : parseRateLimitHeaders(response.headers)
269
+ });
270
+ }
209
271
  if (!envelope) throw new ChronosError("Chronos API returned an invalid response");
210
272
  return envelope.data;
211
273
  }
@@ -226,6 +288,22 @@ function apiErrorMessage(response, envelope) {
226
288
  if (typeof envelope?.message === "string" && envelope.message.trim()) return envelope.message;
227
289
  return response.ok ? "Chronos API returned an invalid response" : `Chronos API request failed with status ${response.status}`;
228
290
  }
291
+ function parseIntHeader(headers, name) {
292
+ const raw = headers.get(name);
293
+ if (raw === null || !/^\d+$/.test(raw)) return;
294
+ return Number.parseInt(raw, 10);
295
+ }
296
+ function parseRateLimitHeaders(headers) {
297
+ const limit = parseIntHeader(headers, "x-ratelimit-limit");
298
+ const remaining = parseIntHeader(headers, "x-ratelimit-remaining");
299
+ const reset = parseIntHeader(headers, "x-ratelimit-reset");
300
+ if (limit === void 0 || remaining === void 0 || reset === void 0) return;
301
+ return {
302
+ limit,
303
+ remaining,
304
+ reset
305
+ };
306
+ }
229
307
  function isEnvelope(value) {
230
308
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
231
309
  return typeof value.success === "boolean";
@@ -303,8 +381,10 @@ var Worker = class {
303
381
  }
304
382
  /**
305
383
  * Request graceful shutdown. The poll loop is aborted immediately; any
306
- * in-flight handler and result-report are allowed to complete to preserve
307
- * at-least-once delivery. Returns the same promise as the active
384
+ * in-flight handler that has not exceeded its timeout, along with the
385
+ * subsequent result-report, is allowed to complete. Timed-out handlers
386
+ * are abandoned (their {@link ChronosContext.signal} fires, but the SDK
387
+ * does not wait for them). Returns the same promise as the active
308
388
  * {@link Worker.start}, or a resolved promise if the worker isn't running.
309
389
  */
310
390
  stop() {
@@ -321,7 +401,7 @@ var Worker = class {
321
401
  } catch (err) {
322
402
  if (this.isStopped) break;
323
403
  this.client.logger.error("Chronos poll loop error", { err: errorToLogValue(err) });
324
- await sleep(this.retryDelayMs, this.pollController?.signal);
404
+ await sleep(retryDelayFor(err, this.retryDelayMs), this.pollController?.signal);
325
405
  }
326
406
  }
327
407
  async claimJob() {
@@ -334,9 +414,10 @@ var Worker = class {
334
414
  async processJob(job) {
335
415
  const handler = this.handlers.get(job.handler);
336
416
  if (!handler) return this.handleUnregisteredJob(job);
337
- let handlerResult;
417
+ const controller = new AbortController();
418
+ let outcome;
338
419
  try {
339
- handlerResult = await handler(createContext(job));
420
+ outcome = await this.runHandlerWithTimeout(handler, createContext(job, controller.signal), job.timeout * 1e3, controller);
340
421
  } catch (err) {
341
422
  const message = errorMessage(err);
342
423
  const handlerErr = new ChronosHandlerError(message, { cause: err });
@@ -347,9 +428,21 @@ var Worker = class {
347
428
  await this.safeReportFailed(job, message);
348
429
  return;
349
430
  }
431
+ if (outcome.timedOut) {
432
+ this.client.logger.error("Chronos handler timed out", {
433
+ ...jobLogMeta(job),
434
+ timeout: job.timeout
435
+ });
436
+ try {
437
+ await this.reportTimeout(job.execution_id);
438
+ } catch (err) {
439
+ this.logResultReportFailure(err, job, "timeout");
440
+ }
441
+ return;
442
+ }
350
443
  let result;
351
444
  try {
352
- result = normalizeHandlerResult(handlerResult);
445
+ result = normalizeHandlerResult(outcome.result);
353
446
  } catch (err) {
354
447
  this.client.logger.error("Chronos handler returned invalid result", {
355
448
  ...jobLogMeta(job),
@@ -364,6 +457,23 @@ var Worker = class {
364
457
  this.logResultReportFailure(err, job, "completed");
365
458
  }
366
459
  }
460
+ runHandlerWithTimeout(handler, ctx, timeoutMs, controller) {
461
+ let timer;
462
+ const handlerPromise = Promise.resolve(handler(ctx)).then((result) => ({
463
+ timedOut: false,
464
+ result
465
+ }));
466
+ handlerPromise.catch(() => {});
467
+ const timeoutPromise = new Promise((resolve) => {
468
+ timer = setTimeout(() => {
469
+ controller.abort();
470
+ resolve({ timedOut: true });
471
+ }, timeoutMs);
472
+ });
473
+ return Promise.race([handlerPromise, timeoutPromise]).finally(() => {
474
+ clearTimeout(timer);
475
+ });
476
+ }
367
477
  async handleUnregisteredJob(job) {
368
478
  const message = `Chronos SDK received job for unregistered handler "${job.handler}"`;
369
479
  this.client.logger.error(message, jobLogMeta(job));
@@ -382,6 +492,9 @@ var Worker = class {
382
492
  ...result === void 0 ? {} : { result }
383
493
  });
384
494
  }
495
+ async reportTimeout(executionId) {
496
+ await this.reportResultWithRetry(executionId, { status: "timeout" });
497
+ }
385
498
  async reportFailed(executionId, error) {
386
499
  await this.reportResultWithRetry(executionId, {
387
500
  status: "failed",
@@ -413,7 +526,7 @@ var Worker = class {
413
526
  attempt,
414
527
  maxAttempts: RESULT_REPORT_MAX_ATTEMPTS
415
528
  });
416
- await sleep(this.retryDelayMs);
529
+ await sleep(retryDelayFor(err, this.retryDelayMs));
417
530
  }
418
531
  throw lastErr;
419
532
  }
@@ -426,7 +539,7 @@ var Worker = class {
426
539
  });
427
540
  }
428
541
  };
429
- function createContext(job) {
542
+ function createContext(job, signal) {
430
543
  return {
431
544
  jobId: job.job_id,
432
545
  executionId: job.execution_id,
@@ -435,6 +548,7 @@ function createContext(job) {
435
548
  scheduledFor: new Date(job.scheduled_for),
436
549
  attempt: job.attempt,
437
550
  timeout: job.timeout,
551
+ signal,
438
552
  schedule: job.schedule
439
553
  };
440
554
  }
@@ -474,7 +588,9 @@ function errorToLogValue(err) {
474
588
  ...base,
475
589
  status: err.status,
476
590
  code: err.code,
477
- requestId: err.requestId
591
+ requestId: err.requestId,
592
+ ...err.retryAfterSeconds !== void 0 && { retryAfterSeconds: err.retryAfterSeconds },
593
+ ...err.rateLimit !== void 0 && { rateLimit: err.rateLimit }
478
594
  };
479
595
  if (err instanceof ChronosNetworkError || err instanceof ChronosHandlerError) return {
480
596
  ...base,
@@ -485,6 +601,10 @@ function errorToLogValue(err) {
485
601
  function truncate(value, maxLength) {
486
602
  return value.length <= maxLength ? value : value.slice(0, maxLength);
487
603
  }
604
+ function retryDelayFor(err, fallbackMs) {
605
+ if (err instanceof ChronosApiError && err.retryAfterSeconds !== void 0) return err.retryAfterSeconds * 1e3;
606
+ return fallbackMs;
607
+ }
488
608
  const NON_TERMINAL_4XX = new Set([
489
609
  408,
490
610
  409,
@@ -508,6 +628,124 @@ function sleep(ms, signal) {
508
628
  });
509
629
  }
510
630
  //#endregion
631
+ //#region src/webhook.ts
632
+ const DEFAULT_TIMESTAMP_TOLERANCE_SECONDS = 300;
633
+ const SIGNATURE_PREFIX = "sha256=";
634
+ const REQUIRED_HEADERS = [
635
+ "x-chronos-signature",
636
+ "x-chronos-timestamp",
637
+ "x-chronos-delivery-id"
638
+ ];
639
+ /**
640
+ * Verifies webhook signatures on Chronos push deliveries.
641
+ *
642
+ * @example
643
+ * ```ts
644
+ * import { Webhook } from '@chronos.sh/sdk';
645
+ *
646
+ * const wh = new Webhook(process.env.CHRONOS_SIGNING_SECRET!);
647
+ *
648
+ * // In your request handler:
649
+ * const payload = await wh.verify(rawBody, {
650
+ * 'x-chronos-signature': req.headers['x-chronos-signature'],
651
+ * 'x-chronos-timestamp': req.headers['x-chronos-timestamp'],
652
+ * 'x-chronos-delivery-id': req.headers['x-chronos-delivery-id'],
653
+ * });
654
+ * ```
655
+ */
656
+ var Webhook = class {
657
+ secrets;
658
+ toleranceSeconds;
659
+ keysPromise;
660
+ /**
661
+ * @param secret - Signing secret or array of secrets (for key rotation).
662
+ * @param options - Optional configuration.
663
+ * @throws {ChronosWebhookVerificationError} If no valid secret is provided.
664
+ */
665
+ constructor(secret, options) {
666
+ const secrets = Array.isArray(secret) ? secret : [secret];
667
+ this.secrets = secrets.filter((s) => typeof s === "string" && s.trim().length > 0).map((s) => s.trim());
668
+ if (this.secrets.length === 0) throw new ChronosWebhookVerificationError("At least one non-empty signing secret is required");
669
+ const tolerance = options?.toleranceSeconds ?? 300;
670
+ if (!Number.isFinite(tolerance) || tolerance < 0) throw new ChronosWebhookVerificationError("toleranceSeconds must be a finite non-negative number");
671
+ this.toleranceSeconds = tolerance;
672
+ }
673
+ /**
674
+ * Verifies the webhook signature and returns the parsed request body.
675
+ *
676
+ * @param body - Raw request body as a string. Must be the exact bytes received — not re-serialized.
677
+ * @param headers - Request headers. Keys are matched case-insensitively.
678
+ * @returns The parsed JSON body on successful verification.
679
+ * @throws {ChronosWebhookVerificationError} If any verification step fails.
680
+ */
681
+ async verify(body, headers) {
682
+ const normalized = normalizeHeaders(headers);
683
+ for (const name of REQUIRED_HEADERS) {
684
+ const value = normalized[name];
685
+ if (!value || typeof value !== "string") throw new ChronosWebhookVerificationError(`Missing required header: ${name}`);
686
+ }
687
+ const signature = normalized["x-chronos-signature"];
688
+ const timestamp = normalized["x-chronos-timestamp"];
689
+ const deliveryId = normalized["x-chronos-delivery-id"];
690
+ if (!/^[0-9]+$/.test(timestamp)) throw new ChronosWebhookVerificationError("Invalid timestamp");
691
+ const ts = Number(timestamp);
692
+ if (ts <= 0) throw new ChronosWebhookVerificationError("Invalid timestamp");
693
+ const now = Math.floor(Date.now() / 1e3);
694
+ const isStale = Math.abs(now - ts) > this.toleranceSeconds;
695
+ if (this.toleranceSeconds > 0 && isStale) throw new ChronosWebhookVerificationError("Timestamp outside tolerance");
696
+ if (!signature.startsWith(SIGNATURE_PREFIX)) throw new ChronosWebhookVerificationError(`Invalid signature format: expected ${SIGNATURE_PREFIX}<hex>`);
697
+ const expectedHex = signature.slice(7);
698
+ if (expectedHex.length === 0) throw new ChronosWebhookVerificationError("Invalid signature: expected 32-byte SHA-256 digest");
699
+ let expectedBytes;
700
+ try {
701
+ expectedBytes = hexDecode(expectedHex);
702
+ } catch {
703
+ throw new ChronosWebhookVerificationError("Invalid signature: malformed hex");
704
+ }
705
+ if (expectedBytes.byteLength !== 32) throw new ChronosWebhookVerificationError("Invalid signature: expected 32-byte SHA-256 digest");
706
+ const signedPayload = `${deliveryId}.${timestamp}.${body}`;
707
+ const data = new TextEncoder().encode(signedPayload);
708
+ try {
709
+ const keys = await this.ensureKeys();
710
+ for (const key of keys) if (await globalThis.crypto.subtle.verify("HMAC", key, expectedBytes, data)) try {
711
+ return JSON.parse(body);
712
+ } catch {
713
+ throw new ChronosWebhookVerificationError("Verified payload is not valid JSON");
714
+ }
715
+ } catch (err) {
716
+ if (err instanceof ChronosWebhookVerificationError) throw err;
717
+ throw new ChronosWebhookVerificationError("Signature verification failed");
718
+ }
719
+ throw new ChronosWebhookVerificationError("Signature verification failed");
720
+ }
721
+ ensureKeys() {
722
+ if (!this.keysPromise) {
723
+ const encoder = new TextEncoder();
724
+ this.keysPromise = Promise.all(this.secrets.map((s) => globalThis.crypto.subtle.importKey("raw", encoder.encode(s), {
725
+ name: "HMAC",
726
+ hash: "SHA-256"
727
+ }, false, ["verify"])));
728
+ }
729
+ return this.keysPromise;
730
+ }
731
+ };
732
+ function normalizeHeaders(headers) {
733
+ const result = {};
734
+ if (headers instanceof Headers) headers.forEach((value, key) => {
735
+ result[key.toLowerCase()] = value;
736
+ });
737
+ else for (const key of Object.keys(headers)) result[key.toLowerCase()] = headers[key];
738
+ return result;
739
+ }
740
+ function hexDecode(hex) {
741
+ if (hex.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(hex)) throw new Error("Invalid hex string");
742
+ const length = hex.length / 2;
743
+ const buffer = new ArrayBuffer(length);
744
+ const view = new DataView(buffer);
745
+ for (let i = 0; i < length; i++) view.setUint8(i, Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16));
746
+ return buffer;
747
+ }
748
+ //#endregion
511
749
  //#region src/index.ts
512
750
  /**
513
751
  * The Chronos SDK client. Composes worker and (future) REST resource
@@ -546,7 +784,11 @@ exports.ChronosConfigError = ChronosConfigError;
546
784
  exports.ChronosError = ChronosError;
547
785
  exports.ChronosHandlerError = ChronosHandlerError;
548
786
  exports.ChronosNetworkError = ChronosNetworkError;
787
+ exports.ChronosRateLimitError = ChronosRateLimitError;
788
+ exports.ChronosWebhookVerificationError = ChronosWebhookVerificationError;
549
789
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
550
790
  exports.DEFAULT_POLL_WAIT_TIME_SECONDS = DEFAULT_POLL_WAIT_TIME_SECONDS;
791
+ exports.DEFAULT_TIMESTAMP_TOLERANCE_SECONDS = DEFAULT_TIMESTAMP_TOLERANCE_SECONDS;
792
+ exports.Webhook = Webhook;
551
793
 
552
794
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/errors.ts","../src/internal/logger.ts","../src/internal/validate.ts","../src/client.ts","../src/worker.ts","../src/index.ts"],"sourcesContent":["/**\n * Base class for all errors thrown by the Chronos SDK. Catch this in a\n * single `catch` to handle any SDK failure generically; use the subclasses\n * to branch on cause.\n *\n * @example\n * ```ts\n * import { Chronos, ChronosError } from '@chronos.sh/sdk';\n *\n * const chronos = new Chronos({ apiKey: 'chrns_...' });\n * try {\n * await chronos.worker.start();\n * } catch (err) {\n * if (err instanceof ChronosError) {\n * console.error('Chronos failed:', err.message);\n * }\n * }\n * ```\n */\nexport class ChronosError extends Error {\n /** Original error/value that caused this SDK error, when one is available. */\n declare readonly cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'ChronosError';\n\n if (options && !('cause' in this)) {\n Object.defineProperty(this, 'cause', {\n value: options.cause,\n configurable: true,\n writable: true,\n });\n }\n }\n}\n\n/**\n * Thrown when SDK options fail validation at `new Chronos({ ... })`. Covers\n * `apiKey`, `baseUrl`, `pollWaitTimeSeconds`, and `retryDelayMs`.\n *\n * @example\n * ```ts\n * import { Chronos, ChronosConfigError } from '@chronos.sh/sdk';\n *\n * try {\n * const chronos = new Chronos({ apiKey: '' });\n * } catch (err) {\n * if (err instanceof ChronosConfigError) {\n * console.error('Invalid Chronos config:', err.message);\n * }\n * }\n * ```\n */\nexport class ChronosConfigError extends ChronosError {\n constructor(message: string) {\n super(message);\n this.name = 'ChronosConfigError';\n }\n}\n\n/** Options for constructing a {@link ChronosApiError}. */\nexport type ChronosApiErrorOptions = {\n /** HTTP status code returned by the Chronos API. */\n status: number;\n /** Application-level error code from the response envelope, when present. */\n code?: string;\n /** Full parsed response payload (envelope + data, or whatever the server returned). */\n body?: unknown;\n /** Value of the `X-Request-Id` response header, when present. Pair with server logs. */\n requestId?: string;\n};\n\n/**\n * Thrown when the Chronos API responds with a non-2xx status or a\n * `success: false` envelope. Carries HTTP `status`, the application\n * `code`, parsed `body`, and the API's `X-Request-Id`.\n *\n * `instanceof ChronosApiError` means the server replied;\n * network/transport failures throw {@link ChronosNetworkError} instead.\n *\n * @example\n * ```ts\n * import { ChronosApiError } from '@chronos.sh/sdk';\n *\n * function handleSdkError(err: unknown) {\n * if (err instanceof ChronosApiError) {\n * if (err.status === 401) return refreshAuth();\n * console.error('API error', { status: err.status, requestId: err.requestId });\n * }\n * }\n * ```\n */\nexport class ChronosApiError extends ChronosError {\n /** HTTP status code returned by the Chronos API. */\n readonly status: number;\n /** Application-level error code from the response envelope, when present. */\n readonly code?: string;\n /** Full parsed response payload (envelope + data, or whatever the server returned). */\n readonly body?: unknown;\n /** Value of the `X-Request-Id` response header, when present. */\n readonly requestId?: string;\n\n constructor(message: string, options: ChronosApiErrorOptions) {\n super(message);\n this.name = 'ChronosApiError';\n this.status = options.status;\n this.code = options.code;\n this.body = options.body;\n this.requestId = options.requestId;\n }\n}\n\n/**\n * Thrown when the underlying `fetch` rejects before the server replies —\n * DNS failure, TCP reset, connection refused, etc. The original error is\n * available on `.cause`.\n *\n * Abort signals propagate unwrapped — `instanceof ChronosNetworkError`\n * always means a real transport failure, not a graceful shutdown.\n *\n * @example\n * ```ts\n * import { ChronosNetworkError } from '@chronos.sh/sdk';\n *\n * function handleSdkError(err: unknown) {\n * if (err instanceof ChronosNetworkError) {\n * console.warn('Transport blip', { cause: err.cause });\n * }\n * }\n * ```\n */\nexport class ChronosNetworkError extends ChronosError {\n /** Original error/value rejected by the underlying `fetch`. */\n declare readonly cause: unknown;\n\n constructor(message: string, options: { cause: unknown }) {\n super(message, options);\n this.name = 'ChronosNetworkError';\n }\n}\n\n/**\n * Wraps an exception thrown by a user-supplied {@link ChronosHandler}. The\n * original error is on `.cause`; `.message` is copied from the original so\n * the SDK reports it to the API as the failure reason.\n *\n * @example\n * ```ts\n * import { ChronosHandlerError } from '@chronos.sh/sdk';\n *\n * if (err instanceof ChronosHandlerError) {\n * console.error('Handler threw', err.cause);\n * }\n * ```\n */\nexport class ChronosHandlerError extends ChronosError {\n /** Original error/value thrown by the user-supplied handler. */\n declare readonly cause: unknown;\n\n constructor(message: string, options: { cause: unknown }) {\n super(message, options);\n this.name = 'ChronosHandlerError';\n }\n}\n","import type { ChronosLogger } from '../types';\n\nexport const defaultLogger: ChronosLogger = {\n debug: (message, meta) => logToConsole(console.debug, message, meta),\n info: (message, meta) => logToConsole(console.info, message, meta),\n warn: (message, meta) => logToConsole(console.warn, message, meta),\n error: (message, meta) => logToConsole(console.error, message, meta),\n};\n\nfunction logToConsole(\n method: (message?: unknown, ...optionalParams: unknown[]) => void,\n message: string,\n meta?: Record<string, unknown>,\n): void {\n if (meta) {\n method(message, meta);\n } else {\n method(message);\n }\n}\n","import { ChronosConfigError, ChronosError } from '../errors';\n\nexport const MAX_HANDLER_NAME_LENGTH = 255;\nexport const MAX_POLL_WAIT_TIME_SECONDS = 20;\n\nexport function validateApiKey(apiKey: string | undefined): string {\n const trimmed = apiKey?.trim();\n if (!trimmed) {\n throw new ChronosConfigError('Chronos apiKey is required');\n }\n return trimmed;\n}\n\nexport function validatePollWaitTime(seconds: number): void {\n if (!Number.isInteger(seconds) || seconds < 0 || seconds > MAX_POLL_WAIT_TIME_SECONDS) {\n throw new ChronosConfigError(\n `pollWaitTimeSeconds must be an integer between 0 and ${MAX_POLL_WAIT_TIME_SECONDS}`,\n );\n }\n}\n\nexport function validateRetryDelayMs(ms: number): void {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new ChronosConfigError('retryDelayMs must be a non-negative number');\n }\n}\n\nexport function normalizeHandlerName(name: string): string {\n const normalized = name.trim();\n\n if (!normalized) {\n throw new ChronosError('Handler name is required');\n }\n\n if (normalized.length > MAX_HANDLER_NAME_LENGTH) {\n throw new ChronosError(`Handler name must be ${MAX_HANDLER_NAME_LENGTH} characters or fewer`);\n }\n\n return normalized;\n}\n","import { ChronosApiError, ChronosConfigError, ChronosError, ChronosNetworkError } from './errors';\nimport { defaultLogger } from './internal/logger';\nimport { validateApiKey } from './internal/validate';\nimport type { ChronosLogger, ChronosOptions, FetchLike } from './types';\n\n/** Default Chronos API base URL. */\nexport const DEFAULT_BASE_URL = 'https://api.chronos.sh';\n\ntype ApiResponse<T> = {\n success: boolean;\n message?: string;\n code?: string;\n data: T;\n};\n\nexport class BaseClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly fetch: FetchLike;\n readonly logger: ChronosLogger;\n private readonly headers: Record<string, string>;\n\n constructor(options: ChronosOptions) {\n this.apiKey = validateApiKey(options.apiKey);\n this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL);\n this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.logger = options.logger ?? defaultLogger;\n this.headers = {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n };\n }\n\n async request<T>(path: string, body: Record<string, unknown>, signal?: AbortSignal): Promise<T> {\n let response: Response;\n try {\n response = await this.fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(body),\n signal,\n });\n } catch (err) {\n if (signal?.aborted) {\n throw err;\n }\n const message = err instanceof Error ? err.message : String(err);\n throw new ChronosNetworkError(`Chronos API request failed: ${message}`, { cause: err });\n }\n\n const payload = await parseJsonResponse<ApiResponse<T>>(response);\n const envelope = isEnvelope(payload) ? (payload as ApiResponse<T>) : undefined;\n\n if (!response.ok || (envelope && !envelope.success)) {\n throw new ChronosApiError(apiErrorMessage(response, envelope), {\n status: response.status,\n code: typeof envelope?.code === 'string' ? envelope.code : undefined,\n body: payload,\n requestId: response.headers.get('x-request-id') ?? undefined,\n });\n }\n\n if (!envelope) {\n // Plain ChronosError, not ChronosApiError — a malformed 200 body is\n // an infrastructure anomaly that should be retried, not a definitive\n // API rejection.\n throw new ChronosError('Chronos API returned an invalid response');\n }\n\n return envelope.data;\n }\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n const normalized = baseUrl.trim().replace(/\\/+$/, '');\n if (!normalized) {\n throw new ChronosConfigError('Chronos baseUrl is required');\n }\n return normalized;\n}\n\nasync function parseJsonResponse<T>(response: Response): Promise<T | null> {\n try {\n return (await response.json()) as T;\n } catch {\n return null;\n }\n}\n\nfunction apiErrorMessage(\n response: Response,\n envelope: Record<string, unknown> | undefined,\n): string {\n if (typeof envelope?.message === 'string' && envelope.message.trim()) {\n return envelope.message;\n }\n\n return response.ok\n ? 'Chronos API returned an invalid response'\n : `Chronos API request failed with status ${response.status}`;\n}\n\n// Malformed bodies (arrays, primitives, missing `success`) are routed to the\n// invalid-response path, not classified as API errors.\nfunction isEnvelope(value: unknown): value is Record<string, unknown> & { success: boolean } {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n return typeof (value as Record<string, unknown>).success === 'boolean';\n}\n","import type { BaseClient } from './client';\nimport { ChronosApiError, ChronosError, ChronosHandlerError, ChronosNetworkError } from './errors';\nimport {\n normalizeHandlerName,\n validatePollWaitTime,\n validateRetryDelayMs,\n} from './internal/validate';\nimport type {\n ChronosContext,\n ChronosHandler,\n ChronosHandlerResult,\n ChronosOptions,\n ChronosSchedule,\n} from './types';\n\n/** Default worker long-poll wait time in seconds. Equal to the API maximum. */\nexport const DEFAULT_POLL_WAIT_TIME_SECONDS = 20;\nconst DEFAULT_RETRY_DELAY_MS = 1_000;\nconst RESULT_REPORT_MAX_ATTEMPTS = 3;\nconst MAX_REPORTED_ERROR_LENGTH = 4_096;\nconst DEFAULT_HANDLER_ERROR = 'Chronos handler failed';\n\ntype ResultStatus = 'completed' | 'failed';\n\ntype ClaimedJob = {\n job_id: string;\n execution_id: string;\n handler: string;\n scheduled_for: string;\n attempt: number;\n timeout: number;\n payload: unknown;\n schedule: ChronosSchedule | null;\n};\n\n/**\n * Long-poll worker. Claims jobs from the Chronos API, dispatches them to\n * registered handlers, and reports results.\n *\n * Construct via `new Chronos({ apiKey }).worker` rather than directly.\n */\nexport class Worker {\n private readonly client: BaseClient;\n private readonly pollWaitTimeSeconds: number;\n private readonly retryDelayMs: number;\n private readonly handlers = new Map<string, ChronosHandler<unknown>>();\n private handlerNames: string[] = [];\n private startPromise?: Promise<void>;\n private pollController?: AbortController;\n\n constructor(client: BaseClient, options: ChronosOptions) {\n this.client = client;\n this.pollWaitTimeSeconds = options.pollWaitTimeSeconds ?? DEFAULT_POLL_WAIT_TIME_SECONDS;\n this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n\n validatePollWaitTime(this.pollWaitTimeSeconds);\n validateRetryDelayMs(this.retryDelayMs);\n }\n\n /**\n * Register a handler for a named job type. Invoked when the Chronos API\n * claims a job whose `handler` field matches `name`. Names are trimmed and\n * must be 1–255 characters.\n *\n * @param name - Handler name. Must match the schedule's `handler` on the API side.\n * @param handler - Async function invoked with the job context. Return a\n * plain object to record a result, or `undefined` for none.\n * @returns The Worker, for chaining.\n * @throws {ChronosError} If the name is invalid, already registered, or `handler` is not a function.\n *\n * @example\n * ```ts\n * chronos.worker\n * .handle('send-email', async (ctx) => ({ sent: true }))\n * .handle('cleanup', async () => undefined);\n * ```\n */\n handle<TPayload = unknown>(name: string, handler: ChronosHandler<TPayload>): this {\n const normalizedName = normalizeHandlerName(name);\n\n if (this.handlers.has(normalizedName)) {\n throw new ChronosError(`Handler \"${normalizedName}\" is already registered`);\n }\n\n if (typeof handler !== 'function') {\n throw new ChronosError(`Handler \"${normalizedName}\" must be a function`);\n }\n\n this.handlers.set(normalizedName, handler as ChronosHandler<unknown>);\n this.handlerNames.push(normalizedName);\n return this;\n }\n\n /**\n * Begin long-polling for jobs. The returned promise resolves when\n * {@link Worker.stop} is called and any in-flight job completes.\n *\n * @throws {ChronosError} Synchronously, if no handlers are registered or the worker is already running.\n */\n start(): Promise<void> {\n if (this.startPromise) {\n throw new ChronosError('Chronos worker is already started');\n }\n\n if (this.handlers.size === 0) {\n throw new ChronosError('Register at least one handler before starting Chronos');\n }\n\n this.pollController = new AbortController();\n this.startPromise = this.runLoop().finally(() => {\n this.startPromise = undefined;\n this.pollController = undefined;\n });\n\n return this.startPromise;\n }\n\n /**\n * Request graceful shutdown. The poll loop is aborted immediately; any\n * in-flight handler and result-report are allowed to complete to preserve\n * at-least-once delivery. Returns the same promise as the active\n * {@link Worker.start}, or a resolved promise if the worker isn't running.\n */\n stop(): Promise<void> {\n this.pollController?.abort();\n return this.startPromise ?? Promise.resolve();\n }\n\n private get isStopped(): boolean {\n return this.pollController?.signal.aborted ?? true;\n }\n\n private async runLoop(): Promise<void> {\n while (!this.isStopped) {\n try {\n const job = await this.claimJob();\n if (job) {\n await this.processJob(job);\n }\n } catch (err) {\n if (this.isStopped) {\n break;\n }\n\n this.client.logger.error('Chronos poll loop error', { err: errorToLogValue(err) });\n await sleep(this.retryDelayMs, this.pollController?.signal);\n }\n }\n }\n\n private async claimJob(): Promise<ClaimedJob | null> {\n if (this.handlerNames.length === 0) {\n throw new ChronosError('Cannot claim jobs without registered handlers');\n }\n\n return this.client.request<ClaimedJob | null>(\n '/v1/worker/jobs/claim',\n {\n wait_time_seconds: this.pollWaitTimeSeconds,\n handlers: this.handlerNames,\n },\n this.pollController?.signal,\n );\n }\n\n private async processJob(job: ClaimedJob): Promise<void> {\n const handler = this.handlers.get(job.handler);\n if (!handler) {\n return this.handleUnregisteredJob(job);\n }\n\n let handlerResult: ChronosHandlerResult;\n try {\n handlerResult = await handler(createContext(job));\n } catch (err) {\n const message = errorMessage(err);\n const handlerErr = new ChronosHandlerError(message, { cause: err });\n this.client.logger.error('Chronos handler failed', {\n ...jobLogMeta(job),\n err: errorToLogValue(handlerErr),\n });\n await this.safeReportFailed(job, message);\n return;\n }\n\n let result: Record<string, unknown> | undefined;\n try {\n result = normalizeHandlerResult(handlerResult);\n } catch (err) {\n this.client.logger.error('Chronos handler returned invalid result', {\n ...jobLogMeta(job),\n err: errorToLogValue(err),\n });\n await this.safeReportFailed(job, errorMessage(err));\n return;\n }\n\n try {\n await this.reportCompleted(job.execution_id, result);\n } catch (err) {\n this.logResultReportFailure(err, job, 'completed');\n }\n }\n\n private async handleUnregisteredJob(job: ClaimedJob): Promise<void> {\n const message = `Chronos SDK received job for unregistered handler \"${job.handler}\"`;\n this.client.logger.error(message, jobLogMeta(job));\n await this.safeReportFailed(job, message);\n }\n\n private async safeReportFailed(job: ClaimedJob, message: string): Promise<void> {\n try {\n await this.reportFailed(job.execution_id, message);\n } catch (err) {\n this.logResultReportFailure(err, job, 'failed');\n }\n }\n\n private async reportCompleted(\n executionId: string,\n result: Record<string, unknown> | undefined,\n ): Promise<void> {\n await this.reportResultWithRetry(executionId, {\n status: 'completed',\n ...(result === undefined ? {} : { result }),\n });\n }\n\n private async reportFailed(executionId: string, error: string): Promise<void> {\n await this.reportResultWithRetry(executionId, {\n status: 'failed',\n error: truncate(error, MAX_REPORTED_ERROR_LENGTH),\n });\n }\n\n private async reportResultWithRetry(\n executionId: string,\n body: Record<string, unknown>,\n ): Promise<void> {\n let lastErr: unknown;\n\n for (let attempt = 1; attempt <= RESULT_REPORT_MAX_ATTEMPTS; attempt++) {\n try {\n await this.client.request(\n `/v1/worker/executions/${encodeURIComponent(executionId)}/result`,\n body,\n );\n return;\n } catch (err) {\n const apiErr = err instanceof ChronosApiError ? err : undefined;\n\n if (apiErr?.status === 409) {\n this.client.logger.warn('Chronos result discarded; execution already terminal', {\n executionId,\n status: body.status,\n code: apiErr.code,\n });\n return;\n }\n\n if (apiErr && isTerminalReportError(apiErr.status)) {\n throw apiErr;\n }\n\n lastErr = err;\n if (attempt === RESULT_REPORT_MAX_ATTEMPTS) {\n break;\n }\n\n this.client.logger.warn('Chronos result report failed; retrying', {\n err: errorToLogValue(err),\n executionId,\n status: body.status,\n attempt,\n maxAttempts: RESULT_REPORT_MAX_ATTEMPTS,\n });\n await sleep(this.retryDelayMs);\n }\n }\n\n throw lastErr;\n }\n\n private logResultReportFailure(err: unknown, job: ClaimedJob, status: ResultStatus): void {\n const isTerminal = err instanceof ChronosApiError && isTerminalReportError(err.status);\n this.client.logger.error(\n isTerminal\n ? 'Chronos result report rejected by API'\n : 'Chronos result report failed after retries',\n { ...jobLogMeta(job), err: errorToLogValue(err), status },\n );\n }\n}\n\nfunction createContext(job: ClaimedJob): ChronosContext<unknown> {\n return {\n jobId: job.job_id,\n executionId: job.execution_id,\n handler: job.handler,\n payload: job.payload,\n scheduledFor: new Date(job.scheduled_for),\n attempt: job.attempt,\n timeout: job.timeout,\n schedule: job.schedule,\n };\n}\n\nfunction jobLogMeta(job: ClaimedJob): Record<string, unknown> {\n return {\n jobId: job.job_id,\n executionId: job.execution_id,\n handler: job.handler,\n };\n}\n\nfunction normalizeHandlerResult(result: ChronosHandlerResult): Record<string, unknown> | undefined {\n if (result === undefined) {\n return undefined;\n }\n\n if (!isPlainObject(result)) {\n throw new ChronosError('Chronos handler result must be a plain object or undefined');\n }\n\n try {\n JSON.stringify(result);\n } catch (err) {\n const reason = err instanceof Error ? err.message : 'value is not JSON-encodable';\n throw new ChronosError(`Chronos handler result is not JSON-encodable: ${reason}`);\n }\n\n return result;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction errorMessage(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n return message.trim() || DEFAULT_HANDLER_ERROR;\n}\n\nfunction errorToLogValue(err: unknown): unknown {\n if (!(err instanceof Error)) {\n return err;\n }\n\n const base = { name: err.name, message: err.message, stack: err.stack };\n\n if (err instanceof ChronosApiError) {\n return { ...base, status: err.status, code: err.code, requestId: err.requestId };\n }\n if (err instanceof ChronosNetworkError || err instanceof ChronosHandlerError) {\n return { ...base, cause: errorToLogValue(err.cause) };\n }\n return base;\n}\n\nfunction truncate(value: string, maxLength: number): string {\n return value.length <= maxLength ? value : value.slice(0, maxLength);\n}\n\n// 409 is handled by the caller before reaching here; 408/429 are transient.\nconst NON_TERMINAL_4XX = new Set([408, 409, 429]);\n\n// 200 reaches here only via an explicit `{ success: false }` envelope from BaseClient.\nfunction isTerminalReportError(status: number): boolean {\n if (status === 200) {\n return true;\n }\n if (NON_TERMINAL_4XX.has(status)) {\n return false;\n }\n return status >= 400 && status < 500;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n const timeout: ReturnType<typeof setTimeout> = setTimeout(done, ms);\n\n function done() {\n clearTimeout(timeout);\n signal?.removeEventListener('abort', done);\n resolve();\n }\n\n signal?.addEventListener('abort', done, { once: true });\n });\n}\n","import { BaseClient, DEFAULT_BASE_URL } from './client';\nimport type { ChronosOptions } from './types';\nimport { DEFAULT_POLL_WAIT_TIME_SECONDS, Worker } from './worker';\n\n/**\n * The Chronos SDK client. Composes worker and (future) REST resource\n * subclients from a single instance.\n *\n * @example\n * ```ts\n * import { Chronos } from '@chronos.sh/sdk';\n *\n * const chronos = new Chronos({ apiKey: process.env.CHRONOS_API_KEY! });\n *\n * chronos.worker.handle<{ to: string }>('send-email', async (ctx) => {\n * await sendEmail(ctx.payload.to);\n * return { sent: true };\n * });\n *\n * await chronos.worker.start();\n * ```\n */\nexport class Chronos {\n /** Long-poll worker for executing pull-mode jobs. */\n readonly worker: Worker;\n\n /**\n * @param options - Client configuration. Only `apiKey` is required.\n * @throws {ChronosConfigError} If `apiKey` is missing or any option fails validation.\n */\n constructor(options: ChronosOptions) {\n const client = new BaseClient(options);\n this.worker = new Worker(client, options);\n }\n}\n\nexport {\n ChronosApiError,\n type ChronosApiErrorOptions,\n ChronosConfigError,\n ChronosError,\n ChronosHandlerError,\n ChronosNetworkError,\n} from './errors';\nexport type {\n ChronosContext,\n ChronosHandler,\n ChronosHandlerResult,\n ChronosLogger,\n ChronosOptions,\n ChronosSchedule,\n FetchLike,\n} from './types';\nexport { DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/errors.ts","../src/internal/logger.ts","../src/internal/validate.ts","../src/client.ts","../src/worker.ts","../src/webhook.ts","../src/index.ts"],"sourcesContent":["/**\n * Base class for all errors thrown by the Chronos SDK. Catch this in a\n * single `catch` to handle any SDK failure generically; use the subclasses\n * to branch on cause.\n *\n * @example\n * ```ts\n * import { Chronos, ChronosError } from '@chronos.sh/sdk';\n *\n * const chronos = new Chronos({ apiKey: 'chrns_...' });\n * try {\n * await chronos.worker.start();\n * } catch (err) {\n * if (err instanceof ChronosError) {\n * console.error('Chronos failed:', err.message);\n * }\n * }\n * ```\n */\nexport class ChronosError extends Error {\n /** Original error/value that caused this SDK error, when one is available. */\n declare readonly cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'ChronosError';\n\n if (options && !('cause' in this)) {\n Object.defineProperty(this, 'cause', {\n value: options.cause,\n configurable: true,\n writable: true,\n });\n }\n }\n}\n\n/**\n * Thrown when SDK options fail validation at `new Chronos({ ... })`. Covers\n * `apiKey`, `baseUrl`, `pollWaitTimeSeconds`, and `retryDelayMs`.\n *\n * @example\n * ```ts\n * import { Chronos, ChronosConfigError } from '@chronos.sh/sdk';\n *\n * try {\n * const chronos = new Chronos({ apiKey: '' });\n * } catch (err) {\n * if (err instanceof ChronosConfigError) {\n * console.error('Invalid Chronos config:', err.message);\n * }\n * }\n * ```\n */\nexport class ChronosConfigError extends ChronosError {\n constructor(message: string) {\n super(message);\n this.name = 'ChronosConfigError';\n }\n}\n\n/** Rate-limit budget from `X-RateLimit-*` response headers. Present on non-429 responses. */\nexport type RateLimitInfo = {\n /** Maximum requests allowed per window. */\n limit: number;\n /** Requests remaining in the current window. */\n remaining: number;\n /** Seconds until the current window resets. */\n reset: number;\n};\n\n/** Options for constructing a {@link ChronosApiError}. */\nexport type ChronosApiErrorOptions = {\n /** HTTP status code returned by the Chronos API. */\n status: number;\n /** Application-level error code from the response envelope, when present. */\n code?: string;\n /** Full parsed response payload (envelope + data, or whatever the server returned). */\n body?: unknown;\n /** Value of the `X-Request-Id` response header, when present. Pair with server logs. */\n requestId?: string;\n /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */\n retryAfterSeconds?: number;\n /** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */\n rateLimit?: RateLimitInfo;\n};\n\n/**\n * Thrown when the Chronos API responds with a non-2xx status or a\n * `success: false` envelope. Carries HTTP `status`, the application\n * `code`, parsed `body`, the API's `X-Request-Id`, and — when the\n * relevant headers are present — rate-limit metadata\n * (`retryAfterSeconds` on 429, `rateLimit` on other errors).\n *\n * `instanceof ChronosApiError` means the server replied;\n * network/transport failures throw {@link ChronosNetworkError} instead.\n * For 429 specifically, see {@link ChronosRateLimitError}.\n *\n * @example\n * ```ts\n * import { ChronosApiError } from '@chronos.sh/sdk';\n *\n * function handleSdkError(err: unknown) {\n * if (err instanceof ChronosApiError) {\n * if (err.status === 401) return refreshAuth();\n * console.error('API error', { status: err.status, requestId: err.requestId });\n * }\n * }\n * ```\n */\nexport class ChronosApiError extends ChronosError {\n /** HTTP status code returned by the Chronos API. */\n readonly status: number;\n /** Application-level error code from the response envelope, when present. */\n readonly code?: string;\n /** Full parsed response payload (envelope + data, or whatever the server returned). */\n readonly body?: unknown;\n /** Value of the `X-Request-Id` response header, when present. */\n readonly requestId?: string;\n /** Seconds to wait before retrying, from the `Retry-After` response header. Present on 429 responses. */\n readonly retryAfterSeconds?: number;\n /** Rate-limit budget from `X-RateLimit-*` headers. Present on non-429 responses. */\n readonly rateLimit?: RateLimitInfo;\n\n constructor(message: string, options: ChronosApiErrorOptions) {\n super(message);\n this.name = 'ChronosApiError';\n this.status = options.status;\n this.code = options.code;\n this.body = options.body;\n this.requestId = options.requestId;\n this.retryAfterSeconds = options.retryAfterSeconds;\n this.rateLimit = options.rateLimit;\n }\n}\n\n/**\n * Thrown when the Chronos API responds with `429 Too Many Requests`.\n * Subclass of {@link ChronosApiError} — `instanceof ChronosRateLimitError`\n * narrows to a rate-limited response. `retryAfterSeconds` is set when the\n * response includes a `Retry-After` header; otherwise it is `undefined`.\n *\n * @example\n * ```ts\n * import { ChronosRateLimitError } from '@chronos.sh/sdk';\n *\n * function handleSdkError(err: unknown) {\n * if (err instanceof ChronosRateLimitError && err.retryAfterSeconds) {\n * console.warn(`Rate limited — retry after ${err.retryAfterSeconds}s`);\n * }\n * }\n * ```\n */\nexport class ChronosRateLimitError extends ChronosApiError {\n constructor(message: string, options: ChronosApiErrorOptions) {\n super(message, options);\n this.name = 'ChronosRateLimitError';\n }\n}\n\n/**\n * Thrown when the underlying `fetch` rejects before the server replies —\n * DNS failure, TCP reset, connection refused, etc. The original error is\n * available on `.cause`.\n *\n * Abort signals propagate unwrapped — `instanceof ChronosNetworkError`\n * always means a real transport failure, not a graceful shutdown.\n *\n * @example\n * ```ts\n * import { ChronosNetworkError } from '@chronos.sh/sdk';\n *\n * function handleSdkError(err: unknown) {\n * if (err instanceof ChronosNetworkError) {\n * console.warn('Transport blip', { cause: err.cause });\n * }\n * }\n * ```\n */\nexport class ChronosNetworkError extends ChronosError {\n /** Original error/value rejected by the underlying `fetch`. */\n declare readonly cause: unknown;\n\n constructor(message: string, options: { cause: unknown }) {\n super(message, options);\n this.name = 'ChronosNetworkError';\n }\n}\n\n/**\n * Wraps an exception thrown by a user-supplied {@link ChronosHandler}. The\n * original error is on `.cause`; `.message` is copied from the original so\n * the SDK reports it to the API as the failure reason.\n *\n * @example\n * ```ts\n * import { ChronosHandlerError } from '@chronos.sh/sdk';\n *\n * if (err instanceof ChronosHandlerError) {\n * console.error('Handler threw', err.cause);\n * }\n * ```\n */\nexport class ChronosHandlerError extends ChronosError {\n /** Original error/value thrown by the user-supplied handler. */\n declare readonly cause: unknown;\n\n constructor(message: string, options: { cause: unknown }) {\n super(message, options);\n this.name = 'ChronosHandlerError';\n }\n}\n\n/**\n * Thrown by {@link Webhook.verify} when signature verification fails.\n * Covers missing or malformed headers, expired timestamps, and signature\n * mismatches.\n *\n * @example\n * ```ts\n * import { Webhook, ChronosWebhookVerificationError } from '@chronos.sh/sdk';\n *\n * const wh = new Webhook(process.env.CHRONOS_SIGNING_SECRET!);\n * try {\n * const payload = await wh.verify(rawBody, headers);\n * } catch (err) {\n * if (err instanceof ChronosWebhookVerificationError) {\n * console.error('Webhook verification failed:', err.message);\n * }\n * }\n * ```\n */\nexport class ChronosWebhookVerificationError extends ChronosError {\n constructor(message: string) {\n super(message);\n this.name = 'ChronosWebhookVerificationError';\n }\n}\n","import type { ChronosLogger } from '../types';\n\nexport const defaultLogger: ChronosLogger = {\n debug: (message, meta) => logToConsole(console.debug, message, meta),\n info: (message, meta) => logToConsole(console.info, message, meta),\n warn: (message, meta) => logToConsole(console.warn, message, meta),\n error: (message, meta) => logToConsole(console.error, message, meta),\n};\n\nfunction logToConsole(\n method: (message?: unknown, ...optionalParams: unknown[]) => void,\n message: string,\n meta?: Record<string, unknown>,\n): void {\n if (meta) {\n method(message, meta);\n } else {\n method(message);\n }\n}\n","import { ChronosConfigError, ChronosError } from '../errors';\n\nexport const MAX_HANDLER_NAME_LENGTH = 255;\nexport const MAX_POLL_WAIT_TIME_SECONDS = 20;\n\nexport function validateApiKey(apiKey: string | undefined): string {\n const trimmed = apiKey?.trim();\n if (!trimmed) {\n throw new ChronosConfigError('Chronos apiKey is required');\n }\n return trimmed;\n}\n\nexport function validatePollWaitTime(seconds: number): void {\n if (!Number.isInteger(seconds) || seconds < 0 || seconds > MAX_POLL_WAIT_TIME_SECONDS) {\n throw new ChronosConfigError(\n `pollWaitTimeSeconds must be an integer between 0 and ${MAX_POLL_WAIT_TIME_SECONDS}`,\n );\n }\n}\n\nexport function validateRetryDelayMs(ms: number): void {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new ChronosConfigError('retryDelayMs must be a non-negative number');\n }\n}\n\nexport function normalizeHandlerName(name: string): string {\n const normalized = name.trim();\n\n if (!normalized) {\n throw new ChronosError('Handler name is required');\n }\n\n if (normalized.length > MAX_HANDLER_NAME_LENGTH) {\n throw new ChronosError(`Handler name must be ${MAX_HANDLER_NAME_LENGTH} characters or fewer`);\n }\n\n return normalized;\n}\n","import {\n ChronosApiError,\n ChronosConfigError,\n ChronosError,\n ChronosNetworkError,\n ChronosRateLimitError,\n type RateLimitInfo,\n} from './errors';\nimport { defaultLogger } from './internal/logger';\nimport { validateApiKey } from './internal/validate';\nimport type { ChronosLogger, ChronosOptions, FetchLike } from './types';\n\n/** Default Chronos API base URL. */\nexport const DEFAULT_BASE_URL = 'https://api.chronos.sh';\n\ntype ApiResponse<T> = {\n success: boolean;\n message?: string;\n code?: string;\n data: T;\n};\n\nexport class BaseClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly fetch: FetchLike;\n readonly logger: ChronosLogger;\n private readonly headers: Record<string, string>;\n\n constructor(options: ChronosOptions) {\n this.apiKey = validateApiKey(options.apiKey);\n this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL);\n this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.logger = options.logger ?? defaultLogger;\n this.headers = {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n };\n }\n\n async request<T>(path: string, body: Record<string, unknown>, signal?: AbortSignal): Promise<T> {\n let response: Response;\n try {\n response = await this.fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(body),\n signal,\n });\n } catch (err) {\n if (signal?.aborted) {\n throw err;\n }\n const message = err instanceof Error ? err.message : String(err);\n throw new ChronosNetworkError(`Chronos API request failed: ${message}`, { cause: err });\n }\n\n const payload = await parseJsonResponse<ApiResponse<T>>(response);\n const envelope = isEnvelope(payload) ? (payload as ApiResponse<T>) : undefined;\n\n if (!response.ok || (envelope && !envelope.success)) {\n const is429 = response.status === 429;\n const ErrorClass = is429 ? ChronosRateLimitError : ChronosApiError;\n throw new ErrorClass(apiErrorMessage(response, envelope), {\n status: response.status,\n code: typeof envelope?.code === 'string' ? envelope.code : undefined,\n body: payload,\n requestId: response.headers.get('x-request-id') ?? undefined,\n retryAfterSeconds: is429 ? parseIntHeader(response.headers, 'retry-after') : undefined,\n rateLimit: is429 ? undefined : parseRateLimitHeaders(response.headers),\n });\n }\n\n if (!envelope) {\n // Plain ChronosError, not ChronosApiError — a malformed 200 body is\n // an infrastructure anomaly that should be retried, not a definitive\n // API rejection.\n throw new ChronosError('Chronos API returned an invalid response');\n }\n\n return envelope.data;\n }\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n const normalized = baseUrl.trim().replace(/\\/+$/, '');\n if (!normalized) {\n throw new ChronosConfigError('Chronos baseUrl is required');\n }\n return normalized;\n}\n\nasync function parseJsonResponse<T>(response: Response): Promise<T | null> {\n try {\n return (await response.json()) as T;\n } catch {\n return null;\n }\n}\n\nfunction apiErrorMessage(\n response: Response,\n envelope: Record<string, unknown> | undefined,\n): string {\n if (typeof envelope?.message === 'string' && envelope.message.trim()) {\n return envelope.message;\n }\n\n return response.ok\n ? 'Chronos API returned an invalid response'\n : `Chronos API request failed with status ${response.status}`;\n}\n\nfunction parseIntHeader(headers: Headers, name: string): number | undefined {\n const raw = headers.get(name);\n if (raw === null || !/^\\d+$/.test(raw)) {\n return undefined;\n }\n return Number.parseInt(raw, 10);\n}\n\nfunction parseRateLimitHeaders(headers: Headers): RateLimitInfo | undefined {\n const limit = parseIntHeader(headers, 'x-ratelimit-limit');\n const remaining = parseIntHeader(headers, 'x-ratelimit-remaining');\n const reset = parseIntHeader(headers, 'x-ratelimit-reset');\n\n if (limit === undefined || remaining === undefined || reset === undefined) {\n return undefined;\n }\n return { limit, remaining, reset };\n}\n\n// Malformed bodies (arrays, primitives, missing `success`) are routed to the\n// invalid-response path, not classified as API errors.\nfunction isEnvelope(value: unknown): value is Record<string, unknown> & { success: boolean } {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n return typeof (value as Record<string, unknown>).success === 'boolean';\n}\n","import type { BaseClient } from './client';\nimport { ChronosApiError, ChronosError, ChronosHandlerError, ChronosNetworkError } from './errors';\nimport {\n normalizeHandlerName,\n validatePollWaitTime,\n validateRetryDelayMs,\n} from './internal/validate';\nimport type {\n ChronosContext,\n ChronosHandler,\n ChronosHandlerResult,\n ChronosOptions,\n ChronosSchedule,\n} from './types';\n\n/** Default worker long-poll wait time in seconds. Equal to the API maximum. */\nexport const DEFAULT_POLL_WAIT_TIME_SECONDS = 20;\nconst DEFAULT_RETRY_DELAY_MS = 1_000;\nconst RESULT_REPORT_MAX_ATTEMPTS = 3;\nconst MAX_REPORTED_ERROR_LENGTH = 4_096;\nconst DEFAULT_HANDLER_ERROR = 'Chronos handler failed';\n\ntype ResultStatus = 'completed' | 'failed' | 'timeout';\n\ntype HandlerOutcome = { timedOut: false; result: ChronosHandlerResult } | { timedOut: true };\n\ntype ClaimedJob = {\n job_id: string;\n execution_id: string;\n handler: string;\n scheduled_for: string;\n attempt: number;\n timeout: number;\n payload: unknown;\n schedule: ChronosSchedule | null;\n};\n\n/**\n * Long-poll worker. Claims jobs from the Chronos API, dispatches them to\n * registered handlers, and reports results.\n *\n * Construct via `new Chronos({ apiKey }).worker` rather than directly.\n */\nexport class Worker {\n private readonly client: BaseClient;\n private readonly pollWaitTimeSeconds: number;\n private readonly retryDelayMs: number;\n private readonly handlers = new Map<string, ChronosHandler<unknown>>();\n private handlerNames: string[] = [];\n private startPromise?: Promise<void>;\n private pollController?: AbortController;\n\n constructor(client: BaseClient, options: ChronosOptions) {\n this.client = client;\n this.pollWaitTimeSeconds = options.pollWaitTimeSeconds ?? DEFAULT_POLL_WAIT_TIME_SECONDS;\n this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n\n validatePollWaitTime(this.pollWaitTimeSeconds);\n validateRetryDelayMs(this.retryDelayMs);\n }\n\n /**\n * Register a handler for a named job type. Invoked when the Chronos API\n * claims a job whose `handler` field matches `name`. Names are trimmed and\n * must be 1–255 characters.\n *\n * @param name - Handler name. Must match the schedule's `handler` on the API side.\n * @param handler - Async function invoked with the job context. Return a\n * plain object to record a result, or `undefined` for none.\n * @returns The Worker, for chaining.\n * @throws {ChronosError} If the name is invalid, already registered, or `handler` is not a function.\n *\n * @example\n * ```ts\n * chronos.worker\n * .handle('send-email', async (ctx) => ({ sent: true }))\n * .handle('cleanup', async () => undefined);\n * ```\n */\n handle<TPayload = unknown>(name: string, handler: ChronosHandler<TPayload>): this {\n const normalizedName = normalizeHandlerName(name);\n\n if (this.handlers.has(normalizedName)) {\n throw new ChronosError(`Handler \"${normalizedName}\" is already registered`);\n }\n\n if (typeof handler !== 'function') {\n throw new ChronosError(`Handler \"${normalizedName}\" must be a function`);\n }\n\n this.handlers.set(normalizedName, handler as ChronosHandler<unknown>);\n this.handlerNames.push(normalizedName);\n return this;\n }\n\n /**\n * Begin long-polling for jobs. The returned promise resolves when\n * {@link Worker.stop} is called and any in-flight job completes.\n *\n * @throws {ChronosError} Synchronously, if no handlers are registered or the worker is already running.\n */\n start(): Promise<void> {\n if (this.startPromise) {\n throw new ChronosError('Chronos worker is already started');\n }\n\n if (this.handlers.size === 0) {\n throw new ChronosError('Register at least one handler before starting Chronos');\n }\n\n this.pollController = new AbortController();\n this.startPromise = this.runLoop().finally(() => {\n this.startPromise = undefined;\n this.pollController = undefined;\n });\n\n return this.startPromise;\n }\n\n /**\n * Request graceful shutdown. The poll loop is aborted immediately; any\n * in-flight handler that has not exceeded its timeout, along with the\n * subsequent result-report, is allowed to complete. Timed-out handlers\n * are abandoned (their {@link ChronosContext.signal} fires, but the SDK\n * does not wait for them). Returns the same promise as the active\n * {@link Worker.start}, or a resolved promise if the worker isn't running.\n */\n stop(): Promise<void> {\n this.pollController?.abort();\n return this.startPromise ?? Promise.resolve();\n }\n\n private get isStopped(): boolean {\n return this.pollController?.signal.aborted ?? true;\n }\n\n private async runLoop(): Promise<void> {\n while (!this.isStopped) {\n try {\n const job = await this.claimJob();\n if (job) {\n await this.processJob(job);\n }\n } catch (err) {\n if (this.isStopped) {\n break;\n }\n\n this.client.logger.error('Chronos poll loop error', { err: errorToLogValue(err) });\n await sleep(retryDelayFor(err, this.retryDelayMs), this.pollController?.signal);\n }\n }\n }\n\n private async claimJob(): Promise<ClaimedJob | null> {\n if (this.handlerNames.length === 0) {\n throw new ChronosError('Cannot claim jobs without registered handlers');\n }\n\n return this.client.request<ClaimedJob | null>(\n '/v1/worker/jobs/claim',\n {\n wait_time_seconds: this.pollWaitTimeSeconds,\n handlers: this.handlerNames,\n },\n this.pollController?.signal,\n );\n }\n\n private async processJob(job: ClaimedJob): Promise<void> {\n const handler = this.handlers.get(job.handler);\n if (!handler) {\n return this.handleUnregisteredJob(job);\n }\n\n const controller = new AbortController();\n let outcome: HandlerOutcome;\n try {\n outcome = await this.runHandlerWithTimeout(\n handler,\n createContext(job, controller.signal),\n job.timeout * 1_000,\n controller,\n );\n } catch (err) {\n const message = errorMessage(err);\n const handlerErr = new ChronosHandlerError(message, { cause: err });\n this.client.logger.error('Chronos handler failed', {\n ...jobLogMeta(job),\n err: errorToLogValue(handlerErr),\n });\n await this.safeReportFailed(job, message);\n return;\n }\n\n if (outcome.timedOut) {\n this.client.logger.error('Chronos handler timed out', {\n ...jobLogMeta(job),\n timeout: job.timeout,\n });\n try {\n await this.reportTimeout(job.execution_id);\n } catch (err) {\n this.logResultReportFailure(err, job, 'timeout');\n }\n return;\n }\n\n let result: Record<string, unknown> | undefined;\n try {\n result = normalizeHandlerResult(outcome.result);\n } catch (err) {\n this.client.logger.error('Chronos handler returned invalid result', {\n ...jobLogMeta(job),\n err: errorToLogValue(err),\n });\n await this.safeReportFailed(job, errorMessage(err));\n return;\n }\n\n try {\n await this.reportCompleted(job.execution_id, result);\n } catch (err) {\n this.logResultReportFailure(err, job, 'completed');\n }\n }\n\n private runHandlerWithTimeout(\n handler: ChronosHandler<unknown>,\n ctx: ChronosContext<unknown>,\n timeoutMs: number,\n controller: AbortController,\n ): Promise<HandlerOutcome> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const handlerPromise = Promise.resolve(handler(ctx)).then(\n (result): HandlerOutcome => ({ timedOut: false, result }),\n );\n handlerPromise.catch(() => {});\n\n const timeoutPromise = new Promise<HandlerOutcome>((resolve) => {\n timer = setTimeout(() => {\n controller.abort();\n resolve({ timedOut: true });\n }, timeoutMs);\n });\n\n return Promise.race([handlerPromise, timeoutPromise]).finally(() => {\n clearTimeout(timer);\n });\n }\n\n private async handleUnregisteredJob(job: ClaimedJob): Promise<void> {\n const message = `Chronos SDK received job for unregistered handler \"${job.handler}\"`;\n this.client.logger.error(message, jobLogMeta(job));\n await this.safeReportFailed(job, message);\n }\n\n private async safeReportFailed(job: ClaimedJob, message: string): Promise<void> {\n try {\n await this.reportFailed(job.execution_id, message);\n } catch (err) {\n this.logResultReportFailure(err, job, 'failed');\n }\n }\n\n private async reportCompleted(\n executionId: string,\n result: Record<string, unknown> | undefined,\n ): Promise<void> {\n await this.reportResultWithRetry(executionId, {\n status: 'completed',\n ...(result === undefined ? {} : { result }),\n });\n }\n\n private async reportTimeout(executionId: string): Promise<void> {\n await this.reportResultWithRetry(executionId, { status: 'timeout' });\n }\n\n private async reportFailed(executionId: string, error: string): Promise<void> {\n await this.reportResultWithRetry(executionId, {\n status: 'failed',\n error: truncate(error, MAX_REPORTED_ERROR_LENGTH),\n });\n }\n\n private async reportResultWithRetry(\n executionId: string,\n body: Record<string, unknown>,\n ): Promise<void> {\n let lastErr: unknown;\n\n for (let attempt = 1; attempt <= RESULT_REPORT_MAX_ATTEMPTS; attempt++) {\n try {\n await this.client.request(\n `/v1/worker/executions/${encodeURIComponent(executionId)}/result`,\n body,\n );\n return;\n } catch (err) {\n const apiErr = err instanceof ChronosApiError ? err : undefined;\n\n if (apiErr?.status === 409) {\n this.client.logger.warn('Chronos result discarded; execution already terminal', {\n executionId,\n status: body.status,\n code: apiErr.code,\n });\n return;\n }\n\n if (apiErr && isTerminalReportError(apiErr.status)) {\n throw apiErr;\n }\n\n lastErr = err;\n if (attempt === RESULT_REPORT_MAX_ATTEMPTS) {\n break;\n }\n\n this.client.logger.warn('Chronos result report failed; retrying', {\n err: errorToLogValue(err),\n executionId,\n status: body.status,\n attempt,\n maxAttempts: RESULT_REPORT_MAX_ATTEMPTS,\n });\n await sleep(retryDelayFor(err, this.retryDelayMs));\n }\n }\n\n throw lastErr;\n }\n\n private logResultReportFailure(err: unknown, job: ClaimedJob, status: ResultStatus): void {\n const isTerminal = err instanceof ChronosApiError && isTerminalReportError(err.status);\n this.client.logger.error(\n isTerminal\n ? 'Chronos result report rejected by API'\n : 'Chronos result report failed after retries',\n { ...jobLogMeta(job), err: errorToLogValue(err), status },\n );\n }\n}\n\nfunction createContext(job: ClaimedJob, signal: AbortSignal): ChronosContext<unknown> {\n return {\n jobId: job.job_id,\n executionId: job.execution_id,\n handler: job.handler,\n payload: job.payload,\n scheduledFor: new Date(job.scheduled_for),\n attempt: job.attempt,\n timeout: job.timeout,\n signal,\n schedule: job.schedule,\n };\n}\n\nfunction jobLogMeta(job: ClaimedJob): Record<string, unknown> {\n return {\n jobId: job.job_id,\n executionId: job.execution_id,\n handler: job.handler,\n };\n}\n\nfunction normalizeHandlerResult(result: ChronosHandlerResult): Record<string, unknown> | undefined {\n if (result === undefined) {\n return undefined;\n }\n\n if (!isPlainObject(result)) {\n throw new ChronosError('Chronos handler result must be a plain object or undefined');\n }\n\n try {\n JSON.stringify(result);\n } catch (err) {\n const reason = err instanceof Error ? err.message : 'value is not JSON-encodable';\n throw new ChronosError(`Chronos handler result is not JSON-encodable: ${reason}`);\n }\n\n return result;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction errorMessage(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n return message.trim() || DEFAULT_HANDLER_ERROR;\n}\n\nfunction errorToLogValue(err: unknown): unknown {\n if (!(err instanceof Error)) {\n return err;\n }\n\n const base = { name: err.name, message: err.message, stack: err.stack };\n\n if (err instanceof ChronosApiError) {\n return {\n ...base,\n status: err.status,\n code: err.code,\n requestId: err.requestId,\n ...(err.retryAfterSeconds !== undefined && { retryAfterSeconds: err.retryAfterSeconds }),\n ...(err.rateLimit !== undefined && { rateLimit: err.rateLimit }),\n };\n }\n if (err instanceof ChronosNetworkError || err instanceof ChronosHandlerError) {\n return { ...base, cause: errorToLogValue(err.cause) };\n }\n return base;\n}\n\nfunction truncate(value: string, maxLength: number): string {\n return value.length <= maxLength ? value : value.slice(0, maxLength);\n}\n\nfunction retryDelayFor(err: unknown, fallbackMs: number): number {\n if (err instanceof ChronosApiError && err.retryAfterSeconds !== undefined) {\n return err.retryAfterSeconds * 1_000;\n }\n return fallbackMs;\n}\n\n// 409 is handled by the caller before reaching here; 408/429 are transient.\nconst NON_TERMINAL_4XX = new Set([408, 409, 429]);\n\n// 200 reaches here only via an explicit `{ success: false }` envelope from BaseClient.\nfunction isTerminalReportError(status: number): boolean {\n if (status === 200) {\n return true;\n }\n if (NON_TERMINAL_4XX.has(status)) {\n return false;\n }\n return status >= 400 && status < 500;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n const timeout: ReturnType<typeof setTimeout> = setTimeout(done, ms);\n\n function done() {\n clearTimeout(timeout);\n signal?.removeEventListener('abort', done);\n resolve();\n }\n\n signal?.addEventListener('abort', done, { once: true });\n });\n}\n","import { ChronosWebhookVerificationError } from './errors';\nimport type { WebhookOptions } from './types';\n\nexport const DEFAULT_TIMESTAMP_TOLERANCE_SECONDS = 300;\n\nconst SIGNATURE_PREFIX = 'sha256=';\nconst REQUIRED_HEADERS = [\n 'x-chronos-signature',\n 'x-chronos-timestamp',\n 'x-chronos-delivery-id',\n] as const;\n\n/**\n * Verifies webhook signatures on Chronos push deliveries.\n *\n * @example\n * ```ts\n * import { Webhook } from '@chronos.sh/sdk';\n *\n * const wh = new Webhook(process.env.CHRONOS_SIGNING_SECRET!);\n *\n * // In your request handler:\n * const payload = await wh.verify(rawBody, {\n * 'x-chronos-signature': req.headers['x-chronos-signature'],\n * 'x-chronos-timestamp': req.headers['x-chronos-timestamp'],\n * 'x-chronos-delivery-id': req.headers['x-chronos-delivery-id'],\n * });\n * ```\n */\nexport class Webhook {\n private readonly secrets: string[];\n private readonly toleranceSeconds: number;\n private keysPromise?: Promise<CryptoKey[]>;\n\n /**\n * @param secret - Signing secret or array of secrets (for key rotation).\n * @param options - Optional configuration.\n * @throws {ChronosWebhookVerificationError} If no valid secret is provided.\n */\n constructor(secret: string | string[], options?: WebhookOptions) {\n const secrets = Array.isArray(secret) ? secret : [secret];\n this.secrets = secrets\n .filter((s) => typeof s === 'string' && s.trim().length > 0)\n .map((s) => s.trim());\n\n if (this.secrets.length === 0) {\n throw new ChronosWebhookVerificationError(\n 'At least one non-empty signing secret is required',\n );\n }\n\n const tolerance = options?.toleranceSeconds ?? DEFAULT_TIMESTAMP_TOLERANCE_SECONDS;\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new ChronosWebhookVerificationError(\n 'toleranceSeconds must be a finite non-negative number',\n );\n }\n this.toleranceSeconds = tolerance;\n }\n\n /**\n * Verifies the webhook signature and returns the parsed request body.\n *\n * @param body - Raw request body as a string. Must be the exact bytes received — not re-serialized.\n * @param headers - Request headers. Keys are matched case-insensitively.\n * @returns The parsed JSON body on successful verification.\n * @throws {ChronosWebhookVerificationError} If any verification step fails.\n */\n async verify(\n body: string,\n headers: Record<string, string | undefined> | Headers,\n ): Promise<unknown> {\n const normalized = normalizeHeaders(headers);\n\n for (const name of REQUIRED_HEADERS) {\n const value = normalized[name];\n if (!value || typeof value !== 'string') {\n throw new ChronosWebhookVerificationError(`Missing required header: ${name}`);\n }\n }\n\n const signature = normalized['x-chronos-signature'] as string;\n const timestamp = normalized['x-chronos-timestamp'] as string;\n const deliveryId = normalized['x-chronos-delivery-id'] as string;\n\n if (!/^[0-9]+$/.test(timestamp)) {\n throw new ChronosWebhookVerificationError('Invalid timestamp');\n }\n\n const ts = Number(timestamp);\n if (ts <= 0) {\n throw new ChronosWebhookVerificationError('Invalid timestamp');\n }\n\n const now = Math.floor(Date.now() / 1000);\n const isStale = Math.abs(now - ts) > this.toleranceSeconds;\n if (this.toleranceSeconds > 0 && isStale) {\n throw new ChronosWebhookVerificationError('Timestamp outside tolerance');\n }\n\n if (!signature.startsWith(SIGNATURE_PREFIX)) {\n throw new ChronosWebhookVerificationError(\n `Invalid signature format: expected ${SIGNATURE_PREFIX}<hex>`,\n );\n }\n\n const expectedHex = signature.slice(SIGNATURE_PREFIX.length);\n if (expectedHex.length === 0) {\n throw new ChronosWebhookVerificationError(\n 'Invalid signature: expected 32-byte SHA-256 digest',\n );\n }\n\n let expectedBytes: ArrayBuffer;\n try {\n expectedBytes = hexDecode(expectedHex);\n } catch {\n throw new ChronosWebhookVerificationError('Invalid signature: malformed hex');\n }\n\n if (expectedBytes.byteLength !== 32) {\n throw new ChronosWebhookVerificationError(\n 'Invalid signature: expected 32-byte SHA-256 digest',\n );\n }\n\n const signedPayload = `${deliveryId}.${timestamp}.${body}`;\n const data = new TextEncoder().encode(signedPayload);\n\n try {\n const keys = await this.ensureKeys();\n for (const key of keys) {\n const valid = await globalThis.crypto.subtle.verify('HMAC', key, expectedBytes, data);\n if (valid) {\n try {\n return JSON.parse(body);\n } catch {\n throw new ChronosWebhookVerificationError('Verified payload is not valid JSON');\n }\n }\n }\n } catch (err) {\n if (err instanceof ChronosWebhookVerificationError) {\n throw err;\n }\n throw new ChronosWebhookVerificationError('Signature verification failed');\n }\n\n throw new ChronosWebhookVerificationError('Signature verification failed');\n }\n\n private ensureKeys(): Promise<CryptoKey[]> {\n if (!this.keysPromise) {\n const encoder = new TextEncoder();\n this.keysPromise = Promise.all(\n this.secrets.map((s) =>\n globalThis.crypto.subtle.importKey(\n 'raw',\n encoder.encode(s),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['verify'],\n ),\n ),\n );\n }\n return this.keysPromise;\n }\n}\n\nfunction normalizeHeaders(\n headers: Record<string, string | undefined> | Headers,\n): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n result[key.toLowerCase()] = value;\n });\n } else {\n for (const key of Object.keys(headers)) {\n result[key.toLowerCase()] = headers[key];\n }\n }\n return result;\n}\n\nfunction hexDecode(hex: string): ArrayBuffer {\n if (hex.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(hex)) {\n throw new Error('Invalid hex string');\n }\n const length = hex.length / 2;\n const buffer = new ArrayBuffer(length);\n const view = new DataView(buffer);\n for (let i = 0; i < length; i++) {\n view.setUint8(i, Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16));\n }\n return buffer;\n}\n","import { BaseClient, DEFAULT_BASE_URL } from './client';\nimport type { ChronosOptions } from './types';\nimport { DEFAULT_POLL_WAIT_TIME_SECONDS, Worker } from './worker';\n\n/**\n * The Chronos SDK client. Composes worker and (future) REST resource\n * subclients from a single instance.\n *\n * @example\n * ```ts\n * import { Chronos } from '@chronos.sh/sdk';\n *\n * const chronos = new Chronos({ apiKey: process.env.CHRONOS_API_KEY! });\n *\n * chronos.worker.handle<{ to: string }>('send-email', async (ctx) => {\n * await sendEmail(ctx.payload.to);\n * return { sent: true };\n * });\n *\n * await chronos.worker.start();\n * ```\n */\nexport class Chronos {\n /** Long-poll worker for executing pull-mode jobs. */\n readonly worker: Worker;\n\n /**\n * @param options - Client configuration. Only `apiKey` is required.\n * @throws {ChronosConfigError} If `apiKey` is missing or any option fails validation.\n */\n constructor(options: ChronosOptions) {\n const client = new BaseClient(options);\n this.worker = new Worker(client, options);\n }\n}\n\nexport {\n ChronosApiError,\n type ChronosApiErrorOptions,\n ChronosConfigError,\n ChronosError,\n ChronosHandlerError,\n ChronosNetworkError,\n ChronosRateLimitError,\n ChronosWebhookVerificationError,\n type RateLimitInfo,\n} from './errors';\nexport type {\n ChronosContext,\n ChronosHandler,\n ChronosHandlerResult,\n ChronosLogger,\n ChronosOptions,\n ChronosSchedule,\n FetchLike,\n WebhookOptions,\n} from './types';\nexport { DEFAULT_TIMESTAMP_TOLERANCE_SECONDS, Webhook } from './webhook';\nexport { DEFAULT_BASE_URL, DEFAULT_POLL_WAIT_TIME_SECONDS };\n"],"mappings":"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"}