Client SDK
Browser SDK for auth, upload, download, and built-in pickers.
@xeonr/uploads-sdk provides a browser-first SDK for integrating upl.im into your application. It handles OAuth authentication, file uploads and downloads, optional built-in picker UI, real-time event streaming, and React bindings.
Install
pnpm add @xeonr/uploads-sdk @xeonr/uploads-protocolQuick start
import { Upl } from '@xeonr/uploads-sdk';
const upl = new Upl({
clientId: 'your-client-id',
env: 'prod',
});
// Upload a file
const image = new Blob([/* bytes */], { type: 'image/png' });
const uploaded = await upl.upload(
{
filename: 'example.png',
contentType: 'image/png',
bucketHint: 'my-bucket',
path: '/images',
},
image,
);
// Download metadata
const resolved = await upl.download({
bucketHint: 'my-bucket',
uploadId: uploaded.uploadId,
});
// Download content
const { data } = await upl.downloadContent({
bucketHint: 'my-bucket',
uploadId: uploaded.uploadId,
as: 'blob',
});Configuration
UplOptions
const upl = new Upl({
clientId: 'your-client-id', // Required — OAuth client ID
env: 'prod', // 'prod' | 'stage' | 'dev' (default: 'prod')
scopes: [ // OAuth scopes requested during login
'upl:bucket:read',
'upl:uploads:create',
'profile',
],
ui: { disabled: false }, // Disable built-in picker UI
auth: { /* see Authentication */ },
});| Option | Type | Default | Description |
|---|---|---|---|
clientId | string | — | OAuth client ID (required) |
env | 'prod' | 'stage' | 'dev' | 'prod' | API environment |
scopes | string[] | ['upl:bucket:read', 'upl:uploads:create', 'profile'] | OAuth scopes |
ui.disabled | boolean | false | Disable built-in bucket/folder/file pickers |
auth | UplAuthOptions | — | Advanced auth configuration |
Environments
| Env | API endpoint | Auth endpoint |
|---|---|---|
prod | uploads-api.xeonr.io | auth.xeonr.io |
stage / dev | uploads-api.xeonr.dev | auth.xeonr.dev |
Upload API
const upload = await upl.upload(request, data);Request fields
| Field | Type | Description |
|---|---|---|
filename | string | Display filename (required) |
bucketId | string | Target bucket ID |
bucketHint | string | Fuzzy bucket match (name, alias, or partial) |
path | string | Directory path (default: /) |
contentType | string | MIME type |
size | number | File size in bytes (inferred from Blob if omitted) |
slug | string | URL-safe slug (defaults to filename) |
tags | string[] | Tags for categorisation |
pickerConfig | UploadPickerConfig | Picker UI configuration |
Data types
The data parameter accepts Blob, ArrayBuffer, or Uint8Array. Files over 8MB automatically use multipart upload.
Upload flow
- Resolve the target bucket (by
bucketId,bucketHint, or interactive picker) - Show folder/overwrite picker UI (unless disabled)
- Request an upload slot from the API
- Upload file content (direct or multipart for large files)
- Finalise the upload record
Picker configuration
await upl.upload({
filename: 'report.pdf',
bucketHint: 'my-bucket',
pickerConfig: {
enabled: true, // Show folder picker (default: true when UI enabled)
allowOverwrite: true, // Allow replacing existing files
overwriteTarget: 'upl_123', // Pre-select a file to overwrite
allowFilenameEdit: true, // Allow editing filename in picker
allowSlugEdit: false, // Allow editing slug in picker
selectionFilter: {
extensions: ['pdf', 'doc', 'docx'],
types: [UploadType.DOCUMENT],
},
},
}, data);Download API
Resolve an upload
const upload = await upl.download({
bucketHint: 'my-bucket',
uploadId: 'upl_123',
});| Field | Type | Description |
|---|---|---|
bucketId / bucketHint | string | Bucket identifier |
uploadId | string | Direct upload ID lookup |
uploadPath | string | Full path lookup |
path | string | Directory filter |
filename | string | Exact filename filter |
type | UploadType | Filter by file type |
tags | string[] | Filter by tags |
nonInteractive | boolean | Throw on ambiguity instead of showing picker |
pickerConfig | DownloadPickerConfig | Picker UI configuration |
If filters match multiple files, the picker UI opens. With nonInteractive: true, ambiguous or no-match cases throw an error.
Download content
const { upload, data } = await upl.downloadContent({
bucketHint: 'my-bucket',
uploadId: 'upl_123',
as: 'blob', // 'blob' | 'arrayBuffer' | 'text' | 'stream'
});The as parameter controls the return type of data:
| Value | Return type |
|---|---|
'blob' (default) | Blob |
'arrayBuffer' | ArrayBuffer |
'text' | string |
'stream' | ReadableStream<Uint8Array> |
React hooks
import { UplProvider, useUpl } from '@xeonr/uploads-sdk';
function Root() {
return (
<UplProvider
options={{
clientId: 'your-client-id',
env: 'prod',
scopes: ['upl:user:read', 'upl:uploads:create'],
}}
>
<App />
</UplProvider>
);
}
function Uploader() {
const { auth, upload } = useUpl();
return (
<div>
{!auth.isAuthenticated && (
<button onClick={() => auth.login()}>Login</button>
)}
<button
disabled={upload.loading}
onClick={async () => {
const file = new Blob(['hello'], { type: 'text/plain' });
await upload.run({ filename: 'hello.txt' }, file);
}}
>
Upload
</button>
</div>
);
}useUpl() return value
const {
client, // Upl instance
auth, // Auth state and methods
upload, // Upload action
download, // Download action
downloadContent, // Download content action
} = useUpl();Auth state
auth.state // UplUserState { status, token, user }
auth.loading // boolean
auth.error // Error | null
auth.isAuthenticated // boolean
auth.login() // Start OAuth login
auth.forceLogin() // Force re-login (clears cached token)
auth.logout() // Clear token
auth.refresh() // Refresh auth stateAction hooks
upload, download, and downloadContent share the same interface:
action.run(...) // Execute the action (same args as the Upl method)
action.loading // boolean
action.error // Error | null
action.data // Result | null
action.reset() // Clear stateProvider options
<UplProvider
options={uplOptions}
authOptions={{
loadOnMount: true, // Auto-load auth state on mount (default: true)
refreshOnFocus: true, // Refresh auth when window regains focus (default: true)
}}
>Non-interactive mode
For server-like or headless flows, disable the UI and pass explicit IDs:
const upl = new Upl({
clientId: 'your-client-id',
ui: { disabled: true },
});
await upl.download({
bucketId: 'bucket-id',
uploadId: 'upload-id',
nonInteractive: true,
});In non-interactive mode, missing or ambiguous bucket/upload selection will throw instead of opening a picker.
Script tag usage
For non-module environments, use the embed build:
<script src="https://cdn.example.com/@xeonr/uploads-sdk/embed.js"></script>
<script>
const upl = new window.Upl({ clientId: 'your-client-id' });
// or: new window.UplSDK.Upl(...)
</script>The embed script exposes:
window.Upl— theUplclasswindow.UplUploadType— theUploadTypeenumwindow.UplSDK— namespace containing both