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

supabase / auth-js / 21284248075

23 Jan 2026 11:14AM UTC coverage: 70.669%. Remained the same
21284248075

push

github

web-flow
docs: small readme update

1083 of 1681 branches covered (64.43%)

Branch coverage included in aggregate %.

1495 of 1967 relevant lines covered (76.0%)

149.71 hits per line

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

100.0
/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('')
16✔
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('')
16✔
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 = (() => {
16✔
26
  const charMap: number[] = new Array(128)
16✔
27

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

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

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

40
  return charMap
16✔
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(
16✔
51
  byte: number | null,
52
  state: { queue: number; queuedBits: number },
53
  emit: (char: string) => void
54
) {
55
  if (byte !== null) {
588✔
56
    state.queue = (state.queue << 8) | byte
570✔
57
    state.queuedBits += 8
570✔
58

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

68
    while (state.queuedBits >= 6) {
12✔
69
      const pos = (state.queue >> (state.queuedBits - 6)) & 63
12✔
70
      emit(TO_BASE64URL[pos])
12✔
71
      state.queuedBits -= 6
12✔
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(
16✔
84
  charCode: number,
85
  state: { queue: number; queuedBits: number },
86
  emit: (byte: number) => void
87
) {
88
  const bits = FROM_BASE64URL[charCode]
13,317✔
89

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

95
    while (state.queuedBits >= 8) {
13,251✔
96
      emit((state.queue >> (state.queuedBits - 8)) & 0xff)
9,923✔
97
      state.queuedBits -= 8
9,923✔
98
    }
99
  } else if (bits === -2) {
66✔
100
    // ignore spaces, tabs, newlines, =
101
    return
64✔
102
  } else {
103
    throw new Error(`Invalid Base64-URL character "${String.fromCharCode(charCode)}"`)
2✔
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) {
16✔
115
  const base64: string[] = []
16✔
116

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

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

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

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

129
  return base64.join('')
16✔
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) {
16✔
139
  const conv: string[] = []
56✔
140

141
  const utf8Emit = (codepoint: number) => {
56✔
142
    conv.push(String.fromCodePoint(codepoint))
8,303✔
143
  }
144

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

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

152
  const byteEmit = (byte: number) => {
56✔
153
    stringFromUTF8(byte, utf8State, utf8Emit)
8,573✔
154
  }
155

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

160
  return conv.join('')
54✔
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) {
16✔
170
  if (codepoint <= 0x7f) {
1,797✔
171
    emit(codepoint)
1,647✔
172
    return
1,647✔
173
  } else if (codepoint <= 0x7ff) {
150✔
174
    emit(0xc0 | (codepoint >> 6))
30✔
175
    emit(0x80 | (codepoint & 0x3f))
30✔
176
    return
30✔
177
  } else if (codepoint <= 0xffff) {
120✔
178
    emit(0xe0 | (codepoint >> 12))
114✔
179
    emit(0x80 | ((codepoint >> 6) & 0x3f))
114✔
180
    emit(0x80 | (codepoint & 0x3f))
114✔
181
    return
114✔
182
  } else if (codepoint <= 0x10ffff) {
6✔
183
    emit(0xf0 | (codepoint >> 18))
4✔
184
    emit(0x80 | ((codepoint >> 12) & 0x3f))
4✔
185
    emit(0x80 | ((codepoint >> 6) & 0x3f))
4✔
186
    emit(0x80 | (codepoint & 0x3f))
4✔
187
    return
4✔
188
  }
189

190
  throw new Error(`Unrecognized Unicode codepoint: ${codepoint.toString(16)}`)
2✔
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) {
16✔
200
  for (let i = 0; i < str.length; i += 1) {
18✔
201
    let codepoint = str.charCodeAt(i)
1,795✔
202

203
    if (codepoint > 0xd7ff && codepoint <= 0xdbff) {
1,795✔
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
4✔
208
      const lowSurrogate = (str.charCodeAt(i + 1) - 0xdc00) & 0xffff
4✔
209
      codepoint = (lowSurrogate | highSurrogate) + 0x10000
4✔
210
      i += 1
4✔
211
    }
212

213
    codepointToUTF8(codepoint, emit)
1,795✔
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(
16✔
226
  byte: number,
227
  state: { utf8seq: number; codepoint: number },
228
  emit: (codepoint: number) => void
229
) {
230
  if (state.utf8seq === 0) {
8,589✔
231
    if (byte <= 0x7f) {
8,315✔
232
      emit(byte)
8,155✔
233
      return
8,155✔
234
    }
235

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

244
    if (state.utf8seq === 2) {
160✔
245
      state.codepoint = byte & 31
32✔
246
    } else if (state.utf8seq === 3) {
128✔
247
      state.codepoint = byte & 15
116✔
248
    } else if (state.utf8seq === 4) {
12✔
249
      state.codepoint = byte & 7
4✔
250
    } else {
251
      throw new Error('Invalid UTF-8 sequence')
8✔
252
    }
253

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

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

263
    if (state.utf8seq === 0) {
270✔
264
      emit(state.codepoint)
148✔
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_ {
16✔
274
  const result: number[] = []
19✔
275
  const state = { queue: 0, queuedBits: 0 }
19✔
276

277
  const onByte = (byte: number) => {
19✔
278
    result.push(byte)
1,350✔
279
  }
280

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

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

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

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

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

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

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

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