Skip to content

Commit

Permalink
chore: Tweak package size
Browse files Browse the repository at this point in the history
- Remove Node.js b64 codec code (not needed)
- Run tests in JSDOM to get window.atob/btoa
- Even with that, can't get under 1KB gziped, oh well..
  • Loading branch information
franky47 committed Oct 29, 2022
1 parent 48f1b63 commit 1742810
Show file tree
Hide file tree
Showing 4 changed files with 431 additions and 53 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"commitlint": "^17.1.2",
"husky": "8.x",
"jest": "^29.2.1",
"jest-environment-jsdom": "^29.2.2",
"npm-run-all": "^4.1.5",
"size-limit": "^8.1.0",
"ts-jest": "^29.0.3",
Expand All @@ -62,13 +63,13 @@
"size-limit": [
{
"path": "./dist/index.mjs",
"limit": "1 KB"
"limit": "1.1 KB"
}
],
"jest": {
"verbose": true,
"preset": "ts-jest/presets/js-with-ts",
"testEnvironment": "node"
"testEnvironment": "jsdom"
},
"prettier": {
"arrowParens": "avoid",
Expand Down
42 changes: 15 additions & 27 deletions src/codec.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
export function base64UrlEncode(input: Uint8Array): string {
if (typeof window === 'undefined') {
// Node.js
return Buffer.from(input).toString('base64url')
} else {
// Browser
return (
window
.btoa(String.fromCharCode(...input))
// Convert to base64url
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/={1,2}$/, '')
)
}
return (
window
.btoa(String.fromCharCode(...input))
// Convert to base64url
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/={1,2}$/, '')
)
}

export function base64UrlDecode(input: string): Uint8Array {
if (typeof window === 'undefined') {
// Node.js
return new Uint8Array(Buffer.from(input, 'base64url'))
} else {
// Browser
return new Uint8Array(
// convert base64url to base64 for atob
window
.atob(input.replace(/-/g, '+').replace(/_/g, '/'))
.split('')
.map(x => x.charCodeAt(0))
)
}
return new Uint8Array(
// convert base64url to base64 for atob
window
.atob(input.replace(/-/g, '+').replace(/_/g, '/'))
.split('')
.map(x => x.charCodeAt(0))
)
}
33 changes: 15 additions & 18 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { base64UrlDecode, base64UrlEncode } from './codec'

export type LocalSecretStateSyncConfig<StateType> = {
export type LocalStateSyncConfig<StateType> = {
encryptionKey: string
onStateUpdated: (newState: StateType) => unknown
stateParser?: Parser<StateType>
Expand All @@ -20,16 +20,13 @@ type LoadedInternalState = {
}
type InternalState = IdleInternalState | LoadedInternalState

export class LocalSecretStateSync<StateType> {
export class LocalStateSync<StateType> {
#internalState: InternalState
private config: Required<
Omit<LocalSecretStateSyncConfig<StateType>, 'encryptionKey'>
Omit<LocalStateSyncConfig<StateType>, 'encryptionKey'>
>

constructor({
encryptionKey,
...config
}: LocalSecretStateSyncConfig<StateType>) {
constructor({ encryptionKey, ...config }: LocalStateSyncConfig<StateType>) {
this.#internalState = {
state: 'idle'
}
Expand All @@ -43,38 +40,38 @@ export class LocalSecretStateSync<StateType> {

public async setState(state: StateType) {
if (typeof window === 'undefined') {
console.warn('LocalSecretStateSync is disabled in Node.js')
console.warn('LocalStateSync is disabled in Node.js')
return
}
if (this.#internalState.state !== 'loaded') {
throw new Error('LocalSecretStateSync is not ready')
throw new Error('LocalStateSync is not ready')
}
const encryptedState = await this.encryptState(state)
window.localStorage.setItem(this.#internalState.storageKey, encryptedState)
}

public clearState() {
if (typeof window === 'undefined') {
console.warn('LocalSecretStateSync is disabled in Node.js')
console.warn('LocalStateSync is disabled in Node.js')
return
}
if (this.#internalState.state !== 'loaded') {
throw new Error('LocalSecretStateSync is not ready')
throw new Error('LocalStateSync is not ready')
}
window.localStorage.removeItem(this.#internalState.storageKey)
}

private async setup(encodedEncryptionKey: string) {
if (typeof window === 'undefined') {
console.warn('LocalStateSync is disabled in Node.js')
return
}
const keyBuffer = base64UrlDecode(encodedEncryptionKey)
if (keyBuffer.byteLength !== 32) {
throw new Error(
'LocalSecretStateSync: encryptionKey must be 32 bytes (48 base64url characters)'
'LocalStateSync: encryptionKey must be 32 bytes (48 base64url characters)'
)
}
if (typeof window === 'undefined') {
console.warn('LocalSecretStateSync is disabled in Node.js')
return
}
const encryptionKey = await window.crypto.subtle.importKey(
'raw',
keyBuffer,
Expand Down Expand Up @@ -127,7 +124,7 @@ export class LocalSecretStateSync<StateType> {

private async decryptState(storageValue: string) {
if (this.#internalState.state !== 'loaded') {
throw new Error('LocalSecretStateSync is not ready')
throw new Error('LocalStateSync is not ready')
}
const [iv, ciphertext] = storageValue.split('.')
const cleartext = await window.crypto.subtle.decrypt(
Expand All @@ -144,7 +141,7 @@ export class LocalSecretStateSync<StateType> {

private async encryptState(state: StateType) {
if (this.#internalState.state !== 'loaded') {
throw new Error('LocalSecretStateSync is not ready')
throw new Error('LocalStateSync is not ready')
}
const serializedState = this.config.stateSerializer(state)
const iv = window.crypto.getRandomValues(new Uint8Array(12))
Expand Down
Loading

0 comments on commit 1742810

Please sign in to comment.