11import { checkNonNegativeInteger , checkStrictlyPositiveInteger } from './check'
2- import { parseChunk } from './chunk'
2+ import { parseChunk , type ParseChunkOptions } from './chunk'
33import { defaultChunkSize } from './constants'
44import { fetchChunk } from './fetch'
55import type { ParseResult } from './types'
66
7+ interface ParseUrlOptions extends ParseChunkOptions {
8+ chunkSize ?: number
9+ firstByte ?: number
10+ lastByte ?: number
11+ requestInit ?: RequestInit
12+ fetchChunk ?: typeof fetchChunk
13+ parseChunk ?: typeof parseChunk
14+ }
15+
716/**
817 * Parses a remote text file in chunks using HTTP range requests.
918 * @param url The URL of the remote text file.
@@ -19,32 +28,25 @@ import type { ParseResult } from './types'
1928 */
2029export async function * parseUrl (
2130 url : string ,
22- options ?: {
23- chunkSize ?: number
24- firstByte ?: number
25- lastByte ?: number
26- requestInit ?: RequestInit
27- fetchChunk ?: typeof fetchChunk
28- parseChunk ?: typeof parseChunk
29- } ,
31+ options : ParseUrlOptions = { } ,
3032) : AsyncGenerator < ParseResult , void , unknown > {
31- const chunkSize = checkStrictlyPositiveInteger ( options ? .chunkSize ) ?? defaultChunkSize
33+ const chunkSize = checkStrictlyPositiveInteger ( options . chunkSize ) ?? defaultChunkSize
3234 // TODO(SL): should we accept negative values (from the end)?
33- let firstByte = checkNonNegativeInteger ( options ? .firstByte ) ?? 0
34- let lastByte = checkNonNegativeInteger ( options ? .lastByte )
35+ let firstByte = checkNonNegativeInteger ( options . firstByte ) ?? 0
36+ let lastByte = checkNonNegativeInteger ( options . lastByte )
3537 if ( lastByte !== undefined && lastByte < firstByte ) {
3638 throw new Error ( 'lastByte must be greater than firstByte' )
3739 }
3840
3941 let cursor = firstByte
4042 let bytes : Uint8Array < ArrayBufferLike > = new Uint8Array ( 0 )
4143 while ( true ) {
42- const { bytes : chunkBytes , fileSize } = await ( options ? .fetchChunk ?? fetchChunk ) ( {
44+ const { bytes : chunkBytes , fileSize } = await ( options . fetchChunk ?? fetchChunk ) ( {
4345 url,
4446 chunkSize,
4547 firstByte,
4648 maxLastByte : lastByte ,
47- requestInit : options ? .requestInit ,
49+ requestInit : options . requestInit ,
4850 } )
4951
5052 // Update lastByte in case it is undefined or greater than the file size
@@ -71,8 +73,16 @@ export async function* parseUrl(
7173 }
7274
7375 let consumedBytes = 0
74- for ( const result of ( options ? .parseChunk ?? parseChunk ) ( bytes , {
76+ for ( const result of ( options . parseChunk ?? parseChunk ) ( bytes , {
7577 ignoreLastRow : true , // the remaining bytes may not contain a full last row
78+ // pass other options
79+ delimiter : options . delimiter ,
80+ newline : options . newline ,
81+ quoteChar : options . quoteChar ,
82+ escapeChar : options . escapeChar ,
83+ comments : options . comments ,
84+ delimitersToGuess : options . delimitersToGuess ,
85+ skipEmptyLines : options . skipEmptyLines ,
7686 } ) ) {
7787 consumedBytes += result . meta . byteCount
7888 if ( consumedBytes > bytes . length ) {
@@ -93,16 +103,38 @@ export async function* parseUrl(
93103 cursor += consumedBytes
94104 firstByte += chunkSize
95105
96- if ( firstByte > lastByte ) {
97- break
98- }
99- /* v8 ignore if -- @preserve */
100- if ( cursor + bytes . length !== firstByte ) {
106+ if ( firstByte <= lastByte ) {
107+ /* v8 ignore if -- @preserve */
108+ if ( cursor + bytes . length !== firstByte ) {
101109 // assertion: it should not happen.
102- throw new Error ( 'Invalid state: non-contiguous offsets' )
110+ throw new Error ( 'Invalid state: non-contiguous offsets' )
111+ }
112+ continue
103113 }
114+
115+ break
104116 }
105117
106- // TODO(SL): What to do with remaining bytes? For now, ignore them.
107- // call again with ignoreLastRow: false for the remaining bytes?
118+ // Parse remaining bytes, if any
119+ if ( bytes . length > 0 ) {
120+ for ( const result of ( options . parseChunk ?? parseChunk ) ( bytes , {
121+ ignoreLastRow : false ,
122+ // pass other options
123+ delimiter : options . delimiter ,
124+ newline : options . newline ,
125+ quoteChar : options . quoteChar ,
126+ escapeChar : options . escapeChar ,
127+ comments : options . comments ,
128+ delimitersToGuess : options . delimitersToGuess ,
129+ skipEmptyLines : options . skipEmptyLines ,
130+ } ) ) {
131+ yield {
132+ ...result ,
133+ meta : {
134+ ...result . meta ,
135+ offset : result . meta . offset + cursor ,
136+ } ,
137+ }
138+ }
139+ }
108140}
0 commit comments