@visgate_ai/client 0.2.17
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 +254 -0
- package/dist/index.cjs +801 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +511 -0
- package/dist/index.d.ts +511 -0
- package/dist/index.js +765 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
// src/exceptions.ts
|
|
2
|
+
var VisgateError = class _VisgateError extends Error {
|
|
3
|
+
constructor(message, errorCode = "VISGATE_ERROR", details = {}, statusCode) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "VisgateError";
|
|
6
|
+
this.message = message;
|
|
7
|
+
this.errorCode = errorCode;
|
|
8
|
+
this.details = details;
|
|
9
|
+
this.statusCode = statusCode;
|
|
10
|
+
Object.setPrototypeOf(this, _VisgateError.prototype);
|
|
11
|
+
}
|
|
12
|
+
toString() {
|
|
13
|
+
return `[${this.errorCode}] ${this.message}`;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var AuthenticationError = class _AuthenticationError extends VisgateError {
|
|
17
|
+
constructor(message = "Invalid or missing API key") {
|
|
18
|
+
super(message, "AUTHENTICATION_ERROR", {}, 401);
|
|
19
|
+
this.name = "AuthenticationError";
|
|
20
|
+
Object.setPrototypeOf(this, _AuthenticationError.prototype);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var ValidationError = class _ValidationError extends VisgateError {
|
|
24
|
+
constructor(message, field) {
|
|
25
|
+
super(message, "VALIDATION_ERROR", field ? { field } : {}, 422);
|
|
26
|
+
this.name = "ValidationError";
|
|
27
|
+
this.field = field;
|
|
28
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var RateLimitError = class _RateLimitError extends VisgateError {
|
|
32
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
33
|
+
super(message, "RATE_LIMIT_ERROR", retryAfter != null ? { retry_after: retryAfter } : {}, 429);
|
|
34
|
+
this.name = "RateLimitError";
|
|
35
|
+
this.retryAfter = retryAfter;
|
|
36
|
+
Object.setPrototypeOf(this, _RateLimitError.prototype);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var ProviderError = class _ProviderError extends VisgateError {
|
|
40
|
+
constructor(message, provider = "unknown") {
|
|
41
|
+
super(message, "PROVIDER_ERROR", { provider });
|
|
42
|
+
this.name = "ProviderError";
|
|
43
|
+
this.provider = provider;
|
|
44
|
+
Object.setPrototypeOf(this, _ProviderError.prototype);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var VisgateTimeoutError = class _VisgateTimeoutError extends VisgateError {
|
|
48
|
+
constructor(message = "Request timed out") {
|
|
49
|
+
super(message, "TIMEOUT_ERROR");
|
|
50
|
+
this.name = "VisgateTimeoutError";
|
|
51
|
+
Object.setPrototypeOf(this, _VisgateTimeoutError.prototype);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var VisgateConnectionError = class _VisgateConnectionError extends VisgateError {
|
|
55
|
+
constructor(message = "Connection failed") {
|
|
56
|
+
super(message, "CONNECTION_ERROR");
|
|
57
|
+
this.name = "VisgateConnectionError";
|
|
58
|
+
Object.setPrototypeOf(this, _VisgateConnectionError.prototype);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// src/resources/billing.ts
|
|
63
|
+
function billingInfoFromResponse(data) {
|
|
64
|
+
return {
|
|
65
|
+
tier: data.tier ?? "free",
|
|
66
|
+
balanceMicro: data.balance_micro ?? 0,
|
|
67
|
+
billingStatus: data.billing_status ?? "active",
|
|
68
|
+
monthlyBudgetCents: data.monthly_budget_cents,
|
|
69
|
+
usageThisMonthCents: data.usage_this_month_cents ?? 0,
|
|
70
|
+
supportEmail: data.support_email,
|
|
71
|
+
companyName: data.company_name,
|
|
72
|
+
freeModeEnabled: data.free_mode_enabled
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function modelPricingFromResponse(data) {
|
|
76
|
+
return {
|
|
77
|
+
id: data.id ?? "",
|
|
78
|
+
providerType: data.provider_type ?? "",
|
|
79
|
+
model: data.model ?? "",
|
|
80
|
+
modelName: data.model_name ?? "",
|
|
81
|
+
modelType: data.model_type ?? "image",
|
|
82
|
+
modelClass: data.model_class,
|
|
83
|
+
costPerUnitMicro: data.cost_per_unit_micro ?? 0,
|
|
84
|
+
usageCount: data.usage_count ?? 0,
|
|
85
|
+
totalSavingsMicro: data.total_savings_micro ?? 0,
|
|
86
|
+
avgSavingsMicro: data.avg_savings_micro ?? 0
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function pricingResponseFromResponse(data) {
|
|
90
|
+
const pricing = data.pricing ?? [];
|
|
91
|
+
return {
|
|
92
|
+
lastUpdated: data.last_updated ?? null,
|
|
93
|
+
pricing: pricing.map(modelPricingFromResponse)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var Billing = class {
|
|
97
|
+
constructor(_client) {
|
|
98
|
+
this._client = _client;
|
|
99
|
+
}
|
|
100
|
+
/** Public billing stats for landing/marketing (GET /billing/stats). May not require auth. */
|
|
101
|
+
async getStats() {
|
|
102
|
+
return await this._client._request("GET", "/billing/stats");
|
|
103
|
+
}
|
|
104
|
+
/** Organization billing info (GET /billing/info). */
|
|
105
|
+
async getInfo() {
|
|
106
|
+
const data = await this._client._request("GET", "/billing/info");
|
|
107
|
+
return billingInfoFromResponse(data);
|
|
108
|
+
}
|
|
109
|
+
/** Create checkout session (POST /billing/checkout). */
|
|
110
|
+
async checkout(amountDollars, returnUrl, options) {
|
|
111
|
+
const body = {
|
|
112
|
+
amount_dollars: amountDollars,
|
|
113
|
+
return_url: returnUrl
|
|
114
|
+
};
|
|
115
|
+
if (options?.customerEmail != null) body.customer_email = options.customerEmail;
|
|
116
|
+
if (options?.firebaseUid != null) body.firebase_uid = options.firebaseUid;
|
|
117
|
+
return await this._client._request("POST", "/billing/checkout", {
|
|
118
|
+
body: JSON.stringify(body)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/** Get pricing for all models (GET /billing/pricing). */
|
|
122
|
+
async getPricing() {
|
|
123
|
+
const data = await this._client._request("GET", "/billing/pricing");
|
|
124
|
+
return pricingResponseFromResponse(data);
|
|
125
|
+
}
|
|
126
|
+
/** Update organization subscription tier (POST /billing/subscription). */
|
|
127
|
+
async updateSubscription(tier) {
|
|
128
|
+
const data = await this._client._request("POST", "/billing/subscription", {
|
|
129
|
+
body: JSON.stringify({ tier })
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
success: Boolean(data.success),
|
|
133
|
+
tier: data.tier ?? tier
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// src/utils.ts
|
|
139
|
+
function parseDateTime(value) {
|
|
140
|
+
if (value == null || value === "") return null;
|
|
141
|
+
const normalized = value.replace("Z", "+00:00");
|
|
142
|
+
const date = new Date(normalized);
|
|
143
|
+
return isNaN(date.getTime()) ? null : date;
|
|
144
|
+
}
|
|
145
|
+
function hasProcessEnv() {
|
|
146
|
+
return typeof process !== "undefined" && typeof process.env !== "undefined";
|
|
147
|
+
}
|
|
148
|
+
function resolveApiKey(apiKey) {
|
|
149
|
+
const key = apiKey ?? (hasProcessEnv() ? process.env.VISGATE_API_KEY : void 0);
|
|
150
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
151
|
+
throw new AuthenticationError(
|
|
152
|
+
"No API key provided. Pass apiKey or set the VISGATE_API_KEY environment variable."
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return key.trim();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/resources/generate.ts
|
|
159
|
+
function generateResultFromResponse(data) {
|
|
160
|
+
return {
|
|
161
|
+
id: data.id,
|
|
162
|
+
status: data.status ?? "success",
|
|
163
|
+
imageUrl: data.image_url ?? null,
|
|
164
|
+
images: data.images ?? [],
|
|
165
|
+
model: data.model,
|
|
166
|
+
provider: data.provider ?? "",
|
|
167
|
+
mode: data.mode ?? "",
|
|
168
|
+
cost: data.estimated_cost_usd ?? 0,
|
|
169
|
+
costPerMegapixel: data.cost_per_megapixel_usd ?? 0,
|
|
170
|
+
latencyMs: data.latency_ms ?? 0,
|
|
171
|
+
resolution: data.resolution ?? {},
|
|
172
|
+
createdAt: parseDateTime(data.created_at)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
var DEFAULT_MODEL = "fal-ai/flux/schnell";
|
|
176
|
+
var Generate = class {
|
|
177
|
+
constructor(_client) {
|
|
178
|
+
this._client = _client;
|
|
179
|
+
}
|
|
180
|
+
async __call__(prompt, model = DEFAULT_MODEL, params) {
|
|
181
|
+
const payload = { prompt, model };
|
|
182
|
+
if (params) payload.params = params;
|
|
183
|
+
const data = await this._client._request("POST", "/generate", {
|
|
184
|
+
body: JSON.stringify(payload)
|
|
185
|
+
});
|
|
186
|
+
return generateResultFromResponse(data);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/resources/images.ts
|
|
191
|
+
function imageResultFromResponse(data) {
|
|
192
|
+
const cacheSource = data.cache_source;
|
|
193
|
+
return {
|
|
194
|
+
id: data.id,
|
|
195
|
+
status: data.status,
|
|
196
|
+
images: data.images ?? [],
|
|
197
|
+
model: data.model,
|
|
198
|
+
provider: data.provider,
|
|
199
|
+
cost: data.cost ?? 0,
|
|
200
|
+
cacheHit: data.cache_hit ?? false,
|
|
201
|
+
latencyMs: data.latency_ms ?? null,
|
|
202
|
+
savedAmount: data.saved_amount,
|
|
203
|
+
marginPercent: data.margin_percent,
|
|
204
|
+
cacheSource: cacheSource === "exact" || cacheSource === "semantic" ? cacheSource : void 0,
|
|
205
|
+
createdAt: parseDateTime(data.created_at)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
var Images = class {
|
|
209
|
+
constructor(_client) {
|
|
210
|
+
this._client = _client;
|
|
211
|
+
}
|
|
212
|
+
async generate(model, prompt, options = {}) {
|
|
213
|
+
const {
|
|
214
|
+
negativePrompt,
|
|
215
|
+
width = 1024,
|
|
216
|
+
height = 1024,
|
|
217
|
+
numImages = 1,
|
|
218
|
+
seed,
|
|
219
|
+
imageUrl,
|
|
220
|
+
preferCache,
|
|
221
|
+
maxLatencyMs,
|
|
222
|
+
webhookUrl,
|
|
223
|
+
callbackId,
|
|
224
|
+
params
|
|
225
|
+
} = options;
|
|
226
|
+
const payload = {
|
|
227
|
+
model,
|
|
228
|
+
prompt,
|
|
229
|
+
width,
|
|
230
|
+
height,
|
|
231
|
+
num_images: numImages
|
|
232
|
+
};
|
|
233
|
+
if (negativePrompt) payload.negative_prompt = negativePrompt;
|
|
234
|
+
if (seed != null) payload.seed = seed;
|
|
235
|
+
if (imageUrl) payload.image_url = imageUrl;
|
|
236
|
+
if (preferCache !== void 0) payload.prefer_cache = preferCache;
|
|
237
|
+
if (maxLatencyMs != null) payload.max_latency_ms = maxLatencyMs;
|
|
238
|
+
if (webhookUrl) payload.webhook_url = webhookUrl;
|
|
239
|
+
if (callbackId) payload.callback_id = callbackId;
|
|
240
|
+
if (params) Object.assign(payload, params);
|
|
241
|
+
const data = await this._client._request("POST", "/images/generate", {
|
|
242
|
+
body: JSON.stringify(payload)
|
|
243
|
+
});
|
|
244
|
+
return imageResultFromResponse(data);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// src/resources/models.ts
|
|
249
|
+
function modelInfoFromResponse(data) {
|
|
250
|
+
return {
|
|
251
|
+
id: data.id ?? "",
|
|
252
|
+
name: data.name ?? "",
|
|
253
|
+
provider: data.provider ?? "",
|
|
254
|
+
mediaType: data.media_type ?? "image",
|
|
255
|
+
description: data.description,
|
|
256
|
+
category: data.category,
|
|
257
|
+
tags: data.tags ?? [],
|
|
258
|
+
coverImageUrl: data.cover_image_url,
|
|
259
|
+
author: data.author,
|
|
260
|
+
url: data.url,
|
|
261
|
+
baseCostMicro: data.base_cost_micro ?? 0,
|
|
262
|
+
normalizedCostMicro: data.normalized_cost_micro ?? 0,
|
|
263
|
+
pricing: data.pricing,
|
|
264
|
+
pricingUnit: data.pricing_unit,
|
|
265
|
+
runCount: data.run_count ?? 0,
|
|
266
|
+
inputTypes: data.input_types ?? [],
|
|
267
|
+
outputType: data.output_type,
|
|
268
|
+
capabilities: data.capabilities ?? [],
|
|
269
|
+
firstSeenAt: data.first_seen_at,
|
|
270
|
+
providerCreatedAt: data.provider_created_at,
|
|
271
|
+
lastSyncedAt: data.last_synced_at,
|
|
272
|
+
compliance: data.compliance ?? void 0
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function featuredSectionFromResponse(data) {
|
|
276
|
+
const models = data.models ?? [];
|
|
277
|
+
return {
|
|
278
|
+
title: data.title ?? "",
|
|
279
|
+
key: data.key ?? "",
|
|
280
|
+
models: models.map(modelInfoFromResponse)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function modelsResponseFromResponse(data) {
|
|
284
|
+
const models = data.models ?? [];
|
|
285
|
+
const featuredRaw = data.featured;
|
|
286
|
+
return {
|
|
287
|
+
models: models.map(modelInfoFromResponse),
|
|
288
|
+
totalCount: data.total_count ?? 0,
|
|
289
|
+
lastUpdated: data.last_updated,
|
|
290
|
+
featured: featuredRaw ? featuredRaw.map(featuredSectionFromResponse) : void 0
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
var Models = class {
|
|
294
|
+
constructor(_client) {
|
|
295
|
+
this._client = _client;
|
|
296
|
+
}
|
|
297
|
+
async list(options = {}) {
|
|
298
|
+
const {
|
|
299
|
+
provider,
|
|
300
|
+
mediaType,
|
|
301
|
+
capability,
|
|
302
|
+
search,
|
|
303
|
+
sort,
|
|
304
|
+
limit = 100,
|
|
305
|
+
featured = false
|
|
306
|
+
} = options;
|
|
307
|
+
const params = new URLSearchParams();
|
|
308
|
+
params.set("limit", String(limit));
|
|
309
|
+
if (provider) params.set("provider", provider);
|
|
310
|
+
if (mediaType) params.set("media_type", mediaType);
|
|
311
|
+
if (capability) params.set("capability", capability);
|
|
312
|
+
if (search) params.set("search", search);
|
|
313
|
+
if (sort) params.set("sort", sort);
|
|
314
|
+
if (featured) params.set("featured", "true");
|
|
315
|
+
const data = await this._client._request("GET", `/models?${params.toString()}`);
|
|
316
|
+
return modelsResponseFromResponse(data);
|
|
317
|
+
}
|
|
318
|
+
async get(modelId) {
|
|
319
|
+
const data = await this._client._request("GET", `/models/${encodeURIComponent(modelId)}`);
|
|
320
|
+
return modelInfoFromResponse(data);
|
|
321
|
+
}
|
|
322
|
+
async search(query, limit = 20) {
|
|
323
|
+
return this.list({ search: query, limit });
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/resources/providers.ts
|
|
328
|
+
function providerKeyInfoFromResponse(data) {
|
|
329
|
+
return {
|
|
330
|
+
provider: data.provider ?? "",
|
|
331
|
+
validated: Boolean(data.validated),
|
|
332
|
+
validatedAt: data.validated_at,
|
|
333
|
+
maskedKey: data.masked_key
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function providerKeysResponseFromResponse(data) {
|
|
337
|
+
const keys = data.keys ?? [];
|
|
338
|
+
return { keys: keys.map(providerKeyInfoFromResponse) };
|
|
339
|
+
}
|
|
340
|
+
function providerValidationResultFromResponse(data) {
|
|
341
|
+
return {
|
|
342
|
+
valid: Boolean(data.valid),
|
|
343
|
+
message: String(data.message ?? "")
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function providerBalanceItemFromResponse(data) {
|
|
347
|
+
return {
|
|
348
|
+
provider: data.provider ?? "",
|
|
349
|
+
configured: Boolean(data.configured),
|
|
350
|
+
available: Boolean(data.available),
|
|
351
|
+
limit: data.limit,
|
|
352
|
+
remaining: data.remaining,
|
|
353
|
+
currency: data.currency,
|
|
354
|
+
message: data.message
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function providerBalancesResponseFromResponse(data) {
|
|
358
|
+
const balances = data.balances ?? [];
|
|
359
|
+
return { balances: balances.map(providerBalanceItemFromResponse) };
|
|
360
|
+
}
|
|
361
|
+
var Providers = class {
|
|
362
|
+
constructor(_client) {
|
|
363
|
+
this._client = _client;
|
|
364
|
+
}
|
|
365
|
+
async listKeys() {
|
|
366
|
+
const data = await this._client._request("GET", "/providers/keys");
|
|
367
|
+
return providerKeysResponseFromResponse(data);
|
|
368
|
+
}
|
|
369
|
+
async setKey(provider, apiKey) {
|
|
370
|
+
const data = await this._client._request("PUT", "/providers/keys", {
|
|
371
|
+
body: JSON.stringify({ provider, api_key: apiKey })
|
|
372
|
+
});
|
|
373
|
+
return providerKeyInfoFromResponse(data);
|
|
374
|
+
}
|
|
375
|
+
async deleteKey(provider) {
|
|
376
|
+
return await this._client._request("DELETE", `/providers/keys/${encodeURIComponent(provider)}`);
|
|
377
|
+
}
|
|
378
|
+
async validateKey(provider, apiKey) {
|
|
379
|
+
const data = await this._client._request("POST", "/providers/validate", {
|
|
380
|
+
body: JSON.stringify({ provider, api_key: apiKey })
|
|
381
|
+
});
|
|
382
|
+
return providerValidationResultFromResponse(data);
|
|
383
|
+
}
|
|
384
|
+
async balances() {
|
|
385
|
+
const data = await this._client._request("GET", "/providers/balances");
|
|
386
|
+
return providerBalancesResponseFromResponse(data);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// src/resources/requests.ts
|
|
391
|
+
function requestStatusFromResponse(data) {
|
|
392
|
+
return {
|
|
393
|
+
requestId: data.request_id ?? data.requestId,
|
|
394
|
+
status: data.status ?? "pending",
|
|
395
|
+
mediaType: data.media_type ?? "video",
|
|
396
|
+
provider: data.provider ?? "",
|
|
397
|
+
model: data.model ?? "",
|
|
398
|
+
outputUrl: data.output_url ?? null,
|
|
399
|
+
errorMessage: data.error_message ?? null,
|
|
400
|
+
createdAt: data.created_at ?? null,
|
|
401
|
+
completedAt: data.completed_at ?? null
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
var Requests = class {
|
|
405
|
+
constructor(_client) {
|
|
406
|
+
this._client = _client;
|
|
407
|
+
}
|
|
408
|
+
/** Get status of an async generation request (video/image). */
|
|
409
|
+
async get(requestId) {
|
|
410
|
+
const data = await this._client._request("GET", `/requests/${encodeURIComponent(requestId)}`);
|
|
411
|
+
return requestStatusFromResponse(data);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// src/resources/usage.ts
|
|
416
|
+
function byProviderFromBreakdown(providerBreakdown) {
|
|
417
|
+
if (!providerBreakdown || typeof providerBreakdown !== "object") return {};
|
|
418
|
+
const out = {};
|
|
419
|
+
for (const [provider, stats] of Object.entries(providerBreakdown)) {
|
|
420
|
+
const s = stats;
|
|
421
|
+
const requests = s?.requests ?? 0;
|
|
422
|
+
if (provider) out[provider] = requests;
|
|
423
|
+
}
|
|
424
|
+
return out;
|
|
425
|
+
}
|
|
426
|
+
function usageSummaryFromResponse(data) {
|
|
427
|
+
const totalRequests = data.total_requests ?? 0;
|
|
428
|
+
const cachedRequests = data.cached_requests ?? 0;
|
|
429
|
+
const cacheHitRateFromApi = data.cache_hit_rate;
|
|
430
|
+
const cacheHitRate = typeof cacheHitRateFromApi === "number" ? cacheHitRateFromApi : totalRequests === 0 ? 0 : cachedRequests / totalRequests * 100;
|
|
431
|
+
const providerBreakdown = data.provider_breakdown;
|
|
432
|
+
const history = data.history;
|
|
433
|
+
return {
|
|
434
|
+
totalRequests,
|
|
435
|
+
successfulRequests: data.successful_requests ?? 0,
|
|
436
|
+
failedRequests: data.failed_requests ?? 0,
|
|
437
|
+
cachedRequests,
|
|
438
|
+
totalProviderCost: data.total_provider_effective_cost ?? data.total_provider_cost ?? 0,
|
|
439
|
+
totalBilledCost: data.total_billed_cost ?? data.total_cost_usd ?? 0,
|
|
440
|
+
totalSavings: data.total_savings ?? 0,
|
|
441
|
+
byProvider: data.by_provider ?? byProviderFromBreakdown(providerBreakdown),
|
|
442
|
+
byModel: data.by_model ?? {},
|
|
443
|
+
period: data.period ?? "month",
|
|
444
|
+
periodStart: parseDateTime(data.period_start),
|
|
445
|
+
periodEnd: parseDateTime(data.period_end),
|
|
446
|
+
cacheHitRate,
|
|
447
|
+
avgLatency: data.avg_latency,
|
|
448
|
+
providerBreakdown,
|
|
449
|
+
history: Array.isArray(history) ? history : void 0
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
var Usage = class {
|
|
453
|
+
constructor(_client) {
|
|
454
|
+
this._client = _client;
|
|
455
|
+
}
|
|
456
|
+
async get(period = "month") {
|
|
457
|
+
const data = await this._client._request("GET", `/usage?period=${encodeURIComponent(period)}`);
|
|
458
|
+
return usageSummaryFromResponse(data);
|
|
459
|
+
}
|
|
460
|
+
async logs(options = {}) {
|
|
461
|
+
const { limit = 50, offset = 0 } = options;
|
|
462
|
+
const params = new URLSearchParams();
|
|
463
|
+
params.set("limit", String(limit));
|
|
464
|
+
params.set("offset", String(offset));
|
|
465
|
+
const data = await this._client._request("GET", `/usage/logs?${params.toString()}`);
|
|
466
|
+
if (Array.isArray(data)) return data;
|
|
467
|
+
return data.logs ?? [];
|
|
468
|
+
}
|
|
469
|
+
async dashboard(period = "month") {
|
|
470
|
+
return await this._client._request(
|
|
471
|
+
"GET",
|
|
472
|
+
`/dashboard?period=${encodeURIComponent(period)}`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Track a download event for a request (POST /usage/downloads).
|
|
477
|
+
* Increments download_count and adds timestamp to downloaded_at.
|
|
478
|
+
*/
|
|
479
|
+
async trackDownload(requestId, outputUrl, options) {
|
|
480
|
+
const body = { request_id: requestId, output_url: outputUrl };
|
|
481
|
+
if (options?.mediaType) body.media_type = options.mediaType;
|
|
482
|
+
const data = await this._client._request("POST", "/usage/downloads", {
|
|
483
|
+
body: JSON.stringify(body)
|
|
484
|
+
});
|
|
485
|
+
return {
|
|
486
|
+
success: Boolean(data.success),
|
|
487
|
+
request_id: data.request_id ?? requestId,
|
|
488
|
+
download_count: data.download_count ?? 0
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// src/resources/videos.ts
|
|
494
|
+
function blobToDataUrl(blob) {
|
|
495
|
+
if (typeof FileReader !== "undefined") {
|
|
496
|
+
return new Promise((resolve, reject) => {
|
|
497
|
+
const reader = new FileReader();
|
|
498
|
+
reader.onload = () => {
|
|
499
|
+
if (typeof reader.result === "string") resolve(reader.result);
|
|
500
|
+
else reject(new Error("FileReader did not return a string"));
|
|
501
|
+
};
|
|
502
|
+
reader.onerror = () => reject(reader.error);
|
|
503
|
+
reader.readAsDataURL(blob);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
return (async () => {
|
|
507
|
+
const ab = await blob.arrayBuffer();
|
|
508
|
+
const mime = blob.type || "image/jpeg";
|
|
509
|
+
const NodeBuffer = typeof globalThis !== "undefined" && globalThis.Buffer;
|
|
510
|
+
const base64 = NodeBuffer ? NodeBuffer.from(ab).toString("base64") : btoa(String.fromCharCode(...new Uint8Array(ab)));
|
|
511
|
+
return `data:${mime};base64,${base64}`;
|
|
512
|
+
})();
|
|
513
|
+
}
|
|
514
|
+
var VIDEO_MODEL_PRESETS = {
|
|
515
|
+
fal: "fal-ai/veo3",
|
|
516
|
+
replicate: "replicate/lucataco/cogvideox-5b",
|
|
517
|
+
runway: "runway/gen4_turbo"
|
|
518
|
+
};
|
|
519
|
+
function videoResultFromResponse(data) {
|
|
520
|
+
const cacheSource = data.cache_source;
|
|
521
|
+
return {
|
|
522
|
+
id: data.id,
|
|
523
|
+
status: data.status,
|
|
524
|
+
videoUrl: data.video_url ?? null,
|
|
525
|
+
model: data.model,
|
|
526
|
+
provider: data.provider,
|
|
527
|
+
cost: data.cost ?? 0,
|
|
528
|
+
cacheHit: data.cache_hit ?? false,
|
|
529
|
+
latencyMs: data.latency_ms ?? null,
|
|
530
|
+
savedAmount: data.saved_amount,
|
|
531
|
+
marginPercent: data.margin_percent,
|
|
532
|
+
cacheSource: cacheSource === "exact" || cacheSource === "semantic" ? cacheSource : void 0,
|
|
533
|
+
createdAt: parseDateTime(data.created_at)
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
var Videos = class {
|
|
537
|
+
constructor(_client) {
|
|
538
|
+
this._client = _client;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Generate a video from a prompt (and optional image for img2vid).
|
|
542
|
+
* Supports managed mode and BYOK (set falKey/replicateKey/runwayKey on client or via proxy env).
|
|
543
|
+
* Use preferAsync: true to avoid 502 when video takes longer than proxy/Cloudflare timeout.
|
|
544
|
+
*/
|
|
545
|
+
async generate(model, prompt, options = {}) {
|
|
546
|
+
const {
|
|
547
|
+
imageUrl,
|
|
548
|
+
imageFile,
|
|
549
|
+
durationSeconds = 5,
|
|
550
|
+
fps,
|
|
551
|
+
webhookUrl,
|
|
552
|
+
callbackId,
|
|
553
|
+
skipGcsUpload,
|
|
554
|
+
preferAsync,
|
|
555
|
+
params
|
|
556
|
+
} = options;
|
|
557
|
+
let resolvedImageUrl = null;
|
|
558
|
+
if (imageFile) {
|
|
559
|
+
resolvedImageUrl = await blobToDataUrl(imageFile);
|
|
560
|
+
} else if (imageUrl) {
|
|
561
|
+
resolvedImageUrl = imageUrl;
|
|
562
|
+
}
|
|
563
|
+
const payload = {
|
|
564
|
+
model,
|
|
565
|
+
prompt,
|
|
566
|
+
duration_seconds: durationSeconds
|
|
567
|
+
};
|
|
568
|
+
if (resolvedImageUrl) payload.image_url = resolvedImageUrl;
|
|
569
|
+
if (fps != null) payload.fps = fps;
|
|
570
|
+
if (webhookUrl) payload.webhook_url = webhookUrl;
|
|
571
|
+
if (callbackId) payload.callback_id = callbackId;
|
|
572
|
+
if (skipGcsUpload === true) payload.skip_gcs_upload = true;
|
|
573
|
+
if (params && Object.keys(params).length > 0) Object.assign(payload, params);
|
|
574
|
+
const headers = {};
|
|
575
|
+
if (preferAsync === true) headers["Prefer"] = "respond-async";
|
|
576
|
+
const data = await this._client._request("POST", "/videos/generate", {
|
|
577
|
+
body: JSON.stringify(payload),
|
|
578
|
+
headers: Object.keys(headers).length > 0 ? headers : void 0
|
|
579
|
+
});
|
|
580
|
+
if ("request_id" in data && typeof data.request_id === "string" && !("video_url" in data)) {
|
|
581
|
+
return { requestId: data.request_id, status: "accepted" };
|
|
582
|
+
}
|
|
583
|
+
return videoResultFromResponse(data);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
// src/client.ts
|
|
588
|
+
var DEFAULT_BASE_URL = "https://visgateai.com/api/v1";
|
|
589
|
+
var DEFAULT_TIMEOUT = 12e4;
|
|
590
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
591
|
+
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
592
|
+
var SDK_VERSION = "0.2.2";
|
|
593
|
+
function getVersion() {
|
|
594
|
+
try {
|
|
595
|
+
if (typeof __VERSION__ !== "undefined") return __VERSION__;
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
return SDK_VERSION;
|
|
599
|
+
}
|
|
600
|
+
function buildHeaders(apiKey, options) {
|
|
601
|
+
const headers = {
|
|
602
|
+
"Content-Type": "application/json",
|
|
603
|
+
"User-Agent": `visgate-client/${getVersion()}`
|
|
604
|
+
};
|
|
605
|
+
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
606
|
+
if (options.falKey) headers["X-Fal-Key"] = options.falKey;
|
|
607
|
+
if (options.replicateKey) headers["X-Replicate-Key"] = options.replicateKey;
|
|
608
|
+
if (options.runwayKey) headers["X-Runway-Key"] = options.runwayKey;
|
|
609
|
+
return headers;
|
|
610
|
+
}
|
|
611
|
+
function backoff(attempt) {
|
|
612
|
+
return Math.min(0.5 * Math.pow(2, attempt), 8) * 1e3;
|
|
613
|
+
}
|
|
614
|
+
function retryWaitFromResponse(response, attempt) {
|
|
615
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
616
|
+
if (retryAfter) {
|
|
617
|
+
const sec = parseInt(retryAfter, 10);
|
|
618
|
+
if (!isNaN(sec)) return sec * 1e3;
|
|
619
|
+
}
|
|
620
|
+
return backoff(attempt);
|
|
621
|
+
}
|
|
622
|
+
async function sleep(ms) {
|
|
623
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
624
|
+
}
|
|
625
|
+
async function handleResponse(response) {
|
|
626
|
+
const status = response.status;
|
|
627
|
+
if (status < 400) {
|
|
628
|
+
const text2 = await response.text();
|
|
629
|
+
if (!text2) return {};
|
|
630
|
+
try {
|
|
631
|
+
return JSON.parse(text2);
|
|
632
|
+
} catch {
|
|
633
|
+
return {};
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
let body = {};
|
|
637
|
+
const text = await response.text();
|
|
638
|
+
try {
|
|
639
|
+
if (text) body = JSON.parse(text);
|
|
640
|
+
} catch {
|
|
641
|
+
}
|
|
642
|
+
if (status === 401) {
|
|
643
|
+
throw new AuthenticationError("Invalid or missing API key");
|
|
644
|
+
}
|
|
645
|
+
if (status === 422) {
|
|
646
|
+
const message2 = body.message ?? text ?? "Validation failed";
|
|
647
|
+
const field = body.details?.field;
|
|
648
|
+
throw new ValidationError(message2, field);
|
|
649
|
+
}
|
|
650
|
+
if (status === 429) {
|
|
651
|
+
const retryAfterHeader = response.headers.get("Retry-After");
|
|
652
|
+
const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
|
|
653
|
+
throw new RateLimitError("Rate limit exceeded", Number.isNaN(retryAfter) ? void 0 : retryAfter);
|
|
654
|
+
}
|
|
655
|
+
const errorCode = body.error ?? `HTTP_${status}`;
|
|
656
|
+
const message = body.message ?? text ?? "Request failed";
|
|
657
|
+
if (String(errorCode).includes("PROVIDER")) {
|
|
658
|
+
const provider = body.details?.provider ?? "unknown";
|
|
659
|
+
throw new ProviderError(message, provider);
|
|
660
|
+
}
|
|
661
|
+
throw new VisgateError(message, errorCode, body.details ?? {}, status);
|
|
662
|
+
}
|
|
663
|
+
var Client = class {
|
|
664
|
+
constructor(options = {}) {
|
|
665
|
+
this.apiKey = options.proxyUrl ? options.apiKey != null && options.apiKey !== "" ? resolveApiKey(options.apiKey) : null : resolveApiKey(options.apiKey);
|
|
666
|
+
this.baseUrl = (options.proxyUrl ?? options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
667
|
+
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
668
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
669
|
+
this.headers = buildHeaders(this.apiKey, options.proxyUrl ? {} : {
|
|
670
|
+
falKey: options.falKey,
|
|
671
|
+
replicateKey: options.replicateKey,
|
|
672
|
+
runwayKey: options.runwayKey
|
|
673
|
+
});
|
|
674
|
+
this.images = new Images(this);
|
|
675
|
+
this.models = new Models(this);
|
|
676
|
+
this.videos = new Videos(this);
|
|
677
|
+
this.requests = new Requests(this);
|
|
678
|
+
this.usage = new Usage(this);
|
|
679
|
+
this.providers = new Providers(this);
|
|
680
|
+
this.billing = new Billing(this);
|
|
681
|
+
this._generate = new Generate(this);
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Generate an image with a single call.
|
|
685
|
+
*/
|
|
686
|
+
generate(prompt, options) {
|
|
687
|
+
return this._generate.__call__(prompt, options?.model, options?.params);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Check API health status.
|
|
691
|
+
*/
|
|
692
|
+
health() {
|
|
693
|
+
return this._request("GET", "/health");
|
|
694
|
+
}
|
|
695
|
+
async _request(method, path, init = {}) {
|
|
696
|
+
const url = `${this.baseUrl}${path}`;
|
|
697
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
698
|
+
const controller = new AbortController();
|
|
699
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
700
|
+
try {
|
|
701
|
+
const fetchInit = {
|
|
702
|
+
method,
|
|
703
|
+
...init,
|
|
704
|
+
headers: { ...this.headers, ...init.headers },
|
|
705
|
+
signal: controller.signal
|
|
706
|
+
};
|
|
707
|
+
if (method !== "GET" && init.body !== void 0) {
|
|
708
|
+
fetchInit.body = init.body;
|
|
709
|
+
}
|
|
710
|
+
const response = await fetch(url, fetchInit);
|
|
711
|
+
clearTimeout(timeoutId);
|
|
712
|
+
if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
|
|
713
|
+
const waitMs = retryWaitFromResponse(response, attempt);
|
|
714
|
+
await sleep(waitMs);
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
return await handleResponse(response);
|
|
718
|
+
} catch (err) {
|
|
719
|
+
clearTimeout(timeoutId);
|
|
720
|
+
if (err instanceof AuthenticationError && this.apiKey === null) {
|
|
721
|
+
throw new AuthenticationError("Proxy returned 401. Check VISGATE_API_KEY on the server (proxy).");
|
|
722
|
+
}
|
|
723
|
+
if (err instanceof VisgateError && !(err instanceof RateLimitError)) throw err;
|
|
724
|
+
if (attempt >= this.maxRetries) {
|
|
725
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
726
|
+
throw new VisgateTimeoutError(`Request timed out after ${this.maxRetries + 1} attempt(s)`);
|
|
727
|
+
}
|
|
728
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
729
|
+
throw new VisgateConnectionError(`Connection failed: ${err.message}`);
|
|
730
|
+
}
|
|
731
|
+
throw err;
|
|
732
|
+
}
|
|
733
|
+
const waitMs = backoff(attempt);
|
|
734
|
+
await sleep(waitMs);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
throw new VisgateError(
|
|
738
|
+
`Request failed after ${this.maxRetries + 1} attempts`,
|
|
739
|
+
"VISGATE_ERROR",
|
|
740
|
+
{},
|
|
741
|
+
void 0
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
/** No-op for API compatibility (JS has no connection pool to close). */
|
|
745
|
+
close() {
|
|
746
|
+
}
|
|
747
|
+
toString() {
|
|
748
|
+
return `Client(baseUrl=${JSON.stringify(this.baseUrl)})`;
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
var AsyncClient = class extends Client {
|
|
752
|
+
constructor(options = {}) {
|
|
753
|
+
super(options);
|
|
754
|
+
}
|
|
755
|
+
toString() {
|
|
756
|
+
return `AsyncClient(baseUrl=${JSON.stringify(this.baseUrl)})`;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
// src/index.ts
|
|
761
|
+
var VERSION = "0.2.2";
|
|
762
|
+
|
|
763
|
+
export { AsyncClient, AuthenticationError, Billing, Client, Generate, Images, Models, ProviderError, Providers, RateLimitError, Requests, Usage, VERSION, VIDEO_MODEL_PRESETS, ValidationError, Videos, VisgateConnectionError, VisgateError, VisgateTimeoutError, billingInfoFromResponse, featuredSectionFromResponse, generateResultFromResponse, imageResultFromResponse, modelInfoFromResponse, modelPricingFromResponse, modelsResponseFromResponse, pricingResponseFromResponse, providerBalanceItemFromResponse, providerBalancesResponseFromResponse, providerKeyInfoFromResponse, providerKeysResponseFromResponse, providerValidationResultFromResponse, requestStatusFromResponse, usageSummaryFromResponse, videoResultFromResponse };
|
|
764
|
+
//# sourceMappingURL=index.js.map
|
|
765
|
+
//# sourceMappingURL=index.js.map
|