···11+import {create} from '#/storage/archive/db'
22+import {type DB} from '#/storage/archive/db/types'
33+import {type Device} from '#/storage/archive/schema'
44+55+export * from '#/storage/archive/schema'
66+77+/**
88+ * Generic archival storage class. DO NOT use this directly. Instead, use the
99+ * exported `Archive` instances below.
1010+ */
1111+export class Archive<Scopes extends unknown[], Schema> {
1212+ protected sep = ':'
1313+ protected store: DB
1414+1515+ constructor({id}: {id: string}) {
1616+ this.store = create({id})
1717+ }
1818+1919+ /**
2020+ * Store a value in archival storage based on scopes and/or keys
2121+ *
2222+ * `set([key], value)`
2323+ * `set([scope, key], value)`
2424+ */
2525+ async set<Key extends keyof Schema>(
2626+ scopes: [...Scopes, Key],
2727+ data: Schema[Key],
2828+ ): Promise<void> {
2929+ // stored as `{ data: <value> }` structure to ease stringification
3030+ return this.store.set(scopes.join(this.sep), JSON.stringify({data}))
3131+ }
3232+3333+ /**
3434+ * Get a value from archival storage based on scopes and/or keys
3535+ *
3636+ * `get([key])`
3737+ * `get([scope, key])`
3838+ */
3939+ async get<Key extends keyof Schema>(
4040+ scopes: [...Scopes, Key],
4141+ ): Promise<Schema[Key] | undefined> {
4242+ const res = await this.store.get(scopes.join(this.sep))
4343+ if (!res) return undefined
4444+ // parsed from storage structure `{ data: <value> }`
4545+ return JSON.parse(res).data
4646+ }
4747+4848+ /**
4949+ * Remove a value from archival storage based on scopes and/or keys
5050+ *
5151+ * `remove([key])`
5252+ * `remove([scope, key])`
5353+ */
5454+ async remove<Key extends keyof Schema>(scopes: [...Scopes, Key]) {
5555+ return this.store.delete(scopes.join(this.sep))
5656+ }
5757+5858+ /**
5959+ * Remove many values from the same archival storage scope by keys
6060+ *
6161+ * `removeMany([], [key])`
6262+ * `removeMany([scope], [key])`
6363+ */
6464+ async removeMany<Key extends keyof Schema>(scopes: [...Scopes], keys: Key[]) {
6565+ return Promise.all(keys.map(key => this.remove([...scopes, key])))
6666+ }
6767+6868+ /**
6969+ * For debugging purposes
7070+ */
7171+ async removeAll() {
7272+ return this.store.clear()
7373+ }
7474+}
7575+7676+/**
7777+ * Device data that's specific to the device and does not vary based on account
7878+ *
7979+ * `device.set([key], true)`
8080+ */
8181+export const deviceArchive = new Archive<[], Device>({
8282+ id: 'bsky_archive_device',
8383+})
8484+8585+if (__DEV__ && typeof window !== 'undefined') {
8686+ // @ts-expect-error - dev global
8787+ window.bsky_archive = {
8888+ deviceArchive,
8989+ }
9090+}
+10
src/storage/archive/schema.ts
···11+/**
22+ * Data that's specific to the device and does not vary based account.
33+ * Values here should be optional (since they may not have been saved
44+ * yet), hence the `Partial`.
55+ *
66+ * See `#/storage/schema.ts` for examples.
77+ */
88+export type Device = Partial<{
99+ // add values here
1010+}>
+1-1
src/storage/schema.ts
···22import {type Geolocation} from '#/geolocation/types'
3344/**
55- * Device data that's specific to the device and does not vary based account
55+ * Data that's specific to the device and does not vary based account
66 */
77export type Device = {
88 /**