skills/kjanat/glb-compressor/glb-compressor-server

glb-compressor-server

SKILL.md

glb-compressor Server

HTTP compression server built on Bun.serve(). Provides synchronous and streaming (SSE) compression endpoints with full CORS support.

Starting the Server

# Production (from installed package)
glb-server

# Development (hot reload)
bun run dev

# Custom port
PORT=3000 bun run start

Default port: 8080 (override via PORT env var).

Endpoints

GET /healthz

Health check. Returns 200 ok.

POST /compress

Synchronous compression. Returns compressed GLB binary.

Accepts: multipart/form-data (field: file) or raw application/octet-stream.

Query params / form fields:

  • preset - Compression preset: default, balanced, aggressive, max
  • simplify - Mesh simplification ratio (0, 1), e.g. 0.5

Form fields override query params when both are provided.

Response headers:

Header Description
X-Request-ID UUID tracking this request
X-Original-Size Input file size in bytes
X-Compressed-Size Output file size in bytes
X-Compression-Method gltfpack or meshopt
X-Compression-Ratio Percentage reduction (e.g. 84.1)
Content-Disposition Suggested download filename

Example:

# Multipart upload
curl -X POST -F "file=@model.glb" \
  "http://localhost:8080/compress?preset=aggressive" \
  -o compressed.glb

# Raw binary upload
curl -X POST --data-binary @model.glb \
  -H "Content-Type: application/octet-stream" \
  "http://localhost:8080/compress?preset=balanced" \
  -o compressed.glb

POST /compress-stream

SSE streaming compression with real-time progress. Multipart only.

Content-Type: text/event-stream

SSE events:

Event Data shape
log { message: string }
result { requestId, filename, data (base64), originalSize, compressedSize, ratio, method }
error { message, requestId, code }

Stream closes after result or error event.

Example (JavaScript):

const form = new FormData();
form.append('file', glbFile);

const response = await fetch(
	'http://localhost:8080/compress-stream?preset=aggressive',
	{ method: 'POST', body: form },
);

const reader = response.body.getReader();
const decoder = new TextDecoder();

// Parse SSE events from the stream
while (true) {
	const { done, value } = await reader.read();
	if (done) break;
	const text = decoder.decode(value);
	// Parse "event: <type>\ndata: <json>\n\n" format
	console.log(text);
}

Error Responses

All errors return structured JSON:

{
	"error": { "code": "ERROR_CODE", "message": "Human-readable message" },
	"requestId": "uuid"
}

Error codes:

Code HTTP Description
NO_FILE_PROVIDED 400 No file in form data
INVALID_GLB 400 Failed GLB magic byte validation
FILE_TOO_LARGE 413 Exceeds 100 MB limit
INVALID_CONTENT_TYPE 415 Non-multipart on streaming endpoint
COMPRESSION_FAILED 500 Pipeline error during compression

CORS

Full CORS enabled on all endpoints. Exposed headers: X-Request-ID, X-Original-Size, X-Compressed-Size, X-Compression-Method, X-Compression-Ratio.

Limits

  • Max file size: 100 MB
  • gltfpack timeout: 60 seconds per file

Architecture Notes

  • import.meta.main guard: safe to import server/main.ts as a library without starting the server.
  • Request ID (X-Request-ID) is generated per request and logged server-side.
  • The /compress-stream endpoint sends the compressed GLB as base64 in the result SSE event (~33% larger than binary). For large files near the 100 MB limit, prefer /compress.
Weekly Installs
5
First Seen
Feb 20, 2026
Installed on
mcpjam5
claude-code5
replit5
junie5
windsurf5
zencoder5