@x5e/gink
Version:
an eventually consistent database
250 lines (224 loc) • 6.18 kB
text/typescript
import { Behavior, BundleBuilder, ChangeBuilder } from "./builders";
import { DBSchema } from "idb";
export const PROTOCOL = "gink";
export type Bytes = Uint8Array;
export type BundleBytes = Bytes;
export type Medallion = number;
export type Timestamp = number;
export type ChainStart = Timestamp;
export type SeenThrough = Timestamp;
export type PriorTime = Timestamp;
export type Offset = number;
export type DirPath = string;
export type FilePath = string;
export type NumberStr = string;
export type ScalarKey = number | string | Bytes;
export type Value =
| number
| string
| boolean
| null
| Bytes
| Map<ScalarKey, Value>
| Array<Value>
| Date
| BigInt;
export type BundleInfoTuple = [
Timestamp,
Medallion,
ChainStart,
PriorTime,
string,
];
export type ChangeSetOffset = number;
export type AsOf = Timestamp | Date | ChangeSetOffset;
export type MuidTuple = [Timestamp, Medallion, Offset];
export type Cookies = Map<string, string>;
export type Indexable = MuidTuple;
export type ActorId = number; // process ID on the server side, something more complex in the browser
export type StorageKey = ScalarKey | MuidTuple | [MuidTuple, MuidTuple] | [];
export type Placement = {
container: MuidTuple;
key: StorageKey;
placement: MuidTuple;
};
export interface BundleListener {
(bundle: BundleView): Promise<void>;
}
export interface ClaimedChain {
medallion: Medallion;
chainStart: ChainStart;
actorId: ActorId;
claimTime: Timestamp;
}
export interface CallBack {
(...args: any[]): void;
}
export interface Indexer {
(value: Muid): Indexable;
}
export interface AuthFunction {
(token: string): boolean;
}
export interface BroadcastFunc {
(bundle: BundleView): Promise<void>;
}
export interface Muid {
medallion: Medallion;
timestamp: Timestamp;
offset: number;
}
export interface BundleInfo {
timestamp: Timestamp;
medallion: Medallion;
chainStart: ChainStart;
priorTime?: PriorTime;
hashCode?: Bytes;
comment?: string;
}
export interface BundleView {
bytes: BundleBytes;
info: BundleInfo;
builder: BundleBuilder;
}
// data structure to represent an Entry; some fields are tuples of 0 or 1 entries because
// the indexeddb system can't handle null or undefined in keys (but can handle tuples).
export interface Entry {
behavior: Behavior;
containerId: MuidTuple;
/**
* storageKey is a KeyType if the entry is for a Directory, a Timestamp if it's for a sequence,
* MuidTuple if it's for a property, and empty list for a box.
*/
storageKey: StorageKey;
entryId: MuidTuple;
pointeeList: Indexable[]; // use an empty list to denote no pointees
value?: Value;
expiry?: Timestamp;
deletion?: boolean;
placementId: MuidTuple;
sourceList: Indexable[]; // used for edges
targetList: Indexable[]; // used for edges
purging?: boolean;
}
export interface Removal {
removing: MuidTuple; // describes the placementId of the thing to be removed
removalId: MuidTuple; // the ID of the movement or entry doing the removing
containerId: MuidTuple;
dest: number;
entryId: MuidTuple;
}
export interface Movement {
entryId: MuidTuple;
movementId: MuidTuple;
containerId: MuidTuple;
dest: number;
purge: boolean;
}
export interface Clearance {
containerId: MuidTuple;
clearanceId: MuidTuple;
purging: boolean;
}
export interface EdgeData {
source: Muid;
target: Muid;
etype?: Muid;
value?: Value;
effective?: number;
}
export interface KeyPair {
publicKey: Bytes;
secretKey: Bytes;
}
export interface Meta {
bundler?: Bundler;
comment?: string;
identity?: string;
}
export interface Bundler {
addChange(changeBuilder: ChangeBuilder): Muid;
commit(comment?: string): Promise<BundleInfo>;
medallion: number;
}
export interface Connection {
subscribe(callback: () => void): () => void;
get hasSentUnackedData(): boolean;
get hasSentInitialSync(): boolean;
get hasRecvInitialSync(): boolean;
get hasSentGreeting(): boolean;
get isReadOnly(): boolean;
get synced(): boolean;
get ready(): Promise<void>;
close(): void;
}
export interface Sealer {
(changes: ChangeBuilder[], meta?: Meta): Promise<BundleInfo>;
}
export interface IndexedDbStoreSchema extends DBSchema {
accumulatorTotals: {
key: MuidTuple;
value: bigint;
};
trxns: {
key: BundleInfoTuple;
value: BundleBytes;
};
chainInfos: {
value: BundleInfo;
key: [number, number];
};
activeChains: {
value: ClaimedChain;
key: [number]; // claimTime
};
containers: {
key: MuidTuple;
value: Bytes;
};
removals: {
value: Removal;
key: MuidTuple; // movementId
indexes: {
"by-container-movement": [MuidTuple, MuidTuple]; // containerId, movementId
"by-removing": [MuidTuple, MuidTuple]; // removing, removalId
};
};
clearances: {
value: Clearance;
key: [MuidTuple, MuidTuple]; // ["containerId", "clearanceId"]
};
entries: {
value: Entry;
key: MuidTuple;
indexes: {
"by-container-key-placement": [
MuidTuple,
ScalarKey | Timestamp | MuidTuple | [],
MuidTuple,
];
"by-container-name": [MuidTuple, string]; // for use with global property and container names
"by-key-placement": [StorageKey, MuidTuple]; // for use with properties and the containers they describe
pointees: Indexable;
locations: [MuidTuple, MuidTuple];
sources: Indexable;
targets: Indexable;
};
};
identities: {
value: string;
key: [Medallion, ChainStart];
};
verifyKeys: {
value: Bytes;
key: [Medallion, ChainStart];
};
secretKeys: {
value: Bytes;
key: Bytes;
};
symmetricKeys: {
value: Bytes;
key: number;
};
}