Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/bindings-typescript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export * from './lib/time_duration';
export * from './lib/timestamp';
export * from './lib/utils';
export * from './lib/identity';
export * from './lib/option';
export * from './sdk';
export { default as t } from './server/type_builders';
11 changes: 2 additions & 9 deletions crates/bindings-typescript/src/lib/algebraic_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ConnectionId } from './connection_id';
import type BinaryReader from './binary_reader';
import BinaryWriter from './binary_writer';
import { Identity } from './identity';
import { Option } from './option';
import {
AlgebraicType as AlgebraicTypeType,
AlgebraicType as AlgebraicTypeValue,
Expand Down Expand Up @@ -89,15 +90,7 @@ export const AlgebraicType: {
value,
}),
createOptionType: function (innerType: AlgebraicTypeType): AlgebraicTypeType {
return AlgebraicTypeValue.Sum({
variants: [
{ name: 'some', algebraicType: innerType },
{
name: 'none',
algebraicType: AlgebraicTypeValue.Product({ elements: [] }),
},
],
});
return Option.getAlgebraicType(innerType);
},
createIdentityType: function (): AlgebraicTypeType {
return Identity.getAlgebraicType();
Expand Down
104 changes: 61 additions & 43 deletions crates/bindings-typescript/src/lib/binary_reader.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
function getUint8Array(
buffer: DataView,
byteOffset: number,
length: number
): Uint8Array {
return new Uint8Array(buffer.buffer, buffer.byteOffset + byteOffset, length);
}

export default class BinaryReader {
#buffer: DataView;
/**
* The DataView used to read values from the binary data.
*
* Note: The DataView's `byteOffset` is relative to the beginning of the
* underlying ArrayBuffer, not the start of the provided Uint8Array input.
* This `BinaryReader`'s `#offset` field is used to track the current read position
* relative to the start of the provided Uint8Array input.
*/
#view: DataView;

/**
* Represents the offset (in bytes) relative to the start of the DataView
* and provided Uint8Array input.
*
* Note: This is *not* the absolute byte offset within the underlying ArrayBuffer.
*/
#offset: number = 0;

constructor(input: Uint8Array) {
this.#buffer = new DataView(
input.buffer,
input.byteOffset,
input.byteLength
);
this.#view = new DataView(input.buffer, input.byteOffset, input.byteLength);
this.#offset = 0;
}

Expand All @@ -24,101 +27,116 @@ export default class BinaryReader {
}

get remaining(): number {
return this.#buffer.byteLength - (this.#buffer.byteOffset + this.#offset);
return this.#view.byteLength - this.#offset;
}

/** Ensure we have at least `n` bytes left to read */
#ensure(n: number): void {
if (this.#offset + n > this.#view.byteLength) {
throw new RangeError(
`Tried to read ${n} byte(s) at relative offset ${this.#offset}, but only ${this.remaining} byte(s) remain`
);
}
}

readUInt8Array(): Uint8Array {
const length = this.readU32();
this.#ensure(length);
return this.readBytes(length);
}

readBool(): boolean {
const value = this.#buffer.getUint8(this.#offset);
const value = this.#view.getUint8(this.#offset);
this.#offset += 1;
return value !== 0;
}

readByte(): number {
const value = this.#buffer.getUint8(this.#offset);
const value = this.#view.getUint8(this.#offset);
this.#offset += 1;
return value;
}

readBytes(length: number): Uint8Array {
const value = getUint8Array(this.#buffer, this.#offset, length);
// Create a Uint8Array view over the DataView's buffer at the current offset
// The #view.buffer is the whole ArrayBuffer, so we need to account for the
// #view's starting position in that buffer (#view.byteOffset) and the current #offset
const array = new Uint8Array(
this.#view.buffer,
this.#view.byteOffset + this.#offset,
length
);
this.#offset += length;
return value;
return array;
}

readI8(): number {
const value = this.#buffer.getInt8(this.#offset);
const value = this.#view.getInt8(this.#offset);
this.#offset += 1;
return value;
}

readU8(): number {
const value = this.#buffer.getUint8(this.#offset);
this.#offset += 1;
return value;
return this.readByte();
}

readI16(): number {
const value = this.#buffer.getInt16(this.#offset, true);
const value = this.#view.getInt16(this.#offset, true);
this.#offset += 2;
return value;
}

readU16(): number {
const value = this.#buffer.getUint16(this.#offset, true);
const value = this.#view.getUint16(this.#offset, true);
this.#offset += 2;
return value;
}

readI32(): number {
const value = this.#buffer.getInt32(this.#offset, true);
const value = this.#view.getInt32(this.#offset, true);
this.#offset += 4;
return value;
}

readU32(): number {
const value = this.#buffer.getUint32(this.#offset, true);
const value = this.#view.getUint32(this.#offset, true);
this.#offset += 4;
return value;
}

readI64(): bigint {
const value = this.#buffer.getBigInt64(this.#offset, true);
const value = this.#view.getBigInt64(this.#offset, true);
this.#offset += 8;
return value;
}

readU64(): bigint {
const value = this.#buffer.getBigUint64(this.#offset, true);
const value = this.#view.getBigUint64(this.#offset, true);
this.#offset += 8;
return value;
}

readU128(): bigint {
const lowerPart = this.#buffer.getBigUint64(this.#offset, true);
const upperPart = this.#buffer.getBigUint64(this.#offset + 8, true);
const lowerPart = this.#view.getBigUint64(this.#offset, true);
const upperPart = this.#view.getBigUint64(this.#offset + 8, true);
this.#offset += 16;

return (upperPart << BigInt(64)) + lowerPart;
}

readI128(): bigint {
const lowerPart = this.#buffer.getBigUint64(this.#offset, true);
const upperPart = this.#buffer.getBigInt64(this.#offset + 8, true);
const lowerPart = this.#view.getBigUint64(this.#offset, true);
const upperPart = this.#view.getBigInt64(this.#offset + 8, true);
this.#offset += 16;

return (upperPart << BigInt(64)) + lowerPart;
}

readU256(): bigint {
const p0 = this.#buffer.getBigUint64(this.#offset, true);
const p1 = this.#buffer.getBigUint64(this.#offset + 8, true);
const p2 = this.#buffer.getBigUint64(this.#offset + 16, true);
const p3 = this.#buffer.getBigUint64(this.#offset + 24, true);
const p0 = this.#view.getBigUint64(this.#offset, true);
const p1 = this.#view.getBigUint64(this.#offset + 8, true);
const p2 = this.#view.getBigUint64(this.#offset + 16, true);
const p3 = this.#view.getBigUint64(this.#offset + 24, true);
this.#offset += 32;

return (
Expand All @@ -130,10 +148,10 @@ export default class BinaryReader {
}

readI256(): bigint {
const p0 = this.#buffer.getBigUint64(this.#offset, true);
const p1 = this.#buffer.getBigUint64(this.#offset + 8, true);
const p2 = this.#buffer.getBigUint64(this.#offset + 16, true);
const p3 = this.#buffer.getBigInt64(this.#offset + 24, true);
const p0 = this.#view.getBigUint64(this.#offset, true);
const p1 = this.#view.getBigUint64(this.#offset + 8, true);
const p2 = this.#view.getBigUint64(this.#offset + 16, true);
const p3 = this.#view.getBigInt64(this.#offset + 24, true);
this.#offset += 32;

return (
Expand All @@ -145,13 +163,13 @@ export default class BinaryReader {
}

readF32(): number {
const value = this.#buffer.getFloat32(this.#offset, true);
const value = this.#view.getFloat32(this.#offset, true);
this.#offset += 4;
return value;
}

readF64(): number {
const value = this.#buffer.getFloat64(this.#offset, true);
const value = this.#view.getFloat64(this.#offset, true);
this.#offset += 8;
return value;
}
Expand Down
9 changes: 8 additions & 1 deletion crates/bindings-typescript/src/lib/connection_id.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { AlgebraicType } from './algebraic_type';
import { hexStringToU128, u128ToHexString, u128ToUint8Array } from './utils';

export type ConnectionIdAlgebraicType = {
tag: 'Product';
value: {
elements: [{ name: '__connection_id__'; algebraicType: { tag: 'U128' } }];
};
};

/**
* A unique identifier for a client connected to a database.
*/
Expand All @@ -18,7 +25,7 @@ export class ConnectionId {
* Get the algebraic type representation of the {@link ConnectionId} type.
* @returns The algebraic type representation of the type.
*/
static getAlgebraicType(): AlgebraicType {
static getAlgebraicType(): ConnectionIdAlgebraicType {
return AlgebraicType.Product({
elements: [
{ name: '__connection_id__', algebraicType: AlgebraicType.U128 },
Expand Down
9 changes: 8 additions & 1 deletion crates/bindings-typescript/src/lib/identity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { AlgebraicType } from './algebraic_type';
import { hexStringToU256, u256ToHexString, u256ToUint8Array } from './utils';

export type IdentityAlgebraicType = {
tag: 'Product';
value: {
elements: [{ name: '__identity__'; algebraicType: { tag: 'U256' } }];
};
};

/**
* A unique identifier for a user connected to a database.
*/
Expand All @@ -22,7 +29,7 @@ export class Identity {
* Get the algebraic type representation of the {@link Identity} type.
* @returns The algebraic type representation of the type.
*/
static getAlgebraicType(): AlgebraicType {
static getAlgebraicType(): IdentityAlgebraicType {
return AlgebraicType.Product({
elements: [{ name: '__identity__', algebraicType: AlgebraicType.U256 }],
});
Expand Down
30 changes: 30 additions & 0 deletions crates/bindings-typescript/src/lib/option.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { AlgebraicType } from './algebraic_type';

export type OptionAlgebraicType = {
tag: 'Sum';
value: {
variants: [
{ name: 'some'; algebraicType: AlgebraicType },
{
name: 'none';
algebraicType: { tag: 'Product'; value: { elements: [] } };
},
];
};
};

export const Option: {
getAlgebraicType(innerType: AlgebraicType): OptionAlgebraicType;
} = {
getAlgebraicType(innerType: AlgebraicType): OptionAlgebraicType {
return AlgebraicType.Sum({
variants: [
{ name: 'some', algebraicType: innerType },
{
name: 'none',
algebraicType: AlgebraicType.Product({ elements: [] }),
},
],
});
},
};
42 changes: 31 additions & 11 deletions crates/bindings-typescript/src/lib/schedule_at.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
import { AlgebraicType } from './algebraic_type';
import { TimeDuration } from './time_duration';
import { Timestamp } from './timestamp';
import { TimeDuration, type TimeDurationAlgebraicType } from './time_duration';
import { Timestamp, type TimestampAlgebraicType } from './timestamp';

export type ScheduleAtAlgebraicType = {
tag: 'Sum';
value: {
variants: [
{ name: 'Interval'; algebraicType: TimeDurationAlgebraicType },
{ name: 'Time'; algebraicType: TimestampAlgebraicType },
];
};
};

type ScheduleAtType = Interval | Time;

export const ScheduleAt: {
interval: (micros: bigint) => ScheduleAtType;
time: (microsSinceUnixEpoch: bigint) => ScheduleAtType;
/**
* Get the algebraic type representation of the {@link ScheduleAt} type.
* @returns The algebraic type representation of the type.
*/
getAlgebraicType(): AlgebraicType;
getAlgebraicType(): ScheduleAtAlgebraicType;
} = {
getAlgebraicType(): AlgebraicType {
interval(value: bigint): ScheduleAtType {
return Interval(value);
},
time(value: bigint): ScheduleAtType {
return Time(value);
},
getAlgebraicType(): ScheduleAtAlgebraicType {
return AlgebraicType.Sum({
variants: [
{
name: 'Interval',
algebraicType: AlgebraicType.createTimeDurationType(),
algebraicType: TimeDuration.getAlgebraicType(),
},
{ name: 'Time', algebraicType: AlgebraicType.createTimestampType() },
{ name: 'Time', algebraicType: Timestamp.getAlgebraicType() },
],
});
},
Expand All @@ -26,18 +46,18 @@ export type Interval = {
tag: 'Interval';
value: TimeDuration;
};
export const Interval = (value: bigint): Interval => ({
export const Interval = (micros: bigint): Interval => ({
tag: 'Interval',
value: new TimeDuration(value),
value: new TimeDuration(micros),
});
export type Time = {
tag: 'Time';
value: Timestamp;
};
export const Time = (value: bigint): Time => ({
export const Time = (microsSinceUnixEpoch: bigint): Time => ({
tag: 'Time',
value: new Timestamp(value),
value: new Timestamp(microsSinceUnixEpoch),
});

export type ScheduleAt = Interval | Time;
export default ScheduleAt;
export type ScheduleAt = ScheduleAtType;
Loading
Loading