or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cpp-core.mdflexbuffers.mdgo.mdindex.mdjava.mdjavascript.mdpython.mdrust.mdschema-compiler.mdverification.md
tile.json

javascript.mddocs/

JavaScript/TypeScript

JavaScript and TypeScript implementation of FlatBuffers providing browser and Node.js compatibility with zero-copy deserialization benefits adapted for JavaScript's memory model and garbage collection.

Capabilities

Installation and Imports

Install FlatBuffers for JavaScript projects and import the necessary components.

# Installation
npm install flatbuffers

# Or with yarn
yarn add flatbuffers
// ES6/TypeScript imports
import { Builder, ByteBuffer } from 'flatbuffers';

// CommonJS imports  
const { Builder, ByteBuffer } = require('flatbuffers');

// Individual imports
import { Builder } from 'flatbuffers';
import { ByteBuffer } from 'flatbuffers';
import { Encoding } from 'flatbuffers';

// FlexBuffers imports
import * as flexbuffers from 'flatbuffers/flexbuffers';

Builder Class

Main builder class for constructing FlatBuffer data in JavaScript/TypeScript environments.

class Builder {
  /**
   * Create a new FlatBuffer builder
   * @param initialSize Initial buffer size in bytes (default: 1024)
   */
  constructor(initialSize?: number);

  /** Reset the builder for reuse */
  clear(): void;

  /** Get current buffer offset */
  offset(): number;

  /** Get the ByteBuffer representing the FlatBuffer */
  dataBuffer(): ByteBuffer;

  /** Get the finished buffer as Uint8Array */
  asUint8Array(): Uint8Array;

  /** Control default value serialization */
  forceDefaults(forceDefaults: boolean): void;

  // String and byte vector creation
  /** Create a string and return its offset */
  createString(s: string | Uint8Array | null | undefined): number;
  
  /** Create a shared string (with deduplication) and return its offset */
  createSharedString(s: string | Uint8Array): number;
  
  /** Create a byte vector and return its offset */
  createByteVector(v: Uint8Array | null | undefined): number;

  // Object helper methods
  /** Create offset for an object (string or IGeneratedObject) */
  createObjectOffset(obj: string | IGeneratedObject | null): number;
  
  /** Create offsets for a list of objects */
  createObjectOffsetList(list: (string | IGeneratedObject)[]): number[];
  
  /** Create a vector from a list of objects */
  createStructOffsetList(list: (string | IGeneratedObject)[], startFunc: (builder: Builder, length: number) => void): number;

  // Vector building
  /** Start building a vector */
  startVector(elemSize: number, numElems: number, alignment: number): void;

  /** End vector construction and return offset */
  endVector(): number;

  // Table/Object building
  /** Start building a table/object */
  startObject(numFields: number): void;

  /** End table construction and return offset */
  endObject(): number;

  // Primitive value methods
  /** Add primitive values to the buffer */
  addInt8(value: number): void;
  addInt16(value: number): void;
  addInt32(value: number): void;
  addInt64(value: bigint): void;
  addFloat32(value: number): void;
  addFloat64(value: number): void;

  /** Write primitive values to buffer at current space */
  writeInt8(value: number): void;
  writeInt16(value: number): void;
  writeInt32(value: number): void;
  writeInt64(value: bigint): void;
  writeFloat32(value: number): void;
  writeFloat64(value: number): void;

  // Table field methods
  /** Add field to current table */
  addFieldInt8(voffset: number, value: number, defaultValue: number | null): void;
  addFieldInt16(voffset: number, value: number, defaultValue: number | null): void;
  addFieldInt32(voffset: number, value: number, defaultValue: number | null): void;
  addFieldInt64(voffset: number, value: bigint, defaultValue: bigint | null): void;
  addFieldFloat32(voffset: number, value: number, defaultValue: number | null): void;
  addFieldFloat64(voffset: number, value: number, defaultValue: number | null): void;
  addFieldOffset(voffset: number, value: number, defaultValue: number): void;

  /** Add struct field to current table */
  addFieldStruct(voffset: number, value: number, defaultValue: number): void;

  // Buffer management
  /** Prepare buffer for writing */
  prep(size: number, additionalBytes: number): void;
  
  /** Add padding bytes */
  pad(byteSize: number): void;
  
  /** Add an offset value */
  addOffset(offset: number): void;

  /** Finish the buffer with root table */
  finish(rootTable: number, fileIdentifier?: string, sizePrefix?: boolean): void;

  /** Finish buffer with size prefix */
  finishSizePrefixed(rootTable: number, fileIdentifier?: string): void;

  /** Check if a required field has been set */
  requiredField(table: number, field: number): void;

  /** Validate nested object serialization */
  nested(obj: number): void;

  /** Check that no nested object is being serialized */
  notNested(): void;

  /** Set vtable slot value */
  slot(voffset: number): void;

  /** Grow ByteBuffer capacity */
  static growByteBuffer(bb: ByteBuffer): ByteBuffer;
}

Usage Example:

import { Builder } from 'flatbuffers';

// Create builder
const builder = new Builder(1024);

// Create string
const nameOffset = builder.createString("Player");

// Create vector  
const scores = [100, 200, 300];
builder.startVector(4, scores.length, 4);
for (let i = scores.length - 1; i >= 0; i--) {
  builder.addInt32(scores[i]);
}
const scoresOffset = builder.endVector();

// Create table
builder.startObject(3);
builder.addFieldOffset(0, nameOffset, 0);     // name field
builder.addFieldInt32(1, 42, 0);              // level field  
builder.addFieldOffset(2, scoresOffset, 0);   // scores field
const player = builder.endObject();

// Finish buffer
builder.finish(player);

// Get binary data
const buffer = builder.asUint8Array();

ByteBuffer Class

Wrapper for reading FlatBuffer data from binary buffers, providing typed access methods.

class ByteBuffer {
  /**
   * Create ByteBuffer from Uint8Array
   * @param bytes Binary data to wrap
   */
  constructor(bytes: Uint8Array);

  /** Create and allocate a new ByteBuffer with given size */
  static allocate(byteSize: number): ByteBuffer;

  /** Clear the buffer and reset position */
  clear(): void;

  /** Get the underlying Uint8Array */
  bytes(): Uint8Array;

  /** Get current read/write position */
  position(): number;

  /** Set read/write position */
  setPosition(position: number): void;

  /** Get buffer capacity */
  capacity(): number;

  // Read methods
  /** Read 8-bit integers */
  readInt8(offset: number): number;
  readUint8(offset: number): number;

  /** Read 16-bit integers */
  readInt16(offset: number): number; 
  readUint16(offset: number): number;
  
  /** Read 32-bit integers */
  readInt32(offset: number): number;
  readUint32(offset: number): number;

  /** Read 64-bit integers */
  readInt64(offset: number): bigint;
  readUint64(offset: number): bigint;

  /** Read floating point numbers */
  readFloat32(offset: number): number;
  readFloat64(offset: number): number;

  // Write methods
  /** Write 8-bit integers */
  writeInt8(offset: number, value: number): void;
  writeUint8(offset: number, value: number): void;

  /** Write 16-bit integers */
  writeInt16(offset: number, value: number): void;
  writeUint16(offset: number, value: number): void;

  /** Write 32-bit integers */
  writeInt32(offset: number, value: number): void;
  writeUint32(offset: number, value: number): void;

  /** Write 64-bit integers */
  writeInt64(offset: number, value: bigint): void;
  writeUint64(offset: number, value: bigint): void;

  /** Write floating point numbers */
  writeFloat32(offset: number, value: number): void;
  writeFloat64(offset: number, value: number): void;

  // FlatBuffer-specific methods
  /** Get buffer file identifier */
  getBufferIdentifier(): string;

  /** Check if buffer has specific identifier */
  __has_identifier(ident: string): boolean;

  /** Look up field in vtable */
  __offset(bbPos: number, vtableOffset: number): number;

  /** Read string at offset */
  __string(offset: number, encoding?: Encoding): string | Uint8Array;

  /** Initialize union table */
  __union(t: Table, offset: number): Table;

  /** Handle union with string member */
  __union_with_string(o: Table | string, offset: number): Table | string;

  /** Get indirect offset */
  __indirect(offset: number): number;

  /** Get vector data start offset */
  __vector(offset: number): number;

  /** Get vector length */
  __vector_len(offset: number): number;

  // Helper methods for object API
  /** Create scalar list from accessor function */
  createScalarList<T>(listAccessor: (i: number) => T | null, listLength: number): T[];

  /** Create object list from accessor function */
  createObjList<T1 extends IUnpackableObject<T2>, T2 extends IGeneratedObject>(
    listAccessor: (i: number) => T1 | null, 
    listLength: number
  ): T2[];
}

Type Definitions

TypeScript type definitions for better development experience and type safety.

// Core type definitions

export type Offset = number;

export type Table = {
  bb: ByteBuffer;
  bb_pos: number;
};

export interface IGeneratedObject {
  pack(builder: Builder): Offset;
}

export interface IUnpackableObject<T> {
  unpack(): T;
}

// Encoding enum for string handling
export enum Encoding {
  UTF8_BYTES = 1,
  UTF16_STRING = 2
}

// Constants (typed arrays, not functions)
export const SIZEOF_SHORT: number = 2;
export const SIZEOF_INT: number = 4; 
export const FILE_IDENTIFIER_LENGTH: number = 4;
export const SIZE_PREFIX_LENGTH: number = 4;

// Utility typed arrays for float/int conversion
export const int32: Int32Array;
export const float32: Float32Array;
export const float64: Float64Array;
export const isLittleEndian: boolean;

FlexBuffers

FlexBuffers provides a more flexible, schemaless alternative to FlatBuffers with dynamic typing and JSON-like data structures.

// FlexBuffers main API
export function builder(): FlexBuffersBuilder;
export function encode(object: unknown, size?: number, deduplicateStrings?: boolean, deduplicateKeys?: boolean, deduplicateKeyVectors?: boolean): Uint8Array;
export function toObject(buffer: ArrayBuffer): unknown;
export function toReference(buffer: ArrayBuffer): Reference;

// FlexBuffers Builder
class FlexBuffersBuilder {
  constructor(size?: number, dedupStrings?: boolean, dedupKeys?: boolean, dedupKeyVectors?: boolean);

  // Value addition methods
  add(value: undefined | null | boolean | bigint | number | DataView | string | Array<unknown> | Record<string, unknown> | unknown): void;
  addKey(key: string): void;
  addInt(value: number, indirect?: boolean, deduplicate?: boolean): void;
  addUInt(value: number, indirect?: boolean, deduplicate?: boolean): void;
  addFloat(value: number, indirect?: boolean, deduplicate?: boolean): void;

  // Container methods
  startVector(): void;
  startMap(presorted?: boolean): void;
  end(): void;

  // Finishing
  finish(): Uint8Array;
  isFinished(): boolean;

  // Buffer management
  computeOffset(newValueSize: number): number;
  pushInt(value: number, width: BitWidth): void;
  pushUInt(value: number, width: BitWidth): void;
}

// FlexBuffers Reference for reading
class Reference {
  // Type checking methods
  isNull(): boolean;
  isNumber(): boolean;
  isFloat(): boolean;
  isInt(): boolean;
  isString(): boolean;
  isBool(): boolean;
  isBlob(): boolean;
  isVector(): boolean;
  isMap(): boolean;

  // Value extraction methods
  boolValue(): boolean | null;
  intValue(): number | bigint | null;
  floatValue(): number | null;
  numericValue(): number | bigint | null;
  stringValue(): string | null;
  blobValue(): Uint8Array | null;

  // Container access
  get(key: number | string): Reference;
  length(): number;
  
  // Convert to JavaScript object
  toObject(): unknown;
}

// BitWidth enum for FlexBuffers
enum BitWidth {
  WIDTH8 = 0,
  WIDTH16 = 1,
  WIDTH32 = 2,
  WIDTH64 = 3
}

FlexBuffers Usage Example:

import * as flexbuffers from 'flatbuffers/flexbuffers';

// Create data using the high-level encode function
const data = {
  name: "Monster",
  hp: 100,
  inventory: [1, 2, 3, 4, 5],
  position: { x: 1.0, y: 2.0, z: 3.0 }
};

// Encode to FlexBuffer
const buffer = flexbuffers.encode(data);

// Decode from FlexBuffer
const decoded = flexbuffers.toObject(buffer.buffer);
console.log(decoded);

// Using the builder for more control
const builder = flexbuffers.builder();
builder.startMap();
  builder.addKey("name");
  builder.add("Monster");
  builder.addKey("hp");
  builder.add(100);
  builder.addKey("inventory");
  builder.startVector();
    builder.add(1);
    builder.add(2);
    builder.add(3);
  builder.end();
builder.end();

const flexBuffer = builder.finish();

// Using Reference for reading
const ref = flexbuffers.toReference(flexBuffer.buffer);
console.log(ref.get("name").stringValue()); // "Monster"
console.log(ref.get("hp").intValue());       // 100
console.log(ref.get("inventory").length());  // 3

Generated Code Structure

When using flatc --ts or flatc --js, the compiler generates TypeScript or JavaScript classes following these patterns.

// Example generated TypeScript (from monster.fbs):

import { Builder, ByteBuffer, Offset } from 'flatbuffers';

export class Vec3 {
  constructor(
    public x: number = 0,
    public y: number = 0, 
    public z: number = 0
  ) {}

  static pack(builder: Builder, obj: Vec3 | null): Offset {
    if (obj === null) return 0;
    builder.prep(4, 12);
    builder.writeFloat32(obj.z);
    builder.writeFloat32(obj.y);
    builder.writeFloat32(obj.x);
    return builder.offset();
  }
}

export class Monster {
  bb: ByteBuffer | null = null;
  bb_pos: number = 0;

  __init(i: number, bb: ByteBuffer): Monster {
    this.bb_pos = i;
    this.bb = bb;
    return this;
  }

  static getRootAsMonster(bb: ByteBuffer, obj?: Monster): Monster {
    return (obj || new Monster()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
  }

  pos(obj?: Vec3): Vec3 | null {
    const offset = this.bb!.__offset(this.bb_pos, 4);
    return offset ? (obj || new Vec3()).__assign(
      this.bb!.readFloat32(this.bb_pos + offset),
      this.bb!.readFloat32(this.bb_pos + offset + 4),
      this.bb!.readFloat32(this.bb_pos + offset + 8)
    ) : null;
  }

  mana(): number {
    const offset = this.bb!.__offset(this.bb_pos, 6);
    return offset ? this.bb!.readInt16(this.bb_pos + offset) : 150;
  }

  hp(): number {
    const offset = this.bb!.__offset(this.bb_pos, 8);
    return offset ? this.bb!.readInt16(this.bb_pos + offset) : 100;
  }

  name(): string | null {
    const offset = this.bb!.__offset(this.bb_pos, 10);
    return offset ? this.bb!.__string(this.bb_pos + offset) : null;
  }

  inventory(index: number): number {
    const offset = this.bb!.__offset(this.bb_pos, 14);
    return offset ? this.bb!.readUint8(this.bb!.__vector(this.bb_pos + offset) + index) : 0;
  }

  inventoryLength(): number {
    const offset = this.bb!.__offset(this.bb_pos, 14);
    return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
  }

  static startMonster(builder: Builder): void {
    builder.startObject(6);
  }

  static addPos(builder: Builder, pos: Offset): void {
    builder.addFieldStruct(0, pos, 0);
  }

  static addMana(builder: Builder, mana: number): void {
    builder.addFieldInt16(1, mana, 150);
  }

  static addHp(builder: Builder, hp: number): void {
    builder.addFieldInt16(2, hp, 100);
  }

  static addName(builder: Builder, name: Offset): void {
    builder.addFieldOffset(3, name, 0);
  }

  static addInventory(builder: Builder, inventory: Offset): void {
    builder.addFieldOffset(5, inventory, 0);
  }

  static createInventoryVector(builder: Builder, data: number[]): Offset {
    builder.startVector(1, data.length, 1);
    for (let i = data.length - 1; i >= 0; i--) {
      builder.addInt8(data[i]!);
    }
    return builder.endVector();
  }

  static endMonster(builder: Builder): Offset {
    return builder.endObject();
  }

  static createMonster(
    builder: Builder,
    pos: Offset,
    mana: number,
    hp: number, 
    name: Offset,
    inventory: Offset
  ): Offset {
    Monster.startMonster(builder);
    Monster.addPos(builder, pos);
    Monster.addMana(builder, mana);
    Monster.addHp(builder, hp);
    Monster.addName(builder, name);
    Monster.addInventory(builder, inventory);
    return Monster.endMonster(builder);
  }
}

Node.js Usage

FlatBuffers works seamlessly in Node.js environments with Buffer integration.

// Node.js specific usage patterns:

import { Builder, ByteBuffer } from 'flatbuffers';
import * as fs from 'fs';

// Reading from file system
const buffer = fs.readFileSync('data.bin');
const uint8Buffer = new Uint8Array(buffer);
const bb = new ByteBuffer(uint8Buffer);

// Writing to file system  
const builder = new Builder(1024);
// ... build data ...
const outputBuffer = builder.asUint8Array();
fs.writeFileSync('output.bin', Buffer.from(outputBuffer));

// Network usage with HTTP
import * as http from 'http';

const server = http.createServer((req, res) => {
  if (req.method === 'POST') {
    const chunks: Buffer[] = [];
    req.on('data', chunk => chunks.push(chunk));
    req.on('end', () => {
      const buffer = Buffer.concat(chunks);
      const bb = new ByteBuffer(new Uint8Array(buffer));
      // Process FlatBuffer data...
    });
  }
});

Browser Usage

FlatBuffers integrates well with modern browser APIs and frameworks.

// Browser-specific usage patterns:

// Fetch API integration
async function loadFlatBuffer(url: string): Promise<ByteBuffer> {
  const response = await fetch(url);
  const arrayBuffer = await response.arrayBuffer();
  return new ByteBuffer(new Uint8Array(arrayBuffer));
}

// Web Workers
// main.js
const worker = new Worker('flatbuffer-worker.js');
const builder = new Builder(1024);
// ... build data ...
const buffer = builder.asUint8Array();
worker.postMessage({ buffer }, [buffer.buffer]);

// flatbuffer-worker.js
self.onmessage = function(e) {
  const bb = new ByteBuffer(new Uint8Array(e.data.buffer));
  // Process data...
};

// IndexedDB storage
function storeFlatBuffer(db: IDBDatabase, data: Builder): Promise<void> {
  return new Promise((resolve, reject) => {
    const transaction = db.transaction(['flatbuffers'], 'readwrite');
    const store = transaction.objectStore('flatbuffers');
    const buffer = data.asUint8Array();
    const request = store.put({ id: 1, data: buffer });
    request.onsuccess = () => resolve();
    request.onerror = () => reject(request.error);
  });
}

Complete Usage Example:

import { Builder, ByteBuffer } from 'flatbuffers';
import { Monster, Vec3 } from './generated/monster';

// Create a monster
const builder = new Builder(1024);

// Create components
const pos = new Vec3(1.0, 2.0, 3.0);
const posOffset = Vec3.pack(builder, pos);
const nameOffset = builder.createString("Dragon");
const inventoryData = [1, 2, 3, 4, 5];
const inventoryOffset = Monster.createInventoryVector(builder, inventoryData);

// Build monster
const monster = Monster.createMonster(
  builder,
  posOffset,
  200,    // mana
  150,    // hp  
  nameOffset,
  inventoryOffset
);

// Finish and get buffer
builder.finish(monster);
const buffer = builder.asUint8Array();

// Read the data back
const bb = new ByteBuffer(buffer);
const readMonster = Monster.getRootAsMonster(bb);

console.log(`Name: ${readMonster.name()}`);
console.log(`HP: ${readMonster.hp()}`);
console.log(`Mana: ${readMonster.mana()}`);

const position = readMonster.pos();
if (position) {
  console.log(`Position: ${position.x}, ${position.y}, ${position.z}`);
}

console.log(`Inventory size: ${readMonster.inventoryLength()}`);
for (let i = 0; i < readMonster.inventoryLength(); i++) {
  console.log(`Item ${i}: ${readMonster.inventory(i)}`);
}