UNPKG

ofx-data-extractor

Version:

A module written in TypeScript that provides a utility to extract data from an OFX file in Node.js and Browser

10 lines (9 loc) 7.89 kB
// Ofx-data-extractor v1.4.8 Copyright (c) 2025 Fábio Pereira França (function(c,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(c=typeof globalThis<"u"?globalThis:c||self,u(c.ofx={}))})(this,function(c){"use strict";const u=/<\/[\w.]+>/g,x=/<[\w.]+>/g,f="<BANKMSGSRSV1>",R="</BANKMSGSRSV1>",m="<CREDITCARDMSGSRSV1>",C="</CREDITCARDMSGSRSV1>",h="<STMTTRN>",d="</STMTTRN>",N="_#_",_=new RegExp(N,"g"),k=[f,h,m],G=[R,C,d];function F(e){const t=e.slice(0,4),n=e.slice(4,6),r=e.slice(6,8),s=e.slice(8,10),a=e.slice(10,12),i=e.slice(12,14);return{yyyy:t,yy:t.slice(2),y:t,MM:n,M:n,dd:r,d:r,hh:s,h:s,mm:a,m:a,ss:i,s:i}}function D(e,t){const n=F(e);let r=t;for(const[s,a]of Object.entries(n))r=r.replace(s,a);return r}var l;(function(e){e.BANK="BANK",e.CREDIT_CARD="CREDIT_CARD"})(l||(l={}));const J=["debit","fee","srvchg","atm","pos","check","payment","directdebit","cash","repeatpmt"],K=e=>e.includes(f)?l.BANK:l.CREDIT_CARD;function P(e){if(String(e.TRNAMT).startsWith("-"))return!0;const t=String(e.TRNTYPE).toLocaleLowerCase();return t==="1"||J.includes(t)}function g(e){const t=e.replace(/(\\)/g,"\\\\").replace(u,n=>y(n,!0)).replace(x,n=>A(n,!0)).replace(/(},})/g,"}}").replace(/(}")/g,'},"').replace(/(]")/g,'],"').replace(/(},])/g,"}]").replace(/(,})/g,"}").replace(/,\s*}/g,"}").replace(/(,",)/,'",').replace(_,'\\"').slice(0,-1);return t.endsWith(",")?t.slice(0,-1):t}const X=e=>JSON.stringify({date:e.slice(0,12),transactionCode:e.slice(12,19),protocol:e.slice(19)}),$=e=>e.startsWith("DT"),w=e=>e.trim();function A(e,t=!1){return k.includes(e)&&!t?e:e.replace(/[<]/g,` "`).replace(/[>]/g,'":{')}function y(e,t=!1){return G.includes(e)&&!t?` ${e}`:`}, `}function V({fitId:e,fitValue:t}){return e==="separated"?X(t):`"${t}",`}function W(e){return e.search(",")>e.search(".")?e.replace(/[.]/g,"").replace(/[,]/g,"."):e.replace(/[,]/g,"")}function j({dateString:e,formatDate:t="y-M-d"}){return D(e,t)}function z(e){let t=e.value.replace(/[{]/g,"").replace(/(},)/g,"").replace(/["]/g,N);const n=e.field.replace(/['"]/g,"");return n.endsWith("AMT")&&(t=W(t)),$(n)&&(t=j({dateString:t,formatDate:e.formatDate})),n==="FITID"?V({fitId:e.fitId,fitValue:t}):e.nativeTypes&&B(n,t)?`${Number(t)},`:`"${t}",`}function H(e,t){let n=e;return e.match(/{(\w|\W)+/)&&(n=n.replace(/({(\w|\W)+)$/,r=>z({field:n.slice(0,n.indexOf(":")),value:r,...t}))),n}function B(e,t){return e.endsWith("ID")||e.endsWith("NUM")?!1:!isNaN(Number(t))}function O(e){const t=e.indexOf(h),n=e.lastIndexOf(d)+d.length;if(t<0||n<0||t>=n)return{oldListText:e,newListText:e};const r=e.substring(t,n),s=new RegExp(h,"g"),a=new RegExp(d,"g"),i=`"STRTTRN":[${r.replace(s,"{").replace(a,"},")}]`;return{oldListText:e,newListText:e.replace(r,i)}}function b(e){if(!(e.indexOf(f)>0))return{newBankStatementTransactions:null,oldBankStatementTransactions:null};const n=O(e.substring(e.indexOf(f),e.indexOf(R)+R.length));return{newBankStatementTransactions:n.newListText,oldBankStatementTransactions:n.oldListText}}function M(e){if(!(e.indexOf(m)>0))return{newCreditCardStatementTransactions:null,oldCreditCardStatementTransactions:null};const n=e.substring(e.indexOf(m),e.indexOf(C)+C.length),r=O(n);return{newCreditCardStatementTransactions:r.newListText,oldCreditCardStatementTransactions:r.oldListText}}function U(e){const t=[];for(const n of e)t.push(...n.replace("<?xml"," ").replace("<?OFX"," ").replace("?>"," ").split(" ").filter(Boolean).map(w).map(r=>r.replace("=",":").replace(/["]/g,"")));return t}function v(e,t){const n={};e.join("").search("<?")>-1&&(e=U(e));for(const r of e){const[s,a]=r.split(":"),i=s.replace(` `,"").toUpperCase();n[i]=t&&B(s,a)?Number(a):String(a).replace(/\?>/,"")}return n}function Y(e){return e.reduce((t,n)=>{const r=Math.abs(+n.TRNAMT);return P(n)?(t.amountOfDebits++,t.debit+=r,t):(t.amountOfCredits++,t.credit+=r,t)},{credit:0,debit:0,amountOfCredits:0,amountOfDebits:0})}class Q{config;internConfig={};constructor(t){this.config=t,this.internConfig=t}getConfig(){return this.internConfig}sanitizeRow(t){return H(t,this.internConfig)}getPartialJsonData(t){const[n,r]=t.split("<OFX>"),s="<OFX>"+r,{sanitizeRow:a}=this;return s.replace(/<(?=[^\/])/g,` <`).replace(u,i=>y(i)).replace(x,i=>A(i)).split(` `).map(w).filter(Boolean).map(a,this).join("")}}function E(e){return e.toString()}async function I(e){return await new Promise((n,r)=>{if(typeof window<"u"&&window.FileReader){const s=new window.FileReader;s.onload=a=>n(a.target.result),s.onerror=a=>r(a.target.error),s.readAsText(e)}else r(new Error("FileReader is not available in this environment."))})}class o{data;dataRead="";constructor(t){this.data=t,t&&(this.dataRead=t)}getData(){return this.dataRead}fromString(t){return new o(t)}fromBuffer(t){return new o(E(t))}async fromBlob(t){return new o(await I(t))}static fromString(t){return new o(t)}static fromBuffer(t){return new o(E(t))}static async fromBlob(t){return new o(await I(t))}}class q{configInstance={}}class p extends q{setConfig(t){this.configInstance=t}getBankTransferList(t){const{newBankStatementTransactions:n}=b(this.configInstance.getPartialJsonData(t));return n?JSON.parse(`{${g(n)}}`)?.BANKMSGSRSV1?.STMTTRNRS?.STMTRS?.BANKTRANLIST?.STRTTRN:[]}getCreditCardTransferList(t){const{newCreditCardStatementTransactions:n}=M(this.configInstance.getPartialJsonData(t));return n?JSON.parse(`{${g(n)}}`)?.CREDITCARDMSGSRSV1?.CCSTMTTRNRS?.CCSTMTRS?.BANKTRANLIST?.STRTTRN:[]}getTransactionsSummary(t){const n=this.getContent(t),{DTEND:r,DTSTART:s,...a}=n.OFX.BANKMSGSRSV1.STMTTRNRS.STMTRS.BANKTRANLIST,i=Y(a.STMTTRN||a.STRTTRN||[]);return{dateStart:s,dateEnd:r,...i}}getContent(t){const n=this.configInstance.getPartialJsonData(t),{newBankStatementTransactions:r,oldBankStatementTransactions:s}=b(n),{newCreditCardStatementTransactions:a,oldCreditCardStatementTransactions:i}=M(n);let T=n;return r&&(T=T.replace(s,r)),a&&(T=T.replace(i,a)),JSON.parse(`{${g(T)}}`)}}class L{customExtractor;customExtractorInstance;dataReaderInstance=new o("");constructor(t){this.customExtractor=t,this.customExtractorInstance=t||new p,this.config({})}data(t){return this.dataReaderInstance=t,this}config(t){return this.customExtractorInstance.setConfig(new Q(t)),this}getType(){return K(this.dataReaderInstance.getData())}getHeaders(){const[t]=this.dataReaderInstance.getData().split("<OFX>");return v(t.split(` `).filter(n=>!!n.trim().length),!!this.customExtractorInstance.configInstance.getConfig().nativeTypes)}getBankTransferList(){return this.customExtractorInstance.getBankTransferList(this.dataReaderInstance.getData())}getCreditCardTransferList(){return this.customExtractorInstance.getCreditCardTransferList(this.dataReaderInstance.getData())}getTransactionsSummary(){return this.customExtractorInstance.getTransactionsSummary(this.dataReaderInstance.getData())}getContent(){return this.customExtractorInstance.getContent(this.dataReaderInstance.getData())}toJson(){const t=this.getHeaders(),n=this.customExtractorInstance.getContent(this.dataReaderInstance.getData());return{...t,...n}}}class S{extractor;constructor(t,n){this.extractor=new L(new p),this.extractor.data(new o(t)),this.extractor.config(n||{})}getType(){return this.extractor.getType()}static fromBuffer(t){return new S(o.fromBuffer(t).getData())}static async fromBlob(t){const n=await o.fromBlob(t);return new S(n.getData())}config(t){return this.extractor.config(t),this}getHeaders(){return this.extractor.getHeaders()}getBankTransferList(){return this.extractor.getBankTransferList()}getCreditCardTransferList(){return this.extractor.getCreditCardTransferList()}getTransactionsSummary(){return this.extractor.getTransactionsSummary()}getContent(){return this.extractor.getContent()}toJson(){return this.extractor.toJson()}}c.Extractor=L,c.Ofx=S,c.OfxExtractor=p,c.Reader=o,c.blobToString=I,c.bufferToString=E,c.fixJsonProblems=g,c.formatDate=D});