csv-stream-lite 1.0.2 → 1.0.3

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.
package/dist/parser.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface CsvEntityOptions {
9
9
  separator?: string;
10
10
  /** Character used to escape special characters. Defaults to '"' */
11
11
  escapeChar?: string;
12
+ /** String used to denote new lines. Defaults to auto-detected '\r', '\n', or '\r\n' */
13
+ newline?: string;
12
14
  }
13
15
  /**
14
16
  * Abstract base class for CSV entities that supports both synchronous and asynchronous parsing.
@@ -17,10 +19,11 @@ export interface CsvEntityOptions {
17
19
  * @typeParam T - The type returned by read operations
18
20
  * @typeParam S - The type yielded by stream operations (defaults to T)
19
21
  */
20
- export declare abstract class CsvEntity<T, S = T> implements Required<CsvEntityOptions> {
22
+ export declare abstract class CsvEntity<T, S = T> {
21
23
  byteBuffer: ByteBuffer;
22
24
  separator: string;
23
25
  escapeChar: string;
26
+ newline?: string;
24
27
  consumed: boolean;
25
28
  /**
26
29
  * Creates a new CSV entity.
@@ -96,6 +99,19 @@ export declare abstract class CsvEntity<T, S = T> implements Required<CsvEntityO
96
99
  export declare class CsvCell extends CsvEntity<string> {
97
100
  chunkSize: number;
98
101
  endOfLineReached: boolean;
102
+ /**
103
+ * Checks if the current buffer position starts with a line ending.
104
+ * Supports both default line endings (\r, \n, \r\n) and custom newline strings.
105
+ *
106
+ * @returns true if at the start of a line ending, false otherwise
107
+ */
108
+ private isAtLineEnd;
109
+ /**
110
+ * Consumes a line ending from the buffer.
111
+ * Handles both default line endings (\r, \n, \r\n) and custom newline strings.
112
+ * Should only be called after isAtLineEnd() returns true.
113
+ */
114
+ private consumeLineEnd;
99
115
  protected parse(): string;
100
116
  protected parseAsync(): Promise<string>;
101
117
  /**
package/dist/parser.js CHANGED
@@ -31,6 +31,7 @@ export class CsvEntity {
31
31
  byteBuffer;
32
32
  separator = ',';
33
33
  escapeChar = '"';
34
+ newline;
34
35
  consumed = false;
35
36
  /**
36
37
  * Creates a new CSV entity.
@@ -49,6 +50,19 @@ export class CsvEntity {
49
50
  if (options?.escapeChar) {
50
51
  this.escapeChar = options.escapeChar;
51
52
  }
53
+ if (options?.newline !== undefined) {
54
+ const newline = options.newline;
55
+ if (newline === '') {
56
+ throw new Error('Invalid CSV newline: newline option must be a non-empty string.');
57
+ }
58
+ if (newline.includes(this.separator)) {
59
+ throw new Error('Invalid CSV newline: newline option must not contain the field separator character.');
60
+ }
61
+ if (newline.includes(this.escapeChar)) {
62
+ throw new Error('Invalid CSV newline: newline option must not contain the escape character.');
63
+ }
64
+ this.newline = newline;
65
+ }
52
66
  }
53
67
  set maxBufferSize(size) {
54
68
  this.byteBuffer.maxBufferSize = size;
@@ -162,6 +176,55 @@ export class CsvEntity {
162
176
  export class CsvCell extends CsvEntity {
163
177
  chunkSize = DEFAULT_CHUNK_SIZE;
164
178
  endOfLineReached = false;
179
+ /**
180
+ * Checks if the current buffer position starts with a line ending.
181
+ * Supports both default line endings (\r, \n, \r\n) and custom newline strings.
182
+ *
183
+ * @returns true if at the start of a line ending, false otherwise
184
+ */
185
+ isAtLineEnd() {
186
+ if (this.newline !== undefined) {
187
+ // Check for custom newline string
188
+ for (let i = 0; i < this.newline.length; i++) {
189
+ const expectedByte = this.newline.charCodeAt(i);
190
+ const actualByte = this.byteBuffer.peek(i);
191
+ if (actualByte === null || actualByte !== expectedByte) {
192
+ return false;
193
+ }
194
+ }
195
+ return true;
196
+ }
197
+ else {
198
+ // Default behavior: check for \r or \n
199
+ return isLineEnd(this.byteBuffer.peek());
200
+ }
201
+ }
202
+ /**
203
+ * Consumes a line ending from the buffer.
204
+ * Handles both default line endings (\r, \n, \r\n) and custom newline strings.
205
+ * Should only be called after isAtLineEnd() returns true.
206
+ */
207
+ consumeLineEnd() {
208
+ if (this.newline !== undefined) {
209
+ // Consume custom newline string - verify each byte matches
210
+ for (let i = 0; i < this.newline.length; i++) {
211
+ const expectedByte = this.newline.charCodeAt(i);
212
+ const actualByte = this.byteBuffer.peek();
213
+ if (actualByte === null || actualByte !== expectedByte) {
214
+ throw new Error('Invariant violation: consumeLineEnd called when not at line end');
215
+ }
216
+ this.byteBuffer.next();
217
+ }
218
+ this.endOfLineReached = true;
219
+ }
220
+ else {
221
+ // Default behavior: consume \r and/or \n
222
+ while (isLineEnd(this.byteBuffer.peek())) {
223
+ this.byteBuffer.next();
224
+ this.endOfLineReached = true;
225
+ }
226
+ }
227
+ }
165
228
  parse() {
166
229
  let str = '';
167
230
  for (const part of this) {
@@ -240,7 +303,7 @@ export class CsvCell extends CsvEntity {
240
303
  }
241
304
  }
242
305
  else {
243
- if (next === separator || isLineEnd(next)) {
306
+ if (next === separator || this.isAtLineEnd()) {
244
307
  break;
245
308
  }
246
309
  }
@@ -258,9 +321,8 @@ export class CsvCell extends CsvEntity {
258
321
  if (this.byteBuffer.peek() === separator) {
259
322
  this.byteBuffer.expect(separator); // consume separator
260
323
  }
261
- while (isLineEnd(this.byteBuffer.peek())) {
262
- this.byteBuffer.next(); // consume line ending
263
- this.endOfLineReached = true;
324
+ if (this.isAtLineEnd()) {
325
+ this.consumeLineEnd();
264
326
  }
265
327
  if (!hadData || chunk.length > 0)
266
328
  yield bytesToString(new Uint8Array(chunk));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "csv-stream-lite",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A lightweight, memory-efficient, zero-dependency streaming CSV parser and stringifier for JavaScript and TypeScript. It works in both Node.js and browser environments.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",