@spicyapi/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/SECURITY.md +4 -0
- package/contracts/openapi.yaml +752 -0
- package/dist/src/docs/index.d.ts +10 -0
- package/dist/src/docs/index.d.ts.map +1 -0
- package/dist/src/docs/index.js +181 -0
- package/dist/src/docs/index.js.map +1 -0
- package/dist/src/generated/openapi.d.ts +888 -0
- package/dist/src/generated/openapi.d.ts.map +1 -0
- package/dist/src/generated/openapi.js +6 -0
- package/dist/src/generated/openapi.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/openapi.d.ts +2 -0
- package/dist/src/openapi.d.ts.map +1 -0
- package/dist/src/openapi.js +18 -0
- package/dist/src/openapi.js.map +1 -0
- package/dist/src/sdk/client.d.ts +41 -0
- package/dist/src/sdk/client.d.ts.map +1 -0
- package/dist/src/sdk/client.js +407 -0
- package/dist/src/sdk/client.js.map +1 -0
- package/dist/src/sdk/errors.d.ts +36 -0
- package/dist/src/sdk/errors.d.ts.map +1 -0
- package/dist/src/sdk/errors.js +53 -0
- package/dist/src/sdk/errors.js.map +1 -0
- package/dist/src/sdk/index.d.ts +4 -0
- package/dist/src/sdk/index.d.ts.map +1 -0
- package/dist/src/sdk/index.js +3 -0
- package/dist/src/sdk/index.js.map +1 -0
- package/dist/src/sdk/types.d.ts +73 -0
- package/dist/src/sdk/types.d.ts.map +1 -0
- package/dist/src/sdk/types.js +2 -0
- package/dist/src/sdk/types.js.map +1 -0
- package/dist/src/webhooks/index.d.ts +28 -0
- package/dist/src/webhooks/index.d.ts.map +1 -0
- package/dist/src/webhooks/index.js +83 -0
- package/dist/src/webhooks/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/sdk/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEpC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CAU3D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,qBAAa,iBAAkB,SAAQ,mBAAmB;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM;CAK9B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;gBAE9B,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,UAAU;CAOrE;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAK5C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class SpicyApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
requestId;
|
|
5
|
+
retryAfterSeconds;
|
|
6
|
+
rateLimitLimit;
|
|
7
|
+
rateLimitRemaining;
|
|
8
|
+
constructor(message, options) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "SpicyApiError";
|
|
11
|
+
this.status = options.status;
|
|
12
|
+
this.code = options.code;
|
|
13
|
+
this.requestId = options.requestId;
|
|
14
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
15
|
+
this.rateLimitLimit = options.rateLimitLimit;
|
|
16
|
+
this.rateLimitRemaining = options.rateLimitRemaining;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class SpicyTransportError extends Error {
|
|
20
|
+
constructor(message, options) {
|
|
21
|
+
super(message, options);
|
|
22
|
+
this.name = "SpicyTransportError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class SpicyTimeoutError extends SpicyTransportError {
|
|
26
|
+
timeoutMs;
|
|
27
|
+
constructor(timeoutMs) {
|
|
28
|
+
super(`request exceeded the local ${timeoutMs}ms timeout`);
|
|
29
|
+
this.name = "SpicyTimeoutError";
|
|
30
|
+
this.timeoutMs = timeoutMs;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class SpicyWaitTimeoutError extends Error {
|
|
34
|
+
taskId;
|
|
35
|
+
timeoutMs;
|
|
36
|
+
lastTask;
|
|
37
|
+
constructor(taskId, timeoutMs, lastTask) {
|
|
38
|
+
super(`task ${taskId} did not reach a terminal state within ${timeoutMs}ms`);
|
|
39
|
+
this.name = "SpicyWaitTimeoutError";
|
|
40
|
+
this.taskId = taskId;
|
|
41
|
+
this.timeoutMs = timeoutMs;
|
|
42
|
+
this.lastTask = lastTask;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class SpicyUploadError extends Error {
|
|
46
|
+
status;
|
|
47
|
+
constructor(message, status) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "SpicyUploadError";
|
|
50
|
+
this.status = status;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/sdk/errors.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IACf,IAAI,CAAqB;IACzB,SAAS,CAAqB;IAC9B,iBAAiB,CAAqB;IACtC,cAAc,CAAqB;IACnC,kBAAkB,CAAqB;IAEhD,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACnD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IACvD,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,mBAAmB;IAC/C,SAAS,CAAS;IAE3B,YAAY,SAAiB;QAC3B,KAAK,CAAC,8BAA8B,SAAS,YAAY,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACrC,MAAM,CAAS;IACf,SAAS,CAAS;IAClB,QAAQ,CAAyB;IAE1C,YAAY,MAAc,EAAE,SAAiB,EAAE,QAAqB;QAClE,KAAK,CAAC,QAAQ,MAAM,0CAA0C,SAAS,IAAI,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,mBAAmB,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { components } from "../generated/openapi.js";
|
|
2
|
+
export type ApiModel = components["schemas"]["APIModel"];
|
|
3
|
+
export type ApiModelPrice = components["schemas"]["APIModelPrice"];
|
|
4
|
+
export type ModelList = components["schemas"]["APIModelList"];
|
|
5
|
+
export type Balance = components["schemas"]["Balance"];
|
|
6
|
+
export type TaskRecord = components["schemas"]["TaskRecord"];
|
|
7
|
+
export type TaskState = TaskRecord["state"];
|
|
8
|
+
export type CreateTaskResult = components["schemas"]["CreateTaskResponse"];
|
|
9
|
+
export type RetryTaskResult = components["schemas"]["RetryTaskResponse"];
|
|
10
|
+
export type UploadTicket = components["schemas"]["UploadURLResponse"];
|
|
11
|
+
export type UploadedFile = components["schemas"]["FileCommitResponse"];
|
|
12
|
+
export type DownloadTicket = components["schemas"]["DownloadURLResponse"];
|
|
13
|
+
export type UploadContentType = components["schemas"]["UploadURLRequest"]["contentType"];
|
|
14
|
+
export interface CreateTaskInput {
|
|
15
|
+
model: string;
|
|
16
|
+
input: Record<string, unknown>;
|
|
17
|
+
callBackUrl?: string;
|
|
18
|
+
/** Caller-declared execution metadata forwarded without a platform content gate. */
|
|
19
|
+
mature?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface ListModelsOptions {
|
|
22
|
+
modality?: "image" | "video" | "audio" | "text" | undefined;
|
|
23
|
+
provider?: string | undefined;
|
|
24
|
+
task?: string | undefined;
|
|
25
|
+
search?: string | undefined;
|
|
26
|
+
includeSchema?: boolean | undefined;
|
|
27
|
+
includeExamples?: boolean | undefined;
|
|
28
|
+
signal?: AbortSignal | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface RequestOptions {
|
|
31
|
+
signal?: AbortSignal | undefined;
|
|
32
|
+
}
|
|
33
|
+
export interface BillableRequestOptions extends RequestOptions {
|
|
34
|
+
idempotencyKey?: string | undefined;
|
|
35
|
+
}
|
|
36
|
+
export interface WaitForTaskOptions extends RequestOptions {
|
|
37
|
+
intervalMs?: number;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
onUpdate?: ((task: TaskRecord) => void | Promise<void>) | undefined;
|
|
40
|
+
}
|
|
41
|
+
export interface ProbeResult {
|
|
42
|
+
path: "/healthz" | "/readyz";
|
|
43
|
+
ok: boolean;
|
|
44
|
+
status: number;
|
|
45
|
+
body: string;
|
|
46
|
+
error?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ServiceStatus {
|
|
49
|
+
operational: boolean;
|
|
50
|
+
health: ProbeResult;
|
|
51
|
+
readiness: ProbeResult;
|
|
52
|
+
checkedAt: string;
|
|
53
|
+
}
|
|
54
|
+
export interface SpicyClientOptions {
|
|
55
|
+
apiKey?: string | undefined;
|
|
56
|
+
apiBaseUrl?: string | undefined;
|
|
57
|
+
serviceBaseUrl?: string | undefined;
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
maxRetries?: number;
|
|
60
|
+
retryBaseDelayMs?: number;
|
|
61
|
+
maxRetryDelayMs?: number;
|
|
62
|
+
fetch?: typeof fetch | undefined;
|
|
63
|
+
now?: (() => number) | undefined;
|
|
64
|
+
random?: (() => number) | undefined;
|
|
65
|
+
sleep?: ((milliseconds: number, signal?: AbortSignal) => Promise<void>) | undefined;
|
|
66
|
+
}
|
|
67
|
+
export interface UploadBytesOptions extends RequestOptions {
|
|
68
|
+
contentType: UploadContentType;
|
|
69
|
+
}
|
|
70
|
+
export interface UploadFileOptions extends RequestOptions {
|
|
71
|
+
contentType?: UploadContentType | undefined;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/sdk/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/sdk/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { components } from "../generated/openapi.js";
|
|
2
|
+
export type WebhookPayload = components["schemas"]["LegacyWebhookPayload"] | components["schemas"]["TaskRecordEnvelope"];
|
|
3
|
+
export type WebhookPayloadVersion = 1 | 2;
|
|
4
|
+
export interface VerifyWebhookOptions {
|
|
5
|
+
rawBody: string | Uint8Array;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
signature: string;
|
|
8
|
+
payloadVersion: WebhookPayloadVersion;
|
|
9
|
+
secret: string;
|
|
10
|
+
toleranceSeconds?: number;
|
|
11
|
+
now?: () => number;
|
|
12
|
+
maxBodyBytes?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface VerifiedWebhook<T extends WebhookPayload = WebhookPayload> {
|
|
15
|
+
payload: T;
|
|
16
|
+
payloadVersion: WebhookPayloadVersion;
|
|
17
|
+
taskId: string;
|
|
18
|
+
deliveryId?: string;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
}
|
|
21
|
+
export type WebhookVerificationReason = "body_too_large" | "invalid_json" | "invalid_payload" | "invalid_secret" | "invalid_signature" | "invalid_timestamp" | "stale_timestamp";
|
|
22
|
+
export declare class SpicyWebhookVerificationError extends Error {
|
|
23
|
+
readonly reason: WebhookVerificationReason;
|
|
24
|
+
constructor(reason: WebhookVerificationReason, message: string);
|
|
25
|
+
}
|
|
26
|
+
export declare function computeWebhookSignature(taskId: string, timestamp: string | number, rawBody: string | Uint8Array, secret: string): string;
|
|
27
|
+
export declare function verifyWebhook<T extends WebhookPayload = WebhookPayload>(options: VerifyWebhookOptions): VerifiedWebhook<T>;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/webhooks/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,MAAM,cAAc,GACxB,UAAU,CAAC,SAAS,CAAC,CAAC,sBAAsB,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAC;AAE9F,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,CAAC,CAAC;AAE1C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,qBAAqB,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc;IACxE,OAAO,EAAE,CAAC,CAAC;IACX,cAAc,EAAE,qBAAqB,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,yBAAyB,GACjC,gBAAgB,GAChB,cAAc,GACd,iBAAiB,GACjB,gBAAgB,GAChB,mBAAmB,GACnB,mBAAmB,GACnB,iBAAiB,CAAC;AAEtB,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC;gBAE/B,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM;CAK/D;AAgBD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,OAAO,EAAE,MAAM,GAAG,UAAU,EAC5B,MAAM,EAAE,MAAM,GACb,MAAM,CAGR;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EACrE,OAAO,EAAE,oBAAoB,GAC5B,eAAe,CAAC,CAAC,CAAC,CAgFpB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
export class SpicyWebhookVerificationError extends Error {
|
|
3
|
+
reason;
|
|
4
|
+
constructor(reason, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "SpicyWebhookVerificationError";
|
|
7
|
+
this.reason = reason;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function rawBytes(rawBody) {
|
|
11
|
+
return typeof rawBody === "string" ? new TextEncoder().encode(rawBody) : rawBody;
|
|
12
|
+
}
|
|
13
|
+
function extractTaskId(payload, version) {
|
|
14
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload))
|
|
15
|
+
return undefined;
|
|
16
|
+
const record = payload;
|
|
17
|
+
if (version === 1)
|
|
18
|
+
return typeof record.task_id === "string" ? record.task_id : undefined;
|
|
19
|
+
if (record.data === null || typeof record.data !== "object" || Array.isArray(record.data))
|
|
20
|
+
return undefined;
|
|
21
|
+
const data = record.data;
|
|
22
|
+
return typeof data.taskId === "string" ? data.taskId : undefined;
|
|
23
|
+
}
|
|
24
|
+
export function computeWebhookSignature(taskId, timestamp, rawBody, secret) {
|
|
25
|
+
const digest = createHash("sha256").update(rawBytes(rawBody)).digest("hex");
|
|
26
|
+
return createHmac("sha256", secret).update(`${taskId}.${timestamp}.${digest}`).digest("base64");
|
|
27
|
+
}
|
|
28
|
+
export function verifyWebhook(options) {
|
|
29
|
+
const bytes = rawBytes(options.rawBody);
|
|
30
|
+
const maxBodyBytes = options.maxBodyBytes ?? 1_048_576;
|
|
31
|
+
if (bytes.byteLength > maxBodyBytes) {
|
|
32
|
+
throw new SpicyWebhookVerificationError("body_too_large", `webhook body exceeds ${maxBodyBytes} bytes`);
|
|
33
|
+
}
|
|
34
|
+
if (!options.secret.trim()) {
|
|
35
|
+
throw new SpicyWebhookVerificationError("invalid_secret", "webhook secret is required");
|
|
36
|
+
}
|
|
37
|
+
if (!/^\d+$/.test(options.timestamp)) {
|
|
38
|
+
throw new SpicyWebhookVerificationError("invalid_timestamp", "webhook timestamp must be Unix seconds");
|
|
39
|
+
}
|
|
40
|
+
let payload;
|
|
41
|
+
try {
|
|
42
|
+
payload = JSON.parse(new TextDecoder().decode(bytes));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new SpicyWebhookVerificationError("invalid_json", "webhook body is not valid JSON");
|
|
46
|
+
}
|
|
47
|
+
const taskId = extractTaskId(payload, options.payloadVersion);
|
|
48
|
+
if (!taskId) {
|
|
49
|
+
throw new SpicyWebhookVerificationError("invalid_payload", `webhook payload version ${options.payloadVersion} does not contain a task ID`);
|
|
50
|
+
}
|
|
51
|
+
const expected = Buffer.from(computeWebhookSignature(taskId, options.timestamp, bytes, options.secret), "base64");
|
|
52
|
+
const suppliedRaw = Buffer.from(options.signature, "base64");
|
|
53
|
+
const supplied = Buffer.alloc(expected.byteLength);
|
|
54
|
+
suppliedRaw.copy(supplied, 0, 0, expected.byteLength);
|
|
55
|
+
const signatureMatches = suppliedRaw.byteLength === expected.byteLength && timingSafeEqual(expected, supplied);
|
|
56
|
+
if (!signatureMatches) {
|
|
57
|
+
throw new SpicyWebhookVerificationError("invalid_signature", "webhook signature does not match");
|
|
58
|
+
}
|
|
59
|
+
const timestamp = Number(options.timestamp);
|
|
60
|
+
if (!Number.isSafeInteger(timestamp)) {
|
|
61
|
+
throw new SpicyWebhookVerificationError("invalid_timestamp", "webhook timestamp is outside the safe range");
|
|
62
|
+
}
|
|
63
|
+
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
64
|
+
if (!Number.isInteger(toleranceSeconds) || toleranceSeconds < 0 || toleranceSeconds > 86_400) {
|
|
65
|
+
throw new TypeError("toleranceSeconds must be an integer between 0 and 86400");
|
|
66
|
+
}
|
|
67
|
+
const nowSeconds = Math.floor((options.now ?? Date.now)() / 1_000);
|
|
68
|
+
if (Math.abs(nowSeconds - timestamp) > toleranceSeconds) {
|
|
69
|
+
throw new SpicyWebhookVerificationError("stale_timestamp", "webhook timestamp is outside the tolerance window");
|
|
70
|
+
}
|
|
71
|
+
const record = payload;
|
|
72
|
+
const deliveryId = options.payloadVersion === 2 && typeof record.request_id === "string"
|
|
73
|
+
? record.request_id
|
|
74
|
+
: undefined;
|
|
75
|
+
return {
|
|
76
|
+
payload: payload,
|
|
77
|
+
payloadVersion: options.payloadVersion,
|
|
78
|
+
taskId,
|
|
79
|
+
...(deliveryId === undefined ? {} : { deliveryId }),
|
|
80
|
+
timestamp,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/webhooks/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spicyapi/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official TypeScript SDK and typed public API contract for SpicyAPI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/SpicyAPI/spicy-devkit.git",
|
|
10
|
+
"directory": "packages/sdk"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://spicyapi.ai",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://spicyapi.ai/contact"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.13.0"
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/src/index.js",
|
|
23
|
+
"types": "./dist/src/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/src/index.d.ts",
|
|
27
|
+
"import": "./dist/src/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./webhooks": {
|
|
30
|
+
"types": "./dist/src/webhooks/index.d.ts",
|
|
31
|
+
"import": "./dist/src/webhooks/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./openapi": {
|
|
34
|
+
"types": "./dist/src/generated/openapi.d.ts",
|
|
35
|
+
"import": "./dist/src/generated/openapi.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist/src",
|
|
40
|
+
"contracts/openapi.yaml",
|
|
41
|
+
"README.md",
|
|
42
|
+
"SECURITY.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.json",
|
|
47
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
48
|
+
}
|
|
49
|
+
}
|