@clawdyup/core
Core package API reference
createClawdy
Creates a file route builder. This is the main entry point for defining upload routes.
import { createClawdy } from "@clawdyup/core";
const f = createClawdy();The returned function f accepts a file type configuration object and returns a chainable builder.
f(fileTypes: FileRouterInput)
.input(zodSchema)
.middleware(fn)
.onUploadComplete(fn)File type configuration
The fileTypes parameter accepts the following shape:
{
image?: { maxFileSize?: FileSize; maxFileCount?: number };
video?: { maxFileSize?: FileSize; maxFileCount?: number };
audio?: { maxFileSize?: FileSize; maxFileCount?: number };
pdf?: { maxFileSize?: FileSize; maxFileCount?: number };
archive?: { maxFileSize?: FileSize; maxFileCount?: number };
raw?: { maxFileSize?: FileSize; maxFileCount?: number };
blob?: { maxFileSize?: FileSize; maxFileCount?: number };
}Builder chain
| Method | Signature | Description |
|---|---|---|
.input() | .input(zodSchema: ZodSchema) | Define a Zod schema for client-supplied metadata. The validated data is available in middleware. |
.middleware() | .middleware((opts: { input, req }) => metadata) | Run server-side logic before the upload starts. Return an object that becomes metadata in onUploadComplete. Throw a ClawdyError to reject the upload. |
.onUploadComplete() | .onUploadComplete((opts: { metadata, file }) => serverData) | Called after the file has been uploaded. The return value is sent back to the client as serverData. |
Example
import { createClawdy, type FileRouter } from "@clawdyup/core";
import { z } from "zod";
const f = createClawdy();
export const uploadRouter = {
profileImage: f({ image: { maxFileSize: "4MB", maxFileCount: 1 } })
.input(z.object({ userId: z.string() }))
.middleware(({ input, req }) => {
// Authenticate and authorize
return { userId: input.userId };
})
.onUploadComplete(({ metadata, file }) => {
console.log("Upload complete for user:", metadata.userId);
console.log("File URL:", file.url);
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;ClawdyError
A custom Error class. Throw it inside .middleware() to reject an upload with a meaningful message.
import { ClawdyError } from "@clawdyup/core";
.middleware(({ req }) => {
const user = getUser(req);
if (!user) throw new ClawdyError("Unauthorized");
return { userId: user.id };
})createRouteHandler
@clawdyup/core/server
Creates Next.js App Router route handlers for your upload router.
import { createRouteHandler } from "@clawdyup/core/server";
import { uploadRouter } from "./clawdy";
export const { GET, POST } = createRouteHandler({
router: uploadRouter,
config: {
apiUrl: "https://your-clawdy-server.com",
apiKey: process.env.CLAWDY_API_KEY, // optional
},
});Parameters
| Parameter | Type | Description |
|---|---|---|
router | FileRouter | Your upload router object. |
config.apiUrl | string | The URL of your Clawdy server. |
config.apiKey | string | undefined | Optional API key for authenticated requests. |
Returns
{ GET, POST } — Export these directly from a Next.js App Router route file (e.g. app/api/clawdy/route.ts).
extractRouterConfig
@clawdyup/core/server
Extracts a serializable route configuration from your upload router. Use this for SSR hydration so the client knows the allowed file types and size limits without an extra network request.
import { extractRouterConfig } from "@clawdyup/core/server";
import { uploadRouter } from "./clawdy";
const routeConfig = extractRouterConfig(uploadRouter);Returns
A RouteConfig object describing every route's allowed file types, max file size, and max file count.
uploadFiles
@clawdyup/core/client
Client-side function for uploading files programmatically, outside of the React components.
import { uploadFiles } from "@clawdyup/core/client";
const response = await uploadFiles({
endpoint: "profileImage",
files: selectedFiles,
input: { userId: "user_123" },
url: "/api/clawdy", // optional, this is the default
onProgress: (percent) => console.log(`${percent}%`),
});Files larger than 4MB are automatically chunked (7MB per chunk) with SHA-256 integrity validation and retry logic (3 attempts with exponential backoff).
Parameters
| Parameter | Type | Description |
|---|---|---|
endpoint | string | The name of the route in your FileRouter. |
files | File[] | An array of File objects to upload. |
input | object | undefined | Optional metadata that matches the .input() Zod schema on the route. |
url | string | undefined | The URL of the upload API endpoint. Defaults to "/api/clawdy". |
onProgress | (percent: number) => void | Optional callback with upload progress (0-100). |
Returns
UploadFileResponse[] — An array of upload results containing name, size, key, url, and serverData.
extractVideoThumbnail
@clawdyup/core/client
Extracts a single frame from a video file and returns it as an image File. Uses the HTML5 Canvas API — runs entirely in the browser with no server processing.
import { extractVideoThumbnail } from "@clawdyup/core/client";
const thumbnailFile = await extractVideoThumbnail(videoFile, {
seekTo: 1,
maxWidth: 480,
quality: 0.8,
format: "image/webp",
});Parameters
| Parameter | Type | Description |
|---|---|---|
file | File | A video File object. |
options.seekTo | number | Time in seconds to capture. Default: 1. Clamped to video duration for short videos. |
options.maxWidth | number | Maximum pixel width. Height scales proportionally. Default: 480. |
options.quality | number | Image quality (0–1). Default: 0.8. |
options.format | string | Output: "image/webp", "image/jpeg", or "image/png". Default: "image/webp". Falls back to JPEG if WebP is unsupported. |
Returns
Promise<File> — An image file named {originalName}_thumb.{ext}.
isVideoFile
@clawdyup/core/client
Checks whether a File is a video based on its MIME type.
import { isVideoFile } from "@clawdyup/core/client";
if (isVideoFile(file)) {
const thumb = await extractVideoThumbnail(file);
}Returns
boolean — true if file.type starts with "video/".
Types
ExtractThumbnailOptions
type ExtractThumbnailOptions = {
seekTo?: number;
maxWidth?: number;
quality?: number;
format?: "image/webp" | "image/jpeg" | "image/png";
};Options for extractVideoThumbnail. All fields are optional with sensible defaults.
FileRouter
type FileRouter = Record<string, UploadRoute>;A record mapping route names to upload route definitions created by the builder chain.
UploadedFile
type UploadedFile = {
key: string;
url: string;
name: string;
size: number;
};Represents a file that has been uploaded and stored.
UploadFileResponse
type UploadFileResponse = {
name: string;
size: number;
key: string;
url: string;
serverData: unknown;
};The response returned to the client after a successful upload, including any data returned from onUploadComplete.
FileSize
type FileSize = `${number}${"KB" | "MB" | "GB"}`;A template literal type for expressing file size limits (e.g. "4MB", "512KB", "1GB").
FileRouterInput
type FileRouterInput = {
[key in "image" | "video" | "audio" | "pdf" | "archive" | "raw" | "blob"]?: {
maxFileSize?: FileSize;
maxFileCount?: number;
};
};The configuration object passed to f() to define which file types a route accepts.
| Key | Accepted MIME types |
|---|---|
image | JPEG, PNG, WebP, GIF, SVG |
video | MP4, MOV, WebM |
audio | Audio types |
pdf | PDF documents |
archive | ZIP, 7z |
raw | Camera RAW formats (Canon, Nikon, DNG) |
blob | Catch-all for any file type |
RouteConfig
type RouteConfig = {
[key: string]: {
fileTypes: string[];
maxFileSize: string;
maxFileCount: number;
};
};A serializable representation of your upload router's configuration, returned by extractRouterConfig.
ChunkedUploadProgress
type ChunkedUploadProgress = {
phase: "hashing" | "init" | "uploading" | "completing";
chunksUploaded: number;
chunksTotal: number;
bytesUploaded: number;
bytesTotal: number;
percent: number;
};Describes the progress of a chunked upload. Files larger than 4MB are automatically chunked.