@viso-lol/sdk 0.0.0-stage → 0.1.0
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 +114 -2
- package/dist/index.d.ts +521 -0
- package/dist/index.js +685 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["segment"],"sources":["../src/bytes.ts","../src/errors.ts","../src/images-input.ts","../src/pagination.ts","../src/resources/face.ts","../src/resources/images.ts","../src/resources/usage.ts","../src/retry.ts","../src/client.ts","../src/upload.ts"],"sourcesContent":["/** Copies a view's bytes: a view may cover only part of a buffer, or a shared buffer. */\nexport function viewBytes(view: ArrayBufferView): Uint8Array<ArrayBuffer> {\n return new Uint8Array(view.buffer, view.byteOffset, view.byteLength).slice();\n}\n\n/** A function, not an inline check, so TypeScript does not narrow `aborted` across awaits. */\nexport function isAborted(signal: AbortSignal | undefined): boolean {\n return signal?.aborted === true;\n}\n","import type { ErrorCode } from \"./types\";\n\nexport interface VisoErrorInit {\n code: ErrorCode | (string & {});\n status: number;\n message: string;\n requestId: string | null;\n details?: Record<string, unknown> | undefined;\n headers?: Headers | undefined;\n}\n\n/** An error response from the API: `{ error: { code, message, details?, requestId } }`. */\nexport class VisoError extends Error {\n /** Stable machine-readable code, for example `images.file_exists`. */\n readonly code: ErrorCode | (string & {});\n /** HTTP status. */\n readonly status: number;\n /** Include it when reporting a problem. */\n readonly requestId: string | null;\n readonly details: Record<string, unknown> | undefined;\n readonly headers: Headers;\n\n constructor(init: VisoErrorInit) {\n super(init.message);\n this.name = \"VisoError\";\n this.code = init.code;\n this.status = init.status;\n this.requestId = init.requestId;\n this.details = init.details;\n this.headers = init.headers ?? new Headers();\n }\n}\n\n/** The request never got a response: network failure or timeout. */\nexport class VisoConnectionError extends Error {\n /** True when the SDK gave up after `timeoutMs`. */\n readonly timeout: boolean;\n\n constructor(message: string, options: { cause?: unknown; timeout?: boolean } = {}) {\n super(message, { cause: options.cause });\n this.name = \"VisoConnectionError\";\n this.timeout = options.timeout ?? false;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Builds a `VisoError` from any failed response, including non-JSON ones from proxies. */\nexport async function errorFromResponse(response: Response): Promise<VisoError> {\n const headerRequestId = response.headers.get(\"x-request-id\");\n const text = await response.text().catch(() => \"\");\n let body: unknown;\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n const envelope = isRecord(body) && isRecord(body[\"error\"]) ? body[\"error\"] : undefined;\n const code = envelope?.[\"code\"];\n const message = envelope?.[\"message\"];\n const requestId = envelope?.[\"requestId\"];\n const details = envelope?.[\"details\"];\n return new VisoError({\n code: typeof code === \"string\" ? code : \"internal\",\n status: response.status,\n message:\n typeof message === \"string\"\n ? message\n : `The API answered ${response.status} ${response.statusText}`.trim(),\n requestId: typeof requestId === \"string\" ? requestId : headerRequestId,\n details: isRecord(details) ? details : undefined,\n headers: response.headers,\n });\n}\n","import { viewBytes } from \"./bytes\";\nimport type { ImageSource } from \"./types\";\n\ntype Binary = Blob | ArrayBuffer | ArrayBufferView;\n\nfunction isBinary(source: ImageSource): source is Binary {\n return source instanceof Blob || source instanceof ArrayBuffer || ArrayBuffer.isView(source);\n}\n\nfunction toBlob(source: Binary): Blob {\n if (source instanceof Blob) {\n return source;\n }\n if (source instanceof ArrayBuffer) {\n return new Blob([source]);\n }\n return new Blob([viewBytes(source)]);\n}\n\nfunction base64ToBlob(base64: string): Blob {\n const data = base64.startsWith(\"data:\") ? base64.slice(base64.indexOf(\",\") + 1) : base64;\n const binary = atob(data);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return new Blob([bytes]);\n}\n\nexport type FaceBody = { json: Record<string, unknown> } | { form: FormData };\n\n/**\n * Face endpoints take JSON (`{ base64 }` or `{ fileId }` images) or multipart (image\n * files). Bytes go as multipart, which avoids base64's 33% overhead; options then travel\n * as JSON-encoded form fields so that, for example, the external ID `\"123\"` stays a string.\n */\nexport function faceBody(\n images: Record<string, ImageSource>,\n options: Record<string, unknown>,\n): FaceBody {\n const sources = Object.entries(images);\n if (!sources.some(([, source]) => isBinary(source))) {\n return { json: { ...options, ...images } };\n }\n const form = new FormData();\n for (const [field, source] of sources) {\n if (isBinary(source)) {\n form.append(field, toBlob(source), field);\n } else if (\"fileId\" in source) {\n form.append(field, source.fileId);\n } else {\n form.append(field, base64ToBlob(source.base64), field);\n }\n }\n for (const [name, value] of Object.entries(options)) {\n if (value !== undefined) {\n form.append(name, JSON.stringify(value));\n }\n }\n return { form };\n}\n","import type { Page } from \"./types\";\n\n/**\n * The first page of a list, which is also an async iterable over every item of every\n * page: `await viso.face.collections.list()` or `for await (const c of ...list())`.\n */\nexport type ListPromise<Item, P extends Page<Item> = Page<Item>> = Promise<P> & AsyncIterable<Item>;\n\nexport function paginate<Item, P extends Page<Item>>(\n fetchPage: (cursor: string | undefined) => Promise<P>,\n firstCursor?: string,\n): ListPromise<Item, P> {\n const first = fetchPage(firstCursor);\n async function* items(): AsyncGenerator<Item> {\n let page = await first;\n while (true) {\n yield* page.data;\n if (page.nextCursor === null) {\n return;\n }\n // oxlint-disable-next-line no-await-in-loop -- each page needs the previous cursor.\n page = await fetchPage(page.nextCursor);\n }\n }\n return Object.assign(first, { [Symbol.asyncIterator]: items });\n}\n","import type { RequestOptions, Viso } from \"../client\";\nimport { faceBody } from \"../images-input\";\nimport { type ListPromise, paginate } from \"../pagination\";\nimport type {\n CompareParams,\n CreateCollectionParams,\n Deleted,\n DetectParams,\n Face,\n FaceCollection,\n FaceComparison,\n FaceDetection,\n FaceIndexResult,\n FaceSearchResult,\n IndexFacesParams,\n ListFacesParams,\n Page,\n SearchByImageParams,\n SearchFacesParams,\n} from \"../types\";\n\nconst segment = encodeURIComponent;\n\nexport class FacesResource {\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n }\n\n /** Detects faces in the image and stores them in the collection, largest first. */\n index(\n collectionId: string,\n params: IndexFacesParams,\n options?: RequestOptions,\n ): Promise<FaceIndexResult> {\n const { image, ...rest } = params;\n return this.#client.request({\n method: \"POST\",\n path: `/face/collections/${segment(collectionId)}/faces`,\n ...faceBody({ image }, rest),\n options,\n });\n }\n\n /** Faces of a collection, oldest first. Iterate with `for await` to read every page. */\n list(\n collectionId: string,\n params: ListFacesParams = {},\n options?: RequestOptions,\n ): ListPromise<Face> {\n return paginate(\n (cursor) =>\n this.#client.request<Page<Face>>({\n method: \"GET\",\n path: `/face/collections/${segment(collectionId)}/faces`,\n query: { externalId: params.externalId, limit: params.limit, cursor },\n options,\n }),\n params.cursor,\n );\n }\n\n delete(collectionId: string, faceId: string, options?: RequestOptions): Promise<Deleted> {\n return this.#client.request({\n method: \"DELETE\",\n path: `/face/collections/${segment(collectionId)}/faces/${segment(faceId)}`,\n options,\n });\n }\n\n /** Searches the collection for faces similar to a stored face. */\n search(\n collectionId: string,\n faceId: string,\n params: SearchFacesParams = {},\n options?: RequestOptions,\n ): Promise<FaceSearchResult> {\n return this.#client.request({\n method: \"POST\",\n path: `/face/collections/${segment(collectionId)}/faces/${segment(faceId)}/search`,\n json: params,\n options,\n });\n }\n}\n\nexport class CollectionsResource {\n readonly faces: FacesResource;\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n this.faces = new FacesResource(client);\n }\n\n create(params: CreateCollectionParams, options?: RequestOptions): Promise<FaceCollection> {\n return this.#client.request({\n method: \"POST\",\n path: \"/face/collections\",\n json: params,\n options,\n });\n }\n\n list(options?: RequestOptions): ListPromise<FaceCollection> {\n return paginate(() =>\n this.#client.request<Page<FaceCollection>>({\n method: \"GET\",\n path: \"/face/collections\",\n options,\n }),\n );\n }\n\n get(collectionId: string, options?: RequestOptions): Promise<FaceCollection> {\n return this.#client.request({\n method: \"GET\",\n path: `/face/collections/${segment(collectionId)}`,\n options,\n });\n }\n\n /** Deletion is accepted at once and finishes in the background. */\n delete(collectionId: string, options?: RequestOptions): Promise<Deleted> {\n return this.#client.request({\n method: \"DELETE\",\n path: `/face/collections/${segment(collectionId)}`,\n options,\n });\n }\n\n /** Searches the collection for the largest face in the image. */\n search(\n collectionId: string,\n params: SearchByImageParams,\n options?: RequestOptions,\n ): Promise<FaceSearchResult> {\n const { image, ...rest } = params;\n return this.#client.request({\n method: \"POST\",\n path: `/face/collections/${segment(collectionId)}/search`,\n ...faceBody({ image }, rest),\n options,\n });\n }\n}\n\nexport class FaceResource {\n readonly collections: CollectionsResource;\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n this.collections = new CollectionsResource(client);\n }\n\n /** Finds faces with their box, landmarks, pose and quality, plus requested attributes. */\n detect(params: DetectParams, options?: RequestOptions): Promise<FaceDetection> {\n const { image, ...rest } = params;\n return this.#client.request({\n method: \"POST\",\n path: \"/face/detect\",\n ...faceBody({ image }, rest),\n options,\n });\n }\n\n /** Compares the largest face in `source` with every face in `target`. */\n compare(params: CompareParams, options?: RequestOptions): Promise<FaceComparison> {\n const { source, target, ...rest } = params;\n return this.#client.request({\n method: \"POST\",\n path: \"/face/compare\",\n ...faceBody({ source, target }, rest),\n options,\n });\n }\n}\n","import { viewBytes } from \"../bytes\";\nimport type { RequestOptions, Viso } from \"../client\";\nimport { type ListPromise, paginate } from \"../pagination\";\nimport type {\n Bucket,\n CreateBucketParams,\n CreateUploadUrlParams,\n Deleted,\n DeleteFilesParams,\n DeleteFilesResult,\n FileList,\n ImageFile,\n ListFilesParams,\n Page,\n SignedUrl,\n SignedUrlParams,\n UpdateBucketParams,\n UploadParams,\n UploadUrl,\n} from \"../types\";\n\nconst segment = encodeURIComponent;\n\nexport class BucketsResource {\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n }\n\n create(params: CreateBucketParams, options?: RequestOptions): Promise<Bucket> {\n return this.#client.request({ method: \"POST\", path: \"/images/buckets\", json: params, options });\n }\n\n list(options?: RequestOptions): ListPromise<Bucket> {\n return paginate(() =>\n this.#client.request<Page<Bucket>>({ method: \"GET\", path: \"/images/buckets\", options }),\n );\n }\n\n get(bucketId: string, options?: RequestOptions): Promise<Bucket> {\n return this.#client.request({\n method: \"GET\",\n path: `/images/buckets/${segment(bucketId)}`,\n options,\n });\n }\n\n /** Changing visibility or cache lifetime purges the bucket's cached files. */\n update(bucketId: string, params: UpdateBucketParams, options?: RequestOptions): Promise<Bucket> {\n return this.#client.request({\n method: \"PATCH\",\n path: `/images/buckets/${segment(bucketId)}`,\n json: params,\n options,\n });\n }\n\n /** Deletes the bucket and every file in it; stored bytes are removed in the background. */\n delete(bucketId: string, options?: RequestOptions): Promise<Deleted> {\n return this.#client.request({\n method: \"DELETE\",\n path: `/images/buckets/${segment(bucketId)}`,\n options,\n });\n }\n}\n\nexport class FilesResource {\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n }\n\n /**\n * Files ordered by path. The promise resolves to the first page (with `folders` when\n * `delimiter` is `/`); `for await` iterates the files of every page.\n */\n list(params: ListFilesParams, options?: RequestOptions): ListPromise<ImageFile, FileList> {\n const { bucketId, cursor: firstCursor, ...query } = params;\n return paginate(\n (cursor) =>\n this.#client.request<FileList>({\n method: \"GET\",\n path: `/images/buckets/${segment(bucketId)}/files`,\n query: { ...query, cursor },\n options,\n }),\n firstCursor,\n );\n }\n\n get(fileId: string, options?: RequestOptions): Promise<ImageFile> {\n return this.#client.request({\n method: \"GET\",\n path: `/images/files/${segment(fileId)}`,\n options,\n });\n }\n\n delete(fileId: string, options?: RequestOptions): Promise<Deleted> {\n return this.#client.request({\n method: \"DELETE\",\n path: `/images/files/${segment(fileId)}`,\n options,\n });\n }\n\n /** Deletes up to 100 files of a bucket at once. */\n deleteMany(params: DeleteFilesParams, options?: RequestOptions): Promise<DeleteFilesResult> {\n const { bucketId, ...json } = params;\n return this.#client.request({\n method: \"POST\",\n path: `/images/buckets/${segment(bucketId)}/files/delete`,\n json,\n options,\n });\n }\n\n /** A time-limited CDN URL, required for files in private buckets. */\n createSignedUrl(\n fileId: string,\n params: SignedUrlParams = {},\n options?: RequestOptions,\n ): Promise<SignedUrl> {\n return this.#client.request({\n method: \"POST\",\n path: `/images/files/${segment(fileId)}/signed-url`,\n json: params,\n options,\n });\n }\n}\n\nfunction contentTypeOf(params: UploadParams): string | undefined {\n if (params.contentType !== undefined) {\n return params.contentType;\n }\n return params.body instanceof Blob && params.body.type !== \"\" ? params.body.type : undefined;\n}\n\nexport class ImagesResource {\n readonly buckets: BucketsResource;\n readonly files: FilesResource;\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n this.buckets = new BucketsResource(client);\n this.files = new FilesResource(client);\n }\n\n /**\n * Uploads a file from your server. Bytes are sent as the raw request body; streams are\n * forwarded as they are read. The file type is detected from its content.\n */\n upload(params: UploadParams, options?: RequestOptions): Promise<ImageFile> {\n return this.#client.request({\n method: \"POST\",\n path: `/images/buckets/${segment(params.bucketId)}/files`,\n query: {\n path: params.path,\n name: params.name,\n overwrite: params.overwrite === undefined ? undefined : String(params.overwrite),\n },\n body: ArrayBuffer.isView(params.body) ? viewBytes(params.body) : params.body,\n contentType: contentTypeOf(params),\n options,\n });\n }\n\n /**\n * A single-use URL a browser can `PUT` one image to without an API key. Create it on\n * your server and pass `uploadUrl` to `uploadToSignedUrl` in the browser.\n */\n createUploadUrl(params: CreateUploadUrlParams, options?: RequestOptions): Promise<UploadUrl> {\n const { bucketId, ...json } = params;\n return this.#client.request({\n method: \"POST\",\n path: `/images/buckets/${segment(bucketId)}/upload-urls`,\n json,\n options,\n });\n }\n}\n","import type { RequestOptions, Viso } from \"../client\";\nimport type { Usage } from \"../types\";\n\nexport class UsageResource {\n readonly #client: Viso;\n\n constructor(client: Viso) {\n this.#client = client;\n }\n\n /** The team's quotas, the project's recent requests and the per-key rate limit. */\n get(options?: RequestOptions): Promise<Usage> {\n return this.#client.request({ method: \"GET\", path: \"/usage\", options });\n }\n}\n","const BASE_DELAY_MS = 500;\nconst MAX_BACKOFF_MS = 8000;\n/** Never wait longer than this for one retry, whatever `Retry-After` says. */\nconst MAX_RETRY_AFTER_MS = 60_000;\n\n/** Methods that are safe to repeat when the outcome of a request is unknown. */\nconst IDEMPOTENT_METHODS = new Set([\"GET\", \"HEAD\", \"PUT\", \"DELETE\"]);\n\n/**\n * 429 and 503 mean the request was not processed (the API refunds quota on 503), so they\n * are retried for any method. Other 5xx and network errors may have had side effects, so\n * only idempotent methods retry them.\n */\nexport function isRetryableStatus(status: number, method: string): boolean {\n if (status === 429 || status === 503) {\n return true;\n }\n return status >= 500 && IDEMPOTENT_METHODS.has(method);\n}\n\nexport function isRetryableConnectionError(method: string): boolean {\n return IDEMPOTENT_METHODS.has(method);\n}\n\n/** `Retry-After` in milliseconds (delta seconds or an HTTP date), or `undefined`. */\nexport function parseRetryAfter(\n value: string | null,\n now: number = Date.now(),\n): number | undefined {\n if (value === null || value.trim() === \"\") {\n return undefined;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - now);\n}\n\n/** Exponential backoff with jitter for retry `attempt` (0-based), honoring `Retry-After`. */\nexport function retryDelay(\n attempt: number,\n retryAfter: string | null = null,\n random: () => number = Math.random,\n): number {\n const fromHeader = parseRetryAfter(retryAfter);\n if (fromHeader !== undefined) {\n return Math.min(fromHeader, MAX_RETRY_AFTER_MS);\n }\n const backoff = Math.min(MAX_BACKOFF_MS, BASE_DELAY_MS * 2 ** attempt);\n return Math.round(backoff * (0.75 + random() * 0.25));\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted === true) {\n reject(signal.reason);\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","import { isAborted } from \"./bytes\";\nimport { errorFromResponse, VisoConnectionError } from \"./errors\";\nimport { FaceResource } from \"./resources/face\";\nimport { ImagesResource } from \"./resources/images\";\nimport { UsageResource } from \"./resources/usage\";\nimport { isRetryableConnectionError, isRetryableStatus, retryDelay, sleep } from \"./retry\";\n\nexport const VERSION = \"0.1.0\";\nexport const DEFAULT_BASE_URL = \"https://api.viso.lol/v1\";\n\n/** The subset of `fetch` the SDK calls; `globalThis.fetch` and most wrappers fit. */\nexport type Fetch = (input: URL | string, init: RequestInit) => Promise<Response>;\n\nexport interface VisoOptions {\n /** A project API key (`viso_sk_...`). Defaults to the `VISO_API_KEY` environment variable. */\n apiKey?: string | undefined;\n /** Defaults to `VISO_BASE_URL` or `https://api.viso.lol/v1`. */\n baseUrl?: string | undefined;\n /** Retries for rate limits, provider throttling and transient failures. Default 2. */\n maxRetries?: number | undefined;\n /** Per-attempt timeout in milliseconds. Default 60 000. */\n timeoutMs?: number | undefined;\n /** A custom `fetch`, for example to add tracing. */\n fetch?: Fetch | undefined;\n /**\n * Secret keys must stay on servers: anyone who can read your page can read the key. Set\n * this only for local experiments. Browsers upload with `uploadToSignedUrl` instead.\n */\n dangerouslyAllowBrowser?: boolean | undefined;\n}\n\n/** Options accepted by every method as its last argument. */\nexport interface RequestOptions {\n signal?: AbortSignal | undefined;\n timeoutMs?: number | undefined;\n maxRetries?: number | undefined;\n}\n\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\ninterface AttemptState {\n url: URL;\n method: HttpMethod;\n headers: Headers;\n body: BodyInit | undefined;\n signal: AbortSignal | undefined;\n timeoutMs: number;\n maxRetries: number;\n attempt: number;\n}\n\nexport type QueryValue = string | number | boolean | undefined;\n\nexport interface ApiRequest {\n method: HttpMethod;\n path: string;\n query?: Record<string, QueryValue> | undefined;\n json?: unknown;\n form?: FormData | undefined;\n body?: BodyInit | undefined;\n contentType?: string | undefined;\n options?: RequestOptions | undefined;\n}\n\nfunction environmentVariable(name: string): string | undefined {\n const process: unknown = Reflect.get(globalThis, \"process\");\n if (typeof process !== \"object\" || process === null) {\n return undefined;\n }\n const env: unknown = Reflect.get(process, \"env\");\n if (typeof env !== \"object\" || env === null) {\n return undefined;\n }\n const value: unknown = Reflect.get(env, name);\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\n/** Read through `globalThis` so the SDK type-checks without the DOM library (Workers). */\nfunction isBrowser(): boolean {\n const browserWindow: unknown = Reflect.get(globalThis, \"window\");\n return (\n typeof browserWindow === \"object\" &&\n browserWindow !== null &&\n Reflect.get(browserWindow, \"document\") !== undefined\n );\n}\n\n/** Streams can be read once, so requests that send one are never retried. */\nfunction isReplayable(body: BodyInit | undefined): boolean {\n return !(typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream);\n}\n\n/**\n * Combines the caller's signal with a per-attempt timeout. `timedOut()` tells our own\n * timeout apart from the caller aborting.\n */\nfunction attemptSignal(timeoutMs: number, signal: AbortSignal | undefined) {\n const controller = new AbortController();\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort(new Error(`Request timed out after ${timeoutMs} ms.`));\n }, timeoutMs);\n const onAbort = () => controller.abort(signal?.reason);\n if (signal?.aborted === true) {\n onAbort();\n } else {\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n return {\n signal: controller.signal,\n timedOut: () => timedOut,\n done: () => {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n },\n };\n}\n\n/**\n * The viso.lol API client.\n *\n * ```ts\n * const viso = new Viso({ apiKey: process.env.VISO_API_KEY });\n * const { faces } = await viso.face.detect({ image: await file.arrayBuffer() });\n * ```\n */\nexport class Viso {\n readonly face: FaceResource;\n readonly images: ImagesResource;\n readonly usage: UsageResource;\n\n readonly baseUrl: string;\n readonly maxRetries: number;\n readonly timeoutMs: number;\n readonly #apiKey: string;\n readonly #fetch: Fetch;\n\n constructor(options: VisoOptions = {}) {\n const apiKey = options.apiKey ?? environmentVariable(\"VISO_API_KEY\");\n if (apiKey === undefined || apiKey === \"\") {\n throw new Error(\n \"Missing API key. Pass `apiKey` or set the VISO_API_KEY environment variable.\",\n );\n }\n if (isBrowser() && options.dangerouslyAllowBrowser !== true) {\n throw new Error(\n \"The viso.lol SDK is running in a browser, which would expose your secret API key. \" +\n \"Call the API from your server, and upload from browsers with signed upload URLs \" +\n \"(`uploadToSignedUrl`). For local experiments only, pass `dangerouslyAllowBrowser: true`.\",\n );\n }\n this.#apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? environmentVariable(\"VISO_BASE_URL\") ?? DEFAULT_BASE_URL)\n .trim()\n .replace(/\\/+$/, \"\");\n this.maxRetries = options.maxRetries ?? 2;\n this.timeoutMs = options.timeoutMs ?? 60_000;\n const customFetch = options.fetch;\n // Some runtimes throw \"Illegal invocation\" when fetch is called detached from globalThis.\n this.#fetch = customFetch ?? ((input, init) => fetch(input, init));\n\n this.face = new FaceResource(this);\n this.images = new ImagesResource(this);\n this.usage = new UsageResource(this);\n }\n\n /** Sends a request, retrying when it is safe, and returns the parsed JSON body. */\n request<T>(request: ApiRequest): Promise<T> {\n const url = new URL(`${this.baseUrl}${request.path}`);\n for (const [name, value] of Object.entries(request.query ?? {})) {\n if (value !== undefined) {\n url.searchParams.set(name, String(value));\n }\n }\n\n const headers = new Headers({\n Authorization: `Bearer ${this.#apiKey}`,\n Accept: \"application/json\",\n });\n let body: BodyInit | undefined;\n if (request.json !== undefined) {\n headers.set(\"Content-Type\", \"application/json\");\n body = JSON.stringify(request.json);\n } else if (request.form !== undefined) {\n body = request.form;\n } else if (request.body !== undefined) {\n headers.set(\"Content-Type\", request.contentType ?? \"application/octet-stream\");\n body = request.body;\n }\n\n const options = request.options ?? {};\n return this.#attempt<T>({\n url,\n method: request.method,\n headers,\n body,\n signal: options.signal,\n timeoutMs: options.timeoutMs ?? this.timeoutMs,\n maxRetries: isReplayable(body) ? (options.maxRetries ?? this.maxRetries) : 0,\n attempt: 0,\n });\n }\n\n async #attempt<T>(state: AttemptState): Promise<T> {\n const { url, method, body, signal } = state;\n const abort = attemptSignal(state.timeoutMs, signal);\n const init: RequestInit & { duplex?: \"half\" } = {\n method,\n headers: state.headers,\n signal: abort.signal,\n };\n if (body !== undefined) {\n init.body = body;\n if (!isReplayable(body)) {\n init.duplex = \"half\";\n }\n }\n const canRetry = state.attempt < state.maxRetries;\n const retry = async (retryAfter: string | null = null) => {\n await sleep(retryDelay(state.attempt, retryAfter), signal);\n return this.#attempt<T>({ ...state, attempt: state.attempt + 1 });\n };\n\n let response: Response;\n try {\n response = await this.#fetch(url, init);\n } catch (error) {\n abort.done();\n if (isAborted(signal)) {\n throw signal?.reason;\n }\n if (canRetry && isRetryableConnectionError(method)) {\n return retry();\n }\n const timeout = abort.timedOut();\n throw new VisoConnectionError(\n timeout\n ? `The request to ${url.pathname} timed out after ${state.timeoutMs} ms.`\n : `Could not reach the viso.lol API at ${url.origin}.`,\n { cause: error, timeout },\n );\n }\n\n try {\n if (response.ok) {\n const data: T = await response.json();\n return data;\n }\n if (!canRetry || !isRetryableStatus(response.status, method)) {\n throw await errorFromResponse(response);\n }\n await response.body?.cancel();\n } finally {\n abort.done();\n }\n return retry(response.headers.get(\"Retry-After\"));\n }\n}\n","import { isAborted, viewBytes } from \"./bytes\";\nimport type { Fetch } from \"./client\";\nimport { errorFromResponse, VisoConnectionError } from \"./errors\";\nimport type { ImageFile } from \"./types\";\n\nexport interface UploadProgress {\n loaded: number;\n /** `null` when the size is unknown. */\n total: number | null;\n}\n\nexport interface UploadToSignedUrlOptions {\n /** Called as bytes are sent (browsers only: needs `XMLHttpRequest`). */\n onProgress?: ((progress: UploadProgress) => void) | undefined;\n signal?: AbortSignal | undefined;\n /** Original file name, kept for downloads. Defaults to the `File` name. */\n name?: string | undefined;\n /** A custom `fetch`, used where `XMLHttpRequest` is not available. */\n fetch?: Fetch | undefined;\n}\n\nexport type SignedUploadBody = Blob | ArrayBuffer | ArrayBufferView;\n\nfunction withName(uploadUrl: string, body: SignedUploadBody, name: string | undefined): string {\n const fileName = name ?? (typeof File !== \"undefined\" && body instanceof File ? body.name : \"\");\n if (fileName === \"\") {\n return uploadUrl;\n }\n const url = new URL(uploadUrl);\n url.searchParams.set(\"name\", fileName.slice(0, 255));\n return url.toString();\n}\n\nfunction contentType(body: SignedUploadBody): string {\n return body instanceof Blob && body.type !== \"\" ? body.type : \"application/octet-stream\";\n}\n\n/** The parts of `XMLHttpRequest` used here, so the SDK type-checks without the DOM library. */\ninterface Xhr {\n readonly status: number;\n readonly responseText: string;\n readonly upload: {\n addEventListener(\n type: \"progress\",\n listener: (event: { loaded: number; total: number; lengthComputable: boolean }) => void,\n ): void;\n };\n open(method: string, url: string): void;\n setRequestHeader(name: string, value: string): void;\n getResponseHeader(name: string): string | null;\n addEventListener(type: \"load\" | \"error\" | \"timeout\" | \"abort\", listener: () => void): void;\n send(body: Blob): void;\n abort(): void;\n}\n\ntype XhrConstructor = new () => Xhr;\n\nfunction isXhrConstructor(value: unknown): value is XhrConstructor {\n return typeof value === \"function\";\n}\n\n/** `XMLHttpRequest` reports upload progress; `fetch` cannot. */\nfunction browserXhr(): XhrConstructor | undefined {\n const value: unknown = Reflect.get(globalThis, \"XMLHttpRequest\");\n return isXhrConstructor(value) ? value : undefined;\n}\n\nfunction viaXhr(\n XhrClass: XhrConstructor,\n url: string,\n body: SignedUploadBody,\n options: UploadToSignedUrlOptions,\n): Promise<Response> {\n return new Promise((resolve, reject) => {\n const xhr = new XhrClass();\n xhr.open(\"PUT\", url);\n xhr.setRequestHeader(\"Content-Type\", contentType(body));\n xhr.upload.addEventListener(\"progress\", (event) => {\n options.onProgress?.({\n loaded: event.loaded,\n total: event.lengthComputable ? event.total : null,\n });\n });\n xhr.addEventListener(\"load\", () => {\n const headers = new Headers();\n const requestId = xhr.getResponseHeader(\"x-request-id\");\n if (requestId !== null) {\n headers.set(\"x-request-id\", requestId);\n }\n resolve(new Response(xhr.responseText, { status: xhr.status, headers }));\n });\n xhr.addEventListener(\"error\", () => reject(new VisoConnectionError(\"The upload failed.\")));\n xhr.addEventListener(\"timeout\", () =>\n reject(new VisoConnectionError(\"The upload timed out.\", { timeout: true })),\n );\n xhr.addEventListener(\"abort\", () => reject(options.signal?.reason));\n options.signal?.addEventListener(\"abort\", () => xhr.abort(), { once: true });\n xhr.send(body instanceof Blob ? body : new Blob([toBytes(body)]));\n });\n}\n\nfunction toBytes(body: ArrayBuffer | ArrayBufferView): Uint8Array<ArrayBuffer> {\n return body instanceof ArrayBuffer ? new Uint8Array(body) : viewBytes(body);\n}\n\n/**\n * Uploads one file to a signed upload URL created on your server with\n * `viso.images.createUploadUrl()`. Safe in browsers: the URL is the credential and it\n * works once. Uploads are not retried automatically; a rejected upload (wrong type, too\n * large) leaves the URL usable for another attempt.\n */\nexport async function uploadToSignedUrl(\n uploadUrl: string,\n body: SignedUploadBody,\n options: UploadToSignedUrlOptions = {},\n): Promise<ImageFile> {\n if (isAborted(options.signal)) {\n throw options.signal?.reason;\n }\n const url = withName(uploadUrl, body, options.name);\n let response: Response;\n const XhrClass = options.fetch === undefined ? browserXhr() : undefined;\n if (XhrClass !== undefined) {\n response = await viaXhr(XhrClass, url, body, options);\n } else {\n const send: Fetch = options.fetch ?? ((input, init) => fetch(input, init));\n try {\n response = await send(url, {\n method: \"PUT\",\n headers: { \"Content-Type\": contentType(body) },\n body: body instanceof Blob ? body : toBytes(body),\n signal: options.signal ?? null,\n });\n } catch (error) {\n if (isAborted(options.signal)) {\n throw options.signal?.reason;\n }\n throw new VisoConnectionError(\"The upload failed.\", { cause: error });\n }\n }\n if (!response.ok) {\n throw await errorFromResponse(response);\n }\n const file: ImageFile = await response.json();\n return file;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viso-lol/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the viso.lol API: face detection, comparison and collections, plus image storage and delivery.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"face-detection",
|
|
7
|
+
"face-recognition",
|
|
8
|
+
"image-storage",
|
|
9
|
+
"viso"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://viso.lol/docs",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/lavincool/viso-cf.git",
|
|
16
|
+
"directory": "packages/sdk"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown && bun run smoke",
|
|
38
|
+
"typecheck": "tsc -p . && tsc -p examples",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"smoke": "tsc -p smoke && node smoke/smoke.mjs && bun smoke/smoke.mjs",
|
|
41
|
+
"example": "bun examples/quickstart.ts"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/bun": "1.4.4",
|
|
45
|
+
"@viso-lol/config": "workspace:*",
|
|
46
|
+
"@viso-lol/shared": "workspace:*",
|
|
47
|
+
"tsdown": "0.23.2",
|
|
48
|
+
"vitest": "5.0.5",
|
|
49
|
+
"zod": "4.6.7"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20"
|
|
53
|
+
}
|
|
54
|
+
}
|