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

cameri / nostream / 24610405788

18 Apr 2026 05:50PM UTC coverage: 62.882%. First build
24610405788

Pull #500

github

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

837 of 1426 branches covered (58.7%)

Branch coverage included in aggregate %.

55 of 97 new or added lines in 4 files covered. (56.7%)

2165 of 3348 relevant lines covered (64.67%)

11.4 hits per line

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

81.4
/src/utils/transform.ts
1
import {
2✔
2
  always,
3
  applySpec,
4
  cond,
5
  equals,
6
  ifElse,
7
  is,
8
  isNil,
9
  multiply,
10
  path,
11
  pathSatisfies,
12
  pipe,
13
  prop,
14
  propSatisfies,
15
  T,
16
} from 'ramda'
17

18
import { Invoice, InvoiceStatus, InvoiceUnit } from '../@types/invoice'
2✔
19
import { User } from '../@types/user'
20

21
const BECH32_ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
2✔
22
const BECH32_ALPHABET_MAP: Record<string, number> = {}
2✔
23
for (let i = 0; i < BECH32_ALPHABET.length; i++) { BECH32_ALPHABET_MAP[BECH32_ALPHABET[i]] = i }
64✔
24

25
function bech32PolymodStep(pre: number): number {
26
  const b = pre >> 25
545✔
27
  return (((pre & 0x1ffffff) << 5) ^
545✔
28
    (-((b >> 0) & 1) & 0x3b6a57b2) ^
29
    (-((b >> 1) & 1) & 0x26508e6d) ^
30
    (-((b >> 2) & 1) & 0x1ea119fa) ^
31
    (-((b >> 3) & 1) & 0x3d4233dd) ^
32
    (-((b >> 4) & 1) & 0x2a1462b3))
33
}
34

35
function bech32PrefixChk(prefix: string): number {
36
  let chk = 1
9✔
37
  for (let i = 0; i < prefix.length; ++i) {
9✔
38
    const c = prefix.charCodeAt(i)
36✔
39
    chk = bech32PolymodStep(chk) ^ (c >> 5)
36✔
40
  }
41
  chk = bech32PolymodStep(chk)
9✔
42
  for (let i = 0; i < prefix.length; ++i) {
9✔
43
    chk = bech32PolymodStep(chk) ^ (prefix.charCodeAt(i) & 0x1f)
36✔
44
  }
45
  return chk
9✔
46
}
47

48
function bech32Convert(data: number[], inBits: number, outBits: number, pad: boolean): number[] {
49
  let value = 0, bits = 0
8✔
50
  const maxV = (1 << outBits) - 1
8✔
51
  const result: number[] = []
8✔
52
  for (const byte of data) {
8✔
53
    value = (value << inBits) | byte
256✔
54
    bits += inBits
256✔
55
    while (bits >= outBits) {
256✔
56
      bits -= outBits
408✔
57
      result.push((value >> bits) & maxV)
408✔
58
    }
59
  }
60
  if (pad && bits > 0) { result.push((value << (outBits - bits)) & maxV) }
8!
61
  return result
8✔
62
}
63

64
function bech32Decode(str: string): { prefix: string; words: number[] } {
65
  const lower = str.toLowerCase()
1✔
66
  const split = lower.lastIndexOf('1')
1✔
67
  if (split < 1 || split + 7 > str.length) { throw new Error(`Invalid bech32: ${str}`) }
1!
68
  const prefix = lower.slice(0, split)
1✔
69
  const wordChars = lower.slice(split + 1)
1✔
70
  let chk = bech32PrefixChk(prefix)
1✔
71
  const words: number[] = []
1✔
72
  for (let i = 0; i < wordChars.length; ++i) {
1✔
73
    const v = BECH32_ALPHABET_MAP[wordChars[i]]
1✔
74
    if (v === undefined) { throw new Error(`Unknown bech32 character: ${wordChars[i]}`) }
1!
NEW
75
    chk = bech32PolymodStep(chk) ^ v
×
NEW
76
    if (i + 6 < wordChars.length) { words.push(v) }
×
77
  }
NEW
78
  if (chk !== 1) { throw new Error('Invalid bech32 checksum') }
×
NEW
79
  return { prefix, words }
×
80
}
81

82
function bech32Encode(prefix: string, words: number[]): string {
83
  prefix = prefix.toLowerCase()
8✔
84
  let chk = bech32PrefixChk(prefix)
8✔
85
  let result = prefix + '1'
8✔
86
  for (const w of words) {
8✔
87
    chk = bech32PolymodStep(chk) ^ w
416✔
88
    result += BECH32_ALPHABET[w]
416✔
89
  }
90
  for (let i = 0; i < 6; ++i) { chk = bech32PolymodStep(chk) }
48✔
91
  chk ^= 1
8✔
92
  for (let i = 0; i < 6; ++i) { result += BECH32_ALPHABET[(chk >> ((5 - i) * 5)) & 0x1f] }
48✔
93
  return result
8✔
94
}
95

96
export const toJSON = (input: any) => JSON.stringify(input)
36✔
97

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

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

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

104
export const fromBigInt = (input: bigint) => input.toString()
2✔
105

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

108
export const fromDBInvoice = applySpec<Invoice>({
2✔
109
  id: prop('id') as () => string,
110
  pubkey: pipe(prop('pubkey') as () => Buffer, fromBuffer),
111
  bolt11: prop('bolt11'),
112
  amountRequested: pipe(prop('amount_requested') as () => string, toBigInt),
113
  amountPaid: ifElse(
114
    propSatisfies(isNil, 'amount_paid'),
115
    always(undefined),
116
    pipe(prop('amount_paid') as () => string, toBigInt),
117
  ),
118
  unit: prop('unit'),
119
  status: prop('status'),
120
  description: prop('description'),
121
  confirmedAt: prop('confirmed_at'),
122
  expiresAt: prop('expires_at'),
123
  updatedAt: prop('updated_at'),
124
  createdAt: prop('created_at'),
125
  verifyURL: prop('verify_url'),
126
})
127

128
export const fromDBUser = applySpec<User>({
2✔
129
  pubkey: pipe(prop('pubkey') as () => Buffer, fromBuffer),
130
  isAdmitted: prop('is_admitted'),
131
  isVanished: prop('is_vanished'),
132
  balance: prop('balance'),
133
  createdAt: prop('created_at'),
134
  updatedAt: prop('updated_at'),
135
})
136

137
export const fromBech32 = (input: string) => {
2✔
138
  const { prefix, words } = bech32Decode(input)
1✔
139
  if (!input.startsWith(prefix)) {
×
140
    throw new Error(`Bech32 invalid prefix: ${prefix}`)
×
141
  }
142

NEW
143
  return Buffer.from(
×
144
    bech32Convert(words, 5, 8, false).slice(0, 32)
145
  ).toString('hex')
146
}
147

148
export const toBech32 = (prefix: string) => (input: string): string => {
9✔
149
  return bech32Encode(prefix, bech32Convert(Array.from(Buffer.from(input, 'hex')), 8, 5, true))
8✔
150
}
151

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

154
export const fromZebedeeInvoice = applySpec<Invoice>({
2✔
155
  id: prop('id'),
156
  pubkey: prop('internalId'),
157
  bolt11: path(['invoice', 'request']),
158
  amountRequested: pipe(prop('amount') as () => string, toBigInt),
159
  description: prop('description'),
160
  unit: prop('unit'),
161
  status: prop('status'),
162
  expiresAt: ifElse(propSatisfies(is(String), 'expiresAt'), pipe(prop('expiresAt'), toDate), always(null)),
163
  confirmedAt: ifElse(propSatisfies(is(String), 'confirmedAt'), pipe(prop('confirmedAt'), toDate), always(null)),
164
  createdAt: ifElse(propSatisfies(is(String), 'createdAt'), pipe(prop('createdAt'), toDate), always(null)),
165
  rawResponse: toJSON,
166
})
167

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

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

© 2026 Coveralls, Inc