@indreamai/client 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/README.md +33 -0
- package/dist/index.cjs +526 -0
- package/dist/index.d.cts +219 -0
- package/dist/index.d.ts +219 -0
- package/dist/index.js +489 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @indreamai/client
|
|
2
|
+
|
|
3
|
+
Official JavaScript/TypeScript client for the Indream Open API.
|
|
4
|
+
|
|
5
|
+
- API docs: https://docs.indream.ai
|
|
6
|
+
- Supports Node.js 18+ and Edge runtimes
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add @indreamai/client
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { IndreamClient } from '@indreamai/client'
|
|
18
|
+
|
|
19
|
+
const client = new IndreamClient({
|
|
20
|
+
apiKey: process.env.INDREAM_API_KEY!,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const created = await client.exports.create({
|
|
24
|
+
editorState,
|
|
25
|
+
ratio: '9:16',
|
|
26
|
+
scale: 0.6,
|
|
27
|
+
fps: 30,
|
|
28
|
+
format: 'mp4',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const task = await client.exports.wait(created.taskId)
|
|
32
|
+
console.log(task.status, task.outputUrl, task.durationSeconds, task.billedStandardSeconds)
|
|
33
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
23
|
+
};
|
|
24
|
+
var __copyProps = (to, from, except, desc) => {
|
|
25
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
26
|
+
for (let key of __getOwnPropNames(from))
|
|
27
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
28
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
29
|
+
}
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// src/index.ts
|
|
35
|
+
var index_exports = {};
|
|
36
|
+
__export(index_exports, {
|
|
37
|
+
APIError: () => APIError,
|
|
38
|
+
AuthError: () => AuthError,
|
|
39
|
+
IndreamClient: () => IndreamClient,
|
|
40
|
+
RateLimitError: () => RateLimitError,
|
|
41
|
+
ValidationError: () => ValidationError,
|
|
42
|
+
createApiError: () => createApiError,
|
|
43
|
+
isExportTaskSnapshot: () => isExportTaskSnapshot,
|
|
44
|
+
isExportWebhookEvent: () => isExportWebhookEvent,
|
|
45
|
+
isExportWebhookEventType: () => isExportWebhookEventType,
|
|
46
|
+
isTaskStatus: () => isTaskStatus,
|
|
47
|
+
parseExportWebhookEvent: () => parseExportWebhookEvent,
|
|
48
|
+
toApiProblem: () => toApiProblem,
|
|
49
|
+
verifyExportWebhookRequest: () => verifyExportWebhookRequest,
|
|
50
|
+
verifyExportWebhookSignature: () => verifyExportWebhookSignature
|
|
51
|
+
});
|
|
52
|
+
module.exports = __toCommonJS(index_exports);
|
|
53
|
+
|
|
54
|
+
// src/errors.ts
|
|
55
|
+
var APIError = class extends Error {
|
|
56
|
+
constructor(problem) {
|
|
57
|
+
super(problem.detail || problem.title);
|
|
58
|
+
this.name = "APIError";
|
|
59
|
+
this.status = problem.status;
|
|
60
|
+
this.type = problem.type;
|
|
61
|
+
this.detail = problem.detail;
|
|
62
|
+
this.errorCode = problem.errorCode;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var AuthError = class extends APIError {
|
|
66
|
+
constructor(problem) {
|
|
67
|
+
super(problem);
|
|
68
|
+
this.name = "AuthError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var ValidationError = class extends APIError {
|
|
72
|
+
constructor(problem) {
|
|
73
|
+
super(problem);
|
|
74
|
+
this.name = "ValidationError";
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var RateLimitError = class extends APIError {
|
|
78
|
+
constructor(problem) {
|
|
79
|
+
super(problem);
|
|
80
|
+
this.name = "RateLimitError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var toApiProblem = (status, payload) => {
|
|
84
|
+
if (payload && typeof payload === "object") {
|
|
85
|
+
const problem = payload;
|
|
86
|
+
if (typeof problem.type === "string" && typeof problem.title === "string") {
|
|
87
|
+
return {
|
|
88
|
+
type: problem.type,
|
|
89
|
+
title: problem.title,
|
|
90
|
+
status: Number(problem.status || status),
|
|
91
|
+
detail: String(problem.detail || problem.title),
|
|
92
|
+
errorCode: problem.errorCode
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
type: "INTERNAL_ERROR",
|
|
98
|
+
title: "Internal server error",
|
|
99
|
+
status,
|
|
100
|
+
detail: "Unexpected API response",
|
|
101
|
+
errorCode: "SDK_UNEXPECTED_RESPONSE"
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
var createApiError = (status, payload) => {
|
|
105
|
+
const problem = toApiProblem(status, payload);
|
|
106
|
+
if (status === 401 || status === 403) {
|
|
107
|
+
return new AuthError(problem);
|
|
108
|
+
}
|
|
109
|
+
if (status === 422 || status === 400) {
|
|
110
|
+
return new ValidationError(problem);
|
|
111
|
+
}
|
|
112
|
+
if (status === 429) {
|
|
113
|
+
return new RateLimitError(problem);
|
|
114
|
+
}
|
|
115
|
+
return new APIError(problem);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// src/resources/exports.ts
|
|
119
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["COMPLETED", "FAILED", "CANCELED"]);
|
|
120
|
+
var sleep = async (ms) => {
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
122
|
+
};
|
|
123
|
+
var ExportsResource = class {
|
|
124
|
+
constructor(client) {
|
|
125
|
+
this.client = client;
|
|
126
|
+
}
|
|
127
|
+
async create(payload, options = {}) {
|
|
128
|
+
var _a;
|
|
129
|
+
const idempotencyKey = (_a = options.idempotencyKey) == null ? void 0 : _a.trim();
|
|
130
|
+
return await this.client.request("/v1/exports", {
|
|
131
|
+
method: "POST",
|
|
132
|
+
body: payload,
|
|
133
|
+
idempotencyKey: idempotencyKey || void 0,
|
|
134
|
+
signal: options.signal
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async get(taskId, options = {}) {
|
|
138
|
+
return await this.client.request(`/v1/exports/${taskId}`, {
|
|
139
|
+
method: "GET",
|
|
140
|
+
signal: options.signal
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async list(params) {
|
|
144
|
+
var _a;
|
|
145
|
+
const search = new URLSearchParams();
|
|
146
|
+
if (params == null ? void 0 : params.pageSize) {
|
|
147
|
+
search.set("pageSize", String(params.pageSize));
|
|
148
|
+
}
|
|
149
|
+
if (params == null ? void 0 : params.pageCursor) {
|
|
150
|
+
search.set("pageCursor", params.pageCursor);
|
|
151
|
+
}
|
|
152
|
+
if (params == null ? void 0 : params.createdByApiKeyId) {
|
|
153
|
+
search.set("createdByApiKeyId", params.createdByApiKeyId);
|
|
154
|
+
}
|
|
155
|
+
const query = search.toString();
|
|
156
|
+
const envelope = await this.client.requestEnvelope(
|
|
157
|
+
`/v1/exports${query ? `?${query}` : ""}`,
|
|
158
|
+
{
|
|
159
|
+
method: "GET",
|
|
160
|
+
signal: params == null ? void 0 : params.signal
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
return {
|
|
164
|
+
items: envelope.data || [],
|
|
165
|
+
nextPageCursor: typeof ((_a = envelope.meta) == null ? void 0 : _a.nextPageCursor) === "string" ? envelope.meta.nextPageCursor : null
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async wait(taskId, options = {}) {
|
|
169
|
+
var _a;
|
|
170
|
+
const timeoutMs = options.timeoutMs || 10 * 60 * 1e3;
|
|
171
|
+
const pollIntervalMs = options.pollIntervalMs || this.client.pollIntervalMs;
|
|
172
|
+
const startedAt = Date.now();
|
|
173
|
+
while (true) {
|
|
174
|
+
if ((_a = options.signal) == null ? void 0 : _a.aborted) {
|
|
175
|
+
throw new Error("wait aborted by caller signal");
|
|
176
|
+
}
|
|
177
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
178
|
+
throw new Error(`wait timeout after ${timeoutMs}ms`);
|
|
179
|
+
}
|
|
180
|
+
const task = await this.get(taskId, {
|
|
181
|
+
signal: options.signal
|
|
182
|
+
});
|
|
183
|
+
if (TERMINAL_STATUSES.has(task.status)) {
|
|
184
|
+
if (task.status === "FAILED" || task.status === "CANCELED") {
|
|
185
|
+
throw new APIError({
|
|
186
|
+
type: "TASK_TERMINAL_FAILURE",
|
|
187
|
+
title: "Task failed",
|
|
188
|
+
status: 422,
|
|
189
|
+
detail: task.error || `Task ended with status ${task.status}`,
|
|
190
|
+
errorCode: "TASK_TERMINAL_FAILURE"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return task;
|
|
194
|
+
}
|
|
195
|
+
await sleep(pollIntervalMs);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// src/resources/editor.ts
|
|
201
|
+
var EditorResource = class {
|
|
202
|
+
constructor(client) {
|
|
203
|
+
this.client = client;
|
|
204
|
+
}
|
|
205
|
+
async capabilities(options = {}) {
|
|
206
|
+
return await this.client.request("/v1/editor/capabilities", {
|
|
207
|
+
method: "GET",
|
|
208
|
+
signal: options.signal
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async validate(editorState, options = {}) {
|
|
212
|
+
return await this.client.request("/v1/editor/validate", {
|
|
213
|
+
method: "POST",
|
|
214
|
+
body: {
|
|
215
|
+
editorState
|
|
216
|
+
},
|
|
217
|
+
signal: options.signal,
|
|
218
|
+
skipRetry: true
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/retry.ts
|
|
224
|
+
var sleep2 = async (ms) => {
|
|
225
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
226
|
+
};
|
|
227
|
+
var shouldRetryStatus = (status) => {
|
|
228
|
+
return status === 429 || status >= 500;
|
|
229
|
+
};
|
|
230
|
+
var computeRetryDelay = (attempt, baseDelayMs = 300, maxDelayMs = 3e3) => {
|
|
231
|
+
const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** attempt);
|
|
232
|
+
const jitter = Math.floor(Math.random() * 100);
|
|
233
|
+
return exponential + jitter;
|
|
234
|
+
};
|
|
235
|
+
var withRetry = async (execute, options) => {
|
|
236
|
+
let attempt = 0;
|
|
237
|
+
while (true) {
|
|
238
|
+
try {
|
|
239
|
+
return await execute(attempt);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (error && typeof error === "object" && "noRetry" in error) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
if (attempt >= options.maxRetries) {
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
const delay = computeRetryDelay(attempt, options.baseDelayMs, options.maxDelayMs);
|
|
248
|
+
await sleep2(delay);
|
|
249
|
+
attempt += 1;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/client.ts
|
|
255
|
+
var IndreamClient = class {
|
|
256
|
+
constructor(options) {
|
|
257
|
+
if (!options.apiKey) {
|
|
258
|
+
throw new Error("apiKey is required");
|
|
259
|
+
}
|
|
260
|
+
this.apiKey = options.apiKey;
|
|
261
|
+
this.baseURL = (options.baseURL || "https://api.indream.ai").replace(/\/$/, "");
|
|
262
|
+
this.timeout = options.timeout || 6e4;
|
|
263
|
+
this.maxRetries = Number.isFinite(options.maxRetries) ? Number(options.maxRetries) : 2;
|
|
264
|
+
this.pollIntervalMs = options.pollIntervalMs || 2e3;
|
|
265
|
+
this.fetchImpl = options.fetch || fetch;
|
|
266
|
+
this.exports = new ExportsResource(this);
|
|
267
|
+
this.editor = new EditorResource(this);
|
|
268
|
+
}
|
|
269
|
+
async request(path, init) {
|
|
270
|
+
const envelope = await this.requestEnvelope(path, init);
|
|
271
|
+
return envelope.data;
|
|
272
|
+
}
|
|
273
|
+
async requestEnvelope(path, init) {
|
|
274
|
+
const url = `${this.baseURL}${path.startsWith("/") ? path : `/${path}`}`;
|
|
275
|
+
const payload = init.body === void 0 ? void 0 : JSON.stringify(init.body);
|
|
276
|
+
const execute = async () => {
|
|
277
|
+
const controller = new AbortController();
|
|
278
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
279
|
+
const externalSignal = init.signal;
|
|
280
|
+
const onExternalAbort = () => controller.abort();
|
|
281
|
+
let hasExternalAbortListener = false;
|
|
282
|
+
if (externalSignal) {
|
|
283
|
+
if (externalSignal.aborted) {
|
|
284
|
+
controller.abort();
|
|
285
|
+
} else {
|
|
286
|
+
externalSignal.addEventListener("abort", onExternalAbort, { once: true });
|
|
287
|
+
hasExternalAbortListener = true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const headers = __spreadValues({
|
|
291
|
+
"x-api-key": this.apiKey,
|
|
292
|
+
Accept: "application/json"
|
|
293
|
+
}, init.headers);
|
|
294
|
+
if (payload !== void 0) {
|
|
295
|
+
headers["Content-Type"] = "application/json";
|
|
296
|
+
}
|
|
297
|
+
if (init.idempotencyKey) {
|
|
298
|
+
headers["Idempotency-Key"] = init.idempotencyKey;
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
const response = await this.fetchImpl(url, {
|
|
302
|
+
method: init.method,
|
|
303
|
+
body: payload,
|
|
304
|
+
headers,
|
|
305
|
+
signal: controller.signal
|
|
306
|
+
});
|
|
307
|
+
const text = await response.text();
|
|
308
|
+
const parsed = text ? JSON.parse(text) : null;
|
|
309
|
+
if (!response.ok) {
|
|
310
|
+
throw createApiError(response.status, parsed);
|
|
311
|
+
}
|
|
312
|
+
if (!parsed || typeof parsed !== "object" || !("data" in parsed)) {
|
|
313
|
+
throw createApiError(response.status, parsed);
|
|
314
|
+
}
|
|
315
|
+
return parsed;
|
|
316
|
+
} finally {
|
|
317
|
+
clearTimeout(timeoutId);
|
|
318
|
+
if (hasExternalAbortListener) {
|
|
319
|
+
externalSignal == null ? void 0 : externalSignal.removeEventListener("abort", onExternalAbort);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
if (init.skipRetry) {
|
|
324
|
+
return await execute();
|
|
325
|
+
}
|
|
326
|
+
return await withRetry(
|
|
327
|
+
async () => {
|
|
328
|
+
try {
|
|
329
|
+
return await execute();
|
|
330
|
+
} catch (error) {
|
|
331
|
+
const apiError = error;
|
|
332
|
+
if ((apiError == null ? void 0 : apiError.status) && shouldRetryStatus(apiError.status)) {
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
336
|
+
throw {
|
|
337
|
+
noRetry: true,
|
|
338
|
+
error
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
if (apiError == null ? void 0 : apiError.status) {
|
|
342
|
+
throw {
|
|
343
|
+
noRetry: true,
|
|
344
|
+
error
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
maxRetries: this.maxRetries
|
|
352
|
+
}
|
|
353
|
+
).catch((error) => {
|
|
354
|
+
if (error && typeof error === "object" && "noRetry" in error) {
|
|
355
|
+
throw error.error;
|
|
356
|
+
}
|
|
357
|
+
throw error;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// src/webhooks.ts
|
|
363
|
+
var INDREAM_WEBHOOK_TIMESTAMP_HEADER = "X-Indream-Timestamp";
|
|
364
|
+
var INDREAM_WEBHOOK_SIGNATURE_HEADER = "X-Indream-Signature";
|
|
365
|
+
var DEFAULT_WEBHOOK_MAX_SKEW_SECONDS = 300;
|
|
366
|
+
var EXPORT_WEBHOOK_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
367
|
+
"EXPORT_STARTED",
|
|
368
|
+
"EXPORT_COMPLETED",
|
|
369
|
+
"EXPORT_FAILED"
|
|
370
|
+
]);
|
|
371
|
+
var TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
372
|
+
"PENDING",
|
|
373
|
+
"PROCESSING",
|
|
374
|
+
"COMPLETED",
|
|
375
|
+
"FAILED",
|
|
376
|
+
"PAUSED",
|
|
377
|
+
"CANCELED"
|
|
378
|
+
]);
|
|
379
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
380
|
+
var isNullableString = (value) => typeof value === "string" || value === null;
|
|
381
|
+
var toArrayBuffer = (bytes) => {
|
|
382
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
383
|
+
copy.set(bytes);
|
|
384
|
+
return copy.buffer;
|
|
385
|
+
};
|
|
386
|
+
var toUtf8ArrayBuffer = (value) => toArrayBuffer(new TextEncoder().encode(value));
|
|
387
|
+
var parseHexToBytes = (value) => {
|
|
388
|
+
const normalized = value.trim().toLowerCase();
|
|
389
|
+
if (!/^[0-9a-f]+$/.test(normalized) || normalized.length % 2 !== 0) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
const bytes = new Uint8Array(normalized.length / 2);
|
|
393
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
394
|
+
const start = index * 2;
|
|
395
|
+
bytes[index] = Number.parseInt(normalized.slice(start, start + 2), 16);
|
|
396
|
+
}
|
|
397
|
+
return bytes;
|
|
398
|
+
};
|
|
399
|
+
var parseTimestampSeconds = (value) => {
|
|
400
|
+
if (!/^\d+$/.test(value)) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const parsed = Number(value);
|
|
404
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
return parsed;
|
|
408
|
+
};
|
|
409
|
+
var resolveHeaderValue = (headers, targetKey) => {
|
|
410
|
+
if (headers instanceof Headers) {
|
|
411
|
+
const value = headers.get(targetKey);
|
|
412
|
+
if (typeof value !== "string") {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
const trimmed = value.trim();
|
|
416
|
+
return trimmed ? trimmed : null;
|
|
417
|
+
}
|
|
418
|
+
const lowerKey = targetKey.toLowerCase();
|
|
419
|
+
for (const [key, rawValue] of Object.entries(headers)) {
|
|
420
|
+
if (key.toLowerCase() !== lowerKey) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
|
|
424
|
+
if (typeof value !== "string") {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
const trimmed = value.trim();
|
|
428
|
+
return trimmed ? trimmed : null;
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
};
|
|
432
|
+
var isExportWebhookEventType = (value) => typeof value === "string" && EXPORT_WEBHOOK_EVENT_TYPES.has(value);
|
|
433
|
+
var isTaskStatus = (value) => typeof value === "string" && TASK_STATUSES.has(value);
|
|
434
|
+
var isExportTaskSnapshot = (value) => {
|
|
435
|
+
if (!isObject(value)) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
return typeof value.taskId === "string" && isNullableString(value.createdByApiKeyId) && isNullableString(value.clientTaskId) && isTaskStatus(value.status) && typeof value.progress === "number" && isNullableString(value.error) && isNullableString(value.outputUrl) && typeof value.durationSeconds === "number" && typeof value.billedStandardSeconds === "number" && typeof value.chargedCredits === "string" && isNullableString(value.callbackUrl) && typeof value.createdAt === "string" && isNullableString(value.completedAt);
|
|
439
|
+
};
|
|
440
|
+
var isExportWebhookEvent = (value) => {
|
|
441
|
+
if (!isObject(value)) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
return isExportWebhookEventType(value.eventType) && typeof value.occurredAt === "string" && isExportTaskSnapshot(value.task);
|
|
445
|
+
};
|
|
446
|
+
var parseExportWebhookEvent = (value) => {
|
|
447
|
+
if (!isExportWebhookEvent(value)) {
|
|
448
|
+
throw new TypeError("Invalid export webhook payload");
|
|
449
|
+
}
|
|
450
|
+
return value;
|
|
451
|
+
};
|
|
452
|
+
var verifyExportWebhookSignature = async ({
|
|
453
|
+
webhookSecret,
|
|
454
|
+
timestamp,
|
|
455
|
+
rawBody,
|
|
456
|
+
signature
|
|
457
|
+
}) => {
|
|
458
|
+
var _a;
|
|
459
|
+
if (!webhookSecret || !timestamp || !signature) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
const signatureBytes = parseHexToBytes(signature);
|
|
463
|
+
if (!signatureBytes) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
const subtle = (_a = globalThis.crypto) == null ? void 0 : _a.subtle;
|
|
467
|
+
if (!subtle) {
|
|
468
|
+
throw new Error("Web Crypto subtle API is not available in current runtime");
|
|
469
|
+
}
|
|
470
|
+
const key = await subtle.importKey(
|
|
471
|
+
"raw",
|
|
472
|
+
toUtf8ArrayBuffer(webhookSecret),
|
|
473
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
474
|
+
false,
|
|
475
|
+
["verify"]
|
|
476
|
+
);
|
|
477
|
+
return subtle.verify(
|
|
478
|
+
"HMAC",
|
|
479
|
+
key,
|
|
480
|
+
toArrayBuffer(signatureBytes),
|
|
481
|
+
toUtf8ArrayBuffer(`${timestamp}.${rawBody}`)
|
|
482
|
+
);
|
|
483
|
+
};
|
|
484
|
+
var verifyExportWebhookRequest = async ({
|
|
485
|
+
webhookSecret,
|
|
486
|
+
rawBody,
|
|
487
|
+
headers,
|
|
488
|
+
maxSkewSeconds = DEFAULT_WEBHOOK_MAX_SKEW_SECONDS,
|
|
489
|
+
nowTimestampSeconds = Math.floor(Date.now() / 1e3)
|
|
490
|
+
}) => {
|
|
491
|
+
const timestamp = resolveHeaderValue(headers, INDREAM_WEBHOOK_TIMESTAMP_HEADER);
|
|
492
|
+
const signature = resolveHeaderValue(headers, INDREAM_WEBHOOK_SIGNATURE_HEADER);
|
|
493
|
+
if (!timestamp || !signature) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
const parsedTimestamp = parseTimestampSeconds(timestamp);
|
|
497
|
+
if (parsedTimestamp === null || !Number.isFinite(maxSkewSeconds) || maxSkewSeconds < 0) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
if (Math.abs(nowTimestampSeconds - parsedTimestamp) > maxSkewSeconds) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
return verifyExportWebhookSignature({
|
|
504
|
+
webhookSecret,
|
|
505
|
+
timestamp,
|
|
506
|
+
rawBody,
|
|
507
|
+
signature
|
|
508
|
+
});
|
|
509
|
+
};
|
|
510
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
511
|
+
0 && (module.exports = {
|
|
512
|
+
APIError,
|
|
513
|
+
AuthError,
|
|
514
|
+
IndreamClient,
|
|
515
|
+
RateLimitError,
|
|
516
|
+
ValidationError,
|
|
517
|
+
createApiError,
|
|
518
|
+
isExportTaskSnapshot,
|
|
519
|
+
isExportWebhookEvent,
|
|
520
|
+
isExportWebhookEventType,
|
|
521
|
+
isTaskStatus,
|
|
522
|
+
parseExportWebhookEvent,
|
|
523
|
+
toApiProblem,
|
|
524
|
+
verifyExportWebhookRequest,
|
|
525
|
+
verifyExportWebhookSignature
|
|
526
|
+
});
|