
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
trelae-files
Advanced tools
A powerful TypeScript/JavaScript SDK for managing file storage, upload, manipulation, and access control via the Trelae Files.
To learn more about how the Trelae Files works — including concepts like projects, namespaces, file uploads, folder structures, and advanced use cases — refer to the official documentation: Get started with Trelae Files
This guide provides a detailed walkthrough of core ideas and integration examples to help you make the most of the Trelae SDK.
npm install trelae-files
# or
yarn add trelae-files
# or
pnpm add trelae-files
import { Trelae } from 'trelae-files';
const trelae = new Trelae({
apiKey: 'your_api_key_here'
});
const namespace = trelae.namespace('namespace-id');
const file = trelae.file('file-id');
await file.delete();
const trelae = new Trelae({
apiKey: 'your_api_key_here', // Required
devMode: true // Optional
});
A namespace is a logical container for organizing files. Every file belongs to a namespace.
"uploads", "reports").The fundamental object in the SDK.
Supports:
image/* files)Files can be grouped using the location property (e.g., "assets/images"), simulating folders.
A virtual structure that helps organize files.
"images/logo.png")A secure token used to authenticate Trelae API requests. Scoped to your project.
const namespace = trelae.createNamespace({ name: 'my-namespace' });
const namespace = trelae.namespace('namespace-id');
await namespace.delete();
await namespace.clear();
const metadata = await namespace.getMetaData();
const { id, uploadUrl } = await namespace.getUploadUrl({
name: 'image.png',
location: 'uploads/images',
fileType: 'image/png',
expiry: '2025-08-01T00:00:00.000Z' // Optional
});
// Step 1: Start upload
const { id, uploadId, partCount, urls } = await namespace.startMultipartUpload({
name: 'big.zip',
location: 'backups',
size: 200_000_000 // bytes
});
// Step 2: Complete upload
await namespace.completeMultipartUpload({
fileId: id,
uploadId,
parts: [
{ partNumber: 1, etag: 'etag-1' },
{ partNumber: 2, etag: 'etag-2' },
]
});
// Optional: Abort upload
await namespace.abortMultipartUpload({ fileId: id, uploadId });
const folder = await namespace.createFolder('assets', 'uploads');
const folderById = namespace.folder('folder-id');
const folderByPath = namespace.folder('uploads', 'assets');
const file = namespace.file('file-id');
const fileByPath = namespace.file('uploads/images', 'logo.png');
await file.delete();
await file.move({
newLocation: 'reports',
newName: 'monthly.pdf'
});
const copy = await file.copy({
newLocation: 'backups',
newName: 'monthly-copy.pdf'
});
const url = await file.getDownloadUrl();
Available only for files with MIME type image/*.
await file
.resize({ width: 300 })
.rotate({ angle: 90 })
.grayscale({})
.save();
resize({ width, height })crop({ width, height })rotate({ angle })flip({ horizontal, vertical })convert({ format })grayscale({})compress({ quality })border({ top, color })const files = trelae.files(['file-id-1', 'file-id-2']);
await files.delete();
const { files, folders } = await namespace.getFiles({
location: 'assets',
limit: 20,
page: 1
});
const { files, folders } = await namespace.getFiles({
query: 'assets',
});
const result = await trelae.search({
query: 'assets',
});
await namespace.deleteLocation('old-assets');
All SDK methods throw structured errors using Utility.
NamespaceErrorFileDeleteErrorFileUploadUrlErrorFileOperationErrorFileDownloadUrlErrorDeleteLocationErrorClearNamespaceErrorSearchErrorimport { handleAPIError } from 'trelae-files';
handleAPIError(data, {
error: 'internal-error',
message: 'Something went wrong'
});
Handles:
{ error, message }{ error: { issues: [...] } }interface TrelaeConfig {
apiKey: string;
devMode?: boolean;
}
interface FileMetaData {
fileType: string;
size: number;
status: string;
createdAt: string;
}
interface FileManipulationResult {
success: boolean;
message: string;
}
{ operation: "resize", params: { width: 300 } }
{ operation: "grayscale", params: {} }
{ operation: "convert", params: { format: "png" } }
Powerful PDF utilities are available on File instances whose fileType is application/pdf.
const pdf = trelae.file('pdf-file-id');
const { pagesExtracted, files } = await pdf.pdfToImages({
// Convert pages 1 to 3 (inclusive) to PNGs
pageSelection: [{ start: 1, end: 3 }],
format: 'png'
});
// Each entry in `files` is a Trelae `File` you can download, move, etc.
// Save Markdown + extracted images back to Trelae (default)
const saved = await pdf.pdfToMarkdown({ saveMarkdown: true, location: 'ocr/results' });
// saved.file -> Markdown File
// saved.images -> Array<File> (images detected in the PDF)
// saved.markdown -> string
// Or get raw OCR content without saving
const raw = await pdf.pdfToMarkdown({ saveMarkdown: false });
// raw.markdown -> string
// raw.images -> Inline image metadata/base64
await pdf.compress().save(); // Optimizes streams and font subsets
Video manipulation is queued via chainable methods and executed on save().
By default, video jobs run asynchronously; poll with getVideoStatus() or pass save({ mode: 'sync' }).
const video = trelae.file('video-id');
const result = await video
.changeVideoFormat({ format: 'mp4', videoCodec: 'libx264', audioCodec: 'aac', crf: 23, preset: 'medium' })
.trimVideo({ start: 0, duration: 30 })
.scaleVideo({ width: 1280 }) // keepAspect auto-enforced
.addTextVideo({ text: 'Demo', x: 60, y: 60, fontSize: 32 })
.save({ mode: 'async' }); // default is 'async'
// Poll if queued
if (result.type === 'video' && (result.status === 'queued' || result.status === 'running')) {
const status = await video.getVideoStatus(result.operationId);
}
changeVideoFormat({ format, videoCodec?, audioCodec?, crf?, preset? })trimVideo({ start?, duration?, end? })scaleVideo({ width?, height?, keepAspect? })rotateFlipVideo({ rotate?, hflip?, vflip? })removeAudio()speedVideo({ videoSpeed?, audioSpeed? })cropVideo({ x, y, width, height })muteSegment({ start, duration })fadeVideo({ type: 'in' | 'out', startTime, duration })addTextVideo({ text, x, y, fontSize?, fontColor?, fontFile?, startTime?, endTime? })addBorderVideo({ color?, top?, bottom?, left?, right? })reverseVideo({ video?, audio? })audioFadeVideo({ type: 'in' | 'out', startTime, duration })motionBlurVideo({ frames?, weights?, outputFps? })grayscaleVideo()colorEQVideo({ brightness?, contrast?, saturation?, gamma? })hueRotateVideo({ degrees, saturation? })boxBlurVideo({ lumaRadius?, lumaPower?, startTime?, endTime? })sharpenVideo({ amount?, size? })volumeVideo({ gain })const queued = await video.audioExtractAsync({
namespaceId: 'ns-123',
location: 'audio',
filename: 'clip.mp3',
format: 'mp3',
bitrateKbps: 192,
});
const status = await video.getAudioExtractStatus(queued.operationId);
// Or block until done:
const done = await video.audioExtractSync({
namespaceId: 'ns-123',
location: 'audio',
filename: 'clip.opus',
format: 'opus',
poll: { intervalMs: 1500, timeoutMs: 5 * 60 * 1000 }
});
Audio manipulation is available on any File whose MIME type is audio/*.
Operations are chainable and executed once you call .save().
const audio = trelae.file('audio-id');
const result = await audio
.changeAudioFormat({ format: 'mp3', audioCodec: 'libmp3lame', bitrateKbps: 192 })
.trimAudio({ start: 10.5, end: 43.7 })
.speedAudio({ atempo: 1.25 })
.volumeAudio({ gainDb: -3 })
.fadeAudio({ type: 'in', startTime: 0, duration: 5 })
.equalizerAudio({ freqHz: 1000, gainDb: 4 })
.lowpassAudio({ cutoffHz: 1200 })
.highpassAudio({ cutoffHz: 200 })
.normalizeAudio({ targetI: -16, targetTP: -1 })
.muteAudioSegment({ start: 30, duration: 5 })
.removeSilenceAudio({ startThresholdDb: -40, stopThresholdDb: -35 })
.setAudioChannels({ channels: 2, layout: 'stereo' })
.setAudioSampleRate({ sampleRateHz: 44100 })
.save({ mode: 'sync' });
changeAudioFormat({ format, audioCodec?, bitrateKbps?, sampleRateHz?, channels? })trimAudio({ start?, duration?, end? })speedAudio({ atempo })volumeAudio({ gain?, gainDb? })fadeAudio({ type: 'in'|'out', startTime, duration })equalizerAudio({ freqHz, widthQ?, gainDb })lowpassAudio({ cutoffHz, order? })highpassAudio({ cutoffHz, order? })normalizeAudio({ targetI?, targetTP?, targetLRA?, dualMono? })muteAudioSegment({ start, duration })removeSilenceAudio({ startThresholdDb?, startDuration?, stopThresholdDb?, stopDuration? })setAudioChannels({ channels, layout? })setAudioSampleRate({ sampleRateHz })compressorAudio({ threshold, ratio?, attack?, release?, knee?, makeupGain? }) Dynamic range compression.echoAudio({ inGain?, outGain?, delays[], decays[] }) Multi-delay echo (delays > 0, decays between 0–1).panAudio({ layout?, mapping }) Channel panning / remapping.denoiseAudio({ reductionDb?, noiseType? }) Simple denoiser.bassBoostAudio({ gainDb?, frequency?, widthQ? }) Low-frequency enhancement.trebleBoostAudio({ gainDb?, frequency?, widthQ? }) High-frequency enhancement.await trelae
.file('audio-id')
.trimAudio({ start: 5, duration: 20 })
.echoAudio({ delays: [200, 400], decays: [0.6, 0.3] })
.compressorAudio({ threshold: -18, ratio: 3 })
.bassBoostAudio({ gainDb: 6, frequency: 120 })
.save({ mode: 'sync' });
const framesJob = await video.extractFramesAsync({
namespaceId: 'ns-123',
location: 'frames',
filename: 'frames.zip',
start: 2,
endTime: 7
});
const framesStatus = await video.getExtractFramesStatus(framesJob.operationId);
// Synchronous helper:
await video.extractFramesSync({
namespaceId: 'ns-123',
location: 'frames',
filename: 'frames.zip'
});
Create archives or expand them in-place. Both operations have Async and Sync helpers.
// Enqueue a new ZIP from multiple files
const { operationId, file: zipPlaceholder } = await trelae.zipAsync({
fileIds: ['file-1', 'file-2', 'file-3'],
namespaceId: 'ns-123',
zipName: 'archive.zip',
location: 'exports'
});
// Poll job status
const zipStatus = await trelae.getZipStatus(operationId);
// Or block until completion
const zipped = await trelae.zipSync({
fileIds: ['file-1', 'file-2'],
namespaceId: 'ns-123',
zipName: 'bundle.zip',
location: 'exports'
});
// Enqueue extraction of an existing ZIP file
const unzipJob = await trelae.unzipAsync({
fileId: 'zip-file-id',
namespaceId: 'ns-123',
location: 'extracted'
});
const unzipStatus = await trelae.getUnzipStatus(unzipJob.operationId);
// Or block until finished
const unzipped = await trelae.unzipSync({
fileId: 'zip-file-id',
namespaceId: 'ns-123',
location: 'extracted'
});
Trelae Files includes content-aware workflows that combine storage with media intelligence:
file.removeBackground().file.pdfToMarkdown({ saveMarkdown }).File objects or base64 images.These features compose seamlessly with existing manipulation chains and signed URL access.
In addition to image manipulation, Trelae now supports text extraction and AI-powered descriptions for image files (image/*).
Extract visible text from an image into Markdown:
const img = trelae.file("file-id");
// Raw Markdown text (default)
const raw = await img.imageToMarkdown();
console.log(raw.markdown);
// Save extracted Markdown back to Trelae
const saved = await img.imageToMarkdown({ saveMarkdown: true, location: "ocr-results" });
console.log(saved.file, saved.markdown);
saveMarkdown: false → returns raw Markdown.saveMarkdown: true → saves .md file to Trelae.Generate a detailed Markdown description of an image using AI (scene, objects, colors, logos, text, etc.):
const img = trelae.file("file-id");
// Raw description
const desc = await img.describeImage();
console.log(desc.markdown);
// Save description into Trelae
const savedDesc = await img.describeImage({ saveMarkdown: true, location: "descriptions" });
console.log(savedDesc.file, savedDesc.markdown);
saveMarkdown: false → returns raw Markdown description.saveMarkdown: true → saves .md file to Trelae.Convert a .md file to HTML or PDF, with optional custom CSS.
const md = trelae.file("markdown-file-id");
const html = await md.markdownConvert({
format: "html",
location: "exports",
name: "readme-render",
css: "body{font-family:Inter,system-ui}",
useDefaultStyles: true
});
const pdf = await md.markdownConvert({
format: "pdf",
location: "exports",
name: "readme"
});
const csv = trelae.file("csv-file-id");
const toXlsx = await csv.csvConvert({
format: "xlsx",
location: "exports",
name: "september-report"
});
const toPdf = await csv.csvConvert({ format: "pdf" });
const toTsv = await csv.csvConvert({ format: "tsv" });
const toJson = await csv.csvConvert({ format: "json" });
Works for files uploaded as text/csv, application/csv, or some text/plain CSVs.
const xlsx = trelae.file("excel-file-id");
const toCsv = await xlsx.excelConvert({
format: "csv",
sheet: "Expenses 2025", // or 0 (index)
location: "exports",
name: "expenses-sept"
});
const toPdf = await xlsx.excelConvert({ format: "pdf" });
const toTsv = await xlsx.excelConvert({ format: "tsv" });
const toJson = await xlsx.excelConvert({ format: "json" });
const book = trelae.file("excel-file-id");
const queued = await book
.addSheet({ name: "Data" })
.insertRows({
sheet: "Data",
startRow: 1,
count: 3,
values: [
["Date", "Item", "Qty", "Price"],
["2025-09-01", "Widget", 3, 9.99],
["2025-09-02", "Gadget", 2, 14.5]
]
})
.setColumnWidth({ sheet: "Data", col: 2, width: 22 })
.dataValidation({
sheet: "Data",
range: "A2:A100",
type: "list",
formulae: ['"Yes,No,Maybe"']
})
.save({ mode: "async" }); // excel jobs default to async
if (queued.type === "excel" && (queued.status === "queued" || queued.status === "running")) {
const status = await book.getExcelStatus(queued.operationId);
// status.status → 'queued' | 'running' | 'completed' | 'failed'
}
const ppt = trelae.file("ppt-file-id");
const res = await ppt.pptToPdf({
location: "exports",
name: "slides-sept"
});
// res.file → the created PDF File
const deck = trelae.file("ppt-file-id");
const r = await deck
.pptSetProps({ title: "Quarterly Review", author: "Ops Team" })
.pptSetLayout({ preset: "LAYOUT_16x9" })
.pptAddSlide({ layoutName: "TITLE", notes: "Intro slide" })
.pptAddText({
slide: 1,
text: [{ text: "Q3 Results", options: { bold: true, fontSize: 38 } }],
box: { w: 10, h: 1.6, center: true },
options: { margin: 8 }
})
.pptAddImage({
slide: 1,
image: { fileId: "logo-file-id" },
box: { w: 1.8, h: 1.8, x: 0.5, y: 0.5 }
})
.save({ mode: "async" });
Supported PPT operations (high-level):
pptSetProps, pptSetLayout, pptAddSlide, pptSetSlideBackground, pptSetNotespptAddText, pptUpdateText, pptAddImage, pptAddShape, pptSetBoxpptAddTable, pptUpdateTableCells, pptAddChart, pptUpdateChartDatapptClearSlide, pptMoveSlide, pptReorderSlidesif (r.type === "ppt" && (r.status === "queued" || r.status === "processing")) {
const s = await deck.getPptStatus(r.operationId);
}
Generate an edited image by describing the change; optionally provide reference image IDs.
const edited = await trelae.file("PRIMARY_IMAGE_ID").imageEdit({
prompt: "Make the sky sunset orange with soft clouds",
outputFormat: "jpeg", // png | jpeg | webp (default: png)
location: "edits/sky",
extraImageIds: ["REF_IMAGE_ID_1", "REF_IMAGE_ID_2"]
});
// edited.file → new File with the edit result
The primary file must be an image/*. References can guide style/content.
Use these to poll long-running tasks:
// Video manipulations:
await file.getVideoStatus(operationId);
// Audio manipulations:
await file.getAudioStatus(operationId);
// Excel manipulations:
await file.getExcelStatus(operationId);
// PPT manipulations:
await file.getPptStatus(operationId);
// Audio extract:
await file.getAudioExtractStatus(operationId);
// Frame extract:
await file.getExtractFramesStatus(operationId);
Most media manipulations default to async; pass .save({ mode: "sync" }) where supported if you need to block until completion.
The SDK exposes additional types used by the new APIs (importable from trelae-files):
PdfToImageOptions / PdfToImageResultFiles — options & result for PDF→Image.PdfToMarkdownOptions / PdfToMarkdownResultFile — options & result for PDF→Markdown (OCR).InlineImageMeta — inline base64 image metadata returned when not saving OCR results.VideoOperation — internal representation for video transformations.VideoStatusResponse — status shape for queued video operations.Tip: you typically won’t need these types directly unless you’re writing wrappers; the method signatures are strongly typed.
MIT License — see LICENSE file.
FAQs
JavaScript SDK for Trelae
We found that trelae-files 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.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.