upthing-sdk 1.0.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/README.md +123 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +126 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# upthing-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [upthing.2klabs.xyz](https://upthing.2klabs.xyz) — upload files and proxy images with one function call.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install upthing-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { UpThingClient } from "upthing-sdk";
|
|
15
|
+
|
|
16
|
+
const upthing = new UpThingClient({
|
|
17
|
+
apiKey: process.env.UPTHING_API_KEY!,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Upload a file
|
|
21
|
+
const file = await upthing.uploadFile("./photo.jpg");
|
|
22
|
+
console.log(file.url);
|
|
23
|
+
// https://upthing.2klabs.xyz/f/UgjIzXI3ZolZ
|
|
24
|
+
|
|
25
|
+
// Proxy an external image — get a URL you can use directly
|
|
26
|
+
const src = upthing.proxyUrl("https://example.com/banner.jpg");
|
|
27
|
+
// <img src={src} />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## API
|
|
31
|
+
|
|
32
|
+
### `new UpThingClient(options)`
|
|
33
|
+
|
|
34
|
+
| Option | Type | Default |
|
|
35
|
+
| --------- | ------ | -------------------------------- |
|
|
36
|
+
| `apiKey` | string | **required** |
|
|
37
|
+
| `baseUrl` | string | `https://upthing.2klabs.xyz` |
|
|
38
|
+
|
|
39
|
+
### Upload methods
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// From a file path (Node.js)
|
|
43
|
+
await upthing.uploadFile("./photo.jpg");
|
|
44
|
+
await upthing.uploadFile("./photo.jpg", { ttlSeconds: 3600 });
|
|
45
|
+
|
|
46
|
+
// From a Blob/File (Node.js + browser)
|
|
47
|
+
await upthing.uploadBlob(file, "photo.jpg");
|
|
48
|
+
|
|
49
|
+
// From a buffer
|
|
50
|
+
await upthing.uploadBuffer(uint8array, "data.bin");
|
|
51
|
+
|
|
52
|
+
// From base64
|
|
53
|
+
await upthing.uploadBase64(base64String, "avatar.png");
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Upload options
|
|
57
|
+
|
|
58
|
+
| Option | Type | Description |
|
|
59
|
+
| ------------- | ------ | ---------------------------------------- |
|
|
60
|
+
| `filename` | string | Override the filename |
|
|
61
|
+
| `projectUuid` | string | Target project (if key isn't pinned) |
|
|
62
|
+
| `ttlSeconds` | number | Auto-delete after N seconds |
|
|
63
|
+
|
|
64
|
+
### Proxy
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Build a URL that serves the proxied image (use in <img>, fetch, etc.)
|
|
68
|
+
const src = upthing.proxyUrl("https://example.com/image.jpg");
|
|
69
|
+
const src = upthing.proxyUrl("https://example.com/image.jpg", {
|
|
70
|
+
projectUuid: "07595179-...",
|
|
71
|
+
ttlSeconds: 3600,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Fetch the image bytes server-side
|
|
75
|
+
const response = await upthing.proxyFetch("https://example.com/image.jpg");
|
|
76
|
+
const buffer = await response.arrayBuffer();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Helpers
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Build a permanent URL from a public_id
|
|
83
|
+
upthing.permanentUrl("UgjIzXI3ZolZ");
|
|
84
|
+
// https://upthing.2klabs.xyz/f/UgjIzXI3ZolZ
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Error handling
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { UpThingSDKError } from "upthing-sdk";
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
await upthing.uploadFile("./huge.mp4");
|
|
94
|
+
} catch (err) {
|
|
95
|
+
if (err instanceof UpThingSDKError) {
|
|
96
|
+
console.error(err.status, err.message);
|
|
97
|
+
// 413 "File exceeds max size"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Browser usage
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const upthing = new UpThingClient({ apiKey: "ak_..." });
|
|
106
|
+
|
|
107
|
+
// Proxy images directly in HTML — no async needed
|
|
108
|
+
document.querySelector("img")!.src = upthing.proxyUrl("https://example.com/photo.jpg");
|
|
109
|
+
|
|
110
|
+
// Upload from a file input
|
|
111
|
+
const input = document.querySelector<HTMLInputElement>("#file-input")!;
|
|
112
|
+
input.addEventListener("change", async () => {
|
|
113
|
+
const file = input.files?.[0];
|
|
114
|
+
if (!file) return;
|
|
115
|
+
const result = await upthing.uploadBlob(file, file.name);
|
|
116
|
+
console.log("Uploaded:", result.url);
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
- Node.js 18+ (native `fetch`) or any modern browser
|
|
123
|
+
- No dependencies
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface UpThingFile {
|
|
2
|
+
uuid: string;
|
|
3
|
+
public_id: string;
|
|
4
|
+
filename: string;
|
|
5
|
+
mime_type: string;
|
|
6
|
+
size_bytes: number;
|
|
7
|
+
url_mode: "permanent" | "signed";
|
|
8
|
+
url: string | null;
|
|
9
|
+
expires_at: string | null;
|
|
10
|
+
ttl_seconds: number | null;
|
|
11
|
+
is_proxied: boolean;
|
|
12
|
+
source_url: string | null;
|
|
13
|
+
view_count: number;
|
|
14
|
+
download_bytes: number;
|
|
15
|
+
created_at: string;
|
|
16
|
+
project_id: number | null;
|
|
17
|
+
}
|
|
18
|
+
export interface UploadOptions {
|
|
19
|
+
filename?: string;
|
|
20
|
+
projectUuid?: string;
|
|
21
|
+
ttlSeconds?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ProxyOptions {
|
|
24
|
+
projectUuid?: string;
|
|
25
|
+
ttlSeconds?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface UpThingError {
|
|
28
|
+
error: string;
|
|
29
|
+
code: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class UpThingSDKError extends Error {
|
|
32
|
+
readonly status: number;
|
|
33
|
+
readonly body: UpThingError;
|
|
34
|
+
constructor(status: number, body: UpThingError);
|
|
35
|
+
}
|
|
36
|
+
export interface UpThingClientOptions {
|
|
37
|
+
apiKey: string;
|
|
38
|
+
baseUrl?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare class UpThingClient {
|
|
41
|
+
private readonly apiKey;
|
|
42
|
+
private readonly baseUrl;
|
|
43
|
+
constructor(options: UpThingClientOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Upload a file from a local path (Node.js only).
|
|
46
|
+
*/
|
|
47
|
+
uploadFile(filePath: string, options?: UploadOptions): Promise<UpThingFile>;
|
|
48
|
+
/**
|
|
49
|
+
* Upload a Blob/File (works in Node.js and browsers).
|
|
50
|
+
*/
|
|
51
|
+
uploadBlob(blob: Blob, filename: string, options?: UploadOptions): Promise<UpThingFile>;
|
|
52
|
+
/**
|
|
53
|
+
* Upload raw bytes with a filename.
|
|
54
|
+
*/
|
|
55
|
+
uploadBuffer(buffer: ArrayBuffer | Uint8Array, filename: string, options?: UploadOptions): Promise<UpThingFile>;
|
|
56
|
+
/**
|
|
57
|
+
* Upload base64-encoded data.
|
|
58
|
+
*/
|
|
59
|
+
uploadBase64(base64Data: string, filename: string, options?: UploadOptions): Promise<UpThingFile>;
|
|
60
|
+
/**
|
|
61
|
+
* Build a proxy query-string URL (without auth). Useful when the
|
|
62
|
+
* project uses `domain` auth mode and no API key is needed in the
|
|
63
|
+
* request — e.g. for `<img src>` on an authorized domain.
|
|
64
|
+
*/
|
|
65
|
+
proxyUrl(url: string, options?: ProxyOptions): string;
|
|
66
|
+
/**
|
|
67
|
+
* Fetch a proxied image and return the raw Response.
|
|
68
|
+
* Sends the API key via header (never exposed in the URL).
|
|
69
|
+
*/
|
|
70
|
+
proxyFetch(url: string, options?: ProxyOptions): Promise<Response>;
|
|
71
|
+
/**
|
|
72
|
+
* Build a permanent delivery URL for a public_id.
|
|
73
|
+
*/
|
|
74
|
+
permanentUrl(publicId: string): string;
|
|
75
|
+
private request;
|
|
76
|
+
}
|
|
77
|
+
export default UpThingClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export class UpThingSDKError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
body;
|
|
4
|
+
constructor(status, body) {
|
|
5
|
+
super(body.error);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.body = body;
|
|
8
|
+
this.name = "UpThingSDKError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class UpThingClient {
|
|
12
|
+
apiKey;
|
|
13
|
+
baseUrl;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.apiKey = options.apiKey;
|
|
16
|
+
this.baseUrl = (options.baseUrl ?? "https://upthing.2klabs.xyz").replace(/\/$/, "");
|
|
17
|
+
}
|
|
18
|
+
// ── Upload ──────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Upload a file from a local path (Node.js only).
|
|
21
|
+
*/
|
|
22
|
+
async uploadFile(filePath, options) {
|
|
23
|
+
const fs = await import("fs");
|
|
24
|
+
const path = await import("path");
|
|
25
|
+
const form = new FormData();
|
|
26
|
+
const blob = new Blob([fs.readFileSync(filePath)]);
|
|
27
|
+
const name = options?.filename ?? path.basename(filePath);
|
|
28
|
+
form.append("file", blob, name);
|
|
29
|
+
if (options?.projectUuid)
|
|
30
|
+
form.append("project_uuid", options.projectUuid);
|
|
31
|
+
if (options?.ttlSeconds != null)
|
|
32
|
+
form.append("ttl_seconds", String(options.ttlSeconds));
|
|
33
|
+
return this.request("POST", "/api/upload", form).then((r) => r.file);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Upload a Blob/File (works in Node.js and browsers).
|
|
37
|
+
*/
|
|
38
|
+
async uploadBlob(blob, filename, options) {
|
|
39
|
+
const form = new FormData();
|
|
40
|
+
form.append("file", blob, filename);
|
|
41
|
+
if (options?.projectUuid)
|
|
42
|
+
form.append("project_uuid", options.projectUuid);
|
|
43
|
+
if (options?.ttlSeconds != null)
|
|
44
|
+
form.append("ttl_seconds", String(options.ttlSeconds));
|
|
45
|
+
return this.request("POST", "/api/upload", form).then((r) => r.file);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Upload raw bytes with a filename.
|
|
49
|
+
*/
|
|
50
|
+
async uploadBuffer(buffer, filename, options) {
|
|
51
|
+
const blob = new Blob([new Uint8Array(buffer)]);
|
|
52
|
+
return this.uploadBlob(blob, filename, options);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Upload base64-encoded data.
|
|
56
|
+
*/
|
|
57
|
+
async uploadBase64(base64Data, filename, options) {
|
|
58
|
+
return this.request("POST", "/api/upload", {
|
|
59
|
+
filename,
|
|
60
|
+
data_base64: base64Data,
|
|
61
|
+
project_uuid: options?.projectUuid,
|
|
62
|
+
ttl_seconds: options?.ttlSeconds,
|
|
63
|
+
}).then((r) => r.file);
|
|
64
|
+
}
|
|
65
|
+
// ── Proxy ───────────────────────────────────────────────────────────
|
|
66
|
+
/**
|
|
67
|
+
* Build a proxy query-string URL (without auth). Useful when the
|
|
68
|
+
* project uses `domain` auth mode and no API key is needed in the
|
|
69
|
+
* request — e.g. for `<img src>` on an authorized domain.
|
|
70
|
+
*/
|
|
71
|
+
proxyUrl(url, options) {
|
|
72
|
+
const params = new URLSearchParams({ url });
|
|
73
|
+
if (options?.projectUuid)
|
|
74
|
+
params.set("project_uuid", options.projectUuid);
|
|
75
|
+
if (options?.ttlSeconds != null)
|
|
76
|
+
params.set("ttl_seconds", String(options.ttlSeconds));
|
|
77
|
+
return `${this.baseUrl}/?${params.toString()}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fetch a proxied image and return the raw Response.
|
|
81
|
+
* Sends the API key via header (never exposed in the URL).
|
|
82
|
+
*/
|
|
83
|
+
async proxyFetch(url, options) {
|
|
84
|
+
const target = this.proxyUrl(url, options);
|
|
85
|
+
const res = await fetch(target, {
|
|
86
|
+
headers: { "X-API-Key": this.apiKey },
|
|
87
|
+
});
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}`, code: 0 }));
|
|
90
|
+
throw new UpThingSDKError(res.status, err);
|
|
91
|
+
}
|
|
92
|
+
return res;
|
|
93
|
+
}
|
|
94
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
95
|
+
/**
|
|
96
|
+
* Build a permanent delivery URL for a public_id.
|
|
97
|
+
*/
|
|
98
|
+
permanentUrl(publicId) {
|
|
99
|
+
return `${this.baseUrl}/f/${publicId}`;
|
|
100
|
+
}
|
|
101
|
+
// ── Internal ────────────────────────────────────────────────────────
|
|
102
|
+
async request(method, path, body) {
|
|
103
|
+
const headers = {
|
|
104
|
+
"X-API-Key": this.apiKey,
|
|
105
|
+
};
|
|
106
|
+
let requestBody;
|
|
107
|
+
if (body instanceof FormData) {
|
|
108
|
+
requestBody = body;
|
|
109
|
+
}
|
|
110
|
+
else if (body != null) {
|
|
111
|
+
headers["Content-Type"] = "application/json";
|
|
112
|
+
requestBody = JSON.stringify(body);
|
|
113
|
+
}
|
|
114
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
115
|
+
method,
|
|
116
|
+
headers,
|
|
117
|
+
body: requestBody,
|
|
118
|
+
});
|
|
119
|
+
if (!res.ok) {
|
|
120
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}`, code: 0 }));
|
|
121
|
+
throw new UpThingSDKError(res.status, err);
|
|
122
|
+
}
|
|
123
|
+
return res.json();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export default UpThingClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "upthing-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript SDK for upthing.2klabs.xyz — upload files, proxy images",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.7.0",
|
|
23
|
+
"typescript": "^5.7"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"upthing",
|
|
28
|
+
"file-upload",
|
|
29
|
+
"image-proxy",
|
|
30
|
+
"s3",
|
|
31
|
+
"cdn"
|
|
32
|
+
]
|
|
33
|
+
}
|