@simmit/sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -28
- package/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -12
- package/dist/index.d.ts +12 -12
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
[](https://github.com/voidly-labs/simmit-sdk-typescript/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@simmit/sdk)
|
|
5
5
|
|
|
6
|
-
TypeScript SDK for
|
|
6
|
+
TypeScript SDK for [Simmit](https://simmit.com), an API for running SimulationCraft in the cloud.
|
|
7
|
+
|
|
8
|
+
Send the same `.simc` profile you'd run locally and Simmit executes SimulationCraft (SimC) for you on managed hardware, so you can run sims from anywhere that can make an HTTP request, with no local SimC build to manage.
|
|
9
|
+
|
|
10
|
+
Documentation: [dashboard.simmit.com/docs](https://dashboard.simmit.com/docs)
|
|
7
11
|
|
|
8
12
|
## Installation
|
|
9
13
|
|
|
@@ -11,29 +15,18 @@ TypeScript SDK for the [Simmit API](https://api.simmit.com) — cloud execution
|
|
|
11
15
|
npm install @simmit/sdk
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
Node 20+. Zero runtime dependencies
|
|
18
|
+
Node 20+. Zero runtime dependencies (global `fetch` and WebCrypto only).
|
|
15
19
|
|
|
16
20
|
## Usage
|
|
17
21
|
|
|
18
22
|
```ts
|
|
19
23
|
import Simmit from '@simmit/sdk'
|
|
20
24
|
|
|
21
|
-
const client = new Simmit(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Or pass the secret key explicitly:
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
const client = new Simmit({ secretKey: 'smt_sk_...' })
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Run a sim and wait for the result
|
|
31
|
-
|
|
32
|
-
`createAndWait` submits a job and polls until it reaches a terminal state,
|
|
33
|
-
resolving with the completed job — ideal for scripts and queue workers that can
|
|
34
|
-
hold a promise open:
|
|
25
|
+
const client = new Simmit({
|
|
26
|
+
secretKey: process.env['SIMMIT_SECRET_KEY'] // This is the default and can be omitted
|
|
27
|
+
})
|
|
35
28
|
|
|
36
|
-
|
|
29
|
+
// Submit a SimC profile, wait for the sim to finish, and read the result.
|
|
37
30
|
const job = await client.jobs.createAndWait({
|
|
38
31
|
build: { channel: 'latest' },
|
|
39
32
|
profile: { text: profileText } // a SimC profile, up to 2 MB
|
|
@@ -42,7 +35,12 @@ const job = await client.jobs.createAndWait({
|
|
|
42
35
|
const result = await client.jobs.getResult(job.id)
|
|
43
36
|
```
|
|
44
37
|
|
|
45
|
-
|
|
38
|
+
The rest of the surface is shown below until an SDK reference lands in the [docs](https://dashboard.simmit.com/docs).
|
|
39
|
+
|
|
40
|
+
### Progress hooks
|
|
41
|
+
|
|
42
|
+
`createAndWait` is ideal for scripts and queue workers that can hold a promise
|
|
43
|
+
open. Hook into progress, or capture the job id before polling starts:
|
|
46
44
|
|
|
47
45
|
```ts
|
|
48
46
|
await client.jobs.createAndWait(
|
|
@@ -79,8 +77,8 @@ await client.jobs.cancel(id) // request cancellation
|
|
|
79
77
|
### Artifact download URLs
|
|
80
78
|
|
|
81
79
|
`getResult` returns each artifact with a stable download `url`, valid for the
|
|
82
|
-
artifact's full retention window. To fetch that URL on demand instead
|
|
83
|
-
browser flow that controls the final fetch
|
|
80
|
+
artifact's full retention window. To fetch that URL on demand instead (e.g. a
|
|
81
|
+
browser flow that controls the final fetch), use:
|
|
84
82
|
|
|
85
83
|
```ts
|
|
86
84
|
const { url } = await client.artifacts.getUrl(artifactId)
|
|
@@ -115,8 +113,8 @@ try {
|
|
|
115
113
|
|
|
116
114
|
`createAndWait` also throws `JobFailedError` / `JobCancelledError` /
|
|
117
115
|
`JobTimedOutError` (each carrying the full `.job`), and `JobWaitTimeoutError` if
|
|
118
|
-
the wait deadline passes before the job finishes
|
|
119
|
-
|
|
116
|
+
the wait deadline passes before the job finishes. The job keeps running, so call
|
|
117
|
+
`client.jobs.cancel(jobId)` to stop the spend.
|
|
120
118
|
|
|
121
119
|
### Response headers
|
|
122
120
|
|
|
@@ -132,8 +130,8 @@ response.headers.get('x-idempotent-replay')
|
|
|
132
130
|
### Verifying webhooks
|
|
133
131
|
|
|
134
132
|
`unwrapWebhook` verifies a webhook signature and returns the parsed event. It is
|
|
135
|
-
standalone
|
|
136
|
-
secret
|
|
133
|
+
standalone (no client and no API key required, just your webhook signing
|
|
134
|
+
secret), so it is safe to run in a webhook receiver:
|
|
137
135
|
|
|
138
136
|
```ts
|
|
139
137
|
import { unwrapWebhook } from '@simmit/sdk'
|
|
@@ -152,10 +150,10 @@ if (event.payload.status === 'completed') {
|
|
|
152
150
|
## Development
|
|
153
151
|
|
|
154
152
|
- Node 20+ (`.nvmrc` pins the dev version), pnpm.
|
|
155
|
-
- `pnpm generate`
|
|
156
|
-
- `pnpm build`
|
|
157
|
-
- `pnpm test`
|
|
158
|
-
- `pnpm smoke`
|
|
153
|
+
- `pnpm generate` regenerates `src/generated/openapi.d.ts` from the committed `openapi.json` snapshot. Never hand-edit generated output; only `src/api-types.ts` may import from `src/generated/`.
|
|
154
|
+
- `pnpm build` builds dual ESM+CJS via tsup.
|
|
155
|
+
- `pnpm test` runs vitest (hermetic; mocks `fetch`, no network).
|
|
156
|
+
- `pnpm smoke` runs a manual check against a real API (needs `SIMMIT_SECRET_KEY`; set `SIMMIT_PROFILE_FILE` to also run a full create-then-result, or `TEST_API_BASE_URL` to target a non-prod endpoint). See `scripts/smoke.mjs`.
|
|
159
157
|
|
|
160
158
|
## License
|
|
161
159
|
|
package/dist/index.cjs
CHANGED
|
@@ -68,7 +68,7 @@ var APIError = class _APIError extends SimmitError {
|
|
|
68
68
|
code;
|
|
69
69
|
/** Typed `meta` from the error envelope. */
|
|
70
70
|
meta;
|
|
71
|
-
/** Raw parsed JSON error body
|
|
71
|
+
/** Raw parsed JSON error body: escape hatch for unmapped fields. */
|
|
72
72
|
error;
|
|
73
73
|
constructor(status, body, message, headers) {
|
|
74
74
|
super(_APIError.makeMessage(status, body, message));
|
|
@@ -395,7 +395,7 @@ function buildHeaders(config, spec, options) {
|
|
|
395
395
|
authorization: `Bearer ${config.secretKey}`,
|
|
396
396
|
...spec.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
397
397
|
...idempotent && !options.idempotencyKey ? {
|
|
398
|
-
// Generated once per call and reused across retry attempts
|
|
398
|
+
// Generated once per call and reused across retry attempts. That
|
|
399
399
|
// is what makes POST retries safe by default. The auto
|
|
400
400
|
// key is an SDK built-in default (lowest tier), so defaultHeaders
|
|
401
401
|
// may override it.
|
|
@@ -448,7 +448,7 @@ var Artifacts = class {
|
|
|
448
448
|
}
|
|
449
449
|
/**
|
|
450
450
|
* Fetch a stable public download URL for an artifact, valid for the
|
|
451
|
-
* artifact's full retention window
|
|
451
|
+
* artifact's full retention window: the same URL `jobs.getResult` returns,
|
|
452
452
|
* fetched on demand (e.g. browser flows that control the final fetch). The
|
|
453
453
|
* artifact is gone (410) once its retention window passes.
|
|
454
454
|
*/
|
|
@@ -531,7 +531,7 @@ var Jobs = class {
|
|
|
531
531
|
);
|
|
532
532
|
}
|
|
533
533
|
/**
|
|
534
|
-
* Fetch the live status of a job in any state
|
|
534
|
+
* Fetch the live status of a job in any state: `status`, `errorCode`,
|
|
535
535
|
* `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a
|
|
536
536
|
* non-terminal job, so it is the supported way to drive a custom poll loop.
|
|
537
537
|
*/
|
|
@@ -546,7 +546,7 @@ var Jobs = class {
|
|
|
546
546
|
}
|
|
547
547
|
/**
|
|
548
548
|
* Fetch the result summary of a terminal job. Throws `ResultNotReadyError`
|
|
549
|
-
* (409) while the job is still running
|
|
549
|
+
* (409) while the job is still running. Poll `/status` or use
|
|
550
550
|
* `createAndWait` rather than `/result` for a job in flight.
|
|
551
551
|
*/
|
|
552
552
|
getResult(jobId, options) {
|
|
@@ -564,7 +564,7 @@ var Jobs = class {
|
|
|
564
564
|
* a 10s cap), then fetches the full record. Resolves with the `CompletedJob`
|
|
565
565
|
* on success; throws `JobFailedError` / `JobCancelledError` /
|
|
566
566
|
* `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`
|
|
567
|
-
* if the deadline passes first
|
|
567
|
+
* if the deadline passes first. The job keeps running and is **not**
|
|
568
568
|
* cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait
|
|
569
569
|
* with `APIUserAbortError`, also without cancelling.
|
|
570
570
|
*/
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/error.ts","../src/api-promise.ts","../src/internal/abort.ts","../src/internal/request.ts","../src/resources/artifacts.ts","../src/resources/credits.ts","../src/internal/poll.ts","../src/resources/jobs.ts","../src/client.ts","../src/webhook.ts"],"sourcesContent":["// Public entry point. Surface specified in DESIGN.md.\nexport { default, default as Simmit } from './client'\nexport type { ClientOptions, RequestOptions } from './client'\nexport { APIPromise } from './api-promise'\nexport * from './error'\nexport type * from './api-types'\n// Resource classes are instantiated by the client (`client.jobs`, `client.credits`);\n// exported as types only so callers can annotate without constructing them.\nexport type { Jobs, JobWaitOptions } from './resources/jobs'\nexport type { Credits } from './resources/credits'\nexport type { Artifacts } from './resources/artifacts'\n// Standalone webhook verification — no client (and no secret key) required.\nexport { unwrapWebhook } from './webhook'\nexport type { WebhookEvent } from './webhook'\n","import type { Job, JobStatus } from './api-types'\n\nexport class SimmitError extends Error {}\n\n/**\n * Value shapes the API's generic error `meta` bag can carry\n * (400/401/404/410/413 responses): JSON scalars, scalar arrays, or arrays of\n * flat objects.\n */\nexport type MetaValue =\n | string\n | number\n | boolean\n | null\n | Array<string | number | boolean>\n | Array<Record<string, string | number | boolean | null>>\n\nexport type GenericMeta = Record<string, MetaValue>\n\n/** The API's uniform error envelope: `{ error, code, meta }`. */\ninterface ErrorEnvelope {\n error?: unknown\n code?: unknown\n meta?: unknown\n}\n\nexport class APIError<\n TStatus extends number | undefined = number | undefined,\n TCode extends string | undefined = string | undefined,\n TMeta = GenericMeta | null\n> extends SimmitError {\n /** HTTP status of the response that caused the error. */\n readonly status: TStatus\n /** HTTP headers of the response that caused the error. */\n readonly headers: Headers | undefined\n /** Machine-readable `code` from the error envelope. */\n readonly code: TCode\n /** Typed `meta` from the error envelope. */\n readonly meta: TMeta\n /** Raw parsed JSON error body — escape hatch for unmapped fields. */\n readonly error: object | undefined\n\n constructor(\n status: TStatus,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ) {\n super(APIError.makeMessage(status, body, message))\n this.status = status\n this.headers = headers\n this.error = body\n const envelope = body as ErrorEnvelope | undefined\n this.code = (\n typeof envelope?.code === 'string' ? envelope.code : undefined\n ) as TCode\n this.meta = (body ? (envelope?.meta ?? null) : undefined) as TMeta\n }\n\n private static makeMessage(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined\n ): string {\n // The API's human-readable message field is named `error`, not `message`.\n const bodyMessage = (body as ErrorEnvelope | undefined)?.error\n const msg =\n typeof bodyMessage === 'string'\n ? bodyMessage\n : body\n ? JSON.stringify(body)\n : message\n\n if (status && msg) return `${status} ${msg}`\n if (status) return `${status} status code (no body)`\n if (msg) return msg\n return '(no status code or body)'\n }\n\n /**\n * Maps a response to the most specific error class: status selects the base\n * class; an enumerated `code` with structured `meta` selects the subclass;\n * anything unrecognized falls back to the status class so new server codes\n * degrade gracefully without breaking `instanceof` handling.\n */\n static generate(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ): APIError<\n number | undefined,\n string | undefined,\n GenericMeta | null | undefined\n > {\n if (!status || !headers) {\n return new APIConnectionError({\n message,\n cause: body instanceof Error ? body : undefined\n })\n }\n\n const code = (body as ErrorEnvelope | undefined)?.code\n\n if (status === 400) return new BadRequestError(400, body, message, headers)\n if (status === 401) {\n return new AuthenticationError(401, body, message, headers)\n }\n if (status === 402) {\n if (code === 'insufficient_credits') {\n return new InsufficientCreditsError(402, body, message, headers)\n }\n if (code === 'insufficient_credits_liability') {\n return new InsufficientCreditsLiabilityError(\n 402,\n body,\n message,\n headers\n )\n }\n return new BillingError(402, body, message, headers)\n }\n if (status === 404) return new NotFoundError(404, body, message, headers)\n if (status === 409) {\n if (code === 'idempotency_key_reuse') {\n return new IdempotencyKeyReuseError(409, body, message, headers)\n }\n if (code === 'result_not_ready') {\n return new ResultNotReadyError(409, body, message, headers)\n }\n if (code === 'job_not_cancellable') {\n return new JobNotCancellableError(409, body, message, headers)\n }\n return new ConflictError(409, body, message, headers)\n }\n if (status === 413) {\n return new RequestTooLargeError(413, body, message, headers)\n }\n if (status === 422) {\n if (code === 'input_sanitized_rejected') {\n return new InvalidProfileError(422, body, message, headers)\n }\n if (code === 'result_unavailable') {\n return new ResultUnavailableError(422, body, message, headers)\n }\n return new UnprocessableEntityError(422, body, message, headers)\n }\n if (status === 429) {\n if (code === 'max_active_jobs_exceeded') {\n return new MaxActiveJobsError(429, body, message, headers)\n }\n return new RateLimitError(429, body, message, headers)\n }\n if (status === 503 && isServiceUnavailableBody(body)) {\n return new ServiceUnavailableError(503, body, message, headers)\n }\n if (status >= 500) {\n return new InternalServerError(status, body, message, headers)\n }\n return new APIError(status, body, message, headers)\n }\n}\n\n// ── 4xx status classes (code subclasses where the spec enumerates) ──────────\n\nexport class BadRequestError extends APIError<400, string> {}\n\nexport type AuthenticationErrorCode =\n | 'missing_token'\n | 'invalid_token'\n | 'revoked_token'\n | 'expired_token'\n\nexport class AuthenticationError extends APIError<\n 401,\n AuthenticationErrorCode\n> {}\n\n// 402 codes are docs-enumerated; the spec leaves `code` un-enumerated, so the\n// base class keeps `string` for forward compatibility.\nexport class BillingError extends APIError<402, string> {}\n\nexport type InsufficientCreditsMeta = {\n reason: string\n ceilingRuntimeSeconds?: number\n /** Largest maxRuntimeSeconds the current balance can cover. */\n maxAffordableRuntimeSeconds?: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsError extends BillingError {\n declare readonly code: 'insufficient_credits'\n declare readonly meta: InsufficientCreditsMeta | null\n}\n\nexport type InsufficientCreditsLiabilityMeta = {\n reason: string\n /** The high-priority fee in effect — top up, or resubmit at priority 'standard'. */\n priorityFeeCredits: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsLiabilityError extends BillingError {\n declare readonly code: 'insufficient_credits_liability'\n declare readonly meta: InsufficientCreditsLiabilityMeta | null\n}\n\nexport class NotFoundError extends APIError<404, string> {}\n\nexport class ConflictError extends APIError<409, string> {}\n\nexport class IdempotencyKeyReuseError extends ConflictError {\n declare readonly code: 'idempotency_key_reuse'\n declare readonly meta: {\n reason: 'idempotency_key_reuse'\n /** ID of the job that originally consumed this idempotency key. */\n originalJobId: string\n docsUrl?: string\n }\n}\n\nexport class ResultNotReadyError extends ConflictError {\n declare readonly code: 'result_not_ready'\n declare readonly meta: {\n status: 'pending' | 'queued' | 'starting' | 'running'\n }\n}\n\nexport class JobNotCancellableError extends ConflictError {\n declare readonly code: 'job_not_cancellable'\n declare readonly meta: { id: string; status: JobStatus }\n}\n\nexport class RequestTooLargeError extends APIError<413, string> {}\n\nexport class UnprocessableEntityError extends APIError<422, string> {}\n\nexport class InvalidProfileError extends UnprocessableEntityError {\n declare readonly code: 'input_sanitized_rejected'\n declare readonly meta: {\n reason: 'input_sanitized_rejected'\n message: string\n docsUrl: string\n /** Sample of rejected lines; see blockedCount/blockedTruncated for the full set. */\n blocked: Array<{ line: number; text: string }>\n blockedCount: number\n blockedTruncated: boolean\n }\n}\n\nexport class ResultUnavailableError extends UnprocessableEntityError {\n declare readonly code: 'result_unavailable'\n declare readonly meta: {\n status: 'completed' | 'failed' | 'cancelled' | 'timed_out'\n }\n}\n\nexport type RateLimitErrorCode =\n | 'rate_limit_exceeded'\n | 'max_active_jobs_exceeded'\n\nexport class RateLimitError extends APIError<429, RateLimitErrorCode> {\n declare readonly meta:\n | { scope: 'developer' }\n | {\n reason: 'max_active_jobs_exceeded'\n maxActiveJobs: number\n activeJobs: number\n }\n | null\n}\n\nexport class MaxActiveJobsError extends RateLimitError {\n declare readonly code: 'max_active_jobs_exceeded'\n declare readonly meta: {\n reason: 'max_active_jobs_exceeded'\n /** Maximum number of jobs the account can have in flight. */\n maxActiveJobs: number\n /** Jobs in flight when this request was rejected. */\n activeJobs: number\n }\n}\n\n// ── 5xx ─────────────────────────────────────────────────────────────────────\n\nexport class InternalServerError extends APIError<number, string> {}\n\n/**\n * 503 carries four enumerated codes with distinct meta — a discriminated\n * union, narrowed via `.body`. `api_maintenance` gets no special retry\n * behavior: standard policy applies, and the typed\n * `meta.retryAfterSeconds` is surfaced so callers can schedule their own\n * resubmission.\n */\nexport type ServiceUnavailableBody =\n | {\n code: 'queue_unavailable'\n meta: { reason: 'queue_unavailable'; queueHealth: string }\n }\n | {\n code: 'queue_health_unknown'\n meta: { reason: 'queue_health_unknown'; laneId: string }\n }\n | {\n code: 'secret_store_unavailable'\n meta: { reason: 'secret_store_unavailable' }\n }\n | { code: 'api_maintenance'; meta: { retryAfterSeconds: number } }\n\nconst SERVICE_UNAVAILABLE_CODES = new Set([\n 'queue_unavailable',\n 'queue_health_unknown',\n 'secret_store_unavailable',\n 'api_maintenance'\n])\n\n// A 503 whose body isn't the enumerated envelope (e.g. load-balancer HTML)\n// falls back to InternalServerError so `.body` below never lies.\nfunction isServiceUnavailableBody(\n body: object | undefined\n): body is ServiceUnavailableBody {\n const code = (body as ErrorEnvelope | undefined)?.code\n return typeof code === 'string' && SERVICE_UNAVAILABLE_CODES.has(code)\n}\n\nexport class ServiceUnavailableError extends InternalServerError {\n declare readonly status: 503\n\n /** The discriminated 503 envelope: `if (e.body.code === 'api_maintenance') e.body.meta.retryAfterSeconds`. */\n get body(): ServiceUnavailableBody {\n return this.error as ServiceUnavailableBody\n }\n}\n\n// ── No HTTP response ────────────────────────────────────────────────────────\n\nexport class APIConnectionError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({\n message,\n cause\n }: { message?: string | undefined; cause?: Error | undefined } = {}) {\n super(undefined, undefined, message ?? 'Connection error.', undefined)\n if (cause) this.cause = cause\n }\n}\n\nexport class APIConnectionTimeoutError extends APIConnectionError {\n constructor({ message }: { message?: string } = {}) {\n super({ message: message ?? 'Request timed out.' })\n }\n}\n\nexport class APIUserAbortError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({ message }: { message?: string } = {}) {\n super(undefined, undefined, message ?? 'Request was aborted.', undefined)\n }\n}\n\n// ── Job-level errors (thrown only by createAndWait) ──────────────────────────\n\n/** Catch-all for a job that reached a terminal state other than `completed`. */\nexport abstract class JobUnsuccessfulError extends SimmitError {\n readonly job: Job\n\n constructor(job: Job, message?: string) {\n super(\n message ??\n `Job ${job.id} ${job.status}` +\n (job.statusReason ? `: ${job.statusReason}` : '') +\n (job.errorCode ? ` (${job.errorCode})` : '')\n )\n this.job = job\n }\n}\n\nexport class JobFailedError extends JobUnsuccessfulError {}\n\n/** Includes queue_timeout auto-cancellation, not just user cancels. */\nexport class JobCancelledError extends JobUnsuccessfulError {}\n\n/** The job hit its runtime ceiling server-side and is billed for what ran. */\nexport class JobTimedOutError extends JobUnsuccessfulError {}\n\n/**\n * The SDK gave up polling — the job itself is still running and billing.\n * Keep tracking via `jobs.get(jobId)` or stop the spend with `jobs.cancel(jobId)`.\n */\nexport class JobWaitTimeoutError extends SimmitError {\n readonly jobId: string\n readonly lastStatus: JobStatus\n\n constructor(args: {\n jobId: string\n lastStatus: JobStatus\n message?: string\n }) {\n super(\n args.message ??\n `Timed out waiting for job ${args.jobId} (last status: ${args.lastStatus}). ` +\n 'The job is still running server-side and continues to bill.'\n )\n this.jobId = args.jobId\n this.lastStatus = args.lastStatus\n }\n}\n\n// ── Webhook verification (thrown by unwrapWebhook) ───────────────────────────\n\nexport class WebhookVerificationError extends SimmitError {}\n","/**\n * A `Promise<T>` with raw-response access — the generic answer to response\n * headers the return types can't see (`X-Idempotent-Replay`, `X-Active-Jobs`,\n * `X-RateLimit-*`).\n *\n * const { data, response } = await client.jobs.create(params).withResponse()\n * response.headers.get('x-idempotent-replay')\n */\nexport class APIPromise<T> extends Promise<T> {\n // Chained promises (.then/.catch) must be plain Promises: this class's\n // constructor signature is incompatible with the executor the runtime\n // would otherwise pass via the species constructor.\n static override get [Symbol.species]() {\n return Promise\n }\n\n readonly #parsed: Promise<{ data: T; response: Response }>\n\n constructor(parsed: Promise<{ data: T; response: Response }>) {\n // The base promise is a pre-settled placeholder that is never observed:\n // then() below delegates to #parsed lazily (catch/finally route through\n // then() per spec). Subscribing eagerly here would reject this instance\n // even when the caller only consumes withResponse(), leaking an\n // unhandled rejection on failures.\n super((resolve) => resolve(undefined as never))\n this.#parsed = parsed\n }\n\n override then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.#parsed\n .then((result) => result.data)\n .then(onfulfilled, onrejected)\n }\n\n withResponse(): Promise<{ data: T; response: Response }> {\n return this.#parsed\n }\n\n asResponse(): Promise<Response> {\n return this.#parsed.then((result) => result.response)\n }\n}\n","// Abort-aware timing utilities shared by the request layer (backoff sleeps) and\n// the createAndWait poll loop. A pending sleep rejects with APIUserAbortError\n// the moment the caller's signal fires.\nimport { APIUserAbortError } from '../error'\n\nexport function throwIfUserAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new APIUserAbortError()\n}\n\nexport function sleep(\n ms: number,\n signal: AbortSignal | undefined\n): Promise<void> {\n return new Promise((resolve, reject) => {\n throwIfUserAborted(signal)\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new APIUserAbortError())\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n","// Internal request layer: header assembly, per-attempt timeout/abort\n// composition, retry with backoff + Retry-After, idempotency-key injection,\n// and error mapping. Not exported from the package.\nimport { APIPromise } from '../api-promise'\nimport {\n APIConnectionError,\n APIConnectionTimeoutError,\n APIError,\n APIUserAbortError\n} from '../error'\nimport type { RequestOptions } from '../client'\nimport { sleep, throwIfUserAborted } from './abort'\n\nexport interface ClientConfig {\n secretKey: string\n baseURL: string\n timeout: number\n maxRetries: number\n defaultHeaders: Record<string, string | null | undefined> | undefined\n fetch: typeof globalThis.fetch\n fetchOptions: RequestInit | undefined\n}\n\nexport interface RequestSpec {\n method: 'GET' | 'POST'\n path: string\n body?: unknown\n /** POST job creation: auto-generate an idempotency-key when none supplied. */\n idempotent?: boolean\n}\n\n// Retry policy constants — typed code config, not env.\nconst INITIAL_BACKOFF_MS = 500\nconst MAX_BACKOFF_MS = 8_000\nconst MAX_RETRY_AFTER_MS = 60_000\n\nexport function makeRequest<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions = {}\n): APIPromise<T> {\n return new APIPromise(run<T>(config, spec, options))\n}\n\nasync function run<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Promise<{ data: T; response: Response }> {\n const maxRetries = options.maxRetries ?? config.maxRetries\n const timeout = options.timeout ?? config.timeout\n const headers = buildHeaders(config, spec, options)\n const url = `${config.baseURL.replace(/\\/+$/, '')}${spec.path}`\n const body = spec.body === undefined ? undefined : JSON.stringify(spec.body)\n\n for (let attempt = 0; ; attempt++) {\n throwIfUserAborted(options.signal)\n\n let result: AttemptResult\n try {\n result = await fetchAttempt(config, spec, options, {\n url,\n headers,\n body,\n timeout\n })\n } catch (err) {\n if (err instanceof APIUserAbortError) throw err\n // Connection error, malformed success body, or per-attempt timeout —\n // all retryable.\n if (attempt < maxRetries) {\n await backoff(attempt, undefined, options.signal)\n continue\n }\n throw err\n }\n\n const { response, json } = result\n\n if (response.ok) {\n return { data: json as T, response }\n }\n\n if (shouldRetryStatus(response.status) && attempt < maxRetries) {\n await backoff(\n attempt,\n response.headers.get('retry-after'),\n options.signal\n )\n continue\n }\n\n throw APIError.generate(\n response.status,\n typeof json === 'object' && json !== null ? json : undefined,\n response.statusText,\n response.headers\n )\n }\n}\n\ninterface AttemptResult {\n response: Response\n /** Parsed JSON body; undefined when an error response carried a non-JSON body. */\n json: unknown\n}\n\nasync function fetchAttempt(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions,\n attempt: {\n url: string\n headers: Record<string, string>\n body: string | undefined\n timeout: number\n }\n): Promise<AttemptResult> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), attempt.timeout)\n const onUserAbort = () => controller.abort()\n options.signal?.addEventListener('abort', onUserAbort, { once: true })\n\n try {\n const response = await config.fetch(attempt.url, {\n ...config.fetchOptions,\n method: spec.method,\n headers: attempt.headers,\n ...(attempt.body !== undefined ? { body: attempt.body } : {}),\n signal: controller.signal\n })\n\n // The body is read inside the timed scope too — a stalled body must not\n // hang past the per-attempt timeout (fetch ties the body stream to the\n // controller's signal, so the abort cancels the read).\n let json: unknown\n if (response.ok) {\n // Success bodies must parse; a truncated/malformed one is treated as a\n // transport failure (classified below) and retried like one.\n json = await response.json()\n } else {\n try {\n json = await response.json()\n } catch (err) {\n // Aborted mid-read is a timeout/abort, not a non-JSON body.\n if (controller.signal.aborted) throw err\n json = undefined // e.g. a load-balancer HTML error page\n }\n }\n return { response, json }\n } catch (err) {\n if (options.signal?.aborted) throw new APIUserAbortError()\n if (controller.signal.aborted) {\n throw new APIConnectionTimeoutError()\n }\n throw new APIConnectionError({\n cause: err instanceof Error ? err : undefined\n })\n } finally {\n clearTimeout(timeoutId)\n options.signal?.removeEventListener('abort', onUserAbort)\n }\n}\n\nfunction buildHeaders(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Record<string, string> {\n const idempotent = spec.idempotent && spec.method === 'POST'\n const merged: Record<string, string | null | undefined> = {\n authorization: `Bearer ${config.secretKey}`,\n ...(spec.body !== undefined ? { 'content-type': 'application/json' } : {}),\n ...(idempotent && !options.idempotencyKey\n ? {\n // Generated once per call and reused across retry attempts — that\n // is what makes POST retries safe by default. The auto\n // key is an SDK built-in default (lowest tier), so defaultHeaders\n // may override it.\n 'idempotency-key': `simmit-node-retry-${crypto.randomUUID()}`\n }\n : {}),\n ...lowercaseKeys(config.defaultHeaders),\n ...(idempotent && options.idempotencyKey\n ? {\n // An explicit key is a per-request option: it must beat constructor\n // defaultHeaders. Raw options.headers still wins last.\n 'idempotency-key': options.idempotencyKey\n }\n : {}),\n ...lowercaseKeys(options.headers)\n }\n\n const headers: Record<string, string> = {}\n for (const [key, value] of Object.entries(merged)) {\n // A null value deletes the header; undefined entries are skipped.\n if (typeof value === 'string') headers[key] = value\n }\n return headers\n}\n\nfunction lowercaseKeys(\n record: Record<string, string | null | undefined> | undefined\n): Record<string, string | null | undefined> {\n if (!record) return {}\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [key.toLowerCase(), value])\n )\n}\n\nfunction shouldRetryStatus(status: number): boolean {\n // 408 kept defensively even though the API never emits it. 409 is never\n // retried: result_not_ready is thrown immediately by design and the other\n // 409s are deterministic.\n return status === 408 || status === 429 || status >= 500\n}\n\nasync function backoff(\n attempt: number,\n retryAfterHeader: string | null | undefined,\n signal: AbortSignal | undefined\n): Promise<void> {\n const retryAfterMs = parseRetryAfter(retryAfterHeader)\n const delay =\n retryAfterMs !== undefined\n ? retryAfterMs\n : Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS) *\n (1 - 0.25 * Math.random())\n await sleep(delay, signal)\n}\n\n/** Accepts `Retry-After` only when it parses to a delay in (0, 60s] — the SDK never sleeps arbitrarily long on a server hint. */\nfunction parseRetryAfter(\n header: string | null | undefined\n): number | undefined {\n if (!header) return undefined\n let ms: number\n if (/^\\d+$/.test(header.trim())) {\n ms = Number(header.trim()) * 1000\n } else {\n ms = new Date(header).getTime() - Date.now()\n }\n return Number.isFinite(ms) && ms > 0 && ms <= MAX_RETRY_AFTER_MS\n ? ms\n : undefined\n}\n","import type { APIPromise } from '../api-promise'\nimport type { ArtifactUrl } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `artifacts` resource. */\nexport class Artifacts {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Fetch a stable public download URL for an artifact, valid for the\n * artifact's full retention window — the same URL `jobs.getResult` returns,\n * fetched on demand (e.g. browser flows that control the final fetch). The\n * artifact is gone (410) once its retention window passes.\n */\n getUrl(\n artifactId: string,\n options?: RequestOptions\n ): APIPromise<ArtifactUrl> {\n return this.#client._request<ArtifactUrl>(\n {\n method: 'GET',\n path: `/v1/simc/artifacts/${encodeURIComponent(artifactId)}/url`\n },\n options\n )\n }\n}\n","import type { APIPromise } from '../api-promise'\nimport type { CreditBalance } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `credits` resource. */\nexport class Credits {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /** Fetch the account's current credit balance and per-grant breakdown. */\n get(options?: RequestOptions): APIPromise<CreditBalance> {\n return this.#client._request<CreditBalance>(\n { method: 'GET', path: '/v1/simc/credits' },\n options\n )\n }\n}\n","// Pure helpers for the createAndWait poll loop. Kept separate from the\n// orchestration so the cadence and deadline math are unit-testable.\nimport type { JobCreateResponse, JobStatus } from '../api-types'\n\nexport const MIN_POLL_INTERVAL_MS = 100\nexport const DEFAULT_POLL_INTERVAL_MS = 1_000\nexport const MAX_POLL_INTERVAL_MS = 10_000\nexport const POLL_BACKOFF_FACTOR = 1.5\nconst DEADLINE_GRACE_MS = 60_000\nconst FALLBACK_WAIT_TIMEOUT_MS = 45 * 60 * 1_000\n\nconst TERMINAL_STATUSES = new Set<JobStatus>([\n 'completed',\n 'failed',\n 'cancelled',\n 'timed_out'\n])\n\nexport function isTerminal(status: JobStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/**\n * Default wait deadline: the applied ceilings the create response reports —\n * `(queueSeconds + runtimeSeconds) × 1000` plus a 60s grace — or a 45-minute\n * fallback when either ceiling is null.\n */\nexport function deriveWaitTimeoutMs(created: JobCreateResponse): number {\n const { runtimeSeconds, queueSeconds } = created.runtime.ceiling\n if (runtimeSeconds != null && queueSeconds != null) {\n return (runtimeSeconds + queueSeconds) * 1_000 + DEADLINE_GRACE_MS\n }\n return FALLBACK_WAIT_TIMEOUT_MS\n}\n\n/** Next poll interval: grow ×1.5, capped at 10s. */\nexport function nextPollInterval(interval: number): number {\n return Math.min(interval * POLL_BACKOFF_FACTOR, MAX_POLL_INTERVAL_MS)\n}\n","import type { APIPromise } from '../api-promise'\nimport type {\n CompletedJob,\n Job,\n JobCancelResponse,\n JobCreateParams,\n JobCreateResponse,\n JobResult,\n JobStatus,\n JobStatusResponse\n} from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\nimport {\n JobCancelledError,\n JobFailedError,\n JobTimedOutError,\n JobWaitTimeoutError\n} from '../error'\nimport { sleep } from '../internal/abort'\nimport {\n DEFAULT_POLL_INTERVAL_MS,\n deriveWaitTimeoutMs,\n isTerminal,\n MIN_POLL_INTERVAL_MS,\n nextPollInterval\n} from '../internal/poll'\n\nexport interface JobWaitOptions extends RequestOptions {\n /** Initial delay between status polls, ms. Grows ×1.5 per poll to a 10s cap; values under 100 are raised to it. Default 1_000. */\n pollIntervalMs?: number\n /** Overall wait deadline, ms. Default derived from the job's applied ceilings. */\n waitTimeoutMs?: number\n /** Fired once with the raw create response (job id, ceilings, input warnings) before polling. */\n onCreated?: (response: JobCreateResponse) => void\n /** Fired after every successful status poll (progress, stage, queue estimate). */\n onPoll?: (status: JobStatusResponse) => void\n}\n\n/**\n * The `jobs` resource. Each single-request method is a thin wrapper over\n * `client._request` with the path/method/types pinned to the spec;\n * `createAndWait` orchestrates several of them.\n */\nexport class Jobs {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Submit a new SimC sim. Returns immediately with the job handle; the sim\n * runs asynchronously. `idempotent: true` makes the request layer attach an\n * auto-generated idempotency key so the POST is safe to retry; pass\n * `options.idempotencyKey` to supply your own.\n */\n create(\n params: JobCreateParams,\n options?: RequestOptions\n ): APIPromise<JobCreateResponse> {\n return this.#client._request<JobCreateResponse>(\n { method: 'POST', path: '/v1/simc/jobs', body: params, idempotent: true },\n options\n )\n }\n\n /** Fetch the full record for a job. */\n get(jobId: string, options?: RequestOptions): APIPromise<Job> {\n return this.#client._request<Job>(\n { method: 'GET', path: `/v1/simc/jobs/${encodeURIComponent(jobId)}` },\n options\n )\n }\n\n /**\n * Fetch the live status of a job in any state — `status`, `errorCode`,\n * `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a\n * non-terminal job, so it is the supported way to drive a custom poll loop.\n */\n getStatus(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobStatusResponse> {\n return this.#client._request<JobStatusResponse>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/status`\n },\n options\n )\n }\n\n /**\n * Fetch the result summary of a terminal job. Throws `ResultNotReadyError`\n * (409) while the job is still running — poll `/status` or use\n * `createAndWait` rather than `/result` for a job in flight.\n */\n getResult(jobId: string, options?: RequestOptions): APIPromise<JobResult> {\n return this.#client._request<JobResult>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/result`\n },\n options\n )\n }\n\n /**\n * Submit a job and resolve once it reaches a terminal state. Polls\n * `GET /v1/simc/jobs/{id}/status` (first after `pollIntervalMs`, then ×1.5 to\n * a 10s cap), then fetches the full record. Resolves with the `CompletedJob`\n * on success; throws `JobFailedError` / `JobCancelledError` /\n * `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`\n * if the deadline passes first — the job keeps running and is **not**\n * cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait\n * with `APIUserAbortError`, also without cancelling.\n */\n async createAndWait(\n params: JobCreateParams,\n options: JobWaitOptions = {}\n ): Promise<CompletedJob> {\n const {\n pollIntervalMs,\n waitTimeoutMs,\n onCreated,\n onPoll,\n ...requestOptions\n } = options\n\n const created = await this.create(params, requestOptions)\n onCreated?.(created)\n\n const deadline =\n Date.now() + (waitTimeoutMs ?? deriveWaitTimeoutMs(created))\n // nextPollInterval only ever grows the interval, so a non-positive seed\n // would hot-poll the status endpoint; floor it.\n let interval = Math.max(\n pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n MIN_POLL_INTERVAL_MS\n )\n let lastStatus: JobStatus = 'pending'\n\n for (;;) {\n const remaining = deadline - Date.now()\n if (remaining <= 0) {\n throw new JobWaitTimeoutError({ jobId: created.id, lastStatus })\n }\n // Never sleep past the deadline, so the wait gives up promptly.\n await sleep(Math.min(interval, remaining), requestOptions.signal)\n\n const status = await this.getStatus(created.id, requestOptions)\n onPoll?.(status)\n lastStatus = status.status\n\n if (isTerminal(status.status)) {\n // The status payload is lightweight; the full record carries the fields\n // CompletedJob and the job-error classes expose.\n const job = await this.get(created.id, requestOptions)\n switch (job.status) {\n case 'completed':\n return job as CompletedJob\n case 'failed':\n throw new JobFailedError(job)\n case 'cancelled':\n throw new JobCancelledError(job)\n case 'timed_out':\n throw new JobTimedOutError(job)\n }\n // Raced back to non-terminal between /status and the full record; keep polling.\n lastStatus = job.status\n }\n interval = nextPollInterval(interval)\n }\n }\n\n /**\n * Request cancellation. Returns `status: 'cancelled'` when the job ended\n * before it ran, or `status: 'cancel_requested'` when an in-flight job was\n * signaled to stop. Repeat calls are naturally idempotent, so no key is sent.\n */\n cancel(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobCancelResponse> {\n return this.#client._request<JobCancelResponse>(\n {\n method: 'POST',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/cancel`\n },\n options\n )\n }\n}\n","import { APIPromise } from './api-promise'\nimport { SimmitError } from './error'\nimport {\n makeRequest,\n type ClientConfig,\n type RequestSpec\n} from './internal/request'\nimport { Artifacts } from './resources/artifacts'\nimport { Credits } from './resources/credits'\nimport { Jobs } from './resources/jobs'\n\nexport interface ClientOptions {\n /** Defaults to process.env['SIMMIT_SECRET_KEY'] — exactly one env fallback. Construction\n * throws SimmitError('Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.').\n * \"Secret key\" is the credential noun end to end (dashboard → docs → env var → option →\n * error): it spends credits and must never ship client-side. */\n secretKey?: string | null\n /** Defaults to process.env['SIMMIT_BASE_URL'] ?? 'https://api.simmit.com'. */\n baseURL?: string | null\n /** Per-attempt timeout in ms. Default 60_000. (Retries can extend total wall time.) */\n timeout?: number\n /** Max retries after the first attempt for retryable failures. Default 2. */\n maxRetries?: number\n /** Headers sent with every request. Merged under per-request headers. */\n defaultHeaders?: Record<string, string | null | undefined>\n /** Custom fetch (testing, proxies). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch\n /** Extra RequestInit fields passed to every fetch call (e.g. undici dispatcher). */\n fetchOptions?: RequestInit\n}\n\nexport interface RequestOptions {\n /** Per-attempt timeout in ms. Overrides ClientOptions.timeout. */\n timeout?: number\n /** Abort the call (including retries and waiting). Throws APIUserAbortError. Never retried. */\n signal?: AbortSignal\n /** Overrides ClientOptions.maxRetries for this call. */\n maxRetries?: number\n /** Merged over defaultHeaders; a null value deletes the header. */\n headers?: Record<string, string | null | undefined>\n /** jobs.create / jobs.createAndWait only: replaces the auto-generated idempotency-key. */\n idempotencyKey?: string\n}\n\nexport default class Simmit {\n readonly jobs: Jobs\n readonly credits: Credits\n readonly artifacts: Artifacts\n\n readonly baseURL: string\n\n readonly #config: ClientConfig\n\n constructor(options: ClientOptions = {}) {\n const secretKey = options.secretKey ?? readEnv('SIMMIT_SECRET_KEY')\n if (!secretKey) {\n throw new SimmitError(\n 'Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.'\n )\n }\n\n this.baseURL =\n options.baseURL ?? readEnv('SIMMIT_BASE_URL') ?? 'https://api.simmit.com'\n\n this.#config = {\n secretKey,\n baseURL: this.baseURL,\n timeout: options.timeout ?? 60_000,\n maxRetries: options.maxRetries ?? 2,\n defaultHeaders: options.defaultHeaders,\n // Resolved lazily so a fetch patched onto globalThis after the client\n // is constructed (msw, APM instrumentation) is still honored.\n fetch: options.fetch ?? ((...args) => globalThis.fetch(...args)),\n fetchOptions: options.fetchOptions\n }\n\n this.jobs = new Jobs(this)\n this.credits = new Credits(this)\n this.artifacts = new Artifacts(this)\n }\n\n /** @internal Resource classes route through here; not public surface. */\n _request<T>(spec: RequestSpec, options?: RequestOptions): APIPromise<T> {\n return makeRequest(this.#config, spec, options)\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined') return undefined\n const value = process.env?.[name]?.trim()\n return value || undefined\n}\n","// Standalone webhook verification. Not a client method: receivers must not\n// need a secret-key-bearing client (whose constructor throws without a key).\n// WebCrypto only — zero deps, and runs in Workers as well as Node.\nimport type { JobStatus } from './api-types'\nimport { WebhookVerificationError } from './error'\n\nconst DEFAULT_TOLERANCE_SECONDS = 300\n\n/** The one hand-written wire type: the webhook payload has no OpenAPI schema. */\nexport interface WebhookEvent {\n kind: 'job.terminal'\n version: 'v1'\n timestamp: string\n payload: {\n id: string\n statusReason: string | null\n status: Extract<\n JobStatus,\n 'completed' | 'failed' | 'cancelled' | 'timed_out'\n >\n }\n}\n\n/**\n * Verifies an `X-Simmit-Signature` header — `t=<unix>,v1=<hex>`, an HMAC-SHA256\n * (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance — and\n * returns the parsed event. Throws `WebhookVerificationError` on a bad\n * signature, malformed header, or stale timestamp.\n *\n * Pass `rawBody` exactly as received: re-serializing changes the bytes and\n * breaks verification. `secret` is the webhook signing secret (dashboard →\n * Clients & Keys → Webhook), not your API key.\n */\nexport async function unwrapWebhook(\n rawBody: string,\n signatureHeader: string,\n secret: string,\n options?: { toleranceSeconds?: number }\n): Promise<WebhookEvent> {\n // An empty secret would otherwise surface as an opaque WebCrypto DataError;\n // a NaN tolerance would make the age check pass for everything.\n if (!secret) {\n throw new WebhookVerificationError('Webhook signing secret is empty.')\n }\n const tolerance = options?.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new WebhookVerificationError(\n 'toleranceSeconds must be a non-negative number.'\n )\n }\n\n const { timestampRaw, timestamp, signature } =\n parseSignatureHeader(signatureHeader)\n\n const expected = await hmacSha256Hex(secret, `${timestampRaw}.${rawBody}`)\n if (!timingSafeEqual(expected, signature)) {\n throw new WebhookVerificationError('Webhook signature does not match.')\n }\n\n // Compare on whole seconds, matching the header's unix-seconds `t`.\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > tolerance) {\n throw new WebhookVerificationError(\n 'Webhook timestamp is outside the tolerance window.'\n )\n }\n\n try {\n return JSON.parse(rawBody) as WebhookEvent\n } catch {\n throw new WebhookVerificationError('Webhook body is not valid JSON.')\n }\n}\n\nfunction parseSignatureHeader(header: string): {\n timestampRaw: string\n timestamp: number\n signature: string\n} {\n let timestampRaw: string | undefined\n let signature: string | undefined\n for (const part of header.split(',')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n const key = part.slice(0, eq).trim()\n const value = part.slice(eq + 1).trim()\n if (key === 't') timestampRaw = value\n else if (key === 'v1') signature = value\n }\n\n // `t` is unix whole seconds; reject anything but digits so the accepted\n // header matches the documented contract.\n if (!timestampRaw || !signature || !/^\\d+$/.test(timestampRaw)) {\n throw new WebhookVerificationError(\n 'Malformed signature header; expected \"t=<unix>,v1=<hex>\".'\n )\n }\n // The signed payload uses the timestamp exactly as sent, so keep the raw\n // string for signing and the parsed number only for the tolerance check.\n return { timestampRaw, timestamp: Number(timestampRaw), signature }\n}\n\nasync function hmacSha256Hex(secret: string, payload: string): Promise<string> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n return toHex(new Uint8Array(mac))\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let hex = ''\n for (const byte of bytes) hex += byte.toString(16).padStart(2, '0')\n return hex\n}\n\n// Constant-time comparison. The digest width is public, so a length mismatch\n// may short-circuit without leaking secret-dependent timing.\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let mismatch = 0\n for (let i = 0; i < a.length; i++) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i)\n }\n return mismatch === 0\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/error.ts","../src/api-promise.ts","../src/internal/abort.ts","../src/internal/request.ts","../src/resources/artifacts.ts","../src/resources/credits.ts","../src/internal/poll.ts","../src/resources/jobs.ts","../src/client.ts","../src/webhook.ts"],"sourcesContent":["// Public entry point. Surface specified in DESIGN.md.\nexport { default, default as Simmit } from './client'\nexport type { ClientOptions, RequestOptions } from './client'\nexport { APIPromise } from './api-promise'\nexport * from './error'\nexport type * from './api-types'\n// Resource classes are instantiated by the client (`client.jobs`, `client.credits`);\n// exported as types only so callers can annotate without constructing them.\nexport type { Jobs, JobWaitOptions } from './resources/jobs'\nexport type { Credits } from './resources/credits'\nexport type { Artifacts } from './resources/artifacts'\n// Standalone webhook verification: no client (and no secret key) required.\nexport { unwrapWebhook } from './webhook'\nexport type { WebhookEvent } from './webhook'\n","import type { Job, JobStatus } from './api-types'\n\nexport class SimmitError extends Error {}\n\n/**\n * Value shapes the API's generic error `meta` bag can carry\n * (400/401/404/410/413 responses): JSON scalars, scalar arrays, or arrays of\n * flat objects.\n */\nexport type MetaValue =\n | string\n | number\n | boolean\n | null\n | Array<string | number | boolean>\n | Array<Record<string, string | number | boolean | null>>\n\nexport type GenericMeta = Record<string, MetaValue>\n\n/** The API's uniform error envelope: `{ error, code, meta }`. */\ninterface ErrorEnvelope {\n error?: unknown\n code?: unknown\n meta?: unknown\n}\n\nexport class APIError<\n TStatus extends number | undefined = number | undefined,\n TCode extends string | undefined = string | undefined,\n TMeta = GenericMeta | null\n> extends SimmitError {\n /** HTTP status of the response that caused the error. */\n readonly status: TStatus\n /** HTTP headers of the response that caused the error. */\n readonly headers: Headers | undefined\n /** Machine-readable `code` from the error envelope. */\n readonly code: TCode\n /** Typed `meta` from the error envelope. */\n readonly meta: TMeta\n /** Raw parsed JSON error body: escape hatch for unmapped fields. */\n readonly error: object | undefined\n\n constructor(\n status: TStatus,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ) {\n super(APIError.makeMessage(status, body, message))\n this.status = status\n this.headers = headers\n this.error = body\n const envelope = body as ErrorEnvelope | undefined\n this.code = (\n typeof envelope?.code === 'string' ? envelope.code : undefined\n ) as TCode\n this.meta = (body ? (envelope?.meta ?? null) : undefined) as TMeta\n }\n\n private static makeMessage(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined\n ): string {\n // The API's human-readable message field is named `error`, not `message`.\n const bodyMessage = (body as ErrorEnvelope | undefined)?.error\n const msg =\n typeof bodyMessage === 'string'\n ? bodyMessage\n : body\n ? JSON.stringify(body)\n : message\n\n if (status && msg) return `${status} ${msg}`\n if (status) return `${status} status code (no body)`\n if (msg) return msg\n return '(no status code or body)'\n }\n\n /**\n * Maps a response to the most specific error class: status selects the base\n * class; an enumerated `code` with structured `meta` selects the subclass;\n * anything unrecognized falls back to the status class so new server codes\n * degrade gracefully without breaking `instanceof` handling.\n */\n static generate(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ): APIError<\n number | undefined,\n string | undefined,\n GenericMeta | null | undefined\n > {\n if (!status || !headers) {\n return new APIConnectionError({\n message,\n cause: body instanceof Error ? body : undefined\n })\n }\n\n const code = (body as ErrorEnvelope | undefined)?.code\n\n if (status === 400) return new BadRequestError(400, body, message, headers)\n if (status === 401) {\n return new AuthenticationError(401, body, message, headers)\n }\n if (status === 402) {\n if (code === 'insufficient_credits') {\n return new InsufficientCreditsError(402, body, message, headers)\n }\n if (code === 'insufficient_credits_liability') {\n return new InsufficientCreditsLiabilityError(\n 402,\n body,\n message,\n headers\n )\n }\n return new BillingError(402, body, message, headers)\n }\n if (status === 404) return new NotFoundError(404, body, message, headers)\n if (status === 409) {\n if (code === 'idempotency_key_reuse') {\n return new IdempotencyKeyReuseError(409, body, message, headers)\n }\n if (code === 'result_not_ready') {\n return new ResultNotReadyError(409, body, message, headers)\n }\n if (code === 'job_not_cancellable') {\n return new JobNotCancellableError(409, body, message, headers)\n }\n return new ConflictError(409, body, message, headers)\n }\n if (status === 413) {\n return new RequestTooLargeError(413, body, message, headers)\n }\n if (status === 422) {\n if (code === 'input_sanitized_rejected') {\n return new InvalidProfileError(422, body, message, headers)\n }\n if (code === 'result_unavailable') {\n return new ResultUnavailableError(422, body, message, headers)\n }\n return new UnprocessableEntityError(422, body, message, headers)\n }\n if (status === 429) {\n if (code === 'max_active_jobs_exceeded') {\n return new MaxActiveJobsError(429, body, message, headers)\n }\n return new RateLimitError(429, body, message, headers)\n }\n if (status === 503 && isServiceUnavailableBody(body)) {\n return new ServiceUnavailableError(503, body, message, headers)\n }\n if (status >= 500) {\n return new InternalServerError(status, body, message, headers)\n }\n return new APIError(status, body, message, headers)\n }\n}\n\n// ── 4xx status classes (code subclasses where the spec enumerates) ──────────\n\nexport class BadRequestError extends APIError<400, string> {}\n\nexport type AuthenticationErrorCode =\n | 'missing_token'\n | 'invalid_token'\n | 'revoked_token'\n | 'expired_token'\n\nexport class AuthenticationError extends APIError<\n 401,\n AuthenticationErrorCode\n> {}\n\n// 402 codes are docs-enumerated; the spec leaves `code` un-enumerated, so the\n// base class keeps `string` for forward compatibility.\nexport class BillingError extends APIError<402, string> {}\n\nexport type InsufficientCreditsMeta = {\n reason: string\n ceilingRuntimeSeconds?: number\n /** Largest maxRuntimeSeconds the current balance can cover. */\n maxAffordableRuntimeSeconds?: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsError extends BillingError {\n declare readonly code: 'insufficient_credits'\n declare readonly meta: InsufficientCreditsMeta | null\n}\n\nexport type InsufficientCreditsLiabilityMeta = {\n reason: string\n /** The high-priority fee in effect. Top up, or resubmit at priority 'standard'. */\n priorityFeeCredits: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsLiabilityError extends BillingError {\n declare readonly code: 'insufficient_credits_liability'\n declare readonly meta: InsufficientCreditsLiabilityMeta | null\n}\n\nexport class NotFoundError extends APIError<404, string> {}\n\nexport class ConflictError extends APIError<409, string> {}\n\nexport class IdempotencyKeyReuseError extends ConflictError {\n declare readonly code: 'idempotency_key_reuse'\n declare readonly meta: {\n reason: 'idempotency_key_reuse'\n /** ID of the job that originally consumed this idempotency key. */\n originalJobId: string\n docsUrl?: string\n }\n}\n\nexport class ResultNotReadyError extends ConflictError {\n declare readonly code: 'result_not_ready'\n declare readonly meta: {\n status: 'pending' | 'queued' | 'starting' | 'running'\n }\n}\n\nexport class JobNotCancellableError extends ConflictError {\n declare readonly code: 'job_not_cancellable'\n declare readonly meta: { id: string; status: JobStatus }\n}\n\nexport class RequestTooLargeError extends APIError<413, string> {}\n\nexport class UnprocessableEntityError extends APIError<422, string> {}\n\nexport class InvalidProfileError extends UnprocessableEntityError {\n declare readonly code: 'input_sanitized_rejected'\n declare readonly meta: {\n reason: 'input_sanitized_rejected'\n message: string\n docsUrl: string\n /** Sample of rejected lines; see blockedCount/blockedTruncated for the full set. */\n blocked: Array<{ line: number; text: string }>\n blockedCount: number\n blockedTruncated: boolean\n }\n}\n\nexport class ResultUnavailableError extends UnprocessableEntityError {\n declare readonly code: 'result_unavailable'\n declare readonly meta: {\n status: 'completed' | 'failed' | 'cancelled' | 'timed_out'\n }\n}\n\nexport type RateLimitErrorCode =\n | 'rate_limit_exceeded'\n | 'max_active_jobs_exceeded'\n\nexport class RateLimitError extends APIError<429, RateLimitErrorCode> {\n declare readonly meta:\n | { scope: 'developer' }\n | {\n reason: 'max_active_jobs_exceeded'\n maxActiveJobs: number\n activeJobs: number\n }\n | null\n}\n\nexport class MaxActiveJobsError extends RateLimitError {\n declare readonly code: 'max_active_jobs_exceeded'\n declare readonly meta: {\n reason: 'max_active_jobs_exceeded'\n /** Maximum number of jobs the account can have in flight. */\n maxActiveJobs: number\n /** Jobs in flight when this request was rejected. */\n activeJobs: number\n }\n}\n\n// ── 5xx ─────────────────────────────────────────────────────────────────────\n\nexport class InternalServerError extends APIError<number, string> {}\n\n/**\n * 503 carries four enumerated codes with distinct meta: a discriminated\n * union, narrowed via `.body`. `api_maintenance` gets no special retry\n * behavior: standard policy applies, and the typed\n * `meta.retryAfterSeconds` is surfaced so callers can schedule their own\n * resubmission.\n */\nexport type ServiceUnavailableBody =\n | {\n code: 'queue_unavailable'\n meta: { reason: 'queue_unavailable'; queueHealth: string }\n }\n | {\n code: 'queue_health_unknown'\n meta: { reason: 'queue_health_unknown'; laneId: string }\n }\n | {\n code: 'secret_store_unavailable'\n meta: { reason: 'secret_store_unavailable' }\n }\n | { code: 'api_maintenance'; meta: { retryAfterSeconds: number } }\n\nconst SERVICE_UNAVAILABLE_CODES = new Set([\n 'queue_unavailable',\n 'queue_health_unknown',\n 'secret_store_unavailable',\n 'api_maintenance'\n])\n\n// A 503 whose body isn't the enumerated envelope (e.g. load-balancer HTML)\n// falls back to InternalServerError so `.body` below never lies.\nfunction isServiceUnavailableBody(\n body: object | undefined\n): body is ServiceUnavailableBody {\n const code = (body as ErrorEnvelope | undefined)?.code\n return typeof code === 'string' && SERVICE_UNAVAILABLE_CODES.has(code)\n}\n\nexport class ServiceUnavailableError extends InternalServerError {\n declare readonly status: 503\n\n /** The discriminated 503 envelope: `if (e.body.code === 'api_maintenance') e.body.meta.retryAfterSeconds`. */\n get body(): ServiceUnavailableBody {\n return this.error as ServiceUnavailableBody\n }\n}\n\n// ── No HTTP response ────────────────────────────────────────────────────────\n\nexport class APIConnectionError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({\n message,\n cause\n }: { message?: string | undefined; cause?: Error | undefined } = {}) {\n super(undefined, undefined, message ?? 'Connection error.', undefined)\n if (cause) this.cause = cause\n }\n}\n\nexport class APIConnectionTimeoutError extends APIConnectionError {\n constructor({ message }: { message?: string } = {}) {\n super({ message: message ?? 'Request timed out.' })\n }\n}\n\nexport class APIUserAbortError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({ message }: { message?: string } = {}) {\n super(undefined, undefined, message ?? 'Request was aborted.', undefined)\n }\n}\n\n// ── Job-level errors (thrown only by createAndWait) ──────────────────────────\n\n/** Catch-all for a job that reached a terminal state other than `completed`. */\nexport abstract class JobUnsuccessfulError extends SimmitError {\n readonly job: Job\n\n constructor(job: Job, message?: string) {\n super(\n message ??\n `Job ${job.id} ${job.status}` +\n (job.statusReason ? `: ${job.statusReason}` : '') +\n (job.errorCode ? ` (${job.errorCode})` : '')\n )\n this.job = job\n }\n}\n\nexport class JobFailedError extends JobUnsuccessfulError {}\n\n/** Includes queue_timeout auto-cancellation, not just user cancels. */\nexport class JobCancelledError extends JobUnsuccessfulError {}\n\n/** The job hit its runtime ceiling server-side and is billed for what ran. */\nexport class JobTimedOutError extends JobUnsuccessfulError {}\n\n/**\n * The SDK gave up polling. The job itself is still running and billing.\n * Keep tracking via `jobs.get(jobId)` or stop the spend with `jobs.cancel(jobId)`.\n */\nexport class JobWaitTimeoutError extends SimmitError {\n readonly jobId: string\n readonly lastStatus: JobStatus\n\n constructor(args: {\n jobId: string\n lastStatus: JobStatus\n message?: string\n }) {\n super(\n args.message ??\n `Timed out waiting for job ${args.jobId} (last status: ${args.lastStatus}). ` +\n 'The job is still running server-side and continues to bill.'\n )\n this.jobId = args.jobId\n this.lastStatus = args.lastStatus\n }\n}\n\n// ── Webhook verification (thrown by unwrapWebhook) ───────────────────────────\n\nexport class WebhookVerificationError extends SimmitError {}\n","/**\n * A `Promise<T>` with raw-response access: the generic answer to response\n * headers the return types can't see (`X-Idempotent-Replay`, `X-Active-Jobs`,\n * `X-RateLimit-*`).\n *\n * const { data, response } = await client.jobs.create(params).withResponse()\n * response.headers.get('x-idempotent-replay')\n */\nexport class APIPromise<T> extends Promise<T> {\n // Chained promises (.then/.catch) must be plain Promises: this class's\n // constructor signature is incompatible with the executor the runtime\n // would otherwise pass via the species constructor.\n static override get [Symbol.species]() {\n return Promise\n }\n\n readonly #parsed: Promise<{ data: T; response: Response }>\n\n constructor(parsed: Promise<{ data: T; response: Response }>) {\n // The base promise is a pre-settled placeholder that is never observed:\n // then() below delegates to #parsed lazily (catch/finally route through\n // then() per spec). Subscribing eagerly here would reject this instance\n // even when the caller only consumes withResponse(), leaking an\n // unhandled rejection on failures.\n super((resolve) => resolve(undefined as never))\n this.#parsed = parsed\n }\n\n override then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.#parsed\n .then((result) => result.data)\n .then(onfulfilled, onrejected)\n }\n\n withResponse(): Promise<{ data: T; response: Response }> {\n return this.#parsed\n }\n\n asResponse(): Promise<Response> {\n return this.#parsed.then((result) => result.response)\n }\n}\n","// Abort-aware timing utilities shared by the request layer (backoff sleeps) and\n// the createAndWait poll loop. A pending sleep rejects with APIUserAbortError\n// the moment the caller's signal fires.\nimport { APIUserAbortError } from '../error'\n\nexport function throwIfUserAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new APIUserAbortError()\n}\n\nexport function sleep(\n ms: number,\n signal: AbortSignal | undefined\n): Promise<void> {\n return new Promise((resolve, reject) => {\n throwIfUserAborted(signal)\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new APIUserAbortError())\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n","// Internal request layer: header assembly, per-attempt timeout/abort\n// composition, retry with backoff + Retry-After, idempotency-key injection,\n// and error mapping. Not exported from the package.\nimport { APIPromise } from '../api-promise'\nimport {\n APIConnectionError,\n APIConnectionTimeoutError,\n APIError,\n APIUserAbortError\n} from '../error'\nimport type { RequestOptions } from '../client'\nimport { sleep, throwIfUserAborted } from './abort'\n\nexport interface ClientConfig {\n secretKey: string\n baseURL: string\n timeout: number\n maxRetries: number\n defaultHeaders: Record<string, string | null | undefined> | undefined\n fetch: typeof globalThis.fetch\n fetchOptions: RequestInit | undefined\n}\n\nexport interface RequestSpec {\n method: 'GET' | 'POST'\n path: string\n body?: unknown\n /** POST job creation: auto-generate an idempotency-key when none supplied. */\n idempotent?: boolean\n}\n\n// Retry policy constants: typed code config, not env.\nconst INITIAL_BACKOFF_MS = 500\nconst MAX_BACKOFF_MS = 8_000\nconst MAX_RETRY_AFTER_MS = 60_000\n\nexport function makeRequest<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions = {}\n): APIPromise<T> {\n return new APIPromise(run<T>(config, spec, options))\n}\n\nasync function run<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Promise<{ data: T; response: Response }> {\n const maxRetries = options.maxRetries ?? config.maxRetries\n const timeout = options.timeout ?? config.timeout\n const headers = buildHeaders(config, spec, options)\n const url = `${config.baseURL.replace(/\\/+$/, '')}${spec.path}`\n const body = spec.body === undefined ? undefined : JSON.stringify(spec.body)\n\n for (let attempt = 0; ; attempt++) {\n throwIfUserAborted(options.signal)\n\n let result: AttemptResult\n try {\n result = await fetchAttempt(config, spec, options, {\n url,\n headers,\n body,\n timeout\n })\n } catch (err) {\n if (err instanceof APIUserAbortError) throw err\n // Connection error, malformed success body, or per-attempt timeout.\n // All retryable.\n if (attempt < maxRetries) {\n await backoff(attempt, undefined, options.signal)\n continue\n }\n throw err\n }\n\n const { response, json } = result\n\n if (response.ok) {\n return { data: json as T, response }\n }\n\n if (shouldRetryStatus(response.status) && attempt < maxRetries) {\n await backoff(\n attempt,\n response.headers.get('retry-after'),\n options.signal\n )\n continue\n }\n\n throw APIError.generate(\n response.status,\n typeof json === 'object' && json !== null ? json : undefined,\n response.statusText,\n response.headers\n )\n }\n}\n\ninterface AttemptResult {\n response: Response\n /** Parsed JSON body; undefined when an error response carried a non-JSON body. */\n json: unknown\n}\n\nasync function fetchAttempt(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions,\n attempt: {\n url: string\n headers: Record<string, string>\n body: string | undefined\n timeout: number\n }\n): Promise<AttemptResult> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), attempt.timeout)\n const onUserAbort = () => controller.abort()\n options.signal?.addEventListener('abort', onUserAbort, { once: true })\n\n try {\n const response = await config.fetch(attempt.url, {\n ...config.fetchOptions,\n method: spec.method,\n headers: attempt.headers,\n ...(attempt.body !== undefined ? { body: attempt.body } : {}),\n signal: controller.signal\n })\n\n // The body is read inside the timed scope too: a stalled body must not\n // hang past the per-attempt timeout (fetch ties the body stream to the\n // controller's signal, so the abort cancels the read).\n let json: unknown\n if (response.ok) {\n // Success bodies must parse; a truncated/malformed one is treated as a\n // transport failure (classified below) and retried like one.\n json = await response.json()\n } else {\n try {\n json = await response.json()\n } catch (err) {\n // Aborted mid-read is a timeout/abort, not a non-JSON body.\n if (controller.signal.aborted) throw err\n json = undefined // e.g. a load-balancer HTML error page\n }\n }\n return { response, json }\n } catch (err) {\n if (options.signal?.aborted) throw new APIUserAbortError()\n if (controller.signal.aborted) {\n throw new APIConnectionTimeoutError()\n }\n throw new APIConnectionError({\n cause: err instanceof Error ? err : undefined\n })\n } finally {\n clearTimeout(timeoutId)\n options.signal?.removeEventListener('abort', onUserAbort)\n }\n}\n\nfunction buildHeaders(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Record<string, string> {\n const idempotent = spec.idempotent && spec.method === 'POST'\n const merged: Record<string, string | null | undefined> = {\n authorization: `Bearer ${config.secretKey}`,\n ...(spec.body !== undefined ? { 'content-type': 'application/json' } : {}),\n ...(idempotent && !options.idempotencyKey\n ? {\n // Generated once per call and reused across retry attempts. That\n // is what makes POST retries safe by default. The auto\n // key is an SDK built-in default (lowest tier), so defaultHeaders\n // may override it.\n 'idempotency-key': `simmit-node-retry-${crypto.randomUUID()}`\n }\n : {}),\n ...lowercaseKeys(config.defaultHeaders),\n ...(idempotent && options.idempotencyKey\n ? {\n // An explicit key is a per-request option: it must beat constructor\n // defaultHeaders. Raw options.headers still wins last.\n 'idempotency-key': options.idempotencyKey\n }\n : {}),\n ...lowercaseKeys(options.headers)\n }\n\n const headers: Record<string, string> = {}\n for (const [key, value] of Object.entries(merged)) {\n // A null value deletes the header; undefined entries are skipped.\n if (typeof value === 'string') headers[key] = value\n }\n return headers\n}\n\nfunction lowercaseKeys(\n record: Record<string, string | null | undefined> | undefined\n): Record<string, string | null | undefined> {\n if (!record) return {}\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [key.toLowerCase(), value])\n )\n}\n\nfunction shouldRetryStatus(status: number): boolean {\n // 408 kept defensively even though the API never emits it. 409 is never\n // retried: result_not_ready is thrown immediately by design and the other\n // 409s are deterministic.\n return status === 408 || status === 429 || status >= 500\n}\n\nasync function backoff(\n attempt: number,\n retryAfterHeader: string | null | undefined,\n signal: AbortSignal | undefined\n): Promise<void> {\n const retryAfterMs = parseRetryAfter(retryAfterHeader)\n const delay =\n retryAfterMs !== undefined\n ? retryAfterMs\n : Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS) *\n (1 - 0.25 * Math.random())\n await sleep(delay, signal)\n}\n\n/** Accepts `Retry-After` only when it parses to a delay in (0, 60s]: the SDK never sleeps arbitrarily long on a server hint. */\nfunction parseRetryAfter(\n header: string | null | undefined\n): number | undefined {\n if (!header) return undefined\n let ms: number\n if (/^\\d+$/.test(header.trim())) {\n ms = Number(header.trim()) * 1000\n } else {\n ms = new Date(header).getTime() - Date.now()\n }\n return Number.isFinite(ms) && ms > 0 && ms <= MAX_RETRY_AFTER_MS\n ? ms\n : undefined\n}\n","import type { APIPromise } from '../api-promise'\nimport type { ArtifactUrl } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `artifacts` resource. */\nexport class Artifacts {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Fetch a stable public download URL for an artifact, valid for the\n * artifact's full retention window: the same URL `jobs.getResult` returns,\n * fetched on demand (e.g. browser flows that control the final fetch). The\n * artifact is gone (410) once its retention window passes.\n */\n getUrl(\n artifactId: string,\n options?: RequestOptions\n ): APIPromise<ArtifactUrl> {\n return this.#client._request<ArtifactUrl>(\n {\n method: 'GET',\n path: `/v1/simc/artifacts/${encodeURIComponent(artifactId)}/url`\n },\n options\n )\n }\n}\n","import type { APIPromise } from '../api-promise'\nimport type { CreditBalance } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `credits` resource. */\nexport class Credits {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /** Fetch the account's current credit balance and per-grant breakdown. */\n get(options?: RequestOptions): APIPromise<CreditBalance> {\n return this.#client._request<CreditBalance>(\n { method: 'GET', path: '/v1/simc/credits' },\n options\n )\n }\n}\n","// Pure helpers for the createAndWait poll loop. Kept separate from the\n// orchestration so the cadence and deadline math are unit-testable.\nimport type { JobCreateResponse, JobStatus } from '../api-types'\n\nexport const MIN_POLL_INTERVAL_MS = 100\nexport const DEFAULT_POLL_INTERVAL_MS = 1_000\nexport const MAX_POLL_INTERVAL_MS = 10_000\nexport const POLL_BACKOFF_FACTOR = 1.5\nconst DEADLINE_GRACE_MS = 60_000\nconst FALLBACK_WAIT_TIMEOUT_MS = 45 * 60 * 1_000\n\nconst TERMINAL_STATUSES = new Set<JobStatus>([\n 'completed',\n 'failed',\n 'cancelled',\n 'timed_out'\n])\n\nexport function isTerminal(status: JobStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/**\n * Default wait deadline derived from the applied ceilings the create response\n * reports: `(queueSeconds + runtimeSeconds) × 1000` plus a 60s grace, falling\n * back to 45 minutes when either ceiling is null.\n */\nexport function deriveWaitTimeoutMs(created: JobCreateResponse): number {\n const { runtimeSeconds, queueSeconds } = created.runtime.ceiling\n if (runtimeSeconds != null && queueSeconds != null) {\n return (runtimeSeconds + queueSeconds) * 1_000 + DEADLINE_GRACE_MS\n }\n return FALLBACK_WAIT_TIMEOUT_MS\n}\n\n/** Next poll interval: grow ×1.5, capped at 10s. */\nexport function nextPollInterval(interval: number): number {\n return Math.min(interval * POLL_BACKOFF_FACTOR, MAX_POLL_INTERVAL_MS)\n}\n","import type { APIPromise } from '../api-promise'\nimport type {\n CompletedJob,\n Job,\n JobCancelResponse,\n JobCreateParams,\n JobCreateResponse,\n JobResult,\n JobStatus,\n JobStatusResponse\n} from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\nimport {\n JobCancelledError,\n JobFailedError,\n JobTimedOutError,\n JobWaitTimeoutError\n} from '../error'\nimport { sleep } from '../internal/abort'\nimport {\n DEFAULT_POLL_INTERVAL_MS,\n deriveWaitTimeoutMs,\n isTerminal,\n MIN_POLL_INTERVAL_MS,\n nextPollInterval\n} from '../internal/poll'\n\nexport interface JobWaitOptions extends RequestOptions {\n /** Initial delay between status polls, ms. Grows ×1.5 per poll to a 10s cap; values under 100 are raised to it. Default 1_000. */\n pollIntervalMs?: number\n /** Overall wait deadline, ms. Default derived from the job's applied ceilings. */\n waitTimeoutMs?: number\n /** Fired once with the raw create response (job id, ceilings, input warnings) before polling. */\n onCreated?: (response: JobCreateResponse) => void\n /** Fired after every successful status poll (progress, stage, queue estimate). */\n onPoll?: (status: JobStatusResponse) => void\n}\n\n/**\n * The `jobs` resource. Each single-request method is a thin wrapper over\n * `client._request` with the path/method/types pinned to the spec;\n * `createAndWait` orchestrates several of them.\n */\nexport class Jobs {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Submit a new SimC sim. Returns immediately with the job handle; the sim\n * runs asynchronously. `idempotent: true` makes the request layer attach an\n * auto-generated idempotency key so the POST is safe to retry; pass\n * `options.idempotencyKey` to supply your own.\n */\n create(\n params: JobCreateParams,\n options?: RequestOptions\n ): APIPromise<JobCreateResponse> {\n return this.#client._request<JobCreateResponse>(\n { method: 'POST', path: '/v1/simc/jobs', body: params, idempotent: true },\n options\n )\n }\n\n /** Fetch the full record for a job. */\n get(jobId: string, options?: RequestOptions): APIPromise<Job> {\n return this.#client._request<Job>(\n { method: 'GET', path: `/v1/simc/jobs/${encodeURIComponent(jobId)}` },\n options\n )\n }\n\n /**\n * Fetch the live status of a job in any state: `status`, `errorCode`,\n * `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a\n * non-terminal job, so it is the supported way to drive a custom poll loop.\n */\n getStatus(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobStatusResponse> {\n return this.#client._request<JobStatusResponse>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/status`\n },\n options\n )\n }\n\n /**\n * Fetch the result summary of a terminal job. Throws `ResultNotReadyError`\n * (409) while the job is still running. Poll `/status` or use\n * `createAndWait` rather than `/result` for a job in flight.\n */\n getResult(jobId: string, options?: RequestOptions): APIPromise<JobResult> {\n return this.#client._request<JobResult>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/result`\n },\n options\n )\n }\n\n /**\n * Submit a job and resolve once it reaches a terminal state. Polls\n * `GET /v1/simc/jobs/{id}/status` (first after `pollIntervalMs`, then ×1.5 to\n * a 10s cap), then fetches the full record. Resolves with the `CompletedJob`\n * on success; throws `JobFailedError` / `JobCancelledError` /\n * `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`\n * if the deadline passes first. The job keeps running and is **not**\n * cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait\n * with `APIUserAbortError`, also without cancelling.\n */\n async createAndWait(\n params: JobCreateParams,\n options: JobWaitOptions = {}\n ): Promise<CompletedJob> {\n const {\n pollIntervalMs,\n waitTimeoutMs,\n onCreated,\n onPoll,\n ...requestOptions\n } = options\n\n const created = await this.create(params, requestOptions)\n onCreated?.(created)\n\n const deadline =\n Date.now() + (waitTimeoutMs ?? deriveWaitTimeoutMs(created))\n // nextPollInterval only ever grows the interval, so a non-positive seed\n // would hot-poll the status endpoint; floor it.\n let interval = Math.max(\n pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n MIN_POLL_INTERVAL_MS\n )\n let lastStatus: JobStatus = 'pending'\n\n for (;;) {\n const remaining = deadline - Date.now()\n if (remaining <= 0) {\n throw new JobWaitTimeoutError({ jobId: created.id, lastStatus })\n }\n // Never sleep past the deadline, so the wait gives up promptly.\n await sleep(Math.min(interval, remaining), requestOptions.signal)\n\n const status = await this.getStatus(created.id, requestOptions)\n onPoll?.(status)\n lastStatus = status.status\n\n if (isTerminal(status.status)) {\n // The status payload is lightweight; the full record carries the fields\n // CompletedJob and the job-error classes expose.\n const job = await this.get(created.id, requestOptions)\n switch (job.status) {\n case 'completed':\n return job as CompletedJob\n case 'failed':\n throw new JobFailedError(job)\n case 'cancelled':\n throw new JobCancelledError(job)\n case 'timed_out':\n throw new JobTimedOutError(job)\n }\n // Raced back to non-terminal between /status and the full record; keep polling.\n lastStatus = job.status\n }\n interval = nextPollInterval(interval)\n }\n }\n\n /**\n * Request cancellation. Returns `status: 'cancelled'` when the job ended\n * before it ran, or `status: 'cancel_requested'` when an in-flight job was\n * signaled to stop. Repeat calls are naturally idempotent, so no key is sent.\n */\n cancel(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobCancelResponse> {\n return this.#client._request<JobCancelResponse>(\n {\n method: 'POST',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/cancel`\n },\n options\n )\n }\n}\n","import { APIPromise } from './api-promise'\nimport { SimmitError } from './error'\nimport {\n makeRequest,\n type ClientConfig,\n type RequestSpec\n} from './internal/request'\nimport { Artifacts } from './resources/artifacts'\nimport { Credits } from './resources/credits'\nimport { Jobs } from './resources/jobs'\n\nexport interface ClientOptions {\n /** Defaults to process.env['SIMMIT_SECRET_KEY'], exactly one env fallback. Construction\n * throws SimmitError('Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.').\n * \"Secret key\" is the credential noun end to end (dashboard → docs → env var → option →\n * error): it spends credits and must never ship client-side. */\n secretKey?: string | null\n /** Defaults to process.env['SIMMIT_BASE_URL'] ?? 'https://api.simmit.com'. */\n baseURL?: string | null\n /** Per-attempt timeout in ms. Default 60_000. (Retries can extend total wall time.) */\n timeout?: number\n /** Max retries after the first attempt for retryable failures. Default 2. */\n maxRetries?: number\n /** Headers sent with every request. Merged under per-request headers. */\n defaultHeaders?: Record<string, string | null | undefined>\n /** Custom fetch (testing, proxies). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch\n /** Extra RequestInit fields passed to every fetch call (e.g. undici dispatcher). */\n fetchOptions?: RequestInit\n}\n\nexport interface RequestOptions {\n /** Per-attempt timeout in ms. Overrides ClientOptions.timeout. */\n timeout?: number\n /** Abort the call (including retries and waiting). Throws APIUserAbortError. Never retried. */\n signal?: AbortSignal\n /** Overrides ClientOptions.maxRetries for this call. */\n maxRetries?: number\n /** Merged over defaultHeaders; a null value deletes the header. */\n headers?: Record<string, string | null | undefined>\n /** jobs.create / jobs.createAndWait only: replaces the auto-generated idempotency-key. */\n idempotencyKey?: string\n}\n\nexport default class Simmit {\n readonly jobs: Jobs\n readonly credits: Credits\n readonly artifacts: Artifacts\n\n readonly baseURL: string\n\n readonly #config: ClientConfig\n\n constructor(options: ClientOptions = {}) {\n const secretKey = options.secretKey ?? readEnv('SIMMIT_SECRET_KEY')\n if (!secretKey) {\n throw new SimmitError(\n 'Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.'\n )\n }\n\n this.baseURL =\n options.baseURL ?? readEnv('SIMMIT_BASE_URL') ?? 'https://api.simmit.com'\n\n this.#config = {\n secretKey,\n baseURL: this.baseURL,\n timeout: options.timeout ?? 60_000,\n maxRetries: options.maxRetries ?? 2,\n defaultHeaders: options.defaultHeaders,\n // Resolved lazily so a fetch patched onto globalThis after the client\n // is constructed (msw, APM instrumentation) is still honored.\n fetch: options.fetch ?? ((...args) => globalThis.fetch(...args)),\n fetchOptions: options.fetchOptions\n }\n\n this.jobs = new Jobs(this)\n this.credits = new Credits(this)\n this.artifacts = new Artifacts(this)\n }\n\n /** @internal Resource classes route through here; not public surface. */\n _request<T>(spec: RequestSpec, options?: RequestOptions): APIPromise<T> {\n return makeRequest(this.#config, spec, options)\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined') return undefined\n const value = process.env?.[name]?.trim()\n return value || undefined\n}\n","// Standalone webhook verification. Not a client method: receivers must not\n// need a secret-key-bearing client (whose constructor throws without a key).\n// WebCrypto only, zero deps, and runs in Workers as well as Node.\nimport type { JobStatus } from './api-types'\nimport { WebhookVerificationError } from './error'\n\nconst DEFAULT_TOLERANCE_SECONDS = 300\n\n/** The one hand-written wire type: the webhook payload has no OpenAPI schema. */\nexport interface WebhookEvent {\n kind: 'job.terminal'\n version: 'v1'\n timestamp: string\n payload: {\n id: string\n statusReason: string | null\n status: Extract<\n JobStatus,\n 'completed' | 'failed' | 'cancelled' | 'timed_out'\n >\n }\n}\n\n/**\n * Verifies an `X-Simmit-Signature` header (`t=<unix>,v1=<hex>`, an HMAC-SHA256\n * (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance) and\n * returns the parsed event. Throws `WebhookVerificationError` on a bad\n * signature, malformed header, or stale timestamp.\n *\n * Pass `rawBody` exactly as received: re-serializing changes the bytes and\n * breaks verification. `secret` is the webhook signing secret (dashboard →\n * Clients & Keys → Webhook), not your API key.\n */\nexport async function unwrapWebhook(\n rawBody: string,\n signatureHeader: string,\n secret: string,\n options?: { toleranceSeconds?: number }\n): Promise<WebhookEvent> {\n // An empty secret would otherwise surface as an opaque WebCrypto DataError;\n // a NaN tolerance would make the age check pass for everything.\n if (!secret) {\n throw new WebhookVerificationError('Webhook signing secret is empty.')\n }\n const tolerance = options?.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new WebhookVerificationError(\n 'toleranceSeconds must be a non-negative number.'\n )\n }\n\n const { timestampRaw, timestamp, signature } =\n parseSignatureHeader(signatureHeader)\n\n const expected = await hmacSha256Hex(secret, `${timestampRaw}.${rawBody}`)\n if (!timingSafeEqual(expected, signature)) {\n throw new WebhookVerificationError('Webhook signature does not match.')\n }\n\n // Compare on whole seconds, matching the header's unix-seconds `t`.\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > tolerance) {\n throw new WebhookVerificationError(\n 'Webhook timestamp is outside the tolerance window.'\n )\n }\n\n try {\n return JSON.parse(rawBody) as WebhookEvent\n } catch {\n throw new WebhookVerificationError('Webhook body is not valid JSON.')\n }\n}\n\nfunction parseSignatureHeader(header: string): {\n timestampRaw: string\n timestamp: number\n signature: string\n} {\n let timestampRaw: string | undefined\n let signature: string | undefined\n for (const part of header.split(',')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n const key = part.slice(0, eq).trim()\n const value = part.slice(eq + 1).trim()\n if (key === 't') timestampRaw = value\n else if (key === 'v1') signature = value\n }\n\n // `t` is unix whole seconds; reject anything but digits so the accepted\n // header matches the documented contract.\n if (!timestampRaw || !signature || !/^\\d+$/.test(timestampRaw)) {\n throw new WebhookVerificationError(\n 'Malformed signature header; expected \"t=<unix>,v1=<hex>\".'\n )\n }\n // The signed payload uses the timestamp exactly as sent, so keep the raw\n // string for signing and the parsed number only for the tolerance check.\n return { timestampRaw, timestamp: Number(timestampRaw), signature }\n}\n\nasync function hmacSha256Hex(secret: string, payload: string): Promise<string> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n return toHex(new Uint8Array(mac))\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let hex = ''\n for (const byte of bytes) hex += byte.toString(16).padStart(2, '0')\n return hex\n}\n\n// Constant-time comparison. The digest width is public, so a length mismatch\n// may short-circuit without leaking secret-dependent timing.\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let mismatch = 0\n for (let i = 0; i < a.length; i++) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i)\n }\n return mismatch === 0\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A `Promise<T>` with raw-response access
|
|
2
|
+
* A `Promise<T>` with raw-response access: the generic answer to response
|
|
3
3
|
* headers the return types can't see (`X-Idempotent-Replay`, `X-Active-Jobs`,
|
|
4
4
|
* `X-RateLimit-*`).
|
|
5
5
|
*
|
|
@@ -1789,7 +1789,7 @@ declare class Artifacts {
|
|
|
1789
1789
|
constructor(client: Simmit);
|
|
1790
1790
|
/**
|
|
1791
1791
|
* Fetch a stable public download URL for an artifact, valid for the
|
|
1792
|
-
* artifact's full retention window
|
|
1792
|
+
* artifact's full retention window: the same URL `jobs.getResult` returns,
|
|
1793
1793
|
* fetched on demand (e.g. browser flows that control the final fetch). The
|
|
1794
1794
|
* artifact is gone (410) once its retention window passes.
|
|
1795
1795
|
*/
|
|
@@ -1832,14 +1832,14 @@ declare class Jobs {
|
|
|
1832
1832
|
/** Fetch the full record for a job. */
|
|
1833
1833
|
get(jobId: string, options?: RequestOptions): APIPromise<Job>;
|
|
1834
1834
|
/**
|
|
1835
|
-
* Fetch the live status of a job in any state
|
|
1835
|
+
* Fetch the live status of a job in any state: `status`, `errorCode`,
|
|
1836
1836
|
* `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a
|
|
1837
1837
|
* non-terminal job, so it is the supported way to drive a custom poll loop.
|
|
1838
1838
|
*/
|
|
1839
1839
|
getStatus(jobId: string, options?: RequestOptions): APIPromise<JobStatusResponse>;
|
|
1840
1840
|
/**
|
|
1841
1841
|
* Fetch the result summary of a terminal job. Throws `ResultNotReadyError`
|
|
1842
|
-
* (409) while the job is still running
|
|
1842
|
+
* (409) while the job is still running. Poll `/status` or use
|
|
1843
1843
|
* `createAndWait` rather than `/result` for a job in flight.
|
|
1844
1844
|
*/
|
|
1845
1845
|
getResult(jobId: string, options?: RequestOptions): APIPromise<JobResult>;
|
|
@@ -1849,7 +1849,7 @@ declare class Jobs {
|
|
|
1849
1849
|
* a 10s cap), then fetches the full record. Resolves with the `CompletedJob`
|
|
1850
1850
|
* on success; throws `JobFailedError` / `JobCancelledError` /
|
|
1851
1851
|
* `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`
|
|
1852
|
-
* if the deadline passes first
|
|
1852
|
+
* if the deadline passes first. The job keeps running and is **not**
|
|
1853
1853
|
* cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait
|
|
1854
1854
|
* with `APIUserAbortError`, also without cancelling.
|
|
1855
1855
|
*/
|
|
@@ -1863,7 +1863,7 @@ declare class Jobs {
|
|
|
1863
1863
|
}
|
|
1864
1864
|
|
|
1865
1865
|
interface ClientOptions {
|
|
1866
|
-
/** Defaults to process.env['SIMMIT_SECRET_KEY']
|
|
1866
|
+
/** Defaults to process.env['SIMMIT_SECRET_KEY'], exactly one env fallback. Construction
|
|
1867
1867
|
* throws SimmitError('Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.').
|
|
1868
1868
|
* "Secret key" is the credential noun end to end (dashboard → docs → env var → option →
|
|
1869
1869
|
* error): it spends credits and must never ship client-side. */
|
|
@@ -1922,7 +1922,7 @@ declare class APIError<TStatus extends number | undefined = number | undefined,
|
|
|
1922
1922
|
readonly code: TCode;
|
|
1923
1923
|
/** Typed `meta` from the error envelope. */
|
|
1924
1924
|
readonly meta: TMeta;
|
|
1925
|
-
/** Raw parsed JSON error body
|
|
1925
|
+
/** Raw parsed JSON error body: escape hatch for unmapped fields. */
|
|
1926
1926
|
readonly error: object | undefined;
|
|
1927
1927
|
constructor(status: TStatus, body: object | undefined, message: string | undefined, headers: Headers | undefined);
|
|
1928
1928
|
private static makeMessage;
|
|
@@ -1954,7 +1954,7 @@ declare class InsufficientCreditsError extends BillingError {
|
|
|
1954
1954
|
}
|
|
1955
1955
|
type InsufficientCreditsLiabilityMeta = {
|
|
1956
1956
|
reason: string;
|
|
1957
|
-
/** The high-priority fee in effect
|
|
1957
|
+
/** The high-priority fee in effect. Top up, or resubmit at priority 'standard'. */
|
|
1958
1958
|
priorityFeeCredits: number;
|
|
1959
1959
|
docsUrl?: string;
|
|
1960
1960
|
};
|
|
@@ -2036,7 +2036,7 @@ declare class MaxActiveJobsError extends RateLimitError {
|
|
|
2036
2036
|
declare class InternalServerError extends APIError<number, string> {
|
|
2037
2037
|
}
|
|
2038
2038
|
/**
|
|
2039
|
-
* 503 carries four enumerated codes with distinct meta
|
|
2039
|
+
* 503 carries four enumerated codes with distinct meta: a discriminated
|
|
2040
2040
|
* union, narrowed via `.body`. `api_maintenance` gets no special retry
|
|
2041
2041
|
* behavior: standard policy applies, and the typed
|
|
2042
2042
|
* `meta.retryAfterSeconds` is surfaced so callers can schedule their own
|
|
@@ -2100,7 +2100,7 @@ declare class JobCancelledError extends JobUnsuccessfulError {
|
|
|
2100
2100
|
declare class JobTimedOutError extends JobUnsuccessfulError {
|
|
2101
2101
|
}
|
|
2102
2102
|
/**
|
|
2103
|
-
* The SDK gave up polling
|
|
2103
|
+
* The SDK gave up polling. The job itself is still running and billing.
|
|
2104
2104
|
* Keep tracking via `jobs.get(jobId)` or stop the spend with `jobs.cancel(jobId)`.
|
|
2105
2105
|
*/
|
|
2106
2106
|
declare class JobWaitTimeoutError extends SimmitError {
|
|
@@ -2127,8 +2127,8 @@ interface WebhookEvent {
|
|
|
2127
2127
|
};
|
|
2128
2128
|
}
|
|
2129
2129
|
/**
|
|
2130
|
-
* Verifies an `X-Simmit-Signature` header
|
|
2131
|
-
* (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance
|
|
2130
|
+
* Verifies an `X-Simmit-Signature` header (`t=<unix>,v1=<hex>`, an HMAC-SHA256
|
|
2131
|
+
* (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance) and
|
|
2132
2132
|
* returns the parsed event. Throws `WebhookVerificationError` on a bad
|
|
2133
2133
|
* signature, malformed header, or stale timestamp.
|
|
2134
2134
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A `Promise<T>` with raw-response access
|
|
2
|
+
* A `Promise<T>` with raw-response access: the generic answer to response
|
|
3
3
|
* headers the return types can't see (`X-Idempotent-Replay`, `X-Active-Jobs`,
|
|
4
4
|
* `X-RateLimit-*`).
|
|
5
5
|
*
|
|
@@ -1789,7 +1789,7 @@ declare class Artifacts {
|
|
|
1789
1789
|
constructor(client: Simmit);
|
|
1790
1790
|
/**
|
|
1791
1791
|
* Fetch a stable public download URL for an artifact, valid for the
|
|
1792
|
-
* artifact's full retention window
|
|
1792
|
+
* artifact's full retention window: the same URL `jobs.getResult` returns,
|
|
1793
1793
|
* fetched on demand (e.g. browser flows that control the final fetch). The
|
|
1794
1794
|
* artifact is gone (410) once its retention window passes.
|
|
1795
1795
|
*/
|
|
@@ -1832,14 +1832,14 @@ declare class Jobs {
|
|
|
1832
1832
|
/** Fetch the full record for a job. */
|
|
1833
1833
|
get(jobId: string, options?: RequestOptions): APIPromise<Job>;
|
|
1834
1834
|
/**
|
|
1835
|
-
* Fetch the live status of a job in any state
|
|
1835
|
+
* Fetch the live status of a job in any state: `status`, `errorCode`,
|
|
1836
1836
|
* `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a
|
|
1837
1837
|
* non-terminal job, so it is the supported way to drive a custom poll loop.
|
|
1838
1838
|
*/
|
|
1839
1839
|
getStatus(jobId: string, options?: RequestOptions): APIPromise<JobStatusResponse>;
|
|
1840
1840
|
/**
|
|
1841
1841
|
* Fetch the result summary of a terminal job. Throws `ResultNotReadyError`
|
|
1842
|
-
* (409) while the job is still running
|
|
1842
|
+
* (409) while the job is still running. Poll `/status` or use
|
|
1843
1843
|
* `createAndWait` rather than `/result` for a job in flight.
|
|
1844
1844
|
*/
|
|
1845
1845
|
getResult(jobId: string, options?: RequestOptions): APIPromise<JobResult>;
|
|
@@ -1849,7 +1849,7 @@ declare class Jobs {
|
|
|
1849
1849
|
* a 10s cap), then fetches the full record. Resolves with the `CompletedJob`
|
|
1850
1850
|
* on success; throws `JobFailedError` / `JobCancelledError` /
|
|
1851
1851
|
* `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`
|
|
1852
|
-
* if the deadline passes first
|
|
1852
|
+
* if the deadline passes first. The job keeps running and is **not**
|
|
1853
1853
|
* cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait
|
|
1854
1854
|
* with `APIUserAbortError`, also without cancelling.
|
|
1855
1855
|
*/
|
|
@@ -1863,7 +1863,7 @@ declare class Jobs {
|
|
|
1863
1863
|
}
|
|
1864
1864
|
|
|
1865
1865
|
interface ClientOptions {
|
|
1866
|
-
/** Defaults to process.env['SIMMIT_SECRET_KEY']
|
|
1866
|
+
/** Defaults to process.env['SIMMIT_SECRET_KEY'], exactly one env fallback. Construction
|
|
1867
1867
|
* throws SimmitError('Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.').
|
|
1868
1868
|
* "Secret key" is the credential noun end to end (dashboard → docs → env var → option →
|
|
1869
1869
|
* error): it spends credits and must never ship client-side. */
|
|
@@ -1922,7 +1922,7 @@ declare class APIError<TStatus extends number | undefined = number | undefined,
|
|
|
1922
1922
|
readonly code: TCode;
|
|
1923
1923
|
/** Typed `meta` from the error envelope. */
|
|
1924
1924
|
readonly meta: TMeta;
|
|
1925
|
-
/** Raw parsed JSON error body
|
|
1925
|
+
/** Raw parsed JSON error body: escape hatch for unmapped fields. */
|
|
1926
1926
|
readonly error: object | undefined;
|
|
1927
1927
|
constructor(status: TStatus, body: object | undefined, message: string | undefined, headers: Headers | undefined);
|
|
1928
1928
|
private static makeMessage;
|
|
@@ -1954,7 +1954,7 @@ declare class InsufficientCreditsError extends BillingError {
|
|
|
1954
1954
|
}
|
|
1955
1955
|
type InsufficientCreditsLiabilityMeta = {
|
|
1956
1956
|
reason: string;
|
|
1957
|
-
/** The high-priority fee in effect
|
|
1957
|
+
/** The high-priority fee in effect. Top up, or resubmit at priority 'standard'. */
|
|
1958
1958
|
priorityFeeCredits: number;
|
|
1959
1959
|
docsUrl?: string;
|
|
1960
1960
|
};
|
|
@@ -2036,7 +2036,7 @@ declare class MaxActiveJobsError extends RateLimitError {
|
|
|
2036
2036
|
declare class InternalServerError extends APIError<number, string> {
|
|
2037
2037
|
}
|
|
2038
2038
|
/**
|
|
2039
|
-
* 503 carries four enumerated codes with distinct meta
|
|
2039
|
+
* 503 carries four enumerated codes with distinct meta: a discriminated
|
|
2040
2040
|
* union, narrowed via `.body`. `api_maintenance` gets no special retry
|
|
2041
2041
|
* behavior: standard policy applies, and the typed
|
|
2042
2042
|
* `meta.retryAfterSeconds` is surfaced so callers can schedule their own
|
|
@@ -2100,7 +2100,7 @@ declare class JobCancelledError extends JobUnsuccessfulError {
|
|
|
2100
2100
|
declare class JobTimedOutError extends JobUnsuccessfulError {
|
|
2101
2101
|
}
|
|
2102
2102
|
/**
|
|
2103
|
-
* The SDK gave up polling
|
|
2103
|
+
* The SDK gave up polling. The job itself is still running and billing.
|
|
2104
2104
|
* Keep tracking via `jobs.get(jobId)` or stop the spend with `jobs.cancel(jobId)`.
|
|
2105
2105
|
*/
|
|
2106
2106
|
declare class JobWaitTimeoutError extends SimmitError {
|
|
@@ -2127,8 +2127,8 @@ interface WebhookEvent {
|
|
|
2127
2127
|
};
|
|
2128
2128
|
}
|
|
2129
2129
|
/**
|
|
2130
|
-
* Verifies an `X-Simmit-Signature` header
|
|
2131
|
-
* (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance
|
|
2130
|
+
* Verifies an `X-Simmit-Signature` header (`t=<unix>,v1=<hex>`, an HMAC-SHA256
|
|
2131
|
+
* (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance) and
|
|
2132
2132
|
* returns the parsed event. Throws `WebhookVerificationError` on a bad
|
|
2133
2133
|
* signature, malformed header, or stale timestamp.
|
|
2134
2134
|
*
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var APIError = class _APIError extends SimmitError {
|
|
|
10
10
|
code;
|
|
11
11
|
/** Typed `meta` from the error envelope. */
|
|
12
12
|
meta;
|
|
13
|
-
/** Raw parsed JSON error body
|
|
13
|
+
/** Raw parsed JSON error body: escape hatch for unmapped fields. */
|
|
14
14
|
error;
|
|
15
15
|
constructor(status, body, message, headers) {
|
|
16
16
|
super(_APIError.makeMessage(status, body, message));
|
|
@@ -337,7 +337,7 @@ function buildHeaders(config, spec, options) {
|
|
|
337
337
|
authorization: `Bearer ${config.secretKey}`,
|
|
338
338
|
...spec.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
339
339
|
...idempotent && !options.idempotencyKey ? {
|
|
340
|
-
// Generated once per call and reused across retry attempts
|
|
340
|
+
// Generated once per call and reused across retry attempts. That
|
|
341
341
|
// is what makes POST retries safe by default. The auto
|
|
342
342
|
// key is an SDK built-in default (lowest tier), so defaultHeaders
|
|
343
343
|
// may override it.
|
|
@@ -390,7 +390,7 @@ var Artifacts = class {
|
|
|
390
390
|
}
|
|
391
391
|
/**
|
|
392
392
|
* Fetch a stable public download URL for an artifact, valid for the
|
|
393
|
-
* artifact's full retention window
|
|
393
|
+
* artifact's full retention window: the same URL `jobs.getResult` returns,
|
|
394
394
|
* fetched on demand (e.g. browser flows that control the final fetch). The
|
|
395
395
|
* artifact is gone (410) once its retention window passes.
|
|
396
396
|
*/
|
|
@@ -473,7 +473,7 @@ var Jobs = class {
|
|
|
473
473
|
);
|
|
474
474
|
}
|
|
475
475
|
/**
|
|
476
|
-
* Fetch the live status of a job in any state
|
|
476
|
+
* Fetch the live status of a job in any state: `status`, `errorCode`,
|
|
477
477
|
* `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a
|
|
478
478
|
* non-terminal job, so it is the supported way to drive a custom poll loop.
|
|
479
479
|
*/
|
|
@@ -488,7 +488,7 @@ var Jobs = class {
|
|
|
488
488
|
}
|
|
489
489
|
/**
|
|
490
490
|
* Fetch the result summary of a terminal job. Throws `ResultNotReadyError`
|
|
491
|
-
* (409) while the job is still running
|
|
491
|
+
* (409) while the job is still running. Poll `/status` or use
|
|
492
492
|
* `createAndWait` rather than `/result` for a job in flight.
|
|
493
493
|
*/
|
|
494
494
|
getResult(jobId, options) {
|
|
@@ -506,7 +506,7 @@ var Jobs = class {
|
|
|
506
506
|
* a 10s cap), then fetches the full record. Resolves with the `CompletedJob`
|
|
507
507
|
* on success; throws `JobFailedError` / `JobCancelledError` /
|
|
508
508
|
* `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`
|
|
509
|
-
* if the deadline passes first
|
|
509
|
+
* if the deadline passes first. The job keeps running and is **not**
|
|
510
510
|
* cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait
|
|
511
511
|
* with `APIUserAbortError`, also without cancelling.
|
|
512
512
|
*/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/error.ts","../src/api-promise.ts","../src/internal/abort.ts","../src/internal/request.ts","../src/resources/artifacts.ts","../src/resources/credits.ts","../src/internal/poll.ts","../src/resources/jobs.ts","../src/client.ts","../src/webhook.ts"],"sourcesContent":["import type { Job, JobStatus } from './api-types'\n\nexport class SimmitError extends Error {}\n\n/**\n * Value shapes the API's generic error `meta` bag can carry\n * (400/401/404/410/413 responses): JSON scalars, scalar arrays, or arrays of\n * flat objects.\n */\nexport type MetaValue =\n | string\n | number\n | boolean\n | null\n | Array<string | number | boolean>\n | Array<Record<string, string | number | boolean | null>>\n\nexport type GenericMeta = Record<string, MetaValue>\n\n/** The API's uniform error envelope: `{ error, code, meta }`. */\ninterface ErrorEnvelope {\n error?: unknown\n code?: unknown\n meta?: unknown\n}\n\nexport class APIError<\n TStatus extends number | undefined = number | undefined,\n TCode extends string | undefined = string | undefined,\n TMeta = GenericMeta | null\n> extends SimmitError {\n /** HTTP status of the response that caused the error. */\n readonly status: TStatus\n /** HTTP headers of the response that caused the error. */\n readonly headers: Headers | undefined\n /** Machine-readable `code` from the error envelope. */\n readonly code: TCode\n /** Typed `meta` from the error envelope. */\n readonly meta: TMeta\n /** Raw parsed JSON error body — escape hatch for unmapped fields. */\n readonly error: object | undefined\n\n constructor(\n status: TStatus,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ) {\n super(APIError.makeMessage(status, body, message))\n this.status = status\n this.headers = headers\n this.error = body\n const envelope = body as ErrorEnvelope | undefined\n this.code = (\n typeof envelope?.code === 'string' ? envelope.code : undefined\n ) as TCode\n this.meta = (body ? (envelope?.meta ?? null) : undefined) as TMeta\n }\n\n private static makeMessage(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined\n ): string {\n // The API's human-readable message field is named `error`, not `message`.\n const bodyMessage = (body as ErrorEnvelope | undefined)?.error\n const msg =\n typeof bodyMessage === 'string'\n ? bodyMessage\n : body\n ? JSON.stringify(body)\n : message\n\n if (status && msg) return `${status} ${msg}`\n if (status) return `${status} status code (no body)`\n if (msg) return msg\n return '(no status code or body)'\n }\n\n /**\n * Maps a response to the most specific error class: status selects the base\n * class; an enumerated `code` with structured `meta` selects the subclass;\n * anything unrecognized falls back to the status class so new server codes\n * degrade gracefully without breaking `instanceof` handling.\n */\n static generate(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ): APIError<\n number | undefined,\n string | undefined,\n GenericMeta | null | undefined\n > {\n if (!status || !headers) {\n return new APIConnectionError({\n message,\n cause: body instanceof Error ? body : undefined\n })\n }\n\n const code = (body as ErrorEnvelope | undefined)?.code\n\n if (status === 400) return new BadRequestError(400, body, message, headers)\n if (status === 401) {\n return new AuthenticationError(401, body, message, headers)\n }\n if (status === 402) {\n if (code === 'insufficient_credits') {\n return new InsufficientCreditsError(402, body, message, headers)\n }\n if (code === 'insufficient_credits_liability') {\n return new InsufficientCreditsLiabilityError(\n 402,\n body,\n message,\n headers\n )\n }\n return new BillingError(402, body, message, headers)\n }\n if (status === 404) return new NotFoundError(404, body, message, headers)\n if (status === 409) {\n if (code === 'idempotency_key_reuse') {\n return new IdempotencyKeyReuseError(409, body, message, headers)\n }\n if (code === 'result_not_ready') {\n return new ResultNotReadyError(409, body, message, headers)\n }\n if (code === 'job_not_cancellable') {\n return new JobNotCancellableError(409, body, message, headers)\n }\n return new ConflictError(409, body, message, headers)\n }\n if (status === 413) {\n return new RequestTooLargeError(413, body, message, headers)\n }\n if (status === 422) {\n if (code === 'input_sanitized_rejected') {\n return new InvalidProfileError(422, body, message, headers)\n }\n if (code === 'result_unavailable') {\n return new ResultUnavailableError(422, body, message, headers)\n }\n return new UnprocessableEntityError(422, body, message, headers)\n }\n if (status === 429) {\n if (code === 'max_active_jobs_exceeded') {\n return new MaxActiveJobsError(429, body, message, headers)\n }\n return new RateLimitError(429, body, message, headers)\n }\n if (status === 503 && isServiceUnavailableBody(body)) {\n return new ServiceUnavailableError(503, body, message, headers)\n }\n if (status >= 500) {\n return new InternalServerError(status, body, message, headers)\n }\n return new APIError(status, body, message, headers)\n }\n}\n\n// ── 4xx status classes (code subclasses where the spec enumerates) ──────────\n\nexport class BadRequestError extends APIError<400, string> {}\n\nexport type AuthenticationErrorCode =\n | 'missing_token'\n | 'invalid_token'\n | 'revoked_token'\n | 'expired_token'\n\nexport class AuthenticationError extends APIError<\n 401,\n AuthenticationErrorCode\n> {}\n\n// 402 codes are docs-enumerated; the spec leaves `code` un-enumerated, so the\n// base class keeps `string` for forward compatibility.\nexport class BillingError extends APIError<402, string> {}\n\nexport type InsufficientCreditsMeta = {\n reason: string\n ceilingRuntimeSeconds?: number\n /** Largest maxRuntimeSeconds the current balance can cover. */\n maxAffordableRuntimeSeconds?: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsError extends BillingError {\n declare readonly code: 'insufficient_credits'\n declare readonly meta: InsufficientCreditsMeta | null\n}\n\nexport type InsufficientCreditsLiabilityMeta = {\n reason: string\n /** The high-priority fee in effect — top up, or resubmit at priority 'standard'. */\n priorityFeeCredits: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsLiabilityError extends BillingError {\n declare readonly code: 'insufficient_credits_liability'\n declare readonly meta: InsufficientCreditsLiabilityMeta | null\n}\n\nexport class NotFoundError extends APIError<404, string> {}\n\nexport class ConflictError extends APIError<409, string> {}\n\nexport class IdempotencyKeyReuseError extends ConflictError {\n declare readonly code: 'idempotency_key_reuse'\n declare readonly meta: {\n reason: 'idempotency_key_reuse'\n /** ID of the job that originally consumed this idempotency key. */\n originalJobId: string\n docsUrl?: string\n }\n}\n\nexport class ResultNotReadyError extends ConflictError {\n declare readonly code: 'result_not_ready'\n declare readonly meta: {\n status: 'pending' | 'queued' | 'starting' | 'running'\n }\n}\n\nexport class JobNotCancellableError extends ConflictError {\n declare readonly code: 'job_not_cancellable'\n declare readonly meta: { id: string; status: JobStatus }\n}\n\nexport class RequestTooLargeError extends APIError<413, string> {}\n\nexport class UnprocessableEntityError extends APIError<422, string> {}\n\nexport class InvalidProfileError extends UnprocessableEntityError {\n declare readonly code: 'input_sanitized_rejected'\n declare readonly meta: {\n reason: 'input_sanitized_rejected'\n message: string\n docsUrl: string\n /** Sample of rejected lines; see blockedCount/blockedTruncated for the full set. */\n blocked: Array<{ line: number; text: string }>\n blockedCount: number\n blockedTruncated: boolean\n }\n}\n\nexport class ResultUnavailableError extends UnprocessableEntityError {\n declare readonly code: 'result_unavailable'\n declare readonly meta: {\n status: 'completed' | 'failed' | 'cancelled' | 'timed_out'\n }\n}\n\nexport type RateLimitErrorCode =\n | 'rate_limit_exceeded'\n | 'max_active_jobs_exceeded'\n\nexport class RateLimitError extends APIError<429, RateLimitErrorCode> {\n declare readonly meta:\n | { scope: 'developer' }\n | {\n reason: 'max_active_jobs_exceeded'\n maxActiveJobs: number\n activeJobs: number\n }\n | null\n}\n\nexport class MaxActiveJobsError extends RateLimitError {\n declare readonly code: 'max_active_jobs_exceeded'\n declare readonly meta: {\n reason: 'max_active_jobs_exceeded'\n /** Maximum number of jobs the account can have in flight. */\n maxActiveJobs: number\n /** Jobs in flight when this request was rejected. */\n activeJobs: number\n }\n}\n\n// ── 5xx ─────────────────────────────────────────────────────────────────────\n\nexport class InternalServerError extends APIError<number, string> {}\n\n/**\n * 503 carries four enumerated codes with distinct meta — a discriminated\n * union, narrowed via `.body`. `api_maintenance` gets no special retry\n * behavior: standard policy applies, and the typed\n * `meta.retryAfterSeconds` is surfaced so callers can schedule their own\n * resubmission.\n */\nexport type ServiceUnavailableBody =\n | {\n code: 'queue_unavailable'\n meta: { reason: 'queue_unavailable'; queueHealth: string }\n }\n | {\n code: 'queue_health_unknown'\n meta: { reason: 'queue_health_unknown'; laneId: string }\n }\n | {\n code: 'secret_store_unavailable'\n meta: { reason: 'secret_store_unavailable' }\n }\n | { code: 'api_maintenance'; meta: { retryAfterSeconds: number } }\n\nconst SERVICE_UNAVAILABLE_CODES = new Set([\n 'queue_unavailable',\n 'queue_health_unknown',\n 'secret_store_unavailable',\n 'api_maintenance'\n])\n\n// A 503 whose body isn't the enumerated envelope (e.g. load-balancer HTML)\n// falls back to InternalServerError so `.body` below never lies.\nfunction isServiceUnavailableBody(\n body: object | undefined\n): body is ServiceUnavailableBody {\n const code = (body as ErrorEnvelope | undefined)?.code\n return typeof code === 'string' && SERVICE_UNAVAILABLE_CODES.has(code)\n}\n\nexport class ServiceUnavailableError extends InternalServerError {\n declare readonly status: 503\n\n /** The discriminated 503 envelope: `if (e.body.code === 'api_maintenance') e.body.meta.retryAfterSeconds`. */\n get body(): ServiceUnavailableBody {\n return this.error as ServiceUnavailableBody\n }\n}\n\n// ── No HTTP response ────────────────────────────────────────────────────────\n\nexport class APIConnectionError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({\n message,\n cause\n }: { message?: string | undefined; cause?: Error | undefined } = {}) {\n super(undefined, undefined, message ?? 'Connection error.', undefined)\n if (cause) this.cause = cause\n }\n}\n\nexport class APIConnectionTimeoutError extends APIConnectionError {\n constructor({ message }: { message?: string } = {}) {\n super({ message: message ?? 'Request timed out.' })\n }\n}\n\nexport class APIUserAbortError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({ message }: { message?: string } = {}) {\n super(undefined, undefined, message ?? 'Request was aborted.', undefined)\n }\n}\n\n// ── Job-level errors (thrown only by createAndWait) ──────────────────────────\n\n/** Catch-all for a job that reached a terminal state other than `completed`. */\nexport abstract class JobUnsuccessfulError extends SimmitError {\n readonly job: Job\n\n constructor(job: Job, message?: string) {\n super(\n message ??\n `Job ${job.id} ${job.status}` +\n (job.statusReason ? `: ${job.statusReason}` : '') +\n (job.errorCode ? ` (${job.errorCode})` : '')\n )\n this.job = job\n }\n}\n\nexport class JobFailedError extends JobUnsuccessfulError {}\n\n/** Includes queue_timeout auto-cancellation, not just user cancels. */\nexport class JobCancelledError extends JobUnsuccessfulError {}\n\n/** The job hit its runtime ceiling server-side and is billed for what ran. */\nexport class JobTimedOutError extends JobUnsuccessfulError {}\n\n/**\n * The SDK gave up polling — the job itself is still running and billing.\n * Keep tracking via `jobs.get(jobId)` or stop the spend with `jobs.cancel(jobId)`.\n */\nexport class JobWaitTimeoutError extends SimmitError {\n readonly jobId: string\n readonly lastStatus: JobStatus\n\n constructor(args: {\n jobId: string\n lastStatus: JobStatus\n message?: string\n }) {\n super(\n args.message ??\n `Timed out waiting for job ${args.jobId} (last status: ${args.lastStatus}). ` +\n 'The job is still running server-side and continues to bill.'\n )\n this.jobId = args.jobId\n this.lastStatus = args.lastStatus\n }\n}\n\n// ── Webhook verification (thrown by unwrapWebhook) ───────────────────────────\n\nexport class WebhookVerificationError extends SimmitError {}\n","/**\n * A `Promise<T>` with raw-response access — the generic answer to response\n * headers the return types can't see (`X-Idempotent-Replay`, `X-Active-Jobs`,\n * `X-RateLimit-*`).\n *\n * const { data, response } = await client.jobs.create(params).withResponse()\n * response.headers.get('x-idempotent-replay')\n */\nexport class APIPromise<T> extends Promise<T> {\n // Chained promises (.then/.catch) must be plain Promises: this class's\n // constructor signature is incompatible with the executor the runtime\n // would otherwise pass via the species constructor.\n static override get [Symbol.species]() {\n return Promise\n }\n\n readonly #parsed: Promise<{ data: T; response: Response }>\n\n constructor(parsed: Promise<{ data: T; response: Response }>) {\n // The base promise is a pre-settled placeholder that is never observed:\n // then() below delegates to #parsed lazily (catch/finally route through\n // then() per spec). Subscribing eagerly here would reject this instance\n // even when the caller only consumes withResponse(), leaking an\n // unhandled rejection on failures.\n super((resolve) => resolve(undefined as never))\n this.#parsed = parsed\n }\n\n override then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.#parsed\n .then((result) => result.data)\n .then(onfulfilled, onrejected)\n }\n\n withResponse(): Promise<{ data: T; response: Response }> {\n return this.#parsed\n }\n\n asResponse(): Promise<Response> {\n return this.#parsed.then((result) => result.response)\n }\n}\n","// Abort-aware timing utilities shared by the request layer (backoff sleeps) and\n// the createAndWait poll loop. A pending sleep rejects with APIUserAbortError\n// the moment the caller's signal fires.\nimport { APIUserAbortError } from '../error'\n\nexport function throwIfUserAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new APIUserAbortError()\n}\n\nexport function sleep(\n ms: number,\n signal: AbortSignal | undefined\n): Promise<void> {\n return new Promise((resolve, reject) => {\n throwIfUserAborted(signal)\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new APIUserAbortError())\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n","// Internal request layer: header assembly, per-attempt timeout/abort\n// composition, retry with backoff + Retry-After, idempotency-key injection,\n// and error mapping. Not exported from the package.\nimport { APIPromise } from '../api-promise'\nimport {\n APIConnectionError,\n APIConnectionTimeoutError,\n APIError,\n APIUserAbortError\n} from '../error'\nimport type { RequestOptions } from '../client'\nimport { sleep, throwIfUserAborted } from './abort'\n\nexport interface ClientConfig {\n secretKey: string\n baseURL: string\n timeout: number\n maxRetries: number\n defaultHeaders: Record<string, string | null | undefined> | undefined\n fetch: typeof globalThis.fetch\n fetchOptions: RequestInit | undefined\n}\n\nexport interface RequestSpec {\n method: 'GET' | 'POST'\n path: string\n body?: unknown\n /** POST job creation: auto-generate an idempotency-key when none supplied. */\n idempotent?: boolean\n}\n\n// Retry policy constants — typed code config, not env.\nconst INITIAL_BACKOFF_MS = 500\nconst MAX_BACKOFF_MS = 8_000\nconst MAX_RETRY_AFTER_MS = 60_000\n\nexport function makeRequest<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions = {}\n): APIPromise<T> {\n return new APIPromise(run<T>(config, spec, options))\n}\n\nasync function run<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Promise<{ data: T; response: Response }> {\n const maxRetries = options.maxRetries ?? config.maxRetries\n const timeout = options.timeout ?? config.timeout\n const headers = buildHeaders(config, spec, options)\n const url = `${config.baseURL.replace(/\\/+$/, '')}${spec.path}`\n const body = spec.body === undefined ? undefined : JSON.stringify(spec.body)\n\n for (let attempt = 0; ; attempt++) {\n throwIfUserAborted(options.signal)\n\n let result: AttemptResult\n try {\n result = await fetchAttempt(config, spec, options, {\n url,\n headers,\n body,\n timeout\n })\n } catch (err) {\n if (err instanceof APIUserAbortError) throw err\n // Connection error, malformed success body, or per-attempt timeout —\n // all retryable.\n if (attempt < maxRetries) {\n await backoff(attempt, undefined, options.signal)\n continue\n }\n throw err\n }\n\n const { response, json } = result\n\n if (response.ok) {\n return { data: json as T, response }\n }\n\n if (shouldRetryStatus(response.status) && attempt < maxRetries) {\n await backoff(\n attempt,\n response.headers.get('retry-after'),\n options.signal\n )\n continue\n }\n\n throw APIError.generate(\n response.status,\n typeof json === 'object' && json !== null ? json : undefined,\n response.statusText,\n response.headers\n )\n }\n}\n\ninterface AttemptResult {\n response: Response\n /** Parsed JSON body; undefined when an error response carried a non-JSON body. */\n json: unknown\n}\n\nasync function fetchAttempt(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions,\n attempt: {\n url: string\n headers: Record<string, string>\n body: string | undefined\n timeout: number\n }\n): Promise<AttemptResult> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), attempt.timeout)\n const onUserAbort = () => controller.abort()\n options.signal?.addEventListener('abort', onUserAbort, { once: true })\n\n try {\n const response = await config.fetch(attempt.url, {\n ...config.fetchOptions,\n method: spec.method,\n headers: attempt.headers,\n ...(attempt.body !== undefined ? { body: attempt.body } : {}),\n signal: controller.signal\n })\n\n // The body is read inside the timed scope too — a stalled body must not\n // hang past the per-attempt timeout (fetch ties the body stream to the\n // controller's signal, so the abort cancels the read).\n let json: unknown\n if (response.ok) {\n // Success bodies must parse; a truncated/malformed one is treated as a\n // transport failure (classified below) and retried like one.\n json = await response.json()\n } else {\n try {\n json = await response.json()\n } catch (err) {\n // Aborted mid-read is a timeout/abort, not a non-JSON body.\n if (controller.signal.aborted) throw err\n json = undefined // e.g. a load-balancer HTML error page\n }\n }\n return { response, json }\n } catch (err) {\n if (options.signal?.aborted) throw new APIUserAbortError()\n if (controller.signal.aborted) {\n throw new APIConnectionTimeoutError()\n }\n throw new APIConnectionError({\n cause: err instanceof Error ? err : undefined\n })\n } finally {\n clearTimeout(timeoutId)\n options.signal?.removeEventListener('abort', onUserAbort)\n }\n}\n\nfunction buildHeaders(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Record<string, string> {\n const idempotent = spec.idempotent && spec.method === 'POST'\n const merged: Record<string, string | null | undefined> = {\n authorization: `Bearer ${config.secretKey}`,\n ...(spec.body !== undefined ? { 'content-type': 'application/json' } : {}),\n ...(idempotent && !options.idempotencyKey\n ? {\n // Generated once per call and reused across retry attempts — that\n // is what makes POST retries safe by default. The auto\n // key is an SDK built-in default (lowest tier), so defaultHeaders\n // may override it.\n 'idempotency-key': `simmit-node-retry-${crypto.randomUUID()}`\n }\n : {}),\n ...lowercaseKeys(config.defaultHeaders),\n ...(idempotent && options.idempotencyKey\n ? {\n // An explicit key is a per-request option: it must beat constructor\n // defaultHeaders. Raw options.headers still wins last.\n 'idempotency-key': options.idempotencyKey\n }\n : {}),\n ...lowercaseKeys(options.headers)\n }\n\n const headers: Record<string, string> = {}\n for (const [key, value] of Object.entries(merged)) {\n // A null value deletes the header; undefined entries are skipped.\n if (typeof value === 'string') headers[key] = value\n }\n return headers\n}\n\nfunction lowercaseKeys(\n record: Record<string, string | null | undefined> | undefined\n): Record<string, string | null | undefined> {\n if (!record) return {}\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [key.toLowerCase(), value])\n )\n}\n\nfunction shouldRetryStatus(status: number): boolean {\n // 408 kept defensively even though the API never emits it. 409 is never\n // retried: result_not_ready is thrown immediately by design and the other\n // 409s are deterministic.\n return status === 408 || status === 429 || status >= 500\n}\n\nasync function backoff(\n attempt: number,\n retryAfterHeader: string | null | undefined,\n signal: AbortSignal | undefined\n): Promise<void> {\n const retryAfterMs = parseRetryAfter(retryAfterHeader)\n const delay =\n retryAfterMs !== undefined\n ? retryAfterMs\n : Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS) *\n (1 - 0.25 * Math.random())\n await sleep(delay, signal)\n}\n\n/** Accepts `Retry-After` only when it parses to a delay in (0, 60s] — the SDK never sleeps arbitrarily long on a server hint. */\nfunction parseRetryAfter(\n header: string | null | undefined\n): number | undefined {\n if (!header) return undefined\n let ms: number\n if (/^\\d+$/.test(header.trim())) {\n ms = Number(header.trim()) * 1000\n } else {\n ms = new Date(header).getTime() - Date.now()\n }\n return Number.isFinite(ms) && ms > 0 && ms <= MAX_RETRY_AFTER_MS\n ? ms\n : undefined\n}\n","import type { APIPromise } from '../api-promise'\nimport type { ArtifactUrl } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `artifacts` resource. */\nexport class Artifacts {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Fetch a stable public download URL for an artifact, valid for the\n * artifact's full retention window — the same URL `jobs.getResult` returns,\n * fetched on demand (e.g. browser flows that control the final fetch). The\n * artifact is gone (410) once its retention window passes.\n */\n getUrl(\n artifactId: string,\n options?: RequestOptions\n ): APIPromise<ArtifactUrl> {\n return this.#client._request<ArtifactUrl>(\n {\n method: 'GET',\n path: `/v1/simc/artifacts/${encodeURIComponent(artifactId)}/url`\n },\n options\n )\n }\n}\n","import type { APIPromise } from '../api-promise'\nimport type { CreditBalance } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `credits` resource. */\nexport class Credits {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /** Fetch the account's current credit balance and per-grant breakdown. */\n get(options?: RequestOptions): APIPromise<CreditBalance> {\n return this.#client._request<CreditBalance>(\n { method: 'GET', path: '/v1/simc/credits' },\n options\n )\n }\n}\n","// Pure helpers for the createAndWait poll loop. Kept separate from the\n// orchestration so the cadence and deadline math are unit-testable.\nimport type { JobCreateResponse, JobStatus } from '../api-types'\n\nexport const MIN_POLL_INTERVAL_MS = 100\nexport const DEFAULT_POLL_INTERVAL_MS = 1_000\nexport const MAX_POLL_INTERVAL_MS = 10_000\nexport const POLL_BACKOFF_FACTOR = 1.5\nconst DEADLINE_GRACE_MS = 60_000\nconst FALLBACK_WAIT_TIMEOUT_MS = 45 * 60 * 1_000\n\nconst TERMINAL_STATUSES = new Set<JobStatus>([\n 'completed',\n 'failed',\n 'cancelled',\n 'timed_out'\n])\n\nexport function isTerminal(status: JobStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/**\n * Default wait deadline: the applied ceilings the create response reports —\n * `(queueSeconds + runtimeSeconds) × 1000` plus a 60s grace — or a 45-minute\n * fallback when either ceiling is null.\n */\nexport function deriveWaitTimeoutMs(created: JobCreateResponse): number {\n const { runtimeSeconds, queueSeconds } = created.runtime.ceiling\n if (runtimeSeconds != null && queueSeconds != null) {\n return (runtimeSeconds + queueSeconds) * 1_000 + DEADLINE_GRACE_MS\n }\n return FALLBACK_WAIT_TIMEOUT_MS\n}\n\n/** Next poll interval: grow ×1.5, capped at 10s. */\nexport function nextPollInterval(interval: number): number {\n return Math.min(interval * POLL_BACKOFF_FACTOR, MAX_POLL_INTERVAL_MS)\n}\n","import type { APIPromise } from '../api-promise'\nimport type {\n CompletedJob,\n Job,\n JobCancelResponse,\n JobCreateParams,\n JobCreateResponse,\n JobResult,\n JobStatus,\n JobStatusResponse\n} from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\nimport {\n JobCancelledError,\n JobFailedError,\n JobTimedOutError,\n JobWaitTimeoutError\n} from '../error'\nimport { sleep } from '../internal/abort'\nimport {\n DEFAULT_POLL_INTERVAL_MS,\n deriveWaitTimeoutMs,\n isTerminal,\n MIN_POLL_INTERVAL_MS,\n nextPollInterval\n} from '../internal/poll'\n\nexport interface JobWaitOptions extends RequestOptions {\n /** Initial delay between status polls, ms. Grows ×1.5 per poll to a 10s cap; values under 100 are raised to it. Default 1_000. */\n pollIntervalMs?: number\n /** Overall wait deadline, ms. Default derived from the job's applied ceilings. */\n waitTimeoutMs?: number\n /** Fired once with the raw create response (job id, ceilings, input warnings) before polling. */\n onCreated?: (response: JobCreateResponse) => void\n /** Fired after every successful status poll (progress, stage, queue estimate). */\n onPoll?: (status: JobStatusResponse) => void\n}\n\n/**\n * The `jobs` resource. Each single-request method is a thin wrapper over\n * `client._request` with the path/method/types pinned to the spec;\n * `createAndWait` orchestrates several of them.\n */\nexport class Jobs {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Submit a new SimC sim. Returns immediately with the job handle; the sim\n * runs asynchronously. `idempotent: true` makes the request layer attach an\n * auto-generated idempotency key so the POST is safe to retry; pass\n * `options.idempotencyKey` to supply your own.\n */\n create(\n params: JobCreateParams,\n options?: RequestOptions\n ): APIPromise<JobCreateResponse> {\n return this.#client._request<JobCreateResponse>(\n { method: 'POST', path: '/v1/simc/jobs', body: params, idempotent: true },\n options\n )\n }\n\n /** Fetch the full record for a job. */\n get(jobId: string, options?: RequestOptions): APIPromise<Job> {\n return this.#client._request<Job>(\n { method: 'GET', path: `/v1/simc/jobs/${encodeURIComponent(jobId)}` },\n options\n )\n }\n\n /**\n * Fetch the live status of a job in any state — `status`, `errorCode`,\n * `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a\n * non-terminal job, so it is the supported way to drive a custom poll loop.\n */\n getStatus(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobStatusResponse> {\n return this.#client._request<JobStatusResponse>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/status`\n },\n options\n )\n }\n\n /**\n * Fetch the result summary of a terminal job. Throws `ResultNotReadyError`\n * (409) while the job is still running — poll `/status` or use\n * `createAndWait` rather than `/result` for a job in flight.\n */\n getResult(jobId: string, options?: RequestOptions): APIPromise<JobResult> {\n return this.#client._request<JobResult>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/result`\n },\n options\n )\n }\n\n /**\n * Submit a job and resolve once it reaches a terminal state. Polls\n * `GET /v1/simc/jobs/{id}/status` (first after `pollIntervalMs`, then ×1.5 to\n * a 10s cap), then fetches the full record. Resolves with the `CompletedJob`\n * on success; throws `JobFailedError` / `JobCancelledError` /\n * `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`\n * if the deadline passes first — the job keeps running and is **not**\n * cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait\n * with `APIUserAbortError`, also without cancelling.\n */\n async createAndWait(\n params: JobCreateParams,\n options: JobWaitOptions = {}\n ): Promise<CompletedJob> {\n const {\n pollIntervalMs,\n waitTimeoutMs,\n onCreated,\n onPoll,\n ...requestOptions\n } = options\n\n const created = await this.create(params, requestOptions)\n onCreated?.(created)\n\n const deadline =\n Date.now() + (waitTimeoutMs ?? deriveWaitTimeoutMs(created))\n // nextPollInterval only ever grows the interval, so a non-positive seed\n // would hot-poll the status endpoint; floor it.\n let interval = Math.max(\n pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n MIN_POLL_INTERVAL_MS\n )\n let lastStatus: JobStatus = 'pending'\n\n for (;;) {\n const remaining = deadline - Date.now()\n if (remaining <= 0) {\n throw new JobWaitTimeoutError({ jobId: created.id, lastStatus })\n }\n // Never sleep past the deadline, so the wait gives up promptly.\n await sleep(Math.min(interval, remaining), requestOptions.signal)\n\n const status = await this.getStatus(created.id, requestOptions)\n onPoll?.(status)\n lastStatus = status.status\n\n if (isTerminal(status.status)) {\n // The status payload is lightweight; the full record carries the fields\n // CompletedJob and the job-error classes expose.\n const job = await this.get(created.id, requestOptions)\n switch (job.status) {\n case 'completed':\n return job as CompletedJob\n case 'failed':\n throw new JobFailedError(job)\n case 'cancelled':\n throw new JobCancelledError(job)\n case 'timed_out':\n throw new JobTimedOutError(job)\n }\n // Raced back to non-terminal between /status and the full record; keep polling.\n lastStatus = job.status\n }\n interval = nextPollInterval(interval)\n }\n }\n\n /**\n * Request cancellation. Returns `status: 'cancelled'` when the job ended\n * before it ran, or `status: 'cancel_requested'` when an in-flight job was\n * signaled to stop. Repeat calls are naturally idempotent, so no key is sent.\n */\n cancel(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobCancelResponse> {\n return this.#client._request<JobCancelResponse>(\n {\n method: 'POST',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/cancel`\n },\n options\n )\n }\n}\n","import { APIPromise } from './api-promise'\nimport { SimmitError } from './error'\nimport {\n makeRequest,\n type ClientConfig,\n type RequestSpec\n} from './internal/request'\nimport { Artifacts } from './resources/artifacts'\nimport { Credits } from './resources/credits'\nimport { Jobs } from './resources/jobs'\n\nexport interface ClientOptions {\n /** Defaults to process.env['SIMMIT_SECRET_KEY'] — exactly one env fallback. Construction\n * throws SimmitError('Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.').\n * \"Secret key\" is the credential noun end to end (dashboard → docs → env var → option →\n * error): it spends credits and must never ship client-side. */\n secretKey?: string | null\n /** Defaults to process.env['SIMMIT_BASE_URL'] ?? 'https://api.simmit.com'. */\n baseURL?: string | null\n /** Per-attempt timeout in ms. Default 60_000. (Retries can extend total wall time.) */\n timeout?: number\n /** Max retries after the first attempt for retryable failures. Default 2. */\n maxRetries?: number\n /** Headers sent with every request. Merged under per-request headers. */\n defaultHeaders?: Record<string, string | null | undefined>\n /** Custom fetch (testing, proxies). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch\n /** Extra RequestInit fields passed to every fetch call (e.g. undici dispatcher). */\n fetchOptions?: RequestInit\n}\n\nexport interface RequestOptions {\n /** Per-attempt timeout in ms. Overrides ClientOptions.timeout. */\n timeout?: number\n /** Abort the call (including retries and waiting). Throws APIUserAbortError. Never retried. */\n signal?: AbortSignal\n /** Overrides ClientOptions.maxRetries for this call. */\n maxRetries?: number\n /** Merged over defaultHeaders; a null value deletes the header. */\n headers?: Record<string, string | null | undefined>\n /** jobs.create / jobs.createAndWait only: replaces the auto-generated idempotency-key. */\n idempotencyKey?: string\n}\n\nexport default class Simmit {\n readonly jobs: Jobs\n readonly credits: Credits\n readonly artifacts: Artifacts\n\n readonly baseURL: string\n\n readonly #config: ClientConfig\n\n constructor(options: ClientOptions = {}) {\n const secretKey = options.secretKey ?? readEnv('SIMMIT_SECRET_KEY')\n if (!secretKey) {\n throw new SimmitError(\n 'Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.'\n )\n }\n\n this.baseURL =\n options.baseURL ?? readEnv('SIMMIT_BASE_URL') ?? 'https://api.simmit.com'\n\n this.#config = {\n secretKey,\n baseURL: this.baseURL,\n timeout: options.timeout ?? 60_000,\n maxRetries: options.maxRetries ?? 2,\n defaultHeaders: options.defaultHeaders,\n // Resolved lazily so a fetch patched onto globalThis after the client\n // is constructed (msw, APM instrumentation) is still honored.\n fetch: options.fetch ?? ((...args) => globalThis.fetch(...args)),\n fetchOptions: options.fetchOptions\n }\n\n this.jobs = new Jobs(this)\n this.credits = new Credits(this)\n this.artifacts = new Artifacts(this)\n }\n\n /** @internal Resource classes route through here; not public surface. */\n _request<T>(spec: RequestSpec, options?: RequestOptions): APIPromise<T> {\n return makeRequest(this.#config, spec, options)\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined') return undefined\n const value = process.env?.[name]?.trim()\n return value || undefined\n}\n","// Standalone webhook verification. Not a client method: receivers must not\n// need a secret-key-bearing client (whose constructor throws without a key).\n// WebCrypto only — zero deps, and runs in Workers as well as Node.\nimport type { JobStatus } from './api-types'\nimport { WebhookVerificationError } from './error'\n\nconst DEFAULT_TOLERANCE_SECONDS = 300\n\n/** The one hand-written wire type: the webhook payload has no OpenAPI schema. */\nexport interface WebhookEvent {\n kind: 'job.terminal'\n version: 'v1'\n timestamp: string\n payload: {\n id: string\n statusReason: string | null\n status: Extract<\n JobStatus,\n 'completed' | 'failed' | 'cancelled' | 'timed_out'\n >\n }\n}\n\n/**\n * Verifies an `X-Simmit-Signature` header — `t=<unix>,v1=<hex>`, an HMAC-SHA256\n * (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance — and\n * returns the parsed event. Throws `WebhookVerificationError` on a bad\n * signature, malformed header, or stale timestamp.\n *\n * Pass `rawBody` exactly as received: re-serializing changes the bytes and\n * breaks verification. `secret` is the webhook signing secret (dashboard →\n * Clients & Keys → Webhook), not your API key.\n */\nexport async function unwrapWebhook(\n rawBody: string,\n signatureHeader: string,\n secret: string,\n options?: { toleranceSeconds?: number }\n): Promise<WebhookEvent> {\n // An empty secret would otherwise surface as an opaque WebCrypto DataError;\n // a NaN tolerance would make the age check pass for everything.\n if (!secret) {\n throw new WebhookVerificationError('Webhook signing secret is empty.')\n }\n const tolerance = options?.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new WebhookVerificationError(\n 'toleranceSeconds must be a non-negative number.'\n )\n }\n\n const { timestampRaw, timestamp, signature } =\n parseSignatureHeader(signatureHeader)\n\n const expected = await hmacSha256Hex(secret, `${timestampRaw}.${rawBody}`)\n if (!timingSafeEqual(expected, signature)) {\n throw new WebhookVerificationError('Webhook signature does not match.')\n }\n\n // Compare on whole seconds, matching the header's unix-seconds `t`.\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > tolerance) {\n throw new WebhookVerificationError(\n 'Webhook timestamp is outside the tolerance window.'\n )\n }\n\n try {\n return JSON.parse(rawBody) as WebhookEvent\n } catch {\n throw new WebhookVerificationError('Webhook body is not valid JSON.')\n }\n}\n\nfunction parseSignatureHeader(header: string): {\n timestampRaw: string\n timestamp: number\n signature: string\n} {\n let timestampRaw: string | undefined\n let signature: string | undefined\n for (const part of header.split(',')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n const key = part.slice(0, eq).trim()\n const value = part.slice(eq + 1).trim()\n if (key === 't') timestampRaw = value\n else if (key === 'v1') signature = value\n }\n\n // `t` is unix whole seconds; reject anything but digits so the accepted\n // header matches the documented contract.\n if (!timestampRaw || !signature || !/^\\d+$/.test(timestampRaw)) {\n throw new WebhookVerificationError(\n 'Malformed signature header; expected \"t=<unix>,v1=<hex>\".'\n )\n }\n // The signed payload uses the timestamp exactly as sent, so keep the raw\n // string for signing and the parsed number only for the tolerance check.\n return { timestampRaw, timestamp: Number(timestampRaw), signature }\n}\n\nasync function hmacSha256Hex(secret: string, payload: string): Promise<string> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n return toHex(new Uint8Array(mac))\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let hex = ''\n for (const byte of bytes) hex += byte.toString(16).padStart(2, '0')\n return hex\n}\n\n// Constant-time comparison. The digest width is public, so a length mismatch\n// may short-circuit without leaking secret-dependent timing.\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let mismatch = 0\n for (let i = 0; i < a.length; i++) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i)\n }\n return mismatch === 0\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/error.ts","../src/api-promise.ts","../src/internal/abort.ts","../src/internal/request.ts","../src/resources/artifacts.ts","../src/resources/credits.ts","../src/internal/poll.ts","../src/resources/jobs.ts","../src/client.ts","../src/webhook.ts"],"sourcesContent":["import type { Job, JobStatus } from './api-types'\n\nexport class SimmitError extends Error {}\n\n/**\n * Value shapes the API's generic error `meta` bag can carry\n * (400/401/404/410/413 responses): JSON scalars, scalar arrays, or arrays of\n * flat objects.\n */\nexport type MetaValue =\n | string\n | number\n | boolean\n | null\n | Array<string | number | boolean>\n | Array<Record<string, string | number | boolean | null>>\n\nexport type GenericMeta = Record<string, MetaValue>\n\n/** The API's uniform error envelope: `{ error, code, meta }`. */\ninterface ErrorEnvelope {\n error?: unknown\n code?: unknown\n meta?: unknown\n}\n\nexport class APIError<\n TStatus extends number | undefined = number | undefined,\n TCode extends string | undefined = string | undefined,\n TMeta = GenericMeta | null\n> extends SimmitError {\n /** HTTP status of the response that caused the error. */\n readonly status: TStatus\n /** HTTP headers of the response that caused the error. */\n readonly headers: Headers | undefined\n /** Machine-readable `code` from the error envelope. */\n readonly code: TCode\n /** Typed `meta` from the error envelope. */\n readonly meta: TMeta\n /** Raw parsed JSON error body: escape hatch for unmapped fields. */\n readonly error: object | undefined\n\n constructor(\n status: TStatus,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ) {\n super(APIError.makeMessage(status, body, message))\n this.status = status\n this.headers = headers\n this.error = body\n const envelope = body as ErrorEnvelope | undefined\n this.code = (\n typeof envelope?.code === 'string' ? envelope.code : undefined\n ) as TCode\n this.meta = (body ? (envelope?.meta ?? null) : undefined) as TMeta\n }\n\n private static makeMessage(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined\n ): string {\n // The API's human-readable message field is named `error`, not `message`.\n const bodyMessage = (body as ErrorEnvelope | undefined)?.error\n const msg =\n typeof bodyMessage === 'string'\n ? bodyMessage\n : body\n ? JSON.stringify(body)\n : message\n\n if (status && msg) return `${status} ${msg}`\n if (status) return `${status} status code (no body)`\n if (msg) return msg\n return '(no status code or body)'\n }\n\n /**\n * Maps a response to the most specific error class: status selects the base\n * class; an enumerated `code` with structured `meta` selects the subclass;\n * anything unrecognized falls back to the status class so new server codes\n * degrade gracefully without breaking `instanceof` handling.\n */\n static generate(\n status: number | undefined,\n body: object | undefined,\n message: string | undefined,\n headers: Headers | undefined\n ): APIError<\n number | undefined,\n string | undefined,\n GenericMeta | null | undefined\n > {\n if (!status || !headers) {\n return new APIConnectionError({\n message,\n cause: body instanceof Error ? body : undefined\n })\n }\n\n const code = (body as ErrorEnvelope | undefined)?.code\n\n if (status === 400) return new BadRequestError(400, body, message, headers)\n if (status === 401) {\n return new AuthenticationError(401, body, message, headers)\n }\n if (status === 402) {\n if (code === 'insufficient_credits') {\n return new InsufficientCreditsError(402, body, message, headers)\n }\n if (code === 'insufficient_credits_liability') {\n return new InsufficientCreditsLiabilityError(\n 402,\n body,\n message,\n headers\n )\n }\n return new BillingError(402, body, message, headers)\n }\n if (status === 404) return new NotFoundError(404, body, message, headers)\n if (status === 409) {\n if (code === 'idempotency_key_reuse') {\n return new IdempotencyKeyReuseError(409, body, message, headers)\n }\n if (code === 'result_not_ready') {\n return new ResultNotReadyError(409, body, message, headers)\n }\n if (code === 'job_not_cancellable') {\n return new JobNotCancellableError(409, body, message, headers)\n }\n return new ConflictError(409, body, message, headers)\n }\n if (status === 413) {\n return new RequestTooLargeError(413, body, message, headers)\n }\n if (status === 422) {\n if (code === 'input_sanitized_rejected') {\n return new InvalidProfileError(422, body, message, headers)\n }\n if (code === 'result_unavailable') {\n return new ResultUnavailableError(422, body, message, headers)\n }\n return new UnprocessableEntityError(422, body, message, headers)\n }\n if (status === 429) {\n if (code === 'max_active_jobs_exceeded') {\n return new MaxActiveJobsError(429, body, message, headers)\n }\n return new RateLimitError(429, body, message, headers)\n }\n if (status === 503 && isServiceUnavailableBody(body)) {\n return new ServiceUnavailableError(503, body, message, headers)\n }\n if (status >= 500) {\n return new InternalServerError(status, body, message, headers)\n }\n return new APIError(status, body, message, headers)\n }\n}\n\n// ── 4xx status classes (code subclasses where the spec enumerates) ──────────\n\nexport class BadRequestError extends APIError<400, string> {}\n\nexport type AuthenticationErrorCode =\n | 'missing_token'\n | 'invalid_token'\n | 'revoked_token'\n | 'expired_token'\n\nexport class AuthenticationError extends APIError<\n 401,\n AuthenticationErrorCode\n> {}\n\n// 402 codes are docs-enumerated; the spec leaves `code` un-enumerated, so the\n// base class keeps `string` for forward compatibility.\nexport class BillingError extends APIError<402, string> {}\n\nexport type InsufficientCreditsMeta = {\n reason: string\n ceilingRuntimeSeconds?: number\n /** Largest maxRuntimeSeconds the current balance can cover. */\n maxAffordableRuntimeSeconds?: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsError extends BillingError {\n declare readonly code: 'insufficient_credits'\n declare readonly meta: InsufficientCreditsMeta | null\n}\n\nexport type InsufficientCreditsLiabilityMeta = {\n reason: string\n /** The high-priority fee in effect. Top up, or resubmit at priority 'standard'. */\n priorityFeeCredits: number\n docsUrl?: string\n}\n\nexport class InsufficientCreditsLiabilityError extends BillingError {\n declare readonly code: 'insufficient_credits_liability'\n declare readonly meta: InsufficientCreditsLiabilityMeta | null\n}\n\nexport class NotFoundError extends APIError<404, string> {}\n\nexport class ConflictError extends APIError<409, string> {}\n\nexport class IdempotencyKeyReuseError extends ConflictError {\n declare readonly code: 'idempotency_key_reuse'\n declare readonly meta: {\n reason: 'idempotency_key_reuse'\n /** ID of the job that originally consumed this idempotency key. */\n originalJobId: string\n docsUrl?: string\n }\n}\n\nexport class ResultNotReadyError extends ConflictError {\n declare readonly code: 'result_not_ready'\n declare readonly meta: {\n status: 'pending' | 'queued' | 'starting' | 'running'\n }\n}\n\nexport class JobNotCancellableError extends ConflictError {\n declare readonly code: 'job_not_cancellable'\n declare readonly meta: { id: string; status: JobStatus }\n}\n\nexport class RequestTooLargeError extends APIError<413, string> {}\n\nexport class UnprocessableEntityError extends APIError<422, string> {}\n\nexport class InvalidProfileError extends UnprocessableEntityError {\n declare readonly code: 'input_sanitized_rejected'\n declare readonly meta: {\n reason: 'input_sanitized_rejected'\n message: string\n docsUrl: string\n /** Sample of rejected lines; see blockedCount/blockedTruncated for the full set. */\n blocked: Array<{ line: number; text: string }>\n blockedCount: number\n blockedTruncated: boolean\n }\n}\n\nexport class ResultUnavailableError extends UnprocessableEntityError {\n declare readonly code: 'result_unavailable'\n declare readonly meta: {\n status: 'completed' | 'failed' | 'cancelled' | 'timed_out'\n }\n}\n\nexport type RateLimitErrorCode =\n | 'rate_limit_exceeded'\n | 'max_active_jobs_exceeded'\n\nexport class RateLimitError extends APIError<429, RateLimitErrorCode> {\n declare readonly meta:\n | { scope: 'developer' }\n | {\n reason: 'max_active_jobs_exceeded'\n maxActiveJobs: number\n activeJobs: number\n }\n | null\n}\n\nexport class MaxActiveJobsError extends RateLimitError {\n declare readonly code: 'max_active_jobs_exceeded'\n declare readonly meta: {\n reason: 'max_active_jobs_exceeded'\n /** Maximum number of jobs the account can have in flight. */\n maxActiveJobs: number\n /** Jobs in flight when this request was rejected. */\n activeJobs: number\n }\n}\n\n// ── 5xx ─────────────────────────────────────────────────────────────────────\n\nexport class InternalServerError extends APIError<number, string> {}\n\n/**\n * 503 carries four enumerated codes with distinct meta: a discriminated\n * union, narrowed via `.body`. `api_maintenance` gets no special retry\n * behavior: standard policy applies, and the typed\n * `meta.retryAfterSeconds` is surfaced so callers can schedule their own\n * resubmission.\n */\nexport type ServiceUnavailableBody =\n | {\n code: 'queue_unavailable'\n meta: { reason: 'queue_unavailable'; queueHealth: string }\n }\n | {\n code: 'queue_health_unknown'\n meta: { reason: 'queue_health_unknown'; laneId: string }\n }\n | {\n code: 'secret_store_unavailable'\n meta: { reason: 'secret_store_unavailable' }\n }\n | { code: 'api_maintenance'; meta: { retryAfterSeconds: number } }\n\nconst SERVICE_UNAVAILABLE_CODES = new Set([\n 'queue_unavailable',\n 'queue_health_unknown',\n 'secret_store_unavailable',\n 'api_maintenance'\n])\n\n// A 503 whose body isn't the enumerated envelope (e.g. load-balancer HTML)\n// falls back to InternalServerError so `.body` below never lies.\nfunction isServiceUnavailableBody(\n body: object | undefined\n): body is ServiceUnavailableBody {\n const code = (body as ErrorEnvelope | undefined)?.code\n return typeof code === 'string' && SERVICE_UNAVAILABLE_CODES.has(code)\n}\n\nexport class ServiceUnavailableError extends InternalServerError {\n declare readonly status: 503\n\n /** The discriminated 503 envelope: `if (e.body.code === 'api_maintenance') e.body.meta.retryAfterSeconds`. */\n get body(): ServiceUnavailableBody {\n return this.error as ServiceUnavailableBody\n }\n}\n\n// ── No HTTP response ────────────────────────────────────────────────────────\n\nexport class APIConnectionError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({\n message,\n cause\n }: { message?: string | undefined; cause?: Error | undefined } = {}) {\n super(undefined, undefined, message ?? 'Connection error.', undefined)\n if (cause) this.cause = cause\n }\n}\n\nexport class APIConnectionTimeoutError extends APIConnectionError {\n constructor({ message }: { message?: string } = {}) {\n super({ message: message ?? 'Request timed out.' })\n }\n}\n\nexport class APIUserAbortError extends APIError<\n undefined,\n undefined,\n undefined\n> {\n constructor({ message }: { message?: string } = {}) {\n super(undefined, undefined, message ?? 'Request was aborted.', undefined)\n }\n}\n\n// ── Job-level errors (thrown only by createAndWait) ──────────────────────────\n\n/** Catch-all for a job that reached a terminal state other than `completed`. */\nexport abstract class JobUnsuccessfulError extends SimmitError {\n readonly job: Job\n\n constructor(job: Job, message?: string) {\n super(\n message ??\n `Job ${job.id} ${job.status}` +\n (job.statusReason ? `: ${job.statusReason}` : '') +\n (job.errorCode ? ` (${job.errorCode})` : '')\n )\n this.job = job\n }\n}\n\nexport class JobFailedError extends JobUnsuccessfulError {}\n\n/** Includes queue_timeout auto-cancellation, not just user cancels. */\nexport class JobCancelledError extends JobUnsuccessfulError {}\n\n/** The job hit its runtime ceiling server-side and is billed for what ran. */\nexport class JobTimedOutError extends JobUnsuccessfulError {}\n\n/**\n * The SDK gave up polling. The job itself is still running and billing.\n * Keep tracking via `jobs.get(jobId)` or stop the spend with `jobs.cancel(jobId)`.\n */\nexport class JobWaitTimeoutError extends SimmitError {\n readonly jobId: string\n readonly lastStatus: JobStatus\n\n constructor(args: {\n jobId: string\n lastStatus: JobStatus\n message?: string\n }) {\n super(\n args.message ??\n `Timed out waiting for job ${args.jobId} (last status: ${args.lastStatus}). ` +\n 'The job is still running server-side and continues to bill.'\n )\n this.jobId = args.jobId\n this.lastStatus = args.lastStatus\n }\n}\n\n// ── Webhook verification (thrown by unwrapWebhook) ───────────────────────────\n\nexport class WebhookVerificationError extends SimmitError {}\n","/**\n * A `Promise<T>` with raw-response access: the generic answer to response\n * headers the return types can't see (`X-Idempotent-Replay`, `X-Active-Jobs`,\n * `X-RateLimit-*`).\n *\n * const { data, response } = await client.jobs.create(params).withResponse()\n * response.headers.get('x-idempotent-replay')\n */\nexport class APIPromise<T> extends Promise<T> {\n // Chained promises (.then/.catch) must be plain Promises: this class's\n // constructor signature is incompatible with the executor the runtime\n // would otherwise pass via the species constructor.\n static override get [Symbol.species]() {\n return Promise\n }\n\n readonly #parsed: Promise<{ data: T; response: Response }>\n\n constructor(parsed: Promise<{ data: T; response: Response }>) {\n // The base promise is a pre-settled placeholder that is never observed:\n // then() below delegates to #parsed lazily (catch/finally route through\n // then() per spec). Subscribing eagerly here would reject this instance\n // even when the caller only consumes withResponse(), leaking an\n // unhandled rejection on failures.\n super((resolve) => resolve(undefined as never))\n this.#parsed = parsed\n }\n\n override then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.#parsed\n .then((result) => result.data)\n .then(onfulfilled, onrejected)\n }\n\n withResponse(): Promise<{ data: T; response: Response }> {\n return this.#parsed\n }\n\n asResponse(): Promise<Response> {\n return this.#parsed.then((result) => result.response)\n }\n}\n","// Abort-aware timing utilities shared by the request layer (backoff sleeps) and\n// the createAndWait poll loop. A pending sleep rejects with APIUserAbortError\n// the moment the caller's signal fires.\nimport { APIUserAbortError } from '../error'\n\nexport function throwIfUserAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new APIUserAbortError()\n}\n\nexport function sleep(\n ms: number,\n signal: AbortSignal | undefined\n): Promise<void> {\n return new Promise((resolve, reject) => {\n throwIfUserAborted(signal)\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new APIUserAbortError())\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n","// Internal request layer: header assembly, per-attempt timeout/abort\n// composition, retry with backoff + Retry-After, idempotency-key injection,\n// and error mapping. Not exported from the package.\nimport { APIPromise } from '../api-promise'\nimport {\n APIConnectionError,\n APIConnectionTimeoutError,\n APIError,\n APIUserAbortError\n} from '../error'\nimport type { RequestOptions } from '../client'\nimport { sleep, throwIfUserAborted } from './abort'\n\nexport interface ClientConfig {\n secretKey: string\n baseURL: string\n timeout: number\n maxRetries: number\n defaultHeaders: Record<string, string | null | undefined> | undefined\n fetch: typeof globalThis.fetch\n fetchOptions: RequestInit | undefined\n}\n\nexport interface RequestSpec {\n method: 'GET' | 'POST'\n path: string\n body?: unknown\n /** POST job creation: auto-generate an idempotency-key when none supplied. */\n idempotent?: boolean\n}\n\n// Retry policy constants: typed code config, not env.\nconst INITIAL_BACKOFF_MS = 500\nconst MAX_BACKOFF_MS = 8_000\nconst MAX_RETRY_AFTER_MS = 60_000\n\nexport function makeRequest<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions = {}\n): APIPromise<T> {\n return new APIPromise(run<T>(config, spec, options))\n}\n\nasync function run<T>(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Promise<{ data: T; response: Response }> {\n const maxRetries = options.maxRetries ?? config.maxRetries\n const timeout = options.timeout ?? config.timeout\n const headers = buildHeaders(config, spec, options)\n const url = `${config.baseURL.replace(/\\/+$/, '')}${spec.path}`\n const body = spec.body === undefined ? undefined : JSON.stringify(spec.body)\n\n for (let attempt = 0; ; attempt++) {\n throwIfUserAborted(options.signal)\n\n let result: AttemptResult\n try {\n result = await fetchAttempt(config, spec, options, {\n url,\n headers,\n body,\n timeout\n })\n } catch (err) {\n if (err instanceof APIUserAbortError) throw err\n // Connection error, malformed success body, or per-attempt timeout.\n // All retryable.\n if (attempt < maxRetries) {\n await backoff(attempt, undefined, options.signal)\n continue\n }\n throw err\n }\n\n const { response, json } = result\n\n if (response.ok) {\n return { data: json as T, response }\n }\n\n if (shouldRetryStatus(response.status) && attempt < maxRetries) {\n await backoff(\n attempt,\n response.headers.get('retry-after'),\n options.signal\n )\n continue\n }\n\n throw APIError.generate(\n response.status,\n typeof json === 'object' && json !== null ? json : undefined,\n response.statusText,\n response.headers\n )\n }\n}\n\ninterface AttemptResult {\n response: Response\n /** Parsed JSON body; undefined when an error response carried a non-JSON body. */\n json: unknown\n}\n\nasync function fetchAttempt(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions,\n attempt: {\n url: string\n headers: Record<string, string>\n body: string | undefined\n timeout: number\n }\n): Promise<AttemptResult> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), attempt.timeout)\n const onUserAbort = () => controller.abort()\n options.signal?.addEventListener('abort', onUserAbort, { once: true })\n\n try {\n const response = await config.fetch(attempt.url, {\n ...config.fetchOptions,\n method: spec.method,\n headers: attempt.headers,\n ...(attempt.body !== undefined ? { body: attempt.body } : {}),\n signal: controller.signal\n })\n\n // The body is read inside the timed scope too: a stalled body must not\n // hang past the per-attempt timeout (fetch ties the body stream to the\n // controller's signal, so the abort cancels the read).\n let json: unknown\n if (response.ok) {\n // Success bodies must parse; a truncated/malformed one is treated as a\n // transport failure (classified below) and retried like one.\n json = await response.json()\n } else {\n try {\n json = await response.json()\n } catch (err) {\n // Aborted mid-read is a timeout/abort, not a non-JSON body.\n if (controller.signal.aborted) throw err\n json = undefined // e.g. a load-balancer HTML error page\n }\n }\n return { response, json }\n } catch (err) {\n if (options.signal?.aborted) throw new APIUserAbortError()\n if (controller.signal.aborted) {\n throw new APIConnectionTimeoutError()\n }\n throw new APIConnectionError({\n cause: err instanceof Error ? err : undefined\n })\n } finally {\n clearTimeout(timeoutId)\n options.signal?.removeEventListener('abort', onUserAbort)\n }\n}\n\nfunction buildHeaders(\n config: ClientConfig,\n spec: RequestSpec,\n options: RequestOptions\n): Record<string, string> {\n const idempotent = spec.idempotent && spec.method === 'POST'\n const merged: Record<string, string | null | undefined> = {\n authorization: `Bearer ${config.secretKey}`,\n ...(spec.body !== undefined ? { 'content-type': 'application/json' } : {}),\n ...(idempotent && !options.idempotencyKey\n ? {\n // Generated once per call and reused across retry attempts. That\n // is what makes POST retries safe by default. The auto\n // key is an SDK built-in default (lowest tier), so defaultHeaders\n // may override it.\n 'idempotency-key': `simmit-node-retry-${crypto.randomUUID()}`\n }\n : {}),\n ...lowercaseKeys(config.defaultHeaders),\n ...(idempotent && options.idempotencyKey\n ? {\n // An explicit key is a per-request option: it must beat constructor\n // defaultHeaders. Raw options.headers still wins last.\n 'idempotency-key': options.idempotencyKey\n }\n : {}),\n ...lowercaseKeys(options.headers)\n }\n\n const headers: Record<string, string> = {}\n for (const [key, value] of Object.entries(merged)) {\n // A null value deletes the header; undefined entries are skipped.\n if (typeof value === 'string') headers[key] = value\n }\n return headers\n}\n\nfunction lowercaseKeys(\n record: Record<string, string | null | undefined> | undefined\n): Record<string, string | null | undefined> {\n if (!record) return {}\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [key.toLowerCase(), value])\n )\n}\n\nfunction shouldRetryStatus(status: number): boolean {\n // 408 kept defensively even though the API never emits it. 409 is never\n // retried: result_not_ready is thrown immediately by design and the other\n // 409s are deterministic.\n return status === 408 || status === 429 || status >= 500\n}\n\nasync function backoff(\n attempt: number,\n retryAfterHeader: string | null | undefined,\n signal: AbortSignal | undefined\n): Promise<void> {\n const retryAfterMs = parseRetryAfter(retryAfterHeader)\n const delay =\n retryAfterMs !== undefined\n ? retryAfterMs\n : Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS) *\n (1 - 0.25 * Math.random())\n await sleep(delay, signal)\n}\n\n/** Accepts `Retry-After` only when it parses to a delay in (0, 60s]: the SDK never sleeps arbitrarily long on a server hint. */\nfunction parseRetryAfter(\n header: string | null | undefined\n): number | undefined {\n if (!header) return undefined\n let ms: number\n if (/^\\d+$/.test(header.trim())) {\n ms = Number(header.trim()) * 1000\n } else {\n ms = new Date(header).getTime() - Date.now()\n }\n return Number.isFinite(ms) && ms > 0 && ms <= MAX_RETRY_AFTER_MS\n ? ms\n : undefined\n}\n","import type { APIPromise } from '../api-promise'\nimport type { ArtifactUrl } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `artifacts` resource. */\nexport class Artifacts {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Fetch a stable public download URL for an artifact, valid for the\n * artifact's full retention window: the same URL `jobs.getResult` returns,\n * fetched on demand (e.g. browser flows that control the final fetch). The\n * artifact is gone (410) once its retention window passes.\n */\n getUrl(\n artifactId: string,\n options?: RequestOptions\n ): APIPromise<ArtifactUrl> {\n return this.#client._request<ArtifactUrl>(\n {\n method: 'GET',\n path: `/v1/simc/artifacts/${encodeURIComponent(artifactId)}/url`\n },\n options\n )\n }\n}\n","import type { APIPromise } from '../api-promise'\nimport type { CreditBalance } from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\n\n/** The `credits` resource. */\nexport class Credits {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /** Fetch the account's current credit balance and per-grant breakdown. */\n get(options?: RequestOptions): APIPromise<CreditBalance> {\n return this.#client._request<CreditBalance>(\n { method: 'GET', path: '/v1/simc/credits' },\n options\n )\n }\n}\n","// Pure helpers for the createAndWait poll loop. Kept separate from the\n// orchestration so the cadence and deadline math are unit-testable.\nimport type { JobCreateResponse, JobStatus } from '../api-types'\n\nexport const MIN_POLL_INTERVAL_MS = 100\nexport const DEFAULT_POLL_INTERVAL_MS = 1_000\nexport const MAX_POLL_INTERVAL_MS = 10_000\nexport const POLL_BACKOFF_FACTOR = 1.5\nconst DEADLINE_GRACE_MS = 60_000\nconst FALLBACK_WAIT_TIMEOUT_MS = 45 * 60 * 1_000\n\nconst TERMINAL_STATUSES = new Set<JobStatus>([\n 'completed',\n 'failed',\n 'cancelled',\n 'timed_out'\n])\n\nexport function isTerminal(status: JobStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/**\n * Default wait deadline derived from the applied ceilings the create response\n * reports: `(queueSeconds + runtimeSeconds) × 1000` plus a 60s grace, falling\n * back to 45 minutes when either ceiling is null.\n */\nexport function deriveWaitTimeoutMs(created: JobCreateResponse): number {\n const { runtimeSeconds, queueSeconds } = created.runtime.ceiling\n if (runtimeSeconds != null && queueSeconds != null) {\n return (runtimeSeconds + queueSeconds) * 1_000 + DEADLINE_GRACE_MS\n }\n return FALLBACK_WAIT_TIMEOUT_MS\n}\n\n/** Next poll interval: grow ×1.5, capped at 10s. */\nexport function nextPollInterval(interval: number): number {\n return Math.min(interval * POLL_BACKOFF_FACTOR, MAX_POLL_INTERVAL_MS)\n}\n","import type { APIPromise } from '../api-promise'\nimport type {\n CompletedJob,\n Job,\n JobCancelResponse,\n JobCreateParams,\n JobCreateResponse,\n JobResult,\n JobStatus,\n JobStatusResponse\n} from '../api-types'\nimport type Simmit from '../client'\nimport type { RequestOptions } from '../client'\nimport {\n JobCancelledError,\n JobFailedError,\n JobTimedOutError,\n JobWaitTimeoutError\n} from '../error'\nimport { sleep } from '../internal/abort'\nimport {\n DEFAULT_POLL_INTERVAL_MS,\n deriveWaitTimeoutMs,\n isTerminal,\n MIN_POLL_INTERVAL_MS,\n nextPollInterval\n} from '../internal/poll'\n\nexport interface JobWaitOptions extends RequestOptions {\n /** Initial delay between status polls, ms. Grows ×1.5 per poll to a 10s cap; values under 100 are raised to it. Default 1_000. */\n pollIntervalMs?: number\n /** Overall wait deadline, ms. Default derived from the job's applied ceilings. */\n waitTimeoutMs?: number\n /** Fired once with the raw create response (job id, ceilings, input warnings) before polling. */\n onCreated?: (response: JobCreateResponse) => void\n /** Fired after every successful status poll (progress, stage, queue estimate). */\n onPoll?: (status: JobStatusResponse) => void\n}\n\n/**\n * The `jobs` resource. Each single-request method is a thin wrapper over\n * `client._request` with the path/method/types pinned to the spec;\n * `createAndWait` orchestrates several of them.\n */\nexport class Jobs {\n readonly #client: Simmit\n\n constructor(client: Simmit) {\n this.#client = client\n }\n\n /**\n * Submit a new SimC sim. Returns immediately with the job handle; the sim\n * runs asynchronously. `idempotent: true` makes the request layer attach an\n * auto-generated idempotency key so the POST is safe to retry; pass\n * `options.idempotencyKey` to supply your own.\n */\n create(\n params: JobCreateParams,\n options?: RequestOptions\n ): APIPromise<JobCreateResponse> {\n return this.#client._request<JobCreateResponse>(\n { method: 'POST', path: '/v1/simc/jobs', body: params, idempotent: true },\n options\n )\n }\n\n /** Fetch the full record for a job. */\n get(jobId: string, options?: RequestOptions): APIPromise<Job> {\n return this.#client._request<Job>(\n { method: 'GET', path: `/v1/simc/jobs/${encodeURIComponent(jobId)}` },\n options\n )\n }\n\n /**\n * Fetch the live status of a job in any state: `status`, `errorCode`,\n * `progress`, and `queue` estimate. Unlike `getResult`, it never throws for a\n * non-terminal job, so it is the supported way to drive a custom poll loop.\n */\n getStatus(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobStatusResponse> {\n return this.#client._request<JobStatusResponse>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/status`\n },\n options\n )\n }\n\n /**\n * Fetch the result summary of a terminal job. Throws `ResultNotReadyError`\n * (409) while the job is still running. Poll `/status` or use\n * `createAndWait` rather than `/result` for a job in flight.\n */\n getResult(jobId: string, options?: RequestOptions): APIPromise<JobResult> {\n return this.#client._request<JobResult>(\n {\n method: 'GET',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/result`\n },\n options\n )\n }\n\n /**\n * Submit a job and resolve once it reaches a terminal state. Polls\n * `GET /v1/simc/jobs/{id}/status` (first after `pollIntervalMs`, then ×1.5 to\n * a 10s cap), then fetches the full record. Resolves with the `CompletedJob`\n * on success; throws `JobFailedError` / `JobCancelledError` /\n * `JobTimedOutError` for the other terminal states, or `JobWaitTimeoutError`\n * if the deadline passes first. The job keeps running and is **not**\n * cancelled (call `cancel(jobId)` to stop the spend). `signal` aborts the wait\n * with `APIUserAbortError`, also without cancelling.\n */\n async createAndWait(\n params: JobCreateParams,\n options: JobWaitOptions = {}\n ): Promise<CompletedJob> {\n const {\n pollIntervalMs,\n waitTimeoutMs,\n onCreated,\n onPoll,\n ...requestOptions\n } = options\n\n const created = await this.create(params, requestOptions)\n onCreated?.(created)\n\n const deadline =\n Date.now() + (waitTimeoutMs ?? deriveWaitTimeoutMs(created))\n // nextPollInterval only ever grows the interval, so a non-positive seed\n // would hot-poll the status endpoint; floor it.\n let interval = Math.max(\n pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n MIN_POLL_INTERVAL_MS\n )\n let lastStatus: JobStatus = 'pending'\n\n for (;;) {\n const remaining = deadline - Date.now()\n if (remaining <= 0) {\n throw new JobWaitTimeoutError({ jobId: created.id, lastStatus })\n }\n // Never sleep past the deadline, so the wait gives up promptly.\n await sleep(Math.min(interval, remaining), requestOptions.signal)\n\n const status = await this.getStatus(created.id, requestOptions)\n onPoll?.(status)\n lastStatus = status.status\n\n if (isTerminal(status.status)) {\n // The status payload is lightweight; the full record carries the fields\n // CompletedJob and the job-error classes expose.\n const job = await this.get(created.id, requestOptions)\n switch (job.status) {\n case 'completed':\n return job as CompletedJob\n case 'failed':\n throw new JobFailedError(job)\n case 'cancelled':\n throw new JobCancelledError(job)\n case 'timed_out':\n throw new JobTimedOutError(job)\n }\n // Raced back to non-terminal between /status and the full record; keep polling.\n lastStatus = job.status\n }\n interval = nextPollInterval(interval)\n }\n }\n\n /**\n * Request cancellation. Returns `status: 'cancelled'` when the job ended\n * before it ran, or `status: 'cancel_requested'` when an in-flight job was\n * signaled to stop. Repeat calls are naturally idempotent, so no key is sent.\n */\n cancel(\n jobId: string,\n options?: RequestOptions\n ): APIPromise<JobCancelResponse> {\n return this.#client._request<JobCancelResponse>(\n {\n method: 'POST',\n path: `/v1/simc/jobs/${encodeURIComponent(jobId)}/cancel`\n },\n options\n )\n }\n}\n","import { APIPromise } from './api-promise'\nimport { SimmitError } from './error'\nimport {\n makeRequest,\n type ClientConfig,\n type RequestSpec\n} from './internal/request'\nimport { Artifacts } from './resources/artifacts'\nimport { Credits } from './resources/credits'\nimport { Jobs } from './resources/jobs'\n\nexport interface ClientOptions {\n /** Defaults to process.env['SIMMIT_SECRET_KEY'], exactly one env fallback. Construction\n * throws SimmitError('Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.').\n * \"Secret key\" is the credential noun end to end (dashboard → docs → env var → option →\n * error): it spends credits and must never ship client-side. */\n secretKey?: string | null\n /** Defaults to process.env['SIMMIT_BASE_URL'] ?? 'https://api.simmit.com'. */\n baseURL?: string | null\n /** Per-attempt timeout in ms. Default 60_000. (Retries can extend total wall time.) */\n timeout?: number\n /** Max retries after the first attempt for retryable failures. Default 2. */\n maxRetries?: number\n /** Headers sent with every request. Merged under per-request headers. */\n defaultHeaders?: Record<string, string | null | undefined>\n /** Custom fetch (testing, proxies). Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch\n /** Extra RequestInit fields passed to every fetch call (e.g. undici dispatcher). */\n fetchOptions?: RequestInit\n}\n\nexport interface RequestOptions {\n /** Per-attempt timeout in ms. Overrides ClientOptions.timeout. */\n timeout?: number\n /** Abort the call (including retries and waiting). Throws APIUserAbortError. Never retried. */\n signal?: AbortSignal\n /** Overrides ClientOptions.maxRetries for this call. */\n maxRetries?: number\n /** Merged over defaultHeaders; a null value deletes the header. */\n headers?: Record<string, string | null | undefined>\n /** jobs.create / jobs.createAndWait only: replaces the auto-generated idempotency-key. */\n idempotencyKey?: string\n}\n\nexport default class Simmit {\n readonly jobs: Jobs\n readonly credits: Credits\n readonly artifacts: Artifacts\n\n readonly baseURL: string\n\n readonly #config: ClientConfig\n\n constructor(options: ClientOptions = {}) {\n const secretKey = options.secretKey ?? readEnv('SIMMIT_SECRET_KEY')\n if (!secretKey) {\n throw new SimmitError(\n 'Missing secret key. Pass secretKey or set SIMMIT_SECRET_KEY.'\n )\n }\n\n this.baseURL =\n options.baseURL ?? readEnv('SIMMIT_BASE_URL') ?? 'https://api.simmit.com'\n\n this.#config = {\n secretKey,\n baseURL: this.baseURL,\n timeout: options.timeout ?? 60_000,\n maxRetries: options.maxRetries ?? 2,\n defaultHeaders: options.defaultHeaders,\n // Resolved lazily so a fetch patched onto globalThis after the client\n // is constructed (msw, APM instrumentation) is still honored.\n fetch: options.fetch ?? ((...args) => globalThis.fetch(...args)),\n fetchOptions: options.fetchOptions\n }\n\n this.jobs = new Jobs(this)\n this.credits = new Credits(this)\n this.artifacts = new Artifacts(this)\n }\n\n /** @internal Resource classes route through here; not public surface. */\n _request<T>(spec: RequestSpec, options?: RequestOptions): APIPromise<T> {\n return makeRequest(this.#config, spec, options)\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined') return undefined\n const value = process.env?.[name]?.trim()\n return value || undefined\n}\n","// Standalone webhook verification. Not a client method: receivers must not\n// need a secret-key-bearing client (whose constructor throws without a key).\n// WebCrypto only, zero deps, and runs in Workers as well as Node.\nimport type { JobStatus } from './api-types'\nimport { WebhookVerificationError } from './error'\n\nconst DEFAULT_TOLERANCE_SECONDS = 300\n\n/** The one hand-written wire type: the webhook payload has no OpenAPI schema. */\nexport interface WebhookEvent {\n kind: 'job.terminal'\n version: 'v1'\n timestamp: string\n payload: {\n id: string\n statusReason: string | null\n status: Extract<\n JobStatus,\n 'completed' | 'failed' | 'cancelled' | 'timed_out'\n >\n }\n}\n\n/**\n * Verifies an `X-Simmit-Signature` header (`t=<unix>,v1=<hex>`, an HMAC-SHA256\n * (timing-safe) over `${t}.${rawBody}` within a 300s default tolerance) and\n * returns the parsed event. Throws `WebhookVerificationError` on a bad\n * signature, malformed header, or stale timestamp.\n *\n * Pass `rawBody` exactly as received: re-serializing changes the bytes and\n * breaks verification. `secret` is the webhook signing secret (dashboard →\n * Clients & Keys → Webhook), not your API key.\n */\nexport async function unwrapWebhook(\n rawBody: string,\n signatureHeader: string,\n secret: string,\n options?: { toleranceSeconds?: number }\n): Promise<WebhookEvent> {\n // An empty secret would otherwise surface as an opaque WebCrypto DataError;\n // a NaN tolerance would make the age check pass for everything.\n if (!secret) {\n throw new WebhookVerificationError('Webhook signing secret is empty.')\n }\n const tolerance = options?.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new WebhookVerificationError(\n 'toleranceSeconds must be a non-negative number.'\n )\n }\n\n const { timestampRaw, timestamp, signature } =\n parseSignatureHeader(signatureHeader)\n\n const expected = await hmacSha256Hex(secret, `${timestampRaw}.${rawBody}`)\n if (!timingSafeEqual(expected, signature)) {\n throw new WebhookVerificationError('Webhook signature does not match.')\n }\n\n // Compare on whole seconds, matching the header's unix-seconds `t`.\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > tolerance) {\n throw new WebhookVerificationError(\n 'Webhook timestamp is outside the tolerance window.'\n )\n }\n\n try {\n return JSON.parse(rawBody) as WebhookEvent\n } catch {\n throw new WebhookVerificationError('Webhook body is not valid JSON.')\n }\n}\n\nfunction parseSignatureHeader(header: string): {\n timestampRaw: string\n timestamp: number\n signature: string\n} {\n let timestampRaw: string | undefined\n let signature: string | undefined\n for (const part of header.split(',')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n const key = part.slice(0, eq).trim()\n const value = part.slice(eq + 1).trim()\n if (key === 't') timestampRaw = value\n else if (key === 'v1') signature = value\n }\n\n // `t` is unix whole seconds; reject anything but digits so the accepted\n // header matches the documented contract.\n if (!timestampRaw || !signature || !/^\\d+$/.test(timestampRaw)) {\n throw new WebhookVerificationError(\n 'Malformed signature header; expected \"t=<unix>,v1=<hex>\".'\n )\n }\n // The signed payload uses the timestamp exactly as sent, so keep the raw\n // string for signing and the parsed number only for the tolerance check.\n return { timestampRaw, timestamp: Number(timestampRaw), signature }\n}\n\nasync function hmacSha256Hex(secret: string, payload: string): Promise<string> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n return toHex(new Uint8Array(mac))\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let hex = ''\n for (const byte of bytes) hex += byte.toString(16).padStart(2, '0')\n return hex\n}\n\n// Constant-time comparison. The digest width is public, so a length mismatch\n// may short-circuit without leaking secret-dependent timing.\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let mismatch = 0\n for (let i = 0; i < a.length; i++) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i)\n }\n return mismatch === 0\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simmit/sdk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "TypeScript SDK for
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "TypeScript SDK for Simmit, an API for running SimulationCraft (SimC) in the cloud",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
@@ -52,8 +52,12 @@
|
|
|
52
52
|
"simulationcraft",
|
|
53
53
|
"simc",
|
|
54
54
|
"wow",
|
|
55
|
+
"world-of-warcraft",
|
|
56
|
+
"dps",
|
|
57
|
+
"simulation",
|
|
55
58
|
"sdk",
|
|
56
|
-
"api-client"
|
|
59
|
+
"api-client",
|
|
60
|
+
"cloud"
|
|
57
61
|
],
|
|
58
62
|
"author": "Voidly Labs",
|
|
59
63
|
"repository": {
|