readline-pager 0.2.7 → 0.3.0

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/main.cjs CHANGED
@@ -1,63 +1,189 @@
1
- Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let node_fs = require("node:fs");
2
6
  let node_fs_promises = require("node:fs/promises");
3
7
  let node_worker_threads = require("node:worker_threads");
4
-
5
8
  //#region src/queue.ts
6
- function createPageQueue() {
7
- const queue = [];
8
- let resolver = null;
9
+ function createRingBuffer(capacity) {
10
+ if (!Number.isFinite(capacity) || capacity <= 0) throw new RangeError("capacity must be a positive number");
11
+ let buf = new Array(capacity);
12
+ let head = 0;
13
+ let tail = 0;
14
+ let count = 0;
15
+ let consumerWaiter = null;
16
+ let producerWaiter = null;
17
+ function push(item) {
18
+ if (count === buf.length) {
19
+ const newCap = buf.length * 2;
20
+ const newBuf = new Array(newCap);
21
+ for (let i = 0; i < count; i++) newBuf[i] = buf[(head + i) % buf.length];
22
+ buf = newBuf;
23
+ head = 0;
24
+ tail = count;
25
+ }
26
+ buf[tail] = item;
27
+ tail++;
28
+ if (tail === buf.length) tail = 0;
29
+ count++;
30
+ if (consumerWaiter) {
31
+ const w = consumerWaiter;
32
+ consumerWaiter = null;
33
+ w();
34
+ }
35
+ }
36
+ function shiftSync() {
37
+ if (count === 0) return null;
38
+ const v = buf[head];
39
+ buf[head] = void 0;
40
+ head++;
41
+ if (head === buf.length) head = 0;
42
+ count--;
43
+ if (producerWaiter) {
44
+ const w = producerWaiter;
45
+ producerWaiter = null;
46
+ w();
47
+ }
48
+ return v;
49
+ }
50
+ async function shift(done = false) {
51
+ if (count) return shiftSync();
52
+ if (done) return null;
53
+ await new Promise((r) => {
54
+ consumerWaiter = r;
55
+ });
56
+ if (count) return shiftSync();
57
+ return null;
58
+ }
59
+ function wake() {
60
+ if (consumerWaiter) {
61
+ const w = consumerWaiter;
62
+ consumerWaiter = null;
63
+ w();
64
+ }
65
+ if (producerWaiter) {
66
+ const w = producerWaiter;
67
+ producerWaiter = null;
68
+ w();
69
+ }
70
+ }
71
+ function clear() {
72
+ for (let i = 0; i < buf.length; i++) buf[i] = void 0;
73
+ head = 0;
74
+ tail = 0;
75
+ count = 0;
76
+ wake();
77
+ }
9
78
  return {
10
- queue,
11
- push(page) {
12
- queue.push(page);
13
- resolver?.();
14
- resolver = null;
15
- },
16
- wake() {
17
- resolver?.();
18
- resolver = null;
79
+ push,
80
+ shift,
81
+ shiftSync,
82
+ wake,
83
+ clear,
84
+ get count() {
85
+ return count;
19
86
  },
20
- async shift(done) {
21
- if (queue.length) return queue.shift();
22
- if (done()) return null;
23
- await new Promise((r) => resolver = r);
24
- if (queue.length) return queue.shift();
25
- if (done()) return null;
26
- return null;
87
+ get capacity() {
88
+ return buf.length;
27
89
  }
28
90
  };
29
91
  }
30
-
31
92
  //#endregion
32
93
  //#region src/reader/backward.reader.ts
33
94
  function createBackwardReader(filepath, options) {
34
95
  const { chunkSize, pageSize, delimiter, prefetch } = options;
35
- const pageQueue = createPageQueue();
96
+ const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
97
+ const local = [];
36
98
  let fd = null;
99
+ let fdSync = null;
37
100
  let pos = 0;
38
101
  let buffer = "";
39
102
  let done = false;
40
103
  let closed = false;
41
- let emittedCount = 0;
42
- let firstLine = null;
43
- let lastLine = null;
44
- const local = [];
45
- async function init() {
46
- if (fd) return;
47
- fd = await (0, node_fs_promises.open)(filepath, "r");
48
- pos = (await fd.stat()).size;
49
- if (pos === 0) done = true;
104
+ let startsWithDelimiter = false;
105
+ fdSync = (0, node_fs.openSync)(filepath, "r");
106
+ pos = (0, node_fs.statSync)(filepath).size;
107
+ if (pos === 0) {
108
+ pageQueue.push([buffer]);
109
+ done = true;
110
+ pageQueue.wake();
50
111
  }
51
- async function fill() {
112
+ (async () => {
113
+ try {
114
+ fd = await (0, node_fs_promises.open)(filepath, "r");
115
+ pos = (await fd.stat()).size;
116
+ if (pos === 0) {
117
+ if (!done) {
118
+ pageQueue.push([buffer]);
119
+ done = true;
120
+ }
121
+ if (fd) {
122
+ await fd.close();
123
+ fd = null;
124
+ }
125
+ pageQueue.wake();
126
+ return;
127
+ }
128
+ while (!done && !closed) {
129
+ while (pageQueue.count < prefetch && pos > 0 && !closed) {
130
+ const readSize = Math.min(chunkSize, pos);
131
+ pos -= readSize;
132
+ const buf = Buffer.allocUnsafe(readSize);
133
+ await fd.read(buf, 0, readSize, pos);
134
+ buffer = buf.toString("utf8") + buffer;
135
+ if (pos === 0 && buffer.startsWith(delimiter)) startsWithDelimiter = true;
136
+ let idx;
137
+ while ((idx = buffer.lastIndexOf(delimiter)) !== -1) {
138
+ const line = buffer.slice(idx + delimiter.length);
139
+ buffer = buffer.slice(0, idx);
140
+ local.push(line);
141
+ while (local.length >= pageSize) {
142
+ const page = local.splice(0, pageSize);
143
+ pageQueue.push(page);
144
+ }
145
+ }
146
+ }
147
+ if (pos === 0 && !done) {
148
+ if (buffer.length > 0) local.push(buffer);
149
+ else if (startsWithDelimiter) local.push("");
150
+ buffer = "";
151
+ while (local.length > 0 && !closed) {
152
+ const page = local.slice(local.length - Math.min(pageSize, local.length));
153
+ local.length -= page.length;
154
+ pageQueue.push(page);
155
+ }
156
+ done = true;
157
+ if (fd) {
158
+ await fd.close();
159
+ fd = null;
160
+ }
161
+ pageQueue.wake();
162
+ break;
163
+ }
164
+ if (!done && !closed) await new Promise((r) => setImmediate(r));
165
+ }
166
+ } catch {
167
+ done = true;
168
+ pageQueue.wake();
169
+ try {
170
+ if (fd) {
171
+ await fd.close();
172
+ fd = null;
173
+ }
174
+ } catch {}
175
+ }
176
+ })();
177
+ function fillSync() {
52
178
  if (done || closed) return;
53
- await init();
54
- if (!fd) return;
55
- while (pageQueue.queue.length < prefetch && pos > 0) {
179
+ if (fdSync === null) return;
180
+ while (pageQueue.count < prefetch && pos > 0 && !closed) {
56
181
  const readSize = Math.min(chunkSize, pos);
57
182
  pos -= readSize;
58
183
  const buf = Buffer.allocUnsafe(readSize);
59
- await fd.read(buf, 0, readSize, pos);
60
- buffer += buf.toString("utf8");
184
+ (0, node_fs.readSync)(fdSync, buf, 0, readSize, pos);
185
+ buffer = buf.toString("utf8") + buffer;
186
+ if (pos === 0 && buffer.startsWith(delimiter)) startsWithDelimiter = true;
61
187
  let idx;
62
188
  while ((idx = buffer.lastIndexOf(delimiter)) !== -1) {
63
189
  const line = buffer.slice(idx + delimiter.length);
@@ -69,52 +195,53 @@ function createBackwardReader(filepath, options) {
69
195
  }
70
196
  }
71
197
  }
72
- if (pos === 0) {
73
- local.push(buffer);
198
+ if (pos === 0 && !done) {
199
+ if (buffer.length > 0) local.push(buffer);
200
+ else if (startsWithDelimiter) local.push("");
74
201
  buffer = "";
75
202
  while (local.length > 0) {
76
- const sliceSize = Math.min(pageSize, local.length);
77
- const page = local.splice(local.length - sliceSize, sliceSize);
203
+ const page = local.slice(local.length - Math.min(pageSize, local.length));
204
+ local.length -= page.length;
78
205
  pageQueue.push(page);
79
206
  }
80
207
  done = true;
81
- if (fd) {
82
- await fd.close();
83
- fd = null;
208
+ if (fdSync !== null) {
209
+ (0, node_fs.closeSync)(fdSync);
210
+ fdSync = null;
84
211
  }
212
+ pageQueue.wake();
85
213
  }
86
214
  }
87
215
  async function next() {
88
216
  if (closed) return null;
89
- await fill();
90
- const page = await pageQueue.shift(() => done);
91
- if (!page) return null;
92
- emittedCount += page.length;
93
- firstLine ??= page[0];
94
- lastLine = page[page.length - 1];
95
- return page;
217
+ return await pageQueue.shift(done);
218
+ }
219
+ function nextSync() {
220
+ if (closed) return null;
221
+ fillSync();
222
+ return pageQueue.shiftSync();
96
223
  }
97
224
  async function close() {
98
225
  closed = true;
99
226
  done = true;
100
- pageQueue.queue.length = 0;
227
+ pageQueue.clear();
101
228
  if (fd) {
102
- await fd.close();
229
+ try {
230
+ await fd.close();
231
+ } catch {}
103
232
  fd = null;
104
233
  }
234
+ if (fdSync !== null) {
235
+ try {
236
+ (0, node_fs.closeSync)(fdSync);
237
+ } catch {}
238
+ fdSync = null;
239
+ }
105
240
  }
106
241
  return {
107
242
  next,
243
+ nextSync,
108
244
  close,
109
- get lineCount() {
110
- return emittedCount;
111
- },
112
- get firstLine() {
113
- return firstLine;
114
- },
115
- get lastLine() {
116
- return lastLine;
117
- },
118
245
  async *[Symbol.asyncIterator]() {
119
246
  try {
120
247
  while (true) {
@@ -123,43 +250,123 @@ function createBackwardReader(filepath, options) {
123
250
  yield p;
124
251
  }
125
252
  } finally {
126
- await close().catch(() => {});
253
+ await close();
254
+ }
255
+ },
256
+ *[Symbol.iterator]() {
257
+ try {
258
+ while (true) {
259
+ const p = nextSync();
260
+ if (!p) break;
261
+ yield p;
262
+ }
263
+ } finally {
264
+ closed = true;
265
+ done = true;
266
+ pageQueue.clear();
267
+ try {
268
+ if (fdSync) (0, node_fs.closeSync)(fdSync);
269
+ } catch {}
270
+ fdSync = null;
271
+ try {
272
+ if (fd?.fd) (0, node_fs.closeSync)(fd.fd);
273
+ } catch {}
274
+ fd = null;
127
275
  }
128
276
  }
129
277
  };
130
278
  }
131
-
132
279
  //#endregion
133
280
  //#region src/reader/forward.reader.ts
134
281
  function createForwardReader(filepath, options) {
135
282
  const { chunkSize, pageSize, delimiter, prefetch } = options;
136
- const pageQueue = createPageQueue();
283
+ const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
284
+ const local = [];
137
285
  let fd = null;
286
+ let fdSync = null;
138
287
  let pos = 0;
139
288
  let size = 0;
140
289
  let buffer = "";
141
290
  let done = false;
142
291
  let closed = false;
143
- let emittedCount = 0;
144
- let firstLine = null;
145
- let lastLine = null;
146
- const local = [];
147
- async function init() {
148
- if (fd) return;
149
- fd = await (0, node_fs_promises.open)(filepath, "r");
150
- size = (await fd.stat()).size;
151
- if (size === 0) done = true;
292
+ let flushed = false;
293
+ fdSync = (0, node_fs.openSync)(filepath, "r");
294
+ size = (0, node_fs.statSync)(filepath).size;
295
+ if (size === 0) {
296
+ pageQueue.push([buffer]);
297
+ done = true;
298
+ pageQueue.wake();
152
299
  }
153
- async function fill() {
300
+ (async () => {
301
+ try {
302
+ fd = await (0, node_fs_promises.open)(filepath, "r");
303
+ size = (await fd.stat()).size;
304
+ if (size === 0) {
305
+ if (!done) {
306
+ pageQueue.push([buffer]);
307
+ done = true;
308
+ }
309
+ if (fd) {
310
+ await fd.close();
311
+ fd = null;
312
+ }
313
+ pageQueue.wake();
314
+ return;
315
+ }
316
+ while (!done && !closed) {
317
+ while (pageQueue.count < prefetch && pos < size && !closed) {
318
+ const readSize = Math.min(chunkSize, size - pos);
319
+ const buf = Buffer.allocUnsafe(readSize);
320
+ const { bytesRead } = await fd.read(buf, 0, readSize, pos);
321
+ pos += bytesRead;
322
+ buffer = buffer + buf.toString("utf8", 0, bytesRead);
323
+ let idx;
324
+ while ((idx = buffer.indexOf(delimiter)) !== -1) {
325
+ const line = buffer.slice(0, idx);
326
+ buffer = buffer.slice(idx + delimiter.length);
327
+ local.push(line);
328
+ while (local.length >= pageSize) pageQueue.push(local.splice(0, pageSize));
329
+ }
330
+ }
331
+ if (pos >= size && !flushed) {
332
+ flushed = true;
333
+ local.push(buffer.length > 0 ? buffer : "");
334
+ buffer = "";
335
+ while (local.length > 0 && !closed) {
336
+ const page = local.slice(0, pageSize);
337
+ local.length -= page.length;
338
+ pageQueue.push(page);
339
+ }
340
+ done = true;
341
+ if (fd) {
342
+ await fd.close();
343
+ fd = null;
344
+ }
345
+ pageQueue.wake();
346
+ break;
347
+ }
348
+ if (!done && !closed) await new Promise((r) => setImmediate(r));
349
+ }
350
+ } catch {
351
+ done = true;
352
+ pageQueue.wake();
353
+ try {
354
+ if (fd) {
355
+ await fd.close();
356
+ fd = null;
357
+ }
358
+ } catch {}
359
+ }
360
+ })();
361
+ function fillSync() {
154
362
  if (done || closed) return;
155
- await init();
156
- if (!fd) return;
157
- while (pageQueue.queue.length < prefetch && pos < size) {
363
+ if (fdSync === null) return;
364
+ while (pageQueue.count < prefetch && pos < size && !closed) {
158
365
  const readSize = Math.min(chunkSize, size - pos);
159
366
  const buf = Buffer.allocUnsafe(readSize);
160
- const { bytesRead } = await fd.read(buf, 0, readSize, pos);
367
+ const bytesRead = (0, node_fs.readSync)(fdSync, buf, 0, readSize, pos);
161
368
  pos += bytesRead;
162
- buffer += buf.toString("utf8", 0, bytesRead);
369
+ buffer = buffer + buf.toString("utf8", 0, bytesRead);
163
370
  let idx;
164
371
  while ((idx = buffer.indexOf(delimiter)) !== -1) {
165
372
  const line = buffer.slice(0, idx);
@@ -168,49 +375,53 @@ function createForwardReader(filepath, options) {
168
375
  while (local.length >= pageSize) pageQueue.push(local.splice(0, pageSize));
169
376
  }
170
377
  }
171
- if (pos >= size) {
172
- const parts = buffer.length > 0 ? buffer.split(delimiter) : [""];
173
- for (const line of parts) local.push(line);
378
+ if (pos >= size && !flushed) {
379
+ flushed = true;
380
+ local.push(buffer.length > 0 ? buffer : "");
174
381
  buffer = "";
175
- while (local.length > 0) pageQueue.push(local.splice(0, pageSize));
382
+ while (local.length > 0) {
383
+ const page = local.slice(0, pageSize);
384
+ local.length -= page.length;
385
+ pageQueue.push(page);
386
+ }
176
387
  done = true;
177
- if (fd) {
178
- await fd.close();
179
- fd = null;
388
+ if (fdSync !== null) {
389
+ (0, node_fs.closeSync)(fdSync);
390
+ fdSync = null;
180
391
  }
392
+ pageQueue.wake();
181
393
  }
182
394
  }
183
395
  async function next() {
184
396
  if (closed) return null;
185
- await fill();
186
- const page = await pageQueue.shift(() => done);
187
- if (!page) return null;
188
- emittedCount += page.length;
189
- firstLine ??= page[0];
190
- lastLine = page[page.length - 1];
191
- return page;
397
+ return await pageQueue.shift(done);
398
+ }
399
+ function nextSync() {
400
+ if (closed) return null;
401
+ fillSync();
402
+ return pageQueue.shiftSync();
192
403
  }
193
404
  async function close() {
194
405
  closed = true;
195
406
  done = true;
196
- pageQueue.queue.length = 0;
407
+ pageQueue.clear();
197
408
  if (fd) {
198
- await fd.close();
409
+ try {
410
+ await fd.close();
411
+ } catch {}
199
412
  fd = null;
200
413
  }
414
+ if (fdSync !== null) {
415
+ try {
416
+ (0, node_fs.closeSync)(fdSync);
417
+ } catch {}
418
+ fdSync = null;
419
+ }
201
420
  }
202
421
  return {
203
422
  next,
423
+ nextSync,
204
424
  close,
205
- get lineCount() {
206
- return emittedCount;
207
- },
208
- get firstLine() {
209
- return firstLine;
210
- },
211
- get lastLine() {
212
- return lastLine;
213
- },
214
425
  async *[Symbol.asyncIterator]() {
215
426
  try {
216
427
  while (true) {
@@ -221,27 +432,45 @@ function createForwardReader(filepath, options) {
221
432
  } finally {
222
433
  await close();
223
434
  }
435
+ },
436
+ *[Symbol.iterator]() {
437
+ try {
438
+ while (true) {
439
+ const p = nextSync();
440
+ if (!p) break;
441
+ yield p;
442
+ }
443
+ } finally {
444
+ closed = true;
445
+ done = true;
446
+ pageQueue.clear();
447
+ try {
448
+ if (fdSync !== null) (0, node_fs.closeSync)(fdSync);
449
+ } catch {}
450
+ fdSync = null;
451
+ try {
452
+ if (fd?.fd) (0, node_fs.closeSync)(fd.fd);
453
+ } catch {}
454
+ fd = null;
455
+ }
224
456
  }
225
457
  };
226
458
  }
227
-
228
459
  //#endregion
229
460
  //#region src/reader/worker.reader.ts
230
- const workerFile = typeof {} !== "undefined" ? new URL("./worker.mjs", require("url").pathToFileURL(__filename).href) : require.resolve("./worker.cjs");
461
+ const workerFile = new URL("./worker.mjs", require("url").pathToFileURL(__filename).href);
231
462
  function createWorkerReader(filepath, options) {
232
463
  const { chunkSize, pageSize, delimiter, prefetch } = options;
464
+ const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
465
+ let done = false;
466
+ let closed = false;
233
467
  const worker = new node_worker_threads.Worker(new URL(workerFile, require("url").pathToFileURL(__filename).href), { workerData: {
234
468
  filepath,
235
469
  chunkSize,
236
470
  pageSize,
237
- delimiter
471
+ delimiter,
472
+ prefetch
238
473
  } });
239
- const pageQueue = createPageQueue();
240
- let done = false;
241
- let closed = false;
242
- let emittedCount = 0;
243
- let firstLine = null;
244
- let lastLine = null;
245
474
  worker.on("message", (msg) => {
246
475
  if (msg.type === "page") pageQueue.push(msg.data);
247
476
  if (msg.type === "done") {
@@ -249,32 +478,35 @@ function createWorkerReader(filepath, options) {
249
478
  pageQueue.wake();
250
479
  }
251
480
  });
481
+ worker.on("error", () => {
482
+ done = true;
483
+ pageQueue.wake();
484
+ });
485
+ worker.on("exit", () => {
486
+ done = true;
487
+ pageQueue.wake();
488
+ });
252
489
  async function next() {
253
490
  if (closed) return null;
254
- const page = await pageQueue.shift(() => done);
255
- if (!page) return null;
256
- emittedCount += page.length;
257
- firstLine ??= page[0];
258
- lastLine = page[page.length - 1];
259
- return page;
491
+ return await pageQueue.shift(done);
492
+ }
493
+ function nextSync() {
494
+ if (closed) return null;
495
+ return pageQueue.shiftSync();
260
496
  }
261
497
  async function close() {
262
498
  closed = true;
263
499
  done = true;
500
+ pageQueue.clear();
264
501
  await worker.terminate();
265
502
  }
503
+ function tryClose() {
504
+ close().catch(() => {});
505
+ }
266
506
  return {
267
507
  next,
508
+ nextSync,
268
509
  close,
269
- get lineCount() {
270
- return emittedCount;
271
- },
272
- get firstLine() {
273
- return firstLine;
274
- },
275
- get lastLine() {
276
- return lastLine;
277
- },
278
510
  async *[Symbol.asyncIterator]() {
279
511
  try {
280
512
  while (true) {
@@ -283,19 +515,29 @@ function createWorkerReader(filepath, options) {
283
515
  yield p;
284
516
  }
285
517
  } finally {
286
- await close();
518
+ tryClose();
519
+ }
520
+ },
521
+ *[Symbol.iterator]() {
522
+ try {
523
+ while (true) {
524
+ const p = nextSync();
525
+ if (!p) break;
526
+ yield p;
527
+ }
528
+ } finally {
529
+ tryClose();
287
530
  }
288
531
  }
289
532
  };
290
533
  }
291
-
292
534
  //#endregion
293
535
  //#region src/main.ts
294
536
  function createPager(filepath, options = {}) {
295
- const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 1, backward = false, useWorker = false } = options;
537
+ const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 8, backward = false, useWorker = false } = options;
296
538
  if (!filepath) throw new Error("filepath required");
297
- if (pageSize <= 0) throw new RangeError("pageSize must be > 0");
298
- if (prefetch <= 0) throw new RangeError("prefetch must be >= 1");
539
+ if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
540
+ if (prefetch < 1) throw new RangeError("prefetch must be >= 1");
299
541
  if (backward && useWorker) throw new Error("backward not supported with useWorker");
300
542
  return useWorker ? createWorkerReader(filepath, {
301
543
  chunkSize,
@@ -314,7 +556,6 @@ function createPager(filepath, options = {}) {
314
556
  delimiter
315
557
  });
316
558
  }
317
-
318
559
  //#endregion
319
560
  exports.createPager = createPager;
320
- exports.default = createPager;
561
+ exports.default = createPager;
package/dist/main.d.cts CHANGED
@@ -9,12 +9,10 @@ interface PagerOptions extends Partial<ReaderOptions> {
9
9
  backward?: boolean;
10
10
  useWorker?: boolean;
11
11
  }
12
- interface Pager extends AsyncIterable<string[]> {
12
+ interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
13
13
  next(): Promise<string[] | null>;
14
+ nextSync(): string[] | null;
14
15
  close(): Promise<void>;
15
- readonly lineCount: number;
16
- readonly firstLine: string | null;
17
- readonly lastLine: string | null;
18
16
  }
19
17
  //#endregion
20
18
  //#region src/main.d.ts
package/dist/main.d.mts CHANGED
@@ -9,12 +9,10 @@ interface PagerOptions extends Partial<ReaderOptions> {
9
9
  backward?: boolean;
10
10
  useWorker?: boolean;
11
11
  }
12
- interface Pager extends AsyncIterable<string[]> {
12
+ interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
13
13
  next(): Promise<string[] | null>;
14
+ nextSync(): string[] | null;
14
15
  close(): Promise<void>;
15
- readonly lineCount: number;
16
- readonly firstLine: string | null;
17
- readonly lastLine: string | null;
18
16
  }
19
17
  //#endregion
20
18
  //#region src/main.d.ts