bebop
Version:
The TypeScript runtime for Bebop, a schema-based binary serialization format.
1,766 lines (1,763 loc) • 62.6 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var TypeScript_exports = {};
__export(TypeScript_exports, {
BebopJson: () => BebopJson,
BebopRuntimeError: () => BebopRuntimeError,
BebopTypeGuard: () => BebopTypeGuard,
BebopView: () => BebopView,
BinarySchema: () => BinarySchema,
Guid: () => Guid,
GuidMap: () => GuidMap
});
module.exports = __toCommonJS(TypeScript_exports);
// binary.ts
var decoder = new TextDecoder();
var RecordReader = class {
/**
* @param schema - BinarySchema object containing metadata about Bebop schemas.
* @private
*/
constructor(schema) {
this.schema = schema;
}
/**
* Reads a Bebop encoded record from a buffer.
*
* @param definitionName - Name of the definition in the schema for the record to read.
* @param data - The buffer to read the record from.
* @returns - The read record as a Record object.
* @throws - Throws an error if the record cannot be decoded directly.
* @public
*/
read(definitionName, data) {
const definition = this.schema.getDefinition(definitionName);
if (definition.kind === 4 /* Enum */) {
throw new BebopRuntimeError("Cannot decode enum directly");
}
const view = BebopView.getInstance();
view.startReading(data);
return this.readDefinition(definition, view);
}
readDefinition(definition, view) {
switch (definition.kind) {
case 4 /* Enum */:
return this.readEnumDefinition(definition, view);
case 3 /* Union */:
return this.readUnionDefinition(definition, view);
case 1 /* Struct */:
return this.readStructDefinition(definition, view);
case 2 /* Message */:
return this.readMessageDefinition(definition, view);
default:
throw new BebopRuntimeError(`Unknown type kind: ${definition.kind}`);
}
}
readStructDefinition(definition, view) {
const record = {};
Object.values(definition.fields).forEach((field) => {
record[field.name] = this.readField(field, view);
if (!(field.name in record) || record[field.name] === void 0) {
throw new BebopRuntimeError(`Missing field ${field.name}`);
}
});
if (!definition.isMutable) {
Object.freeze(record);
}
return record;
}
readMessageDefinition(definition, view) {
const record = {};
const length = view.readMessageLength();
const end = view.index + length;
const fields = Object.values(definition.fields);
while (true) {
const discriminator = view.readByte();
if (discriminator === 0) {
return record;
}
const field = fields.find((f) => f.constantValue === discriminator);
if (field === void 0) {
view.index = end;
return record;
}
record[field.name] = this.readField(field, view);
}
}
readField(field, view) {
if (field.typeId >= 0) {
const definition = this.schema.getDefinition(field.typeId);
return this.readDefinition(definition, view);
}
switch (field.fieldProperties.type) {
case "scalar":
return this.readScalar(field.typeId, view);
case "array":
return this.readArray(
field.fieldProperties,
field.fieldProperties.depth,
view
);
case "map":
return this.readMap(field.fieldProperties, view);
default:
throw new BebopRuntimeError(
`Unknown field type: ${field.fieldProperties}`
);
}
}
readScalar(typeId, view) {
switch (typeId) {
case -1 /* Bool */:
return !!view.readByte();
case -2 /* Byte */:
return view.readByte();
case -3 /* UInt16 */:
return view.readUint16();
case -4 /* Int16 */:
return view.readInt16();
case -5 /* UInt32 */:
return view.readUint32();
case -6 /* Int32 */:
return view.readInt32();
case -7 /* UInt64 */:
return view.readUint64();
case -8 /* Int64 */:
return view.readInt64();
case -9 /* Float32 */:
return view.readFloat32();
case -10 /* Float64 */:
return view.readFloat64();
case -11 /* String */:
return view.readString();
case -13 /* Date */:
return view.readDate();
case -12 /* Guid */:
return view.readGuid();
default:
throw new BebopRuntimeError(`Unknown scalar type: ${typeId}`);
}
}
readArray(field, depth, view) {
if (field.type !== "array") {
throw new BebopRuntimeError(`Expected array field, got ${field.type}`);
}
const memberType = field.memberTypeId;
if (depth > 0) {
const length2 = view.readUint32();
const array2 = new Array(length2);
for (let i = 0; i < length2; i++) {
array2[i] = this.readArray(field, depth - 1, view);
}
return array2;
}
if (memberType === -2 /* Byte */) {
return view.readBytes();
}
let definition;
if (memberType >= 0) {
definition = this.schema.getDefinition(memberType);
}
const length = view.readUint32();
const array = new Array(length);
for (let i = 0; i < length; i++) {
if (definition !== void 0) {
array[i] = this.readDefinition(definition, view);
} else {
array[i] = this.readScalar(memberType, view);
}
}
return array;
}
readMap(field, view) {
if (field.type !== "map") {
throw new BebopRuntimeError(`Expected map field, got ${field.type}`);
}
const keyType = field.keyTypeId;
const valueType = field.valueTypeId;
const map = field.keyTypeId === -12 /* Guid */ ? new GuidMap() : /* @__PURE__ */ new Map();
const size = view.readUint32();
let definition;
if (valueType >= 0) {
definition = this.schema.getDefinition(valueType);
}
for (let i = 0; i < size; i++) {
const key = this.readScalar(keyType, view);
let value;
if (definition !== void 0) {
value = this.readDefinition(definition, view);
} else if (field.nestedType !== void 0) {
const nested = field.nestedType;
if (nested.type === "array") {
value = this.readArray(nested, nested.depth, view);
} else if (nested.type === "map") {
value = this.readMap(nested, view);
}
} else {
value = this.readScalar(valueType, view);
}
if (value === void 0) {
throw new BebopRuntimeError(`Error decoding map value for key ${key}`);
}
map.set(key, value);
}
return map;
}
readEnumDefinition(definition, view) {
switch (definition.baseType) {
case -2 /* Byte */:
return view.readByte();
case -3 /* UInt16 */:
return view.readUint16();
case -4 /* Int16 */:
return view.readInt16();
case -5 /* UInt32 */:
return view.readUint32();
case -6 /* Int32 */:
return view.readInt32();
case -7 /* UInt64 */:
return view.readUint64();
case -8 /* Int64 */:
return view.readInt64();
default:
throw new BebopRuntimeError(
`Unknown enum base type: ${definition.baseType}`
);
}
}
readUnionDefinition(definition, view) {
const length = view.readMessageLength();
const end = view.index + 1 + length;
const discriminator = view.readByte();
const branch = definition.branches.find(
(b) => b.discriminator === discriminator
);
if (branch === void 0) {
view.index = end;
throw new BebopRuntimeError(`Unknown discriminator: ${discriminator}`);
}
return {
discriminator,
value: this.readDefinition(
this.schema.getDefinition(branch.typeId),
view
)
};
}
};
var RecordWriter = class {
/**
* @param schema Binary schema used for encoding the data.
* @private
*/
constructor(schema) {
this.schema = schema;
}
/**
* Encodes a given record according to a provided definition name and returns it as a Uint8Array.
*
* @param definitionName Name of the definition to be used for encoding.
* @param record The record to be encoded.
* @returns Encoded record as a Uint8Array.
*/
write(definitionName, record) {
const definition = this.schema.getDefinition(definitionName);
const view = BebopView.getInstance();
view.startWriting();
this.writeDefinition(definition, view, record);
return view.toArray();
}
writeDefinition(definition, view, record) {
switch (definition.kind) {
case 4 /* Enum */:
this.writeEnumDefinition(definition, view, record);
break;
case 3 /* Union */:
this.writeUnionDefinition(definition, view, record);
break;
case 1 /* Struct */:
this.writeStructDefinition(definition, view, record);
break;
case 2 /* Message */:
this.writeMessageDefinition(definition, view, record);
break;
}
}
writeStructDefinition(definition, view, record) {
if (!this.isRecord(record)) {
throw new BebopRuntimeError(`Expected object, got ${typeof record}`);
}
const before = view.length;
Object.values(definition.fields).forEach((field) => {
if (!(field.name in record)) {
throw new BebopRuntimeError(`Missing field: ${field.name}`);
}
if (record[field.name] === void 0) {
throw new BebopRuntimeError(`Field ${field.name} is undefined`);
}
this.writeField(field, view, record[field.name]);
});
const after = view.length;
return after - before;
}
writeMessageDefinition(definition, view, record) {
if (!this.isRecord(record)) {
throw new BebopRuntimeError(`Expected object, got ${typeof record}`);
}
const before = view.length;
const pos = view.reserveMessageLength();
const start = view.length;
Object.values(definition.fields).forEach((field) => {
if (field.constantValue === void 0 || field.constantValue === null) {
throw new BebopRuntimeError(
`Missing constant value for field: ${field.name}`
);
}
if (typeof field.constantValue !== "number") {
throw new BebopRuntimeError(
`Expected number, got ${typeof field.constantValue} for field: ${field.name}`
);
}
if (field.name in record && record[field.name] !== void 0) {
view.writeByte(field.constantValue);
this.writeField(field, view, record[field.name]);
}
});
view.writeByte(0);
const end = view.length;
view.fillMessageLength(pos, end - start);
const after = view.length;
return after - before;
}
writeEnumDefinition(definition, view, value) {
if (typeof value !== "number" && typeof value !== "bigint") {
throw new BebopRuntimeError(
`Expected number or bigint, got ${typeof value}`
);
}
if ((definition.baseType === -8 /* Int64 */ || definition.baseType === -7 /* UInt64 */) && typeof value !== "bigint") {
throw new BebopRuntimeError(`Expected bigint, got ${typeof value}`);
}
let valueFound = false;
for (const member in definition.members) {
if (definition.members[member].value === value) {
valueFound = true;
break;
}
}
if (!valueFound) {
throw new BebopRuntimeError(
`Enum '${definition.name}' does not contain value: ${value}`
);
}
switch (definition.baseType) {
case -2 /* Byte */:
BebopTypeGuard.ensureUint8(value);
view.writeByte(value);
break;
case -3 /* UInt16 */:
BebopTypeGuard.ensureUint16(value);
view.writeUint16(value);
break;
case -4 /* Int16 */:
BebopTypeGuard.ensureInt16(value);
view.writeInt16(value);
break;
case -5 /* UInt32 */:
BebopTypeGuard.ensureUint32(value);
view.writeUint32(value);
break;
case -6 /* Int32 */:
BebopTypeGuard.ensureInt32(value);
view.writeInt32(value);
break;
case -7 /* UInt64 */:
BebopTypeGuard.ensureUint64(value);
view.writeUint64(value);
break;
case -8 /* Int64 */:
BebopTypeGuard.ensureInt64(value);
view.writeInt64(value);
break;
default:
throw new BebopRuntimeError(
`Unknown enum base type: ${definition.baseType}`
);
}
}
writeUnionDefinition(definition, view, record) {
if (record === null || record === void 0 || typeof record !== "object") {
throw new BebopRuntimeError(`Expected non-null object value`);
}
if (!("discriminator" in record && typeof record.discriminator === "number")) {
throw new BebopRuntimeError(`Expected number 'discriminator' property`);
}
if (!("value" in record && record.value !== null && typeof record.value === "object")) {
throw new BebopRuntimeError(`Expected 'value' property`);
}
const branch = definition.branches.find(
(b) => b.discriminator === record.discriminator
);
if (branch === void 0) {
throw new BebopRuntimeError(
`No branch found for discriminator: ${record.discriminator}`
);
}
const branchDefinition = this.schema.getDefinition(branch.typeId);
const before = view.length;
const pos = view.reserveMessageLength();
const start = view.length + 1;
view.writeByte(record.discriminator);
this.writeDefinition(branchDefinition, view, record.value);
const end = view.length;
view.fillMessageLength(pos, end - start);
const after = view.length;
return after - before;
}
writeField(field, view, value) {
if (field.typeId >= 0) {
const definition = this.schema.getDefinition(field.typeId);
this.writeDefinition(definition, view, value);
return;
}
switch (field.fieldProperties.type) {
case "scalar":
this.writeScalar(field.typeId, view, value);
break;
case "array":
this.writeArray(
field.fieldProperties,
field.fieldProperties.depth,
view,
value
);
break;
case "map":
this.writeMap(field.fieldProperties, view, value);
break;
default:
throw new BebopRuntimeError(
`Unknown field type: ${field.fieldProperties}`
);
}
}
writeArray(field, depth, view, value) {
if (field.type !== "array") {
throw new BebopRuntimeError(`Expected array field, got ${field.type}`);
}
if (!Array.isArray(value) && !(value instanceof Uint8Array)) {
throw new BebopRuntimeError(`Expected array, got ${typeof value}`);
}
if (field.memberTypeId === -2 /* Byte */ && !(value instanceof Uint8Array)) {
throw new BebopRuntimeError(`Expected Uint8Array, got ${typeof value}`);
}
const memberType = field.memberTypeId;
const length = value.length;
if (depth > 0) {
view.writeUint32(length);
for (let i = 0; i < length; i++) {
this.writeArray(field, depth - 1, view, value[i]);
}
return;
}
if (memberType === -2 /* Byte */) {
view.writeBytes(value);
} else {
view.writeUint32(length);
let definition;
if (memberType >= 0) {
definition = this.schema.getDefinition(memberType);
}
for (let i = 0; i < length; i++) {
if (definition !== void 0) {
this.writeDefinition(definition, view, value[i]);
} else {
this.writeScalar(memberType, view, value[i]);
}
}
}
}
writeMap(field, view, value) {
if (field.type !== "map") {
throw new BebopRuntimeError(`Expected map field, got ${field.type}`);
}
if (!(value instanceof Map || value instanceof GuidMap)) {
throw new BebopRuntimeError(`Expected Map, got ${typeof value}`);
}
const keyType = field.keyTypeId;
const valueType = field.valueTypeId;
const size = value.size;
view.writeUint32(size);
let definition;
if (valueType >= 0) {
definition = this.schema.getDefinition(valueType);
}
for (const [k, v] of value.entries()) {
this.writeScalar(keyType, view, k);
if (definition !== void 0) {
this.writeDefinition(definition, view, v);
} else if (field.nestedType !== void 0) {
const nested = field.nestedType;
if (nested.type === "array") {
this.writeArray(
nested,
nested.depth,
view,
v
);
} else if (nested.type === "map") {
this.writeMap(
nested,
view,
v
);
}
} else {
this.writeScalar(valueType, view, v);
}
}
}
writeScalar(typeId, view, value) {
switch (typeId) {
case -1 /* Bool */:
BebopTypeGuard.ensureBoolean(value);
view.writeByte(Number(value));
break;
case -2 /* Byte */:
BebopTypeGuard.ensureUint8(value);
view.writeByte(value);
break;
case -3 /* UInt16 */:
BebopTypeGuard.ensureUint16(value);
view.writeUint16(value);
break;
case -4 /* Int16 */:
BebopTypeGuard.ensureInt16(value);
view.writeInt16(value);
break;
case -5 /* UInt32 */:
BebopTypeGuard.ensureUint32(value);
view.writeUint32(value);
break;
case -6 /* Int32 */:
BebopTypeGuard.ensureInt32(value);
view.writeInt32(value);
break;
case -7 /* UInt64 */:
BebopTypeGuard.ensureUint64(value);
view.writeUint64(value);
break;
case -8 /* Int64 */:
BebopTypeGuard.ensureInt64(value);
view.writeInt64(value);
break;
case -9 /* Float32 */:
BebopTypeGuard.ensureFloat(value);
view.writeFloat32(value);
break;
case -10 /* Float64 */:
BebopTypeGuard.ensureFloat(value);
view.writeFloat64(value);
break;
case -11 /* String */:
BebopTypeGuard.ensureString(value);
view.writeString(value);
break;
case -12 /* Guid */:
BebopTypeGuard.ensureGuid(value);
view.writeGuid(value);
break;
case -13 /* Date */:
BebopTypeGuard.ensureDate(value);
view.writeDate(value);
break;
default:
throw new BebopRuntimeError(`Unknown scalar type: ${typeId}`);
}
}
isRecord(value) {
return value !== null && typeof value === "object";
}
};
var BinarySchema = class {
/**
* Create a new BinarySchema instance.
* @param data - The binary data array.
*/
constructor(data) {
this.data = data;
this.view = new DataView(this.data.buffer);
this.pos = 0;
this.reader = new RecordReader(this);
this.writer = new RecordWriter(this);
this.dataProxy = new Proxy(this.data, {
get: (target, prop) => {
if (prop === "length") {
return target.length;
}
if (typeof prop === "string" && !isNaN(Number(prop))) {
return target[Number(prop)];
}
if (typeof prop === "string" && typeof target[prop] === "function") {
return target[prop].bind(target);
}
throw new BebopRuntimeError(`Cannot access property ${String(prop)}`);
},
set: (_, __, ___) => {
throw new BebopRuntimeError("Cannot modify schema data");
}
});
}
view;
dataProxy;
pos;
ArrayType = -14;
MapType = -15;
parsedSchema;
indexToDefinition = {};
nameToDefinition = {};
reader;
writer;
/**
* Get the schema.
* This method should only be called once per instance.
*/
get() {
if (this.parsedSchema !== void 0) {
return;
}
const schemaVersion = this.getUint8();
const numDefinedTypes = this.getUint32();
let definedTypes = {};
for (let i = 0; i < numDefinedTypes; i++) {
const def = this.getDefinedType(i);
definedTypes[def.name] = def;
this.indexToDefinition[i] = def;
this.nameToDefinition[def.name] = def;
}
const serviceCount = this.getUint32();
let services = {};
for (let i = 0; i < serviceCount; i++) {
const service = this.getServiceDefinition();
services[service.name] = service;
}
this.parsedSchema = {
bebopVersion: schemaVersion,
definitions: definedTypes,
services
};
Object.freeze(this.parsedSchema);
}
/**
* Returns the getd schema.
*/
get ast() {
if (this.parsedSchema === void 0) {
this.get();
}
return this.parsedSchema;
}
/**
* Returns the raw binary data of the schema wrapped in an immutable Uint8Array.
*/
get raw() {
return this.dataProxy;
}
/**
* Get a Definition by its index or name.
* @param index - The index or name of the Definition.
* @returns - The requested Definition.
* @throws - Will throw an error if no Definition is found at the provided index.
*/
getDefinition(index) {
const definition = typeof index === "number" ? this.indexToDefinition[index] : this.nameToDefinition[index];
if (!definition) {
throw new BebopRuntimeError(`No definition found at index: ${index}`);
}
return definition;
}
getDefinedType(index) {
const name = this.getString();
const kind = this.getUint8();
const decorators = this.getDecorators();
switch (kind) {
case 4 /* Enum */:
return this.getEnumDefinition(name, kind, decorators, index);
case 3 /* Union */:
return this.getUnionDefinition(name, kind, decorators, index);
case 1 /* Struct */:
return this.getStructDefinition(name, kind, decorators, index);
case 2 /* Message */:
return this.getMessageDefinition(name, kind, decorators, index);
default:
throw new BebopRuntimeError(`Unknown type kind: ${kind}`);
}
}
getDecorators() {
const decoratorCount = this.getUint8();
const decorators = [];
for (let i = 0; i < decoratorCount; i++) {
const identifier = this.getString();
decorators.push({
identifier,
...this.getDecorator()
});
}
return decorators;
}
getDecorator() {
const argCount = this.getUint8();
const args = {};
for (let i = 0; i < argCount; i++) {
const identifier = this.getString();
const typeId = this.getTypeId();
const argumentValue = this.getConstantValue(typeId);
args[identifier] = {
typeId,
value: argumentValue
};
}
return { arguments: args };
}
getEnumDefinition(name, kind, decorators, index) {
const baseType = this.getTypeId();
const isBitFlags = this.getBool();
const minimalEncodeSize = this.getInt32();
const memberCount = this.getUint8();
const members = {};
for (let i = 0; i < memberCount; i++) {
const member = this.getEnumMember(baseType);
members[member.name] = member;
}
return {
index,
name,
isBitFlags,
kind,
decorators,
minimalEncodeSize,
baseType,
members
};
}
getEnumMember(baseType) {
const name = this.getString();
const decorators = this.getDecorators();
const value = this.getConstantValue(baseType);
return { name, decorators, value };
}
getUnionDefinition(name, kind, decorators, index) {
const minimalEncodeSize = this.getInt32();
const branchCount = this.getUint8();
const branches = new Array(branchCount).fill(null).map(() => this.getUnionBranch());
return {
index,
name,
kind,
decorators,
minimalEncodeSize,
branchCount,
branches
};
}
getUnionBranch() {
const discriminator = this.getUint8();
const typeId = this.getTypeId();
return { discriminator, typeId };
}
getStructDefinition(name, kind, decorators, index) {
const isMutable = this.getBool();
const minimalEncodeSize = this.getInt32();
const isFixedSize = this.getBool();
const fields = this.getFields(kind);
return {
index,
name,
kind,
decorators,
isMutable,
minimalEncodeSize,
isFixedSize,
fields
};
}
getMessageDefinition(name, kind, decorators, index) {
const minimalEncodeSize = this.getInt32();
const fields = this.getFields(kind);
return {
index,
minimalEncodeSize,
name,
kind,
decorators,
fields
};
}
getFields(parentKind) {
const numFields = this.getUint8();
const fields = {};
for (let i = 0; i < numFields; i++) {
const field = this.getField(parentKind);
fields[field.name] = field;
}
return fields;
}
getField(parentKind) {
const fieldName = this.getString();
let fieldTypeId = this.getTypeId();
let fieldProperties;
if (fieldTypeId === this.ArrayType || fieldTypeId === this.MapType) {
fieldProperties = this.getNestedType(
fieldTypeId === this.ArrayType ? "array" : "map"
);
} else {
fieldProperties = { type: "scalar" };
}
const decorators = this.getDecorators();
const constantValue = parentKind === 2 /* Message */ ? this.getConstantValue(-2 /* Byte */) : null;
return {
name: fieldName,
typeId: fieldTypeId,
fieldProperties,
decorators,
constantValue
};
}
getNestedType(parentType) {
if (parentType === "array") {
const depth = this.getUint8();
const memberTypeId = this.getTypeId();
return { type: parentType, memberTypeId, depth };
}
if (parentType === "map") {
const keyTypeId = this.getTypeId();
const valueTypeId = this.getTypeId();
let nestedType;
if (valueTypeId === this.ArrayType || valueTypeId === this.MapType) {
nestedType = this.getNestedType(
valueTypeId === this.ArrayType ? "array" : "map"
);
}
return {
type: parentType,
keyTypeId,
valueTypeId,
nestedType
};
}
throw new BebopRuntimeError("Invalid initial type");
}
getConstantValue(typeId) {
switch (typeId) {
case -1 /* Bool */:
return this.getBool() ? 1 : 0;
case -2 /* Byte */:
return this.getUint8();
case -3 /* UInt16 */:
return this.getUint16();
case -4 /* Int16 */:
return this.getInt16();
case -5 /* UInt32 */:
return this.getUint32();
case -6 /* Int32 */:
return this.getInt32();
case -7 /* UInt64 */:
return BigInt(this.getUint64());
case -8 /* Int64 */:
return BigInt(this.getInt64());
case -9 /* Float32 */:
return this.getFloat32();
case -10 /* Float64 */:
return this.getFloat64();
case -11 /* String */:
return this.getString();
case -12 /* Guid */:
return Guid.fromBytes(this.getGuid(), 0);
default:
throw new BebopRuntimeError(`Unsupported constant type ID: ${typeId}`);
}
}
getServiceDefinition() {
let name = this.getString();
let decorators = this.getDecorators();
let methods = {};
let methodCount = this.getUint32();
for (let i = 0; i < methodCount; i++) {
let methodName = this.getString();
let methodDecorators = this.getDecorators();
let methodType = this.getUint8();
let requestTypeId = this.getTypeId();
let responseTypeId = this.getTypeId();
let id = this.getUint32();
methods[methodName] = {
name: methodName,
decorators: methodDecorators,
methodType,
requestTypeId,
responseTypeId,
id
};
}
return {
name,
decorators,
methods
};
}
getString() {
const start = this.pos;
while (this.pos < this.data.length && this.data[this.pos] !== 0) {
this.pos++;
}
const strBytes = this.data.subarray(start, this.pos);
if (this.pos < this.data.length) {
this.pos++;
}
return decoder.decode(strBytes);
}
getUint8() {
let value = this.view.getUint8(this.pos);
this.pos++;
return value;
}
getUint16() {
let value = this.view.getUint16(this.pos, true);
this.pos += 2;
return value;
}
getInt16() {
let value = this.view.getInt16(this.pos, true);
this.pos += 2;
return value;
}
getUint32() {
let value = this.view.getUint32(this.pos, true);
this.pos += 4;
return value;
}
getInt32() {
let value = this.view.getInt32(this.pos, true);
this.pos += 4;
return value;
}
getUint64() {
let value = this.view.getBigUint64(this.pos, true);
this.pos += 8;
return Number(value);
}
getInt64() {
let value = this.view.getBigInt64(this.pos, true);
this.pos += 8;
return Number(value);
}
getFloat32() {
let value = this.view.getFloat32(this.pos, true);
this.pos += 4;
return value;
}
getFloat64() {
let value = this.view.getFloat64(this.pos, true);
this.pos += 8;
return value;
}
getBool() {
return this.getUint8() !== 0;
}
getTypeId() {
let typeId = this.view.getInt32(this.pos, true);
this.pos += 4;
return typeId;
}
getGuid() {
let value = this.data.subarray(this.pos, this.pos + 16);
this.pos += 16;
return value;
}
};
// index.ts
var hexDigits = "0123456789abcdef";
var asciiToHex = [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
0,
0,
0,
0,
0,
0,
0,
10,
11,
12,
13,
14,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
10,
11,
12,
13,
14,
15,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
];
var guidDelimiter = "-";
var ticksBetweenEpochs = 621355968000000000n;
var dateMask = 0x3fffffffffffffffn;
var emptyByteArray = new Uint8Array(0);
var emptyString = "";
var byteToHex = [];
for (const x of hexDigits) {
for (const y of hexDigits) {
byteToHex.push(x + y);
}
}
var hasCryptoGetRandomValues = typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function";
var BebopRuntimeError = class extends Error {
constructor(message) {
super(message);
this.name = "BebopRuntimeError";
}
};
var Guid = class _Guid {
/**
* Constructs a new Guid object with the specified value.
* @param value The value of the GUID.
*/
constructor(value) {
this.value = value;
}
static empty = new _Guid("00000000-0000-0000-0000-000000000000");
/**
* Gets the string value of the Guid.
* @returns The string representation of the Guid.
*/
toString() {
return this.value;
}
/**
* Checks if the Guid is empty.
* @returns true if the Guid is empty, false otherwise.
*/
isEmpty() {
return this.value === _Guid.empty.value;
}
/**
* Checks if a value is a Guid.
* @param value The value to be checked.
* @returns true if the value is a Guid, false otherwise.
*/
static isGuid(value) {
return value instanceof _Guid;
}
/**
* Parses a string into a Guid.
* @param value The string to be parsed.
* @returns A new Guid that represents the parsed value.
* @throws {BebopRuntimeError} If the input string is not a valid Guid.
*/
static parseGuid(value) {
let cleanedInput = "";
let count = 0;
for (let i = 0; i < value.length; i++) {
let ch = value[i].toLowerCase();
if (hexDigits.indexOf(ch) !== -1) {
cleanedInput += ch;
count++;
} else if (ch !== "-") {
throw new BebopRuntimeError(`Invalid GUID: ${value}`);
}
}
if (count !== 32) {
throw new BebopRuntimeError(`Invalid GUID: ${value}`);
}
const guidString = cleanedInput.slice(0, 8) + "-" + cleanedInput.slice(8, 12) + "-" + cleanedInput.slice(12, 16) + "-" + cleanedInput.slice(16, 20) + "-" + cleanedInput.slice(20);
return new _Guid(guidString);
}
/**
* Creates a an insecure new Guid using Math.random.
* @returns A new Guid.
*/
static newGuid() {
let guid = "";
const now = Date.now();
for (let i = 0; i < 36; i++) {
if (i === 8 || i === 13 || i === 18 || i === 23) {
guid += "-";
} else if (i === 14) {
guid += "4";
} else if (i === 19) {
guid += Math.random() > 0.5 ? "a" : "b";
} else {
guid += hexDigits[(Math.random() * 16 + now) % 16 | 0];
}
}
return new _Guid(guid);
}
/**
* Creates a new cryptographically secure Guid using Crypto.getRandomValues.
* @returns A new secure Guid.
* @throws {BebopRuntimeError} If Crypto.getRandomValues is not available.
*/
static newSecureGuid() {
if (!hasCryptoGetRandomValues) {
throw new BebopRuntimeError(
"Crypto.getRandomValues is not available. Please include a polyfill or use in an environment that supports it."
);
}
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
bytes[6] = bytes[6] & 15 | 64;
bytes[8] = bytes[8] & 63 | 128;
return _Guid.fromBytes(bytes, 0);
}
/**
* Checks if the Guid is equal to another Guid.
* @param other The other Guid to be compared with.
* @returns true if the Guids are equal, false otherwise.
*/
equals(other) {
if (this === other) {
return true;
}
if (!(other instanceof _Guid)) {
return false;
}
for (let i = 0; i < this.value.length; i++) {
if (this.value[i] !== other.value[i]) {
return false;
}
}
return true;
}
/**
* Writes the Guid to a DataView.
* @param view The DataView to write to.
* @param length The position to start writing at.
*/
writeToView(view, length) {
var p = 0, a = 0;
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
p += this.value.charCodeAt(p) === 45;
view.setUint32(length, a, true);
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
p += this.value.charCodeAt(p) === 45;
view.setUint16(length + 4, a, true);
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
p += this.value.charCodeAt(p) === 45;
view.setUint16(length + 6, a, true);
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
p += this.value.charCodeAt(p) === 45;
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
view.setUint32(length + 8, a, false);
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
a = a << 4 | asciiToHex[this.value.charCodeAt(p++)];
view.setUint32(length + 12, a, false);
}
/**
* Creates a Guid from a byte array.
* @param buffer The byte array to create the Guid from.
* @param index The position in the array to start reading from.
* @returns A new Guid that represents the byte array.
*/
static fromBytes(buffer, index) {
var s = byteToHex[buffer[index + 3]];
s += byteToHex[buffer[index + 2]];
s += byteToHex[buffer[index + 1]];
s += byteToHex[buffer[index]];
s += guidDelimiter;
s += byteToHex[buffer[index + 5]];
s += byteToHex[buffer[index + 4]];
s += guidDelimiter;
s += byteToHex[buffer[index + 7]];
s += byteToHex[buffer[index + 6]];
s += guidDelimiter;
s += byteToHex[buffer[index + 8]];
s += byteToHex[buffer[index + 9]];
s += guidDelimiter;
s += byteToHex[buffer[index + 10]];
s += byteToHex[buffer[index + 11]];
s += byteToHex[buffer[index + 12]];
s += byteToHex[buffer[index + 13]];
s += byteToHex[buffer[index + 14]];
s += byteToHex[buffer[index + 15]];
return new _Guid(s);
}
/**
* Converts the Guid to a string when it's used as a primitive.
* @returns The string representation of the Guid.
*/
[Symbol.toPrimitive](hint) {
if (hint === "string" || hint === "default") {
return this.toString();
}
throw new Error(`Guid cannot be converted to ${hint}`);
}
};
var GuidMap = class _GuidMap {
map;
/**
* Creates a new GuidMap instance.
* @param entries - An optional array or iterable containing key-value pairs to initialize the map.
*/
constructor(entries) {
if (entries instanceof Map) {
this.map = new Map(
entries
);
} else if (entries && typeof entries[Symbol.iterator] === "function") {
this.map = new Map(
[...entries].map(([key, value]) => [key.toString(), value])
);
} else {
this.map = /* @__PURE__ */ new Map();
}
}
/**
* Sets the value associated with the specified `Guid` key in the map.
* @param key The `Guid` key.
* @param value The value to be set.
* @returns The updated `GuidMap` instance.
*/
set(key, value) {
this.map.set(key.toString(), value);
return this;
}
/**
* Retrieves the value associated with the specified `Guid` key from the map.
* @param key The `Guid` key.
* @returns The associated value, or `undefined` if the key is not found.
*/
get(key) {
return this.map.get(key.toString());
}
/**
* Deletes the value associated with the specified `Guid` key from the map.
* @param key The `Guid` key.
* @returns `true` if the key was found and deleted, or `false` otherwise.
*/
delete(key) {
return this.map.delete(key.toString());
}
/**
* Checks if the map contains the specified `Guid` key.
* @param key The `Guid` key.
* @returns `true` if the key is found, or `false` otherwise.
*/
has(key) {
return this.map.has(key.toString());
}
/**
* Removes all entries from the map.
*/
clear() {
this.map.clear();
}
/**
* Returns the number of entries in the map.
* @returns The number of entries in the map.
*/
get size() {
return this.map.size;
}
/**
* Executes the provided callback function once for each key-value pair in the map.
* @param callbackFn The callback function to execute.
*/
forEach(callbackFn) {
this.map.forEach((value, keyString) => {
callbackFn(value, Guid.parseGuid(keyString), this);
});
}
/**
* Returns an iterator that yields key-value pairs in the map.
* @returns An iterator for key-value pairs in the map.
*/
*entries() {
for (const [keyString, value] of this.map.entries()) {
yield [Guid.parseGuid(keyString), value];
}
}
/**
* Returns an iterator that yields the keys of the map.
* @returns An iterator for the keys of the map.
*/
*keys() {
for (const keyString of this.map.keys()) {
yield Guid.parseGuid(keyString);
}
}
/**
* Returns an iterator that yields the values in the map.
* @returns An iterator for the values in the map.
*/
*values() {
yield* this.map.values();
}
/**
* Returns an iterator that yields key-value pairs in the map.
* This method is invoked when using the spread operator or destructuring the map.
* @returns An iterator for key-value pairs in the map.
*/
[Symbol.iterator]() {
return this.entries();
}
/**
* The constructor function used to create derived objects.
*/
get [Symbol.species]() {
return _GuidMap;
}
};
var BebopView = class _BebopView {
static textDecoder;
static writeBuffer = new Uint8Array(256);
static writeBufferView = new DataView(_BebopView.writeBuffer.buffer);
static instance;
static getInstance() {
if (!_BebopView.instance) {
_BebopView.instance = new _BebopView();
}
return _BebopView.instance;
}
minimumTextDecoderLength = 300;
buffer;
view;
index;
// read pointer
length;
// write pointer
constructor() {
this.buffer = _BebopView.writeBuffer;
this.view = _BebopView.writeBufferView;
this.index = 0;
this.length = 0;
}
startReading(buffer) {
this.buffer = buffer;
this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
this.index = 0;
this.length = buffer.length;
}
startWriting() {
this.buffer = _BebopView.writeBuffer;
this.view = _BebopView.writeBufferView;
this.index = 0;
this.length = 0;
}
guaranteeBufferLength(length) {
if (length > this.buffer.length) {
const data = new Uint8Array(length << 1);
data.set(this.buffer);
this.buffer = data;
this.view = new DataView(data.buffer);
}
}
growBy(amount) {
this.length += amount;
this.guaranteeBufferLength(this.length);
}
skip(amount) {
this.index += amount;
}
toArray() {
return this.buffer.subarray(0, this.length);
}
readByte() {
return this.buffer[this.index++];
}
readUint16() {
const result = this.view.getUint16(this.index, true);
this.index += 2;
return result;
}
readInt16() {
const result = this.view.getInt16(this.index, true);
this.index += 2;
return result;
}
readUint32() {
const result = this.view.getUint32(this.index, true);
this.index += 4;
return result;
}
readInt32() {
const result = this.view.getInt32(this.index, true);
this.index += 4;
return result;
}
readUint64() {
const result = this.view.getBigUint64(this.index, true);
this.index += 8;
return result;
}
readInt64() {
const result = this.view.getBigInt64(this.index, true);
this.index += 8;
return result;
}
readFloat32() {
const result = this.view.getFloat32(this.index, true);
this.index += 4;
return result;
}
readFloat64() {
const result = this.view.getFloat64(this.index, true);
this.index += 8;
return result;
}
writeByte(value) {
const index = this.length;
this.growBy(1);
this.buffer[index] = value;
}
writeUint16(value) {
const index = this.length;
this.growBy(2);
this.view.setUint16(index, value, true);
}
writeInt16(value) {
const index = this.length;
this.growBy(2);
this.view.setInt16(index, value, true);
}
writeUint32(value) {
const index = this.length;
this.growBy(4);
this.view.setUint32(index, value, true);
}
writeInt32(value) {
const index = this.length;
this.growBy(4);
this.view.setInt32(index, value, true);
}
writeUint64(value) {
const index = this.length;
this.growBy(8);
this.view.setBigUint64(index, value, true);
}
writeInt64(value) {
const index = this.length;
this.growBy(8);
this.view.setBigInt64(index, value, true);
}
writeFloat32(value) {
const index = this.length;
this.growBy(4);
this.view.setFloat32(index, value, true);
}
writeFloat64(value) {
const index = this.length;
this.growBy(8);
this.view.setFloat64(index, value, true);
}
readBytes() {
const length = this.readUint32();
if (length === 0) {
return emptyByteArray;
}
const start = this.index, end = start + length;
this.index = end;
return this.buffer.subarray(start, end);
}
writeBytes(value) {
const byteCount = value.length;
this.writeUint32(byteCount);
if (byteCount === 0) {
return;
}
const index = this.length;
this.growBy(byteCount);
this.buffer.set(value, index);
}
/**
* Reads a length-prefixed UTF-8-encoded string.
*/
readString() {
const lengthBytes = this.readUint32();
if (lengthBytes === 0) {
return emptyString;
}
if (lengthBytes >= this.minimumTextDecoderLength) {
if (typeof require !== "undefined") {
if (typeof TextDecoder === "undefined") {
throw new BebopRuntimeError("TextDecoder is not defined on 'global'. Please include a polyfill.");
}
}
if (_BebopView.textDecoder === void 0) {
_BebopView.textDecoder = new TextDecoder();
}
return _BebopView.textDecoder.decode(this.buffer.subarray(this.index, this.index += lengthBytes));
}
const end = this.index + lengthBytes;
let result = "";
let codePoint;
while (this.index < end) {
const a = this.buffer[this.index++];
if (a < 192) {
codePoint = a;
} else {
const b = this.buffer[this.index++];
if (a < 224) {
codePoint = (a & 31) << 6 | b & 63;
} else {
const c = this.buffer[this.index++];
if (a < 240) {
codePoint = (a & 15) << 12 | (b & 63) << 6 | c & 63;
} else {
const d = this.buffer[this.index++];
codePoint = (a & 7) << 18 | (b & 63) << 12 | (c & 63) << 6 | d & 63;
}
}
}
if (codePoint < 65536) {
result += String.fromCharCode(codePoint);
} else {
codePoint -= 65536;
result += String.fromCharCode((codePoint >> 10) + 55296, (codePoint & (1 << 10) - 1) + 56320);
}
}
this.index = end;
return result;
}
/**
* Writes a length-prefixed UTF-8-encoded string.
*/
writeString(value) {
const stringLength = value.length;
if (stringLength === 0) {
this.writeUint32(0);
return;
}
const maxBytes = 4 + stringLength * 3;
this.guaranteeBufferLength(this.length + maxBytes);
let w = this.length + 4;
const start = w;
let codePoint;
for (let i = 0; i < stringLength; i++) {
const a = value.charCodeAt(i);
if (i + 1 === stringLength || a < 55296 || a >= 56320) {
codePoint = a;
} else {
const b = value.charCodeAt(++i);
codePoint = (a << 10) + b + (65536 - (55296 << 10) - 56320);
}
if (codePoint < 128) {
this.buffer[w++] = codePoint;
} else {
if (codePoint < 2048) {
this.buffer[w++] = codePoint >> 6 & 31 | 192;
} else {
if (codePoint < 65536) {
this.buffer[w++] = codePoint >> 12 & 15 | 224;
} else {
this.buffer[w++] = codePoint >> 18 & 7 | 240;
this.buffer[w++] = codePoint >> 12 & 63 | 128;
}
this.buffer[w++] = codePoint >> 6 & 63 | 128;
}
this.buffer[w++] = codePoint & 63 | 128;
}
}
const written = w - start;
this.view.setUint32(this.length, written, true);
this.length += 4 + written;
}
readGuid() {
const guid = Guid.fromBytes(this.buffer, this.index);
this.index += 16;
return guid;
}
writeGuid(value) {
const i = this.length;
this.growBy(16);
value.writeToView(this.view, i);
}
// A note on these numbers:
// 62135596800000 ms is the difference between the C# epoch (0001-01-01) and the Unix epoch (1970-01-01).
// 0.0001 is the number of milliseconds per "tick" (a tick is 100 ns).
// 429496.7296 is the number of milliseconds in 2^32 ticks.
// 0x3fffffff is a mask to ignore the "Kind" bits of the Date.ToBinary value.
// 0x40000000 is a mask to set the "Kind" bits to "DateTimeKind.Utc".
readDate() {
const ticks = this.readUint64() & dateMask;
const ms = (ticks - ticksBetweenEpochs) / 10000n;
return new Date(Number(ms));
}
writeDate(date) {
const ms = BigInt(date.getTime());
const ticks = ms * 10000n + ticksBetweenEpochs;
this.writeUint64(ticks & dateMask);
}
/**
* Reserve some space to write a message's length prefix, and return its index.
* The length is stored as a little-endian fixed-width unsigned 32-bit integer, so 4 bytes are reserved.
*/
reserveMessageLength() {
const i = this.length;
this.growBy(4);
return i;
}
/**
* Fill in a message's length prefix.
*/
fillMessageLength(position, messageLength) {
this.view.setUint32(position, messageLength, true);
}
/**
* Read out a message's length prefix.
*/
readMessageLength() {
const result = this.view.getUint32(this.index, true);
this.index += 4;
return result;
}
};
var typeMarker = "#btyp