@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 viso.lol
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,115 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @viso-lol/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The official TypeScript SDK for the [viso.lol](https://viso.lol) API: face detection, comparison and collections, plus image storage and CDN delivery.
|
|
4
|
+
|
|
5
|
+
- Zero runtime dependencies. Works anywhere `fetch` exists: Node.js 20+, Bun, Deno, Cloudflare Workers.
|
|
6
|
+
- Typed requests, responses and error codes.
|
|
7
|
+
- Automatic retries with backoff for rate limits and transient failures.
|
|
8
|
+
- Auto-pagination with `for await`.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @viso-lol/sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quickstart
|
|
15
|
+
|
|
16
|
+
Create an API key in the dashboard and keep it on your server.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { Viso } from "@viso-lol/sdk";
|
|
20
|
+
|
|
21
|
+
const viso = new Viso({ apiKey: process.env.VISO_API_KEY });
|
|
22
|
+
|
|
23
|
+
// Images
|
|
24
|
+
const bucket = await viso.images.buckets.create({ name: "Avatars", visibility: "public" });
|
|
25
|
+
const file = await viso.images.upload({
|
|
26
|
+
bucketId: bucket.id,
|
|
27
|
+
path: "users/ada.png",
|
|
28
|
+
body: await readFile("ada.png"),
|
|
29
|
+
});
|
|
30
|
+
console.log(file.url); // https://cdn.viso.lol/bkt_.../users/ada.png
|
|
31
|
+
|
|
32
|
+
// Face (enable Face for the project in the dashboard first)
|
|
33
|
+
const { faces } = await viso.face.detect({ image: { fileId: file.id }, attributes: "all" });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`apiKey` defaults to the `VISO_API_KEY` environment variable and `baseUrl` to `VISO_BASE_URL` or `https://api.viso.lol/v1`.
|
|
37
|
+
|
|
38
|
+
## Images from browsers
|
|
39
|
+
|
|
40
|
+
Never send an API key to a browser. Create a single-use upload URL on your server and upload to it from the client:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// Server
|
|
44
|
+
const { uploadUrl } = await viso.images.createUploadUrl({
|
|
45
|
+
bucketId,
|
|
46
|
+
path: `avatars/${userId}.png`,
|
|
47
|
+
maxSize: 5 * 1024 * 1024,
|
|
48
|
+
expiresIn: 600,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Browser
|
|
52
|
+
import { uploadToSignedUrl } from "@viso-lol/sdk";
|
|
53
|
+
|
|
54
|
+
const file = await uploadToSignedUrl(uploadUrl, input.files[0], {
|
|
55
|
+
onProgress: ({ loaded, total }) => console.log(loaded, total),
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The `Viso` client refuses to run in browsers (it would expose your key) unless you pass `dangerouslyAllowBrowser: true` for a local experiment.
|
|
60
|
+
|
|
61
|
+
## Face images
|
|
62
|
+
|
|
63
|
+
Every Face method takes images as bytes (`Blob`, `File`, `ArrayBuffer`, `Uint8Array`, Node `Buffer`), as base64 (`{ base64 }`, data URLs work too) or as a file stored with Images (`{ fileId }`). Bytes are sent as multipart, without base64 overhead.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const result = await viso.face.compare({
|
|
67
|
+
source: { fileId: "file_..." },
|
|
68
|
+
target: await blob.arrayBuffer(),
|
|
69
|
+
similarityThreshold: 90,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const collection = await viso.face.collections.create({ name: "Members" });
|
|
73
|
+
await viso.face.collections.faces.index(collection.id, { image: photo, externalId: "user_42" });
|
|
74
|
+
const { matches } = await viso.face.collections.search(collection.id, { image: selfie });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Pagination
|
|
78
|
+
|
|
79
|
+
List methods resolve to the first page and are also async iterables over every item:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const page = await viso.images.files.list({ bucketId, prefix: "users/", delimiter: "/" });
|
|
83
|
+
console.log(page.folders, page.nextCursor);
|
|
84
|
+
|
|
85
|
+
for await (const file of viso.images.files.list({ bucketId })) {
|
|
86
|
+
console.log(file.path);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Errors and retries
|
|
91
|
+
|
|
92
|
+
API errors throw `VisoError` with a stable `code`, the HTTP `status`, the `requestId` (include it when you contact support) and optional `details`. Network failures and timeouts throw `VisoConnectionError`.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { VisoError } from "@viso-lol/sdk";
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await viso.images.upload({ bucketId, path: "a.png", body });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error instanceof VisoError && error.code === "images.file_exists") {
|
|
101
|
+
// ...
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The client retries up to `maxRetries` times (default 2) with exponential backoff and jitter, honoring `Retry-After`:
|
|
107
|
+
|
|
108
|
+
- `429` and `503` for any method (the request was not processed);
|
|
109
|
+
- other `5xx` responses and network errors only for `GET`, `PUT` and `DELETE`.
|
|
110
|
+
|
|
111
|
+
Quota errors (`403 quota.exceeded`) are never retried. Uploads from streams are not retried because a stream can be read once. Every method accepts a final `{ signal, timeoutMs, maxRetries }` argument.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API shapes. They are written out by hand so the published declarations have no
|
|
3
|
+
* dependencies; `types.check.ts` fails the build when they drift from the API's schemas.
|
|
4
|
+
*/
|
|
5
|
+
/** Stable error codes returned by the API (see https://viso.lol/docs). */
|
|
6
|
+
type ErrorCode = "request.invalid" | "request.malformed" | "request.body_too_large" | "request.unsupported_media_type" | "request.not_found" | "auth.missing_api_key" | "auth.invalid_api_key" | "auth.api_key_revoked" | "auth.api_key_expired" | "auth.insufficient_scope" | "auth.unauthenticated" | "auth.forbidden" | "rate_limit.exceeded" | "quota.exceeded" | "plan.limit_reached" | "resource.not_found" | "resource.conflict" | "images.file_too_large" | "images.unsupported_type" | "images.file_exists" | "images.invalid_path" | "images.length_required" | "images.upload_token_invalid" | "images.upload_token_expired" | "images.upload_token_used" | "face.not_enabled" | "face.image_too_large" | "face.unsupported_format" | "face.image_too_small" | "face.invalid_image" | "face.no_face_detected" | "face.provider_unavailable" | "face.provider_capacity" | "waitlist.verification_failed" | "internal";
|
|
7
|
+
interface Page<T> {
|
|
8
|
+
object: "list";
|
|
9
|
+
data: T[];
|
|
10
|
+
/** Pass as `cursor` to get the next page; `null` on the last page. */
|
|
11
|
+
nextCursor: string | null;
|
|
12
|
+
}
|
|
13
|
+
interface PageParams {
|
|
14
|
+
/** Items per page, 1–100. */
|
|
15
|
+
limit?: number | undefined;
|
|
16
|
+
cursor?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
interface Deleted {
|
|
19
|
+
id: string;
|
|
20
|
+
deleted: true;
|
|
21
|
+
}
|
|
22
|
+
type ImageMimeType = "image/jpeg" | "image/png" | "image/webp" | "image/gif" | "image/avif" | "image/svg+xml";
|
|
23
|
+
type BucketVisibility = "public" | "private";
|
|
24
|
+
interface Bucket {
|
|
25
|
+
object: "bucket";
|
|
26
|
+
id: string;
|
|
27
|
+
projectId: string;
|
|
28
|
+
name: string;
|
|
29
|
+
slug: string;
|
|
30
|
+
/** `public` files are served by anyone with the URL; `private` files need a signed URL. */
|
|
31
|
+
visibility: BucketVisibility;
|
|
32
|
+
allowedMimeTypes: ImageMimeType[];
|
|
33
|
+
/** Largest accepted file, in bytes. */
|
|
34
|
+
maxFileSize: number;
|
|
35
|
+
/** CDN and browser cache lifetime, in seconds. */
|
|
36
|
+
cacheMaxAge: number;
|
|
37
|
+
createdAt: string;
|
|
38
|
+
updatedAt: string;
|
|
39
|
+
}
|
|
40
|
+
interface ImageFile {
|
|
41
|
+
object: "file";
|
|
42
|
+
id: string;
|
|
43
|
+
bucketId: string;
|
|
44
|
+
path: string;
|
|
45
|
+
/** Public CDN URL, or `null` in private buckets (use a signed URL). */
|
|
46
|
+
url: string | null;
|
|
47
|
+
originalName: string | null;
|
|
48
|
+
/** Detected from the file's content. */
|
|
49
|
+
mimeType: ImageMimeType;
|
|
50
|
+
size: number;
|
|
51
|
+
width: number | null;
|
|
52
|
+
height: number | null;
|
|
53
|
+
etag: string;
|
|
54
|
+
sha256: string;
|
|
55
|
+
createdAt: string;
|
|
56
|
+
}
|
|
57
|
+
interface FileList extends Page<ImageFile> {
|
|
58
|
+
/** Sub-folders of `prefix` when `delimiter` is `/`. */
|
|
59
|
+
folders: {
|
|
60
|
+
prefix: string;
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
interface CreateBucketParams {
|
|
64
|
+
name: string;
|
|
65
|
+
slug?: string | undefined;
|
|
66
|
+
visibility?: BucketVisibility | undefined;
|
|
67
|
+
allowedMimeTypes?: ImageMimeType[] | undefined;
|
|
68
|
+
maxFileSize?: number | undefined;
|
|
69
|
+
cacheMaxAge?: number | undefined;
|
|
70
|
+
}
|
|
71
|
+
type UpdateBucketParams = Partial<CreateBucketParams>;
|
|
72
|
+
interface ListFilesParams extends PageParams {
|
|
73
|
+
bucketId: string;
|
|
74
|
+
/** Only paths starting with this. */
|
|
75
|
+
prefix?: string | undefined;
|
|
76
|
+
/** Group deeper paths into `folders` instead of listing them. */
|
|
77
|
+
delimiter?: "/" | undefined;
|
|
78
|
+
}
|
|
79
|
+
/** Raw bytes accepted by uploads. Streams are sent as they are read and are never retried. */
|
|
80
|
+
type UploadBody = Blob | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array> | string;
|
|
81
|
+
interface UploadParams {
|
|
82
|
+
bucketId: string;
|
|
83
|
+
/** Where to store the file inside the bucket, for example `avatars/ada.png`. */
|
|
84
|
+
path: string;
|
|
85
|
+
body: UploadBody;
|
|
86
|
+
/** Ignored by the API (types are detected from content) but sent for completeness. */
|
|
87
|
+
contentType?: string | undefined;
|
|
88
|
+
/** Original file name, kept for downloads. */
|
|
89
|
+
name?: string | undefined;
|
|
90
|
+
/** Replace an existing file at `path` instead of failing with `images.file_exists`. */
|
|
91
|
+
overwrite?: boolean | undefined;
|
|
92
|
+
}
|
|
93
|
+
interface CreateUploadUrlParams {
|
|
94
|
+
bucketId: string;
|
|
95
|
+
path: string;
|
|
96
|
+
maxSize?: number | undefined;
|
|
97
|
+
allowedMimeTypes?: ImageMimeType[] | undefined;
|
|
98
|
+
/** Seconds until the URL expires, 60–3600 (default 900). */
|
|
99
|
+
expiresIn?: number | undefined;
|
|
100
|
+
overwrite?: boolean | undefined;
|
|
101
|
+
}
|
|
102
|
+
interface UploadUrl {
|
|
103
|
+
object: "upload_url";
|
|
104
|
+
uploadUrl: string;
|
|
105
|
+
method: "PUT";
|
|
106
|
+
expiresAt: string;
|
|
107
|
+
constraints: {
|
|
108
|
+
path: string;
|
|
109
|
+
maxSize: number;
|
|
110
|
+
allowedMimeTypes: ImageMimeType[];
|
|
111
|
+
overwrite: boolean;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
interface SignedUrlParams {
|
|
115
|
+
/** Seconds until the URL expires, 60–86400 (default 3600). */
|
|
116
|
+
expiresIn?: number | undefined;
|
|
117
|
+
/** Serve with `Content-Disposition: attachment`. */
|
|
118
|
+
download?: boolean | undefined;
|
|
119
|
+
}
|
|
120
|
+
interface SignedUrl {
|
|
121
|
+
object: "signed_url";
|
|
122
|
+
url: string;
|
|
123
|
+
expiresAt: string;
|
|
124
|
+
}
|
|
125
|
+
interface DeleteFilesParams {
|
|
126
|
+
bucketId: string;
|
|
127
|
+
/** Up to 100 file IDs. */
|
|
128
|
+
ids: string[];
|
|
129
|
+
}
|
|
130
|
+
interface DeleteFilesResult {
|
|
131
|
+
deleted: string[];
|
|
132
|
+
notFound: string[];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A JPEG or PNG of up to 5 MB and at least 80×80 pixels: raw bytes, base64 (a `data:` URL
|
|
136
|
+
* works too), or the ID of a file stored with Images in the same project.
|
|
137
|
+
*/
|
|
138
|
+
type ImageSource = Blob | ArrayBuffer | ArrayBufferView | {
|
|
139
|
+
base64: string;
|
|
140
|
+
} | {
|
|
141
|
+
fileId: string;
|
|
142
|
+
};
|
|
143
|
+
type FaceAttribute = "ageRange" | "beard" | "emotions" | "eyeDirection" | "eyeglasses" | "eyesOpen" | "faceOccluded" | "gender" | "mouthOpen" | "mustache" | "smile" | "sunglasses";
|
|
144
|
+
type Emotion = "happy" | "sad" | "angry" | "confused" | "disgusted" | "surprised" | "calm" | "fear" | "unknown";
|
|
145
|
+
type QualityFilter = "none" | "auto" | "low" | "medium" | "high";
|
|
146
|
+
/** Position and size as ratios of the image width and height (0–1). */
|
|
147
|
+
interface BoundingBox {
|
|
148
|
+
left: number;
|
|
149
|
+
top: number;
|
|
150
|
+
width: number;
|
|
151
|
+
height: number;
|
|
152
|
+
}
|
|
153
|
+
interface FaceFlag {
|
|
154
|
+
value: boolean;
|
|
155
|
+
/** 0–100. */
|
|
156
|
+
confidence: number;
|
|
157
|
+
}
|
|
158
|
+
interface FaceDetail {
|
|
159
|
+
boundingBox: BoundingBox;
|
|
160
|
+
/** Confidence that this is a face, 0–100. */
|
|
161
|
+
confidence: number;
|
|
162
|
+
landmarks: {
|
|
163
|
+
type: string;
|
|
164
|
+
x: number;
|
|
165
|
+
y: number;
|
|
166
|
+
}[];
|
|
167
|
+
pose: {
|
|
168
|
+
roll: number;
|
|
169
|
+
yaw: number;
|
|
170
|
+
pitch: number;
|
|
171
|
+
};
|
|
172
|
+
quality: {
|
|
173
|
+
brightness: number;
|
|
174
|
+
sharpness: number;
|
|
175
|
+
};
|
|
176
|
+
ageRange?: {
|
|
177
|
+
low: number;
|
|
178
|
+
high: number;
|
|
179
|
+
};
|
|
180
|
+
gender?: {
|
|
181
|
+
value: "male" | "female";
|
|
182
|
+
confidence: number;
|
|
183
|
+
};
|
|
184
|
+
smile?: FaceFlag;
|
|
185
|
+
eyeglasses?: FaceFlag;
|
|
186
|
+
sunglasses?: FaceFlag;
|
|
187
|
+
beard?: FaceFlag;
|
|
188
|
+
mustache?: FaceFlag;
|
|
189
|
+
eyesOpen?: FaceFlag;
|
|
190
|
+
mouthOpen?: FaceFlag;
|
|
191
|
+
faceOccluded?: FaceFlag;
|
|
192
|
+
emotions?: {
|
|
193
|
+
type: Emotion;
|
|
194
|
+
confidence: number;
|
|
195
|
+
}[];
|
|
196
|
+
eyeDirection?: {
|
|
197
|
+
yaw: number;
|
|
198
|
+
pitch: number;
|
|
199
|
+
confidence: number;
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
interface DetectParams {
|
|
203
|
+
image: ImageSource;
|
|
204
|
+
/** `default` (box, landmarks, pose, quality), `all`, or the attributes you need. */
|
|
205
|
+
attributes?: "default" | "all" | FaceAttribute[] | undefined;
|
|
206
|
+
}
|
|
207
|
+
interface FaceDetection {
|
|
208
|
+
object: "face_detection";
|
|
209
|
+
faces: FaceDetail[];
|
|
210
|
+
image: {
|
|
211
|
+
width: number | null;
|
|
212
|
+
height: number | null;
|
|
213
|
+
format: "jpeg" | "png";
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
interface CompareParams {
|
|
217
|
+
/** The image with the face to look for (its largest face is used). */
|
|
218
|
+
source: ImageSource;
|
|
219
|
+
/** The image to look in. */
|
|
220
|
+
target: ImageSource;
|
|
221
|
+
/** Minimum similarity for a match, 0–100 (default 80). */
|
|
222
|
+
similarityThreshold?: number | undefined;
|
|
223
|
+
}
|
|
224
|
+
interface ComparedFace {
|
|
225
|
+
boundingBox: BoundingBox;
|
|
226
|
+
confidence: number;
|
|
227
|
+
}
|
|
228
|
+
interface FaceComparison {
|
|
229
|
+
object: "face_comparison";
|
|
230
|
+
sourceFace: ComparedFace;
|
|
231
|
+
matches: {
|
|
232
|
+
similarity: number;
|
|
233
|
+
face: FaceDetail;
|
|
234
|
+
}[];
|
|
235
|
+
unmatched: FaceDetail[];
|
|
236
|
+
}
|
|
237
|
+
type FaceCollectionStatus = "creating" | "active" | "deleting";
|
|
238
|
+
interface FaceCollection {
|
|
239
|
+
object: "face_collection";
|
|
240
|
+
id: string;
|
|
241
|
+
projectId: string;
|
|
242
|
+
name: string;
|
|
243
|
+
slug: string;
|
|
244
|
+
status: FaceCollectionStatus;
|
|
245
|
+
faceCount: number;
|
|
246
|
+
faceModelVersion: string | null;
|
|
247
|
+
createdAt: string;
|
|
248
|
+
}
|
|
249
|
+
interface CreateCollectionParams {
|
|
250
|
+
name: string;
|
|
251
|
+
slug?: string | undefined;
|
|
252
|
+
}
|
|
253
|
+
interface Face {
|
|
254
|
+
object: "face";
|
|
255
|
+
id: string;
|
|
256
|
+
collectionId: string;
|
|
257
|
+
externalId: string | null;
|
|
258
|
+
/** The Images file the face was indexed from, if any. */
|
|
259
|
+
sourceFileId: string | null;
|
|
260
|
+
boundingBox: BoundingBox;
|
|
261
|
+
confidence: number;
|
|
262
|
+
createdAt: string;
|
|
263
|
+
}
|
|
264
|
+
interface IndexFacesParams {
|
|
265
|
+
image: ImageSource;
|
|
266
|
+
/** Your identifier for the person or image, for example a user ID. */
|
|
267
|
+
externalId?: string | undefined;
|
|
268
|
+
/** Index at most this many faces, largest first (1–100, default 1). */
|
|
269
|
+
maxFaces?: number | undefined;
|
|
270
|
+
qualityFilter?: QualityFilter | undefined;
|
|
271
|
+
}
|
|
272
|
+
interface FaceIndexResult {
|
|
273
|
+
object: "face_index_result";
|
|
274
|
+
indexed: Face[];
|
|
275
|
+
unindexed: {
|
|
276
|
+
boundingBox: BoundingBox;
|
|
277
|
+
reasons: string[];
|
|
278
|
+
}[];
|
|
279
|
+
}
|
|
280
|
+
interface ListFacesParams extends PageParams {
|
|
281
|
+
externalId?: string | undefined;
|
|
282
|
+
}
|
|
283
|
+
interface SearchFacesParams {
|
|
284
|
+
/** Return at most this many matches (1–100, default 10). */
|
|
285
|
+
maxFaces?: number | undefined;
|
|
286
|
+
/** Minimum similarity, 0–100 (default 80). */
|
|
287
|
+
threshold?: number | undefined;
|
|
288
|
+
}
|
|
289
|
+
interface SearchByImageParams extends SearchFacesParams {
|
|
290
|
+
image: ImageSource;
|
|
291
|
+
}
|
|
292
|
+
interface FaceSearchResult {
|
|
293
|
+
object: "face_search_result";
|
|
294
|
+
/** The face that was searched for, when searching by image. */
|
|
295
|
+
searchedFace: ComparedFace | null;
|
|
296
|
+
matches: {
|
|
297
|
+
similarity: number;
|
|
298
|
+
face: Face;
|
|
299
|
+
}[];
|
|
300
|
+
}
|
|
301
|
+
interface Quota {
|
|
302
|
+
used: number;
|
|
303
|
+
limit: number;
|
|
304
|
+
}
|
|
305
|
+
interface Usage {
|
|
306
|
+
object: "usage";
|
|
307
|
+
/** Current monthly period (UTC), for example `2026-10`. */
|
|
308
|
+
period: string;
|
|
309
|
+
/** Shared by every project of the team. */
|
|
310
|
+
team: {
|
|
311
|
+
storageBytes: Quota;
|
|
312
|
+
faceOperations: Quota;
|
|
313
|
+
storedFaces: Quota;
|
|
314
|
+
};
|
|
315
|
+
project: {
|
|
316
|
+
id: string;
|
|
317
|
+
/** Requests in the last 24 hours (estimated); `null` while analytics are unavailable. */
|
|
318
|
+
requests24h: number | null;
|
|
319
|
+
};
|
|
320
|
+
rateLimit: {
|
|
321
|
+
limit: number;
|
|
322
|
+
windowSeconds: number;
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* The first page of a list, which is also an async iterable over every item of every
|
|
327
|
+
* page: `await viso.face.collections.list()` or `for await (const c of ...list())`.
|
|
328
|
+
*/
|
|
329
|
+
type ListPromise<Item, P extends Page<Item> = Page<Item>> = Promise<P> & AsyncIterable<Item>;
|
|
330
|
+
declare class FacesResource {
|
|
331
|
+
#private;
|
|
332
|
+
constructor(client: Viso);
|
|
333
|
+
/** Detects faces in the image and stores them in the collection, largest first. */
|
|
334
|
+
index(collectionId: string, params: IndexFacesParams, options?: RequestOptions): Promise<FaceIndexResult>;
|
|
335
|
+
/** Faces of a collection, oldest first. Iterate with `for await` to read every page. */
|
|
336
|
+
list(collectionId: string, params?: ListFacesParams, options?: RequestOptions): ListPromise<Face>;
|
|
337
|
+
delete(collectionId: string, faceId: string, options?: RequestOptions): Promise<Deleted>;
|
|
338
|
+
/** Searches the collection for faces similar to a stored face. */
|
|
339
|
+
search(collectionId: string, faceId: string, params?: SearchFacesParams, options?: RequestOptions): Promise<FaceSearchResult>;
|
|
340
|
+
}
|
|
341
|
+
declare class CollectionsResource {
|
|
342
|
+
#private;
|
|
343
|
+
readonly faces: FacesResource;
|
|
344
|
+
constructor(client: Viso);
|
|
345
|
+
create(params: CreateCollectionParams, options?: RequestOptions): Promise<FaceCollection>;
|
|
346
|
+
list(options?: RequestOptions): ListPromise<FaceCollection>;
|
|
347
|
+
get(collectionId: string, options?: RequestOptions): Promise<FaceCollection>;
|
|
348
|
+
/** Deletion is accepted at once and finishes in the background. */
|
|
349
|
+
delete(collectionId: string, options?: RequestOptions): Promise<Deleted>;
|
|
350
|
+
/** Searches the collection for the largest face in the image. */
|
|
351
|
+
search(collectionId: string, params: SearchByImageParams, options?: RequestOptions): Promise<FaceSearchResult>;
|
|
352
|
+
}
|
|
353
|
+
declare class FaceResource {
|
|
354
|
+
#private;
|
|
355
|
+
readonly collections: CollectionsResource;
|
|
356
|
+
constructor(client: Viso);
|
|
357
|
+
/** Finds faces with their box, landmarks, pose and quality, plus requested attributes. */
|
|
358
|
+
detect(params: DetectParams, options?: RequestOptions): Promise<FaceDetection>;
|
|
359
|
+
/** Compares the largest face in `source` with every face in `target`. */
|
|
360
|
+
compare(params: CompareParams, options?: RequestOptions): Promise<FaceComparison>;
|
|
361
|
+
}
|
|
362
|
+
declare class BucketsResource {
|
|
363
|
+
#private;
|
|
364
|
+
constructor(client: Viso);
|
|
365
|
+
create(params: CreateBucketParams, options?: RequestOptions): Promise<Bucket>;
|
|
366
|
+
list(options?: RequestOptions): ListPromise<Bucket>;
|
|
367
|
+
get(bucketId: string, options?: RequestOptions): Promise<Bucket>;
|
|
368
|
+
/** Changing visibility or cache lifetime purges the bucket's cached files. */
|
|
369
|
+
update(bucketId: string, params: UpdateBucketParams, options?: RequestOptions): Promise<Bucket>;
|
|
370
|
+
/** Deletes the bucket and every file in it; stored bytes are removed in the background. */
|
|
371
|
+
delete(bucketId: string, options?: RequestOptions): Promise<Deleted>;
|
|
372
|
+
}
|
|
373
|
+
declare class FilesResource {
|
|
374
|
+
#private;
|
|
375
|
+
constructor(client: Viso);
|
|
376
|
+
/**
|
|
377
|
+
* Files ordered by path. The promise resolves to the first page (with `folders` when
|
|
378
|
+
* `delimiter` is `/`); `for await` iterates the files of every page.
|
|
379
|
+
*/
|
|
380
|
+
list(params: ListFilesParams, options?: RequestOptions): ListPromise<ImageFile, FileList>;
|
|
381
|
+
get(fileId: string, options?: RequestOptions): Promise<ImageFile>;
|
|
382
|
+
delete(fileId: string, options?: RequestOptions): Promise<Deleted>;
|
|
383
|
+
/** Deletes up to 100 files of a bucket at once. */
|
|
384
|
+
deleteMany(params: DeleteFilesParams, options?: RequestOptions): Promise<DeleteFilesResult>;
|
|
385
|
+
/** A time-limited CDN URL, required for files in private buckets. */
|
|
386
|
+
createSignedUrl(fileId: string, params?: SignedUrlParams, options?: RequestOptions): Promise<SignedUrl>;
|
|
387
|
+
}
|
|
388
|
+
declare class ImagesResource {
|
|
389
|
+
#private;
|
|
390
|
+
readonly buckets: BucketsResource;
|
|
391
|
+
readonly files: FilesResource;
|
|
392
|
+
constructor(client: Viso);
|
|
393
|
+
/**
|
|
394
|
+
* Uploads a file from your server. Bytes are sent as the raw request body; streams are
|
|
395
|
+
* forwarded as they are read. The file type is detected from its content.
|
|
396
|
+
*/
|
|
397
|
+
upload(params: UploadParams, options?: RequestOptions): Promise<ImageFile>;
|
|
398
|
+
/**
|
|
399
|
+
* A single-use URL a browser can `PUT` one image to without an API key. Create it on
|
|
400
|
+
* your server and pass `uploadUrl` to `uploadToSignedUrl` in the browser.
|
|
401
|
+
*/
|
|
402
|
+
createUploadUrl(params: CreateUploadUrlParams, options?: RequestOptions): Promise<UploadUrl>;
|
|
403
|
+
}
|
|
404
|
+
declare class UsageResource {
|
|
405
|
+
#private;
|
|
406
|
+
constructor(client: Viso);
|
|
407
|
+
/** The team's quotas, the project's recent requests and the per-key rate limit. */
|
|
408
|
+
get(options?: RequestOptions): Promise<Usage>;
|
|
409
|
+
}
|
|
410
|
+
export declare const VERSION = "0.1.0";
|
|
411
|
+
export declare const DEFAULT_BASE_URL = "https://api.viso.lol/v1";
|
|
412
|
+
/** The subset of `fetch` the SDK calls; `globalThis.fetch` and most wrappers fit. */
|
|
413
|
+
type Fetch = (input: URL | string, init: RequestInit) => Promise<Response>;
|
|
414
|
+
interface VisoOptions {
|
|
415
|
+
/** A project API key (`viso_sk_...`). Defaults to the `VISO_API_KEY` environment variable. */
|
|
416
|
+
apiKey?: string | undefined;
|
|
417
|
+
/** Defaults to `VISO_BASE_URL` or `https://api.viso.lol/v1`. */
|
|
418
|
+
baseUrl?: string | undefined;
|
|
419
|
+
/** Retries for rate limits, provider throttling and transient failures. Default 2. */
|
|
420
|
+
maxRetries?: number | undefined;
|
|
421
|
+
/** Per-attempt timeout in milliseconds. Default 60 000. */
|
|
422
|
+
timeoutMs?: number | undefined;
|
|
423
|
+
/** A custom `fetch`, for example to add tracing. */
|
|
424
|
+
fetch?: Fetch | undefined;
|
|
425
|
+
/**
|
|
426
|
+
* Secret keys must stay on servers: anyone who can read your page can read the key. Set
|
|
427
|
+
* this only for local experiments. Browsers upload with `uploadToSignedUrl` instead.
|
|
428
|
+
*/
|
|
429
|
+
dangerouslyAllowBrowser?: boolean | undefined;
|
|
430
|
+
}
|
|
431
|
+
/** Options accepted by every method as its last argument. */
|
|
432
|
+
interface RequestOptions {
|
|
433
|
+
signal?: AbortSignal | undefined;
|
|
434
|
+
timeoutMs?: number | undefined;
|
|
435
|
+
maxRetries?: number | undefined;
|
|
436
|
+
}
|
|
437
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
438
|
+
type QueryValue = string | number | boolean | undefined;
|
|
439
|
+
interface ApiRequest {
|
|
440
|
+
method: HttpMethod;
|
|
441
|
+
path: string;
|
|
442
|
+
query?: Record<string, QueryValue> | undefined;
|
|
443
|
+
json?: unknown;
|
|
444
|
+
form?: FormData | undefined;
|
|
445
|
+
body?: BodyInit | undefined;
|
|
446
|
+
contentType?: string | undefined;
|
|
447
|
+
options?: RequestOptions | undefined;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* The viso.lol API client.
|
|
451
|
+
*
|
|
452
|
+
* ```ts
|
|
453
|
+
* const viso = new Viso({ apiKey: process.env.VISO_API_KEY });
|
|
454
|
+
* const { faces } = await viso.face.detect({ image: await file.arrayBuffer() });
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
export declare class Viso {
|
|
458
|
+
#private;
|
|
459
|
+
readonly face: FaceResource;
|
|
460
|
+
readonly images: ImagesResource;
|
|
461
|
+
readonly usage: UsageResource;
|
|
462
|
+
readonly baseUrl: string;
|
|
463
|
+
readonly maxRetries: number;
|
|
464
|
+
readonly timeoutMs: number;
|
|
465
|
+
constructor(options?: VisoOptions);
|
|
466
|
+
/** Sends a request, retrying when it is safe, and returns the parsed JSON body. */
|
|
467
|
+
request<T>(request: ApiRequest): Promise<T>;
|
|
468
|
+
}
|
|
469
|
+
interface VisoErrorInit {
|
|
470
|
+
code: ErrorCode | (string & {});
|
|
471
|
+
status: number;
|
|
472
|
+
message: string;
|
|
473
|
+
requestId: string | null;
|
|
474
|
+
details?: Record<string, unknown> | undefined;
|
|
475
|
+
headers?: Headers | undefined;
|
|
476
|
+
}
|
|
477
|
+
/** An error response from the API: `{ error: { code, message, details?, requestId } }`. */
|
|
478
|
+
export declare class VisoError extends Error {
|
|
479
|
+
/** Stable machine-readable code, for example `images.file_exists`. */
|
|
480
|
+
readonly code: ErrorCode | (string & {});
|
|
481
|
+
/** HTTP status. */
|
|
482
|
+
readonly status: number;
|
|
483
|
+
/** Include it when reporting a problem. */
|
|
484
|
+
readonly requestId: string | null;
|
|
485
|
+
readonly details: Record<string, unknown> | undefined;
|
|
486
|
+
readonly headers: Headers;
|
|
487
|
+
constructor(init: VisoErrorInit);
|
|
488
|
+
}
|
|
489
|
+
/** The request never got a response: network failure or timeout. */
|
|
490
|
+
export declare class VisoConnectionError extends Error {
|
|
491
|
+
/** True when the SDK gave up after `timeoutMs`. */
|
|
492
|
+
readonly timeout: boolean;
|
|
493
|
+
constructor(message: string, options?: {
|
|
494
|
+
cause?: unknown;
|
|
495
|
+
timeout?: boolean;
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
interface UploadProgress {
|
|
499
|
+
loaded: number;
|
|
500
|
+
/** `null` when the size is unknown. */
|
|
501
|
+
total: number | null;
|
|
502
|
+
}
|
|
503
|
+
interface UploadToSignedUrlOptions {
|
|
504
|
+
/** Called as bytes are sent (browsers only: needs `XMLHttpRequest`). */
|
|
505
|
+
onProgress?: ((progress: UploadProgress) => void) | undefined;
|
|
506
|
+
signal?: AbortSignal | undefined;
|
|
507
|
+
/** Original file name, kept for downloads. Defaults to the `File` name. */
|
|
508
|
+
name?: string | undefined;
|
|
509
|
+
/** A custom `fetch`, used where `XMLHttpRequest` is not available. */
|
|
510
|
+
fetch?: Fetch | undefined;
|
|
511
|
+
}
|
|
512
|
+
type SignedUploadBody = Blob | ArrayBuffer | ArrayBufferView;
|
|
513
|
+
/**
|
|
514
|
+
* Uploads one file to a signed upload URL created on your server with
|
|
515
|
+
* `viso.images.createUploadUrl()`. Safe in browsers: the URL is the credential and it
|
|
516
|
+
* works once. Uploads are not retried automatically; a rejected upload (wrong type, too
|
|
517
|
+
* large) leaves the URL usable for another attempt.
|
|
518
|
+
*/
|
|
519
|
+
export declare function uploadToSignedUrl(uploadUrl: string, body: SignedUploadBody, options?: UploadToSignedUrlOptions): Promise<ImageFile>;
|
|
520
|
+
export type { BoundingBox, Bucket, BucketVisibility, BucketsResource, CollectionsResource, CompareParams, ComparedFace, CreateBucketParams, CreateCollectionParams, CreateUploadUrlParams, DeleteFilesParams, DeleteFilesResult, Deleted, DetectParams, Emotion, ErrorCode, Face, FaceAttribute, FaceCollection, FaceCollectionStatus, FaceComparison, FaceDetail, FaceDetection, FaceFlag, FaceIndexResult, FaceResource, FaceSearchResult, FacesResource, Fetch, FileList, FilesResource, ImageFile, ImageMimeType, ImageSource, ImagesResource, IndexFacesParams, ListFacesParams, ListFilesParams, ListPromise, Page, PageParams, QualityFilter, Quota, RequestOptions, SearchByImageParams, SearchFacesParams, SignedUploadBody, SignedUrl, SignedUrlParams, UpdateBucketParams, UploadBody, UploadParams, UploadProgress, UploadToSignedUrlOptions, UploadUrl, Usage, UsageResource, VisoOptions };
|
|
521
|
+
//# sourceMappingURL=index.d.ts.map
|