@atproto/lex-json 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,13 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, test } from 'vitest'
2
2
  import { LexValue, lexEquals, parseCid } from '@atproto/lex-data'
3
3
  import { JsonValue } from './json.js'
4
- import { jsonToLex, lexParse, lexStringify, lexToJson } from './lex-json.js'
4
+ import {
5
+ jsonToLex,
6
+ lexParse,
7
+ lexParseJsonBytes,
8
+ lexStringify,
9
+ lexToJson,
10
+ } from './lex-json.js'
5
11
 
6
12
  export const validVectors: Array<{
7
13
  name: string
@@ -330,6 +336,24 @@ export const acceptableVectors: Array<{
330
336
  $link: 'bafkreiccldh766hwcnuxnf2wh6jgzepf2nlu2lvcllt63eww5p6chi4ity',
331
337
  },
332
338
  },
339
+ {
340
+ note: 'blob with CBOR CID ref',
341
+ json: {
342
+ $type: 'blob',
343
+ ref: {
344
+ $link: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
345
+ },
346
+ mimeType: 'image/png',
347
+ size: 1,
348
+ },
349
+ },
350
+ {
351
+ note: 'object with empty $type',
352
+ json: {
353
+ $type: '',
354
+ foo: 'bar',
355
+ },
356
+ },
333
357
  ]
334
358
 
335
359
  export const invalidVectors: Array<{
@@ -364,58 +388,143 @@ export const invalidVectors: Array<{
364
388
 
365
389
  describe(lexParse, () => {
366
390
  describe('valid vectors', () => {
367
- for (const { name, json, lex } of validVectors) {
368
- describe(name, () => {
369
- it('parses lex from string', () => {
391
+ describe('strict mode', () => {
392
+ for (const { name, json, lex } of validVectors) {
393
+ test(name, () => {
370
394
  expect(
371
- lexEquals(lex, lexParse(JSON.stringify(json), { strict: false })),
395
+ lexEquals(lex, lexParse(JSON.stringify(json), { strict: true })),
372
396
  ).toBe(true)
397
+ })
398
+ }
399
+ })
400
+ describe('non-strict mode', () => {
401
+ for (const { name, json, lex } of validVectors) {
402
+ test(name, () => {
373
403
  expect(
374
- lexEquals(lex, lexParse(JSON.stringify(json), { strict: true })),
404
+ lexEquals(lex, lexParse(JSON.stringify(json), { strict: false })),
375
405
  ).toBe(true)
376
406
  })
377
- })
378
- }
407
+ }
408
+ })
379
409
  })
380
410
 
381
411
  describe('acceptable vectors', () => {
382
- for (const { note, json } of acceptableVectors) {
383
- describe(note, () => {
384
- it('parses lex from string in non-strict mode', () => {
412
+ describe('strict mode', () => {
413
+ for (const { note, json } of acceptableVectors) {
414
+ test(note, () => {
415
+ expect(() =>
416
+ lexParse(JSON.stringify(json), { strict: true }),
417
+ ).toThrow()
418
+ })
419
+ }
420
+ })
421
+ describe('non-strict mode', () => {
422
+ for (const { note, json } of acceptableVectors) {
423
+ test(note, () => {
385
424
  expect(() =>
386
425
  lexParse(JSON.stringify(json), { strict: false }),
387
426
  ).not.toThrow()
388
427
  })
428
+ }
429
+ })
430
+ })
389
431
 
390
- it('parses lex from string in strict mode', () => {
432
+ describe('invalid vectors', () => {
433
+ describe('strict mode', () => {
434
+ for (const { note, json } of invalidVectors) {
435
+ test(note, () => {
391
436
  expect(() =>
392
437
  lexParse(JSON.stringify(json), { strict: true }),
393
438
  ).toThrow()
394
439
  })
440
+ }
441
+ })
442
+ describe('non-strict mode', () => {
443
+ for (const { note, json } of invalidVectors) {
444
+ test(note, () => {
445
+ expect(() =>
446
+ lexParse(JSON.stringify(json), { strict: false }),
447
+ ).not.toThrow()
448
+ })
449
+ }
450
+ })
451
+ })
452
+ })
453
+
454
+ describe(lexParseJsonBytes, () => {
455
+ describe('valid vectors', () => {
456
+ describe('strict mode', () => {
457
+ describe('with pretty-printed JSON', () => {
458
+ for (const { name, json, lex } of validVectors) {
459
+ test(name, () => {
460
+ const jsonBytes = Buffer.from(JSON.stringify(json, undefined, 4))
461
+ expect(
462
+ lexEquals(lex, lexParseJsonBytes(jsonBytes, { strict: true })),
463
+ ).toBe(true)
464
+ })
465
+ }
395
466
  })
396
- }
467
+ describe('with compact JSON', () => {
468
+ for (const { name, json, lex } of validVectors) {
469
+ test(name, () => {
470
+ const jsonBytes = Buffer.from(JSON.stringify(json))
471
+ expect(
472
+ lexEquals(lex, lexParseJsonBytes(jsonBytes, { strict: true })),
473
+ ).toBe(true)
474
+ })
475
+ }
476
+ })
477
+ })
478
+
479
+ describe('non-strict mode', () => {
480
+ for (const { name, json, lex } of validVectors) {
481
+ test(name, () => {
482
+ const jsonBytes = Buffer.from(JSON.stringify(json))
483
+ expect(
484
+ lexEquals(lex, lexParseJsonBytes(jsonBytes, { strict: false })),
485
+ ).toBe(true)
486
+ })
487
+ }
488
+ })
489
+ })
490
+
491
+ describe('acceptable vectors', () => {
492
+ describe('strict mode', () => {
493
+ for (const { note, json } of acceptableVectors) {
494
+ test(note, () => {
495
+ const jsonBytes = Buffer.from(JSON.stringify(json))
496
+ expect(() => lexParseJsonBytes(jsonBytes, { strict: true })).toThrow()
497
+ })
498
+ }
499
+ })
500
+ describe('non-strict mode', () => {
501
+ for (const { note, json } of acceptableVectors) {
502
+ test(note, () => {
503
+ const jsonBytes = Buffer.from(JSON.stringify(json))
504
+ expect(() =>
505
+ lexParseJsonBytes(jsonBytes, { strict: false }),
506
+ ).not.toThrow()
507
+ })
508
+ }
509
+ })
397
510
  })
398
511
 
399
512
  describe('invalid vectors', () => {
400
513
  describe('strict mode', () => {
401
514
  for (const { note, json } of invalidVectors) {
402
- describe(note, () => {
403
- it('throws when parsing malformed JSON', () => {
404
- expect(() =>
405
- lexParse(JSON.stringify(json), { strict: true }),
406
- ).toThrow()
407
- })
515
+ test(note, () => {
516
+ const jsonBytes = Buffer.from(JSON.stringify(json))
517
+ expect(() => lexParseJsonBytes(jsonBytes, { strict: true })).toThrow()
408
518
  })
409
519
  }
410
520
  })
411
521
  describe('non-strict mode', () => {
412
522
  for (const { note, json } of invalidVectors) {
413
- describe(note, () => {
414
- it('does not throws when parsing malformed JSON', () => {
415
- expect(() =>
416
- lexParse(JSON.stringify(json), { strict: false }),
417
- ).not.toThrow()
418
- })
523
+ test(note, () => {
524
+ const jsonBytes = Buffer.from(JSON.stringify(json))
525
+ expect(() =>
526
+ lexParseJsonBytes(jsonBytes, { strict: false }),
527
+ ).not.toThrow()
419
528
  })
420
529
  }
421
530
  })
@@ -425,10 +534,8 @@ describe(lexParse, () => {
425
534
  describe(lexStringify, () => {
426
535
  describe('valid vectors', () => {
427
536
  for (const { name, json, lex } of validVectors) {
428
- describe(name, () => {
429
- it('stringifies lex to string', () => {
430
- expect(JSON.parse(lexStringify(lex))).toEqual(json)
431
- })
537
+ test(name, () => {
538
+ expect(JSON.parse(lexStringify(lex))).toStrictEqual(json)
432
539
  })
433
540
  }
434
541
  })
@@ -436,63 +543,66 @@ describe(lexStringify, () => {
436
543
 
437
544
  describe(jsonToLex, () => {
438
545
  describe('valid vectors', () => {
439
- for (const { name, json, lex } of validVectors) {
440
- describe(name, () => {
441
- it('converts json to lex (in strict mode)', () => {
546
+ describe('strict mode', () => {
547
+ for (const { name, json, lex } of validVectors) {
548
+ test(name, () => {
442
549
  expect(lexEquals(jsonToLex(json, { strict: true }), lex)).toBe(true)
443
550
  expect(lexEquals(lex, jsonToLex(json, { strict: true }))).toBe(true)
444
551
  })
552
+ }
553
+ })
445
554
 
446
- it('converts json to lex (in non-strict mode)', () => {
555
+ describe('non-strict mode', () => {
556
+ for (const { name, json, lex } of validVectors) {
557
+ test(name, () => {
447
558
  expect(lexEquals(jsonToLex(json, { strict: false }), lex)).toBe(true)
448
559
  expect(lexEquals(lex, jsonToLex(json, { strict: false }))).toBe(true)
449
560
  })
450
- })
451
- }
561
+ }
562
+ })
452
563
  })
453
564
 
454
565
  describe('acceptable vectors', () => {
455
- for (const { note, json } of acceptableVectors) {
456
- describe(note, () => {
457
- it('parses lex from json in strict mode', () => {
566
+ describe('strict mode', () => {
567
+ for (const { note, json } of acceptableVectors) {
568
+ test(note, () => {
458
569
  expect(() => jsonToLex(json, { strict: true })).toThrow()
459
570
  })
571
+ }
572
+ })
460
573
 
461
- it('parses lex from json in non-strict mode', () => {
574
+ describe('non-strict mode', () => {
575
+ for (const { note, json } of acceptableVectors) {
576
+ test(note, () => {
462
577
  expect(() => jsonToLex(json, { strict: false })).not.toThrow()
463
578
  })
464
- })
465
- }
579
+ }
580
+ })
466
581
  })
467
582
 
468
583
  describe('invalid vectors', () => {
469
- for (const { note, json } of invalidVectors) {
470
- describe(note, () => {
471
- it(`throws for malformed object`, () => {
584
+ describe('strict mode', () => {
585
+ for (const { note, json } of invalidVectors) {
586
+ test(note, () => {
472
587
  expect(() => jsonToLex(json, { strict: true })).toThrow()
473
588
  })
474
-
475
- it('throws for nested malformed object', () => {
476
- expect(() => jsonToLex({ nested: json }, { strict: true })).toThrow()
477
- expect(() => jsonToLex([json], { strict: true })).toThrow()
478
- })
479
-
480
- it('does not throw in non-strict mode', () => {
589
+ }
590
+ })
591
+ describe('non-strict mode', () => {
592
+ for (const { note, json } of invalidVectors) {
593
+ test(note, () => {
481
594
  expect(() => jsonToLex(json, { strict: false })).not.toThrow()
482
595
  })
483
- })
484
- }
596
+ }
597
+ })
485
598
  })
486
599
  })
487
600
 
488
601
  describe(lexToJson, () => {
489
602
  describe('valid vectors', () => {
490
603
  for (const { name, json, lex } of validVectors) {
491
- describe(name, () => {
492
- it('converts lex to json', () => {
493
- expect(lexToJson(lex)).toEqual(json)
494
- expect(lexToJson(lex)).toEqual(json)
495
- })
604
+ test(name, () => {
605
+ expect(lexToJson(lex)).toStrictEqual(json)
496
606
  })
497
607
  }
498
608
  })
@@ -501,10 +611,19 @@ describe(lexToJson, () => {
501
611
  describe('json > lex > json', () => {
502
612
  describe('valid vectors', () => {
503
613
  for (const { name, json } of validVectors) {
504
- describe(name, () => {
505
- it('converts json to lex', () => {
506
- expect(lexToJson(jsonToLex(json))).toEqual(json)
507
- })
614
+ test(name, () => {
615
+ expect(lexToJson(jsonToLex(json))).toStrictEqual(json)
616
+ })
617
+ }
618
+ })
619
+ })
620
+
621
+ describe('json (binary) > lex > json', () => {
622
+ describe('valid vectors', () => {
623
+ for (const { name, json } of validVectors) {
624
+ test(name, () => {
625
+ const jsonBytes = Buffer.from(JSON.stringify(json, undefined, 4))
626
+ expect(lexToJson(lexParseJsonBytes(jsonBytes))).toStrictEqual(json)
508
627
  })
509
628
  }
510
629
  })
@@ -513,13 +632,360 @@ describe('json > lex > json', () => {
513
632
  describe('lex > json > lex', () => {
514
633
  describe('valid vectors', () => {
515
634
  for (const { name, lex } of validVectors) {
516
- describe(name, () => {
517
- it('converts lex to json', () => {
518
- ;('')
519
- expect(lexEquals(jsonToLex(lexToJson(lex)), lex)).toBe(true)
520
- expect(lexEquals(lex, jsonToLex(lexToJson(lex)))).toBe(true)
521
- })
635
+ test(name, () => {
636
+ expect(lexEquals(jsonToLex(lexToJson(lex)), lex)).toBe(true)
637
+ expect(lexEquals(lex, jsonToLex(lexToJson(lex)))).toBe(true)
522
638
  })
523
639
  }
524
640
  })
525
641
  })
642
+
643
+ describe('lexParseJsonBytes strict mode error parity with lexParse', () => {
644
+ describe('invalid JSON input throws SyntaxError containing "Unexpected token"', () => {
645
+ test('lexParse throws with Unexpected token', () => {
646
+ expect(() => lexParse('not valid json', { strict: true })).toThrow(
647
+ /Unexpected token/,
648
+ )
649
+ })
650
+
651
+ test('lexParseJsonBytes throws with Unexpected token', () => {
652
+ expect(() =>
653
+ lexParseJsonBytes(Buffer.from('not valid json'), { strict: true }),
654
+ ).toThrow(/Unexpected token/)
655
+ })
656
+
657
+ test('lexParseJsonBytes non-strict also throws with Unexpected token for invalid JSON', () => {
658
+ expect(() =>
659
+ lexParseJsonBytes(Buffer.from('not valid json'), { strict: false }),
660
+ ).toThrow(/Unexpected token/)
661
+ })
662
+ })
663
+
664
+ describe('float numbers: strict throws TypeError, non-strict accepts', () => {
665
+ const jsonStr = '{"value":1.5}'
666
+
667
+ test('lexParse strict throws TypeError with value in message', () => {
668
+ expect(() => lexParse(jsonStr, { strict: true })).toThrow(TypeError)
669
+ expect(() => lexParse(jsonStr, { strict: true })).toThrow(
670
+ 'Invalid non-integer number: 1.5',
671
+ )
672
+ })
673
+
674
+ test('lexParseJsonBytes strict throws same TypeError', () => {
675
+ const bytes = Buffer.from(jsonStr)
676
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
677
+ TypeError,
678
+ )
679
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
680
+ 'Invalid non-integer number: 1.5',
681
+ )
682
+ })
683
+
684
+ test('lexParse non-strict accepts float', () => {
685
+ expect(() => lexParse(jsonStr, { strict: false })).not.toThrow()
686
+ })
687
+
688
+ test('lexParseJsonBytes non-strict accepts float', () => {
689
+ expect(() =>
690
+ lexParseJsonBytes(Buffer.from(jsonStr), { strict: false }),
691
+ ).not.toThrow()
692
+ })
693
+ })
694
+
695
+ describe('exponent notation: safe integers accepted, unsafe integers rejected', () => {
696
+ test('lexParse strict accepts 1e10 (safe integer)', () => {
697
+ expect(lexParse('1e10', { strict: true })).toBe(1e10)
698
+ })
699
+
700
+ test('lexParseJsonBytes strict accepts 1e10 (safe integer)', () => {
701
+ expect(lexParseJsonBytes(Buffer.from('1e10'), { strict: true })).toBe(
702
+ 1e10,
703
+ )
704
+ })
705
+
706
+ test('lexParse strict rejects 1e20 (unsafe integer)', () => {
707
+ expect(() => lexParse('1e20', { strict: true })).toThrow(TypeError)
708
+ })
709
+
710
+ test('lexParseJsonBytes strict rejects 1e20 (unsafe integer)', () => {
711
+ expect(() =>
712
+ lexParseJsonBytes(Buffer.from('1e20'), { strict: true }),
713
+ ).toThrow(TypeError)
714
+ })
715
+ })
716
+
717
+ describe('invalid blob: strict throws TypeError, non-strict returns plain object', () => {
718
+ const invalidBlobJson = '{"$type":"blob"}'
719
+
720
+ test('lexParse strict throws TypeError with "Invalid blob object"', () => {
721
+ expect(() => lexParse(invalidBlobJson, { strict: true })).toThrow(
722
+ TypeError,
723
+ )
724
+ expect(() => lexParse(invalidBlobJson, { strict: true })).toThrow(
725
+ 'Invalid blob object',
726
+ )
727
+ })
728
+
729
+ test('lexParseJsonBytes strict throws same TypeError', () => {
730
+ const bytes = Buffer.from(invalidBlobJson)
731
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
732
+ TypeError,
733
+ )
734
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
735
+ 'Invalid blob object',
736
+ )
737
+ })
738
+
739
+ test('lexParse non-strict returns plain object', () => {
740
+ expect(() => lexParse(invalidBlobJson, { strict: false })).not.toThrow()
741
+ })
742
+
743
+ test('lexParseJsonBytes non-strict returns plain object', () => {
744
+ expect(() =>
745
+ lexParseJsonBytes(Buffer.from(invalidBlobJson), { strict: false }),
746
+ ).not.toThrow()
747
+ })
748
+ })
749
+
750
+ describe('blob with CBOR CID: strict throws TypeError, non-strict returns plain object', () => {
751
+ const blobWithCborCidJson = JSON.stringify({
752
+ $type: 'blob',
753
+ ref: {
754
+ $link: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
755
+ },
756
+ mimeType: 'image/png',
757
+ size: 1,
758
+ })
759
+
760
+ test('lexParse strict throws TypeError with "Invalid blob object"', () => {
761
+ expect(() => lexParse(blobWithCborCidJson, { strict: true })).toThrow(
762
+ TypeError,
763
+ )
764
+ expect(() => lexParse(blobWithCborCidJson, { strict: true })).toThrow(
765
+ 'Invalid blob object',
766
+ )
767
+ })
768
+
769
+ test('lexParseJsonBytes strict throws same TypeError', () => {
770
+ const bytes = Buffer.from(blobWithCborCidJson)
771
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
772
+ TypeError,
773
+ )
774
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
775
+ 'Invalid blob object',
776
+ )
777
+ })
778
+
779
+ test('lexParse non-strict returns plain object', () => {
780
+ expect(() =>
781
+ lexParse(blobWithCborCidJson, { strict: false }),
782
+ ).not.toThrow()
783
+ })
784
+
785
+ test('lexParseJsonBytes non-strict returns plain object', () => {
786
+ expect(() =>
787
+ lexParseJsonBytes(Buffer.from(blobWithCborCidJson), { strict: false }),
788
+ ).not.toThrow()
789
+ })
790
+ })
791
+
792
+ describe('invalid $link: strict throws TypeError, non-strict returns plain object', () => {
793
+ const invalidLinkJson = '{"$link":"."}'
794
+
795
+ test('lexParse strict throws TypeError with "Invalid $link object"', () => {
796
+ expect(() => lexParse(invalidLinkJson, { strict: true })).toThrow(
797
+ TypeError,
798
+ )
799
+ expect(() => lexParse(invalidLinkJson, { strict: true })).toThrow(
800
+ 'Invalid $link object',
801
+ )
802
+ })
803
+
804
+ test('lexParseJsonBytes strict throws same TypeError', () => {
805
+ const bytes = Buffer.from(invalidLinkJson)
806
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
807
+ TypeError,
808
+ )
809
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
810
+ 'Invalid $link object',
811
+ )
812
+ })
813
+
814
+ test('lexParse non-strict returns plain object', () => {
815
+ expect(() => lexParse(invalidLinkJson, { strict: false })).not.toThrow()
816
+ })
817
+
818
+ test('lexParseJsonBytes non-strict returns plain object', () => {
819
+ expect(() =>
820
+ lexParseJsonBytes(Buffer.from(invalidLinkJson), { strict: false }),
821
+ ).not.toThrow()
822
+ })
823
+ })
824
+
825
+ describe('$link with extra fields: strict throws TypeError, non-strict returns plain object', () => {
826
+ const linkWithExtraJson =
827
+ '{"$link":"bafkreiccldh766hwcnuxnf2wh6jgzepf2nlu2lvcllt63eww5p6chi4ity","extra":"field"}'
828
+
829
+ test('lexParse strict throws TypeError with "Invalid $link object"', () => {
830
+ expect(() => lexParse(linkWithExtraJson, { strict: true })).toThrow(
831
+ TypeError,
832
+ )
833
+ expect(() => lexParse(linkWithExtraJson, { strict: true })).toThrow(
834
+ 'Invalid $link object',
835
+ )
836
+ })
837
+
838
+ test('lexParseJsonBytes strict throws same TypeError', () => {
839
+ const bytes = Buffer.from(linkWithExtraJson)
840
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
841
+ TypeError,
842
+ )
843
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
844
+ 'Invalid $link object',
845
+ )
846
+ })
847
+
848
+ test('lexParse non-strict returns plain object', () => {
849
+ expect(() => lexParse(linkWithExtraJson, { strict: false })).not.toThrow()
850
+ })
851
+
852
+ test('lexParseJsonBytes non-strict returns plain object', () => {
853
+ expect(() =>
854
+ lexParseJsonBytes(Buffer.from(linkWithExtraJson), { strict: false }),
855
+ ).not.toThrow()
856
+ })
857
+ })
858
+
859
+ describe('invalid $bytes: strict throws TypeError, non-strict returns plain object', () => {
860
+ const invalidBytesJson = '{"$bytes":"🐻"}'
861
+
862
+ test('lexParse strict throws TypeError with "Invalid $bytes object"', () => {
863
+ expect(() => lexParse(invalidBytesJson, { strict: true })).toThrow(
864
+ TypeError,
865
+ )
866
+ expect(() => lexParse(invalidBytesJson, { strict: true })).toThrow(
867
+ 'Invalid $bytes object',
868
+ )
869
+ })
870
+
871
+ test('lexParseJsonBytes strict throws same TypeError', () => {
872
+ const bytes = Buffer.from(invalidBytesJson)
873
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
874
+ TypeError,
875
+ )
876
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
877
+ 'Invalid $bytes object',
878
+ )
879
+ })
880
+
881
+ test('lexParse non-strict returns plain object', () => {
882
+ expect(() => lexParse(invalidBytesJson, { strict: false })).not.toThrow()
883
+ })
884
+
885
+ test('lexParseJsonBytes non-strict returns plain object', () => {
886
+ expect(() =>
887
+ lexParseJsonBytes(Buffer.from(invalidBytesJson), { strict: false }),
888
+ ).not.toThrow()
889
+ })
890
+ })
891
+
892
+ describe('$bytes with extra fields: strict throws TypeError, non-strict returns plain object', () => {
893
+ const bytesWithExtraJson =
894
+ '{"$bytes":"nFERjvLLiw9qm45JrqH9QTzyC2Lu1Xb4ne6+sBrCzI0","extra":"field"}'
895
+
896
+ test('lexParse strict throws TypeError with "Invalid $bytes object"', () => {
897
+ expect(() => lexParse(bytesWithExtraJson, { strict: true })).toThrow(
898
+ TypeError,
899
+ )
900
+ expect(() => lexParse(bytesWithExtraJson, { strict: true })).toThrow(
901
+ 'Invalid $bytes object',
902
+ )
903
+ })
904
+
905
+ test('lexParseJsonBytes strict throws same TypeError', () => {
906
+ const bytes = Buffer.from(bytesWithExtraJson)
907
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
908
+ TypeError,
909
+ )
910
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
911
+ 'Invalid $bytes object',
912
+ )
913
+ })
914
+
915
+ test('lexParse non-strict returns plain object', () => {
916
+ expect(() =>
917
+ lexParse(bytesWithExtraJson, { strict: false }),
918
+ ).not.toThrow()
919
+ })
920
+
921
+ test('lexParseJsonBytes non-strict returns plain object', () => {
922
+ expect(() =>
923
+ lexParseJsonBytes(Buffer.from(bytesWithExtraJson), { strict: false }),
924
+ ).not.toThrow()
925
+ })
926
+ })
927
+
928
+ describe('empty $type: strict throws TypeError, non-strict returns plain object', () => {
929
+ const emptyTypeJson = '{"$type":"","foo":"bar"}'
930
+
931
+ test('lexParse strict throws TypeError with "Empty $type property"', () => {
932
+ expect(() => lexParse(emptyTypeJson, { strict: true })).toThrow(TypeError)
933
+ expect(() => lexParse(emptyTypeJson, { strict: true })).toThrow(
934
+ 'Empty $type property',
935
+ )
936
+ })
937
+
938
+ test('lexParseJsonBytes strict throws same TypeError', () => {
939
+ const bytes = Buffer.from(emptyTypeJson)
940
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
941
+ TypeError,
942
+ )
943
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
944
+ 'Empty $type property',
945
+ )
946
+ })
947
+
948
+ test('lexParse non-strict returns plain object', () => {
949
+ expect(() => lexParse(emptyTypeJson, { strict: false })).not.toThrow()
950
+ })
951
+
952
+ test('lexParseJsonBytes non-strict returns plain object', () => {
953
+ expect(() =>
954
+ lexParseJsonBytes(Buffer.from(emptyTypeJson), { strict: false }),
955
+ ).not.toThrow()
956
+ })
957
+ })
958
+
959
+ describe('non-string $type: strict throws TypeError, non-strict returns plain object', () => {
960
+ const nonStringTypeJson = '{"$type":123,"foo":"bar"}'
961
+
962
+ test('lexParse strict throws TypeError with type name in message', () => {
963
+ expect(() => lexParse(nonStringTypeJson, { strict: true })).toThrow(
964
+ TypeError,
965
+ )
966
+ expect(() => lexParse(nonStringTypeJson, { strict: true })).toThrow(
967
+ 'Invalid $type property (number)',
968
+ )
969
+ })
970
+
971
+ test('lexParseJsonBytes strict throws same TypeError', () => {
972
+ const bytes = Buffer.from(nonStringTypeJson)
973
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
974
+ TypeError,
975
+ )
976
+ expect(() => lexParseJsonBytes(bytes, { strict: true })).toThrow(
977
+ 'Invalid $type property (number)',
978
+ )
979
+ })
980
+
981
+ test('lexParse non-strict returns plain object', () => {
982
+ expect(() => lexParse(nonStringTypeJson, { strict: false })).not.toThrow()
983
+ })
984
+
985
+ test('lexParseJsonBytes non-strict returns plain object', () => {
986
+ expect(() =>
987
+ lexParseJsonBytes(Buffer.from(nonStringTypeJson), { strict: false }),
988
+ ).not.toThrow()
989
+ })
990
+ })
991
+ })