@f-o-t/ofx 2.2.0 → 2.3.1
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 +81 -22
- package/dist/index.d.ts +146 -60
- package/dist/index.js +262 -210
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -464,36 +464,95 @@ type StreamEvent =
|
|
|
464
464
|
| { type: "complete"; transactionCount: number };
|
|
465
465
|
```
|
|
466
466
|
|
|
467
|
-
## Schemas
|
|
467
|
+
## Validation & Schemas
|
|
468
468
|
|
|
469
|
-
All Zod schemas
|
|
469
|
+
All parsing and generation functions use Zod schemas for runtime validation, ensuring type safety and data integrity.
|
|
470
|
+
|
|
471
|
+
### Input Validation for Generation
|
|
472
|
+
|
|
473
|
+
When generating OFX files, you can validate your inputs using the exported schemas:
|
|
470
474
|
|
|
471
475
|
```typescript
|
|
472
|
-
import {
|
|
476
|
+
import {
|
|
477
|
+
generateBankStatement,
|
|
478
|
+
generateBankStatementOptionsSchema,
|
|
479
|
+
type GenerateBankStatementOptions,
|
|
480
|
+
} from "@fot/ofx";
|
|
481
|
+
|
|
482
|
+
// Validate options before generating
|
|
483
|
+
const options: GenerateBankStatementOptions = {
|
|
484
|
+
bankId: "123456",
|
|
485
|
+
accountId: "987654321",
|
|
486
|
+
accountType: "CHECKING",
|
|
487
|
+
currency: "USD",
|
|
488
|
+
startDate: new Date("2025-01-01"),
|
|
489
|
+
endDate: new Date("2025-01-31"),
|
|
490
|
+
transactions: [],
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// Runtime validation
|
|
494
|
+
const validatedOptions = generateBankStatementOptionsSchema.parse(options);
|
|
495
|
+
const statement = generateBankStatement(validatedOptions);
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Available Schemas
|
|
473
499
|
|
|
474
|
-
|
|
500
|
+
All Zod schemas are exported for custom validation and extension:
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
import {
|
|
504
|
+
transactionSchema,
|
|
505
|
+
bankAccountSchema,
|
|
506
|
+
ofxDocumentSchema,
|
|
507
|
+
} from "@fot/ofx";
|
|
508
|
+
|
|
509
|
+
// Extend schemas for custom validation
|
|
510
|
+
const customTransactionSchema = transactionSchema.extend({
|
|
475
511
|
customField: z.string(),
|
|
476
512
|
});
|
|
477
513
|
```
|
|
478
514
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
- `
|
|
482
|
-
- `
|
|
483
|
-
- `
|
|
484
|
-
- `
|
|
485
|
-
- `
|
|
486
|
-
- `
|
|
487
|
-
- `
|
|
488
|
-
- `
|
|
489
|
-
- `
|
|
490
|
-
- `
|
|
491
|
-
- `
|
|
492
|
-
- `
|
|
493
|
-
- `
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
- `
|
|
515
|
+
**Parsing Schemas:**
|
|
516
|
+
- `ofxDocumentSchema` - Complete OFX document
|
|
517
|
+
- `ofxHeaderSchema` - OFX file header
|
|
518
|
+
- `ofxResponseSchema` - OFX response body
|
|
519
|
+
- `transactionSchema` - Individual transaction
|
|
520
|
+
- `transactionTypeSchema` - Transaction type enum
|
|
521
|
+
- `transactionListSchema` - List of transactions
|
|
522
|
+
- `bankAccountSchema` - Bank account information
|
|
523
|
+
- `creditCardAccountSchema` - Credit card account information
|
|
524
|
+
- `accountTypeSchema` - Account type enum
|
|
525
|
+
- `balanceSchema` - Balance information
|
|
526
|
+
- `statusSchema` - Status response
|
|
527
|
+
- `financialInstitutionSchema` - Financial institution info
|
|
528
|
+
- `signOnResponseSchema` - Sign-on response
|
|
529
|
+
- `ofxDateSchema` - OFX date with timezone
|
|
530
|
+
|
|
531
|
+
**Generation Schemas:**
|
|
532
|
+
- `generateHeaderOptionsSchema` - OFX header generation options
|
|
533
|
+
- `generateTransactionInputSchema` - Transaction input for generation
|
|
534
|
+
- `generateBankStatementOptionsSchema` - Bank statement generation options
|
|
535
|
+
- `generateCreditCardStatementOptionsSchema` - Credit card statement generation options
|
|
536
|
+
|
|
537
|
+
## Security
|
|
538
|
+
|
|
539
|
+
This library includes several security features to protect against malicious OFX files:
|
|
540
|
+
|
|
541
|
+
### Prototype Pollution Protection
|
|
542
|
+
|
|
543
|
+
The SGML parser is protected against prototype pollution attacks. Malicious OFX files attempting to inject `__proto__`, `constructor`, or `prototype` tags are safely ignored, preventing potential remote code execution.
|
|
544
|
+
|
|
545
|
+
### Input Validation
|
|
546
|
+
|
|
547
|
+
All parsing functions validate input data against strict Zod schemas, rejecting malformed or invalid OFX data before processing. This prevents:
|
|
548
|
+
- Type confusion attacks
|
|
549
|
+
- Invalid date/number formats
|
|
550
|
+
- Missing required fields
|
|
551
|
+
- Unexpected data structures
|
|
552
|
+
|
|
553
|
+
### Safe Entity Decoding
|
|
554
|
+
|
|
555
|
+
HTML entities in OFX text fields are decoded using a whitelist approach, preventing XSS-style attacks through crafted entity sequences
|
|
497
556
|
|
|
498
557
|
## Performance
|
|
499
558
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1616,74 +1616,160 @@ declare function getTransactions(document: OFXDocument): OFXTransaction[];
|
|
|
1616
1616
|
declare function getAccountInfo(document: OFXDocument): (OFXBankAccount | OFXCreditCardAccount)[];
|
|
1617
1617
|
declare function getBalance(document: OFXDocument): BalanceInfo[];
|
|
1618
1618
|
declare function getSignOnInfo(document: OFXDocument): OFXSignOnResponse;
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1619
|
+
import { z as z2 } from "zod";
|
|
1620
|
+
declare const generateHeaderOptionsSchema: z2.ZodOptional<z2.ZodObject<{
|
|
1621
|
+
version: z2.ZodOptional<z2.ZodString>;
|
|
1622
|
+
encoding: z2.ZodOptional<z2.ZodString>;
|
|
1623
|
+
charset: z2.ZodOptional<z2.ZodString>;
|
|
1624
|
+
}, z2.core.$strip>>;
|
|
1625
|
+
type GenerateHeaderOptions = z2.infer<typeof generateHeaderOptionsSchema>;
|
|
1624
1626
|
declare function generateHeader(options?: GenerateHeaderOptions): string;
|
|
1625
|
-
|
|
1626
|
-
type:
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1627
|
+
declare const generateTransactionInputSchema: z2.ZodObject<{
|
|
1628
|
+
type: z2.ZodEnum<{
|
|
1629
|
+
CREDIT: "CREDIT";
|
|
1630
|
+
DEBIT: "DEBIT";
|
|
1631
|
+
INT: "INT";
|
|
1632
|
+
DIV: "DIV";
|
|
1633
|
+
FEE: "FEE";
|
|
1634
|
+
SRVCHG: "SRVCHG";
|
|
1635
|
+
DEP: "DEP";
|
|
1636
|
+
ATM: "ATM";
|
|
1637
|
+
POS: "POS";
|
|
1638
|
+
XFER: "XFER";
|
|
1639
|
+
CHECK: "CHECK";
|
|
1640
|
+
PAYMENT: "PAYMENT";
|
|
1641
|
+
CASH: "CASH";
|
|
1642
|
+
DIRECTDEP: "DIRECTDEP";
|
|
1643
|
+
DIRECTDEBIT: "DIRECTDEBIT";
|
|
1644
|
+
REPEATPMT: "REPEATPMT";
|
|
1645
|
+
HOLD: "HOLD";
|
|
1646
|
+
OTHER: "OTHER";
|
|
1647
|
+
}>;
|
|
1648
|
+
datePosted: z2.ZodDate;
|
|
1649
|
+
amount: z2.ZodNumber;
|
|
1650
|
+
fitId: z2.ZodString;
|
|
1651
|
+
name: z2.ZodOptional<z2.ZodString>;
|
|
1652
|
+
memo: z2.ZodOptional<z2.ZodString>;
|
|
1653
|
+
checkNum: z2.ZodOptional<z2.ZodString>;
|
|
1654
|
+
refNum: z2.ZodOptional<z2.ZodString>;
|
|
1655
|
+
}, z2.core.$strip>;
|
|
1656
|
+
type GenerateTransactionInput = z2.infer<typeof generateTransactionInputSchema>;
|
|
1657
|
+
declare const generateBankStatementOptionsSchema: z2.ZodObject<{
|
|
1658
|
+
bankId: z2.ZodString;
|
|
1659
|
+
accountId: z2.ZodString;
|
|
1660
|
+
accountType: z2.ZodEnum<{
|
|
1661
|
+
CHECKING: "CHECKING";
|
|
1662
|
+
SAVINGS: "SAVINGS";
|
|
1663
|
+
MONEYMRKT: "MONEYMRKT";
|
|
1664
|
+
CREDITLINE: "CREDITLINE";
|
|
1665
|
+
CD: "CD";
|
|
1666
|
+
}>;
|
|
1667
|
+
currency: z2.ZodString;
|
|
1668
|
+
startDate: z2.ZodDate;
|
|
1669
|
+
endDate: z2.ZodDate;
|
|
1670
|
+
transactions: z2.ZodArray<z2.ZodObject<{
|
|
1671
|
+
type: z2.ZodEnum<{
|
|
1672
|
+
CREDIT: "CREDIT";
|
|
1673
|
+
DEBIT: "DEBIT";
|
|
1674
|
+
INT: "INT";
|
|
1675
|
+
DIV: "DIV";
|
|
1676
|
+
FEE: "FEE";
|
|
1677
|
+
SRVCHG: "SRVCHG";
|
|
1678
|
+
DEP: "DEP";
|
|
1679
|
+
ATM: "ATM";
|
|
1680
|
+
POS: "POS";
|
|
1681
|
+
XFER: "XFER";
|
|
1682
|
+
CHECK: "CHECK";
|
|
1683
|
+
PAYMENT: "PAYMENT";
|
|
1684
|
+
CASH: "CASH";
|
|
1685
|
+
DIRECTDEP: "DIRECTDEP";
|
|
1686
|
+
DIRECTDEBIT: "DIRECTDEBIT";
|
|
1687
|
+
REPEATPMT: "REPEATPMT";
|
|
1688
|
+
HOLD: "HOLD";
|
|
1689
|
+
OTHER: "OTHER";
|
|
1690
|
+
}>;
|
|
1691
|
+
datePosted: z2.ZodDate;
|
|
1692
|
+
amount: z2.ZodNumber;
|
|
1693
|
+
fitId: z2.ZodString;
|
|
1694
|
+
name: z2.ZodOptional<z2.ZodString>;
|
|
1695
|
+
memo: z2.ZodOptional<z2.ZodString>;
|
|
1696
|
+
checkNum: z2.ZodOptional<z2.ZodString>;
|
|
1697
|
+
refNum: z2.ZodOptional<z2.ZodString>;
|
|
1698
|
+
}, z2.core.$strip>>;
|
|
1699
|
+
ledgerBalance: z2.ZodOptional<z2.ZodObject<{
|
|
1700
|
+
amount: z2.ZodNumber;
|
|
1701
|
+
asOfDate: z2.ZodDate;
|
|
1702
|
+
}, z2.core.$strip>>;
|
|
1703
|
+
availableBalance: z2.ZodOptional<z2.ZodObject<{
|
|
1704
|
+
amount: z2.ZodNumber;
|
|
1705
|
+
asOfDate: z2.ZodDate;
|
|
1706
|
+
}, z2.core.$strip>>;
|
|
1707
|
+
financialInstitution: z2.ZodOptional<z2.ZodObject<{
|
|
1708
|
+
org: z2.ZodOptional<z2.ZodString>;
|
|
1709
|
+
fid: z2.ZodOptional<z2.ZodString>;
|
|
1710
|
+
}, z2.core.$strip>>;
|
|
1711
|
+
language: z2.ZodOptional<z2.ZodString>;
|
|
1712
|
+
}, z2.core.$strip>;
|
|
1713
|
+
type GenerateBankStatementOptions = z2.infer<typeof generateBankStatementOptionsSchema>;
|
|
1657
1714
|
declare function generateBankStatement(options: GenerateBankStatementOptions): string;
|
|
1658
|
-
|
|
1659
|
-
accountId:
|
|
1660
|
-
currency:
|
|
1661
|
-
startDate:
|
|
1662
|
-
endDate:
|
|
1663
|
-
transactions:
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1715
|
+
declare const generateCreditCardStatementOptionsSchema: z2.ZodObject<{
|
|
1716
|
+
accountId: z2.ZodString;
|
|
1717
|
+
currency: z2.ZodString;
|
|
1718
|
+
startDate: z2.ZodDate;
|
|
1719
|
+
endDate: z2.ZodDate;
|
|
1720
|
+
transactions: z2.ZodArray<z2.ZodObject<{
|
|
1721
|
+
type: z2.ZodEnum<{
|
|
1722
|
+
CREDIT: "CREDIT";
|
|
1723
|
+
DEBIT: "DEBIT";
|
|
1724
|
+
INT: "INT";
|
|
1725
|
+
DIV: "DIV";
|
|
1726
|
+
FEE: "FEE";
|
|
1727
|
+
SRVCHG: "SRVCHG";
|
|
1728
|
+
DEP: "DEP";
|
|
1729
|
+
ATM: "ATM";
|
|
1730
|
+
POS: "POS";
|
|
1731
|
+
XFER: "XFER";
|
|
1732
|
+
CHECK: "CHECK";
|
|
1733
|
+
PAYMENT: "PAYMENT";
|
|
1734
|
+
CASH: "CASH";
|
|
1735
|
+
DIRECTDEP: "DIRECTDEP";
|
|
1736
|
+
DIRECTDEBIT: "DIRECTDEBIT";
|
|
1737
|
+
REPEATPMT: "REPEATPMT";
|
|
1738
|
+
HOLD: "HOLD";
|
|
1739
|
+
OTHER: "OTHER";
|
|
1740
|
+
}>;
|
|
1741
|
+
datePosted: z2.ZodDate;
|
|
1742
|
+
amount: z2.ZodNumber;
|
|
1743
|
+
fitId: z2.ZodString;
|
|
1744
|
+
name: z2.ZodOptional<z2.ZodString>;
|
|
1745
|
+
memo: z2.ZodOptional<z2.ZodString>;
|
|
1746
|
+
checkNum: z2.ZodOptional<z2.ZodString>;
|
|
1747
|
+
refNum: z2.ZodOptional<z2.ZodString>;
|
|
1748
|
+
}, z2.core.$strip>>;
|
|
1749
|
+
ledgerBalance: z2.ZodOptional<z2.ZodObject<{
|
|
1750
|
+
amount: z2.ZodNumber;
|
|
1751
|
+
asOfDate: z2.ZodDate;
|
|
1752
|
+
}, z2.core.$strip>>;
|
|
1753
|
+
availableBalance: z2.ZodOptional<z2.ZodObject<{
|
|
1754
|
+
amount: z2.ZodNumber;
|
|
1755
|
+
asOfDate: z2.ZodDate;
|
|
1756
|
+
}, z2.core.$strip>>;
|
|
1757
|
+
financialInstitution: z2.ZodOptional<z2.ZodObject<{
|
|
1758
|
+
org: z2.ZodOptional<z2.ZodString>;
|
|
1759
|
+
fid: z2.ZodOptional<z2.ZodString>;
|
|
1760
|
+
}, z2.core.$strip>>;
|
|
1761
|
+
language: z2.ZodOptional<z2.ZodString>;
|
|
1762
|
+
}, z2.core.$strip>;
|
|
1763
|
+
type GenerateCreditCardStatementOptions = z2.infer<typeof generateCreditCardStatementOptionsSchema>;
|
|
1678
1764
|
declare function generateCreditCardStatement(options: GenerateCreditCardStatementOptions): string;
|
|
1679
|
-
import { z as
|
|
1765
|
+
import { z as z3 } from "zod";
|
|
1680
1766
|
declare function getEncodingFromCharset(charset?: string): string;
|
|
1681
1767
|
type ParseResult<T> = {
|
|
1682
1768
|
success: true;
|
|
1683
1769
|
data: T;
|
|
1684
1770
|
} | {
|
|
1685
1771
|
success: false;
|
|
1686
|
-
error:
|
|
1772
|
+
error: z3.ZodError;
|
|
1687
1773
|
};
|
|
1688
1774
|
declare function parse(content: string): ParseResult<OFXDocument>;
|
|
1689
1775
|
declare function parseOrThrow(content: string): OFXDocument;
|
|
@@ -1797,4 +1883,4 @@ declare function formatOfxDate(date: Date, timezone?: {
|
|
|
1797
1883
|
offset: number;
|
|
1798
1884
|
name: string;
|
|
1799
1885
|
}): string;
|
|
1800
|
-
export { parseStreamToArray, parseStream, parseOrThrow, parseBufferOrThrow, parseBuffer, parseBatchStreamToArray, parseBatchStream, parse, getTransactions, getSignOnInfo, getEncodingFromCharset, getBalance, getAccountInfo, generateHeader, generateCreditCardStatement, generateBankStatement, formatOfxDate, StreamOptions, StreamEvent, ParseResult, OFXTransactionType, OFXTransactionList, OFXTransaction, OFXStatus, OFXSignOnResponse, OFXSignOnMessageSetResponse, OFXResponse, OFXHeader, OFXFinancialInstitution, OFXDocument, OFXDate, OFXCreditCardStatementTransactionResponse, OFXCreditCardStatementResponse, OFXCreditCardMessageSetResponse, OFXCreditCardAccount, OFXBankStatementTransactionResponse, OFXBankStatementResponse, OFXBankMessageSetResponse, OFXBankAccount, OFXBalance, OFXAccountType, GenerateTransactionInput, GenerateHeaderOptions, GenerateCreditCardStatementOptions, GenerateBankStatementOptions, BatchStreamEvent, BatchParsedFile, BatchFileInput, BalanceInfo };
|
|
1886
|
+
export { parseStreamToArray, parseStream, parseOrThrow, parseBufferOrThrow, parseBuffer, parseBatchStreamToArray, parseBatchStream, parse, getTransactions, getSignOnInfo, getEncodingFromCharset, getBalance, getAccountInfo, generateTransactionInputSchema, generateHeaderOptionsSchema, generateHeader, generateCreditCardStatementOptionsSchema, generateCreditCardStatement, generateBankStatementOptionsSchema, generateBankStatement, formatOfxDate, StreamOptions, StreamEvent, ParseResult, OFXTransactionType, OFXTransactionList, OFXTransaction, OFXStatus, OFXSignOnResponse, OFXSignOnMessageSetResponse, OFXResponse, OFXHeader, OFXFinancialInstitution, OFXDocument, OFXDate, OFXCreditCardStatementTransactionResponse, OFXCreditCardStatementResponse, OFXCreditCardMessageSetResponse, OFXCreditCardAccount, OFXBankStatementTransactionResponse, OFXBankStatementResponse, OFXBankMessageSetResponse, OFXBankAccount, OFXBalance, OFXAccountType, GenerateTransactionInput, GenerateHeaderOptions, GenerateCreditCardStatementOptions, GenerateBankStatementOptions, BatchStreamEvent, BatchParsedFile, BatchFileInput, BalanceInfo };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
// src/utils.ts
|
|
2
2
|
var toArray = (value) => Array.isArray(value) ? value : [value];
|
|
3
|
+
var ENTITY_MAP = {
|
|
4
|
+
"&": "&",
|
|
5
|
+
"'": "'",
|
|
6
|
+
">": ">",
|
|
7
|
+
"<": "<",
|
|
8
|
+
""": '"'
|
|
9
|
+
};
|
|
10
|
+
var ENTITY_REGEX = /&(?:amp|lt|gt|quot|apos);/g;
|
|
11
|
+
function decodeEntities(text) {
|
|
12
|
+
if (!text.includes("&"))
|
|
13
|
+
return text;
|
|
14
|
+
return text.replace(ENTITY_REGEX, (match) => ENTITY_MAP[match] ?? match);
|
|
15
|
+
}
|
|
3
16
|
var pad = (n, width = 2) => n.toString().padStart(width, "0");
|
|
4
17
|
function escapeOfxText(text) {
|
|
5
18
|
if (!text.includes("&") && !text.includes("<") && !text.includes(">")) {
|
|
@@ -95,182 +108,19 @@ function getSignOnInfo(document) {
|
|
|
95
108
|
return document.OFX.SIGNONMSGSRSV1.SONRS;
|
|
96
109
|
}
|
|
97
110
|
// src/generator.ts
|
|
98
|
-
function generateHeader(options) {
|
|
99
|
-
const version = options?.version ?? "100";
|
|
100
|
-
const encoding = options?.encoding ?? "USASCII";
|
|
101
|
-
const charset = options?.charset ?? "1252";
|
|
102
|
-
return [
|
|
103
|
-
"OFXHEADER:100",
|
|
104
|
-
"DATA:OFXSGML",
|
|
105
|
-
`VERSION:${version}`,
|
|
106
|
-
"SECURITY:NONE",
|
|
107
|
-
`ENCODING:${encoding}`,
|
|
108
|
-
`CHARSET:${charset}`,
|
|
109
|
-
"COMPRESSION:NONE",
|
|
110
|
-
"OLDFILEUID:NONE",
|
|
111
|
-
"NEWFILEUID:NONE",
|
|
112
|
-
""
|
|
113
|
-
].join(`
|
|
114
|
-
`);
|
|
115
|
-
}
|
|
116
|
-
function generateTransaction(trn) {
|
|
117
|
-
const lines = [
|
|
118
|
-
"<STMTTRN>",
|
|
119
|
-
`<TRNTYPE>${trn.type}`,
|
|
120
|
-
`<DTPOSTED>${formatOfxDate(trn.datePosted)}`,
|
|
121
|
-
`<TRNAMT>${formatAmount(trn.amount)}`,
|
|
122
|
-
`<FITID>${escapeOfxText(trn.fitId)}`
|
|
123
|
-
];
|
|
124
|
-
if (trn.name) {
|
|
125
|
-
lines.push(`<NAME>${escapeOfxText(trn.name)}`);
|
|
126
|
-
}
|
|
127
|
-
if (trn.memo) {
|
|
128
|
-
lines.push(`<MEMO>${escapeOfxText(trn.memo)}`);
|
|
129
|
-
}
|
|
130
|
-
if (trn.checkNum) {
|
|
131
|
-
lines.push(`<CHECKNUM>${escapeOfxText(trn.checkNum)}`);
|
|
132
|
-
}
|
|
133
|
-
if (trn.refNum) {
|
|
134
|
-
lines.push(`<REFNUM>${escapeOfxText(trn.refNum)}`);
|
|
135
|
-
}
|
|
136
|
-
lines.push("</STMTTRN>");
|
|
137
|
-
return lines.join(`
|
|
138
|
-
`);
|
|
139
|
-
}
|
|
140
|
-
function generateBankStatement(options) {
|
|
141
|
-
const parts = [generateHeader()];
|
|
142
|
-
const serverDate = formatOfxDate(new Date);
|
|
143
|
-
const language = options.language ?? "POR";
|
|
144
|
-
parts.push(`<OFX>
|
|
145
|
-
<SIGNONMSGSRSV1>
|
|
146
|
-
<SONRS>
|
|
147
|
-
<STATUS>
|
|
148
|
-
<CODE>0
|
|
149
|
-
<SEVERITY>INFO
|
|
150
|
-
</STATUS>
|
|
151
|
-
<DTSERVER>${serverDate}
|
|
152
|
-
<LANGUAGE>${language}`);
|
|
153
|
-
if (options.financialInstitution) {
|
|
154
|
-
parts.push("<FI>");
|
|
155
|
-
if (options.financialInstitution.org) {
|
|
156
|
-
parts.push(`<ORG>${escapeOfxText(options.financialInstitution.org)}`);
|
|
157
|
-
}
|
|
158
|
-
if (options.financialInstitution.fid) {
|
|
159
|
-
parts.push(`<FID>${escapeOfxText(options.financialInstitution.fid)}`);
|
|
160
|
-
}
|
|
161
|
-
parts.push("</FI>");
|
|
162
|
-
}
|
|
163
|
-
parts.push(`</SONRS>
|
|
164
|
-
</SIGNONMSGSRSV1>
|
|
165
|
-
<BANKMSGSRSV1>
|
|
166
|
-
<STMTTRNRS>
|
|
167
|
-
<TRNUID>0
|
|
168
|
-
<STATUS>
|
|
169
|
-
<CODE>0
|
|
170
|
-
<SEVERITY>INFO
|
|
171
|
-
</STATUS>
|
|
172
|
-
<STMTRS>
|
|
173
|
-
<CURDEF>${options.currency}
|
|
174
|
-
<BANKACCTFROM>
|
|
175
|
-
<BANKID>${escapeOfxText(options.bankId)}
|
|
176
|
-
<ACCTID>${escapeOfxText(options.accountId)}
|
|
177
|
-
<ACCTTYPE>${options.accountType}
|
|
178
|
-
</BANKACCTFROM>
|
|
179
|
-
<BANKTRANLIST>
|
|
180
|
-
<DTSTART>${formatOfxDate(options.startDate)}
|
|
181
|
-
<DTEND>${formatOfxDate(options.endDate)}`);
|
|
182
|
-
for (const trn of options.transactions) {
|
|
183
|
-
parts.push(generateTransaction(trn));
|
|
184
|
-
}
|
|
185
|
-
parts.push("</BANKTRANLIST>");
|
|
186
|
-
if (options.ledgerBalance) {
|
|
187
|
-
parts.push(`<LEDGERBAL>
|
|
188
|
-
<BALAMT>${formatAmount(options.ledgerBalance.amount)}
|
|
189
|
-
<DTASOF>${formatOfxDate(options.ledgerBalance.asOfDate)}
|
|
190
|
-
</LEDGERBAL>`);
|
|
191
|
-
}
|
|
192
|
-
if (options.availableBalance) {
|
|
193
|
-
parts.push(`<AVAILBAL>
|
|
194
|
-
<BALAMT>${formatAmount(options.availableBalance.amount)}
|
|
195
|
-
<DTASOF>${formatOfxDate(options.availableBalance.asOfDate)}
|
|
196
|
-
</AVAILBAL>`);
|
|
197
|
-
}
|
|
198
|
-
parts.push(`</STMTRS>
|
|
199
|
-
</STMTTRNRS>
|
|
200
|
-
</BANKMSGSRSV1>
|
|
201
|
-
</OFX>`);
|
|
202
|
-
return parts.join(`
|
|
203
|
-
`);
|
|
204
|
-
}
|
|
205
|
-
function generateCreditCardStatement(options) {
|
|
206
|
-
const parts = [generateHeader()];
|
|
207
|
-
const serverDate = formatOfxDate(new Date);
|
|
208
|
-
const language = options.language ?? "POR";
|
|
209
|
-
parts.push(`<OFX>
|
|
210
|
-
<SIGNONMSGSRSV1>
|
|
211
|
-
<SONRS>
|
|
212
|
-
<STATUS>
|
|
213
|
-
<CODE>0
|
|
214
|
-
<SEVERITY>INFO
|
|
215
|
-
</STATUS>
|
|
216
|
-
<DTSERVER>${serverDate}
|
|
217
|
-
<LANGUAGE>${language}`);
|
|
218
|
-
if (options.financialInstitution) {
|
|
219
|
-
parts.push("<FI>");
|
|
220
|
-
if (options.financialInstitution.org) {
|
|
221
|
-
parts.push(`<ORG>${escapeOfxText(options.financialInstitution.org)}`);
|
|
222
|
-
}
|
|
223
|
-
if (options.financialInstitution.fid) {
|
|
224
|
-
parts.push(`<FID>${escapeOfxText(options.financialInstitution.fid)}`);
|
|
225
|
-
}
|
|
226
|
-
parts.push("</FI>");
|
|
227
|
-
}
|
|
228
|
-
parts.push(`</SONRS>
|
|
229
|
-
</SIGNONMSGSRSV1>
|
|
230
|
-
<CREDITCARDMSGSRSV1>
|
|
231
|
-
<CCSTMTTRNRS>
|
|
232
|
-
<TRNUID>0
|
|
233
|
-
<STATUS>
|
|
234
|
-
<CODE>0
|
|
235
|
-
<SEVERITY>INFO
|
|
236
|
-
</STATUS>
|
|
237
|
-
<CCSTMTRS>
|
|
238
|
-
<CURDEF>${options.currency}
|
|
239
|
-
<CCACCTFROM>
|
|
240
|
-
<ACCTID>${escapeOfxText(options.accountId)}
|
|
241
|
-
</CCACCTFROM>
|
|
242
|
-
<BANKTRANLIST>
|
|
243
|
-
<DTSTART>${formatOfxDate(options.startDate)}
|
|
244
|
-
<DTEND>${formatOfxDate(options.endDate)}`);
|
|
245
|
-
for (const trn of options.transactions) {
|
|
246
|
-
parts.push(generateTransaction(trn));
|
|
247
|
-
}
|
|
248
|
-
parts.push("</BANKTRANLIST>");
|
|
249
|
-
if (options.ledgerBalance) {
|
|
250
|
-
parts.push(`<LEDGERBAL>
|
|
251
|
-
<BALAMT>${formatAmount(options.ledgerBalance.amount)}
|
|
252
|
-
<DTASOF>${formatOfxDate(options.ledgerBalance.asOfDate)}
|
|
253
|
-
</LEDGERBAL>`);
|
|
254
|
-
}
|
|
255
|
-
if (options.availableBalance) {
|
|
256
|
-
parts.push(`<AVAILBAL>
|
|
257
|
-
<BALAMT>${formatAmount(options.availableBalance.amount)}
|
|
258
|
-
<DTASOF>${formatOfxDate(options.availableBalance.asOfDate)}
|
|
259
|
-
</AVAILBAL>`);
|
|
260
|
-
}
|
|
261
|
-
parts.push(`</CCSTMTRS>
|
|
262
|
-
</CCSTMTTRNRS>
|
|
263
|
-
</CREDITCARDMSGSRSV1>
|
|
264
|
-
</OFX>`);
|
|
265
|
-
return parts.join(`
|
|
266
|
-
`);
|
|
267
|
-
}
|
|
268
|
-
// src/parser.ts
|
|
269
111
|
import { z as z2 } from "zod";
|
|
270
112
|
|
|
271
113
|
// src/schemas.ts
|
|
272
114
|
import { z } from "zod";
|
|
273
115
|
var toFloat = (val) => Number.parseFloat(val);
|
|
116
|
+
var dateComponentsSchema = z.object({
|
|
117
|
+
year: z.number(),
|
|
118
|
+
month: z.number(),
|
|
119
|
+
day: z.number(),
|
|
120
|
+
hour: z.number(),
|
|
121
|
+
minute: z.number(),
|
|
122
|
+
second: z.number()
|
|
123
|
+
});
|
|
274
124
|
var DATE_REGEX = /^(\d{4})(\d{2})(\d{2})(\d{2})?(\d{2})?(\d{2})?/;
|
|
275
125
|
var TIMEZONE_REGEX = /\[([+-]?\d+):(\w+)\]/;
|
|
276
126
|
function parseDateComponents(val) {
|
|
@@ -463,15 +313,234 @@ var ofxDocumentSchema = z.object({
|
|
|
463
313
|
OFX: ofxResponseSchema
|
|
464
314
|
});
|
|
465
315
|
|
|
316
|
+
// src/generator.ts
|
|
317
|
+
var generateHeaderOptionsSchema = z2.object({
|
|
318
|
+
version: z2.string().optional(),
|
|
319
|
+
encoding: z2.string().optional(),
|
|
320
|
+
charset: z2.string().optional()
|
|
321
|
+
}).optional();
|
|
322
|
+
function generateHeader(options) {
|
|
323
|
+
const version = options?.version ?? "100";
|
|
324
|
+
const encoding = options?.encoding ?? "USASCII";
|
|
325
|
+
const charset = options?.charset ?? "1252";
|
|
326
|
+
return [
|
|
327
|
+
"OFXHEADER:100",
|
|
328
|
+
"DATA:OFXSGML",
|
|
329
|
+
`VERSION:${version}`,
|
|
330
|
+
"SECURITY:NONE",
|
|
331
|
+
`ENCODING:${encoding}`,
|
|
332
|
+
`CHARSET:${charset}`,
|
|
333
|
+
"COMPRESSION:NONE",
|
|
334
|
+
"OLDFILEUID:NONE",
|
|
335
|
+
"NEWFILEUID:NONE",
|
|
336
|
+
""
|
|
337
|
+
].join(`
|
|
338
|
+
`);
|
|
339
|
+
}
|
|
340
|
+
var generateTransactionInputSchema = z2.object({
|
|
341
|
+
type: transactionTypeSchema,
|
|
342
|
+
datePosted: z2.date(),
|
|
343
|
+
amount: z2.number(),
|
|
344
|
+
fitId: z2.string().min(1),
|
|
345
|
+
name: z2.string().optional(),
|
|
346
|
+
memo: z2.string().optional(),
|
|
347
|
+
checkNum: z2.string().optional(),
|
|
348
|
+
refNum: z2.string().optional()
|
|
349
|
+
});
|
|
350
|
+
function generateTransaction(trn) {
|
|
351
|
+
const lines = [
|
|
352
|
+
"<STMTTRN>",
|
|
353
|
+
`<TRNTYPE>${trn.type}`,
|
|
354
|
+
`<DTPOSTED>${formatOfxDate(trn.datePosted)}`,
|
|
355
|
+
`<TRNAMT>${formatAmount(trn.amount)}`,
|
|
356
|
+
`<FITID>${escapeOfxText(trn.fitId)}`
|
|
357
|
+
];
|
|
358
|
+
if (trn.name) {
|
|
359
|
+
lines.push(`<NAME>${escapeOfxText(trn.name)}`);
|
|
360
|
+
}
|
|
361
|
+
if (trn.memo) {
|
|
362
|
+
lines.push(`<MEMO>${escapeOfxText(trn.memo)}`);
|
|
363
|
+
}
|
|
364
|
+
if (trn.checkNum) {
|
|
365
|
+
lines.push(`<CHECKNUM>${escapeOfxText(trn.checkNum)}`);
|
|
366
|
+
}
|
|
367
|
+
if (trn.refNum) {
|
|
368
|
+
lines.push(`<REFNUM>${escapeOfxText(trn.refNum)}`);
|
|
369
|
+
}
|
|
370
|
+
lines.push("</STMTTRN>");
|
|
371
|
+
return lines.join(`
|
|
372
|
+
`);
|
|
373
|
+
}
|
|
374
|
+
var balanceSchema2 = z2.object({
|
|
375
|
+
amount: z2.number(),
|
|
376
|
+
asOfDate: z2.date()
|
|
377
|
+
});
|
|
378
|
+
var financialInstitutionSchema2 = z2.object({
|
|
379
|
+
org: z2.string().optional(),
|
|
380
|
+
fid: z2.string().optional()
|
|
381
|
+
});
|
|
382
|
+
var generateBankStatementOptionsSchema = z2.object({
|
|
383
|
+
bankId: z2.string().min(1),
|
|
384
|
+
accountId: z2.string().min(1),
|
|
385
|
+
accountType: accountTypeSchema,
|
|
386
|
+
currency: z2.string().min(1),
|
|
387
|
+
startDate: z2.date(),
|
|
388
|
+
endDate: z2.date(),
|
|
389
|
+
transactions: z2.array(generateTransactionInputSchema),
|
|
390
|
+
ledgerBalance: balanceSchema2.optional(),
|
|
391
|
+
availableBalance: balanceSchema2.optional(),
|
|
392
|
+
financialInstitution: financialInstitutionSchema2.optional(),
|
|
393
|
+
language: z2.string().optional()
|
|
394
|
+
});
|
|
395
|
+
function generateBankStatement(options) {
|
|
396
|
+
const parts = [generateHeader()];
|
|
397
|
+
const serverDate = formatOfxDate(new Date);
|
|
398
|
+
const language = options.language ?? "POR";
|
|
399
|
+
parts.push(`<OFX>
|
|
400
|
+
<SIGNONMSGSRSV1>
|
|
401
|
+
<SONRS>
|
|
402
|
+
<STATUS>
|
|
403
|
+
<CODE>0
|
|
404
|
+
<SEVERITY>INFO
|
|
405
|
+
</STATUS>
|
|
406
|
+
<DTSERVER>${serverDate}
|
|
407
|
+
<LANGUAGE>${language}`);
|
|
408
|
+
if (options.financialInstitution) {
|
|
409
|
+
parts.push("<FI>");
|
|
410
|
+
if (options.financialInstitution.org) {
|
|
411
|
+
parts.push(`<ORG>${escapeOfxText(options.financialInstitution.org)}`);
|
|
412
|
+
}
|
|
413
|
+
if (options.financialInstitution.fid) {
|
|
414
|
+
parts.push(`<FID>${escapeOfxText(options.financialInstitution.fid)}`);
|
|
415
|
+
}
|
|
416
|
+
parts.push("</FI>");
|
|
417
|
+
}
|
|
418
|
+
parts.push(`</SONRS>
|
|
419
|
+
</SIGNONMSGSRSV1>
|
|
420
|
+
<BANKMSGSRSV1>
|
|
421
|
+
<STMTTRNRS>
|
|
422
|
+
<TRNUID>0
|
|
423
|
+
<STATUS>
|
|
424
|
+
<CODE>0
|
|
425
|
+
<SEVERITY>INFO
|
|
426
|
+
</STATUS>
|
|
427
|
+
<STMTRS>
|
|
428
|
+
<CURDEF>${options.currency}
|
|
429
|
+
<BANKACCTFROM>
|
|
430
|
+
<BANKID>${escapeOfxText(options.bankId)}
|
|
431
|
+
<ACCTID>${escapeOfxText(options.accountId)}
|
|
432
|
+
<ACCTTYPE>${options.accountType}
|
|
433
|
+
</BANKACCTFROM>
|
|
434
|
+
<BANKTRANLIST>
|
|
435
|
+
<DTSTART>${formatOfxDate(options.startDate)}
|
|
436
|
+
<DTEND>${formatOfxDate(options.endDate)}`);
|
|
437
|
+
for (const trn of options.transactions) {
|
|
438
|
+
parts.push(generateTransaction(trn));
|
|
439
|
+
}
|
|
440
|
+
parts.push("</BANKTRANLIST>");
|
|
441
|
+
if (options.ledgerBalance) {
|
|
442
|
+
parts.push(`<LEDGERBAL>
|
|
443
|
+
<BALAMT>${formatAmount(options.ledgerBalance.amount)}
|
|
444
|
+
<DTASOF>${formatOfxDate(options.ledgerBalance.asOfDate)}
|
|
445
|
+
</LEDGERBAL>`);
|
|
446
|
+
}
|
|
447
|
+
if (options.availableBalance) {
|
|
448
|
+
parts.push(`<AVAILBAL>
|
|
449
|
+
<BALAMT>${formatAmount(options.availableBalance.amount)}
|
|
450
|
+
<DTASOF>${formatOfxDate(options.availableBalance.asOfDate)}
|
|
451
|
+
</AVAILBAL>`);
|
|
452
|
+
}
|
|
453
|
+
parts.push(`</STMTRS>
|
|
454
|
+
</STMTTRNRS>
|
|
455
|
+
</BANKMSGSRSV1>
|
|
456
|
+
</OFX>`);
|
|
457
|
+
return parts.join(`
|
|
458
|
+
`);
|
|
459
|
+
}
|
|
460
|
+
var generateCreditCardStatementOptionsSchema = z2.object({
|
|
461
|
+
accountId: z2.string().min(1),
|
|
462
|
+
currency: z2.string().min(1),
|
|
463
|
+
startDate: z2.date(),
|
|
464
|
+
endDate: z2.date(),
|
|
465
|
+
transactions: z2.array(generateTransactionInputSchema),
|
|
466
|
+
ledgerBalance: balanceSchema2.optional(),
|
|
467
|
+
availableBalance: balanceSchema2.optional(),
|
|
468
|
+
financialInstitution: financialInstitutionSchema2.optional(),
|
|
469
|
+
language: z2.string().optional()
|
|
470
|
+
});
|
|
471
|
+
function generateCreditCardStatement(options) {
|
|
472
|
+
const parts = [generateHeader()];
|
|
473
|
+
const serverDate = formatOfxDate(new Date);
|
|
474
|
+
const language = options.language ?? "POR";
|
|
475
|
+
parts.push(`<OFX>
|
|
476
|
+
<SIGNONMSGSRSV1>
|
|
477
|
+
<SONRS>
|
|
478
|
+
<STATUS>
|
|
479
|
+
<CODE>0
|
|
480
|
+
<SEVERITY>INFO
|
|
481
|
+
</STATUS>
|
|
482
|
+
<DTSERVER>${serverDate}
|
|
483
|
+
<LANGUAGE>${language}`);
|
|
484
|
+
if (options.financialInstitution) {
|
|
485
|
+
parts.push("<FI>");
|
|
486
|
+
if (options.financialInstitution.org) {
|
|
487
|
+
parts.push(`<ORG>${escapeOfxText(options.financialInstitution.org)}`);
|
|
488
|
+
}
|
|
489
|
+
if (options.financialInstitution.fid) {
|
|
490
|
+
parts.push(`<FID>${escapeOfxText(options.financialInstitution.fid)}`);
|
|
491
|
+
}
|
|
492
|
+
parts.push("</FI>");
|
|
493
|
+
}
|
|
494
|
+
parts.push(`</SONRS>
|
|
495
|
+
</SIGNONMSGSRSV1>
|
|
496
|
+
<CREDITCARDMSGSRSV1>
|
|
497
|
+
<CCSTMTTRNRS>
|
|
498
|
+
<TRNUID>0
|
|
499
|
+
<STATUS>
|
|
500
|
+
<CODE>0
|
|
501
|
+
<SEVERITY>INFO
|
|
502
|
+
</STATUS>
|
|
503
|
+
<CCSTMTRS>
|
|
504
|
+
<CURDEF>${options.currency}
|
|
505
|
+
<CCACCTFROM>
|
|
506
|
+
<ACCTID>${escapeOfxText(options.accountId)}
|
|
507
|
+
</CCACCTFROM>
|
|
508
|
+
<BANKTRANLIST>
|
|
509
|
+
<DTSTART>${formatOfxDate(options.startDate)}
|
|
510
|
+
<DTEND>${formatOfxDate(options.endDate)}`);
|
|
511
|
+
for (const trn of options.transactions) {
|
|
512
|
+
parts.push(generateTransaction(trn));
|
|
513
|
+
}
|
|
514
|
+
parts.push("</BANKTRANLIST>");
|
|
515
|
+
if (options.ledgerBalance) {
|
|
516
|
+
parts.push(`<LEDGERBAL>
|
|
517
|
+
<BALAMT>${formatAmount(options.ledgerBalance.amount)}
|
|
518
|
+
<DTASOF>${formatOfxDate(options.ledgerBalance.asOfDate)}
|
|
519
|
+
</LEDGERBAL>`);
|
|
520
|
+
}
|
|
521
|
+
if (options.availableBalance) {
|
|
522
|
+
parts.push(`<AVAILBAL>
|
|
523
|
+
<BALAMT>${formatAmount(options.availableBalance.amount)}
|
|
524
|
+
<DTASOF>${formatOfxDate(options.availableBalance.asOfDate)}
|
|
525
|
+
</AVAILBAL>`);
|
|
526
|
+
}
|
|
527
|
+
parts.push(`</CCSTMTRS>
|
|
528
|
+
</CCSTMTTRNRS>
|
|
529
|
+
</CREDITCARDMSGSRSV1>
|
|
530
|
+
</OFX>`);
|
|
531
|
+
return parts.join(`
|
|
532
|
+
`);
|
|
533
|
+
}
|
|
466
534
|
// src/parser.ts
|
|
535
|
+
import { z as z3 } from "zod";
|
|
467
536
|
var CHARSET_MAP = {
|
|
468
537
|
"1252": "windows-1252",
|
|
469
538
|
"WINDOWS-1252": "windows-1252",
|
|
470
539
|
CP1252: "windows-1252",
|
|
471
|
-
"8859-1": "
|
|
472
|
-
"ISO-8859-1": "
|
|
473
|
-
LATIN1: "
|
|
474
|
-
"LATIN-1": "
|
|
540
|
+
"8859-1": "windows-1252",
|
|
541
|
+
"ISO-8859-1": "windows-1252",
|
|
542
|
+
LATIN1: "windows-1252",
|
|
543
|
+
"LATIN-1": "windows-1252",
|
|
475
544
|
"UTF-8": "utf-8",
|
|
476
545
|
UTF8: "utf-8",
|
|
477
546
|
NONE: "utf-8",
|
|
@@ -483,18 +552,10 @@ function getEncodingFromCharset(charset) {
|
|
|
483
552
|
const normalized = charset.toUpperCase().trim();
|
|
484
553
|
return CHARSET_MAP[normalized] ?? "windows-1252";
|
|
485
554
|
}
|
|
486
|
-
var ENTITY_MAP = {
|
|
487
|
-
"&": "&",
|
|
488
|
-
"'": "'",
|
|
489
|
-
">": ">",
|
|
490
|
-
"<": "<",
|
|
491
|
-
""": '"'
|
|
492
|
-
};
|
|
493
|
-
var ENTITY_REGEX = /&(?:amp|lt|gt|quot|apos);/g;
|
|
494
|
-
function decodeEntities(text) {
|
|
495
|
-
return text.replace(ENTITY_REGEX, (match) => ENTITY_MAP[match] ?? match);
|
|
496
|
-
}
|
|
497
555
|
function addToContent(content, key, value) {
|
|
556
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
498
559
|
const existing = content[key];
|
|
499
560
|
if (existing !== undefined) {
|
|
500
561
|
if (Array.isArray(existing)) {
|
|
@@ -558,7 +619,7 @@ function generateFitId(txn, index) {
|
|
|
558
619
|
let hash = 0;
|
|
559
620
|
for (let i = 0;i < input.length; i++) {
|
|
560
621
|
hash = (hash << 5) - hash + input.charCodeAt(i);
|
|
561
|
-
hash = hash
|
|
622
|
+
hash = hash | 0;
|
|
562
623
|
}
|
|
563
624
|
return `AUTO${Math.abs(hash).toString(16).toUpperCase().padStart(8, "0")}`;
|
|
564
625
|
}
|
|
@@ -642,7 +703,7 @@ function parse(content) {
|
|
|
642
703
|
try {
|
|
643
704
|
if (typeof content !== "string") {
|
|
644
705
|
return {
|
|
645
|
-
error: new
|
|
706
|
+
error: new z3.ZodError([
|
|
646
707
|
{
|
|
647
708
|
code: "invalid_type",
|
|
648
709
|
expected: "string",
|
|
@@ -655,7 +716,7 @@ function parse(content) {
|
|
|
655
716
|
}
|
|
656
717
|
if (content.trim() === "") {
|
|
657
718
|
return {
|
|
658
|
-
error: new
|
|
719
|
+
error: new z3.ZodError([
|
|
659
720
|
{
|
|
660
721
|
code: "custom",
|
|
661
722
|
message: "Content cannot be empty",
|
|
@@ -677,7 +738,7 @@ function parse(content) {
|
|
|
677
738
|
success: true
|
|
678
739
|
};
|
|
679
740
|
} catch (err) {
|
|
680
|
-
if (err instanceof
|
|
741
|
+
if (err instanceof z3.ZodError) {
|
|
681
742
|
return { error: err, success: false };
|
|
682
743
|
}
|
|
683
744
|
throw err;
|
|
@@ -733,7 +794,7 @@ function hasUtf8MultiByte(buffer) {
|
|
|
733
794
|
}
|
|
734
795
|
function parseHeaderFromBuffer(buffer) {
|
|
735
796
|
const maxHeaderSize = Math.min(buffer.length, 1000);
|
|
736
|
-
const headerSection = new TextDecoder("
|
|
797
|
+
const headerSection = new TextDecoder("windows-1252").decode(buffer.slice(0, maxHeaderSize));
|
|
737
798
|
const header = {};
|
|
738
799
|
const singleLineMatch = headerSection.match(/^(OFXHEADER:\d+.*?)(?=<OFX|<\?xml)/is);
|
|
739
800
|
if (singleLineMatch?.[1]) {
|
|
@@ -775,7 +836,7 @@ function parseBuffer(buffer) {
|
|
|
775
836
|
try {
|
|
776
837
|
if (!(buffer instanceof Uint8Array)) {
|
|
777
838
|
return {
|
|
778
|
-
error: new
|
|
839
|
+
error: new z3.ZodError([
|
|
779
840
|
{
|
|
780
841
|
code: "invalid_type",
|
|
781
842
|
expected: "object",
|
|
@@ -788,7 +849,7 @@ function parseBuffer(buffer) {
|
|
|
788
849
|
}
|
|
789
850
|
if (buffer.length === 0) {
|
|
790
851
|
return {
|
|
791
|
-
error: new
|
|
852
|
+
error: new z3.ZodError([
|
|
792
853
|
{
|
|
793
854
|
code: "custom",
|
|
794
855
|
message: "Buffer cannot be empty",
|
|
@@ -803,7 +864,7 @@ function parseBuffer(buffer) {
|
|
|
803
864
|
const content = decoder.decode(buffer);
|
|
804
865
|
return parse(content);
|
|
805
866
|
} catch (err) {
|
|
806
|
-
if (err instanceof
|
|
867
|
+
if (err instanceof z3.ZodError) {
|
|
807
868
|
return { error: err, success: false };
|
|
808
869
|
}
|
|
809
870
|
throw err;
|
|
@@ -817,19 +878,6 @@ function parseBufferOrThrow(buffer) {
|
|
|
817
878
|
return result.data;
|
|
818
879
|
}
|
|
819
880
|
// src/stream.ts
|
|
820
|
-
var ENTITY_MAP2 = {
|
|
821
|
-
"&": "&",
|
|
822
|
-
"'": "'",
|
|
823
|
-
">": ">",
|
|
824
|
-
"<": "<",
|
|
825
|
-
""": '"'
|
|
826
|
-
};
|
|
827
|
-
var ENTITY_REGEX2 = /&(?:amp|lt|gt|quot|apos);/g;
|
|
828
|
-
function decodeEntities2(text) {
|
|
829
|
-
if (!text.includes("&"))
|
|
830
|
-
return text;
|
|
831
|
-
return text.replace(ENTITY_REGEX2, (match) => ENTITY_MAP2[match] ?? match);
|
|
832
|
-
}
|
|
833
881
|
function parseHeaderFromBuffer2(buffer) {
|
|
834
882
|
const lines = buffer.split(/\r?\n/);
|
|
835
883
|
const header = {};
|
|
@@ -966,7 +1014,7 @@ async function* parseStream(input, options) {
|
|
|
966
1014
|
state.objectStack.length = Math.max(pathIndex + 1, 1);
|
|
967
1015
|
}
|
|
968
1016
|
} else if (textContent) {
|
|
969
|
-
const decoded =
|
|
1017
|
+
const decoded = decodeEntities(textContent);
|
|
970
1018
|
const existing = currentObj[tagName];
|
|
971
1019
|
if (existing !== undefined) {
|
|
972
1020
|
if (Array.isArray(existing)) {
|
|
@@ -1015,7 +1063,7 @@ async function* parseStream(input, options) {
|
|
|
1015
1063
|
combined.set(chunk, offset);
|
|
1016
1064
|
offset += chunk.length;
|
|
1017
1065
|
}
|
|
1018
|
-
const headerSection = new TextDecoder("
|
|
1066
|
+
const headerSection = new TextDecoder("windows-1252").decode(combined.slice(0, Math.min(combined.length, 1000)));
|
|
1019
1067
|
if (headerSection.includes("<OFX") || headerSection.includes("<?xml")) {
|
|
1020
1068
|
const charsetMatch = headerSection.match(/CHARSET:(\S+)/i);
|
|
1021
1069
|
if (charsetMatch && !detectedEncoding) {
|
|
@@ -1088,7 +1136,7 @@ async function* parseBatchStream(files, options) {
|
|
|
1088
1136
|
yield { type: "file_start", fileIndex: i, filename: file.filename };
|
|
1089
1137
|
try {
|
|
1090
1138
|
let fileTransactionCount = 0;
|
|
1091
|
-
const headerSection = new TextDecoder("
|
|
1139
|
+
const headerSection = new TextDecoder("windows-1252").decode(file.buffer.slice(0, Math.min(file.buffer.length, 1000)));
|
|
1092
1140
|
const charsetMatch = headerSection.match(/CHARSET:(\S+)/i);
|
|
1093
1141
|
const encoding = charsetMatch ? getEncodingFromCharset(charsetMatch[1]) : options?.encoding ?? "utf-8";
|
|
1094
1142
|
const decoder = new TextDecoder(encoding);
|
|
@@ -1196,8 +1244,12 @@ export {
|
|
|
1196
1244
|
getEncodingFromCharset,
|
|
1197
1245
|
getBalance,
|
|
1198
1246
|
getAccountInfo,
|
|
1247
|
+
generateTransactionInputSchema,
|
|
1248
|
+
generateHeaderOptionsSchema,
|
|
1199
1249
|
generateHeader,
|
|
1250
|
+
generateCreditCardStatementOptionsSchema,
|
|
1200
1251
|
generateCreditCardStatement,
|
|
1252
|
+
generateBankStatementOptionsSchema,
|
|
1201
1253
|
generateBankStatement,
|
|
1202
1254
|
formatOfxDate
|
|
1203
1255
|
};
|
package/package.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"url": "https://github.com/F-O-T/montte-nx/issues"
|
|
4
4
|
},
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"zod": "4.1
|
|
6
|
+
"zod": "4.2.1"
|
|
7
7
|
},
|
|
8
8
|
"description": "Typesafe ofx handling",
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@biomejs/biome": "2.3.
|
|
11
|
-
"@types/bun": "1.3.
|
|
10
|
+
"@biomejs/biome": "2.3.10",
|
|
11
|
+
"@types/bun": "1.3.5",
|
|
12
12
|
"bumpp": "10.3.2",
|
|
13
|
-
"bunup": "0.16.
|
|
13
|
+
"bunup": "0.16.11",
|
|
14
14
|
"typescript": "5.9.3"
|
|
15
15
|
},
|
|
16
16
|
"exports": {
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
},
|
|
60
60
|
"type": "module",
|
|
61
61
|
"types": "./dist/index.d.ts",
|
|
62
|
-
"version": "2.
|
|
62
|
+
"version": "2.3.1"
|
|
63
63
|
}
|