CBOR Package for Meteor
This package provides CBOR (Concise Binary Object Representation) support for Meteor applications, enabling efficient binary data transmission through DDP while maintaining backward compatibility with EJSON.
Built on cbor-x - A high-performance, standards-compliant CBOR implementation that provides robust encoding/decoding with native binary data support.
Features
- 🚀 Native File/Buffer/Blob support - Send File objects directly through Meteor methods
- 📦 Compact binary encoding - 25-70% size reduction compared to EJSON base64 encoding
- 🔄 Backward compatible - Works alongside existing EJSON without breaking changes
- 🌐 Cross-platform - Works in browsers and Node.js
- 📊 Smart format selection - Automatically chooses best encoding format
- 🏷️ Semantic tagging - Preserves exact type information
Quick Start
Installation
meteor add cbor
Basic Usage
1import { CBOR } from 'meteor/cbor'; 2 3// Basic encoding/decoding 4const data = { message: "Hello", binary: new Uint8Array([1, 2, 3]) }; 5const encoded = CBOR.encode(data); 6const decoded = CBOR.decode(encoded); 7 8// File upload example - now works seamlessly! 9const fileInput = document.getElementById('fileInput'); 10fileInput.addEventListener('change', (event) => { 11 const file = event.target.files[0]; 12 13 // This now works directly - no conversion needed! 14 Meteor.call('uploadFile', { file }, (error, result) => { 15 if (error) { 16 console.error('Upload failed:', error); 17 } else { 18 console.log('File uploaded successfully:', result); 19 } 20 }); 21});
Server Method
1Meteor.methods({ 2 uploadFile(params) { 3 check(params, { 4 file: Object // File object or File-like proxy 5 }); 6 7 const { file } = params; 8 console.log(`Received file: ${file.name}, size: ${file.size} bytes`); 9 10 // On server, file.data contains the binary data 11 if (file._isFileProxy) { 12 // Server environment - file.data is a Buffer 13 const buffer = file.data; 14 console.log('File content:', buffer.toString('utf8')); 15 } 16 17 return { success: true, filename: file.name }; 18 } 19});
Migration from EJSON
Phase 1: Add CBOR Support
-
Add the package:
meteor add cbor
-
Update your DDP usage:
1// In ddp-common or connection code 2import { DDPCommon } from 'meteor/ddp-common'; 3 4// Enable CBOR support 5const capabilities = { 6 ejson: true, 7 cbor: true, 8 binaryStreaming: false 9};
Phase 2: Gradual Migration
The package automatically detects when to use CBOR vs EJSON:
- CBOR is used for: File objects, Buffer objects, Blob objects, large payloads (>1KB)
- EJSON is used for: Small objects, simple data, backward compatibility
Phase 3: Monitor Performance
1// Analyze encoding efficiency 2const analysis = CBOR.analyze(yourData); 3console.log(`Size reduction: ${analysis.savingsPercent}%`); 4console.log(`CBOR: ${analysis.cborSize} bytes, JSON: ${analysis.jsonSize} bytes`);
API Reference
Core Functions
CBOR.encode(value)
→ Uint8Array
Encodes a JavaScript value to CBOR binary format.
CBOR.encodeAsync(value)
→ Promise<Uint8Array>
Async version for File/Blob objects that need to read binary data.
CBOR.decode(data)
→ Any
Decodes CBOR binary data back to JavaScript values.
CBOR.stringify(value)
→ String
Encodes to CBOR then base64 (for network transport).
CBOR.parse(string)
→ Any
Parses base64-encoded CBOR data.
Utility Functions
CBOR.hasBinaryData(value)
→ Boolean
Checks if a value contains binary data that would benefit from CBOR.
CBOR.clone(value)
→ Any
Deep clones a value using CBOR round-trip.
CBOR.equals(a, b)
→ Boolean
Compares two values for equality using CBOR serialization.
CBOR.analyze(value)
→ Object
Returns size comparison between CBOR and JSON encoding.
EJSON Compatibility
CBOR.fromEJSON(ejsonValue)
→ Any
Converts EJSON-style objects to CBOR format.
CBOR.toEJSON(cborValue)
→ Any
Converts CBOR values back to EJSON format.
Supported Types
Native JavaScript Types
null
,undefined
,boolean
,number
,string
Array
,Object
Date
Binary Types
Uint8Array
and other TypedArraysFile
(browser) → reconstructed as File or File-like proxyBuffer
(Node.js) → reconstructed as Buffer or Uint8ArrayBlob
(browser) → reconstructed as Blob or Blob-like proxy
Special Types
NaN
,Infinity
,-Infinity
RegExp
(preserved with flags)- MongoDB
ObjectId
(via semantic tags)
Performance Benefits
File Upload Comparison
1// Before: EJSON with base64 encoding 2const file = new File([new Uint8Array(1000000)], 'test.bin'); // 1MB file 3 4// EJSON (base64): ~1.33MB over the wire 5const ejsonSize = EJSON.stringify({ file: { $binary: base64(file) } }).length; 6 7// CBOR: ~1.001MB over the wire (0% overhead) 8const cborSize = CBOR.stringify({ file }).length; 9 10// Result: 25% bandwidth savings!
Network Traffic Reduction
Data Type | EJSON Size | CBOR Size | Savings |
---|---|---|---|
1MB File | 1.33MB | 1.001MB | 25% |
JSON Object (10KB) | 10KB | 7KB | 30% |
Binary Array (100KB) | 133KB | 100KB | 25% |
Configuration
DDP Integration
1// Enable CBOR in DDP connections 2const connection = DDP.connect(url, { 3 supportsCBOR: true, 4 preferCBOR: false, // Only use CBOR when beneficial 5});
Format Selection
1// Force CBOR for all messages 2DDPCommon.stringifyDDPWithCBOR(message, { 3 supportsCBOR: true, 4 preferCBOR: true 5}); 6 7// Auto-select best format 8const format = DDPCommon.chooseBestFormat(message, capabilities);
Debugging
Enable CBOR Debugging
1// In browser console or server 2CBOR._debug = true; 3 4// Analyze message efficiency 5const analysis = CBOR.analyze(yourMessage); 6console.table(analysis);
Inspect CBOR Data
1// Convert CBOR to readable format 2const encoded = CBOR.encode(data); 3const hex = Array.from(encoded).map(b => b.toString(16).padStart(2, '0')).join(' '); 4console.log('CBOR hex:', hex);
TypeScript Support
The package includes TypeScript definitions:
1interface CBORAnalysis { 2 cborSize: number; 3 jsonSize: number; 4 compressionRatio: number; 5 savings: number; 6 savingsPercent: number; 7} 8 9declare const CBOR: { 10 encode(value: any): Uint8Array; 11 encodeAsync(value: any): Promise<Uint8Array>; 12 decode(data: Uint8Array): any; 13 stringify(value: any): string; 14 parse(string: string): any; 15 hasBinaryData(value: any): boolean; 16 analyze(value: any): CBORAnalysis; 17 // ... more methods 18};
Troubleshooting
Common Issues
-
"CBOR package not found"
- Make sure you've added the package:
meteor add cbor
- Make sure you've added the package:
-
File objects become empty
- Use the CBOR-enabled methods, not legacy EJSON methods
- Ensure both client and server support CBOR
-
Performance not improved
- Check if CBOR is actually being used:
CBOR.hasBinaryData(yourData)
- Monitor with
CBOR.analyze(yourData)
- Check if CBOR is actually being used:
-
Compatibility issues
- CBOR gracefully falls back to EJSON for unsupported clients
- Check
DDPCommon.negotiateCapabilities()
result
Browser Compatibility
- Modern browsers: Full File/Blob support
- Older browsers: Falls back to EJSON automatically
- Node.js: Full Buffer support + File-like proxies
Migration Checklist
- Add
cbor
package - Update method handlers to expect File objects
- Test file uploads in both environments
- Monitor performance improvements
- Update TypeScript definitions if needed
Implementation Details
This package is built on cbor-x, a high-performance CBOR implementation that provides:
- Standards Compliance: Full RFC 8949 CBOR support
- High Performance: Optimized native encoding/decoding
- Streaming Support: Efficient handling of large binary data
- Extension System: Semantic tagging for custom types
- Battle Tested: Used in production across many projects
Why cbor-x?
Rather than maintaining a custom CBOR implementation, we leverage cbor-x because:
- Maturity: Extensively tested and optimized
- Performance: Native code optimizations where available
- Standards: Full CBOR specification compliance
- Maintenance: Regular updates and security patches
- Ecosystem: Wide adoption and community support
Contributing
This package is part of Meteor core. For issues and contributions:
- Test your changes with
meteor test-packages cbor
- Ensure backward compatibility
- Update documentation for new features
- Add test cases for edge cases
License
MIT - Part of the Meteor platform.