commit eb6089fd397571037e5bfd15c50429557ba1d298
Author: William Casarin <jb55@jb55.com>
Date: Sat, 27 May 2023 13:00:29 -0700
init commit
Diffstat:
7 files changed, 674 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,12 @@
+.build-result
+.buildcmd
+.direnv/
+.envrc
+.rgignore
+Makefile
+events.txt
+index.js
+invoice_events.txt
+node_modules/
+prices.db
+test.ledger
diff --git a/index.ts b/index.ts
@@ -0,0 +1,227 @@
+
+import fs from 'fs';
+import readline from 'readline';
+import assert from 'assert';
+
+interface Zap {
+ kind: number;
+ content: string;
+ pubkey: string;
+ tags: string[][];
+}
+
+// Zap Types
+interface ZapProfileType {
+ type: "profile" // profile
+ pubkey: string
+}
+
+interface ZapEventType {
+ type: "event"
+ evid: string
+ pubkey: string
+}
+
+type ZapType = ZapProfileType | ZapEventType
+
+// Descriptions
+interface GenericDesc {
+ type: "generic" // generic
+ value: string
+}
+
+interface LNDesc {
+ type: "lnurl" // lnurl
+ description: string
+ address: string
+}
+
+interface ZapDesc {
+ type: "zap" // zap
+ zap_type: ZapType
+ zap: Zap
+}
+
+type Description = LNDesc | GenericDesc | ZapDesc
+
+interface Posting {
+ account: string;
+ amount: string;
+}
+
+interface Transaction {
+ date: Date;
+ description: Description;
+ postings: Posting[];
+}
+
+function description_string(desc: Description): string {
+ switch (desc.type) {
+ case "generic":
+ return desc.value
+ case "lnurl":
+ return desc.description
+ case "zap":
+ return "Zap"
+ }
+}
+
+function determine_zap_type(zap: Zap): ZapType {
+ const etag = get_tag(zap.tags, "e")
+ const ptag = get_tag(zap.tags, "p")
+
+ if (!ptag)
+ return null
+
+ if (!etag) {
+ let profile_zap: ZapProfileType = { pubkey: ptag, type: "profile" }
+ return profile_zap
+ }
+
+ let zap_ev: ZapEventType = { evid: etag, pubkey: ptag, type: "event" }
+ return zap_ev
+}
+
+function get_tag(tags: string[][], key: string): string {
+ let v = tags.find(tag => tag[0] === key)
+ return v && v[1]
+}
+
+function get_lnurl_description(json: string[][]): LNDesc {
+ const description = get_tag(json, "text/plain")
+ const address = get_tag(json, "text/identifier")
+ return { description, address, type: "lnurl" }
+}
+
+function is_json(str: string): boolean {
+ return str[0] === '{' || (str[0] === '[' && str[1] === '[')
+}
+
+function create_generic_desc(value: string): GenericDesc {
+ return { type: "generic", value }
+}
+
+function determine_description(str: string): Description {
+ try {
+ let json
+ if (is_json(str) && (json = JSON.parse(str))) {
+ if (json.kind === 9734) {
+ let zaptype: ZapType = determine_zap_type(json)
+ let zap_desc: ZapDesc = { zap_type: zaptype, zap: json, type: "zap" }
+ return zap_desc
+ } else if (json.length > 0) {
+ return get_lnurl_description(json)
+ }
+
+ return create_generic_desc(str)
+ }
+
+ return create_generic_desc(str)
+ } catch(e) {
+ return create_generic_desc(str)
+ }
+}
+
+function msat(val: number): string {
+ return `${val} msat`
+}
+
+function classify_account(str: string):string {
+ if (str.includes("Damus Merch"))
+ return "merch:tshirt"
+ if (str.includes("Damus Hat"))
+ return "merch:hat"
+ if (str.includes("@tipjar"))
+ return "lnurl:jb55@sendsats.lol:tipjar"
+ return "unknown"
+}
+
+function determine_postings(credit: number, debit: number, desc: Description): Posting[] {
+ let postings: Posting[] = []
+ switch (desc.type) {
+ case "generic":
+ // todo: categorize
+ const is_credit = credit > 0
+ const acct = is_credit? "income" : "expenses"
+ const amount = is_credit? credit : debit
+ const subacct = classify_account(desc.value)
+ postings.push({ account: `${acct}:${subacct}`, amount: msat(is_credit? -amount : amount) })
+ postings.push({ account: `assets:cln`, amount: msat(is_credit? amount : -amount) })
+ break
+ case "lnurl":
+ assert(debit === 0)
+ postings.push({ account: `income:lnurl:${desc.address}`, amount: msat(-credit) })
+ postings.push({ account: `assets:cln`, amount: msat(credit) })
+ break
+ case "zap":
+ assert(debit === 0)
+ switch (desc.zap_type.type) {
+ case 'profile':
+ postings.push({ account: `income:zap:profile:${desc.zap_type.pubkey}`, amount: msat(-credit) })
+ break
+ case 'event':
+ // todo: attribute this to profile as well?
+ let evtype = desc.zap_type as ZapEventType
+ postings.push({ account: `income:zap:event:${evtype.evid}`, amount: msat(-credit) })
+ break
+ }
+ postings.push({ account: `assets:cln`, amount: msat(credit) })
+ break
+ }
+
+ return postings
+}
+
+async function process(filePath: string) {
+ const fileStream = fs.createReadStream(filePath);
+
+ const rl = readline.createInterface({
+ input: fileStream,
+ crlfDelay: Infinity
+ });
+
+ console.log("P 2022-07-22 msat 0.00000036 CAD")
+
+ for await (const line of rl) {
+ let parts = line.split('\t');
+
+ if(parts.length !== 6) {
+ console.error(`Invalid line: ${line} ${parts.length} !== 5`);
+ continue;
+ }
+
+ const credit = Number(parts[2])
+ const debit = Number(parts[3])
+
+ if (credit === 0 && debit === 0)
+ continue
+
+ const timestamp = Number(parts[4])
+ const date = new Date(timestamp * 1000)
+ const description = determine_description(parts[5])
+ const postings = determine_postings(credit, debit, description)
+
+ // type,.tag,.credit_msat,.debit_msat,.timestamp,.description
+ let transaction: Transaction = { date, description, postings };
+ console.log(transactionToLedger(transaction));
+ }
+}
+
+function format_date(date: Date): string {
+ const year = date.getFullYear();
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
+ const day = date.getDate().toString().padStart(2, '0');
+
+ return `${year}-${month}-${day}`;
+}
+
+function transactionToLedger(transaction: Transaction): string {
+ const tx = `${format_date(transaction.date)} ${description_string(transaction.description)}\n`
+ return tx + transaction.postings.map(p => ` ${p.account} ${p.amount}`).join("\n") + "\n"
+}
+
+async function main() {
+ await process('invoice_events.txt');
+}
+
+main().catch(console.error);
diff --git a/package-lock.json b/package-lock.json
@@ -0,0 +1,387 @@
+{
+ "name": "accounting",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "accounting",
+ "version": "0.1.0",
+ "dependencies": {
+ "@types/node": "^20.2.5"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5",
+ "ts-node": "^10.9.1",
+ "typescript": "^5.0.4"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.2.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
+ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ=="
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "node_modules/buffer-equal": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.2.tgz",
+ "integrity": "sha512-4hr0gS7+NK47X6WbA/okVFrN5qGh3WLT7N3hMRv7+hlkXnbUIdU2u05n6r0RQv6cq6xke06nVl70r0NW0WM2OQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/bunker": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/bunker/-/bunker-0.1.2.tgz",
+ "integrity": "sha512-YnahkcXBNT522S46k5LUA9P18lzvgkunbMl0qIJQ8oeRMQ+dAg3YI3k32q5TnO+AAUErFHO6R768To6jslgYmQ==",
+ "dev": true,
+ "dependencies": {
+ "burrito": ">=0.2.5 <0.3"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/burrito": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/burrito/-/burrito-0.2.12.tgz",
+ "integrity": "sha512-ZhhT5iVTAgzQ+s8rily7m45Swxe/cU3dVCHTzqmHVWD/cc0Ds3W4Q4MExbkevY+fm0Me3lEwpehIy6TH7p+ehw==",
+ "dev": true,
+ "dependencies": {
+ "traverse": "~0.5.1",
+ "uglify-js": "~1.1.1"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/burrito/node_modules/traverse": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.5.2.tgz",
+ "integrity": "sha512-PUBVcfB3RqgLpzgTRGNiqK4duqrDbgGa1bobbUtzUwLiBNAjZ7vd5eCOdBxqZ/Fgezagr9o69IxP2fZp41RGFA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/charm": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz",
+ "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==",
+ "dev": true
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
+ "node_modules/deep-equal": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.0.0.tgz",
+ "integrity": "sha512-p1bI/kkDPT6auUI0U+WLuIIrzmDIDo80I406J8tT4y6I4ZGtBuMeTudrKDtBdMJFAcxqrQdx27gosqPVyY3IvQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/difflet": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/difflet/-/difflet-0.2.6.tgz",
+ "integrity": "sha512-ruldDDRmY1t678UOAJBng6sL77f62SqjHj0498YC0EJhxIe2yKkqJn2qEchwG3eU/dqJ/RxPZkAnYjePS4pDCw==",
+ "dev": true,
+ "dependencies": {
+ "charm": "0.1.x",
+ "deep-is": "0.1.x",
+ "traverse": "0.6.x"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "node_modules/mkdirp": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
+ "integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==",
+ "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
+ "dev": true
+ },
+ "node_modules/nopt": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz",
+ "integrity": "sha512-gIOTA/uJuhPwFqp+spY7VQ1satbnGlD+iQVZxI18K6hs8Evq4sX81Ml7BB5byP/LsbR2yBVtmvdEmhi7evJ6Aw==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ }
+ },
+ "node_modules/runforcover": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/runforcover/-/runforcover-0.0.2.tgz",
+ "integrity": "sha512-yarCIK2HcAOadqnKW419+FA38qpWDCKcOr5RZU+jnyLL/hn3No9BHZY+YJDEzvQ0k8Oyl7ffLjZv9ZUxvyKoLQ==",
+ "dev": true,
+ "dependencies": {
+ "bunker": "0.1.X"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/slide": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+ "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tap": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/tap/-/tap-0.2.6.tgz",
+ "integrity": "sha512-uLvaKbh3+A4nh+P3SrfX52kWGkVvP37UYI7LxKjRkd6Bjdqbyc7MARaPFGl7SdwyNwWtZHlUxQX3w9pVNf3FKQ==",
+ "bundleDependencies": [
+ "inherits",
+ "tap-consumer",
+ "yamlish"
+ ],
+ "dev": true,
+ "dependencies": {
+ "buffer-equal": "~0.0.0",
+ "deep-equal": "~0.0.0",
+ "difflet": "~0.2.0",
+ "inherits": "*",
+ "mkdirp": "~0.3",
+ "nopt": "~2",
+ "runforcover": "~0.0.2",
+ "slide": "*",
+ "yamlish": "*"
+ },
+ "bin": {
+ "tap": "bin/tap.js"
+ }
+ },
+ "node_modules/tap/node_modules/inherits": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "WTFPL2"
+ },
+ "node_modules/tap/node_modules/yamlish": {
+ "version": "0.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/traverse": {
+ "version": "0.6.7",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz",
+ "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "dev": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
+ "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/uglify-js": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.1.1.tgz",
+ "integrity": "sha512-YYY9Dle1leC+btgrHnAR05eq0aRdcPJsXlYYD+SYw2lqc5HFuFNHg3wWEW4SNE0iXXEUl0fz43gTQ3r1YK76rg==",
+ "dev": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "accounting",
+ "description": "damus accounting",
+ "version": "0.1.0",
+ "repository": {
+ "url": "https://github.com/jb55/accounting"
+ },
+ "main": "index.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5",
+ "ts-node": "^10.9.1",
+ "typescript": "^5.0.4"
+ },
+ "dependencies": {
+ "@types/node": "^20.2.5"
+ }
+}
diff --git a/run b/run
@@ -0,0 +1 @@
+npx ts-node index.ts
diff --git a/shell.nix b/shell.nix
@@ -0,0 +1,5 @@
+{ pkgs ? import <nixpkgs> {} }:
+with pkgs;
+mkShell {
+ buildInputs = [ ledger ];
+}
diff --git a/tsconfig.json b/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "es6",
+ "noImplicitAny": true,
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "outDir": "dist",
+ "baseUrl": ".",
+ "paths": {
+ "*": [
+ "node_modules/*",
+ "src/types/*"
+ ]
+ }
+ },
+ "include": [
+ "src/**/*"
+ ]
+}