tablinum 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +72 -61
  2. package/dist/brands.d.ts +5 -0
  3. package/dist/crud/collection-handle.d.ts +5 -5
  4. package/dist/crud/query-builder.d.ts +4 -13
  5. package/dist/crud/watch.d.ts +0 -10
  6. package/dist/db/create-tablinum.d.ts +7 -0
  7. package/dist/db/database-handle.d.ts +22 -1
  8. package/dist/db/epoch.d.ts +48 -0
  9. package/dist/db/invite.d.ts +8 -0
  10. package/dist/db/key-rotation.d.ts +24 -0
  11. package/dist/db/members.d.ts +24 -0
  12. package/dist/db/runtime-config.d.ts +16 -0
  13. package/dist/index.d.ts +8 -1
  14. package/dist/index.js +1552 -820
  15. package/dist/layers/EpochStoreLive.d.ts +6 -0
  16. package/dist/layers/GiftWrapLive.d.ts +5 -0
  17. package/dist/layers/IdentityLive.d.ts +5 -0
  18. package/dist/layers/PublishQueueLive.d.ts +5 -0
  19. package/dist/layers/RelayLive.d.ts +3 -0
  20. package/dist/layers/StorageLive.d.ts +4 -0
  21. package/dist/layers/SyncStatusLive.d.ts +3 -0
  22. package/dist/layers/TablinumLive.d.ts +5 -0
  23. package/dist/layers/index.d.ts +8 -0
  24. package/dist/schema/field.d.ts +0 -1
  25. package/dist/schema/types.d.ts +0 -4
  26. package/dist/services/Config.d.ts +16 -0
  27. package/dist/services/EpochStore.d.ts +6 -0
  28. package/dist/services/GiftWrap.d.ts +6 -0
  29. package/dist/services/Identity.d.ts +6 -0
  30. package/dist/services/PublishQueue.d.ts +6 -0
  31. package/dist/services/Relay.d.ts +6 -0
  32. package/dist/services/Storage.d.ts +6 -0
  33. package/dist/services/Sync.d.ts +6 -0
  34. package/dist/services/SyncStatus.d.ts +6 -0
  35. package/dist/services/Tablinum.d.ts +7 -0
  36. package/dist/services/index.d.ts +10 -0
  37. package/dist/storage/idb.d.ts +11 -2
  38. package/dist/storage/lww.d.ts +0 -5
  39. package/dist/storage/records-store.d.ts +0 -7
  40. package/dist/svelte/collection.svelte.d.ts +9 -6
  41. package/dist/svelte/deferred.d.ts +7 -0
  42. package/dist/svelte/index.svelte.d.ts +6 -6
  43. package/dist/svelte/index.svelte.js +1881 -1023
  44. package/dist/svelte/query.svelte.d.ts +6 -6
  45. package/dist/svelte/tablinum.svelte.d.ts +35 -0
  46. package/dist/sync/gift-wrap.d.ts +6 -2
  47. package/dist/sync/negentropy.d.ts +1 -1
  48. package/dist/sync/publish-queue.d.ts +3 -2
  49. package/dist/sync/relay.d.ts +9 -2
  50. package/dist/sync/sync-service.d.ts +5 -2
  51. package/dist/sync/sync-status.d.ts +1 -0
  52. package/dist/utils/uuid.d.ts +0 -1
  53. package/package.json +1 -1
  54. package/dist/main.d.ts +0 -1
  55. package/dist/storage/events-store.d.ts +0 -6
  56. package/dist/storage/giftwraps-store.d.ts +0 -6
  57. package/dist/svelte/database.svelte.d.ts +0 -15
  58. package/dist/svelte/live-query.svelte.d.ts +0 -8
package/README.md CHANGED
@@ -60,7 +60,7 @@ const program = Effect.gen(function* () {
60
60
  const todo = yield* todos.get(id);
61
61
 
62
62
  // Query
63
- const pending = yield* todos.where("done").equals(false).toArray();
63
+ const pending = yield* todos.where("done").equals(false).get();
64
64
 
65
65
  // Update
66
66
  yield* todos.update(id, { done: true });
@@ -76,13 +76,26 @@ Effect.runPromise(Effect.scoped(program));
76
76
 
77
77
  Import from `tablinum/svelte` for reactive bindings that use Svelte 5 runes. No Effect knowledge needed — the API is plain async/await.
78
78
 
79
+ This API uses Svelte's async runes support, so enable it in your app config:
80
+
81
+ ```js
82
+ // svelte.config.js
83
+ const config = {
84
+ compilerOptions: {
85
+ experimental: {
86
+ async: true,
87
+ },
88
+ },
89
+ };
90
+ ```
91
+
79
92
  ### Setup
80
93
 
81
94
  Create a database helper that defines your schema and initializes the database:
82
95
 
83
96
  ```typescript
84
97
  // src/lib/db.ts
85
- import { createTablinum, collection, field } from "tablinum/svelte";
98
+ import { Tablinum, collection, field } from "tablinum/svelte";
86
99
 
87
100
  const schema = {
88
101
  todos: collection(
@@ -97,92 +110,90 @@ const schema = {
97
110
 
98
111
  export type AppSchema = typeof schema;
99
112
 
100
- export async function initDb() {
101
- return createTablinum({
102
- schema,
103
- relays: ["wss://relay.example.com"],
104
- });
105
- }
113
+ export const db = new Tablinum({
114
+ schema,
115
+ relays: ["wss://relay.example.com"],
116
+ });
117
+
118
+ export const todos = db.collection("todos");
106
119
  ```
107
120
 
108
121
  ### Component
109
122
 
110
- Collections and live queries are reactive their `.items` property updates automatically when data changes.
123
+ Async collection reads are reactive when used inside `$derived(await ...)` expressions.
111
124
 
112
125
  ```svelte
113
126
  <script lang="ts">
114
- import { onDestroy } from "svelte";
115
- import { initDb, type AppSchema } from "$lib/db";
116
- import type { Database, Collection, LiveQuery } from "tablinum/svelte";
117
-
118
- let db: Database<AppSchema> | null = $state(null);
119
- let todos: Collection<AppSchema["todos"]> | null = $state(null);
120
- let pending: LiveQuery<{ id: string; title: string; done: boolean }> | null = $state(null);
127
+ import { db, todos } from "$lib/db";
121
128
 
122
129
  let title = $state("");
123
-
124
- async function init() {
125
- db = await initDb();
126
- todos = db.collection("todos");
127
- pending = todos.where("done").equals(false).live();
128
- }
129
-
130
- init();
130
+ let booted = $derived(await db.ready.then(() => true, () => false));
131
+ let pending = $derived(
132
+ booted && db.status === "ready"
133
+ ? await todos.where("done").equals(false).get()
134
+ : [],
135
+ );
131
136
 
132
137
  async function addTodo(e: SubmitEvent) {
133
138
  e.preventDefault();
134
- if (!todos || !title.trim()) return;
139
+ if (!title.trim()) return;
135
140
  await todos.add({ title: title.trim(), done: false });
136
141
  title = "";
137
142
  }
138
143
 
139
144
  async function toggle(id: string, currentDone: boolean) {
140
- await todos?.update(id, { done: !currentDone });
145
+ await todos.update(id, { done: !currentDone });
141
146
  }
142
147
 
143
148
  async function remove(id: string) {
144
- await todos?.delete(id);
149
+ await todos.delete(id);
145
150
  }
146
-
147
- onDestroy(() => {
148
- pending?.destroy();
149
- db?.close();
150
- });
151
151
  </script>
152
152
 
153
- <!-- All todos (reactive via collection.items) -->
154
- <p>{todos?.items.length ?? 0} total</p>
155
-
156
- <!-- Filtered todos (reactive via live query) -->
157
- <form onsubmit={addTodo}>
158
- <input bind:value={title} placeholder="Add a todo..." />
159
- <button type="submit">Add</button>
160
- </form>
161
-
162
- <ul>
163
- {#each pending?.items ?? [] as todo (todo.id)}
164
- <li>
165
- <input type="checkbox" checked={todo.done} onchange={() => toggle(todo.id, todo.done)} />
166
- <span>{todo.title}</span>
167
- <button onclick={() => remove(todo.id)}>Delete</button>
168
- </li>
169
- {/each}
170
- </ul>
171
-
172
- <!-- Sync status is reactive -->
173
- {#if db?.status === "syncing"}
174
- <p>Syncing...</p>
175
- {/if}
176
-
177
- <button onclick={() => db?.sync()}>Sync</button>
153
+ <svelte:boundary>
154
+ {#snippet pending()}
155
+ <p>Initializing database...</p>
156
+ {/snippet}
157
+
158
+ {#if db.status === "error"}
159
+ <p>{db.error?.message}</p>
160
+ {:else}
161
+ <p>{pending.length} pending</p>
162
+
163
+ <form onsubmit={addTodo}>
164
+ <input bind:value={title} placeholder="Add a todo..." />
165
+ <button type="submit">Add</button>
166
+ </form>
167
+
168
+ <ul>
169
+ {#each pending as todo (todo.id)}
170
+ <li>
171
+ <input
172
+ type="checkbox"
173
+ checked={todo.done}
174
+ onchange={() => toggle(todo.id, todo.done)}
175
+ />
176
+ <span>{todo.title}</span>
177
+ <button onclick={() => remove(todo.id)}>Delete</button>
178
+ </li>
179
+ {/each}
180
+ </ul>
181
+
182
+ {#if db.syncStatus === "syncing"}
183
+ <p>Syncing...</p>
184
+ {/if}
185
+
186
+ <button onclick={() => db.sync()}>Sync</button>
187
+ {/if}
188
+ </svelte:boundary>
178
189
  ```
179
190
 
180
191
  ### Key concepts
181
192
 
182
- - **`createTablinum`** returns a `Promise<Database>` (no Effect boilerplate)
183
- - **`collection.items`** is a reactive `$state` array of all records in the collection
184
- - **`.where("field").equals(value).live()`** returns a `LiveQuery` whose `.items` update automatically
185
- - **Cleanup**: call `.destroy()` on live queries and `.close()` on the database in `onDestroy`
193
+ - **`new Tablinum(config)`** starts initialization immediately and exposes `db.ready`
194
+ - **Async queries are reactive** when used inside `$derived(await ...)`
195
+ - **`db.status`** tracks initialization and terminal state; **`db.syncStatus`** tracks sync activity
196
+ - **`createTablinum(config)`** still exists as a convenience and resolves once `db.ready` completes
186
197
 
187
198
  ## License
188
199
 
@@ -0,0 +1,5 @@
1
+ import { Brand } from "effect";
2
+ export type EpochId = Brand.Branded<string, "EpochId">;
3
+ export declare const EpochId: Brand.Constructor<EpochId>;
4
+ export type DatabaseName = Brand.Branded<string, "DatabaseName">;
5
+ export declare const DatabaseName: Brand.Constructor<DatabaseName>;
@@ -1,21 +1,21 @@
1
- import { Effect, Stream } from "effect";
1
+ import { Effect, Option, Stream } from "effect";
2
2
  import type { CollectionDef, CollectionFields } from "../schema/collection.ts";
3
3
  import type { InferRecord } from "../schema/types.ts";
4
4
  import type { RecordValidator, PartialValidator } from "../schema/validate.ts";
5
5
  import type { IDBStorageHandle, StoredEvent } from "../storage/idb.ts";
6
6
  import { NotFoundError, StorageError, ValidationError } from "../errors.ts";
7
7
  import type { WatchContext } from "./watch.ts";
8
- import type { WhereClause, OrderByBuilder } from "./query-builder.ts";
8
+ import type { WhereClause, QueryBuilder } from "./query-builder.ts";
9
9
  export interface CollectionHandle<C extends CollectionDef<CollectionFields>> {
10
10
  readonly add: (data: Omit<InferRecord<C>, "id">) => Effect.Effect<string, ValidationError | StorageError>;
11
11
  readonly update: (id: string, data: Partial<Omit<InferRecord<C>, "id">>) => Effect.Effect<void, ValidationError | StorageError | NotFoundError>;
12
12
  readonly delete: (id: string) => Effect.Effect<void, StorageError | NotFoundError>;
13
13
  readonly get: (id: string) => Effect.Effect<InferRecord<C>, StorageError | NotFoundError>;
14
- readonly first: () => Effect.Effect<InferRecord<C> | null, StorageError>;
14
+ readonly first: () => Effect.Effect<Option.Option<InferRecord<C>>, StorageError>;
15
15
  readonly count: () => Effect.Effect<number, StorageError>;
16
16
  readonly watch: () => Stream.Stream<ReadonlyArray<InferRecord<C>>, StorageError>;
17
17
  readonly where: (field: string & keyof Omit<InferRecord<C>, "id">) => WhereClause<InferRecord<C>>;
18
- readonly orderBy: (field: string & keyof Omit<InferRecord<C>, "id">) => OrderByBuilder<InferRecord<C>>;
18
+ readonly orderBy: (field: string & keyof Omit<InferRecord<C>, "id">) => QueryBuilder<InferRecord<C>>;
19
19
  }
20
- export type OnWriteCallback = (event: StoredEvent) => Effect.Effect<void>;
20
+ export type OnWriteCallback = (event: StoredEvent) => Effect.Effect<void, StorageError>;
21
21
  export declare function createCollectionHandle<C extends CollectionDef<CollectionFields>>(def: C, storage: IDBStorageHandle, watchCtx: WatchContext, validator: RecordValidator<C["fields"]>, partialValidator: PartialValidator<C["fields"]>, makeEventId: () => string, onWrite?: OnWriteCallback): CollectionHandle<C>;
@@ -1,4 +1,4 @@
1
- import { Effect, Stream } from "effect";
1
+ import { Effect, Option, Stream } from "effect";
2
2
  import type { IDBStorageHandle } from "../storage/idb.ts";
3
3
  import type { StorageError, ValidationError } from "../errors.ts";
4
4
  import type { CollectionDef, CollectionFields } from "../schema/collection.ts";
@@ -24,19 +24,10 @@ export interface QueryBuilder<T> {
24
24
  readonly offset: (n: number) => QueryBuilder<T>;
25
25
  readonly limit: (n: number) => QueryBuilder<T>;
26
26
  readonly get: () => Effect.Effect<ReadonlyArray<T>, StorageError | ValidationError>;
27
- readonly first: () => Effect.Effect<T | null, StorageError | ValidationError>;
27
+ readonly first: () => Effect.Effect<Option.Option<T>, StorageError | ValidationError>;
28
28
  readonly count: () => Effect.Effect<number, StorageError | ValidationError>;
29
29
  readonly watch: () => Stream.Stream<ReadonlyArray<T>, StorageError | ValidationError>;
30
30
  }
31
- export interface OrderByBuilder<T> {
32
- readonly reverse: () => OrderByBuilder<T>;
33
- readonly offset: (n: number) => OrderByBuilder<T>;
34
- readonly limit: (n: number) => OrderByBuilder<T>;
35
- readonly get: () => Effect.Effect<ReadonlyArray<T>, StorageError | ValidationError>;
36
- readonly first: () => Effect.Effect<T | null, StorageError | ValidationError>;
37
- readonly count: () => Effect.Effect<number, StorageError | ValidationError>;
38
- readonly watch: () => Stream.Stream<ReadonlyArray<T>, StorageError | ValidationError>;
39
- }
40
- export type QueryExecutor<T> = QueryBuilder<T>;
31
+ export type OrderByBuilder<T> = QueryBuilder<T>;
41
32
  export declare function createWhereClause<T>(storage: IDBStorageHandle, watchCtx: WatchContext, collectionName: string, def: CollectionDef<CollectionFields>, fieldName: string, mapRecord: (record: Record<string, unknown>) => T): WhereClause<T>;
42
- export declare function createOrderByBuilder<T>(storage: IDBStorageHandle, watchCtx: WatchContext, collectionName: string, def: CollectionDef<CollectionFields>, fieldName: string, mapRecord: (record: Record<string, unknown>) => T): OrderByBuilder<T>;
33
+ export declare function createOrderByBuilder<T>(storage: IDBStorageHandle, watchCtx: WatchContext, collectionName: string, def: CollectionDef<CollectionFields>, fieldName: string, mapRecord: (record: Record<string, unknown>) => T): QueryBuilder<T>;
@@ -10,16 +10,6 @@ export interface WatchContext {
10
10
  readonly pubsub: PubSub.PubSub<ChangeEvent>;
11
11
  readonly replayingRef: Ref.Ref<boolean>;
12
12
  }
13
- /**
14
- * Create a reactive Stream that emits the current result set whenever it changes.
15
- */
16
13
  export declare function watchCollection<T>(ctx: WatchContext, storage: IDBStorageHandle, collectionName: string, filter?: (record: Record<string, unknown>) => boolean, mapRecord?: (record: Record<string, unknown>) => T): Stream.Stream<ReadonlyArray<T>, StorageError>;
17
- /**
18
- * Notify subscribers of a change.
19
- */
20
14
  export declare function notifyChange(ctx: WatchContext, event: ChangeEvent): Effect.Effect<void>;
21
- /**
22
- * Emit a replay-complete notification for all collections that changed.
23
- * Call after sync replay to trigger batched watch updates.
24
- */
25
15
  export declare function notifyReplayComplete(ctx: WatchContext, collections: ReadonlyArray<string>): Effect.Effect<void>;
@@ -1,12 +1,19 @@
1
1
  import { Effect, Scope } from "effect";
2
2
  import type { SchemaConfig } from "../schema/types.ts";
3
3
  import type { DatabaseHandle } from "./database-handle.ts";
4
+ import type { EpochKeyInput } from "./epoch.ts";
4
5
  import { CryptoError, StorageError, ValidationError } from "../errors.ts";
5
6
  export interface TablinumConfig<S extends SchemaConfig> {
6
7
  readonly schema: S;
7
8
  readonly relays: readonly string[];
8
9
  readonly privateKey?: Uint8Array | undefined;
10
+ readonly epochKeys?: ReadonlyArray<EpochKeyInput> | undefined;
9
11
  readonly dbName?: string | undefined;
10
12
  readonly onSyncError?: ((error: Error) => void) | undefined;
13
+ readonly onRemoved?: ((info: {
14
+ epochId: string;
15
+ removedBy: string;
16
+ }) => void) | undefined;
17
+ readonly onMembersChanged?: (() => void) | undefined;
11
18
  }
12
19
  export declare function createTablinum<S extends SchemaConfig>(config: TablinumConfig<S>): Effect.Effect<DatabaseHandle<S>, ValidationError | StorageError | CryptoError, Scope.Scope>;
@@ -1,13 +1,34 @@
1
1
  import type { Effect } from "effect";
2
+ import type { CollectionDef, CollectionFields } from "../schema/collection.ts";
2
3
  import type { CollectionHandle } from "../crud/collection-handle.ts";
3
4
  import type { SchemaConfig } from "../schema/types.ts";
4
- import type { StorageError, SyncError, RelayError, CryptoError } from "../errors.ts";
5
+ import type { StorageError, SyncError, RelayError, CryptoError, ValidationError } from "../errors.ts";
6
+ import type { Invite } from "./invite.ts";
7
+ import type { MemberRecord } from "./members.ts";
8
+ import type { RelayStatus } from "../sync/relay.ts";
5
9
  export type SyncStatus = "idle" | "syncing";
6
10
  export interface DatabaseHandle<S extends SchemaConfig> {
7
11
  readonly collection: <K extends string & keyof S>(name: K) => CollectionHandle<S[K]>;
12
+ readonly publicKey: string;
13
+ readonly members: CollectionHandle<CollectionDef<CollectionFields>>;
8
14
  readonly exportKey: () => string;
15
+ readonly exportInvite: () => Invite;
9
16
  readonly close: () => Effect.Effect<void, StorageError>;
10
17
  readonly rebuild: () => Effect.Effect<void, StorageError>;
11
18
  readonly sync: () => Effect.Effect<void, SyncError | RelayError | CryptoError | StorageError>;
12
19
  readonly getSyncStatus: () => Effect.Effect<SyncStatus>;
20
+ readonly subscribeSyncStatus: (callback: (status: SyncStatus) => void) => () => void;
21
+ readonly pendingCount: () => Effect.Effect<number>;
22
+ readonly subscribePendingCount: (callback: (count: number) => void) => () => void;
23
+ readonly getRelayStatus: () => RelayStatus;
24
+ readonly subscribeRelayStatus: (callback: (status: RelayStatus) => void) => () => void;
25
+ readonly addMember: (pubkey: string) => Effect.Effect<void, ValidationError | StorageError | CryptoError>;
26
+ readonly removeMember: (pubkey: string) => Effect.Effect<void, ValidationError | StorageError | SyncError | RelayError | CryptoError>;
27
+ readonly getMembers: () => Effect.Effect<ReadonlyArray<MemberRecord>, StorageError>;
28
+ readonly setProfile: (profile: {
29
+ name?: string;
30
+ picture?: string;
31
+ about?: string;
32
+ nip05?: string;
33
+ }) => Effect.Effect<void, ValidationError | StorageError | CryptoError>;
13
34
  }
@@ -0,0 +1,48 @@
1
+ import { Option, Schema } from "effect";
2
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
3
+ import { EpochId, DatabaseName } from "../brands.ts";
4
+ export { EpochId, DatabaseName };
5
+ export { bytesToHex, hexToBytes };
6
+ export interface EpochKeyInput {
7
+ readonly epochId: EpochId;
8
+ readonly key: string;
9
+ }
10
+ export interface EpochKey {
11
+ readonly id: EpochId;
12
+ readonly privateKey: string;
13
+ readonly publicKey: string;
14
+ readonly createdBy: string;
15
+ readonly parentEpoch?: EpochId;
16
+ }
17
+ export interface EpochStore {
18
+ readonly epochs: Map<EpochId, EpochKey>;
19
+ readonly keysByPublicKey: Map<string, Uint8Array>;
20
+ currentEpochId: EpochId;
21
+ }
22
+ export declare const EpochKeyInputSchema: Schema.Struct<{
23
+ readonly epochId: Schema.String;
24
+ readonly key: Schema.String;
25
+ }>;
26
+ interface EpochStoreSnapshot {
27
+ readonly epochs: ReadonlyArray<{
28
+ readonly id: string;
29
+ readonly privateKey: string;
30
+ readonly createdBy: string;
31
+ readonly parentEpoch?: string;
32
+ }>;
33
+ readonly currentEpochId: string;
34
+ }
35
+ export declare function createEpochKey(id: EpochId, privateKeyHex: string, createdBy: string, parentEpoch?: EpochId): EpochKey;
36
+ export declare function createEpochStore(initialEpoch: EpochKey): EpochStore;
37
+ export declare function addEpoch(store: EpochStore, epoch: EpochKey): void;
38
+ export declare function hydrateEpochStore(snapshot: EpochStoreSnapshot): EpochStore;
39
+ export declare function createEpochStoreFromInputs(epochKeys: ReadonlyArray<EpochKeyInput>, options?: {
40
+ readonly createdBy?: string | undefined;
41
+ }): EpochStore;
42
+ export declare function getCurrentEpoch(store: EpochStore): EpochKey;
43
+ export declare function getCurrentPublicKey(store: EpochStore): string;
44
+ export declare function getAllPublicKeys(store: EpochStore): string[];
45
+ export declare function getDecryptionKey(store: EpochStore, publicKey: string): Uint8Array | undefined;
46
+ export declare function exportEpochKeys(store: EpochStore): ReadonlyArray<EpochKeyInput>;
47
+ export declare function stringifyEpochStore(store: EpochStore): string;
48
+ export declare function deserializeEpochStore(raw: string): Option.Option<EpochStore>;
@@ -0,0 +1,8 @@
1
+ import { DatabaseName, type EpochKeyInput } from "./epoch.ts";
2
+ export interface Invite {
3
+ readonly epochKeys: Array<EpochKeyInput>;
4
+ readonly relays: string[];
5
+ readonly dbName: DatabaseName;
6
+ }
7
+ export declare function encodeInvite(invite: Invite): string;
8
+ export declare function decodeInvite(encoded: string): Invite;
@@ -0,0 +1,24 @@
1
+ import { Option } from "effect";
2
+ import type { NostrEvent } from "nostr-tools/pure";
3
+ import type { EpochKey, EpochStore } from "./epoch.ts";
4
+ import { EpochId } from "./epoch.ts";
5
+ export interface RotationResult {
6
+ readonly epoch: EpochKey;
7
+ readonly wrappedEvents: NostrEvent[];
8
+ readonly removalNotices: NostrEvent[];
9
+ }
10
+ export interface RotationData {
11
+ readonly _rotation: true;
12
+ readonly epochId: EpochId;
13
+ readonly epochKey: string;
14
+ readonly parentEpoch: EpochId;
15
+ readonly removedMembers: readonly string[];
16
+ }
17
+ export interface RemovalNotice {
18
+ readonly _removed: true;
19
+ readonly epochId: EpochId;
20
+ readonly removedBy: string;
21
+ }
22
+ export declare function createRotation(epochStore: EpochStore, senderPrivateKey: Uint8Array, senderPublicKey: string, remainingMemberPubkeys: string[], removedMemberPubkeys: string[]): RotationResult;
23
+ export declare function parseRotationEvent(content: string, dTag: string): Option.Option<RotationData>;
24
+ export declare function parseRemovalNotice(content: string, dTag: string): Option.Option<RemovalNotice>;
@@ -0,0 +1,24 @@
1
+ import { Effect, Option } from "effect";
2
+ import type { CollectionDef } from "../schema/collection.ts";
3
+ import type { RelayHandle } from "../sync/relay.ts";
4
+ import { RelayError } from "../errors.ts";
5
+ import type { EpochId } from "../brands.ts";
6
+ export interface MemberRecord {
7
+ readonly id: string;
8
+ readonly name?: string;
9
+ readonly picture?: string;
10
+ readonly about?: string;
11
+ readonly nip05?: string;
12
+ readonly addedAt: number;
13
+ readonly addedInEpoch: EpochId;
14
+ readonly removedAt?: number;
15
+ readonly removedInEpoch?: EpochId;
16
+ }
17
+ export interface AuthorProfile {
18
+ readonly name?: string;
19
+ readonly picture?: string;
20
+ readonly about?: string;
21
+ readonly nip05?: string;
22
+ }
23
+ export declare const membersCollectionDef: CollectionDef;
24
+ export declare function fetchAuthorProfile(relay: RelayHandle, relayUrls: readonly string[], pubkey: string): Effect.Effect<Option.Option<AuthorProfile>, RelayError>;
@@ -0,0 +1,16 @@
1
+ import { Effect } from "effect";
2
+ import { ValidationError } from "../errors.ts";
3
+ import { type EpochKeyInput, DatabaseName } from "./epoch.ts";
4
+ export interface RuntimeConfigSource {
5
+ readonly relays: readonly string[];
6
+ readonly privateKey?: Uint8Array | undefined;
7
+ readonly epochKeys?: ReadonlyArray<EpochKeyInput> | undefined;
8
+ readonly dbName?: string | undefined;
9
+ }
10
+ export interface ResolvedRuntimeConfig {
11
+ readonly relays: readonly string[];
12
+ readonly privateKey?: Uint8Array | undefined;
13
+ readonly epochKeys?: ReadonlyArray<EpochKeyInput> | undefined;
14
+ readonly dbName: DatabaseName;
15
+ }
16
+ export declare function resolveRuntimeConfig(source: RuntimeConfigSource): Effect.Effect<ResolvedRuntimeConfig, ValidationError>;
package/dist/index.d.ts CHANGED
@@ -6,7 +6,14 @@ export type { InferRecord, SchemaConfig } from "./schema/types.ts";
6
6
  export { createTablinum } from "./db/create-tablinum.ts";
7
7
  export type { TablinumConfig } from "./db/create-tablinum.ts";
8
8
  export type { DatabaseHandle, SyncStatus } from "./db/database-handle.ts";
9
+ export { encodeInvite, decodeInvite } from "./db/invite.ts";
10
+ export type { Invite } from "./db/invite.ts";
11
+ export { EpochId, DatabaseName } from "./brands.ts";
12
+ export type { EpochKey, EpochKeyInput } from "./db/epoch.ts";
13
+ export type { MemberRecord } from "./db/members.ts";
9
14
  export type { CollectionHandle } from "./crud/collection-handle.ts";
10
15
  export type { CollectionOptions } from "./schema/collection.ts";
11
- export type { WhereClause, QueryBuilder, OrderByBuilder, QueryExecutor, } from "./crud/query-builder.ts";
16
+ export type { WhereClause, QueryBuilder, OrderByBuilder } from "./crud/query-builder.ts";
17
+ export { TablinumLive } from "./layers/TablinumLive.ts";
18
+ export type { TablinumConfigShape } from "./services/Config.ts";
12
19
  export { ValidationError, StorageError, CryptoError, RelayError, SyncError, NotFoundError, ClosedError, } from "./errors.ts";