
Security News
Lovable’s OJ Rewrites Vite’s Dev Server in Rust as AI Lowers the Cost of Forking Open Source
Lovable’s OJ rewrites Vite’s dev server in Rust, reducing memory use and preview times as AI lowers the cost of open source reimplementation.
@encody/vue
Advanced tools
TUS-based resumable upload queue with a Vue 3 composable. Handles concurrency, retries, SSE status updates, file validation, and error codes out of the box.
npm install @encody/vue
import { useEncody } from '@encody/vue'
const { add, clear, files, isActive } = useEncody({
token: () => getTokenFromBackend()
})
All options accept a raw value or a Vue ref — the composable stays reactive either way.
Live demo and playground: demo.encody.io
| Option | Default | Description |
|---|---|---|
baseUrl | 'https://app.encody.io' | API origin. Override for on-prem or enterprise installs. |
endpoint | {baseUrl}/media/upload | TUS upload endpoint. Derived from baseUrl if omitted. |
sseUrl | {baseUrl}/api/upload-events | SSE endpoint for real-time status updates. Derived from baseUrl if omitted. |
token | — | async () => string — called before each upload and on SSE reconnect. Must return a short-lived JWT. Never expose your API key to the browser — request the token from your own backend, which exchanges the API key for a JWT against the Encody API. |
concurrency | 3 | Max parallel uploads. |
retryDelays | [0, 3000, 10000, 30000] | Backoff delays in ms between TUS retries. Pass [] to disable. |
maxSize | null | Max file size in bytes. null = unlimited. |
allowedTypes | [] | Allowed MIME types. Supports wildcards (image/*). Empty = all types allowed. |
The token function is called by the SDK before every upload and on SSE reconnect. It must return a short-lived JWT issued by the Encody API.
Your API key must never leave your server. The recommended flow is:
Browser → POST /token → Your backend → POST /api/token → Encody API
(Authorization: Bearer ek_...)
Minimal Node.js token endpoint (no dependencies):
// token-server.mjs
import { createServer } from 'node:http'
const {
PORT = 3000,
ENCODY_API_KEY,
ENCODY_BASE_URL = 'https://app.encody.io'
} = process.env
createServer(async (req, res) => {
if (req.method !== 'POST' || req.url !== '/token') {
res.writeHead(404).end()
return
}
const { token } = await fetch(`${ENCODY_BASE_URL}/api/token`, {
method: 'POST',
headers: { Authorization: `Bearer ${ENCODY_API_KEY}` }
}).then(r => r.json())
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ token }))
}).listen(PORT)
ENCODY_API_KEY=ek_... node token-server.mjs
Then pass it to useEncody:
const { add, files } = useEncody({
token: async () => {
const res = await fetch('/token', { method: 'POST' })
return (await res.json()).token
}
})
const {
add, // add files to the queue
clear, // clear the queue
files, // ComputedRef<FileRecord[]> — reactive queue state
isActive, // ComputedRef<boolean> — true while any upload is in progress
file, // (id) => { retry, cancel, pause, resume, updateMeta }
useFile, // (id | Ref<id>) => ComputedRef<FileRecord> — reactive single file
useBatch, // (batchId) => { files, progress, isComplete, hasFailed }
on, // (event, handler) => void
off, // (event, handler) => void
} = useEncody(options)
add(input, options?)// single file
add(file)
// multiple files — auto-grouped as a batch
add([file1, file2])
// with custom meta (stored to DB, accessible server-side)
add(files, { meta: { folderId: '123', tag: 'avatar' } })
// per-file meta
add([
{ file: fileA, meta: { tag: 'cover' } },
{ file: fileB, meta: { tag: 'thumb' } }
])
file(id)file(id).retry()
file(id).cancel()
file(id).pause()
file(id).resume()
file(id).updateMeta({ tag: 'updated' })
on('file:uploading', ({ fileId, filename, size, meta }) => {})
on('file:ready', ({ fileId, url, meta }) => {})
on('file:failed', ({ fileId, code, error, meta }) => {})
on('file:progress', ({ fileId, progress, meta }) => {})
on('batch:complete', ({ batchId, files }) => {})
on('batch:failed', ({ batchId, failed, succeeded }) => {})
on('sse:error', () => {})
file(id).downloadUrl()Returns a Promise resolving to the authenticated download URL. Works with queue IDs (resolves the server file ID automatically) or raw server file IDs fetched from a REST listing — the caller doesn't need to know which kind of ID it has.
// from the upload queue
const url = await file(record.id).downloadUrl()
// from a REST listing — works identically
const url = await file(upload.fileId).downloadUrl()
const url = await file(upload.id).downloadUrl()
// in a Vue template
<a :href="await file(record.id).downloadUrl()">Download</a>
file(id).download(filename?)Fetches the file with auth and triggers a browser download dialog — no signed URLs needed. Uses the JWT from your token getter if configured, otherwise falls back to credentials: 'include' for session auth.
// from the upload queue
await file(record.id).download()
await file(record.id).download('custom-name.jpg')
// from a REST listing — works identically
await file(upload.fileId).download()
// in a template
<button @click="file(record.id).download(record.name)">Download</button>
Both methods accept any server-side id — the server resolves either format automatically.
FileRecord shape{
id: string,
status: 'queued' | 'uploading' | 'processing' | 'ready' | 'failed' | 'cancelled',
progress: number, // 0–99 during upload, 99 during processing, 100 when ready
name: string,
size: number,
mimeType: string,
meta: object,
batchId: string | null,
error: string | null,
errorCode: ErrorCode | null,
url: string | null, // populated when status === 'ready'
serverFileId: string | null, // server-side file ID, populated when status === 'ready' — use for download()
}
import { defaultMessages, EncodyError, ErrorCode } from '@encody/vue'
ErrorCode.TOKEN_EMPTY // token() returned falsy
ErrorCode.UPLOAD_FAILED // network or server error
ErrorCode.VIRUS_DETECTED // antivirus flagged the file
ErrorCode.SSE_ERROR // SSE connection failed repeatedly
ErrorCode.FILE_TOO_LARGE // exceeds maxSize
ErrorCode.FILE_TYPE_NOT_ALLOWED // not in allowedTypes
Use defaultMessages[code] as fallbacks, override per code for i18n:
const messages = {
[ErrorCode.FILE_TOO_LARGE]: 'Die Datei ist zu groß'
}
function errorMessage(file) {
return messages[file.errorCode] ?? file.error
}
import { createEncody } from '@encody/vue/core'
const instance = createEncody({ token, baseUrl })
instance.add(files)
instance.on('file:ready', handler)
instance.destroy()
MIT
Marcus Spiegel spiegel@uscreen.de — published and supported by u|screen
FAQs
Encody client SDK — resumable upload queue with Vue composables
The npm package @encody/vue receives a total of 6 weekly downloads. As such, @encody/vue popularity was classified as not popular.
We found that @encody/vue demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Lovable’s OJ rewrites Vite’s dev server in Rust, reducing memory use and preview times as AI lowers the cost of open source reimplementation.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.