diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index cef9a77..0000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,151 +0,0 @@
-{
- "root": true,
- "parserOptions": {
- "ecmaVersion": 9,
- "ecmaFeatures": {
- "jsx": true
- },
- "sourceType": "module",
- "allowImportExportEverywhere": false
- },
-
- "env": {
- "es6": true,
- "node": true
- },
-
- "globals": {
- "document": false,
- "navigator": false,
- "window": false,
- "location": false,
- "URL": false,
- "URLSearchParams": false,
- "fetch": false,
- "EventSource": false,
- "localStorage": false,
- "sessionStorage": false,
- "BigInt": false
- },
-
- "rules": {
- "accessor-pairs": 2,
- "arrow-spacing": [2, {"before": true, "after": true}],
- "block-spacing": [2, "always"],
- "brace-style": [2, "1tbs", {"allowSingleLine": true}],
- "comma-dangle": 0,
- "comma-spacing": [2, {"before": false, "after": true}],
- "comma-style": [2, "last"],
- "constructor-super": 2,
- "curly": [0, "multi-line"],
- "dot-location": [2, "property"],
- "eol-last": 2,
- "eqeqeq": [2, "allow-null"],
- "generator-star-spacing": [2, {"before": true, "after": true}],
- "handle-callback-err": [2, "^(err|error)$"],
- "indent": 0,
- "jsx-quotes": [2, "prefer-double"],
- "key-spacing": [2, {"beforeColon": false, "afterColon": true}],
- "keyword-spacing": [2, {"before": true, "after": true}],
- "new-cap": 0,
- "new-parens": 0,
- "no-array-constructor": 2,
- "no-caller": 2,
- "no-class-assign": 2,
- "no-cond-assign": 2,
- "no-const-assign": 2,
- "no-control-regex": 0,
- "no-debugger": 0,
- "no-delete-var": 2,
- "no-dupe-args": 2,
- "no-dupe-class-members": 2,
- "no-dupe-keys": 2,
- "no-duplicate-case": 2,
- "no-empty-character-class": 2,
- "no-empty-pattern": 2,
- "no-eval": 0,
- "no-ex-assign": 2,
- "no-extend-native": 2,
- "no-extra-bind": 2,
- "no-extra-boolean-cast": 2,
- "no-extra-parens": [2, "functions"],
- "no-fallthrough": 2,
- "no-floating-decimal": 2,
- "no-func-assign": 2,
- "no-implied-eval": 2,
- "no-inner-declarations": [0, "functions"],
- "no-invalid-regexp": 2,
- "no-irregular-whitespace": 2,
- "no-iterator": 2,
- "no-label-var": 2,
- "no-labels": [2, {"allowLoop": false, "allowSwitch": false}],
- "no-lone-blocks": 2,
- "no-mixed-spaces-and-tabs": 2,
- "no-multi-spaces": 2,
- "no-multi-str": 2,
- "no-multiple-empty-lines": [2, {"max": 2}],
- "no-native-reassign": 2,
- "no-negated-in-lhs": 2,
- "no-new": 0,
- "no-new-func": 2,
- "no-new-object": 2,
- "no-new-require": 2,
- "no-new-symbol": 2,
- "no-new-wrappers": 2,
- "no-obj-calls": 2,
- "no-octal": 2,
- "no-octal-escape": 2,
- "no-path-concat": 0,
- "no-proto": 2,
- "no-redeclare": 2,
- "no-regex-spaces": 2,
- "no-return-assign": 0,
- "no-self-assign": 2,
- "no-self-compare": 2,
- "no-sequences": 2,
- "no-shadow-restricted-names": 2,
- "no-spaced-func": 2,
- "no-sparse-arrays": 2,
- "no-this-before-super": 2,
- "no-throw-literal": 2,
- "no-trailing-spaces": 2,
- "no-undef": 2,
- "no-undef-init": 2,
- "no-unexpected-multiline": 2,
- "no-unneeded-ternary": [2, {"defaultAssignment": false}],
- "no-unreachable": 2,
- "no-unused-vars": [
- 2,
- {"vars": "local", "args": "none", "varsIgnorePattern": "^_"}
- ],
- "no-useless-call": 2,
- "no-useless-constructor": 2,
- "no-with": 2,
- "one-var": [0, {"initialized": "never"}],
- "operator-linebreak": [
- 2,
- "after",
- {"overrides": {"?": "before", ":": "before"}}
- ],
- "padded-blocks": [2, "never"],
- "quotes": [
- 2,
- "single",
- {"avoidEscape": true, "allowTemplateLiterals": true}
- ],
- "semi": [2, "never"],
- "semi-spacing": [2, {"before": false, "after": true}],
- "space-before-blocks": [2, "always"],
- "space-before-function-paren": 0,
- "space-in-parens": [2, "never"],
- "space-infix-ops": 2,
- "space-unary-ops": [2, {"words": true, "nonwords": false}],
- "spaced-comment": 0,
- "template-curly-spacing": [2, "never"],
- "use-isnan": 2,
- "valid-typeof": 2,
- "wrap-iife": [2, "any"],
- "yield-star-spacing": [2, "both"],
- "yoda": [0]
- }
-}
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..312a86c
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,96 @@
+{
+ "plugins": [
+ "import",
+ "jsdoc",
+ "node",
+ "oxc",
+ "promise",
+ "unicorn"
+ ],
+ "categories": {
+ "correctness": "warn",
+ "suspicious": "warn",
+ "perf": "warn",
+ "pedantic": "warn"
+ },
+ "files": ["**/*.js"],
+ "rules": {
+ "eslint/array-callback-return": ["warn", { "checkForEach": true }],
+ "eslint/eqeqeq": ["warn", "always", {"null": "ignore"}],
+ "eslint/func-style": ["off", "expression"],
+ "eslint/guard-for-in": "warn",
+ "eslint/no-duplicate-imports": ["warn", { "includeExports": true }],
+ "eslint/no-empty": "warn",
+ "eslint/no-empty-function": "warn",
+ "eslint/no-iterator": "warn",
+ "eslint/no-lone-blocks": "warn",
+ "eslint/no-multi-assign": "warn",
+ "eslint/no-multi-str": "warn",
+ "eslint/no-nested-ternary": "warn",
+ "eslint/no-new-func": "warn",
+ "eslint/no-proto": "warn",
+ "eslint/no-return-assign": ["warn", "always"],
+ "eslint/no-script-url": "warn",
+ "eslint/no-unused-expressions": "warn",
+ "eslint/no-var": "warn",
+ "eslint/no-void": "warn",
+ "eslint/prefer-exponentiation-operator": "warn",
+ "eslint/prefer-numeric-literals": "warn",
+ "eslint/prefer-object-has-own": "warn",
+ "eslint/prefer-object-spread": "warn",
+ "eslint/prefer-spread": "warn",
+ "eslint/yoda": "warn",
+ "eslint/max-depth": "off",
+ "eslint/max-classes-per-file": "off",
+ "eslint/max-lines": "off",
+ "eslint/max-lines-per-function": "off",
+ "eslint/max-nested-callbacks": "off",
+ "eslint/new-cap": "off",
+ "eslint/radix": "off",
+ "import/consistent-type-specifier-style": "warn",
+ "import/exports-last": "warn",
+ "import/first": "warn",
+ "import/group-exports": "warn",
+ "import/no-amd": "warn",
+ "import/no-commonjs": "warn",
+ "import/no-cycle": "warn",
+ "import/no-dynamic-require": "warn",
+ "import/no-mutable-exports": "warn",
+ "import/no-named-default": "warn",
+ "import/no-webpack-loader-syntax": "warn",
+ "import/unambiguous": "warn",
+ "import/max-dependencies": "off",
+ "jsdoc/check-access": "warn",
+ "jsdoc/empty-tags": "warn",
+ "jsdoc/require-param-description": "off",
+ "jsdoc/require-returns-description": "off",
+ "node/no-new-require": "warn",
+ "oxc/bad-bitwise-operator": "warn",
+ "oxc/no-barrel-file": "warn",
+ "promise/prefer-await-to-callbacks": "warn",
+ "promise/prefer-await-to-then": "warn",
+ "promise/spec-only": "warn",
+ "unicorn/no-abusive-eslint-disable": "warn",
+ "unicorn/no-anonymous-default-export": "warn",
+ "unicorn/no-array-for-each": "warn",
+ "unicorn/no-array-method-this-argument": "warn",
+ "unicorn/no-array-reduce": "warn",
+ "unicorn/no-document-cookie": "warn",
+ "unicorn/no-for-loop": "warn",
+ "unicorn/no-length-as-slice-end": "warn",
+ "unicorn/no-nested-ternary": "warn",
+ "unicorn/prefer-array-index-of": "warn",
+ "unicorn/prefer-global-this": "warn",
+ "unicorn/prefer-modern-math-apis": "warn",
+ "unicorn/prefer-node-protocol": "warn",
+ "unicorn/prefer-number-properties": "warn",
+ "unicorn/prefer-object-from-entries": "warn"
+ },
+ "env": {
+ "node": true,
+ "builtin": true
+ },
+ "ignorePatterns": [
+ "node_modules/*"
+ ]
+}
diff --git a/.prettierrc.yaml b/.prettierrc.yaml
deleted file mode 100644
index 16c878e..0000000
--- a/.prettierrc.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-semi: false
-arrowParens: avoid
-insertPragma: false
-printWidth: 80
-proseWrap: preserve
-singleQuote: true
-trailingComma: none
-useTabs: false
-jsxBracketSameLine: false
-bracketSpacing: false
diff --git a/README.md b/README.md
index 242eb58..f2b2860 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ A lightweight and naïve library for decoding lightning network payment requests
It doesn't recover payee from signature, doesn't check signature, doesn't parse fallback addresses and doesn't do any encoding -- therefore dependencies are very minimal (no libsecp256k1 here).
-Code derived from [bolt11](https://npmjs.com/package/bolt11), which has the full functionality but it's a pain to run in browsers.
+Code derived from [bolt11](https://npmjs.com/package/bolt11), which has the full functionality, but it's a pain to run in browsers.
Spits out "sections" of the invoice, in a way that is used to make visualizations like https://bolt11.org/.
diff --git a/bolt11.d.ts b/bolt11.d.ts
deleted file mode 100644
index 74fd82a..0000000
--- a/bolt11.d.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-declare module "light-bolt11-decoder" {
- type NetworkSection = {
- name: 'coin_network';
- letters: string;
- value?: {
- bech32: string;
- pubKeyHash: number;
- scriptHash: number;
- validWitnessVersions: number[];
- };
- };
-
- type FeatureBits = {
- option_data_loss_protect: string;
- initial_routing_sync: string;
- option_upfront_shutdown_script: string;
- gossip_queries: string;
- var_onion_optin: string;
- gossip_queries_ex: string;
- option_static_remotekey: string;
- payment_secret: string;
- basic_mpp: string;
- option_support_large_channel: string;
- extra_bits: {
- start_bit: number;
- bits: unknown[];
- has_required: boolean;
- };
- };
-
- type RouteHint = {
- pubkey: string;
- short_channel_id: string;
- fee_base_msat: number;
- fee_proportional_millionths: number;
- cltv_expiry_delta: number;
- };
-
- type RouteHintSection = {
- name: "route_hint";
- tag: "r";
- letters: string;
- value: RouteHint[];
- };
-
- type FeatureBitsSection = {
- name: "feature_bits";
- tag: "9";
- letters: string;
- value: FeatureBits;
- };
-
- type Section =
- | { name: "paymentRequest"; value: string }
- | { name: "expiry"; value: number }
- | { name: "checksum"; letters: string }
- | NetworkSection
- | { name: "amount"; letters: string; value: string }
- | { name: "separator"; letters: string }
- | { name: "timestamp"; letters: string; value: number }
- | { name: "payment_hash"; tag: "p"; letters: string; value: string }
- | { name: "description"; tag: "d"; letters: string; value: string }
- | { name: "payment_secret"; tag: "s"; letters: string; value: string }
- | {
- name: "min_final_cltv_expiry";
- tag: "c";
- letters: string;
- value: number;
- }
- | FeatureBitsSection
- | RouteHintSection
- | { name: "signature"; letters: string; value: string };
-
- type PaymentJSON = {
- paymentRequest: string;
- sections: Section[];
- expiry: number;
- route_hints: RouteHint[][];
- };
-
- type DecodedInvoice = {
- paymentRequest: string;
- sections: Section[];
- expiry: number;
- route_hints: RouteHint[][];
- };
-
- function decode(invoice: string): DecodedInvoice;
-}
diff --git a/bolt11.js b/bolt11.js
index d1897ea..c402a85 100644
--- a/bolt11.js
+++ b/bolt11.js
@@ -1,6 +1,66 @@
-const {bech32, hex, utf8} = require('@scure/base')
+import {bech32, hex, utf8} from '@scure/base'
+
+/**
+ *
+ * @typedef {{bech32: string, pubKeyHash: number, scriptHash: number, validWitnessVersions: number[]}} Network
+ *
+ * @typedef {{
+ * name: 'coin_network',
+ * letters: string,
+ * value?: Network
+ * }} NetworkSection
+ *
+ * @typedef {{
+ * option_data_loss_protect: string,
+ * initial_routing_sync: string,
+ * option_upfront_shutdown_script: string,
+ * gossip_queries: string,
+ * var_onion_optin: string,
+ * gossip_queries_ex: string,
+ * option_static_remotekey: string,
+ * payment_secret: string,
+ * basic_mpp: string,
+ * option_support_large_channel: string,
+ * extra_bits: {
+ * start_bit: number,
+ * bits: unknown[],
+ * has_required: boolean
+ * }
+ * }} FeatureBits
+ *
+ * @typedef {{ pubkey: string, short_channel_id: string, fee_base_msat: number, fee_proportional_millionths: number, cltv_expiry_delta: number }} RouteHint
+ * @typedef {{ name: "route_hint", tag: "r", letters: string, value: RouteHint[] }} RouteHintSection
+ * @typedef {{ name: "feature_bits", tag: "9", letters: string, value: FeatureBits }} FeatureBitsSection
+ *
+ * @typedef {
+ * | { name: "paymentRequest", value: string }
+ * | { name: "expiry", value: number }
+ * | { name: "checksum", letters: string }
+ * | NetworkSection
+ * | { name: "amount", letters: string; value: string }
+ * | { name: "separator", letters: string }
+ * | { name: "timestamp", letters: string, value: number }
+ * | { name: "payment_hash", tag: "p", letters: string, value: string }
+ * | { name: "description", tag: "d", letters: string, value: string }
+ * | { name: "payment_secret", tag: "s", letters: string, value: string }
+ * | {
+ * name: "min_final_cltv_expiry",
+ * tag: "c",
+ * letters: string,
+ * value: number
+ * }
+ * | FeatureBitsSection
+ * | RouteHintSection
+ * | { name: "signature", letters: string, value: string }
+ * | { name: "lightning_network", letters: string }
+ * } Section
+ *
+ * @typedef {{ paymentRequest: string, sections: Section[], expiry: number, route_hints: RouteHint[][] }} DecodedInvoice
+ *
+ */
// defaults for encode; default timestamp is current time at call
+/** @type {Network} */
const DEFAULTNETWORK = {
// default network is bitcoin
bech32: 'bc',
@@ -8,31 +68,35 @@ const DEFAULTNETWORK = {
scriptHash: 0x05,
validWitnessVersions: [0]
}
+/** @type {Network} */
const TESTNETWORK = {
bech32: 'tb',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
}
+/** @type {Network} */
const SIGNETNETWORK = {
bech32: 'tbs',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
}
+/** @type {Network} */
const REGTESTNETWORK = {
bech32: 'bcrt',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
}
+/** @type {Network} */
const SIMNETWORK = {
bech32: 'sb',
pubKeyHash: 0x3f,
scriptHash: 0x7b,
validWitnessVersions: [0]
}
-
+/** @type {string[]} */
const FEATUREBIT_ORDER = [
'option_data_loss_protect',
'initial_routing_sync',
@@ -92,38 +156,50 @@ const TAGPARSERS = {
5: featureBitsParser // keep feature bits as array of 5 bit words
}
+/**
+ *
+ * @param {string} tagCode
+ * @returns {function(*): {tagCode: number, words: `unknown1${string}`}}
+ */
function getUnknownParser(tagCode) {
return words => ({
- tagCode: parseInt(tagCode),
+ tagCode: Number.parseInt(tagCode),
words: bech32.encode('unknown', words, Number.MAX_SAFE_INTEGER)
})
}
+/**
+ *
+ * @param {number[]} words
+ * @returns {*}
+ */
function wordsToIntBE(words) {
- return words.reverse().reduce((total, item, index) => {
- return total + item * Math.pow(32, index)
- }, 0)
+ return words.toReversed().reduce((total, item, index) => total + item * (32**index), 0)
}
-// first convert from words to buffer, trimming padding where necessary
-// parse in 51 byte chunks. See encoder for details.
+/**
+ * First convert from words to buffer, trimming padding where necessary
+ * parse in 51 byte chunks. See encoder for details.
+ * @param {number[]} words
+ * @returns {*[]}
+ */
function routingInfoParser(words) {
const routes = []
- let pubkey,
- shortChannelId,
- feeBaseMSats,
- feeProportionalMillionths,
- cltvExpiryDelta
+ let pubkey
+ let shortChannelId
+ let feeBaseMSats
+ let feeProportionalMillionths
+ let cltvExpiryDelta
let routesBuffer = bech32.fromWordsUnsafe(words)
while (routesBuffer.length > 0) {
pubkey = hex.encode(routesBuffer.slice(0, 33)) // 33 bytes
shortChannelId = hex.encode(routesBuffer.slice(33, 41)) // 8 bytes
- feeBaseMSats = parseInt(hex.encode(routesBuffer.slice(41, 45)), 16) // 4 bytes
- feeProportionalMillionths = parseInt(
+ feeBaseMSats = Number.parseInt(hex.encode(routesBuffer.slice(41, 45)), 16) // 4 bytes
+ feeProportionalMillionths = Number.parseInt(
hex.encode(routesBuffer.slice(45, 49)),
16
) // 4 bytes
- cltvExpiryDelta = parseInt(hex.encode(routesBuffer.slice(49, 51)), 16) // 2 bytes
+ cltvExpiryDelta = Number.parseInt(hex.encode(routesBuffer.slice(49, 51)), 16) // 2 bytes
routesBuffer = routesBuffer.slice(51)
@@ -138,10 +214,15 @@ function routingInfoParser(words) {
return routes
}
+/**
+ *
+ * @param {Uint8Array} words
+ * @returns {{}}
+ */
function featureBitsParser(words) {
const bools = words
.slice()
- .reverse()
+ .toReversed()
.map(word => [
!!(word & 0b1),
!!(word & 0b10),
@@ -150,23 +231,22 @@ function featureBitsParser(words) {
!!(word & 0b10000)
])
.reduce((finalArr, itemArr) => finalArr.concat(itemArr), [])
- while (bools.length < FEATUREBIT_ORDER.length * 2) {
+ while (bools.length < FEATUREBIT_ORDER.length * 2)
bools.push(false)
- }
const featureBits = {}
- FEATUREBIT_ORDER.forEach((featureName, index) => {
+ for (const featureName of FEATUREBIT_ORDER) {
+ const index = FEATUREBIT_ORDER.indexOf(featureName);
let status
- if (bools[index * 2]) {
+ if (bools[index * 2])
status = 'required'
- } else if (bools[index * 2 + 1]) {
+ else if (bools[index * 2 + 1])
status = 'supported'
- } else {
+ else
status = 'unsupported'
- }
featureBits[featureName] = status
- })
+ }
const extraBits = bools.slice(FEATUREBIT_ORDER.length * 2)
featureBits.extra_bits = {
@@ -174,7 +254,7 @@ function featureBitsParser(words) {
bits: extraBits,
has_required: extraBits.reduce(
(result, bit, index) =>
- index % 2 !== 0 ? result || false : result || bit,
+ index % 2 === 0 ? result || bit : result || false,
false
)
}
@@ -182,18 +262,23 @@ function featureBitsParser(words) {
return featureBits
}
+/**
+ *
+ * @param {string} hrpString
+ * @param {boolean} outputString
+ * @returns {string|bigint}
+ */
function hrpToMillisat(hrpString, outputString) {
let divisor, value
- if (hrpString.slice(-1).match(/^[munp]$/)) {
+ if (/^[munp]$/.test(hrpString.slice(-1))) {
divisor = hrpString.slice(-1)
value = hrpString.slice(0, -1)
- } else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) {
+ } else if (/^[^munp0-9]$/.test(hrpString.slice(-1)))
throw new Error('Not a valid multiplier for the amount')
- } else {
+ else
value = hrpString
- }
- if (!value.match(/^\d+$/))
+ if (!/^\d+$/.test(value))
throw new Error('Not a valid human readable amount')
const valueBN = BigInt(value)
@@ -205,31 +290,36 @@ function hrpToMillisat(hrpString, outputString) {
if (
(divisor === 'p' && !(valueBN % BigInt(10) === BigInt(0))) ||
millisatoshisBN > MAX_MILLISATS
- ) {
+ )
throw new Error('Amount is outside of valid range')
- }
return outputString ? millisatoshisBN.toString() : millisatoshisBN
}
-// decode will only have extra comments that aren't covered in encode comments.
-// also if anything is hard to read I'll comment.
+/**
+ * Decode will only have extra comments that aren't covered in encode comments.
+ * Also, if anything is hard to read I'll comment.
+ * @param {string} paymentRequest
+ * @param {Network=} network
+ * @returns {DecodedInvoice}
+ */
function decode(paymentRequest, network) {
if (typeof paymentRequest !== 'string')
throw new Error('Lightning Payment Request must be string')
if (paymentRequest.slice(0, 2).toLowerCase() !== 'ln')
throw new Error('Not a proper lightning payment request')
+ /** @type {Section[]} */
const sections = []
const decoded = bech32.decode(paymentRequest, Number.MAX_SAFE_INTEGER)
- paymentRequest = paymentRequest.toLowerCase()
+ const paymentRequest_lower = paymentRequest.toLowerCase()
const prefix = decoded.prefix
let words = decoded.words
- let letters = paymentRequest.slice(prefix.length + 1)
+ let letters = paymentRequest_lower.slice(prefix.length + 1)
let sigWords = words.slice(-104)
words = words.slice(0, -104)
- // Without reverse lookups, can't say that the multipier at the end must
+ // Without reverse lookups, can't say that the multiplier at the end must
// have a number before it, so instead we parse, and if the second group
// doesn't have anything, there's a good chance the last letter of the
// coin type got captured by the third group, so just re-regex without
@@ -237,9 +327,8 @@ function decode(paymentRequest, network) {
let prefixMatches = prefix.match(/^ln(\S+?)(\d*)([a-zA-Z]?)$/)
if (prefixMatches && !prefixMatches[2])
prefixMatches = prefix.match(/^ln(\S+)$/)
- if (!prefixMatches) {
+ if (!prefixMatches)
throw new Error('Not a proper lightning payment request')
- }
// "ln" section
sections.push({
@@ -250,7 +339,16 @@ function decode(paymentRequest, network) {
// "bc" section
const bech32Prefix = prefixMatches[1]
let coinNetwork
- if (!network) {
+ if (network) {
+ if (
+ network.bech32 === undefined ||
+ network.pubKeyHash === undefined ||
+ network.scriptHash === undefined ||
+ !Array.isArray(network.validWitnessVersions)
+ )
+ throw new Error('Invalid network')
+ coinNetwork = network
+ } else {
switch (bech32Prefix) {
case DEFAULTNETWORK.bech32:
coinNetwork = DEFAULTNETWORK
@@ -268,19 +366,10 @@ function decode(paymentRequest, network) {
coinNetwork = SIMNETWORK
break
}
- } else {
- if (
- network.bech32 === undefined ||
- network.pubKeyHash === undefined ||
- network.scriptHash === undefined ||
- !Array.isArray(network.validWitnessVersions)
- )
- throw new Error('Invalid network')
- coinNetwork = network
}
- if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {
+ if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix)
throw new Error('Unknown coin bech32 prefix')
- }
+
sections.push({
name: 'coin_network',
letters: bech32Prefix,
@@ -298,9 +387,8 @@ function decode(paymentRequest, network) {
letters: prefixMatches[2] + prefixMatches[3],
value: millisatoshis
})
- } else {
+ } else
millisatoshis = null
- }
// "1" separator
sections.push({
@@ -318,7 +406,10 @@ function decode(paymentRequest, network) {
})
letters = letters.slice(7)
- let tagName, parser, tagLength, tagWords
+ let tagName
+ let parser
+ let tagLength
+ let tagWords
// we have no tag count to go on, so just keep hacking off words
// until we have none.
while (words.length > 0) {
@@ -371,10 +462,9 @@ function decode(paymentRequest, network) {
}
for (let name in TAGCODES) {
- if (name === 'route_hint') {
+ if (name === 'route_hint')
// route hints can be multiple, so this won't work for them
continue
- }
Object.defineProperty(result, name, {
get() {
@@ -385,13 +475,18 @@ function decode(paymentRequest, network) {
return result
+ /**
+ *
+ * @param {string} name
+ * @returns {*|undefined}
+ */
function getValue(name) {
let section = sections.find(s => s.name === name)
return section ? section.value : undefined
}
}
-module.exports = {
+export {
decode,
hrpToMillisat
}
diff --git a/examples/.oxlintrc.json b/examples/.oxlintrc.json
new file mode 100644
index 0000000..777d298
--- /dev/null
+++ b/examples/.oxlintrc.json
@@ -0,0 +1,106 @@
+{
+ "plugins": [
+ "import",
+ "jsdoc",
+ "jsx-a11y",
+ "node",
+ "oxc",
+ "promise",
+ "react",
+ "react-perf",
+ "unicorn"
+ ],
+ "categories": {
+ "correctness": "warn",
+ "suspicious": "warn",
+ "perf": "warn",
+ "pedantic": "warn"
+ },
+ "files": ["**/*.js"],
+ "rules": {
+ "eslint/array-callback-return": ["warn", { "checkForEach": true }],
+ "eslint/eqeqeq": ["warn", "always", {"null": "ignore"}],
+ "eslint/func-style": ["off", "expression"],
+ "eslint/guard-for-in": "warn",
+ "eslint/no-duplicate-imports": ["warn", { "includeExports": true }],
+ "eslint/no-empty": "warn",
+ "eslint/no-empty-function": "warn",
+ "eslint/no-iterator": "warn",
+ "eslint/no-lone-blocks": "warn",
+ "eslint/no-multi-assign": "warn",
+ "eslint/no-multi-str": "warn",
+ "eslint/no-nested-ternary": "warn",
+ "eslint/no-new-func": "warn",
+ "eslint/no-proto": "warn",
+ "eslint/no-return-assign": ["warn", "always"],
+ "eslint/no-script-url": "warn",
+ "eslint/no-unused-expressions": "warn",
+ "eslint/no-var": "warn",
+ "eslint/no-void": "warn",
+ "eslint/prefer-exponentiation-operator": "warn",
+ "eslint/prefer-numeric-literals": "warn",
+ "eslint/prefer-object-has-own": "warn",
+ "eslint/prefer-object-spread": "warn",
+ "eslint/prefer-spread": "warn",
+ "eslint/yoda": "warn",
+ "eslint/max-depth": "off",
+ "eslint/max-classes-per-file": "off",
+ "eslint/max-lines": "off",
+ "eslint/max-lines-per-function": "off",
+ "eslint/max-nested-callbacks": "off",
+ "eslint/new-cap": "off",
+ "eslint/radix": "off",
+ "import/consistent-type-specifier-style": "warn",
+ "import/exports-last": "warn",
+ "import/first": "warn",
+ "import/group-exports": "warn",
+ "import/no-amd": "warn",
+ "import/no-commonjs": "warn",
+ "import/no-cycle": "warn",
+ "import/no-dynamic-require": "warn",
+ "import/no-mutable-exports": "warn",
+ "import/no-named-default": "warn",
+ "import/no-webpack-loader-syntax": "warn",
+ "import/unambiguous": "warn",
+ "import/max-dependencies": "off",
+ "jsdoc/check-access": "warn",
+ "jsdoc/empty-tags": "warn",
+ "jsdoc/require-param-description": "off",
+ "jsdoc/require-returns-description": "off",
+ "node/no-new-require": "warn",
+ "oxc/bad-bitwise-operator": "warn",
+ "oxc/no-barrel-file": "warn",
+ "promise/prefer-await-to-callbacks": "warn",
+ "promise/prefer-await-to-then": "warn",
+ "promise/spec-only": "warn",
+ "react/button-has-type": "warn",
+ "react/no-danger": "warn",
+ "react/no-unknown-property": "warn",
+ "react/require-render-return": "warn",
+ "react/prefer-es6-class": "warn",
+ "react/self-closing-comp": "warn",
+ "unicorn/no-abusive-eslint-disable": "warn",
+ "unicorn/no-anonymous-default-export": "warn",
+ "unicorn/no-array-for-each": "warn",
+ "unicorn/no-array-method-this-argument": "warn",
+ "unicorn/no-array-reduce": "warn",
+ "unicorn/no-document-cookie": "warn",
+ "unicorn/no-for-loop": "warn",
+ "unicorn/no-length-as-slice-end": "warn",
+ "unicorn/no-nested-ternary": "warn",
+ "unicorn/prefer-array-index-of": "warn",
+ "unicorn/prefer-global-this": "warn",
+ "unicorn/prefer-modern-math-apis": "warn",
+ "unicorn/prefer-node-protocol": "warn",
+ "unicorn/prefer-number-properties": "warn",
+ "unicorn/prefer-object-from-entries": "warn"
+ },
+ "env": {
+ "builtin": true,
+ "browser": true,
+ "node": true
+ },
+ "ignorePatterns": [
+ "node_modules/*"
+ ]
+}
diff --git a/examples/build.js b/examples/build.js
deleted file mode 100755
index 52bf337..0000000
--- a/examples/build.js
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env node
-
-const esbuild = require('esbuild')
-const nodeGlobals = require('@esbuild-plugins/node-globals-polyfill').default
-
-esbuild
- .build({
- entryPoints: ['demo.jsx'],
- outfile: 'demo.build.js',
- bundle: true,
- plugins: [nodeGlobals({buffer: true})],
- define: {
- window: 'self',
- global: 'self'
- },
- sourcemap: 'inline'
- })
- .then(() => console.log('build success.'))
diff --git a/examples/demo.jsx b/examples/demo.jsx
index 501d216..5f45894 100644
--- a/examples/demo.jsx
+++ b/examples/demo.jsx
@@ -1,8 +1,8 @@
import {decode} from 'light-bolt11-decoder'
import React, {useState} from 'react'
-import {render} from 'react-dom'
+import {createRoot} from 'react-dom/client'
import useComputedState from 'use-computed-state'
-import styled, {css} from 'styled-components'
+import {styled, css} from 'styled-components'
const TAGCOLORS = {
lightning_network: 'rgb(31, 31, 40)',
@@ -118,4 +118,5 @@ function Demo() {
)
}
-render(