Skip to content

Commit 3e18918

Browse files
committed
fix(createReadStream): Handle invalid JSON file and invalid elements
1 parent c61a184 commit 3e18918

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

src/constants.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,10 @@ const CHARACTER = {
1414
NEW_LINE: "\n",
1515
};
1616

17-
export { CHARACTER };
17+
const ERRORS = {
18+
INVALID_FILE: "Invalid JSON array file",
19+
INVALID_ELEMENT: (element: string) =>
20+
`Invalid element found in JSON array - ${element}`,
21+
};
22+
23+
export { CHARACTER, ERRORS };

src/modules/JsonArrayStreamer.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ReadStream, createReadStream } from "fs";
22
import { once } from "events";
33
import type { ElementType, ReadStreamOptions } from "../index.types";
4-
import { CHARACTER } from "../constants";
4+
import { CHARACTER, ERRORS } from "../constants";
55

66
class JsonArrayStreamer<T> {
77
private readStream: ReadStream | null;
@@ -41,6 +41,15 @@ class JsonArrayStreamer<T> {
4141
}
4242
}
4343

44+
private getParsedElement<T>() {
45+
try {
46+
const element: T = JSON.parse(this.chunkBuffer);
47+
return element;
48+
} catch (error) {
49+
throw new Error(ERRORS.INVALID_ELEMENT(this.chunkBuffer));
50+
}
51+
}
52+
4453
private addToResult(element: T, filter?: (element: T) => boolean) {
4554
if (!filter) {
4655
this.resultBuffer.push(element);
@@ -66,7 +75,7 @@ class JsonArrayStreamer<T> {
6675

6776
if (char === CHARACTER.QUOTE) {
6877
if (this.isCharInsideQuotes && !this.isCharEscaped) {
69-
const element: T = JSON.parse(this.chunkBuffer);
78+
const element = <T>this.getParsedElement();
7079
this.addToResult(element, filter);
7180
this.resetParser();
7281
} else if (this.chunkBuffer === CHARACTER.QUOTE) {
@@ -85,8 +94,8 @@ class JsonArrayStreamer<T> {
8594
char: string,
8695
filter?: (element: T) => boolean
8796
) {
88-
if ([CHARACTER.COMMA, CHARACTER.BRACKET.CLOSE].includes(char)) {
89-
const element: T = JSON.parse(this.chunkBuffer);
97+
if (char === CHARACTER.COMMA) {
98+
const element = <T>this.getParsedElement();
9099
this.addToResult(element, filter);
91100
this.resetParser();
92101
} else {
@@ -109,7 +118,7 @@ class JsonArrayStreamer<T> {
109118
this.elementEnclosureCount -= 1;
110119

111120
if (this.elementEnclosureCount === 0) {
112-
const element: T = JSON.parse(this.chunkBuffer);
121+
const element = <T>this.getParsedElement();
113122
this.addToResult(element, filter);
114123
this.resetParser();
115124
}
@@ -127,19 +136,29 @@ class JsonArrayStreamer<T> {
127136
}
128137

129138
public async *stream(chunkSize: number, filter?: (element: T) => boolean) {
130-
for await (const chunk of this.chunkGenerator()) {
139+
characterStream: for await (const chunk of this.chunkGenerator()) {
131140
for (let char of chunk) {
132141
if (!this.rootDetected) {
142+
if (
143+
![
144+
CHARACTER.SPACE,
145+
CHARACTER.NEW_LINE,
146+
CHARACTER.BRACKET.OPEN,
147+
].includes(char)
148+
)
149+
throw new Error(ERRORS.INVALID_FILE);
150+
133151
this.rootDetected = char === CHARACTER.BRACKET.OPEN;
134152
continue;
135153
}
136154

137155
if (!this.elementDetected) {
156+
if (char === CHARACTER.BRACKET.CLOSE) break characterStream;
157+
138158
this.elementDetected = ![
139159
CHARACTER.SPACE,
140160
CHARACTER.COMMA,
141161
CHARACTER.NEW_LINE,
142-
CHARACTER.BRACKET.CLOSE,
143162
].includes(char);
144163
}
145164

@@ -158,6 +177,11 @@ class JsonArrayStreamer<T> {
158177
this.elementType = "others";
159178
this.elementParser = this.primitiveElementParser;
160179
}
180+
} else if (
181+
this.elementParser === this.primitiveElementParser &&
182+
char === CHARACTER.BRACKET.CLOSE
183+
) {
184+
break characterStream;
161185
}
162186

163187
this.elementParser(char, filter);
@@ -175,7 +199,7 @@ class JsonArrayStreamer<T> {
175199
this.readStream = null;
176200

177201
if (this.chunkBuffer.length) {
178-
const element: T = JSON.parse(this.chunkBuffer);
202+
const element = <T>this.getParsedElement();
179203
this.addToResult(element, filter);
180204
this.resetParser();
181205
}

0 commit comments

Comments
 (0)