UNPKG

@deepkit/bson

Version:

Deepkit BSON parser

1,116 lines 83.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.__ΩBSONSizer = exports.__ΩBSONSerializer = exports.__ΩBSONSerializerState = exports.bsonBinarySerializer = exports.BSONBinarySerializer = exports.DigitByteRuntimeCode = exports.Writer = exports.AutoBuffer = exports.__ΩAutoBufferSerializer = exports.__ΩAutoBufferSerializerState = exports.ValueWithBSONSerializer = exports.JS_INT_MIN = exports.JS_INT_MAX = void 0; exports.hexToByte = hexToByte; exports.uuidStringToByte = uuidStringToByte; exports.stringByteLength = stringByteLength; exports.getValueSize = getValueSize; exports.wrapValue = wrapValue; exports.wrapObjectId = wrapObjectId; exports.wrapUUID = wrapUUID; exports.writeUint32LE = writeUint32LE; exports.writeInt32LE = writeInt32LE; exports.writeFloat64LE = writeFloat64LE; exports.createBSONSizer = createBSONSizer; exports.serializeBSONWithoutOptimiser = serializeBSONWithoutOptimiser; exports.getBSONSerializer = getBSONSerializer; exports.getBSONSizer = getBSONSizer; exports.serializeBSON = serializeBSON; const __ΩArrayBufferView = [() => __ΩArrayBufferLike, 'TArrayBuffer', 'buffer', 'byteLength', 'byteOffset', 'ArrayBufferView', 'n!c"Pe"!4#9\'4$9\'4%9Mw&y']; const __ΩArrayBufferLike = [() => __ΩArrayBufferTypes, () => __ΩArrayBufferTypes, 'ArrayBufferLike', 'n!n"gfw#y']; const __ΩArrayBufferTypes = ['ArrayBuffer', 'ArrayBufferTypes', 'P_4!Mw"y']; function __assignType(fn, args) { fn.__type = args; return fn; } /* * Deepkit Framework * Copyright (C) 2021 Deepkit UG, Marc J. Schmidt * * This program is free software: you can redistribute it and/or modify * it under the terms of the MIT License. * * You should have received a copy of the MIT License along with this program. */ const core_1 = require("@deepkit/core"); /*@ts-ignore*/ var { __ΩReceiveType, __ΩMongoId, __ΩUUID, __ΩTypeClass, __ΩTypeObjectLiteral, __ΩTypeBigInt, __ΩTypeLiteral, __ΩTypeArray, __ΩTypeTuple } = require('@deepkit/type'); const type_1 = require("@deepkit/type"); const bson_deserializer_templates_js_1 = require("./bson-deserializer-templates.js"); const continuation_js_1 = require("./continuation.js"); /*@ts-ignore*/ var { __ΩBSONType } = require('./utils.js'); const utils_js_1 = require("./utils.js"); // BSON MAX VALUES const BSON_INT32_MAX = 0x7fffffff; const BSON_INT32_MIN = -0x80000000; // JS MAX PRECISE VALUES exports.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double. exports.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double. const LONG_MAX = 'undefined' !== typeof BigInt ? BigInt('9223372036854775807') : 9223372036854775807; const LONG_MIN = 'undefined' !== typeof BigInt ? BigInt('-9223372036854775807') : -9223372036854775807; function hexToByte(hex, index = 0, offset = 0) { let code1 = hex.charCodeAt(index * 2 + offset) - 48; if (code1 > 9) code1 -= 39; let code2 = hex.charCodeAt((index * 2) + offset + 1) - 48; if (code2 > 9) code2 -= 39; return code1 * 16 + code2; } hexToByte.__type = ['hex', 'index', () => 0, 'offset', () => 0, 'hexToByte', 'P&2!\'2">#\'2$>%\'/&']; function uuidStringToByte(hex, index = 0) { let offset = 0; //e.g. bef8de96-41fe-442f-b70c-c3a150f8c96c if (index > 3) offset += 1; if (index > 5) offset += 1; if (index > 7) offset += 1; if (index > 9) offset += 1; return hexToByte(hex, index, offset); } uuidStringToByte.__type = ['hex', 'index', () => 0, 'uuidStringToByte', 'P&2!\'2">#\'/$']; function stringByteLength(str) { if (!str) return 0; let size = 0; for (let i = 0; i < str.length; i++) { const c = str.charCodeAt(i); // surrogate pair if (c >= 0xD800 && c <= 0xDBFF && i + 1 < str.length) { const lo = str.charCodeAt(i + 1); if (lo >= 0xDC00 && lo <= 0xDFFF) { // surrogate pair is a 4-byte character in UTF-8 size += 4; // move past the low surrogate since it's part of the character i++; continue; } } if (c < 128) size += 1; else if (c > 127 && c < 2048) size += 2; else size += 3; } return size; } stringByteLength.__type = ['str', 'stringByteLength', 'P&2!\'/"']; function getBinaryBigIntSize(value) { let hex = value.toString(16); if (hex[0] === '-') hex = hex.slice(1); if (hex === '0') return 4 + 1; if (hex.length % 2) hex = '0' + hex; return 4 + 1 + Math.ceil(hex.length / 2); } getBinaryBigIntSize.__type = ['value', 'getBinaryBigIntSize', 'P*2!\'/"']; function getSignedBinaryBigIntSize(value) { let hex = value.toString(16); if (hex[0] === '-') hex = hex.slice(1); if (hex === '0') return 4 + 1; if (hex.length % 2) hex = '0' + hex; return 4 + 1 + 1 + Math.ceil(hex.length / 2); } getSignedBinaryBigIntSize.__type = ['value', 'getSignedBinaryBigIntSize', 'P*2!\'/"']; function getValueSize(value) { if (value instanceof ValueWithBSONSerializer) { if ((0, type_1.isUUIDType)(value.type)) { return 4 + 1 + 16; } else if ((0, type_1.isMongoIdType)(value.type)) { return 12; } else if ((0, type_1.isBinaryBigIntType)(value.type)) { const binaryBigInt = type_1.binaryBigIntAnnotation.getFirst(value.type); return binaryBigInt === 0 /* BinaryBigIntType.unsigned */ ? getBinaryBigIntSize(value.value) : getSignedBinaryBigIntSize(value.value); } else { return getValueSize(value.value); } } else if ('boolean' === typeof value) { return 1; } else if ('string' === typeof value) { //size + content + null return 4 + stringByteLength(value) + 1; } else if ('bigint' === typeof value) { //per default bigint will be serialized as long, to be compatible with default mongo driver and mongo database. return 8; } else if ('number' === typeof value) { if (Math.floor(value) === value) { //it's an int if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) { //32bit return 4; } else if (value >= exports.JS_INT_MIN && value <= exports.JS_INT_MAX) { //double, 64bit return 8; } else { //long return 8; } } else { //double return 8; } } else if (value instanceof Date) { return 8; } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) { let size = 4; //size size += 1; //sub type size += value.byteLength; return size; } else if ((0, core_1.isArray)(value)) { let size = 4; //object size for (let i = 0; i < value.length; i++) { size += 1; //element type size += (0, utils_js_1.digitByteSize)(i); //element name size += getValueSize(value[i]); } size += 1; //null return size; } else if (value && value['_bsontype'] === 'Binary') { let size = 4; //size size += 1; //sub type size += value.buffer.byteLength; return size; } else if (value instanceof RegExp) { return stringByteLength(value.source) + 1 + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1; } else if ((0, core_1.isObject)(value)) { let size = 4; //object size for (let i in value) { if (!(0, core_1.hasProperty)(value, i)) continue; size += 1; //element type size += stringByteLength(i) + 1; //element name + null size += getValueSize(value[i]); } size += 1; //null return size; } //isObject() should be last return 0; } getValueSize.__type = ['value', 'getValueSize', 'P"2!\'/"']; class ValueWithBSONSerializer { constructor(value, type) { this.value = value; this.type = type; } } exports.ValueWithBSONSerializer = ValueWithBSONSerializer; ValueWithBSONSerializer.__type = ['value', 'Type', 'type', 'constructor', 'ValueWithBSONSerializer', 'P"2!:"w"2#:"0$5w%']; function wrapValue(value, type = wrapValue.Ω?.[0]) { wrapValue.Ω = undefined; return new ValueWithBSONSerializer(value, (0, type_1.resolveReceiveType)(type)); } wrapValue.__type = ['value', () => __ΩReceiveType, 'type', 'wrapValue', 'P"2!"o""2#8"/$']; const mongoIdType = (0, type_1.typeOf)([], [() => __ΩMongoId, 'n!']); function wrapObjectId(value) { return new ValueWithBSONSerializer(value, mongoIdType); } wrapObjectId.__type = ['value', 'wrapObjectId', 'P&2!"/"']; const uuidIdType = (0, type_1.typeOf)([], [() => __ΩUUID, 'n!']); function wrapUUID(value) { return new ValueWithBSONSerializer(value, uuidIdType); } wrapUUID.__type = ['value', 'wrapUUID', 'P&2!"/"']; function writeUint32LE(buffer, offset, value) { buffer[offset] = value & 0xff; buffer[offset + 1] = (value >> 8) & 0xff; buffer[offset + 2] = (value >> 16) & 0xff; buffer[offset + 3] = (value >> 24) & 0xff; } writeUint32LE.__type = ['buffer', 'offset', 'value', 'writeUint32LE', 'PW2!\'2"\'2#"/$']; function writeInt32LE(buffer, offset, value) { // writing is the same as unsigned return writeUint32LE(buffer, offset, value); } writeInt32LE.__type = ['buffer', 'offset', 'value', 'writeInt32LE', 'PW2!\'2"\'2#"/$']; const float64Buffer = new ArrayBuffer(8); const u32 = new Uint32Array(float64Buffer); const f64 = new Float64Array(float64Buffer); function writeFloat64LE(buffer, offset, value) { f64[0] = value; buffer[offset] = u32[0] & 0xff; buffer[offset + 1] = (u32[0] >> 8) & 0xff; buffer[offset + 2] = (u32[0] >> 16) & 0xff; buffer[offset + 3] = (u32[0] >> 24) & 0xff; buffer[offset + 4] = u32[1] & 0xff; buffer[offset + 5] = (u32[1] >> 8) & 0xff; buffer[offset + 6] = (u32[1] >> 16) & 0xff; buffer[offset + 7] = (u32[1] >> 24) & 0xff; } writeFloat64LE.__type = ['buffer', 'offset', 'value', 'writeFloat64LE', 'PW2!\'2"\'2#$/$']; const __ΩAutoBufferSerializerState = [() => Writer, 'writer', 'AutoBufferSerializerState', 'PP7!4"8Mw#y']; exports.__ΩAutoBufferSerializerState = __ΩAutoBufferSerializerState; const __ΩAutoBufferSerializer = ['data', () => __ΩAutoBufferSerializerState, 'state', '', 'AutoBufferSerializer', 'P"2!n"2#$/$w%y']; exports.__ΩAutoBufferSerializer = __ΩAutoBufferSerializer; class AutoBuffer { constructor(prepend = 0, initialSize = 256) { this.prepend = prepend; this.initialSize = initialSize; this._buffer = new Uint8Array(this.initialSize); this.state = { writer: new Writer(this._buffer) }; } get buffer() { return this._buffer.subarray(0, this.size); } get size() { return this.state.writer.offset; } setOffset(offset) { this.state.writer.offset = offset; } apply(serializer, data) { this.state.writer.offset = this.prepend; serializer(data, this.state); if (this.prepend + this.state.writer.offset > this._buffer.byteLength) { // increase buffer and retry const nextBuffer = new Uint8Array(this.prepend + this.state.writer.offset); if (this.prepend) nextBuffer.set(this._buffer.subarray(0, this.prepend)); this._buffer = nextBuffer; this.state.writer.buffer = this._buffer; this.state.writer.reset(); this.state.writer.offset = this.prepend; serializer(data, this.state); } } } exports.AutoBuffer = AutoBuffer; AutoBuffer.__type = ['_buffer', function () { return new Uint8Array(this.initialSize); }, 'state', function () { return { writer: new Writer(this._buffer) }; }, 'prepend', () => 0, 'initialSize', () => 256, 'constructor', 'offset', 'setOffset', () => __ΩAutoBufferSerializer, 'serializer', 'data', 'apply', 'AutoBuffer', 'W3!>"!3#>$P\'2%:>&\'2\';>("0)!!P\'2*"0+Pn,2-"2."0/5w0']; class Writer { constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; this.typeOffset = 0; } reset() { this.offset = 0; this.typeOffset = 0; } /** * If typeOffset is defined, the type will be written at this offset. * Useful for writing type information for members in array/object literals. */ writeType(v) { if (this.typeOffset !== 0) { this.buffer[this.typeOffset] = v; this.typeOffset = 0; } } resetWriteType() { this.typeOffset = 0; } prepareWriteType() { this.typeOffset = this.offset; // It might be that a subsequent content writer is omitted (circular reference), // then we want undefined to be written as type. this.buffer[this.offset] = utils_js_1.BSONType.UNDEFINED; this.offset += 1; } writeUint32(v) { writeUint32LE(this.buffer, this.offset, v); this.offset += 4; } writeInt32(v) { writeInt32LE(this.buffer, this.offset, v); this.offset += 4; } writeDouble(v) { writeFloat64LE(this.buffer, this.offset, v); this.offset += 8; } writeDelayedSize(v, position) { writeUint32LE(this.buffer, position, v); } writeByte(v) { this.buffer[this.offset++] = v; } writeBuffer(buffer, offset = 0) { // buffer.copy(this.buffer, this.buffer.byteOffset + this.offset); for (let i = offset; i < buffer.byteLength; i++) { this.buffer[this.offset++] = buffer[i]; } // this.offset += buffer.byteLength; } writeNull() { this.writeByte(0); } writeAsciiString(str) { str = 'string' === typeof str ? str : '' + str; for (let i = 0; i < str.length; i++) { this.buffer[this.offset++] = str.charCodeAt(i); } } writeString(str) { if (!str) return; if (typeof str !== 'string') return; for (let i = 0; i < str.length; i++) { const c = str.charCodeAt(i); // surrogate pairs for characters outside the BMP if (c >= 0xD800 && c <= 0xDBFF && i + 1 < str.length) { const hi = c; const lo = str.charCodeAt(i + 1); if (lo >= 0xDC00 && lo <= 0xDFFF) { // combine the surrogate pair and subtract 0x10000 for UTF-8 encoding const codePoint = ((hi - 0xD800) * 0x400) + (lo - 0xDC00) + 0x10000; this.buffer[this.offset++] = (codePoint >> 18) | 240; this.buffer[this.offset++] = ((codePoint >> 12) & 63) | 128; this.buffer[this.offset++] = ((codePoint >> 6) & 63) | 128; this.buffer[this.offset++] = (codePoint & 63) | 128; // skip the next code unit, since it's part of the surrogate pair i++; continue; } } if (c < 128) { this.buffer[this.offset++] = c; } else if (c > 127 && c < 2048) { this.buffer[this.offset++] = (c >> 6) | 192; this.buffer[this.offset++] = ((c & 63) | 128); } else { this.buffer[this.offset++] = (c >> 12) | 224; this.buffer[this.offset++] = ((c >> 6) & 63) | 128; this.buffer[this.offset++] = (c & 63) | 128; } } } getBigIntBSONType(value) { if (BSON_INT32_MIN <= value && value <= BSON_INT32_MAX) { return utils_js_1.BSONType.INT; } else if (LONG_MIN <= value && value <= LONG_MAX) { return utils_js_1.BSONType.LONG; } else { return utils_js_1.BSONType.BINARY; } } writeBigIntLong(value) { if (value < 0) { this.writeInt32(~Number(-value % BigInt(utils_js_1.TWO_PWR_32_DBL_N)) + 1 | 0); //low this.writeInt32(~(Number(-value / BigInt(utils_js_1.TWO_PWR_32_DBL_N))) | 0); //high } else { this.writeInt32(Number(value % BigInt(utils_js_1.TWO_PWR_32_DBL_N)) | 0); //low this.writeInt32(Number(value / BigInt(utils_js_1.TWO_PWR_32_DBL_N)) | 0); //high } } writeBigIntBinary(value) { //custom binary let hex = value.toString(16); if (hex[0] === '-') hex = hex.slice(1); if (hex === '0') { this.writeUint32(0); this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT); return; } if (hex.length % 2) hex = '0' + hex; let size = Math.ceil(hex.length / 2); this.writeUint32(size); this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT); for (let i = 0; i < size; i++) { this.buffer[this.offset++] = hexToByte(hex, i); } } writeSignedBigIntBinary(value) { //custom binary let hex = value.toString(16); let signum = 0; if (hex[0] === '-') { //negative number signum = 1; hex = hex.slice(1); } if (hex === '0') { this.writeUint32(0); this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT); return; } if (hex.length % 2) hex = '0' + hex; let size = Math.ceil(hex.length / 2); this.writeUint32(1 + size); this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT); this.buffer[this.offset++] = signum === 1 ? 255 : 0; //0xff means negative, 0 means positive for (let i = 0; i < size; i++) { this.buffer[this.offset++] = hexToByte(hex, i); } } writeLong(value) { if (value > 9223372036854775807) value = 9223372036854775807; if (value < -9223372036854775807) value = -9223372036854775807; if (value < 0) { this.writeInt32(~(-value % utils_js_1.TWO_PWR_32_DBL_N) + 1 | 0); //low this.writeInt32(~(-value / utils_js_1.TWO_PWR_32_DBL_N) | 0); //high } else { this.writeInt32((value % utils_js_1.TWO_PWR_32_DBL_N) | 0); //low this.writeInt32((value / utils_js_1.TWO_PWR_32_DBL_N) | 0); //high } } writeUUID(value) { this.writeUint32(16); this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_UUID); this.buffer[this.offset + 0] = uuidStringToByte(value, 0); this.buffer[this.offset + 1] = uuidStringToByte(value, 1); this.buffer[this.offset + 2] = uuidStringToByte(value, 2); this.buffer[this.offset + 3] = uuidStringToByte(value, 3); //- this.buffer[this.offset + 4] = uuidStringToByte(value, 4); this.buffer[this.offset + 5] = uuidStringToByte(value, 5); //- this.buffer[this.offset + 6] = uuidStringToByte(value, 6); this.buffer[this.offset + 7] = uuidStringToByte(value, 7); //- this.buffer[this.offset + 8] = uuidStringToByte(value, 8); this.buffer[this.offset + 9] = uuidStringToByte(value, 9); //- this.buffer[this.offset + 10] = uuidStringToByte(value, 10); this.buffer[this.offset + 11] = uuidStringToByte(value, 11); this.buffer[this.offset + 12] = uuidStringToByte(value, 12); this.buffer[this.offset + 13] = uuidStringToByte(value, 13); this.buffer[this.offset + 14] = uuidStringToByte(value, 14); this.buffer[this.offset + 15] = uuidStringToByte(value, 15); this.offset += 16; } writeObjectId(value) { this.buffer[this.offset + 0] = hexToByte(value, 0); this.buffer[this.offset + 1] = hexToByte(value, 1); this.buffer[this.offset + 2] = hexToByte(value, 2); this.buffer[this.offset + 3] = hexToByte(value, 3); this.buffer[this.offset + 4] = hexToByte(value, 4); this.buffer[this.offset + 5] = hexToByte(value, 5); this.buffer[this.offset + 6] = hexToByte(value, 6); this.buffer[this.offset + 7] = hexToByte(value, 7); this.buffer[this.offset + 8] = hexToByte(value, 8); this.buffer[this.offset + 9] = hexToByte(value, 9); this.buffer[this.offset + 10] = hexToByte(value, 10); this.buffer[this.offset + 11] = hexToByte(value, 11); this.offset += 12; } write(value) { if (value instanceof ValueWithBSONSerializer) { if (value.value !== undefined && value.value !== null) { if ((0, type_1.isUUIDType)(value.type)) { this.writeType(utils_js_1.BSONType.BINARY); this.writeUUID(value.value); return; } else if ((0, type_1.isMongoIdType)(value.type)) { this.writeType(utils_js_1.BSONType.OID); this.writeObjectId(value.value); return; } else if ((0, type_1.isBinaryBigIntType)(value.type)) { this.writeType(utils_js_1.BSONType.BINARY); const binary = type_1.binaryBigIntAnnotation.getFirst(value.type); if (binary === 1 /* BinaryBigIntType.signed */) { this.writeSignedBigIntBinary(value.value); } else { this.writeBigIntBinary(value.value); } return; } } this.write(value.value); } else if ('boolean' === typeof value) { this.writeType(utils_js_1.BSONType.BOOLEAN); this.writeByte(value ? 1 : 0); } else if (value instanceof RegExp) { this.writeType(utils_js_1.BSONType.REGEXP); this.writeString(value.source); this.writeNull(); if (value.ignoreCase) this.writeString('i'); if (value.global) this.writeString('s'); //BSON does not use the RegExp flag format if (value.multiline) this.writeString('m'); this.writeNull(); } else if ('string' === typeof value) { //size + content + null this.writeType(utils_js_1.BSONType.STRING); const start = this.offset; this.offset += 4; //size placeholder this.writeString(value); this.writeByte(0); //null this.writeDelayedSize(this.offset - start - 4, start); } else if ('number' === typeof value) { if (Math.floor(value) === value && value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) { //32bit int this.writeType(utils_js_1.BSONType.INT); this.writeInt32(value); } else { //double this.writeType(utils_js_1.BSONType.NUMBER); this.writeDouble(value); } } else if (value instanceof Date) { this.writeType(utils_js_1.BSONType.DATE); this.writeLong(value.valueOf()); } else if ('bigint' === typeof value) { // This is only called for bigint in any structures. this.writeType(utils_js_1.BSONType.LONG); this.writeBigIntLong(value); } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) { this.writeType(utils_js_1.BSONType.BINARY); this.writeArrayBuffer(value); } else if ((0, core_1.isArray)(value)) { this.writeType(utils_js_1.BSONType.ARRAY); const start = this.offset; this.offset += 4; //size for (let i = 0; i < value.length; i++) { this.prepareWriteType(); this.writeAsciiString('' + i); this.writeByte(0); this.write(value[i]); } this.writeNull(); this.writeDelayedSize(this.offset - start, start); } else if (value === undefined) { this.writeType(utils_js_1.BSONType.UNDEFINED); } else if (value === null) { this.writeType(utils_js_1.BSONType.NULL); } else if ((0, core_1.isObject)(value)) { this.writeType(utils_js_1.BSONType.OBJECT); const start = this.offset; this.offset += 4; //size for (let i in value) { if (!(0, core_1.hasProperty)(value, i)) continue; this.prepareWriteType(); this.writeString(i); this.writeByte(0); this.write(value[i]); } this.writeNull(); this.writeDelayedSize(this.offset - start, start); } else { //the sizer incldues the type and name, so we have to write that this.writeType(utils_js_1.BSONType.UNDEFINED); } } writeArrayBuffer(value) { let view = value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value.buffer, value.byteOffset, value.byteLength); if (value['_bsontype'] === 'Binary') { view = value.buffer; } this.writeUint32(value.byteLength); this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT); for (let i = 0; i < value.byteLength; i++) { this.buffer[this.offset++] = view[i]; } } } exports.Writer = Writer; Writer.__type = ['typeOffset', function () { return 0; }, 'buffer', 'offset', () => 0, 'constructor', 'reset', 'v', 'writeType', 'resetWriteType', 'prepareWriteType', 'writeUint32', 'writeInt32', 'writeDouble', 'position', 'writeDelayedSize', 'writeByte', () => 0, 'writeBuffer', 'writeNull', 'str', 'writeAsciiString', 'writeString', 'value', 'getBigIntBSONType', 'writeBigIntLong', 'writeBigIntBinary', 'writeSignedBigIntBinary', 'writeLong', 'writeUUID', 'writeObjectId', 'write', () => __ΩArrayBufferView, 'writeArrayBuffer', 'Writer', '\'3!>"PW2#:\'2$:>%"0&P"0\'P\'2("0)P"0*P"0+P\'2("0,P\'2("0-P\'2("0.P\'2(\'2/"00P\'2("01PW2#\'2$>2"03P"04PP&\'J25"06P&25"07P*28\'09P*28"0:P*28"0;P*28"0<P\'28"0=P&28"0>P&28"0?P"28$0@PP_nAJ28"0B5wC']; function getNameWriterCode(name) { const nameSetter = []; //todo: support utf8 names for (let i = 0; i < name.length; i++) { nameSetter.push(`state.writer.buffer[state.writer.offset++] = ${name.charCodeAt(i)};`); } return ` //write name: '${name}' ${nameSetter.join('\n')} state.writer.writeByte(0); //null `; } getNameWriterCode.__type = ['name', 'getNameWriterCode', 'P&2!&/"']; function sizerObjectLiteral(type, state, options) { handleObjectLiteral(type, state, 'sizer', options); } sizerObjectLiteral.__type = [() => __ΩTypeClass, () => __ΩTypeObjectLiteral, 'type', () => type_1.TemplateState, 'state', () => __ΩBSONSerializerOptions, 'options', 'sizerObjectLiteral', 'PPn!n"J2#P7$2%n&2\'"/(']; function serializeObjectLiteral(type, state, options) { handleObjectLiteral(type, state, 'serialization', options); } serializeObjectLiteral.__type = [() => __ΩTypeClass, () => __ΩTypeObjectLiteral, 'type', () => type_1.TemplateState, 'state', () => __ΩBSONSerializerOptions, 'options', 'serializeObjectLiteral', 'PPn!n"J2#P7$2%n&2\'"/(']; function handleObjectLiteral(type, state, target, options) { let before = 'state.size += 4; //object size'; let after = 'state.size += 1; //null'; if (target === 'serialization') { const start = state.compilerContext.reserveName('start'); before = ` var ${start} = state.writer.offset; state.writer.offset += 4; //size`; after = ` state.writer.writeNull(); state.writer.writeDelayedSize(state.writer.offset - ${start}, ${start});`; } //emdedded for the moment disabled. treat it as normal property. // const embedded = embeddedAnnotation.getFirst(type); // if (embedded) { // if (type.kind !== ReflectionKind.class) throw new SerializationError(`Object literals can not be embedded`, collapsePath(state.path)); // const constructorProperties = getConstructorProperties(type); // if (!constructorProperties.properties.length) throw new BSONError(`Can not embed class ${getClassName(type.classType)} since it has no constructor properties`); // // if (constructorProperties.properties.length === 1) { // const first = constructorProperties.properties[0]; // let name = getNameExpression(state.namingStrategy.getPropertyName(first), state); // const setter = getEmbeddedAccessor(type, false, '', state.namingStrategy, first, embedded); // state.addCode(executeTemplates(state.fork('', new ContainerAccessor(state.accessor, name)).forPropertyName(setter || state.propertyName), first.type)); // } else { // const lines: string[] = []; // const containerProperty = getEmbeddedProperty(type); // // for (const property of constructorProperties.properties) { // const setter = getEmbeddedAccessor(type, true, '', state.namingStrategy, property, embedded); // lines.push(executeTemplates(state.fork('', new ContainerAccessor(state.accessor, JSON.stringify(property.name))).forPropertyName(setter), property.type)); // } // // if (containerProperty) { // state.addCode(` // ${lines.join('\n')} // `); // } else { // if (target === 'serialization') { // serializePropertyNameAware(type, state, BSONType.OBJECT, `'object' === typeof ${state.accessor}`, ` // //embedded class with multiple properties // ${before} // ${lines.join('\n')} // ${after} // `); // } else { // sizerPropertyNameAware(type, state, `'object' === typeof ${state.accessor}`, ` // //embedded class with multiple properties // ${before} // ${lines.join('\n')} // ${after} // `); // } // } // } // return; // } //following line resets propertyName, so we have to store it before const propertyName = state.propertyName; if (target === 'serialization') { serializePropertyNameAware(type, state, utils_js_1.BSONType.OBJECT, `'object' === typeof ${state.accessor}`, ''); } else { sizerPropertyNameAware(type, state, `'object' === typeof ${state.accessor}`, ''); } const lines = []; const signatures = []; const existing = []; state.setContext({ unpopulatedSymbol: type_1.unpopulatedSymbol }); for (const member of (0, type_1.resolveTypeMembers)(type)) { if (member.kind === type_1.ReflectionKind.indexSignature) { if (type_1.excludedAnnotation.isExcluded(member.type, state.registry.serializer.name)) continue; signatures.push(member); } if (!(0, type_1.isPropertyMemberType)(member)) continue; if (!(0, utils_js_1.isSerializable)(member.type)) continue; const propertyName = String(state.namingStrategy.getPropertyName(member, state.registry.serializer.name)); const readName = (0, type_1.getNameExpression)((0, type_1.memberNameToString)(member.name), state); existing.push(readName); //back references are only serialized when it's not forMongoDatabase if ((0, type_1.isBackReferenceType)(member.type) && options.forMongoDatabase === true) continue; if (type_1.excludedAnnotation.isExcluded(member.type, state.registry.serializer.name)) continue; const accessor = `${state.accessor}[${readName}]`; const propertyState = state.fork('', accessor).extendPath(propertyName); const setUndefined = (0, type_1.isOptional)(member) ? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.undefined }) : (0, type_1.isNullable)(member) ? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.null }) : ''; const template = (0, type_1.executeTemplates)(propertyState.fork(), member.type); if (!template) { // console.error('missing template for member', member.name, 'of', member.type); // throw new BSONError(`No template found for ${String(member.name)}: ${ReflectionKind[member.type.kind]}`); } let nameWriter = ``; if (target === 'serialization') { nameWriter = ` state.writer.prepareWriteType(); ${propertyNameWrite(propertyName)} `; } else if (target === 'sizer') { nameWriter = ` //type + name + null state.size += 1 + ${stringByteLength(propertyName)} + 1; `; } const optional = (0, type_1.isOptional)(member) || (0, type_1.hasDefaultValue)(member); let converter = ` ${nameWriter} if (${accessor} === unpopulatedSymbol) { //don't do anything since not loaded } else if (${optional} && (${accessor} === undefined || ${accessor} === null)) { ${setUndefined} } else { ${template} } `; if (optional) { lines.push(` if (${readName} in ${state.accessor}) { ${converter} } `); } else { lines.push(converter); } } if (signatures.length) { const i = state.compilerContext.reserveName('i'); const existingCheck = existing.map(__assignType(v => `${i} === ${v}`, ['v', '', 'P"2!"/"'])).join(' || ') || 'false'; const signatureLines = []; state.setContext({ stringByteLength }); (0, type_1.sortSignatures)(signatures); for (const signature of signatures) { const accessor = new type_1.ContainerAccessor(state.accessor, i); const propertyState = state.fork(undefined, accessor).extendPath(new type_1.RuntimeCode(i)); const setUndefined = (0, type_1.isOptional)(signature.type) ? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.undefined }) : (0, type_1.isNullable)(signature.type) ? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.null }) : ''; let nameWriter = ``; if (target === 'serialization') { nameWriter = ` state.writer.prepareWriteType(); state.writer.writeString(${i}); state.writer.writeByte(0); `; } else if (target === 'sizer') { nameWriter = ` //type + name + null state.size += 1 + stringByteLength(${i}) + 1; `; } signatureLines.push(`else if (${(0, type_1.getIndexCheck)(state.compilerContext, i, signature.index)}) { ${nameWriter} if (${accessor} === undefined) { ${setUndefined} } else { ${(0, type_1.executeTemplates)(propertyState, signature.type)} } }`); } state.setContext({ hasProperty: core_1.hasProperty }); //the index signature type could be: string, number, symbol. //or a literal when it was constructed by a mapped type. lines.push(` for (const ${i} in ${state.accessor}) { if (!hasProperty(${state.accessor}, ${i})) continue; if (${existingCheck}) continue; if (false) {} ${signatureLines.join(' ')} } `); } state.addCode(` //handle objectLiteral via propertyName="${(0, type_1.getPropertyNameString)(propertyName)}" ${before} ${lines.join('\n')} ${after} `); if (type.kind === type_1.ReflectionKind.class && type_1.referenceAnnotation.hasAnnotations(type)) { state.setContext({ isObject: core_1.isObject, isReferenceInstance: type_1.isReferenceInstance, isReferenceHydrated: type_1.isReferenceHydrated }); const reflection = type_1.ReflectionClass.from(type.classType); //the primary key is serialised for unhydrated references const index = (0, type_1.getNameExpression)(reflection.getPrimary().getName(), state); const primaryKey = reflection.getPrimary().getType(); //if a reference or forMongoDatabase=true only the foreign primary key is serialized state.replaceTemplate(` if ((${options.forMongoDatabase === true}) || (isReferenceInstance(${state.accessor}) && !isReferenceHydrated(${state.accessor}))) { ${(0, type_1.executeTemplates)(state.fork(state.setter, `${state.accessor}[${index}]`).forPropertyName(propertyName), primaryKey)} } else { ${state.template} } `); } } handleObjectLiteral.__type = [() => __ΩTypeClass, () => __ΩTypeObjectLiteral, 'type', () => type_1.TemplateState, 'state', "serialization", "sizer", 'target', () => __ΩBSONSerializerOptions, 'options', 'handleObjectLiteral', 'PPn!n"J2#P7$2%P.&.\'J2(n)2*"/+']; function propertyNameWrite(propertyName) { if (propertyName) { if (propertyName instanceof type_1.RuntimeCode) { return ` state.writer.writeString(${propertyName.code}); state.writer.writeByte(0); `; } else { return getNameWriterCode(propertyName); } } return ''; } propertyNameWrite.__type = [() => type_1.RuntimeCode, 'propertyName', 'propertyNameWrite', 'PP&P7!J2"8"/#']; function serializePropertyNameAware(type, state, bsonType, typeChecker, code) { state.template = ` //serializer for ${type.kind}, via propertyName="${(0, type_1.getPropertyNameString)(state.propertyName)}" ${typeChecker ? `if (!(${typeChecker})) ${state.throwCode(type)}` : ''} state.writer.writeType(${bsonType}); //BSON type = ${utils_js_1.BSONType[bsonType]} ${code} `; } serializePropertyNameAware.__type = ['Type', 'type', () => type_1.TemplateState, 'state', () => __ΩBSONType, 'bsonType', 'typeChecker', 'code', 'serializePropertyNameAware', 'P"w!2"P7#2$n%2&&2\'&2($/)']; class DigitByteRuntimeCode extends type_1.RuntimeCode { constructor(code) { super(code); this.code = code; } } exports.DigitByteRuntimeCode = DigitByteRuntimeCode; DigitByteRuntimeCode.__type = [() => type_1.RuntimeCode, 'code', 'constructor', 'DigitByteRuntimeCode', 'P7!P&2":"0#5w$']; function sizerPropertyNameAware(type, state, typeChecker, code) { const checker = typeChecker ? `if (!(${typeChecker})) ${state.throwCode(type)}` : ''; state.template = ` ${checker} ${state.template} ${code} `; } sizerPropertyNameAware.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'typeChecker', 'code', 'sizerPropertyNameAware', 'P"w!2"P7#2$&2%&2&$/\'']; function sizerAny(type, state) { state.setContext({ getValueSize }); sizerPropertyNameAware(type, state, ``, `state.size += getValueSize(${state.accessor});`); } sizerAny.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerAny', 'P"w!2"P7#2$"/%']; function serializeAny(type, state) { state.addCode(` state.writer.write(${state.accessor}); `); } serializeAny.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeAny', 'P"w!2"P7#2$"/%']; function sizerBoolean(type, state) { sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'boolean'`, ` state.size += 1; `); } sizerBoolean.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerBoolean', 'P"w!2"P7#2$"/%']; function sizerNumber(type, state) { state.setContext({ getValueSize }); //per default bigint will be serialized as long, to be compatible with default mongo driver and mongo database. //We should add a new annotation, maybe like `bigint & Binary` to make it binary (unlimited size) sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'bigint' || (typeof ${state.accessor} === 'number' && !Number.isNaN(${state.accessor}))`, ` state.size += getValueSize(${state.accessor}); `); } sizerNumber.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerNumber', 'P"w!2"P7#2$"/%']; function serializeBoolean(type, state) { serializePropertyNameAware(type, state, utils_js_1.BSONType.BOOLEAN, `typeof ${state.accessor} === 'boolean'`, ` state.writer.writeByte(${state.accessor} ? 1 : 0); `); } serializeBoolean.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeBoolean', 'P"w!2"P7#2$"/%']; function serializeString(type, state) { if (type_1.uuidAnnotation.getFirst(type)) { serializePropertyNameAware(type, state, utils_js_1.BSONType.BINARY, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 36`, `state.writer.writeUUID(${state.accessor});`); return; } if (type_1.mongoIdAnnotation.getFirst(type)) { serializePropertyNameAware(type, state, utils_js_1.BSONType.OID, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 24`, `state.writer.writeObjectId(${state.accessor});`); return; } const start = state.compilerContext.reserveName('start'); serializePropertyNameAware(type, state, utils_js_1.BSONType.STRING, `typeof ${state.accessor} === 'string'`, ` var ${start} = state.writer.offset; state.writer.offset += 4; //size placeholder state.writer.writeString(${state.accessor}); state.writer.writeByte(0); //null state.writer.writeDelayedSize(state.writer.offset - ${start} - 4, ${start}); `); } serializeString.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeString', 'P"w!2"P7#2$"/%']; function sizeString(type, state) { if (type_1.uuidAnnotation.getFirst(type)) { sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 36`, ` state.size += 4 + 1 + 16; `); return; } if (type_1.mongoIdAnnotation.getFirst(type)) { sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 24`, ` state.size += 12; `); return; } state.setContext({ getValueSize }); sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'string'`, ` state.size += getValueSize(${state.accessor}); `); } sizeString.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizeString', 'P"w!2"P7#2$"/%']; function serializeNumber(type, state) { state.addCode(` if ('bigint' === typeof ${state.accessor}) { //long state.writer.writeType(${utils_js_1.BSONType.LONG}); state.writer.writeBigIntLong(${state.accessor}); } else if ('number' === typeof ${state.accessor} && !Number.isNaN(${state.accessor})) { if (Math.floor(${state.accessor}) === ${state.accessor} && ${state.accessor} >= ${BSON_INT32_MIN} && ${state.accessor} <= ${BSON_INT32_MAX}) { //32bit int state.writer.writeType(${utils_js_1.BSONType.INT}); state.writer.writeInt32(${state.accessor}); } else { //double, 64bit state.writer.writeType(${utils_js_1.BSONType.NUMBER}); state.writer.writeDouble(${state.accessor}); } } `); } serializeNumber.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeNumber', 'P"w!2"P7#2$"/%']; function sizerBigInt(type, state) { const binaryBigInt = type_1.binaryBigIntAnnotation.getFirst(type); if (binaryBigInt !== undefined) { state.setContext({ getBinaryBigIntSize, getSignedBinaryBigIntSize }); const bigIntSize = binaryBigInt === 0 /* BinaryBigIntType.unsigned */ ? 'getBinaryBigIntSize' : 'getSignedBinaryBigIntSize'; //per default bigint will be serialized as long, to be compatible with default mongo driver and mongo database. //We should add a new annotation, maybe like `bigint & Binary` to make it binary (unlimited size) sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'bigint' || (typeof ${state.accessor} === 'number' && !Number.isNaN(${state.accessor}))`, ` state.size += ${bigIntSize}(${state.accessor}); `); } else { sizerNumber(type, state); } } sizerBigInt.__type = [() => __ΩTypeBigInt, 'type', () => type_1.TemplateState, 'state', 'sizerBigInt', 'Pn!2"P7#2$"/%']; function serializeBigInt(type, state) { const binaryBigInt = type_1.binaryBigIntAnnotation.getFirst(type); if (binaryBigInt !== undefined) { const writeBigInt = binaryBigInt === 0 /* BinaryBigIntType.unsigned */ ? 'writeBigIntBinary' : 'writeSignedBigIntBinary'; state.addCode(` if ('bigint' === typeof ${state.accessor} || ('number' === typeof ${state.accessor} && !Number.isNaN(${state.accessor}))) { //long state.writer.writeType(${utils_js_1.BSONType.BINARY}); state.writer.${writeBigInt}(${state.accessor}); }`); } else { serializeNumber(type, state); } } serializeBigInt.__type = [() => __ΩTypeBigInt, 'type', () => type_1.TemplateState, 'state', 'serializeBigInt', 'Pn!2"P7#2$"/%']; function sizerRegExp(type, state) { state.setContext({ stringByteLength }); sizerPropertyNameAware(type, state, `${state.accessor} instanceof RegExp`, ` state.size += stringByteLength(${state.accessor}.source) + 1 + (${state.accessor}.global ? 1 : 0) + (${state.accessor}.ignoreCase ? 1 : 0) + (${state.accessor}.multiline ? 1 : 0) + 1; `); } sizerRegExp.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerRegExp', 'P"w!2"P7#2$"/%']; function serializeRegExp(type, state) { serializePropertyNameAware(type, state, utils_js_1.BSONType.REGEXP, `${state.accessor} instanceof RegExp`, ` state.writer.writeString(${state.accessor}.source); state.writer.writeNull(); if (${state.accessor}.ignoreCase) state.writer.writeString('i'); if (${state.accessor}.global) state.writer.writeString('s'); //BSON does not use the RegExp flag format if (${state.accessor}.multiline) state.writer.writeString('m'); state.writer.writeNull(); `); } serializeRegExp.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeRegExp', 'P"w!2"P7#2$"/%']; function sizerLiteral(type, state) { if ('string' === typeof type.literal) { sizeString(type, state); } else if ('number' === typeof type.literal || 'bigint' === typeof type.literal) { sizerNumber(type, state); } else if ('boolean' === typeof type.literal) { sizerBoolean(type, state); } else if (type.literal instanceof RegExp) { sizerRegExp(type, state); } } sizerLiteral.__type = [() => __ΩTypeLiteral, 'type', () => type_1.TemplateState, 'state', 'sizerLiteral', 'Pn!2"P7#2$"/%']; function serializeLiteral(type, state) { if ('string' === typeof type.literal) { serializeString(type, state); } else if ('number' === typeof type.literal || 'bigint' === typeof type.literal) { serializeNumber(type, state); } else if ('boolean' === typeof type.literal) { serializeBoolean(type, state); } else if (type.literal instanceof RegExp) { serializeRegExp(type, state); } } serializeLiteral.__type = [() => __ΩTypeLiteral, 'type', () => type_1.TemplateState, 'state', 'serializeLiteral', 'Pn!2"P7#2$"/%']; function sizerBinary(type, state) { state.setContext({ ArrayBuffer }); sizerPropertyNameAware(type, state, `${state.accessor} instanceof ArrayBuffer || ArrayBuffer.isView(${state.accessor})`, ` state.size += 4 + 1 + ${state.accessor}.byteLength; `); } sizerBinary.__type = [() => __ΩTypeClass, 'type', () => type_1.TemplateState, 'state', 'sizerBinary', 'Pn!2"P7#2$"/%']; function serializeBinary(type, state) { state.setContext({ ArrayBuffer }); serializePropertyNameAware(type, state, utils_js_1.BSONType.BINARY, `${state.accessor} instanceof ArrayBuffer || ArrayBuffer.isView(${state.accessor})`, ` state.writer.writeArrayBuffer(${state.accessor}); `); } serializeBinary.__type = [() => __ΩTypeClass, 'type', () => type_1.TemplateState, 'state', 'serializeBinary', 'Pn!2"P7#2$"/%']; function sizerArray(type, state) { const elementType = type.type; state.setContext({ isIterable: core_1.isIterable }); const i = state.compilerContext.reserveName('arrayItemIndex'); const item = state.compilerContext.reserveName('item'); state.setContext({ digitByteSize: utils_js_1.digitByteSize }); const memberState = state.fork('', item).extendPath(