autumn-js
Version:
Autumn JS Library
256 lines (252 loc) • 6.64 kB
JavaScript
"use client";
import {
createEntityMethod,
deleteEntityMethod,
getEntityMethod
} from "./chunk-OYBOLAL5.mjs";
import {
attachMethod,
cancelMethod,
checkMethod,
checkoutMethod,
openBillingPortalMethod,
queryMethod,
setupPaymentMethod,
trackMethod
} from "./chunk-6EXNYWJX.mjs";
import {
listProductsMethod
} from "./chunk-V575SGOW.mjs";
import {
createCode,
redeemCode
} from "./chunk-SUENW2KS.mjs";
import {
logAuthError
} from "./chunk-JVCOY7ZL.mjs";
import {
logFetchError
} from "./chunk-CIBQ4BJU.mjs";
import {
AutumnError,
toContainerResult
} from "./chunk-QN2ALOVD.mjs";
import {
eventAggregateMethod,
eventListMethod
} from "./chunk-XDQLJ7QR.mjs";
import {
createCustomerMethod
} from "./chunk-WFFVOEUG.mjs";
// src/libraries/react/client/ReactAutumnClient.tsx
var AutumnClient = class {
backendUrl;
getBearerToken;
customerData;
includeCredentials;
prefix;
camelCase;
headers;
framework;
defaultReturnUrl;
suppressLogs;
constructor({
backendUrl,
getBearerToken,
customerData,
includeCredentials,
betterAuthUrl,
headers,
pathPrefix,
defaultReturnUrl,
suppressLogs
}) {
this.backendUrl = backendUrl;
this.getBearerToken = getBearerToken;
this.customerData = customerData;
this.includeCredentials = includeCredentials;
this.prefix = "/api/autumn";
let camelCase = false;
if (betterAuthUrl) {
this.prefix = "/api/auth/autumn";
this.backendUrl = betterAuthUrl;
camelCase = true;
}
const providedPrefix = pathPrefix;
if (providedPrefix) {
const normalized = `/${providedPrefix}`.replace(/\/+/g, "/").replace(/\/$/, "");
this.prefix = normalized;
}
this.headers = headers;
if (betterAuthUrl) camelCase = true;
this.camelCase = camelCase;
this.defaultReturnUrl = defaultReturnUrl;
this.suppressLogs = suppressLogs ?? false;
}
/**
* Detects if the backend supports CORS credentials by making an OPTIONS request
*/
async detectCors() {
if (this.prefix?.includes("/api/auth")) {
return { valid: true, includeCredentials: true };
}
const testEndpoint = `${this.backendUrl}${this.prefix}/cors`;
try {
await fetch(testEndpoint, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({})
});
return { valid: true, includeCredentials: true };
} catch (_) {
try {
await fetch(testEndpoint, {
method: "POST",
credentials: "omit",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({})
});
return { valid: true, includeCredentials: false };
} catch (_2) {
return { valid: false, includeCredentials: void 0 };
}
}
}
/**
* Automatically determines whether to include credentials based on CORS detection
*/
async shouldIncludeCredentials() {
if (this.includeCredentials !== void 0) {
return this.includeCredentials;
}
try {
const corsResult = await this.detectCors();
if (corsResult.valid) {
console.warn(
`[Autumn] Detected CORS credentials: ${corsResult.includeCredentials}`
);
console.warn(
`[Autumn] To disable this warning, you can set includeCredentials={${corsResult.includeCredentials ? "true" : "false"}} in <AutumnProvider />`
);
this.includeCredentials = corsResult.includeCredentials;
return corsResult.includeCredentials || false;
}
console.warn(`[Autumn] CORS detection failed, defaulting to false`);
return false;
} catch (error) {
console.error(`[Autumn] Error detecting CORS: ${error.message}`);
return false;
}
}
async getHeaders() {
let headers = {
"Content-Type": "application/json"
};
if (this.getBearerToken) {
try {
const token = await this.getBearerToken();
headers.Authorization = `Bearer ${token}`;
} catch (_) {
console.error(`Failed to call getToken() in AutumnProvider`);
}
}
if (this.headers) {
headers = { ...headers, ...this.headers };
}
return headers;
}
async handleFetch({
path,
method,
body
}) {
body = method === "POST" ? {
...body,
[this.camelCase ? "customerData" : "customer_data"]: this.customerData || void 0
} : void 0;
const includeCredentials = await this.shouldIncludeCredentials();
try {
const response = await fetch(`${this.backendUrl}${path}`, {
method,
body: body ? JSON.stringify(body) : void 0,
headers: await this.getHeaders(),
credentials: includeCredentials ? "include" : "omit"
});
const loggedError = this.suppressLogs ? false : await logAuthError(response);
return await toContainerResult({
response,
logger: console,
logError: !this.suppressLogs && !loggedError
});
} catch (error) {
if (!this.suppressLogs) {
logFetchError({
method,
backendUrl: this.backendUrl || "",
path,
error
});
}
return {
data: null,
error: new AutumnError({
message: error instanceof Error ? error.message : JSON.stringify(error),
code: "fetch_failed"
})
};
}
}
async post(path, body) {
return await this.handleFetch({
path,
method: "POST",
body
});
}
async get(path) {
return await this.handleFetch({
path,
method: "GET"
});
}
async delete(path) {
return await this.handleFetch({
path,
method: "DELETE"
});
}
async createCustomer(params) {
return await createCustomerMethod({
client: this,
params
});
}
attach = attachMethod.bind(this);
checkout = checkoutMethod.bind(this);
cancel = cancelMethod.bind(this);
check = checkMethod.bind(this);
track = trackMethod.bind(this);
openBillingPortal = openBillingPortalMethod.bind(this);
setupPayment = setupPaymentMethod.bind(this);
query = queryMethod.bind(this);
entities = {
create: createEntityMethod.bind(this),
get: getEntityMethod.bind(this),
delete: deleteEntityMethod.bind(this)
};
referrals = {
createCode: createCode.bind(this),
redeemCode: redeemCode.bind(this)
};
products = {
list: listProductsMethod.bind(this)
};
events = {
list: eventListMethod.bind(this),
aggregate: eventAggregateMethod.bind(this)
};
};
export {
AutumnClient
};