@atproto/lex-json 0.0.15 β†’ 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.
@@ -0,0 +1,125 @@
1
+ import { bench, describe } from 'vitest'
2
+ import { JsonValue } from './json.js'
3
+ import {
4
+ LexParseOptions,
5
+ jsonToLex,
6
+ lexParse,
7
+ parseSpecialJsonObject,
8
+ } from './lex-json.js'
9
+
10
+ // This benchmark compares the performance of two implementations of
11
+ // lexParse:
12
+ // - One that uses a reviver function with JSON.parse to directly parse special
13
+ // objects and handle numbers (lexParse with reviver)
14
+ // - One that first parses JSON to a plain JS object and then converts it to
15
+ // LexValue using jsonToLex (lexParse with jsonToLex)
16
+
17
+ describe('small object', () => {
18
+ benchData({
19
+ $type: 'app.bsky.feed.post',
20
+ text: 'Hello world! πŸ‘‹',
21
+ createdAt: '2024-01-01T00:00:00Z',
22
+ })
23
+ })
24
+
25
+ describe('simple mixed structure', () => {
26
+ benchData({
27
+ cid: {
28
+ $link: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
29
+ },
30
+ bytes: {
31
+ $bytes: 'nFERjvLLiw9qm45JrqH9QTzyC2Lu1Xb4ne6+sBrCzI0',
32
+ },
33
+ blob: {
34
+ $type: 'blob',
35
+ ref: {
36
+ $link: 'bafkreig77vqcdozl2wyk6z3cscaj5q5fggi53aoh64fewkdiri3cdauyn4',
37
+ },
38
+ mimeType: 'image/jpeg',
39
+ size: 10000,
40
+ },
41
+ nested: {
42
+ array: [
43
+ {
44
+ number: 42,
45
+ string: 'hello world',
46
+ bool: true,
47
+ null: null,
48
+ },
49
+ ],
50
+ string: 'Hello δΈ–η•Œ! 🌍🌎🌏 Γ‘oΓ±o',
51
+ createdAt: '2024-01-01T00:00:00Z',
52
+ },
53
+ })
54
+ })
55
+
56
+ describe('large payload', () => {
57
+ benchData({
58
+ items: Array.from({ length: 25 }, (_, i) => ({
59
+ id: i,
60
+ name: `Item ${i}`,
61
+ longUnicode:
62
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit 🀩.\n'.repeat(
63
+ 2,
64
+ ),
65
+ tags: ['tag1', 'tag2', 'tag3'],
66
+ bytes: {
67
+ $bytes: Buffer.from(`This is some byte data for item ${i}`).toString(
68
+ 'base64',
69
+ ),
70
+ },
71
+ cid: {
72
+ $link: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
73
+ },
74
+ metadata: {
75
+ created: '2024-01-01T00:00:00Z',
76
+ count: i * 10,
77
+ nested: {
78
+ flag: i % 2 === 0,
79
+ values: [i, i * 2, i * 3],
80
+ },
81
+ items: Array.from({ length: 5 }, (_, j) => ({
82
+ id: `${i}-${j}`,
83
+ value: `Value ${i}-${j}`,
84
+ })),
85
+ },
86
+ })),
87
+ })
88
+ })
89
+
90
+ function benchData(data: unknown, options?: LexParseOptions) {
91
+ const jsonString = JSON.stringify(data)
92
+
93
+ const withReviver: typeof lexParse = (input, options = { strict: true }) => {
94
+ return JSON.parse(input, (key: string, value: JsonValue) => {
95
+ switch (typeof value) {
96
+ case 'object':
97
+ if (value === null) return null
98
+ if (Array.isArray(value)) return value
99
+ return parseSpecialJsonObject(value, options) ?? value
100
+ case 'number':
101
+ if (Number.isSafeInteger(value)) return value
102
+ if (options && options.strict === false) return value
103
+ throw new TypeError(`Invalid non-integer number: ${value}`)
104
+ default:
105
+ return value
106
+ }
107
+ })
108
+ }
109
+
110
+ const naiveParse: typeof lexParse = (input, options) => {
111
+ return jsonToLex(JSON.parse(input), options) as any
112
+ }
113
+
114
+ bench('current', () => {
115
+ lexParse(jsonString, options)
116
+ })
117
+
118
+ bench('with-reviver', () => {
119
+ withReviver(jsonString, options)
120
+ })
121
+
122
+ bench('naive', () => {
123
+ naiveParse(jsonString, options)
124
+ })
125
+ }
@@ -336,6 +336,24 @@ export const acceptableVectors: Array<{
336
336
  $link: 'bafkreiccldh766hwcnuxnf2wh6jgzepf2nlu2lvcllt63eww5p6chi4ity',
337
337
  },
338
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
+ },
339
357
  ]
340
358
 
341
359
  export const invalidVectors: Array<{
@@ -621,3 +639,353 @@ describe('lex > json > lex', () => {
621
639
  }
622
640
  })
623
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
+ })
package/src/lex-json.ts CHANGED
@@ -100,41 +100,26 @@ export function lexParse<T extends LexValue = LexValue>(
100
100
  input: string,
101
101
  options: LexParseOptions = { strict: false },
102
102
  ): T {
103
- return JSON.parse(input, function (key: string, value: JsonValue): LexValue {
104
- switch (typeof value) {
105
- case 'object':
106
- if (value === null) return null
107
- if (Array.isArray(value)) return value
108
- return parseSpecialJsonObject(value, options) ?? value
109
- case 'number':
110
- if (Number.isSafeInteger(value)) return value
111
- if (options.strict) {
112
- throw new TypeError(`Invalid non-integer number: ${value}`)
113
- }
114
- // fallthrough
115
- default:
116
- return value
117
- }
118
- })
103
+ // @NOTE see ./lex-json.bench.ts for performance comparison of implementation
104
+ // that uses a reviver function in JSON.parse vs. the current implementation.
105
+ return jsonToLex(JSON.parse(input), options) as T
119
106
  }
120
107
 
121
108
  /**
122
- * Parses a `Uint8Array` containing JSON data into a Lex value.
109
+ * Parses a JSON string from a byte array into Lex values.
123
110
  */
124
111
  export function lexParseJsonBytes(
125
- jsonBytes: Uint8Array,
112
+ bytes: Uint8Array,
126
113
  options?: LexParseOptions,
127
114
  ): LexValue {
128
- // @TODO optimize this to avoid intermediate string. This requires a custom
129
- // JSON parser that can operate on binary data, which is non-trivial, but
130
- // could be a future improvement if performance is a concern. See the link
131
- // below for an example of a JSON parser that operates on binary data in
132
- // @ipld/dag-json
133
-
134
- // https://github.com/ipld/js-dag-json/blob/57912da6e9d64a179f7d2384c3b6d7b07fbfb143/src/index.js#L161
135
-
136
- const jsonString = utf8FromBytes(jsonBytes)
137
- return lexParse(jsonString, options)
115
+ // @NOTE see ./json-bytes-decoder.bench.ts for performance comparison of
116
+ // implementation that uses a decoder class that operates directly on bytes
117
+ // vs. the current implementation that first decodes bytes to string and then
118
+ // parses JSON. For more common cases, it seems that the trivial
119
+ // implementation works better than the decoder based solution, while having a
120
+ // small overhead for slower cases (~2% difference). Because of this, we keep
121
+ // the trivial implementation:
122
+ return lexParse(utf8FromBytes(bytes), options)
138
123
  }
139
124
 
140
125
  /**
@@ -181,10 +166,8 @@ export function jsonToLex(
181
166
  }
182
167
  case 'number':
183
168
  if (Number.isSafeInteger(value)) return value
184
- if (options.strict) {
185
- throw new TypeError(`Invalid non-integer number: ${value}`)
186
- }
187
- // fallthrough
169
+ if (options.strict === false) return value
170
+ throw new TypeError(`Invalid non-integer number: ${value}`)
188
171
  case 'boolean':
189
172
  case 'string':
190
173
  return value
@@ -327,7 +310,10 @@ function encodeLexMap(input: LexMap): JsonObject {
327
310
  return copy ?? (input as JsonObject)
328
311
  }
329
312
 
330
- function parseSpecialJsonObject(
313
+ /**
314
+ * @internal
315
+ */
316
+ export function parseSpecialJsonObject(
331
317
  input: LexMap,
332
318
  options: LexParseOptions,
333
319
  ): Cid | Uint8Array | BlobRef | undefined {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "extends": ["../../../tsconfig/isomorphic.json"],
3
3
  "include": ["./src"],
4
- "exclude": ["**/*.test.ts"],
4
+ "exclude": ["**/*.bench.ts", "**/*.test.ts"],
5
5
  "compilerOptions": {
6
6
  "noImplicitAny": true,
7
7
  "importHelpers": true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "extends": "../../../tsconfig/vitest.json",
3
- "include": ["./tests", "./src/**/*.test.ts"],
3
+ "include": ["./tests", "./src/**/*.bench.ts", "./src/**/*.test.ts"],
4
4
  "compilerOptions": {
5
5
  "noImplicitAny": true,
6
6
  "rootDir": "./",