Initial project import
This commit is contained in:
394
node_modules/stripe/esm/utils.js
generated
vendored
Normal file
394
node_modules/stripe/esm/utils.js
generated
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
import { DEFAULT_BASE_ADDRESSES, } from './Types.js';
|
||||
const OPTIONS_KEYS = [
|
||||
'apiKey',
|
||||
'idempotencyKey',
|
||||
'stripeAccount',
|
||||
'apiVersion',
|
||||
'maxNetworkRetries',
|
||||
'timeout',
|
||||
'apiBase',
|
||||
'authenticator',
|
||||
'stripeContext',
|
||||
'headers',
|
||||
'additionalHeaders',
|
||||
'streaming',
|
||||
];
|
||||
export function isOptionsHash(o) {
|
||||
return (o &&
|
||||
typeof o === 'object' &&
|
||||
OPTIONS_KEYS.some((prop) => Object.prototype.hasOwnProperty.call(o, prop)));
|
||||
}
|
||||
/**
|
||||
* Stringifies an Object, accommodating nested objects
|
||||
* (forming the conventional key 'parent[child]=value')
|
||||
*/
|
||||
export function queryStringifyRequestData(data) {
|
||||
return stringifyRequestData(data);
|
||||
}
|
||||
/**
|
||||
* Encodes a value for use in a query string, keeping brackets unencoded
|
||||
* for readability (the server accepts both encoded and unencoded brackets).
|
||||
*/
|
||||
function encodeQueryValue(value) {
|
||||
return (encodeURIComponent(value)
|
||||
// Encode characters not encoded by encodeURIComponent but encoded by qs
|
||||
.replace(/!/g, '%21')
|
||||
.replace(/\*/g, '%2A')
|
||||
.replace(/\(/g, '%28')
|
||||
.replace(/\)/g, '%29')
|
||||
.replace(/'/g, '%27')
|
||||
// Decode brackets for readability (server accepts both)
|
||||
.replace(/%5B/g, '[')
|
||||
.replace(/%5D/g, ']'));
|
||||
}
|
||||
/**
|
||||
* Converts a value to a string representation for query string encoding.
|
||||
* Dates are converted to Unix timestamps.
|
||||
*/
|
||||
function valueToString(value) {
|
||||
if (value instanceof Date) {
|
||||
return Math.floor(value.getTime() / 1000).toString();
|
||||
}
|
||||
if (value === null) {
|
||||
return '';
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
/**
|
||||
* Custom query string stringifier that handles nested objects and arrays.
|
||||
* Produces output compatible with the qs library's indexed array format.
|
||||
*/
|
||||
function stringifyRequestData(data) {
|
||||
const pairs = [];
|
||||
function encode(key, value) {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value === null || typeof value !== 'object' || value instanceof Date) {
|
||||
// Primitive value (including null and Date)
|
||||
pairs.push(encodeQueryValue(key) + '=' + encodeQueryValue(valueToString(value)));
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
// Array: use indexed format arr[0], arr[1], etc.
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i] !== undefined) {
|
||||
encode(key + '[' + i + ']', value[i]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Object: recurse with bracket notation
|
||||
for (const k of Object.keys(value)) {
|
||||
encode(key + '[' + k + ']', value[k]);
|
||||
}
|
||||
}
|
||||
// Handle top-level object
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
for (const key of Object.keys(data)) {
|
||||
encode(key, data[key]);
|
||||
}
|
||||
}
|
||||
return pairs.join('&');
|
||||
}
|
||||
/**
|
||||
* Outputs a new function with interpolated object property values.
|
||||
* Use like so:
|
||||
* const fn = makeURLInterpolator('some/url/{param1}/{param2}');
|
||||
* fn({ param1: 123, param2: 456 }); // => 'some/url/123/456'
|
||||
*/
|
||||
export const makeURLInterpolator = (() => {
|
||||
const rc = {
|
||||
'\n': '\\n',
|
||||
'"': '\\"',
|
||||
'\u2028': '\\u2028',
|
||||
'\u2029': '\\u2029',
|
||||
};
|
||||
return (str) => {
|
||||
const cleanString = str.replace(/["\n\r\u2028\u2029]/g, ($0) => rc[$0]);
|
||||
return (outputs) => {
|
||||
return cleanString.replace(/\{([\s\S]+?)\}/g, ($0, $1) => {
|
||||
const output = outputs[$1];
|
||||
if (isValidEncodeUriComponentType(output))
|
||||
return encodeURIComponent(output);
|
||||
return '';
|
||||
});
|
||||
};
|
||||
};
|
||||
})();
|
||||
function isValidEncodeUriComponentType(value) {
|
||||
return ['number', 'string', 'boolean'].includes(typeof value);
|
||||
}
|
||||
export function extractUrlParams(path) {
|
||||
const params = path.match(/\{\w+\}/g);
|
||||
if (!params) {
|
||||
return [];
|
||||
}
|
||||
return params.map((param) => param.replace(/[{}]/g, ''));
|
||||
}
|
||||
/**
|
||||
* enforces that only supplied API bases are allowed.
|
||||
*/
|
||||
export const validateApiBase = (apiBase) => {
|
||||
if (typeof apiBase !== 'string') {
|
||||
throw new Error(`API base must be a string, got: ${typeof apiBase}`);
|
||||
}
|
||||
return apiBase in DEFAULT_BASE_ADDRESSES;
|
||||
};
|
||||
export function processOptions(options) {
|
||||
const result = {
|
||||
authenticator: null,
|
||||
headers: {},
|
||||
settings: {},
|
||||
streaming: false,
|
||||
apiBase: null,
|
||||
};
|
||||
if (!options) {
|
||||
return result;
|
||||
}
|
||||
if (options.apiKey) {
|
||||
result.authenticator = createApiKeyAuthenticator(options.apiKey);
|
||||
}
|
||||
if (options.idempotencyKey) {
|
||||
result.headers['Idempotency-Key'] = options.idempotencyKey;
|
||||
}
|
||||
if (options.stripeAccount) {
|
||||
result.headers['Stripe-Account'] = options.stripeAccount;
|
||||
}
|
||||
if (options.stripeContext) {
|
||||
if (result.headers['Stripe-Account']) {
|
||||
throw new Error("Can't specify both stripeAccount and stripeContext.");
|
||||
}
|
||||
result.headers['Stripe-Context'] = options.stripeContext;
|
||||
}
|
||||
if (options.apiVersion) {
|
||||
result.headers['Stripe-Version'] = options.apiVersion;
|
||||
}
|
||||
if (Number.isInteger(options.maxNetworkRetries)) {
|
||||
result.settings.maxNetworkRetries = options.maxNetworkRetries;
|
||||
}
|
||||
if (Number.isInteger(options.timeout)) {
|
||||
result.settings.timeout = options.timeout;
|
||||
}
|
||||
if (options.authenticator) {
|
||||
if (options.apiKey) {
|
||||
throw new Error("Can't specify both apiKey and authenticator.");
|
||||
}
|
||||
if (typeof options.authenticator !== 'function') {
|
||||
throw new Error('The authenticator must be a function ' +
|
||||
'receiving a request as the first parameter.');
|
||||
}
|
||||
result.authenticator = options.authenticator;
|
||||
}
|
||||
if (options.headers) {
|
||||
Object.assign(result.headers, options.headers);
|
||||
}
|
||||
if (options.streaming) {
|
||||
result.streaming = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Remove empty values from an object
|
||||
*/
|
||||
export function removeNullish(obj) {
|
||||
if (typeof obj !== 'object') {
|
||||
throw new Error('Argument must be an object');
|
||||
}
|
||||
return Object.keys(obj).reduce((result, key) => {
|
||||
if (obj[key] != null) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
/**
|
||||
* Normalize standard HTTP Headers:
|
||||
* {'foo-bar': 'hi'}
|
||||
* becomes
|
||||
* {'Foo-Bar': 'hi'}
|
||||
*/
|
||||
export function normalizeHeaders(obj) {
|
||||
if (!(obj && typeof obj === 'object')) {
|
||||
return obj;
|
||||
}
|
||||
return Object.keys(obj).reduce((result, header) => {
|
||||
result[normalizeHeader(header)] = obj[header];
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
/**
|
||||
* Stolen from https://github.com/marten-de-vries/header-case-normalizer/blob/master/index.js#L36-L41
|
||||
* without the exceptions which are irrelevant to us.
|
||||
*/
|
||||
export function normalizeHeader(header) {
|
||||
return header
|
||||
.split('-')
|
||||
.map((text) => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase())
|
||||
.join('-');
|
||||
}
|
||||
/**
|
||||
* Allow for special capitalization cases (such as OAuth)
|
||||
*/
|
||||
export function pascalToCamelCase(name) {
|
||||
if (name === 'OAuth') {
|
||||
return 'oauth';
|
||||
}
|
||||
else {
|
||||
return name[0].toLowerCase() + name.substring(1);
|
||||
}
|
||||
}
|
||||
export function isObject(obj) {
|
||||
const type = typeof obj;
|
||||
return (type === 'function' || type === 'object') && !!obj;
|
||||
}
|
||||
// For use in multipart requests
|
||||
export function flattenAndStringify(data) {
|
||||
const result = {};
|
||||
const step = (obj, prevKey) => {
|
||||
Object.entries(obj).forEach(([key, value]) => {
|
||||
const newKey = prevKey ? `${prevKey}[${key}]` : key;
|
||||
if (isObject(value)) {
|
||||
if (!(value instanceof Uint8Array) &&
|
||||
!Object.prototype.hasOwnProperty.call(value, 'data')) {
|
||||
// Non-buffer non-file Objects are recursively flattened
|
||||
return step(value, newKey);
|
||||
}
|
||||
else {
|
||||
// Buffers and file objects are stored without modification
|
||||
result[newKey] = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Primitives are converted to strings
|
||||
result[newKey] = String(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
step(data, null);
|
||||
return result;
|
||||
}
|
||||
export function validateInteger(name, n, defaultVal) {
|
||||
if (!Number.isInteger(n)) {
|
||||
if (defaultVal !== undefined) {
|
||||
return defaultVal;
|
||||
}
|
||||
else {
|
||||
throw new Error(`${name} must be an integer`);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
export const AI_AGENTS = [
|
||||
// The beginning of the section generated from our OpenAPI spec
|
||||
['ANTIGRAVITY_CLI_ALIAS', 'antigravity'],
|
||||
['CLAUDECODE', 'claude_code'],
|
||||
['CLINE_ACTIVE', 'cline'],
|
||||
['CODEX_SANDBOX', 'codex_cli'],
|
||||
['CODEX_THREAD_ID', 'codex_cli'],
|
||||
['CODEX_SANDBOX_NETWORK_DISABLED', 'codex_cli'],
|
||||
['CODEX_CI', 'codex_cli'],
|
||||
['CURSOR_AGENT', 'cursor'],
|
||||
['GEMINI_CLI', 'gemini_cli'],
|
||||
['OPENCLAW_SHELL', 'openclaw'],
|
||||
['OPENCODE', 'open_code'],
|
||||
// The end of the section generated from our OpenAPI spec
|
||||
];
|
||||
export function detectAIAgent(env) {
|
||||
for (const [envVar, agentName] of AI_AGENTS) {
|
||||
if (env[envVar]) {
|
||||
return agentName;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
export function createApiKeyAuthenticator(apiKey) {
|
||||
const authenticator = (request) => {
|
||||
request.headers.Authorization = 'Bearer ' + apiKey;
|
||||
return Promise.resolve();
|
||||
};
|
||||
// For testing
|
||||
authenticator._apiKey = apiKey;
|
||||
return authenticator;
|
||||
}
|
||||
/**
|
||||
* Joins an array of Uint8Arrays into a single Uint8Array
|
||||
*/
|
||||
export function concat(arrays) {
|
||||
const totalLength = arrays.reduce((len, array) => len + array.length, 0);
|
||||
const merged = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
arrays.forEach((array) => {
|
||||
merged.set(array, offset);
|
||||
offset += array.length;
|
||||
});
|
||||
return merged;
|
||||
}
|
||||
/**
|
||||
* Replaces Date objects with Unix timestamps
|
||||
*/
|
||||
function dateTimeReplacer(key, value) {
|
||||
if (this[key] instanceof Date) {
|
||||
return Math.floor(this[key].getTime() / 1000).toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* JSON stringifies an Object, replacing Date objects with Unix timestamps
|
||||
*/
|
||||
export function jsonStringifyRequestData(data) {
|
||||
return JSON.stringify(data, dateTimeReplacer);
|
||||
}
|
||||
/**
|
||||
* Inspects the given path to determine if the endpoint is for v1 or v2 API
|
||||
*/
|
||||
export function getAPIMode(path) {
|
||||
if (!path) {
|
||||
return 'v1';
|
||||
}
|
||||
return path.startsWith('/v2') ? 'v2' : 'v1';
|
||||
}
|
||||
export function parseHttpHeaderAsString(header) {
|
||||
if (Array.isArray(header)) {
|
||||
return header.join(', ');
|
||||
}
|
||||
return String(header);
|
||||
}
|
||||
export function parseHttpHeaderAsNumber(header) {
|
||||
const value = Array.isArray(header) ? header[0] : header;
|
||||
if (value == null) {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = String(value).trim();
|
||||
if (trimmed === '') {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(trimmed);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return undefined;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
export function parseHeadersForFetch(headers) {
|
||||
return Object.entries(headers).map(([key, value]) => {
|
||||
return [key, parseHttpHeaderAsString(value)];
|
||||
});
|
||||
}
|
||||
const CALL_SITE_MARKER = '\nOriginating from:';
|
||||
/**
|
||||
* We do some async indirection in our HTTP calling code that means stack traces aren't correct traced back to their actual origin in userland
|
||||
* So, we call this with a manually sourced error right before we do async HTTP operations, capturing the stack trace
|
||||
* NOTE: Modifies the `err` arg.
|
||||
*/
|
||||
export function attachCallSiteToError(err, callSiteStack) {
|
||||
if (!err || !err.stack || !callSiteStack) {
|
||||
return;
|
||||
}
|
||||
const callerFrames = callSiteStack.substring(callSiteStack.indexOf('\n') + 1);
|
||||
const existingMarkerIdx = err.stack.indexOf(CALL_SITE_MARKER);
|
||||
const baseStack = existingMarkerIdx >= 0
|
||||
? err.stack.substring(0, existingMarkerIdx)
|
||||
: err.stack;
|
||||
err.stack = `${baseStack}${CALL_SITE_MARKER}\n${callerFrames}`;
|
||||
}
|
||||
//# sourceMappingURL=utils.js.map
|
||||
Reference in New Issue
Block a user