diff --git a/src/identity.ts b/src/identity.ts index 204cf14..eaeb406 100644 --- a/src/identity.ts +++ b/src/identity.ts @@ -1,52 +1,57 @@ +// Helper function convert from string to Uint8Array +function hexStringToUint8Array(str: string): Uint8Array { + let matches = str.match(/.{1,2}/g) || []; + let data = Uint8Array.from(matches.map((byte: string) => parseInt(byte, 16))); + return data; +} + +// Helper function for converting Uint8Array to hex string +function uint8ArrayToHexString(array: Uint8Array): string { + return Array.prototype.map + .call(array, (x) => ("00" + x.toString(16)).slice(-2)) + .join(""); +} + /** * A unique public identifier for a client connected to a database. */ export class Identity { - private data: Uint8Array; + private data: string; /** * Creates a new `Identity`. */ - constructor(data: Uint8Array) { - this.data = data; + constructor(data: { __identity_bytes: string } | Uint8Array) { + // we get a JSON with __identity_bytes when getting a token with a JSON API + // and an Uint8Array when using BSATN + this.data = + data.constructor === Uint8Array + ? uint8ArrayToHexString(data as Uint8Array) + : (data as { __identity_bytes: string }).__identity_bytes; } /** * Compare two identities for equality. */ isEqual(other: Identity): boolean { - if (this.data.length !== other.data.length) { - return false; - } - for (let i = 0; i < this.data.length; i++) { - if (this.data[i] !== other.data[i]) { - return false; - } - } - return true; + return this.toHexString() === other.toHexString(); } /** * Print the identity as a hexadecimal string. */ toHexString(): string { - return Array.prototype.map - .call(this.data, (x) => ("00" + x.toString(16)).slice(-2)) - .join(""); + return this.data; } toUint8Array(): Uint8Array { - return this.data; + return hexStringToUint8Array(this.toHexString()); } /** * Parse an Identity from a hexadecimal string. */ static fromString(str: string): Identity { - let matches = str.match(/.{1,2}/g) || []; - let data = Uint8Array.from( - matches.map((byte: string) => parseInt(byte, 16)) - ); - return new Identity(data); + return new Identity({ __identity_bytes: str }); } } diff --git a/src/serializer.ts b/src/serializer.ts index e78e753..1c2c487 100644 --- a/src/serializer.ts +++ b/src/serializer.ts @@ -1,5 +1,6 @@ import { AlgebraicType, BuiltinType } from "./algebraic_type"; import BinaryWriter from "./binary_writer"; +import { Identity } from "./identity"; export interface Serializer { write(type: AlgebraicType, value: any): any; @@ -42,10 +43,19 @@ export class JSONSerializer { case AlgebraicType.Type.ProductType: let serializedArray: any[] = []; for (const element of type.product.elements) { - const serialized = this.serializeType( - element.algebraicType, - value[element.name] - ); + let serialized: any; + // If the value is an identity we can't use the `[]` operator, so we're + // special casing the identity type. It might be possible to define the + // `__identity_bytes` property on Identity, but I don't have time to check + // at the moment + if (value.constructor === Identity) { + serialized = value.toHexString(); + } else { + serialized = this.serializeType( + element.algebraicType, + value[element.name] + ); + } serializedArray.push(serialized); } return serializedArray; @@ -97,7 +107,14 @@ export class BinarySerializer { break; case AlgebraicType.Type.ProductType: for (const element of type.product.elements) { - this.write(element.algebraicType, value[element.name]); + this.write( + element.algebraicType, + // If the value is an Identity we have to return an Uint8Array instead of trying + // to use the `[]` operator. + value.constructor === Identity + ? value.toUint8Array() + : value[element.name] + ); } break; case AlgebraicType.Type.SumType: diff --git a/src/spacetimedb.ts b/src/spacetimedb.ts index c06feae..0e33a6e 100644 --- a/src/spacetimedb.ts +++ b/src/spacetimedb.ts @@ -989,9 +989,7 @@ export class SpacetimeDBClient { const event = txUpdate["event"] as any; const functionCall = event["function_call"] as any; - const identity: Identity = Identity.fromString( - event["caller_identity"] - ); + const identity: Identity = new Identity(event["caller_identity"]); const originalReducerName: string = functionCall["reducer"]; const reducerName: string = toPascalCase(originalReducerName); const args = JSON.parse(functionCall["args"]);