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

supabase / auth-js / 21284056506

23 Jan 2026 11:08AM UTC coverage: 69.764% (-0.9%) from 70.669%
21284056506

Pull #1131

github

web-flow
Merge 3516059e3 into 25c6b4271
Pull Request #1131: chore(deps-dev): bump jws from 3.2.2 to 3.2.3

1070 of 1681 branches covered (63.65%)

Branch coverage included in aggregate %.

1475 of 1967 relevant lines covered (74.99%)

69.09 hits per line

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

98.13
/src/lib/base64url.ts
1
/**
2
 * Avoid modifying this file. It's part of
3
 * https://github.com/supabase-community/base64url-js.  Submit all fixes on
4
 * that repo!
5
 */
6

7
import { Uint8Array_ } from './webauthn.dom'
8

9
/**
10
 * An array of characters that encode 6 bits into a Base64-URL alphabet
11
 * character.
12
 */
13
const TO_BASE64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split('')
8✔
14

15
/**
16
 * An array of characters that can appear in a Base64-URL encoded string but
17
 * should be ignored.
18
 */
19
const IGNORE_BASE64URL = ' \t\n\r='.split('')
8✔
20

21
/**
22
 * An array of 128 numbers that map a Base64-URL character to 6 bits, or if -2
23
 * used to skip the character, or if -1 used to error out.
24
 */
25
const FROM_BASE64URL = (() => {
8✔
26
  const charMap: number[] = new Array(128)
8✔
27

28
  for (let i = 0; i < charMap.length; i += 1) {
8✔
29
    charMap[i] = -1
1,024✔
30
  }
31

32
  for (let i = 0; i < IGNORE_BASE64URL.length; i += 1) {
8✔
33
    charMap[IGNORE_BASE64URL[i].charCodeAt(0)] = -2
40✔
34
  }
35

36
  for (let i = 0; i < TO_BASE64URL.length; i += 1) {
8✔
37
    charMap[TO_BASE64URL[i].charCodeAt(0)] = i
512✔
38
  }
39

40
  return charMap
8✔
41
})()
42

43
/**
44
 * Converts a byte to a Base64-URL string.
45
 *
46
 * @param byte The byte to convert, or null to flush at the end of the byte sequence.
47
 * @param state The Base64 conversion state. Pass an initial value of `{ queue: 0, queuedBits: 0 }`.
48
 * @param emit A function called with the next Base64 character when ready.
49
 */
50
export function byteToBase64URL(
8✔
51
  byte: number | null,
52
  state: { queue: number; queuedBits: number },
53
  emit: (char: string) => void
54
) {
55
  if (byte !== null) {
294✔
56
    state.queue = (state.queue << 8) | byte
285✔
57
    state.queuedBits += 8
285✔
58

59
    while (state.queuedBits >= 6) {
285✔
60
      const pos = (state.queue >> (state.queuedBits - 6)) & 63
377✔
61
      emit(TO_BASE64URL[pos])
377✔
62
      state.queuedBits -= 6
377✔
63
    }
64
  } else if (state.queuedBits > 0) {
9✔
65
    state.queue = state.queue << (6 - state.queuedBits)
6✔
66
    state.queuedBits = 6
6✔
67

68
    while (state.queuedBits >= 6) {
6✔
69
      const pos = (state.queue >> (state.queuedBits - 6)) & 63
6✔
70
      emit(TO_BASE64URL[pos])
6✔
71
      state.queuedBits -= 6
6✔
72
    }
73
  }
74
}
75

76
/**
77
 * Converts a String char code (extracted using `string.charCodeAt(position)`) to a sequence of Base64-URL characters.
78
 *
79
 * @param charCode The char code of the JavaScript string.
80
 * @param state The Base64 state. Pass an initial value of `{ queue: 0, queuedBits: 0 }`.
81
 * @param emit A function called with the next byte.
82
 */
83
export function byteFromBase64URL(
8✔
84
  charCode: number,
85
  state: { queue: number; queuedBits: number },
86
  emit: (byte: number) => void
87
) {
88
  const bits = FROM_BASE64URL[charCode]
6,124✔
89

90
  if (bits > -1) {
6,124✔
91
    // valid Base64-URL character
92
    state.queue = (state.queue << 6) | bits
6,091✔
93
    state.queuedBits += 6
6,091✔
94

95
    while (state.queuedBits >= 8) {
6,091✔
96
      emit((state.queue >> (state.queuedBits - 8)) & 0xff)
4,562✔
97
      state.queuedBits -= 8
4,562✔
98
    }
99
  } else if (bits === -2) {
33✔
100
    // ignore spaces, tabs, newlines, =
101
    return
32✔
102
  } else {
103
    throw new Error(`Invalid Base64-URL character "${String.fromCharCode(charCode)}"`)
1✔
104
  }
105
}
106

107
/**
108
 * Converts a JavaScript string (which may include any valid character) into a
109
 * Base64-URL encoded string. The string is first encoded in UTF-8 which is
110
 * then encoded as Base64-URL.
111
 *
112
 * @param str The string to convert.
113
 */
114
export function stringToBase64URL(str: string) {
8✔
115
  const base64: string[] = []
8✔
116

117
  const emitter = (char: string) => {
8✔
118
    base64.push(char)
379✔
119
  }
120

121
  const state = { queue: 0, queuedBits: 0 }
8✔
122

123
  stringToUTF8(str, (byte: number) => {
8✔
124
    byteToBase64URL(byte, state, emitter)
282✔
125
  })
126

127
  byteToBase64URL(null, state, emitter)
8✔
128

129
  return base64.join('')
8✔
130
}
131

132
/**
133
 * Converts a Base64-URL encoded string into a JavaScript string. It is assumed
134
 * that the underlying string has been encoded as UTF-8.
135
 *
136
 * @param str The Base64-URL encoded string.
137
 */
138
export function stringFromBase64URL(str: string) {
8✔
139
  const conv: string[] = []
27✔
140

141
  const utf8Emit = (codepoint: number) => {
27✔
142
    conv.push(String.fromCodePoint(codepoint))
3,880✔
143
  }
144

145
  const utf8State = {
27✔
146
    utf8seq: 0,
147
    codepoint: 0,
148
  }
149

150
  const b64State = { queue: 0, queuedBits: 0 }
27✔
151

152
  const byteEmit = (byte: number) => {
27✔
153
    stringFromUTF8(byte, utf8State, utf8Emit)
4,015✔
154
  }
155

156
  for (let i = 0; i < str.length; i += 1) {
27✔
157
    byteFromBase64URL(str.charCodeAt(i), b64State, byteEmit)
5,391✔
158
  }
159

160
  return conv.join('')
26✔
161
}
162

163
/**
164
 * Converts a Unicode codepoint to a multi-byte UTF-8 sequence.
165
 *
166
 * @param codepoint The Unicode codepoint.
167
 * @param emit      Function which will be called for each UTF-8 byte that represents the codepoint.
168
 */
169
export function codepointToUTF8(codepoint: number, emit: (byte: number) => void) {
8✔
170
  if (codepoint <= 0x7f) {
148✔
171
    emit(codepoint)
73✔
172
    return
73✔
173
  } else if (codepoint <= 0x7ff) {
75✔
174
    emit(0xc0 | (codepoint >> 6))
15✔
175
    emit(0x80 | (codepoint & 0x3f))
15✔
176
    return
15✔
177
  } else if (codepoint <= 0xffff) {
60✔
178
    emit(0xe0 | (codepoint >> 12))
57✔
179
    emit(0x80 | ((codepoint >> 6) & 0x3f))
57✔
180
    emit(0x80 | (codepoint & 0x3f))
57✔
181
    return
57✔
182
  } else if (codepoint <= 0x10ffff) {
3✔
183
    emit(0xf0 | (codepoint >> 18))
2✔
184
    emit(0x80 | ((codepoint >> 12) & 0x3f))
2✔
185
    emit(0x80 | ((codepoint >> 6) & 0x3f))
2✔
186
    emit(0x80 | (codepoint & 0x3f))
2✔
187
    return
2✔
188
  }
189

190
  throw new Error(`Unrecognized Unicode codepoint: ${codepoint.toString(16)}`)
1✔
191
}
192

193
/**
194
 * Converts a JavaScript string to a sequence of UTF-8 bytes.
195
 *
196
 * @param str  The string to convert to UTF-8.
197
 * @param emit Function which will be called for each UTF-8 byte of the string.
198
 */
199
export function stringToUTF8(str: string, emit: (byte: number) => void) {
8✔
200
  for (let i = 0; i < str.length; i += 1) {
8✔
201
    let codepoint = str.charCodeAt(i)
147✔
202

203
    if (codepoint > 0xd7ff && codepoint <= 0xdbff) {
147✔
204
      // most UTF-16 codepoints are Unicode codepoints, except values in this
205
      // range where the next UTF-16 codepoint needs to be combined with the
206
      // current one to get the Unicode codepoint
207
      const highSurrogate = ((codepoint - 0xd800) * 0x400) & 0xffff
2✔
208
      const lowSurrogate = (str.charCodeAt(i + 1) - 0xdc00) & 0xffff
2✔
209
      codepoint = (lowSurrogate | highSurrogate) + 0x10000
2✔
210
      i += 1
2✔
211
    }
212

213
    codepointToUTF8(codepoint, emit)
147✔
214
  }
215
}
216

217
/**
218
 * Converts a UTF-8 byte to a Unicode codepoint.
219
 *
220
 * @param byte  The UTF-8 byte next in the sequence.
221
 * @param state The shared state between consecutive UTF-8 bytes in the
222
 *              sequence, an object with the shape `{ utf8seq: 0, codepoint: 0 }`.
223
 * @param emit  Function which will be called for each codepoint.
224
 */
225
export function stringFromUTF8(
8✔
226
  byte: number,
227
  state: { utf8seq: number; codepoint: number },
228
  emit: (codepoint: number) => void
229
) {
230
  if (state.utf8seq === 0) {
4,023✔
231
    if (byte <= 0x7f) {
3,886✔
232
      emit(byte)
3,806✔
233
      return
3,806✔
234
    }
235

236
    // count the number of 1 leading bits until you reach 0
237
    for (let leadingBit = 1; leadingBit < 6; leadingBit += 1) {
80✔
238
      if (((byte >> (7 - leadingBit)) & 1) === 0) {
230✔
239
        state.utf8seq = leadingBit
79✔
240
        break
79✔
241
      }
242
    }
243

244
    if (state.utf8seq === 2) {
80✔
245
      state.codepoint = byte & 31
16✔
246
    } else if (state.utf8seq === 3) {
64✔
247
      state.codepoint = byte & 15
58✔
248
    } else if (state.utf8seq === 4) {
6✔
249
      state.codepoint = byte & 7
2✔
250
    } else {
251
      throw new Error('Invalid UTF-8 sequence')
4✔
252
    }
253

254
    state.utf8seq -= 1
76✔
255
  } else if (state.utf8seq > 0) {
137✔
256
    if (byte <= 0x7f) {
137✔
257
      throw new Error('Invalid UTF-8 sequence')
2✔
258
    }
259

260
    state.codepoint = (state.codepoint << 6) | (byte & 63)
135✔
261
    state.utf8seq -= 1
135✔
262

263
    if (state.utf8seq === 0) {
135✔
264
      emit(state.codepoint)
74✔
265
    }
266
  }
267
}
268

269
/**
270
 * Helper functions to convert different types of strings to Uint8Array
271
 */
272

273
export function base64UrlToUint8Array(str: string): Uint8Array_ {
8✔
274
  const result: number[] = []
9✔
275
  const state = { queue: 0, queuedBits: 0 }
9✔
276

277
  const onByte = (byte: number) => {
9✔
278
    result.push(byte)
547✔
279
  }
280

281
  for (let i = 0; i < str.length; i += 1) {
9✔
282
    byteFromBase64URL(str.charCodeAt(i), state, onByte)
733✔
283
  }
284

285
  return new Uint8Array(result)
9✔
286
}
287

288
export function stringToUint8Array(str: string): Uint8Array_ {
8✔
289
  const result: number[] = []
×
290
  stringToUTF8(str, (byte: number) => result.push(byte))
×
291
  return new Uint8Array(result)
×
292
}
293

294
export function bytesToBase64URL(bytes: Uint8Array) {
8✔
295
  const result: string[] = []
1✔
296
  const state = { queue: 0, queuedBits: 0 }
1✔
297

298
  const onChar = (char: string) => {
1✔
299
    result.push(char)
4✔
300
  }
301

302
  bytes.forEach((byte) => byteToBase64URL(byte, state, onChar))
3✔
303

304
  // always call with `null` after processing all bytes
305
  byteToBase64URL(null, state, onChar)
1✔
306

307
  return result.join('')
1✔
308
}
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