Compare commits

...

3 Commits

Author SHA1 Message Date
Eric Bailey 1e75948f91 Remove test 2026-01-26 13:28:57 -06:00
Eric Bailey 6a442675d9 Cleanup 2026-01-26 13:28:02 -06:00
Eric Bailey 2886816c1e Add idb-keyval backed archival storage 2026-01-25 18:08:12 -06:00
8 changed files with 157 additions and 1 deletions
+1
View File
@@ -171,6 +171,7 @@
"fast-text-encoding": "^1.0.6",
"history": "^5.3.0",
"hls.js": "^1.6.2",
"idb-keyval": "^6.2.2",
"js-sha256": "^0.9.0",
"jwt-decode": "^4.0.0",
"lande": "^1.0.10",
+22
View File
@@ -0,0 +1,22 @@
import {MMKV} from '@bsky.app/react-native-mmkv'
import {type DB} from '#/storage/archive/db/types'
export function create({id}: {id: string}): DB {
const store = new MMKV({id})
return {
async get(key: string): Promise<string | undefined> {
return store.getString(key) ?? undefined
},
async set(key: string, value: string): Promise<void> {
store.set(key, value)
},
async delete(key: string): Promise<void> {
store.delete(key)
},
async clear(): Promise<void> {
store.clearAll()
},
}
}
+22
View File
@@ -0,0 +1,22 @@
import {clear, createStore, del, get, set} from 'idb-keyval'
import {type DB} from '#/storage/archive/db/types'
export function create({id}: {id: string}): DB {
const store = createStore(id, id)
return {
async get(key: string): Promise<string | undefined> {
return get(key, store) ?? undefined
},
async set(key: string, value: string): Promise<void> {
await set(key, value, store)
},
async delete(key: string): Promise<void> {
await del(key, store)
},
async clear(): Promise<void> {
await clear(store)
},
}
}
+6
View File
@@ -0,0 +1,6 @@
export type DB = {
get(key: string): Promise<string | undefined>
set(key: string, value: string): Promise<void>
delete(key: string): Promise<void>
clear(): Promise<void>
}
+90
View File
@@ -0,0 +1,90 @@
import {create} from '#/storage/archive/db'
import {type DB} from '#/storage/archive/db/types'
import {type Device} from '#/storage/archive/schema'
export * from '#/storage/archive/schema'
/**
* Generic archival storage class. DO NOT use this directly. Instead, use the
* exported `Archive` instances below.
*/
export class Archive<Scopes extends unknown[], Schema> {
protected sep = ':'
protected store: DB
constructor({id}: {id: string}) {
this.store = create({id})
}
/**
* Store a value in archival storage based on scopes and/or keys
*
* `set([key], value)`
* `set([scope, key], value)`
*/
async set<Key extends keyof Schema>(
scopes: [...Scopes, Key],
data: Schema[Key],
): Promise<void> {
// stored as `{ data: <value> }` structure to ease stringification
return this.store.set(scopes.join(this.sep), JSON.stringify({data}))
}
/**
* Get a value from archival storage based on scopes and/or keys
*
* `get([key])`
* `get([scope, key])`
*/
async get<Key extends keyof Schema>(
scopes: [...Scopes, Key],
): Promise<Schema[Key] | undefined> {
const res = await this.store.get(scopes.join(this.sep))
if (!res) return undefined
// parsed from storage structure `{ data: <value> }`
return JSON.parse(res).data
}
/**
* Remove a value from archival storage based on scopes and/or keys
*
* `remove([key])`
* `remove([scope, key])`
*/
async remove<Key extends keyof Schema>(scopes: [...Scopes, Key]) {
return this.store.delete(scopes.join(this.sep))
}
/**
* Remove many values from the same archival storage scope by keys
*
* `removeMany([], [key])`
* `removeMany([scope], [key])`
*/
async removeMany<Key extends keyof Schema>(scopes: [...Scopes], keys: Key[]) {
return Promise.all(keys.map(key => this.remove([...scopes, key])))
}
/**
* For debugging purposes
*/
async removeAll() {
return this.store.clear()
}
}
/**
* Device data that's specific to the device and does not vary based on account
*
* `device.set([key], true)`
*/
export const deviceArchive = new Archive<[], Device>({
id: 'bsky_archive_device',
})
if (__DEV__ && typeof window !== 'undefined') {
// @ts-expect-error - dev global
window.bsky_archive = {
deviceArchive,
}
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Data that's specific to the device and does not vary based account.
* Values here should be optional (since they may not have been saved
* yet), hence the `Partial`.
*
* See `#/storage/schema.ts` for examples.
*/
export type Device = Partial<{
// add values here
}>
+1 -1
View File
@@ -2,7 +2,7 @@ import {type ID as PolicyUpdate202508} from '#/components/PolicyUpdateOverlay/up
import {type Geolocation} from '#/geolocation/types'
/**
* Device data that's specific to the device and does not vary based account
* Data that's specific to the device and does not vary based account
*/
export type Device = {
/**
+5
View File
@@ -13093,6 +13093,11 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
idb-keyval@^6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.2.tgz#b0171b5f73944854a3291a5cdba8e12768c4854a"
integrity sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==
ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"