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 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
@@ -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
+ }