• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cameri / nostream / 24610290453

18 Apr 2026 05:44PM UTC coverage: 62.867%. First build
24610290453

Pull #500

github

web-flow
Merge 25392682c into 9084e6832
Pull Request #500: chore: inline 5 dependencies, removing external packages

837 of 1426 branches covered (58.7%)

Branch coverage included in aggregate %.

59 of 102 new or added lines in 5 files covered. (57.84%)

2163 of 3346 relevant lines covered (64.64%)

11.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.4
/src/utils/transform.ts
1
import { always, applySpec, cond, equals, ifElse, is, isNil, multiply, path, pathSatisfies, pipe, prop, propSatisfies, T } from 'ramda'
2✔
2

3
import { Invoice, InvoiceStatus, InvoiceUnit } from '../@types/invoice'
2✔
4
import { User } from '../@types/user'
5

6
const BECH32_ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
2✔
7
const BECH32_ALPHABET_MAP: Record<string, number> = {}
2✔
8
for (let i = 0; i < BECH32_ALPHABET.length; i++) { BECH32_ALPHABET_MAP[BECH32_ALPHABET[i]] = i }
64✔
9

10
function bech32PolymodStep(pre: number): number {
11
  const b = pre >> 25
545✔
12
  return (((pre & 0x1ffffff) << 5) ^
545✔
13
    (-((b >> 0) & 1) & 0x3b6a57b2) ^
14
    (-((b >> 1) & 1) & 0x26508e6d) ^
15
    (-((b >> 2) & 1) & 0x1ea119fa) ^
16
    (-((b >> 3) & 1) & 0x3d4233dd) ^
17
    (-((b >> 4) & 1) & 0x2a1462b3))
18
}
19

20
function bech32PrefixChk(prefix: string): number {
21
  let chk = 1
9✔
22
  for (let i = 0; i < prefix.length; ++i) {
9✔
23
    const c = prefix.charCodeAt(i)
36✔
24
    chk = bech32PolymodStep(chk) ^ (c >> 5)
36✔
25
  }
26
  chk = bech32PolymodStep(chk)
9✔
27
  for (let i = 0; i < prefix.length; ++i) {
9✔
28
    chk = bech32PolymodStep(chk) ^ (prefix.charCodeAt(i) & 0x1f)
36✔
29
  }
30
  return chk
9✔
31
}
32

33
function bech32Convert(data: number[], inBits: number, outBits: number, pad: boolean): number[] {
34
  let value = 0, bits = 0
8✔
35
  const maxV = (1 << outBits) - 1
8✔
36
  const result: number[] = []
8✔
37
  for (const byte of data) {
8✔
38
    value = (value << inBits) | byte
256✔
39
    bits += inBits
256✔
40
    while (bits >= outBits) {
256✔
41
      bits -= outBits
408✔
42
      result.push((value >> bits) & maxV)
408✔
43
    }
44
  }
45
  if (pad && bits > 0) { result.push((value << (outBits - bits)) & maxV) }
8!
46
  return result
8✔
47
}
48

49
function bech32Decode(str: string): { prefix: string; words: number[] } {
50
  const lower = str.toLowerCase()
1✔
51
  const split = lower.lastIndexOf('1')
1✔
52
  if (split < 1 || split + 7 > str.length) { throw new Error(`Invalid bech32: ${str}`) }
1!
53
  const prefix = lower.slice(0, split)
1✔
54
  const wordChars = lower.slice(split + 1)
1✔
55
  let chk = bech32PrefixChk(prefix)
1✔
56
  const words: number[] = []
1✔
57
  for (let i = 0; i < wordChars.length; ++i) {
1✔
58
    const v = BECH32_ALPHABET_MAP[wordChars[i]]
1✔
59
    if (v === undefined) { throw new Error(`Unknown bech32 character: ${wordChars[i]}`) }
1!
NEW
60
    chk = bech32PolymodStep(chk) ^ v
×
NEW
61
    if (i + 6 < wordChars.length) { words.push(v) }
×
62
  }
NEW
63
  if (chk !== 1) { throw new Error('Invalid bech32 checksum') }
×
NEW
64
  return { prefix, words }
×
65
}
66

67
function bech32Encode(prefix: string, words: number[]): string {
68
  prefix = prefix.toLowerCase()
8✔
69
  let chk = bech32PrefixChk(prefix)
8✔
70
  let result = prefix + '1'
8✔
71
  for (const w of words) {
8✔
72
    chk = bech32PolymodStep(chk) ^ w
416✔
73
    result += BECH32_ALPHABET[w]
416✔
74
  }
75
  for (let i = 0; i < 6; ++i) { chk = bech32PolymodStep(chk) }
48✔
76
  chk ^= 1
8✔
77
  for (let i = 0; i < 6; ++i) { result += BECH32_ALPHABET[(chk >> ((5 - i) * 5)) & 0x1f] }
48✔
78
  return result
8✔
79
}
80

81
export const toJSON = (input: any) => JSON.stringify(input)
36✔
82

83
export const toBuffer = (input: any) => Buffer.from(input, 'hex')
334✔
84

85
export const fromBuffer = (input: Buffer) => input.toString('hex')
115✔
86

87
export const toBigInt = (input: string | number): bigint => BigInt(input)
14✔
88

89
export const fromBigInt = (input: bigint) => input.toString()
2✔
90

91
const addTime = (ms: number) => (input: Date) => new Date(input.getTime() + ms)
5✔
92

93
export const fromDBInvoice = applySpec<Invoice>({
2✔
94
  id: prop('id') as () => string,
95
  pubkey: pipe(prop('pubkey') as () => Buffer, fromBuffer),
96
  bolt11: prop('bolt11'),
97
  amountRequested: pipe(prop('amount_requested') as () => string, toBigInt),
98
  amountPaid: ifElse(
99
    propSatisfies(isNil, 'amount_paid'),
100
    always(undefined),
101
    pipe(prop('amount_paid') as () => string, toBigInt),
102
  ),
103
  unit: prop('unit'),
104
  status: prop('status'),
105
  description: prop('description'),
106
  confirmedAt: prop('confirmed_at'),
107
  expiresAt: prop('expires_at'),
108
  updatedAt: prop('updated_at'),
109
  createdAt: prop('created_at'),
110
  verifyURL: prop('verify_url'),
111
})
112

113
export const fromDBUser = applySpec<User>({
2✔
114
  pubkey: pipe(prop('pubkey') as () => Buffer, fromBuffer),
115
  isAdmitted: prop('is_admitted'),
116
  isVanished: prop('is_vanished'),
117
  balance: prop('balance'),
118
  createdAt: prop('created_at'),
119
  updatedAt: prop('updated_at'),
120
})
121

122
export const fromBech32 = (input: string) => {
2✔
123
  const { prefix, words } = bech32Decode(input)
1✔
124
  if (!input.startsWith(prefix)) {
×
125
    throw new Error(`Bech32 invalid prefix: ${prefix}`)
×
126
  }
127

NEW
128
  return Buffer.from(
×
129
    bech32Convert(words, 5, 8, false).slice(0, 32)
130
  ).toString('hex')
131
}
132

133
export const toBech32 = (prefix: string) => (input: string): string => {
9✔
134
  return bech32Encode(prefix, bech32Convert(Array.from(Buffer.from(input, 'hex')), 8, 5, true))
8✔
135
}
136

137
export const toDate = (input: string | number) => new Date(input)
25✔
138

139
export const fromZebedeeInvoice = applySpec<Invoice>({
2✔
140
  id: prop('id'),
141
  pubkey: prop('internalId'),
142
  bolt11: path(['invoice', 'request']),
143
  amountRequested: pipe(prop('amount') as () => string, toBigInt),
144
  description: prop('description'),
145
  unit: prop('unit'),
146
  status: prop('status'),
147
  expiresAt: ifElse(
148
    propSatisfies(is(String), 'expiresAt'),
149
    pipe(prop('expiresAt'), toDate),
150
    always(null),
151
  ),
152
  confirmedAt: ifElse(
153
    propSatisfies(is(String), 'confirmedAt'),
154
    pipe(prop('confirmedAt'), toDate),
155
    always(null),
156
  ),
157
  createdAt: ifElse(
158
    propSatisfies(is(String), 'createdAt'),
159
    pipe(prop('createdAt'), toDate),
160
    always(null),
161
  ),
162
  rawResponse: toJSON,
163
})
164

165
export const fromNodelessInvoice = applySpec<Invoice>({
2✔
166
  id: prop('id'),
167
  pubkey: path(['metadata', 'requestId']),
168
  bolt11: prop('lightningInvoice'),
169
  amountRequested: pipe(prop('satsAmount') as () => number, toBigInt),
170
  description: path(['metadata', 'description']),
171
  unit: path(['metadata', 'unit']),
172
  status: pipe(
173
    prop('status'),
174
    cond([
175
      [equals('new'), always(InvoiceStatus.PENDING)],
176
      [equals('pending_confirmation'), always(InvoiceStatus.PENDING)],
177
      [equals('underpaid'), always(InvoiceStatus.PENDING)],
178
      [equals('in_flight'), always(InvoiceStatus.PENDING)],
179
      [equals('paid'), always(InvoiceStatus.COMPLETED)],
180
      [equals('overpaid'), always(InvoiceStatus.COMPLETED)],
181
      [equals('expired'), always(InvoiceStatus.EXPIRED)],
182
    ]),
183
  ),
184
  expiresAt: ifElse(
185
    propSatisfies(is(String), 'expiresAt'),
186
    pipe(prop('expiresAt'), toDate),
187
    ifElse(
188
      propSatisfies(is(String), 'createdAt'),
189
      pipe(prop('createdAt'), toDate, addTime(15 * 60000)),
190
      always(null),
191
    ),
192
  ),
193
  confirmedAt: cond([
194
    [propSatisfies(is(String), 'paidAt'), pipe(prop('paidAt'), toDate)],
195
    [T, always(null)],
196
  ]),
197
  createdAt: ifElse(
198
    propSatisfies(is(String), 'createdAt'),
199
    pipe(prop('createdAt'), toDate),
200
    always(null),
201
  ),
202
  // rawResponse: toJSON,
203
})
204

205
export const fromOpenNodeInvoice = applySpec<Invoice>({
2✔
206
  id: prop('id'),
207
  pubkey: prop('order_id'),
208
  bolt11: ifElse(
209
    pathSatisfies(is(String), ['lightning_invoice', 'payreq']),
210
    path(['lightning_invoice', 'payreq']),
211
    path(['lightning', 'payreq'])
212
  ),
213
  amountRequested: pipe(
214
    ifElse(
215
      propSatisfies(is(Number), 'amount'),
216
      prop('amount'),
217
      prop('price'),
218
    ) as () => number,
219
    toBigInt,
220
  ),
221
  description: prop('description'),
222
  unit: always(InvoiceUnit.SATS),
223
  status: pipe(
224
    prop('status'),
225
    cond([
226
      [equals('expired'), always(InvoiceStatus.EXPIRED)],
227
      [equals('refunded'), always(InvoiceStatus.EXPIRED)],
228
      [equals('unpaid'), always(InvoiceStatus.PENDING)],
229
      [equals('processing'), always(InvoiceStatus.PENDING)],
230
      [equals('underpaid'), always(InvoiceStatus.PENDING)],
231
      [equals('paid'), always(InvoiceStatus.COMPLETED)],
232
    ]),
233
  ),
234
  expiresAt: pipe(
235
    cond([
236
      [pathSatisfies(is(String), ['lightning', 'expires_at']), path(['lightning', 'expires_at'])],
237
      [pathSatisfies(is(Number), ['lightning_invoice', 'expires_at']), pipe(path(['lightning_invoice', 'expires_at']), multiply(1000))],
238
    ]),
239
    toDate,
240
  ),
241
  confirmedAt: cond([
242
    [propSatisfies(equals('paid'), 'status'), () => new Date()],
3✔
243
    [T, always(null)],
244
  ]),
245
  createdAt: pipe(
246
    ifElse(
247
      propSatisfies(is(Number), 'created_at'),
248
      pipe(prop('created_at'), multiply(1000)),
249
      prop('created_at'),
250
    ),
251
    toDate,
252
  ),
253
  rawResponse: toJSON,
254
})
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc