Files
bsky-social-app/src/storage
Samuel Newman 045f3965ec move labeler cache to mmkv, make moderation config sync
The per-account labeler cache was the only async dependency in
configureModerationForAccount. MMKV-backed sync reads let session
setup apply labelers in the same tick. No AsyncStorage backfill: the
cache is rewritten on every preferences fetch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 21:41:04 +03:00
..
2026-07-23 18:25:40 +03:00
2026-07-23 18:25:40 +03:00
2026-07-23 18:25:40 +03:00
2024-09-05 12:31:24 -07:00

#/storage

Usage

Import the correctly scoped store from #/storage. Each instance of Storage (the base class, not to be used directly), has the following interface:

  • set([...scope, key], value)
  • get([...scope, key])
  • remove([...scope, key])
  • removeMany([...scope], [...keys])

For example, using our device store looks like this, since it's scoped to the device (the most base level scope):

import { device } from '#/storage';

device.set(['foobar'], true);
device.get(['foobar']);
device.remove(['foobar']);
device.removeMany([], ['foobar']);

TypeScript

Stores are strongly typed, and when setting a given value, it will need to conform to the schemas defined in #/storage/schema. When getting a value, it will be returned to you as the type defined in its schema.

Scoped Stores

Some stores are (or might be) scoped to an account or other identifier. In this case, storage instances are created with type-guards, like this:

type AccountSchema = {
  language: `${string}-${string}`;
};

type DID = `did:${string}`;

const account = new Storage<
  [DID],
  AccountSchema
>({
  id: 'account',
});

account.set(
  ['did:plc:abc', 'language'],
  'en-US',
);

const language = account.get([
  'did:plc:abc',
  'language',
]);

Here, if ['did:plc:abc'] is not supplied along with the key of language, the get will return undefined (and TS will yell at you).