@f-o-t/ofx 2.0.0 → 2.2.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/README.md +123 -3
- package/dist/index.d.ts +269 -79
- package/dist/index.js +359 -12
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -343,7 +343,7 @@ var transactionSchema = z.object({
|
|
|
343
343
|
DTAVAIL: ofxDateSchema.optional(),
|
|
344
344
|
DTPOSTED: ofxDateSchema,
|
|
345
345
|
DTUSER: ofxDateSchema.optional(),
|
|
346
|
-
FITID: z.string(),
|
|
346
|
+
FITID: z.string().optional(),
|
|
347
347
|
MEMO: z.string().optional(),
|
|
348
348
|
NAME: z.string().optional(),
|
|
349
349
|
PAYEEID: z.string().optional(),
|
|
@@ -360,6 +360,14 @@ var accountTypeSchema = z.enum([
|
|
|
360
360
|
"CREDITLINE",
|
|
361
361
|
"CD"
|
|
362
362
|
]);
|
|
363
|
+
var extendedAccountTypeSchema = z.enum([
|
|
364
|
+
"CHECKING",
|
|
365
|
+
"SAVINGS",
|
|
366
|
+
"MONEYMRKT",
|
|
367
|
+
"CREDITLINE",
|
|
368
|
+
"CD",
|
|
369
|
+
"CREDITCARD"
|
|
370
|
+
]);
|
|
363
371
|
var bankAccountSchema = z.object({
|
|
364
372
|
ACCTID: z.string(),
|
|
365
373
|
ACCTKEY: z.string().optional(),
|
|
@@ -367,6 +375,13 @@ var bankAccountSchema = z.object({
|
|
|
367
375
|
BANKID: z.string(),
|
|
368
376
|
BRANCHID: z.string().optional()
|
|
369
377
|
});
|
|
378
|
+
var flexibleBankAccountSchema = z.object({
|
|
379
|
+
ACCTID: z.string(),
|
|
380
|
+
ACCTKEY: z.string().optional(),
|
|
381
|
+
ACCTTYPE: extendedAccountTypeSchema.optional(),
|
|
382
|
+
BANKID: z.string().optional(),
|
|
383
|
+
BRANCHID: z.string().optional()
|
|
384
|
+
});
|
|
370
385
|
var creditCardAccountSchema = z.object({
|
|
371
386
|
ACCTID: z.string(),
|
|
372
387
|
ACCTKEY: z.string().optional()
|
|
@@ -390,11 +405,14 @@ var bankStatementResponseSchema = z.object({
|
|
|
390
405
|
});
|
|
391
406
|
var creditCardStatementResponseSchema = z.object({
|
|
392
407
|
AVAILBAL: balanceSchema.optional(),
|
|
408
|
+
BANKACCTFROM: flexibleBankAccountSchema.optional(),
|
|
393
409
|
BANKTRANLIST: transactionListSchema.optional(),
|
|
394
|
-
CCACCTFROM: creditCardAccountSchema,
|
|
410
|
+
CCACCTFROM: creditCardAccountSchema.optional(),
|
|
395
411
|
CURDEF: z.string().default("USD"),
|
|
396
412
|
LEDGERBAL: balanceSchema.optional(),
|
|
397
413
|
MKTGINFO: z.string().optional()
|
|
414
|
+
}).refine((data) => data.CCACCTFROM || data.BANKACCTFROM, {
|
|
415
|
+
message: "Either CCACCTFROM or BANKACCTFROM is required"
|
|
398
416
|
});
|
|
399
417
|
var signOnResponseSchema = z.object({
|
|
400
418
|
ACCESSKEY: z.string().optional(),
|
|
@@ -405,14 +423,14 @@ var signOnResponseSchema = z.object({
|
|
|
405
423
|
STATUS: statusSchema
|
|
406
424
|
});
|
|
407
425
|
var bankStatementTransactionResponseSchema = z.object({
|
|
408
|
-
STATUS: statusSchema,
|
|
426
|
+
STATUS: statusSchema.optional(),
|
|
409
427
|
STMTRS: bankStatementResponseSchema.optional(),
|
|
410
|
-
TRNUID: z.string()
|
|
428
|
+
TRNUID: z.string().optional()
|
|
411
429
|
});
|
|
412
430
|
var creditCardStatementTransactionResponseSchema = z.object({
|
|
413
431
|
CCSTMTRS: creditCardStatementResponseSchema.optional(),
|
|
414
|
-
STATUS: statusSchema,
|
|
415
|
-
TRNUID: z.string()
|
|
432
|
+
STATUS: statusSchema.optional(),
|
|
433
|
+
TRNUID: z.string().optional()
|
|
416
434
|
});
|
|
417
435
|
var singleOrArray = (schema) => z.union([schema, z.array(schema)]).optional();
|
|
418
436
|
var bankMessageSetResponseSchema = z.object({
|
|
@@ -446,6 +464,25 @@ var ofxDocumentSchema = z.object({
|
|
|
446
464
|
});
|
|
447
465
|
|
|
448
466
|
// src/parser.ts
|
|
467
|
+
var CHARSET_MAP = {
|
|
468
|
+
"1252": "windows-1252",
|
|
469
|
+
"WINDOWS-1252": "windows-1252",
|
|
470
|
+
CP1252: "windows-1252",
|
|
471
|
+
"8859-1": "iso-8859-1",
|
|
472
|
+
"ISO-8859-1": "iso-8859-1",
|
|
473
|
+
LATIN1: "iso-8859-1",
|
|
474
|
+
"LATIN-1": "iso-8859-1",
|
|
475
|
+
"UTF-8": "utf-8",
|
|
476
|
+
UTF8: "utf-8",
|
|
477
|
+
NONE: "utf-8",
|
|
478
|
+
"": "utf-8"
|
|
479
|
+
};
|
|
480
|
+
function getEncodingFromCharset(charset) {
|
|
481
|
+
if (!charset)
|
|
482
|
+
return "utf-8";
|
|
483
|
+
const normalized = charset.toUpperCase().trim();
|
|
484
|
+
return CHARSET_MAP[normalized] ?? "windows-1252";
|
|
485
|
+
}
|
|
449
486
|
var ENTITY_MAP = {
|
|
450
487
|
"&": "&",
|
|
451
488
|
"'": "'",
|
|
@@ -513,6 +550,18 @@ function sgmlToObject(sgml) {
|
|
|
513
550
|
}
|
|
514
551
|
return result;
|
|
515
552
|
}
|
|
553
|
+
function generateFitId(txn, index) {
|
|
554
|
+
const date = String(txn.DTPOSTED ?? "");
|
|
555
|
+
const amount = String(txn.TRNAMT ?? "0");
|
|
556
|
+
const name = String(txn.NAME ?? txn.MEMO ?? "");
|
|
557
|
+
const input = `${date}:${amount}:${name}:${index}`;
|
|
558
|
+
let hash = 0;
|
|
559
|
+
for (let i = 0;i < input.length; i++) {
|
|
560
|
+
hash = (hash << 5) - hash + input.charCodeAt(i);
|
|
561
|
+
hash = hash & hash;
|
|
562
|
+
}
|
|
563
|
+
return `AUTO${Math.abs(hash).toString(16).toUpperCase().padStart(8, "0")}`;
|
|
564
|
+
}
|
|
516
565
|
function normalizeResponseArray(msgs, responseKey, statementKey) {
|
|
517
566
|
const responses = msgs[responseKey];
|
|
518
567
|
if (!responses)
|
|
@@ -522,13 +571,40 @@ function normalizeResponseArray(msgs, responseKey, statementKey) {
|
|
|
522
571
|
const tranList = stmt?.BANKTRANLIST;
|
|
523
572
|
if (tranList?.STMTTRN !== undefined) {
|
|
524
573
|
tranList.STMTTRN = toArray(tranList.STMTTRN);
|
|
574
|
+
const transactions = tranList.STMTTRN;
|
|
575
|
+
transactions.forEach((txn, idx) => {
|
|
576
|
+
if (!txn.FITID) {
|
|
577
|
+
txn.FITID = generateFitId(txn, idx);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
525
580
|
}
|
|
526
581
|
}
|
|
527
582
|
}
|
|
583
|
+
function normalizeSignOn(data) {
|
|
584
|
+
const ofx = data.OFX;
|
|
585
|
+
if (!ofx)
|
|
586
|
+
return;
|
|
587
|
+
const signonMsgs = ofx.SIGNONMSGSRSV1;
|
|
588
|
+
const sonrs = signonMsgs?.SONRS;
|
|
589
|
+
if (!sonrs)
|
|
590
|
+
return;
|
|
591
|
+
const status = sonrs.STATUS;
|
|
592
|
+
if (!status)
|
|
593
|
+
return;
|
|
594
|
+
if (!sonrs.DTSERVER && status.DTSERVER) {
|
|
595
|
+
sonrs.DTSERVER = status.DTSERVER;
|
|
596
|
+
delete status.DTSERVER;
|
|
597
|
+
}
|
|
598
|
+
if (!sonrs.LANGUAGE && status.LANGUAGE) {
|
|
599
|
+
sonrs.LANGUAGE = status.LANGUAGE;
|
|
600
|
+
delete status.LANGUAGE;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
528
603
|
function normalizeTransactions(data) {
|
|
529
604
|
const ofx = data.OFX;
|
|
530
605
|
if (!ofx)
|
|
531
606
|
return data;
|
|
607
|
+
normalizeSignOn(data);
|
|
532
608
|
const bankMsgs = ofx.BANKMSGSRSV1;
|
|
533
609
|
if (bankMsgs) {
|
|
534
610
|
normalizeResponseArray(bankMsgs, "STMTTRNRS", "STMTRS");
|
|
@@ -614,6 +690,132 @@ function parseOrThrow(content) {
|
|
|
614
690
|
}
|
|
615
691
|
return result.data;
|
|
616
692
|
}
|
|
693
|
+
function isValidUtf8(buffer) {
|
|
694
|
+
let i = 0;
|
|
695
|
+
while (i < buffer.length) {
|
|
696
|
+
const byte = buffer[i];
|
|
697
|
+
if (byte === undefined)
|
|
698
|
+
break;
|
|
699
|
+
if (byte <= 127) {
|
|
700
|
+
i++;
|
|
701
|
+
} else if ((byte & 224) === 192) {
|
|
702
|
+
const b1 = buffer[i + 1];
|
|
703
|
+
if (i + 1 >= buffer.length || b1 === undefined || (b1 & 192) !== 128)
|
|
704
|
+
return false;
|
|
705
|
+
i += 2;
|
|
706
|
+
} else if ((byte & 240) === 224) {
|
|
707
|
+
const b1 = buffer[i + 1];
|
|
708
|
+
const b2 = buffer[i + 2];
|
|
709
|
+
if (i + 2 >= buffer.length || b1 === undefined || b2 === undefined || (b1 & 192) !== 128 || (b2 & 192) !== 128)
|
|
710
|
+
return false;
|
|
711
|
+
i += 3;
|
|
712
|
+
} else if ((byte & 248) === 240) {
|
|
713
|
+
const b1 = buffer[i + 1];
|
|
714
|
+
const b2 = buffer[i + 2];
|
|
715
|
+
const b3 = buffer[i + 3];
|
|
716
|
+
if (i + 3 >= buffer.length || b1 === undefined || b2 === undefined || b3 === undefined || (b1 & 192) !== 128 || (b2 & 192) !== 128 || (b3 & 192) !== 128)
|
|
717
|
+
return false;
|
|
718
|
+
i += 4;
|
|
719
|
+
} else {
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
function hasUtf8MultiByte(buffer) {
|
|
726
|
+
for (let i = 0;i < buffer.length; i++) {
|
|
727
|
+
const byte = buffer[i];
|
|
728
|
+
if (byte !== undefined && byte > 127) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
function parseHeaderFromBuffer(buffer) {
|
|
735
|
+
const maxHeaderSize = Math.min(buffer.length, 1000);
|
|
736
|
+
const headerSection = new TextDecoder("iso-8859-1").decode(buffer.slice(0, maxHeaderSize));
|
|
737
|
+
const header = {};
|
|
738
|
+
const singleLineMatch = headerSection.match(/^(OFXHEADER:\d+.*?)(?=<OFX|<\?xml)/is);
|
|
739
|
+
if (singleLineMatch?.[1]) {
|
|
740
|
+
const headerPart = singleLineMatch[1];
|
|
741
|
+
const fieldRegex = /(\w+):([^\s<]+)/g;
|
|
742
|
+
let fieldMatch = fieldRegex.exec(headerPart);
|
|
743
|
+
while (fieldMatch !== null) {
|
|
744
|
+
const key = fieldMatch[1];
|
|
745
|
+
const value = fieldMatch[2];
|
|
746
|
+
if (key && value !== undefined) {
|
|
747
|
+
header[key] = value;
|
|
748
|
+
}
|
|
749
|
+
fieldMatch = fieldRegex.exec(headerPart);
|
|
750
|
+
}
|
|
751
|
+
} else {
|
|
752
|
+
const lines = headerSection.split(/\r?\n/);
|
|
753
|
+
for (const line of lines) {
|
|
754
|
+
const trimmed = line.trim();
|
|
755
|
+
if (trimmed.startsWith("<?xml") || trimmed.startsWith("<OFX")) {
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
const match = trimmed.match(/^(\w+):(.*)$/);
|
|
759
|
+
if (match?.[1] && match[2] !== undefined) {
|
|
760
|
+
header[match[1]] = match[2];
|
|
761
|
+
}
|
|
762
|
+
if (trimmed === "" && Object.keys(header).length > 0) {
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const parsedHeader = ofxHeaderSchema.parse(header);
|
|
768
|
+
let encoding = getEncodingFromCharset(parsedHeader.CHARSET);
|
|
769
|
+
if (encoding !== "utf-8" && hasUtf8MultiByte(buffer) && isValidUtf8(buffer)) {
|
|
770
|
+
encoding = "utf-8";
|
|
771
|
+
}
|
|
772
|
+
return { encoding, header: parsedHeader };
|
|
773
|
+
}
|
|
774
|
+
function parseBuffer(buffer) {
|
|
775
|
+
try {
|
|
776
|
+
if (!(buffer instanceof Uint8Array)) {
|
|
777
|
+
return {
|
|
778
|
+
error: new z2.ZodError([
|
|
779
|
+
{
|
|
780
|
+
code: "invalid_type",
|
|
781
|
+
expected: "object",
|
|
782
|
+
message: "Expected Uint8Array",
|
|
783
|
+
path: []
|
|
784
|
+
}
|
|
785
|
+
]),
|
|
786
|
+
success: false
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
if (buffer.length === 0) {
|
|
790
|
+
return {
|
|
791
|
+
error: new z2.ZodError([
|
|
792
|
+
{
|
|
793
|
+
code: "custom",
|
|
794
|
+
message: "Buffer cannot be empty",
|
|
795
|
+
path: []
|
|
796
|
+
}
|
|
797
|
+
]),
|
|
798
|
+
success: false
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
const { encoding } = parseHeaderFromBuffer(buffer);
|
|
802
|
+
const decoder = new TextDecoder(encoding);
|
|
803
|
+
const content = decoder.decode(buffer);
|
|
804
|
+
return parse(content);
|
|
805
|
+
} catch (err) {
|
|
806
|
+
if (err instanceof z2.ZodError) {
|
|
807
|
+
return { error: err, success: false };
|
|
808
|
+
}
|
|
809
|
+
throw err;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function parseBufferOrThrow(buffer) {
|
|
813
|
+
const result = parseBuffer(buffer);
|
|
814
|
+
if (!result.success) {
|
|
815
|
+
throw result.error;
|
|
816
|
+
}
|
|
817
|
+
return result.data;
|
|
818
|
+
}
|
|
617
819
|
// src/stream.ts
|
|
618
820
|
var ENTITY_MAP2 = {
|
|
619
821
|
"&": "&",
|
|
@@ -628,7 +830,7 @@ function decodeEntities2(text) {
|
|
|
628
830
|
return text;
|
|
629
831
|
return text.replace(ENTITY_REGEX2, (match) => ENTITY_MAP2[match] ?? match);
|
|
630
832
|
}
|
|
631
|
-
function
|
|
833
|
+
function parseHeaderFromBuffer2(buffer) {
|
|
632
834
|
const lines = buffer.split(/\r?\n/);
|
|
633
835
|
const header = {};
|
|
634
836
|
let bodyStartIndex = 0;
|
|
@@ -672,7 +874,7 @@ function tryParseBalance(obj) {
|
|
|
672
874
|
const result = balanceSchema.safeParse(obj);
|
|
673
875
|
return result.success ? result.data : null;
|
|
674
876
|
}
|
|
675
|
-
async function* parseStream(input) {
|
|
877
|
+
async function* parseStream(input, options) {
|
|
676
878
|
const state = {
|
|
677
879
|
buffer: "",
|
|
678
880
|
currentObject: {},
|
|
@@ -682,7 +884,8 @@ async function* parseStream(input) {
|
|
|
682
884
|
objectStack: [{}],
|
|
683
885
|
transactionCount: 0
|
|
684
886
|
};
|
|
685
|
-
|
|
887
|
+
let detectedEncoding = options?.encoding;
|
|
888
|
+
let decoder = new TextDecoder(detectedEncoding ?? "utf-8");
|
|
686
889
|
const tagRegex = /<(\/?)([\w.]+)>([^<]*)/g;
|
|
687
890
|
let pendingLedgerBalance;
|
|
688
891
|
let pendingAvailableBalance;
|
|
@@ -690,10 +893,14 @@ async function* parseStream(input) {
|
|
|
690
893
|
async function* processChunk(chunk, isLast = false) {
|
|
691
894
|
state.buffer += chunk;
|
|
692
895
|
if (!state.headerParsed) {
|
|
693
|
-
const headerResult =
|
|
896
|
+
const headerResult = parseHeaderFromBuffer2(state.buffer);
|
|
694
897
|
if (headerResult) {
|
|
695
898
|
state.headerParsed = true;
|
|
696
899
|
state.inHeader = false;
|
|
900
|
+
if (!detectedEncoding && headerResult.header.CHARSET) {
|
|
901
|
+
detectedEncoding = getEncodingFromCharset(headerResult.header.CHARSET);
|
|
902
|
+
decoder = new TextDecoder(detectedEncoding);
|
|
903
|
+
}
|
|
697
904
|
yield { data: headerResult.header, type: "header" };
|
|
698
905
|
state.buffer = state.buffer.slice(headerResult.bodyStart);
|
|
699
906
|
} else {
|
|
@@ -794,7 +1001,32 @@ async function* parseStream(input) {
|
|
|
794
1001
|
}
|
|
795
1002
|
if (input instanceof ReadableStream) {
|
|
796
1003
|
const reader = input.getReader();
|
|
1004
|
+
const initialChunks = [];
|
|
1005
|
+
let headerFound = false;
|
|
797
1006
|
try {
|
|
1007
|
+
while (!headerFound) {
|
|
1008
|
+
const { done, value } = await reader.read();
|
|
1009
|
+
if (done)
|
|
1010
|
+
break;
|
|
1011
|
+
initialChunks.push(value);
|
|
1012
|
+
const combined = new Uint8Array(initialChunks.reduce((sum, chunk) => sum + chunk.length, 0));
|
|
1013
|
+
let offset = 0;
|
|
1014
|
+
for (const chunk of initialChunks) {
|
|
1015
|
+
combined.set(chunk, offset);
|
|
1016
|
+
offset += chunk.length;
|
|
1017
|
+
}
|
|
1018
|
+
const headerSection = new TextDecoder("iso-8859-1").decode(combined.slice(0, Math.min(combined.length, 1000)));
|
|
1019
|
+
if (headerSection.includes("<OFX") || headerSection.includes("<?xml")) {
|
|
1020
|
+
const charsetMatch = headerSection.match(/CHARSET:(\S+)/i);
|
|
1021
|
+
if (charsetMatch && !detectedEncoding) {
|
|
1022
|
+
detectedEncoding = getEncodingFromCharset(charsetMatch[1]);
|
|
1023
|
+
decoder = new TextDecoder(detectedEncoding);
|
|
1024
|
+
}
|
|
1025
|
+
headerFound = true;
|
|
1026
|
+
const content = decoder.decode(combined);
|
|
1027
|
+
yield* processChunk(content);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
798
1030
|
while (true) {
|
|
799
1031
|
const { done, value } = await reader.read();
|
|
800
1032
|
if (done)
|
|
@@ -816,13 +1048,13 @@ async function* parseStream(input) {
|
|
|
816
1048
|
}
|
|
817
1049
|
yield { transactionCount: state.transactionCount, type: "complete" };
|
|
818
1050
|
}
|
|
819
|
-
async function parseStreamToArray(input) {
|
|
1051
|
+
async function parseStreamToArray(input, options) {
|
|
820
1052
|
const result = {
|
|
821
1053
|
accounts: [],
|
|
822
1054
|
balances: [],
|
|
823
1055
|
transactions: []
|
|
824
1056
|
};
|
|
825
|
-
for await (const event of parseStream(input)) {
|
|
1057
|
+
for await (const event of parseStream(input, options)) {
|
|
826
1058
|
switch (event.type) {
|
|
827
1059
|
case "header":
|
|
828
1060
|
result.header = event.data;
|
|
@@ -840,13 +1072,128 @@ async function parseStreamToArray(input) {
|
|
|
840
1072
|
}
|
|
841
1073
|
return result;
|
|
842
1074
|
}
|
|
1075
|
+
async function* createChunkIterable(content, chunkSize = 65536) {
|
|
1076
|
+
for (let i = 0;i < content.length; i += chunkSize) {
|
|
1077
|
+
yield content.slice(i, i + chunkSize);
|
|
1078
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
async function* parseBatchStream(files, options) {
|
|
1082
|
+
let totalTransactions = 0;
|
|
1083
|
+
let errorCount = 0;
|
|
1084
|
+
for (let i = 0;i < files.length; i++) {
|
|
1085
|
+
const file = files[i];
|
|
1086
|
+
if (!file)
|
|
1087
|
+
continue;
|
|
1088
|
+
yield { type: "file_start", fileIndex: i, filename: file.filename };
|
|
1089
|
+
try {
|
|
1090
|
+
let fileTransactionCount = 0;
|
|
1091
|
+
const headerSection = new TextDecoder("iso-8859-1").decode(file.buffer.slice(0, Math.min(file.buffer.length, 1000)));
|
|
1092
|
+
const charsetMatch = headerSection.match(/CHARSET:(\S+)/i);
|
|
1093
|
+
const encoding = charsetMatch ? getEncodingFromCharset(charsetMatch[1]) : options?.encoding ?? "utf-8";
|
|
1094
|
+
const decoder = new TextDecoder(encoding);
|
|
1095
|
+
const content = decoder.decode(file.buffer);
|
|
1096
|
+
const chunkIterable = createChunkIterable(content);
|
|
1097
|
+
for await (const event of parseStream(chunkIterable, options)) {
|
|
1098
|
+
switch (event.type) {
|
|
1099
|
+
case "header":
|
|
1100
|
+
yield { type: "header", fileIndex: i, data: event.data };
|
|
1101
|
+
break;
|
|
1102
|
+
case "transaction":
|
|
1103
|
+
yield { type: "transaction", fileIndex: i, data: event.data };
|
|
1104
|
+
fileTransactionCount++;
|
|
1105
|
+
break;
|
|
1106
|
+
case "account":
|
|
1107
|
+
yield { type: "account", fileIndex: i, data: event.data };
|
|
1108
|
+
break;
|
|
1109
|
+
case "balance":
|
|
1110
|
+
yield { type: "balance", fileIndex: i, data: event.data };
|
|
1111
|
+
break;
|
|
1112
|
+
case "complete":
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
totalTransactions += fileTransactionCount;
|
|
1117
|
+
yield {
|
|
1118
|
+
type: "file_complete",
|
|
1119
|
+
fileIndex: i,
|
|
1120
|
+
filename: file.filename,
|
|
1121
|
+
transactionCount: fileTransactionCount
|
|
1122
|
+
};
|
|
1123
|
+
} catch (err) {
|
|
1124
|
+
errorCount++;
|
|
1125
|
+
yield {
|
|
1126
|
+
type: "file_error",
|
|
1127
|
+
fileIndex: i,
|
|
1128
|
+
filename: file.filename,
|
|
1129
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1133
|
+
}
|
|
1134
|
+
yield {
|
|
1135
|
+
type: "batch_complete",
|
|
1136
|
+
totalFiles: files.length,
|
|
1137
|
+
totalTransactions,
|
|
1138
|
+
errorCount
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
async function parseBatchStreamToArray(files, options) {
|
|
1142
|
+
const results = files.map((file, index) => ({
|
|
1143
|
+
fileIndex: index,
|
|
1144
|
+
filename: file.filename,
|
|
1145
|
+
transactions: [],
|
|
1146
|
+
accounts: [],
|
|
1147
|
+
balances: []
|
|
1148
|
+
}));
|
|
1149
|
+
for await (const event of parseBatchStream(files, options)) {
|
|
1150
|
+
switch (event.type) {
|
|
1151
|
+
case "header": {
|
|
1152
|
+
const result = results[event.fileIndex];
|
|
1153
|
+
if (result)
|
|
1154
|
+
result.header = event.data;
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
case "transaction": {
|
|
1158
|
+
const result = results[event.fileIndex];
|
|
1159
|
+
if (result)
|
|
1160
|
+
result.transactions.push(event.data);
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
case "account": {
|
|
1164
|
+
const result = results[event.fileIndex];
|
|
1165
|
+
if (result)
|
|
1166
|
+
result.accounts.push(event.data);
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
case "balance": {
|
|
1170
|
+
const result = results[event.fileIndex];
|
|
1171
|
+
if (result)
|
|
1172
|
+
result.balances.push(event.data);
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
case "file_error": {
|
|
1176
|
+
const result = results[event.fileIndex];
|
|
1177
|
+
if (result)
|
|
1178
|
+
result.error = event.error;
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
return results;
|
|
1184
|
+
}
|
|
843
1185
|
export {
|
|
844
1186
|
parseStreamToArray,
|
|
845
1187
|
parseStream,
|
|
846
1188
|
parseOrThrow,
|
|
1189
|
+
parseBufferOrThrow,
|
|
1190
|
+
parseBuffer,
|
|
1191
|
+
parseBatchStreamToArray,
|
|
1192
|
+
parseBatchStream,
|
|
847
1193
|
parse,
|
|
848
1194
|
getTransactions,
|
|
849
1195
|
getSignOnInfo,
|
|
1196
|
+
getEncodingFromCharset,
|
|
850
1197
|
getBalance,
|
|
851
1198
|
getAccountInfo,
|
|
852
1199
|
generateHeader,
|
package/package.json
CHANGED
|
@@ -51,14 +51,13 @@
|
|
|
51
51
|
"build": "bunup",
|
|
52
52
|
"check": "biome check --write .",
|
|
53
53
|
"dev": "bunup --watch",
|
|
54
|
-
"publish": "bunx npm publish",
|
|
55
54
|
"release": "bumpp --commit --push --tag",
|
|
56
55
|
"test": "bun test",
|
|
57
56
|
"test:coverage": "bun test --coverage",
|
|
58
57
|
"test:watch": "bun test --watch",
|
|
59
|
-
"typecheck": "tsc
|
|
58
|
+
"typecheck": "tsc "
|
|
60
59
|
},
|
|
61
60
|
"type": "module",
|
|
62
61
|
"types": "./dist/index.d.ts",
|
|
63
|
-
"version": "2.
|
|
62
|
+
"version": "2.2.0"
|
|
64
63
|
}
|