or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-decoding.mdcbor-types.mddiagnostic-tools.mdencoding-decoding.mdindex.mdintegration-utilities.mdstream-processing.md
tile.json

advanced-decoding.mddocs/

Advanced Decoding

Comprehensive decoding functions with support for multiple items, asynchronous operations, extended result information, and fine-grained control over the decoding process.

Capabilities

Decode Multiple Items

Decode all CBOR items from input data that may contain multiple encoded values.

/**
 * Decode all CBOR items from input
 * @param {BufferLike} input - CBOR data containing multiple items
 * @param {DecoderOptions|decodeCallback} [options] - Options or callback
 * @param {decodeCallback} [cb] - Callback function
 * @returns {Promise<any[]>|void} Array of decoded items or void if callback provided
 */
function decodeAll(input, options, cb);

/**
 * Synchronously decode all CBOR items
 * @param {BufferLike} input - CBOR data containing multiple items
 * @param {DecoderOptions} [options] - Decoding options
 * @returns {any[]} Array of decoded items
 */
function decodeAllSync(input, options);

Usage Examples:

const cbor = require("cbor");

// Encode multiple items
const item1 = { name: "Alice", type: "user" };
const item2 = { name: "Bob", type: "admin" };  
const item3 = [1, 2, 3, 4, 5];

const encoded = Buffer.concat([
  cbor.encode(item1),
  cbor.encode(item2),
  cbor.encode(item3)
]);

// Decode all items synchronously
const allItems = cbor.decodeAllSync(encoded);
console.log(allItems); // [{ name: "Alice", type: "user" }, { name: "Bob", type: "admin" }, [1, 2, 3, 4, 5]]

// Decode all items with options
const allItemsWithOptions = cbor.decodeAllSync(encoded, {
  preferMap: true,
  max_depth: 10
});

// Async decoding with callback
cbor.decodeAll(encoded, (err, items) => {
  if (err) {
    console.error('Decoding failed:', err);
  } else {
    console.log('Decoded items:', items);
  }
});

// Async decoding with Promise
async function decodeMultiple() {
  try {
    const items = await cbor.decodeAll(encoded, {
      extendedResults: false,
      preferWeb: true
    });
    return items;
  } catch (error) {
    console.error('Async decode failed:', error);
    throw error;
  }
}

Decode First Item

Decode only the first CBOR item from input, leaving remaining data unused.

/**
 * Decode first CBOR item from input (async)
 * @param {BufferLike} input - CBOR data
 * @param {DecoderOptions|decodeCallback} [options] - Options or callback
 * @param {decodeCallback} [cb] - Callback function
 * @returns {Promise<any>|void} Decoded item or void if callback provided
 */
function decodeFirst(input, options, cb);

/**
 * Synchronously decode first CBOR item
 * @param {BufferLike} input - CBOR data
 * @param {DecoderOptions} [options] - Decoding options
 * @returns {any} Decoded item
 */
function decodeFirstSync(input, options);

Usage Examples:

const cbor = require("cbor");

// Multiple encoded items
const multipleItems = Buffer.concat([
  cbor.encode("first"),
  cbor.encode("second"),  
  cbor.encode("third")
]);

// Decode only first item synchronously
const firstItem = cbor.decodeFirstSync(multipleItems);
console.log(firstItem); // "first"

// Decode first with extended results
const firstWithExtended = cbor.decodeFirstSync(multipleItems, {
  extendedResults: true
});
console.log(firstWithExtended.value); // "first"
console.log(firstWithExtended.length); // Number of bytes consumed
console.log(firstWithExtended.bytes); // Actual bytes used
console.log(firstWithExtended.unused); // Remaining bytes

// Async decoding with callback
cbor.decodeFirst(multipleItems, { preferMap: true }, (err, item) => {
  if (err) {
    console.error('Decode error:', err);
  } else {
    console.log('First item:', item);
  }
});

// Async decoding with Promise
async function decodeFirstAsync() {
  const result = await cbor.decodeFirst(multipleItems, {
    max_depth: 64,
    extendedResults: true
  });
  
  console.log('Value:', result.value);
  console.log('Bytes used:', result.length);
  
  // Process remaining data if needed  
  if (result.unused && result.unused.length > 0) {
    return await cbor.decodeFirst(result.unused);
  }
}

Extended Results

Get detailed information about the decoding process including byte usage and remaining data.

/**
 * Extended result object with additional metadata
 * @typedef {Object} ExtendedResults
 * @property {any} value - The decoded value
 * @property {number} length - Number of bytes read from original input
 * @property {Buffer} bytes - Bytes of original input used to produce value
 * @property {Buffer} [unused] - Leftover bytes (only for decodeFirst/decodeFirstSync)
 */

Usage Examples:

const cbor = require("cbor");

const data = { name: "Charlie", items: [1, 2, 3] };
const encoded = cbor.encode(data);

// Get extended results
const result = cbor.decodeFirstSync(encoded, { extendedResults: true });

console.log('Decoded value:', result.value);
console.log('Bytes consumed:', result.length);
console.log('Input bytes used:', result.bytes);
console.log('Unused bytes:', result.unused); // Should be empty Buffer

// Useful for processing streams of CBOR data
function processStream(buffer) {
  let offset = 0;
  const results = [];
  
  while (offset < buffer.length) {
    try {
      const remaining = buffer.slice(offset);
      const result = cbor.decodeFirstSync(remaining, { extendedResults: true });
      
      results.push(result.value);
      offset += result.length;
      
    } catch (error) {
      console.error('Failed to decode at offset', offset, ':', error);
      break;
    }
  }
  
  return results;
}

Advanced Decoding Options

Fine-tune the decoding process with comprehensive configuration options.

/**
 * Decoder configuration options
 * @typedef {Object} DecoderOptions
 * @property {number} [max_depth=-1] - Maximum depth to parse (-1 for unlimited)
 * @property {TagMap} [tags] - Mapping from tag numbers to conversion functions
 * @property {boolean} [preferMap=false] - Prefer Map instances over plain objects
 * @property {boolean} [preferWeb=false] - Prefer Uint8Arrays over Buffers
 * @property {BufferEncoding} [encoding='hex'] - Input encoding if input is string
 * @property {boolean} [required=false] - Error when no data in input
 * @property {boolean} [extendedResults=false] - Emit extended result objects
 * @property {boolean} [preventDuplicateKeys=false] - Error on duplicate map keys
 */

Usage Examples:

const cbor = require("cbor");

// Maximum depth protection
const deepObject = { level1: { level2: { level3: { level4: "deep" } } } };
const encodedDeep = cbor.encode(deepObject);

try {
  const decoded = cbor.decodeFirstSync(encodedDeep, { max_depth: 2 });
} catch (error) {
  console.log('Depth limit exceeded'); // Will throw error
}

// Custom tag handlers
const decodedWithTags = cbor.decodeFirstSync(encodedData, {
  tags: {
    0: (dateString) => new Date(dateString),
    1: (epochTime) => new Date(epochTime * 1000),
    100: (value) => new MyCustomClass(value)
  }
});

// Prefer Map objects for better key type support
const mapData = new Map([
  [1, "number key"],
  ["str", "string key"],
  [true, "boolean key"]
]);
const encodedMap = cbor.encode(mapData);
const decodedAsMap = cbor.decodeFirstSync(encodedMap, { preferMap: true });

// Web platform compatibility
const decodedWeb = cbor.decodeFirstSync(encodedData, {
  preferWeb: true  // Use Uint8Arrays instead of Buffers
});

// Strict duplicate key checking
const duplicateKeyData = '{"a": 1, "a": 2}'; // Invalid CBOR with duplicate keys
try {
  const decoded = cbor.decodeFirstSync(duplicateKeyData, {
    preventDuplicateKeys: true
  });
} catch (error) {
  console.log('Duplicate keys detected');
}

// Require data to be present
try {
  const decoded = cbor.decodeFirstSync(Buffer.alloc(0), { required: true });
} catch (error) {
  console.log('No data found but required');
}

Error Handling and Recovery

Handle various decoding errors and implement recovery strategies.

Usage Examples:

const cbor = require("cbor");

// Comprehensive error handling
function safeDecode(input, options = {}) {
  try {
    return {
      success: true,
      data: cbor.decodeFirstSync(input, options),
      error: null
    };
  } catch (error) {
    let errorType = 'unknown';
    
    if (error.message.includes('depth')) {
      errorType = 'max_depth_exceeded';
    } else if (error.message.includes('truncated')) {
      errorType = 'incomplete_data';
    } else if (error.message.includes('duplicate')) {
      errorType = 'duplicate_keys';
    } else if (error.message.includes('Unexpected')) {
      errorType = 'invalid_cbor';
    }
    
    return {
      success: false,
      data: null,
      error: { type: errorType, message: error.message }
    };
  }
}

// Decode with fallback options
function decodeWithFallback(input) {
  // Try strict decoding first
  let result = safeDecode(input, {
    max_depth: 32,
    preventDuplicateKeys: true,
    required: true
  });
  
  if (result.success) {
    return result.data;
  }
  
  // Try relaxed decoding
  result = safeDecode(input, {
    max_depth: -1,
    preventDuplicateKeys: false,
    required: false
  });
  
  if (result.success) {
    console.warn('Decoded with relaxed options');
    return result.data;
  }
  
  throw new Error(`Unable to decode: ${result.error.message}`);
}

// Async error handling
async function asyncDecodeWithRetry(input, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await cbor.decodeFirst(input, {
        max_depth: 64,
        extendedResults: true
      });
    } catch (error) {
      console.warn(`Decode attempt ${attempt} failed:`, error.message);
      
      if (attempt === maxRetries) {
        throw new Error(`Failed to decode after ${maxRetries} attempts: ${error.message}`);
      }
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 100 * attempt));
    }
  }
}