Skip to content

Instantly share code, notes, and snippets.

@ThatsJustCheesy
Last active July 27, 2024 12:15
Show Gist options
  • Save ThatsJustCheesy/c3ee972805d7d5f6b1d8a129fe9bc1d7 to your computer and use it in GitHub Desktop.
Save ThatsJustCheesy/c3ee972805d7d5f6b1d8a129fe9bc1d7 to your computer and use it in GitHub Desktop.
Automerge SQL storage adapter
import { Column, DataType, Model, Table } from "sequelize-typescript";
// Implements storage for Automerge history entries (incremental changes and snapshots).
// https://automerge.org/docs/under-the-hood/storage/#the-storage-model
@Table({
// https://github.com/sequelize/sequelize-typescript/issues/105#issuecomment-832075653
indexes: [
{
name: "automerge_entry_key",
fields: ["key0", "key1", "key2"],
unique: true,
},
],
})
export class AutomergeEntry extends Model<AutomergeEntry> {
@Column
declare key0: string;
@Column
declare key1: string;
@Column
declare key2: string;
@Column(DataType.BLOB)
declare value: Uint8Array;
}
import { Chunk, StorageAdapterInterface, StorageKey } from "@automerge/automerge-repo";
import { WhereOptions } from "sequelize";
import { AutomergeEntry } from "../models/automerge-entry.js";
function queryFromAutomergeKey(key: StorageKey): WhereOptions<AutomergeEntry> {
let where: WhereOptions<AutomergeEntry> = {};
if (key[0] !== undefined) where.key0 = key[0];
if (key[1] !== undefined) where.key1 = key[1];
if (key[2] !== undefined) where.key2 = key[2];
return where;
}
function automergeChunkFromEntry(entry: AutomergeEntry): Chunk {
return { key: [entry.key0, entry.key1, entry.key2], data: entry.value };
}
// https://automerge.org/docs/under-the-hood/storage/#the-storage-model
export class SqlStorageAdapter implements StorageAdapterInterface {
async load(key: StorageKey): Promise<Uint8Array | undefined> {
const entry = await AutomergeEntry.findOne({
attributes: ["value"],
where: queryFromAutomergeKey(key),
});
if (entry) return entry.value;
}
async save(key: StorageKey, data: Uint8Array): Promise<void> {
// @ts-ignore
await AutomergeEntry.upsert({
...queryFromAutomergeKey(key),
value: Buffer.from(data),
});
}
async remove(key: StorageKey): Promise<void> {
await AutomergeEntry.destroy({
where: queryFromAutomergeKey(key),
});
}
async loadRange(keyPrefix: StorageKey): Promise<Chunk[]> {
const entries = await AutomergeEntry.findAll({
where: queryFromAutomergeKey(keyPrefix),
});
return entries.map(automergeChunkFromEntry);
}
async removeRange(keyPrefix: StorageKey): Promise<void> {
await AutomergeEntry.destroy({
where: queryFromAutomergeKey(keyPrefix),
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment