EjsonDecoder.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.EjsonDecoder = void 0;
  4. const values_1 = require("../bson/values");
  5. const JsonDecoder_1 = require("../json/JsonDecoder");
  6. const JsonDecoder_2 = require("../json/JsonDecoder");
  7. class EjsonDecoder extends JsonDecoder_1.JsonDecoder {
  8. constructor(options = {}) {
  9. super();
  10. this.options = options;
  11. }
  12. decodeFromString(json) {
  13. const bytes = new TextEncoder().encode(json);
  14. return this.decode(bytes);
  15. }
  16. readAny() {
  17. this.skipWhitespace();
  18. const reader = this.reader;
  19. const uint8 = reader.uint8;
  20. const char = uint8[reader.x];
  21. switch (char) {
  22. case 34:
  23. return this.readStr();
  24. case 91:
  25. return this.readArr();
  26. case 102:
  27. return this.readFalse();
  28. case 110:
  29. return this.readNull();
  30. case 116:
  31. return this.readTrue();
  32. case 123:
  33. return this.readObjWithEjsonSupport();
  34. default:
  35. if ((char >= 48 && char <= 57) || char === 45)
  36. return this.readNum();
  37. throw new Error('Invalid JSON');
  38. }
  39. }
  40. readArr() {
  41. const reader = this.reader;
  42. if (reader.u8() !== 0x5b)
  43. throw new Error('Invalid JSON');
  44. const arr = [];
  45. const uint8 = reader.uint8;
  46. let first = true;
  47. while (true) {
  48. this.skipWhitespace();
  49. const char = uint8[reader.x];
  50. if (char === 0x5d)
  51. return reader.x++, arr;
  52. if (char === 0x2c)
  53. reader.x++;
  54. else if (!first)
  55. throw new Error('Invalid JSON');
  56. this.skipWhitespace();
  57. arr.push(this.readAny());
  58. first = false;
  59. }
  60. }
  61. readObjWithEjsonSupport() {
  62. const reader = this.reader;
  63. if (reader.u8() !== 0x7b)
  64. throw new Error('Invalid JSON');
  65. const obj = {};
  66. const uint8 = reader.uint8;
  67. let first = true;
  68. while (true) {
  69. this.skipWhitespace();
  70. let char = uint8[reader.x];
  71. if (char === 0x7d) {
  72. reader.x++;
  73. return this.transformEjsonObject(obj);
  74. }
  75. if (char === 0x2c)
  76. reader.x++;
  77. else if (!first)
  78. throw new Error('Invalid JSON');
  79. this.skipWhitespace();
  80. char = uint8[reader.x++];
  81. if (char !== 0x22)
  82. throw new Error('Invalid JSON');
  83. const key = (0, JsonDecoder_2.readKey)(reader);
  84. if (key === '__proto__')
  85. throw new Error('Invalid JSON');
  86. this.skipWhitespace();
  87. if (reader.u8() !== 0x3a)
  88. throw new Error('Invalid JSON');
  89. this.skipWhitespace();
  90. obj[key] = this.readValue();
  91. first = false;
  92. }
  93. }
  94. readValue() {
  95. this.skipWhitespace();
  96. const reader = this.reader;
  97. const uint8 = reader.uint8;
  98. const char = uint8[reader.x];
  99. switch (char) {
  100. case 34:
  101. return this.readStr();
  102. case 91:
  103. return this.readArr();
  104. case 102:
  105. return this.readFalse();
  106. case 110:
  107. return this.readNull();
  108. case 116:
  109. return this.readTrue();
  110. case 123:
  111. return this.readRawObj();
  112. default:
  113. if ((char >= 48 && char <= 57) || char === 45)
  114. return this.readNum();
  115. throw new Error('Invalid JSON');
  116. }
  117. }
  118. readRawObj() {
  119. const reader = this.reader;
  120. if (reader.u8() !== 0x7b)
  121. throw new Error('Invalid JSON');
  122. const obj = {};
  123. const uint8 = reader.uint8;
  124. let first = true;
  125. while (true) {
  126. this.skipWhitespace();
  127. let char = uint8[reader.x];
  128. if (char === 0x7d) {
  129. reader.x++;
  130. return obj;
  131. }
  132. if (char === 0x2c)
  133. reader.x++;
  134. else if (!first)
  135. throw new Error('Invalid JSON');
  136. this.skipWhitespace();
  137. char = uint8[reader.x++];
  138. if (char !== 0x22)
  139. throw new Error('Invalid JSON');
  140. const key = (0, JsonDecoder_2.readKey)(reader);
  141. if (key === '__proto__')
  142. throw new Error('Invalid JSON');
  143. this.skipWhitespace();
  144. if (reader.u8() !== 0x3a)
  145. throw new Error('Invalid JSON');
  146. this.skipWhitespace();
  147. obj[key] = this.readValue();
  148. first = false;
  149. }
  150. }
  151. transformEjsonObject(obj) {
  152. const keys = Object.keys(obj);
  153. const hasExactKeys = (expectedKeys) => {
  154. if (keys.length !== expectedKeys.length)
  155. return false;
  156. return expectedKeys.every((key) => keys.includes(key));
  157. };
  158. const specialKeys = keys.filter((key) => key.startsWith('$'));
  159. if (specialKeys.length > 0) {
  160. if (specialKeys.includes('$oid')) {
  161. if (!hasExactKeys(['$oid'])) {
  162. throw new Error('Invalid ObjectId format: extra keys not allowed');
  163. }
  164. const oidStr = obj.$oid;
  165. if (typeof oidStr === 'string' && /^[0-9a-fA-F]{24}$/.test(oidStr)) {
  166. return this.parseObjectId(oidStr);
  167. }
  168. throw new Error('Invalid ObjectId format');
  169. }
  170. if (specialKeys.includes('$numberInt')) {
  171. if (!hasExactKeys(['$numberInt'])) {
  172. throw new Error('Invalid Int32 format: extra keys not allowed');
  173. }
  174. const intStr = obj.$numberInt;
  175. if (typeof intStr === 'string') {
  176. const value = parseInt(intStr, 10);
  177. if (!isNaN(value) && value >= -2147483648 && value <= 2147483647) {
  178. return new values_1.BsonInt32(value);
  179. }
  180. }
  181. throw new Error('Invalid Int32 format');
  182. }
  183. if (specialKeys.includes('$numberLong')) {
  184. if (!hasExactKeys(['$numberLong'])) {
  185. throw new Error('Invalid Int64 format: extra keys not allowed');
  186. }
  187. const longStr = obj.$numberLong;
  188. if (typeof longStr === 'string') {
  189. const value = parseFloat(longStr);
  190. if (!isNaN(value)) {
  191. return new values_1.BsonInt64(value);
  192. }
  193. }
  194. throw new Error('Invalid Int64 format');
  195. }
  196. if (specialKeys.includes('$numberDouble')) {
  197. if (!hasExactKeys(['$numberDouble'])) {
  198. throw new Error('Invalid Double format: extra keys not allowed');
  199. }
  200. const doubleStr = obj.$numberDouble;
  201. if (typeof doubleStr === 'string') {
  202. if (doubleStr === 'Infinity')
  203. return new values_1.BsonFloat(Infinity);
  204. if (doubleStr === '-Infinity')
  205. return new values_1.BsonFloat(-Infinity);
  206. if (doubleStr === 'NaN')
  207. return new values_1.BsonFloat(NaN);
  208. const value = parseFloat(doubleStr);
  209. if (!isNaN(value)) {
  210. return new values_1.BsonFloat(value);
  211. }
  212. }
  213. throw new Error('Invalid Double format');
  214. }
  215. if (specialKeys.includes('$numberDecimal')) {
  216. if (!hasExactKeys(['$numberDecimal'])) {
  217. throw new Error('Invalid Decimal128 format: extra keys not allowed');
  218. }
  219. const decimalStr = obj.$numberDecimal;
  220. if (typeof decimalStr === 'string') {
  221. return new values_1.BsonDecimal128(new Uint8Array(16));
  222. }
  223. throw new Error('Invalid Decimal128 format');
  224. }
  225. if (specialKeys.includes('$binary')) {
  226. if (!hasExactKeys(['$binary'])) {
  227. throw new Error('Invalid Binary format: extra keys not allowed');
  228. }
  229. const binaryObj = obj.$binary;
  230. if (typeof binaryObj === 'object' && binaryObj !== null) {
  231. const binaryKeys = Object.keys(binaryObj);
  232. if (binaryKeys.length === 2 && binaryKeys.includes('base64') && binaryKeys.includes('subType')) {
  233. const base64 = binaryObj.base64;
  234. const subType = binaryObj.subType;
  235. if (typeof base64 === 'string' && typeof subType === 'string') {
  236. const data = this.base64ToUint8Array(base64);
  237. const subtype = parseInt(subType, 16);
  238. return new values_1.BsonBinary(subtype, data);
  239. }
  240. }
  241. }
  242. throw new Error('Invalid Binary format');
  243. }
  244. if (specialKeys.includes('$uuid')) {
  245. if (!hasExactKeys(['$uuid'])) {
  246. throw new Error('Invalid UUID format: extra keys not allowed');
  247. }
  248. const uuidStr = obj.$uuid;
  249. if (typeof uuidStr === 'string' && this.isValidUuid(uuidStr)) {
  250. const data = this.uuidToBytes(uuidStr);
  251. return new values_1.BsonBinary(4, data);
  252. }
  253. throw new Error('Invalid UUID format');
  254. }
  255. if (specialKeys.includes('$code') && !specialKeys.includes('$scope')) {
  256. if (!hasExactKeys(['$code'])) {
  257. throw new Error('Invalid Code format: extra keys not allowed');
  258. }
  259. const code = obj.$code;
  260. if (typeof code === 'string') {
  261. return new values_1.BsonJavascriptCode(code);
  262. }
  263. throw new Error('Invalid Code format');
  264. }
  265. if (specialKeys.includes('$code') && specialKeys.includes('$scope')) {
  266. if (!hasExactKeys(['$code', '$scope'])) {
  267. throw new Error('Invalid CodeWScope format: extra keys not allowed');
  268. }
  269. const code = obj.$code;
  270. const scope = obj.$scope;
  271. if (typeof code === 'string' && typeof scope === 'object' && scope !== null) {
  272. return new values_1.BsonJavascriptCodeWithScope(code, this.transformEjsonObject(scope));
  273. }
  274. throw new Error('Invalid CodeWScope format');
  275. }
  276. if (specialKeys.includes('$symbol')) {
  277. if (!hasExactKeys(['$symbol'])) {
  278. throw new Error('Invalid Symbol format: extra keys not allowed');
  279. }
  280. const symbol = obj.$symbol;
  281. if (typeof symbol === 'string') {
  282. return new values_1.BsonSymbol(symbol);
  283. }
  284. throw new Error('Invalid Symbol format');
  285. }
  286. if (specialKeys.includes('$timestamp')) {
  287. if (!hasExactKeys(['$timestamp'])) {
  288. throw new Error('Invalid Timestamp format: extra keys not allowed');
  289. }
  290. const timestampObj = obj.$timestamp;
  291. if (typeof timestampObj === 'object' && timestampObj !== null) {
  292. const timestampKeys = Object.keys(timestampObj);
  293. if (timestampKeys.length === 2 && timestampKeys.includes('t') && timestampKeys.includes('i')) {
  294. const t = timestampObj.t;
  295. const i = timestampObj.i;
  296. if (typeof t === 'number' && typeof i === 'number' && t >= 0 && i >= 0) {
  297. return new values_1.BsonTimestamp(i, t);
  298. }
  299. }
  300. }
  301. throw new Error('Invalid Timestamp format');
  302. }
  303. if (specialKeys.includes('$regularExpression')) {
  304. if (!hasExactKeys(['$regularExpression'])) {
  305. throw new Error('Invalid RegularExpression format: extra keys not allowed');
  306. }
  307. const regexObj = obj.$regularExpression;
  308. if (typeof regexObj === 'object' && regexObj !== null) {
  309. const regexKeys = Object.keys(regexObj);
  310. if (regexKeys.length === 2 && regexKeys.includes('pattern') && regexKeys.includes('options')) {
  311. const pattern = regexObj.pattern;
  312. const options = regexObj.options;
  313. if (typeof pattern === 'string' && typeof options === 'string') {
  314. return new RegExp(pattern, options);
  315. }
  316. }
  317. }
  318. throw new Error('Invalid RegularExpression format');
  319. }
  320. if (specialKeys.includes('$dbPointer')) {
  321. if (!hasExactKeys(['$dbPointer'])) {
  322. throw new Error('Invalid DBPointer format: extra keys not allowed');
  323. }
  324. const dbPointerObj = obj.$dbPointer;
  325. if (typeof dbPointerObj === 'object' && dbPointerObj !== null) {
  326. const dbPointerKeys = Object.keys(dbPointerObj);
  327. if (dbPointerKeys.length === 2 && dbPointerKeys.includes('$ref') && dbPointerKeys.includes('$id')) {
  328. const ref = dbPointerObj.$ref;
  329. const id = dbPointerObj.$id;
  330. if (typeof ref === 'string' && id !== undefined) {
  331. const transformedId = this.transformEjsonObject(id);
  332. if (transformedId instanceof values_1.BsonObjectId) {
  333. return new values_1.BsonDbPointer(ref, transformedId);
  334. }
  335. }
  336. }
  337. }
  338. throw new Error('Invalid DBPointer format');
  339. }
  340. if (specialKeys.includes('$date')) {
  341. if (!hasExactKeys(['$date'])) {
  342. throw new Error('Invalid Date format: extra keys not allowed');
  343. }
  344. const dateValue = obj.$date;
  345. if (typeof dateValue === 'string') {
  346. const date = new Date(dateValue);
  347. if (!isNaN(date.getTime())) {
  348. return date;
  349. }
  350. }
  351. else if (typeof dateValue === 'object' && dateValue !== null) {
  352. const longObj = dateValue;
  353. const longKeys = Object.keys(longObj);
  354. if (longKeys.length === 1 && longKeys[0] === '$numberLong' && typeof longObj.$numberLong === 'string') {
  355. const timestamp = parseFloat(longObj.$numberLong);
  356. if (!isNaN(timestamp)) {
  357. return new Date(timestamp);
  358. }
  359. }
  360. }
  361. throw new Error('Invalid Date format');
  362. }
  363. if (specialKeys.includes('$minKey')) {
  364. if (!hasExactKeys(['$minKey'])) {
  365. throw new Error('Invalid MinKey format: extra keys not allowed');
  366. }
  367. if (obj.$minKey === 1) {
  368. return new values_1.BsonMinKey();
  369. }
  370. throw new Error('Invalid MinKey format');
  371. }
  372. if (specialKeys.includes('$maxKey')) {
  373. if (!hasExactKeys(['$maxKey'])) {
  374. throw new Error('Invalid MaxKey format: extra keys not allowed');
  375. }
  376. if (obj.$maxKey === 1) {
  377. return new values_1.BsonMaxKey();
  378. }
  379. throw new Error('Invalid MaxKey format');
  380. }
  381. if (specialKeys.includes('$undefined')) {
  382. if (!hasExactKeys(['$undefined'])) {
  383. throw new Error('Invalid Undefined format: extra keys not allowed');
  384. }
  385. if (obj.$undefined === true) {
  386. return undefined;
  387. }
  388. throw new Error('Invalid Undefined format');
  389. }
  390. }
  391. if (keys.includes('$ref') && keys.includes('$id')) {
  392. const ref = obj.$ref;
  393. const id = this.transformEjsonObject(obj.$id);
  394. const result = { $ref: ref, $id: id };
  395. if (keys.includes('$db')) {
  396. result.$db = obj.$db;
  397. }
  398. for (const key of keys) {
  399. if (key !== '$ref' && key !== '$id' && key !== '$db') {
  400. result[key] = this.transformEjsonObject(obj[key]);
  401. }
  402. }
  403. return result;
  404. }
  405. const result = {};
  406. for (const [key, val] of Object.entries(obj)) {
  407. if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
  408. result[key] = this.transformEjsonObject(val);
  409. }
  410. else if (Array.isArray(val)) {
  411. result[key] = val.map((item) => typeof item === 'object' && item !== null && !Array.isArray(item)
  412. ? this.transformEjsonObject(item)
  413. : item);
  414. }
  415. else {
  416. result[key] = val;
  417. }
  418. }
  419. return result;
  420. }
  421. parseObjectId(hex) {
  422. const timestamp = parseInt(hex.slice(0, 8), 16);
  423. const process = parseInt(hex.slice(8, 18), 16);
  424. const counter = parseInt(hex.slice(18, 24), 16);
  425. return new values_1.BsonObjectId(timestamp, process, counter);
  426. }
  427. base64ToUint8Array(base64) {
  428. const binary = atob(base64);
  429. const bytes = new Uint8Array(binary.length);
  430. for (let i = 0; i < binary.length; i++) {
  431. bytes[i] = binary.charCodeAt(i);
  432. }
  433. return bytes;
  434. }
  435. isValidUuid(uuid) {
  436. const uuidPattern = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
  437. return uuidPattern.test(uuid);
  438. }
  439. uuidToBytes(uuid) {
  440. const hex = uuid.replace(/-/g, '');
  441. const bytes = new Uint8Array(16);
  442. for (let i = 0; i < 16; i++) {
  443. bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
  444. }
  445. return bytes;
  446. }
  447. }
  448. exports.EjsonDecoder = EjsonDecoder;
  449. //# sourceMappingURL=EjsonDecoder.js.map