UNPKG

@anthropic-ai/sdk

Version:
96 lines (84 loc) • 3.49 kB
import { AnthropicError } from '../../core/error'; /** * Percent-encode everything that isn't safe to have in a path without encoding safe chars. * * Taken from https://datatracker.ietf.org/doc/html/rfc3986#section-3.3: * > unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * > sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" * > pchar = unreserved / pct-encoded / sub-delims / ":" / "@" */ export function encodeURIPath(str: string) { return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent); } const EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null)); export const createPathTagFunction = (pathEncoder = encodeURIPath) => function path(statics: readonly string[], ...params: readonly unknown[]): string { // If there are no params, no processing is needed. if (statics.length === 1) return statics[0]!; let postPath = false; const invalidSegments = []; const path = statics.reduce((previousValue, currentValue, index) => { if (/[?#]/.test(currentValue)) { postPath = true; } const value = params[index]; let encoded = (postPath ? encodeURIComponent : pathEncoder)('' + value); if ( index !== params.length && (value == null || (typeof value === 'object' && // handle values from other realms value.toString === Object.getPrototypeOf(Object.getPrototypeOf((value as any).hasOwnProperty ?? EMPTY) ?? EMPTY) ?.toString)) ) { encoded = value + ''; invalidSegments.push({ start: previousValue.length + currentValue.length, length: encoded.length, error: `Value of type ${Object.prototype.toString .call(value) .slice(8, -1)} is not a valid path parameter`, }); } if (!postPath && encoded === '') { invalidSegments.push({ start: previousValue.length + currentValue.length, length: 0, error: 'Value "" can\'t be safely passed as a path parameter', }); } return previousValue + currentValue + (index === params.length ? '' : encoded); }, ''); const pathOnly = path.split(/[?#]/, 1)[0]!; const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi; let match; // Find all invalid segments while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) { invalidSegments.push({ start: match.index, length: match[0].length, error: `Value "${match[0]}" can\'t be safely passed as a path parameter`, }); } invalidSegments.sort((a, b) => a.start - b.start); if (invalidSegments.length > 0) { const marks: string[] = []; for (const segment of invalidSegments) { for (let i = segment.start; i < segment.start + Math.max(1, segment.length); i++) { marks[i] = '^'; } } const underline = Array.from(marks, (mark) => mark ?? ' ').join(''); throw new AnthropicError( `Path parameters result in path with invalid segments:\n${invalidSegments .map((e) => e.error) .join('\n')}\n${path}\n${underline}`, ); } return path; }; /** * URI-encodes path params and ensures that none before any ? or # is empty and none introduces an unsafe /./ or /../ path segment. */ export const path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);