UNPKG

@subscribe.dev/client

Version:

JavaScript/TypeScript client for SubscribeDev API - A drop-in for AI generation across 100+ models with built-in billing and rate limiting

579 lines (578 loc) 19.9 kB
import D from "p-queue"; import C from "p-retry"; function U(f) { return f && f.__esModule && Object.prototype.hasOwnProperty.call(f, "default") ? f.default : f; } var P = { exports: {} }; (function(f) { var e = Object.prototype.hasOwnProperty, t = "~"; function s() { } Object.create && (s.prototype = /* @__PURE__ */ Object.create(null), new s().__proto__ || (t = !1)); function c(u, r, i) { this.fn = u, this.context = r, this.once = i || !1; } function o(u, r, i, a, g) { if (typeof i != "function") throw new TypeError("The listener must be a function"); var p = new c(i, a || u, g), d = t ? t + r : r; return u._events[d] ? u._events[d].fn ? u._events[d] = [u._events[d], p] : u._events[d].push(p) : (u._events[d] = p, u._eventsCount++), u; } function m(u, r) { --u._eventsCount === 0 ? u._events = new s() : delete u._events[r]; } function l() { this._events = new s(), this._eventsCount = 0; } l.prototype.eventNames = function() { var r = [], i, a; if (this._eventsCount === 0) return r; for (a in i = this._events) e.call(i, a) && r.push(t ? a.slice(1) : a); return Object.getOwnPropertySymbols ? r.concat(Object.getOwnPropertySymbols(i)) : r; }, l.prototype.listeners = function(r) { var i = t ? t + r : r, a = this._events[i]; if (!a) return []; if (a.fn) return [a.fn]; for (var g = 0, p = a.length, d = new Array(p); g < p; g++) d[g] = a[g].fn; return d; }, l.prototype.listenerCount = function(r) { var i = t ? t + r : r, a = this._events[i]; return a ? a.fn ? 1 : a.length : 0; }, l.prototype.emit = function(r, i, a, g, p, d) { var v = t ? t + r : r; if (!this._events[v]) return !1; var n = this._events[v], y = arguments.length, w, h; if (n.fn) { switch (n.once && this.removeListener(r, n.fn, void 0, !0), y) { case 1: return n.fn.call(n.context), !0; case 2: return n.fn.call(n.context, i), !0; case 3: return n.fn.call(n.context, i, a), !0; case 4: return n.fn.call(n.context, i, a, g), !0; case 5: return n.fn.call(n.context, i, a, g, p), !0; case 6: return n.fn.call(n.context, i, a, g, p, d), !0; } for (h = 1, w = new Array(y - 1); h < y; h++) w[h - 1] = arguments[h]; n.fn.apply(n.context, w); } else { var R = n.length, E; for (h = 0; h < R; h++) switch (n[h].once && this.removeListener(r, n[h].fn, void 0, !0), y) { case 1: n[h].fn.call(n[h].context); break; case 2: n[h].fn.call(n[h].context, i); break; case 3: n[h].fn.call(n[h].context, i, a); break; case 4: n[h].fn.call(n[h].context, i, a, g); break; default: if (!w) for (E = 1, w = new Array(y - 1); E < y; E++) w[E - 1] = arguments[E]; n[h].fn.apply(n[h].context, w); } } return !0; }, l.prototype.on = function(r, i, a) { return o(this, r, i, a, !1); }, l.prototype.once = function(r, i, a) { return o(this, r, i, a, !0); }, l.prototype.removeListener = function(r, i, a, g) { var p = t ? t + r : r; if (!this._events[p]) return this; if (!i) return m(this, p), this; var d = this._events[p]; if (d.fn) d.fn === i && (!g || d.once) && (!a || d.context === a) && m(this, p); else { for (var v = 0, n = [], y = d.length; v < y; v++) (d[v].fn !== i || g && !d[v].once || a && d[v].context !== a) && n.push(d[v]); n.length ? this._events[p] = n.length === 1 ? n[0] : n : m(this, p); } return this; }, l.prototype.removeAllListeners = function(r) { var i; return r ? (i = t ? t + r : r, this._events[i] && m(this, i)) : (this._events = new s(), this._eventsCount = 0), this; }, l.prototype.off = l.prototype.removeListener, l.prototype.addListener = l.prototype.on, l.prefixed = t, l.EventEmitter = l, f.exports = l; })(P); var k = P.exports; const q = /* @__PURE__ */ U(k); class b extends Error { constructor(e, t, s, c, o) { super(e), this.name = "SubscribeDevError", this.code = t, this.statusCode = s, this.details = c, this.requestId = o; } } class A extends b { constructor(e, t, s, c) { super(e, "INSUFFICIENT_BALANCE", 402, { remainingCredits: t, requiredCredits: s }, c), this.name = "InsufficientBalanceError"; } } class x extends b { constructor(e, t, s, c) { super(e, "RATE_LIMIT_EXCEEDED", 429, { resetTime: t, retryAfter: s }, c), this.name = "RateLimitError", this.resetTime = t, this.retryAfter = s; } } class O extends b { constructor(e, t) { super(e, "AUTHENTICATION_FAILED", 401, void 0, t), this.name = "AuthenticationError"; } } class L extends b { constructor(e, t) { super(e, "ACCESS_DENIED", 403, void 0, t), this.name = "AccessDeniedError"; } } class I extends b { constructor(e, t) { super(e, "NOT_FOUND", 404, void 0, t), this.name = "NotFoundError"; } } class S extends b { constructor(e, t, s) { super(e, "VALIDATION_ERROR", 400, t, s), this.name = "ValidationError"; } } class _ extends b { constructor(e, t) { super(e, "USER_UNSUBSCRIBED", 402, void 0, t), this.name = "UnsubscribedError"; } } class T extends q { constructor(e) { if (super(), this.config = { baseUrl: e.baseUrl || "https://subscribedev.volterapp-dev.com", timeout: e.timeout || 3e5, // 5 minutes maxRetries: e.maxRetries || 2, debug: e.debug || !1, ...e }, this.queue = new D({ concurrency: 10, // Max concurrent requests interval: 6e4, // 1 minute intervalCap: 60 // Max requests per minute }), this.fetch = globalThis.fetch?.bind(globalThis), !this.fetch) throw new Error("fetch is not available. Please provide a fetch polyfill."); this.debug("SubscribeDevClient initialized", { baseUrl: this.config.baseUrl }); } get pricingUrl() { return `${this.config.baseUrl}/subscribe`; } /** * Run a prediction (main method matching Replicate.js API) * Note: Now returns completed predictions directly - no polling needed */ async run(e, t) { const { input: s, response_format: c } = t; this.debug("Starting prediction", { model: e, input: s, response_format: c }); const o = { model: e, input: t.input }; t.response_format && (o.response_format = t.response_format); const m = await this.makeRequest("POST", "/v1/run", o); return this.debug("Prediction completed", { hasOutput: !!m.output }), this.emit("usage-changed", { operation: "run", model: e }), m; } /** * Create a new prediction */ async createPrediction(e, t) { return this.queue.add(async () => await C( async () => { const s = { model: e, input: t.input }; t.response_format && (s.response_format = t.response_format); const c = await this.makeRequest("POST", "/v1/run", s); this.emit("usage-changed", { operation: "createPrediction", model: e }); const o = (/* @__PURE__ */ new Date()).toISOString(); return { id: `pred_${Math.random().toString(36).substring(2, 15)}`, created_at: o, started_at: o, completed_at: o, status: "succeeded", input: t.input, output: c.output, urls: { get: `${this.config.baseUrl}/v1/predictions/pred_${Math.random().toString(36).substring(2, 15)}` } }; }, { retries: this.config.maxRetries, onFailedAttempt: (s) => { this.debug("Prediction creation failed, retrying", { attempt: s.attemptNumber, error: s.message }); }, // @ts-ignore: Don't retry on UnsubscribedError shouldRetry: (s) => !(s instanceof _), // Reduce retry delay to prevent timeouts factor: 1.5, minTimeout: 500, maxTimeout: 2e3 } )); } /** * Get prediction status * @deprecated Since predictions now complete synchronously, this is mainly for historical lookups */ async getPrediction(e) { return console.warn("[DEPRECATED] getPrediction() is deprecated. Predictions now complete synchronously in run()."), this.queue.add(async () => { const t = await this.makeRequest("GET", `/v1/predictions/${e}`); return this.transformResponse(t); }); } /** * Cancel a prediction */ async cancelPrediction(e) { return this.queue.add(async () => { const t = await this.makeRequest("POST", `/v1/predictions/${e}/cancel`); return this.transformResponse(t); }); } /** * Wait for prediction completion with optional progress callback * @deprecated Since predictions now complete synchronously, this method is no longer needed */ async waitForPrediction(e, t) { console.warn("[DEPRECATED] waitForPrediction() is deprecated. Predictions now complete synchronously in run()."), this.debug("Waiting for prediction completion", { id: e }); let s = 0; const c = 1800; for (; s < c; ) { const o = await this.getPrediction(e); if (t && t(o), ["succeeded", "failed", "canceled"].includes(o.status)) return this.debug("Prediction completed", { id: e, status: o.status }), o; const m = Math.min(1e3 + s * 100, 5e3); await new Promise((l) => setTimeout(l, m)), s++; } throw new b( "Prediction timeout - exceeded maximum wait time", "PREDICTION_TIMEOUT", 408 ); } /** * Get account balance information */ async getBalance() { const t = (await this.makeRequest("GET", "/v1/subscriptions/usage")).credits?.userBalance; return { allocatedCredits: t?.allocatedCredits || 0, usedCredits: t?.usedCredits || 0, remainingCredits: t?.remainingCredits || 0 }; } /** * Get transaction history */ async getTransactions(e = {}) { const t = new URLSearchParams(); e.limit && t.set("limit", e.limit.toString()), e.offset && t.set("offset", e.offset.toString()), e.status && t.set("status", e.status), e.model && t.set("model", e.model), e.startDate && t.set("start_date", e.startDate), e.endDate && t.set("end_date", e.endDate); const s = `/v1/transactions${t.toString() ? "?" + t.toString() : ""}`; return this.makeRequest("GET", s); } /** * Get rate limit information */ async getRateLimits() { return this.makeRequest("GET", "/v1/rate-limits"); } /** * Get user storage data using auth context */ async getStorage(e = {}) { const t = new URLSearchParams(); e.appVersion && t.set("appVersion", e.appVersion); const s = `/v1/storage${t.toString() ? "?" + t.toString() : ""}`; return this.makeRequest("GET", s); } /** * Update user storage data using auth context */ async setStorage(e, t = {}) { const s = new URLSearchParams(); t.appVersion && s.set("appVersion", t.appVersion); const c = `/v1/storage${s.toString() ? "?" + s.toString() : ""}`; return this.makeRequest("PUT", c, { sessionData: e }); } /** * Delete user storage data using auth context */ async deleteStorage(e = {}) { const t = new URLSearchParams(); e.appVersion && t.set("appVersion", e.appVersion); const s = `/v1/storage${t.toString() ? "?" + t.toString() : ""}`; await this.makeRequest("DELETE", s); } /** * Browser convenience method: Persist session data locally and sync to server * Automatically saves the entire session object to localStorage and server */ async persistSessionData(e = {}) { if (typeof globalThis < "u" && typeof globalThis.localStorage > "u") throw new Error("persistSessionData is only available in browser environments"); const t = await this.getStorage(e), s = `subscribeDev_session_${this.config.apiKey.slice(-8)}`; return globalThis.localStorage.setItem(s, JSON.stringify(t.sessionData)), t; } /** * Browser convenience method: Get session data from server and update localStorage * Always fetches from server to ensure data is synchronized */ async getSessionData(e = {}) { const s = (await this.getStorage(e)).sessionData || {}; if (typeof globalThis < "u" && typeof globalThis.localStorage < "u") { const c = `subscribeDev_session_${this.config.apiKey.slice(-8)}`; globalThis.localStorage.setItem(c, JSON.stringify(s)); } return s; } /** * Get user's subscription status using userKey authentication * Requires userKey to be provided in client configuration */ async getSubscriptionStatus() { if (console.log("[SubscribeDevClient] getSubscriptionStatus called"), !this.config.userKey) throw console.log("[SubscribeDevClient] No userKey provided, throwing ValidationError"), new S( "User key is required to get subscription status. Provide userKey in client configuration.", { method: "getSubscriptionStatus" } ); console.log("[SubscribeDevClient] Making request to /v1/subscriptions/plan with userKey:", this.config.userKey.substring(0, 10) + "..."); try { const e = await this.makeRequest("GET", "/v1/subscriptions/plan"); console.log("[SubscribeDevClient] Received response:", { hasUserPlan: !!e.userPlan, planId: e.userPlan?.planId, status: e.userPlan?.status, hasPlanDetails: !!e.userPlan?.plan }); const t = e.userPlan; if (!t || t.status === "none" || !t.plan) return console.log("[SubscribeDevClient] User has no active subscription, returning none status"), { hasActiveSubscription: !1, status: t?.status || "none" }; const s = { hasActiveSubscription: t.status === "active", plan: t.plan ? { id: t.plan.id || t.planId, name: t.plan.name, price: t.plan.price, tokenLimit: t.plan.tokenLimit || t.plan.token_limit, subtitle: t.plan.subtitle, description: t.plan.description, features: t.plan.features } : void 0, status: t.status, startedAt: t.startedAt, endsAt: t.endsAt }; return console.log("[SubscribeDevClient] Returning subscription status:", { hasActiveSubscription: s.hasActiveSubscription, status: s.status, planName: s.plan?.name }), s; } catch (e) { throw console.log("[SubscribeDevClient] Error in getSubscriptionStatus:", { errorType: e.constructor.name, message: e.message }), e; } } /** * Get available subscription plans for the project */ async getSubscriptionPlans() { return this.makeRequest("GET", "/v1/subscriptions/status"); } /** * Create a checkout session for subscription payment * Requires userKey to be provided in client configuration */ async createCheckoutSession(e) { if (!this.config.userKey) throw new S( "User key is required to create checkout session. Provide userKey in client configuration.", { method: "createCheckoutSession" } ); return this.makeRequest("POST", "/v1/subscriptions/checkout", e); } /** * Sign up for the free plan * Requires userKey to be provided in client configuration */ async signupForFree(e) { if (!this.config.userKey) throw new S( "User key is required to sign up for free plan. Provide userKey in client configuration.", { method: "signupForFree" } ); return this.makeRequest("POST", "/v1/subscriptions/free-signup", e); } /** * Cancel user subscription * Requires userKey to be provided in client configuration */ async cancelSubscription(e, t = !0) { if (!this.config.userKey) throw new S( "User key is required to cancel subscription. Provide userKey in client configuration.", { method: "cancelSubscription" } ); return this.makeRequest("POST", `/v1/subscriptions/cancel/${e}`, { cancelAtPeriodEnd: t }); } /** * Get comprehensive usage limits and current consumption * Requires userKey to be provided in client configuration */ async getUsageLimits() { if (!this.config.userKey) throw new S( "User key is required to get usage limits. Provide userKey in client configuration.", { method: "getUsageLimits" } ); return this.makeRequest("GET", "/v1/subscriptions/usage"); } /** * Get comprehensive usage information including credits, rate limits, and consumption * Alias for getUsageLimits() - Requires userKey to be provided in client configuration */ async getUsage() { return this.getUsageLimits(); } /** * Make authenticated HTTP request to SubscribeDev API */ async makeRequest(e, t, s) { const c = `${this.config.baseUrl}${t}`, o = { Authorization: `Bearer ${this.config.apiKey}`, "Content-Type": "application/json", "User-Agent": "@subscribe.dev/client/0.1.0" }; this.config.userKey && (o["X-User-Token"] = this.config.userKey); const m = { method: e, headers: o, signal: AbortSignal.timeout(this.config.timeout) }; s && ["POST", "PUT", "PATCH"].includes(e) && (m.body = JSON.stringify(s)), this.debug("Making request", { method: e, url: c, headers: { ...o, Authorization: "[redacted]" } }); const l = await this.fetch(c, m), u = await l.text(); let r; try { r = u ? JSON.parse(u) : null; } catch { r = { error: { message: u } }; } return l.ok || this.handleErrorResponse(l, r), this.debug("Request successful", { status: l.status, data: r }), r; } /** * Handle error responses and throw appropriate error types */ handleErrorResponse(e, t) { const s = e.headers.get("x-request-id") || void 0, c = t?.error || {}, o = c.message || `HTTP ${e.status}`; switch (e.status) { case 400: throw new S(o, c.details, s); case 401: throw new O(o, s); case 402: throw c.code === "USER_UNSUBSCRIBED" || o.toLowerCase().includes("unsubscribed") || o.toLowerCase().includes("subscription") ? new _(o, s) : new A( o, c.details?.remainingCredits, c.details?.requiredCredits, s ); case 403: throw new L(o, s); case 404: throw new I(o, s); case 429: throw new x( o, c.details?.resetTime, c.details?.retryAfter, s ); default: throw new b( o, c.code || "UNKNOWN_ERROR", e.status, c.details, s ); } } /** * Transform API response to match Replicate.js format */ transformResponse(e) { return { id: e.id, version: e.version, created_at: e.created_at, started_at: e.started_at, completed_at: e.completed_at, status: e.status, input: e.input, output: e.output, error: e.error, logs: e.logs, metrics: e.metrics, // URLs are no longer included in completed predictions (use output field instead) urls: e.urls || void 0 }; } /** * Debug logging */ debug(e, t) { this.config.debug && console.log(`[SubscribeDevClient] ${e}`, t || ""); } /** * Static method to create client (for convenience) */ static create(e) { return new T(e); } } export { L as AccessDeniedError, O as AuthenticationError, A as InsufficientBalanceError, I as NotFoundError, x as RateLimitError, T as SubscribeDevClient, b as SubscribeDevError, _ as UnsubscribedError, S as ValidationError }; //# sourceMappingURL=index.js.map