High-capacity fast raw data storage in UTF-16 strings.
You can use the following command to install this package, or replace pnpm add
with your package manager of choice.
pnpm add
// Require the store classes
import { Schema, SchemaStore, t } from '@flowr/store';
// Define the enum with the IDs and export it
export const Id = Object.freeze({
AgeUpdate: 0,
StrengthUpdate: 1,
Planet: 2,
User: 3
});
// Create the store in a file and export it
export const store = new SchemaStore()
// Add a schema with an age field stored as a int32:
// Schema<Id.AgeUpdate, { age: number }>
.add(new Schema(Id.AgeUpdate).int32('age'))
// Add a schema with a strength field stored as a float32:
// Schema<Id.StrengthUpdate, { strength: number }>
.add(new Schema(Id.StrengthUpdate).float32('strength'));
// Serialize an `Id.AgeUpdate` object into a string containing:
// - The schema ID (0)
// - The age field (20)
const buffer = store.serialize(Id.AgeUpdate, { age: 20 }).toString();
Important
The IDs passed in the Schema
constant need to be known at compile time, this can be done in the following ways:
- (TS) An
enum
object, which has all of its members strictly typed. - (TS) An object with
as const
, which makes the values known at compile time. - An object with
Object.freeze
as shown in the example, which is equivalent as the previous. - An object with a JSDoc defining the types as the specific values.
- Multiple
const
ants for each value, or- (TS) Class fields marked as
readonly
.
- (TS) Class fields marked as
Tip
The serialized string is encoded in UTF-16, meaning it can store 16 bits per character. Each type stores a different number of bits, for example, a single character can store:
- 16 booleans
- 8 2-bit unsigned integers (0-3)
- 4 4-bit unsigned integers (0-15)
- 2 8-bit unsigned integers (0-255)
- 1 16-bit integer (0-65535)
As a use-case, Discord's custom_id
field in message components can store up to 100 UTF-16 characters, which means it has a storage of 1600 bits, below you can see the supported types and their storage in bits. Keep in mind that the schema ID is stored as a 16-bit integer, and that the property names are not stored.
The schema can be defined using the following methods:
Adds an array with a dynamic length to the schema.
// A schema with a single field `names` that is an array of strings:
const schema = new Schema(Id.Planets).array('names', t.string);
// → Schema<Id.Planets, { names: string[] }>
To track the length of the array, it will serialize a 16-bit unsigned integer before the array.
An alternative to array
that has a fixed length. This variant requires the exact number of elements to be
serialized, but it will save space by not storing the length of the array.
// A schema with a single field `names` that is an array of exactly 3 strings:
const schema = new Schema(Id.Planets).fixedLengthArray('names', t.string, 3);
// → Schema<Id.Planets, { names: [string, string, string] }>
Adds a nullable property of the specified type to the schema.
// A schema with a single field `capitalId` that is a nullable string:
const schema = new Schema(Id.Planets).nullable('capitalId', t.uint32);
// → Schema<Id.Planets, { capitalId: number | null }>
To track whether or not a property is optional, it will serialize a bit, which signals whether or not the value
is defined, due to this, its bit size is null
.
Adds a string to the schema.
// A schema with a single field `name` that is a string:
const schema = new Schema(Id.Planet).string('name');
// → Schema<Id.Planets, { name: string }>
The string is serialized as UTF-8, and the length is serialized as a 16-bit unsigned integer before the string.
Adds a boolean (single bit) to the schema.
// A schema with a single field `isHabitable` that is a boolean:
const schema = new Schema(Id.Planet).boolean('isHabitable');
// → Schema<Id.Planets, { isHabitable: boolean }>
Adds a bit (0 or 1) to the schema. This is a numeric version of boolean
.
// A schema with a single field `isHabitable` that is a bit:
const schema = new Schema(Id.Planet).bit('isHabitable');
// → Schema<Id.Planets, { isHabitable: 0 | 1 }>
Adds a 2-bit signed integer to the schema. It can store values from -2 to 1, inclusive.
// A schema with a single field `type` that is a 2-bit signed integer:
const schema = new Schema(Id.Planet).int2('type');
// → Schema<Id.Planets, { type: -2 | -1 | 0 | 1 }>
Adds a 2-bit unsigned integer to the schema. It can store values from 0 to 3, inclusive.
// A schema with a single field `type` that is a 2-bit unsigned integer:
const schema = new Schema(Id.Planet).uint2('type');
// → Schema<Id.Planets, { type: 0 | 1 | 2 | 3 }>
Adds a 4-bit signed integer to the schema. It can store values from -8 to 7, inclusive.
// A schema with a single field `type` that is a 4-bit signed integer:
const schema = new Schema(Id.Planet).int4('type');
// → Schema<Id.Planets, { type: -8..=7 }>
Adds a 4-bit unsigned integer to the schema. It can store values from 0 to 15, inclusive.
// A schema with a single field `type` that is a 4-bit unsigned integer:
const schema = new Schema(Id.Planet).uint4('type');
// → Schema<Id.Planets, { type: 0..=15 }>
Adds an 8-bit signed integer to the schema. It can store values from -128 to 127, inclusive.
// A schema with a single field `type` that is an 8-bit signed integer:
const schema = new Schema(Id.Planet).int8('type');
// → Schema<Id.Planets, { type: -128..=127 }>
Adds an 8-bit unsigned integer to the schema. It can store values from 0 to 255, inclusive.
// A schema with a single field `type` that is an 8-bit unsigned integer:
const schema = new Schema(Id.Planet).uint8('type');
// → Schema<Id.Planets, { type: 0..=255 }>
Adds a 16-bit signed integer to the schema. It can store values from -32768 to 32767, inclusive.
// A schema with a single field `type` that is a 16-bit signed integer:
const schema = new Schema(Id.Planet).int16('type');
// → Schema<Id.Planets, { type: -32768..=32767 }>
Adds a 16-bit unsigned integer to the schema. It can store values from 0 to 65535, inclusive.
// A schema with a single field `type` that is a 16-bit unsigned integer:
const schema = new Schema(Id.Planet).uint16('type');
// → Schema<Id.Planets, { type: 0..=65535 }>
Adds a 32-bit signed integer to the schema. It can store values from 2,147,483,648 to 2,147,483,647, inclusive.
// A schema with a single field `type` that is a 32-bit signed integer:
const schema = new Schema(Id.Planet).int32('type');
// → Schema<Id.Planets, { type: 2_147_483_648..=2_147_483_647 }>
Adds a 32-bit unsigned integer to the schema. It can store values from 0 to 4,294,967,295, inclusive.
// A schema with a single field `type` that is a 32-bit unsigned integer:
const schema = new Schema(Id.Planet).uint32('type');
// → Schema<Id.Planets, { type: 0..=4294967295 }>
Adds a 64-bit signed integer to the schema. It can store values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, inclusive.
Note: values smaller than Number.MIN_SAFE_INTEGER
or larger than Number.MAX_SAFE_INTEGER
will lose precision, if you need to store larger numbers, consider using bigInt64
.
// A schema with a single field `type` that is a 64-bit signed integer:
const schema = new Schema(Id.Planet).int64('type');
// → Schema<Id.Planets, { type: -9_223_372_036_854_775_808..=9_223_372_036_854_775_807 }>
Adds a 64-bit unsigned integer to the schema. It can store values from 0 to 18,446,744,073,709,551,615, inclusive.
Note: values larger than 9,007,199,254,740,991 (Number.MAX_SAFE_INTEGER
) will lose precision, if you need to store larger numbers, use bigUint64
.
// A schema with a single field `type` that is a 64-bit unsigned integer:
const schema = new Schema(Id.Planet).uint64('type');
// → Schema<Id.Planets, { type: 0..=18_446_744_073_709_551_615 }>
Note: values larger than Number.MAX_SAFE_INTEGER
will be truncated.
Alternative to int32
that uses BigInt
. It can store values from 2,147,483,648 to 2,147,483,647, inclusive.
// A schema with a single field `type` that is a 32-bit integer:
const schema = new Schema(Id.Planet).bigInt32('type');
// → Schema<Id.Planets, { type: 2_147_483_648n..=2_147_483_647n }>
Alternative to uint32
that uses BigInt
. It can store values from 0 to 4,294,967,295, inclusive.
// A schema with a single field `type` that is a 32-bit integer:
const schema = new Schema(Id.Planet).bigUint32('type');
// → Schema<Id.Planets, { type: 0n..=4294967295n }>
Alternative to int64
that uses BigInt
. It can store values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, inclusive.
// A schema with a single field `type` that is a 64-bit integer:
const schema = new Schema(Id.Planet).bigInt64('type');
// → Schema<Id.Planets, { type: -9_223_372_036_854_775_808n..=9_223_372_036_854_775_807n }>
Alternative to uint64
that uses BigInt
. It can store values from 0 to 18,446,744,073,709,551,615, inclusive.
// A schema with a single field `type` that is a 64-bit integer:
const schema = new Schema(Id.Planet).bigUint64('type');
// → Schema<Id.Planets, { type: 0n..=18_446_744_073_709_551_615n }>
Adds a 32-bit floating-point number to the schema.
// A schema with a single field `radius` that is a 32-bit floating-point number:
const schema = new Schema(Id.Planet).float32('radius');
// → Schema<Id.Planets, { radius: number }>
Adds a 64-bit floating-point number to the schema.
// A schema with a single field `radius` that is a 64-bit floating-point number:
const schema = new Schema(Id.Planet).float64('radius');
// → Schema<Id.Planets, { radius: number }>
Adds a 64-bit snowflake to the schema.
const schema = new Schema(Id.User).snowflake('id');
// → Schema<Id.User, { id: bigint }>
A constant value that will not get serialized into the buffer, useful for adding extra information to the resulting payloads, such as the name of a piece to run.
// A schema with a single field `handlerName` that is a specific constant:
const schema = new Schema(Id.Planets).constant('handlerFileName', 'planets.ts');
// → Schema<Id.Planets, { handlerFileName: 'planets.ts' }>