UNPKG

convex

Version:

Client for the Convex Cloud

316 lines (315 loc) 9.61 kB
"use strict"; import * as Base64 from "base64-js"; const LITTLE_ENDIAN = true; const MIN_INT64 = BigInt("-9223372036854775808"); const MAX_INT64 = BigInt("9223372036854775807"); const ZERO = BigInt("0"); const EIGHT = BigInt("8"); const TWOFIFTYSIX = BigInt("256"); export class GenericId { constructor(tableName, id) { this.tableName = tableName; this.id = id; } equals(other) { if (other instanceof GenericId) { return this.tableName === other.tableName && this.id === other.id; } return false; } static fromJSON(obj) { if (typeof obj.$id !== "string") { throw new Error( `Object ${JSON.stringify(obj)} isn't a valid Id: $id isn't a string.` ); } const parts = obj.$id.split("|"); if (parts.length !== 2) { throw new Error( `Object ${JSON.stringify(obj)} isn't a valid Id: Wrong number of parts.` ); } return new GenericId(parts[0], parts[1]); } toJSON() { const idString = `${this.tableName}|${this.id}`; return { $id: idString }; } toString() { return this.id; } inspect() { return `Id('${this.tableName}', '${this.id}')`; } } function isSpecial(n) { return Number.isNaN(n) || !Number.isFinite(n) || Object.is(n, -0); } export function slowBigIntToBase64(value) { if (value < ZERO) { value -= MIN_INT64 + MIN_INT64; } let hex = value.toString(16); if (hex.length % 2 == 1) hex = "0" + hex; const bytes = new Uint8Array(new ArrayBuffer(8)); let i = 0; for (const hexByte of hex.match(/.{2}/g).reverse()) { bytes.set([parseInt(hexByte, 16)], i++); value >>= EIGHT; } return Base64.fromByteArray(bytes); } export function slowBase64ToBigInt(encoded) { const integerBytes = Base64.toByteArray(encoded); if (integerBytes.byteLength !== 8) { throw new Error( `Received ${integerBytes.byteLength} bytes, expected 8 for $integer` ); } let value = ZERO; let power = ZERO; for (const byte of integerBytes) { value += BigInt(byte) * TWOFIFTYSIX ** power; power++; } if (value > MAX_INT64) { value += MIN_INT64 + MIN_INT64; } return value; } export function modernBigIntToBase64(value) { if (value < MIN_INT64 || MAX_INT64 < value) { throw new Error( `BigInt ${value} does not fit into a 64-bit signed integer.` ); } const buffer = new ArrayBuffer(8); new DataView(buffer).setBigInt64(0, value, true); return Base64.fromByteArray(new Uint8Array(buffer)); } export function modernBase64ToBigInt(encoded) { const integerBytes = Base64.toByteArray(encoded); if (integerBytes.byteLength !== 8) { throw new Error( `Received ${integerBytes.byteLength} bytes, expected 8 for $integer` ); } const intBytesView = new DataView(integerBytes.buffer); return intBytesView.getBigInt64(0, true); } export const bigIntToBase64 = DataView.prototype.setBigInt64 ? modernBigIntToBase64 : slowBigIntToBase64; export const base64ToBigInt = DataView.prototype.getBigInt64 ? modernBase64ToBigInt : slowBase64ToBigInt; const MAX_IDENTIFIER_LEN = 64; const ALL_UNDERSCORES = /^_+$/; const IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/; function validateObjectField(k) { if (k.length === 0) { throw new Error("Empty field names are disallowed."); } if (k.length > MAX_IDENTIFIER_LEN) { throw new Error( `Field name ${k} exceeds maximum field name length ${MAX_IDENTIFIER_LEN}.` ); } if (k.startsWith("$")) { throw new Error(`Field name ${k} starts with a '$', which is reserved.`); } if (ALL_UNDERSCORES.test(k)) { throw new Error(`Field name ${k} can't exclusively be underscores.`); } if (!IDENTIFIER_REGEX.test(k)) { throw new Error( `Field name ${k} must only contain alphanumeric characters or underscores and can't start with a number.` ); } } function jsonToConvexInternal(value) { if (value === null) { return value; } if (typeof value === "boolean") { return value; } if (typeof value === "number") { return value; } if (typeof value === "string") { return value; } if (value instanceof Array) { return value.map(jsonToConvexInternal); } if (typeof value !== "object") { throw new Error(`Unexpected type of ${value}`); } const entries = Object.entries(value); if (entries.length === 1) { const key = entries[0][0]; if (key === "$id" || key === "$weakRef" || key === "$strongRef") { return GenericId.fromJSON(value); } if (key === "$bytes") { if (typeof value.$bytes !== "string") { throw new Error(`Malformed $bytes field on ${value}`); } return Base64.toByteArray(value.$bytes).buffer; } if (key === "$integer") { if (typeof value.$integer !== "string") { throw new Error(`Malformed $integer field on ${value}`); } return base64ToBigInt(value.$integer); } if (key === "$float") { if (typeof value.$float !== "string") { throw new Error(`Malformed $float field on ${value}`); } const floatBytes = Base64.toByteArray(value.$float); if (floatBytes.byteLength !== 8) { throw new Error( `Received ${floatBytes.byteLength} bytes, expected 8 for $float` ); } const floatBytesView = new DataView(floatBytes.buffer); const float = floatBytesView.getFloat64(0, LITTLE_ENDIAN); if (!isSpecial(float)) { throw new Error(`Float ${float} should be encoded as a number`); } return float; } if (key === "$set") { if (!(value.$set instanceof Array)) { throw new Error(`Malformed $set field on ${value}`); } return new Set(value.$set.map(jsonToConvexInternal)); } if (key === "$map") { if (!(value.$map instanceof Array)) { throw new Error(`Malformed $map field on ${value}`); } const map = /* @__PURE__ */ new Map(); for (const pair of value.$map) { if (!(pair instanceof Array) || pair.length !== 2) { throw new Error(`Malformed pair in $map ${value}`); } const k = jsonToConvexInternal(pair[0]); const v = jsonToConvexInternal(pair[1]); map.set(k, v); } return map; } } const out = {}; for (const [k, v] of Object.entries(value)) { validateObjectField(k); out[k] = jsonToConvexInternal(v); } return out; } export function jsonToConvex(value) { return jsonToConvexInternal(value); } function stringifyValueForError(value) { return JSON.stringify(value, (_key, value2) => { if (value2 === void 0) { return "undefined"; } return value2; }); } function convexToJsonInternal(value, originalValue, context) { if (value === void 0) { throw new Error( `undefined is not a valid Convex value (present at path ${context} in original object ${stringifyValueForError( originalValue )}). To learn about Convex's supported types, see https://docs.convex.dev/using/types.` ); } if (value === null) { return value; } if (value instanceof GenericId) { return value.toJSON(); } if (typeof value === "bigint") { if (value < MIN_INT64 || MAX_INT64 < value) { throw new Error( `BigInt ${value} does not fit into a 64-bit signed integer.` ); } return { $integer: bigIntToBase64(value) }; } if (typeof value === "number") { if (isSpecial(value)) { const buffer = new ArrayBuffer(8); new DataView(buffer).setFloat64(0, value, LITTLE_ENDIAN); return { $float: Base64.fromByteArray(new Uint8Array(buffer)) }; } else { return value; } } if (typeof value === "boolean") { return value; } if (typeof value === "string") { return value; } if (value instanceof ArrayBuffer) { return { $bytes: Base64.fromByteArray(new Uint8Array(value)) }; } if (value instanceof Array) { return value.map( (value2, i) => convexToJsonInternal(value2, originalValue, context + `[${i}]`) ); } if (value instanceof Set) { return { $set: [...value].map( (value2, i) => convexToJsonInternal(value2, originalValue, context + `.keys()[${i}]`) ) }; } if (value instanceof Map) { return { $map: [...value].map(([k, v], i) => { const jsonKey = convexToJsonInternal( k, originalValue, context + `.keys()[${i}]` ); const jsonValue = convexToJsonInternal( v, originalValue, context + `.values()[${i}]` ); return [jsonKey, jsonValue]; }) }; } if (typeof value !== "object") { throw new Error( `${value} is not a supported Convex type (present at path ${context} in original object ${stringifyValueForError( originalValue )}). To learn about Convex's supported types, see https://docs.convex.dev/using/types.` ); } const prototype = Object.getPrototypeOf(value); if (prototype !== null && prototype !== Object.prototype) { throw new Error( `${value} is not a supported Convex type (present at path ${context} in original object ${stringifyValueForError( originalValue )}). To learn about Convex's supported types, see https://docs.convex.dev/using/types.` ); } const out = {}; for (const [k, v] of Object.entries(value)) { validateObjectField(k); out[k] = convexToJsonInternal(v, originalValue, context + `.${k}`); } return out; } export function convexToJson(value) { return convexToJsonInternal(value, value, ""); } //# sourceMappingURL=values.js.map