@foretag/tanstack-db-surrealdb 0.5.7 → 0.6.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.
package/README.md CHANGED
@@ -1,137 +1,313 @@
1
- # TanstackDB SurrealDB Collections
1
+ # @foretag/tanstack-db-surrealdb
2
2
 
3
- Add Offline / Local First Caching & Syncing to your SurrealDB app with TanstackDB and Loro (CRDTs). Designed for SurrealDB v3 and SurrealDB JS SDK v2.
3
+ TanStack DB collection adapter for SurrealDB JS with:
4
4
 
5
- - Local / Offline first applications with TanstackDB and Loro
6
- - High performance with Low resource consumption
7
- - Works with Web, Desktop or Native (WASM based)
8
- - Support for React, Svelte, Vue and any Framework!
5
+ - Realtime replication (`LIVE`)
6
+ - Local-first writes
7
+ - Optional E2EE envelopes (`version/algorithm/key_id/nonce/ciphertext`)
8
+ - Optional Loro CRDT replication (`json`, `richtext`)
9
+ - Query-driven sync modes (`eager`, `on-demand`, `progressive`)
9
10
 
10
- ## Installation
11
+ ## Install
11
12
 
12
- ### NPM
13
13
  ```sh
14
- # NPM
15
14
  npm install @foretag/tanstack-db-surrealdb
16
- # Bun
17
- bun install @foretag/tanstack-db-surrealdb
15
+ # or
16
+ bun add @foretag/tanstack-db-surrealdb
18
17
  ```
19
18
 
20
- ## Usage
19
+ ## Quick Start
20
+
21
21
  ```ts
22
- // db.ts
23
- import { QueryClient } from '@tanstack/svelte-query'; // Can also use '@tanstack/react-query' etc.
22
+ import { createCollection } from '@tanstack/db';
23
+ import { QueryClient } from '@tanstack/query-core';
24
24
  import { Surreal } from 'surrealdb';
25
+ import { surrealCollectionOptions } from '@foretag/tanstack-db-surrealdb';
25
26
 
26
- export const db = new Surreal();
27
- await db.connect('ws://localhost:8000/rpc');
28
- await db.use({ ns: 'ns', db: 'db' });
27
+ const db = new Surreal();
28
+ const queryClient = new QueryClient();
29
29
 
30
- export const queryClient = new QueryClient();
30
+ type Product = { id: string; name: string; price: number };
31
31
 
32
- // collections/products.ts
33
- import { eq } from '@tanstack/db';
34
- import { db, queryClient } from '../db';
35
- import { createCollection } from '@tanstack/db';
36
- import { surrealCollectionOptions } from '@foretag/tanstack-db-surrealdb';
37
- import { useLiveQuery } from '@tanstack/react-db';
38
-
39
- // Collection Type, could also be generated
40
- type Product = {
41
- id: string;
42
- name: string;
43
- price: number;
44
- store?: string;
32
+ export const products = createCollection(
33
+ surrealCollectionOptions<Product>({
34
+ db,
35
+ table: { name: 'product' },
36
+ queryClient,
37
+ queryKey: ['product'],
38
+ syncMode: 'eager',
39
+ }),
40
+ );
41
+ ```
42
+
43
+ ## Adapter API
44
+
45
+ ```ts
46
+ type SurrealCollectionOptions<T> = {
47
+ db: Surreal;
48
+ table: Table | { name: string; relation?: boolean } | string;
49
+ queryClient: QueryClient;
50
+ queryKey: readonly unknown[];
51
+ syncMode?: 'eager' | 'on-demand' | 'progressive';
52
+ e2ee?: {
53
+ enabled: boolean;
54
+ crypto: CryptoProvider;
55
+ aad?: (ctx: { table: string; id: string; kind: 'base'|'update'|'snapshot'; baseTable?: string }) => Uint8Array;
56
+ };
57
+ crdt?: {
58
+ enabled: boolean;
59
+ profile: 'json' | 'richtext';
60
+ updatesTable: Table | { name: string } | string;
61
+ snapshotsTable?: Table | { name: string } | string;
62
+ // Optional overrides. If omitted, adapter uses built-in handlers for `profile`.
63
+ materialize?: (doc: LoroDoc, id: string) => T;
64
+ applyLocalChange?: (doc: LoroDoc, change: { type: 'insert'|'update'|'delete'; value: T }) => void;
65
+ persistMaterializedView?: boolean;
66
+ actor?: string | ((ctx: { id: string; change?: { type: 'insert'|'update'|'delete'; value: T } }) => string | undefined);
67
+ localActorId?: string; // deprecated
68
+ };
45
69
  };
70
+ ```
46
71
 
47
- export const products = createCollection(
48
- surrealCollectionOptions<Product>({
49
- db,
50
- queryKey: ['products'],
51
- queryClient,
52
- useLoro: true, // Optional if you need CRDTs
53
- table: { name: 'products' },
54
- }),
55
- )
56
-
57
- const data = useLiveQuery((q) =>
58
- q
59
- .from({ products })
60
- .where(({ products }) => eq(products.store, '123'))
61
- )
72
+ ## E2EE
73
+
74
+ Envelope fields stored in Surreal records:
75
+
76
+ ```ts
77
+ type EncryptedEnvelope = {
78
+ version: number;
79
+ algorithm: string;
80
+ key_id: string;
81
+ nonce: string;
82
+ ciphertext: string;
83
+ };
62
84
  ```
63
85
 
64
- For syncModes, please see [Example](https://github.com/ForetagInc/tanstack-db-surrealdb/blob/master/examples/syncMode.ts)
86
+ Default AAD:
65
87
 
66
- ## Vite / Next.JS
88
+ - Base records: `<table>:<record_id>`
89
+ - CRDT updates/snapshots: `<updates_or_snapshots_table>:<base_table>:<doc_id>`
67
90
 
68
- ### Vite
69
- ```sh
70
- bun install vite-plugin-wasm vite-plugin-top-level-await -D
91
+ Included provider:
92
+
93
+ - `WebCryptoAESGCM` (`AES-256-GCM`, versioned envelope)
94
+
95
+ ## CRDT Profiles
96
+
97
+ CRDT is managed by profile by default:
98
+
99
+ - `profile: 'json'` uses built-in JSON handlers
100
+ - `profile: 'richtext'` uses built-in richtext handlers
101
+
102
+ Advanced overrides are still available:
103
+
104
+ - `createLoroProfile('json' | 'richtext')`
105
+ - `materialize` and `applyLocalChange` in `crdt` options
106
+
107
+ For CRDT loop-prevention metadata, prefer `crdt.actor` so actor identity can be resolved per doc/write. `localActorId` remains only for backwards compatibility.
108
+
109
+ ## CRDT Table Requirements
110
+
111
+ For `crdt.enabled: true`, users must provide:
112
+
113
+ - Base table (`table`) for record identity and optional materialized metadata.
114
+ - Updates table (`crdt.updatesTable`) as append-only CRDT log.
115
+
116
+ Optional:
117
+
118
+ - Snapshots table (`crdt.snapshotsTable`) for compaction and faster hydration.
119
+
120
+ If `crdt.updatesTable` is missing, CRDT mode cannot function.
121
+
122
+ ## SQL Templates
123
+
124
+ ### Plain
125
+
126
+ ```sql
127
+ DEFINE TABLE note SCHEMAFULL;
128
+ DEFINE FIELD title ON note TYPE string;
129
+ DEFINE FIELD body ON note TYPE string;
130
+ DEFINE FIELD updated_at ON note TYPE datetime VALUE time::now();
131
+ DEFINE INDEX note_updated ON note FIELDS updated_at;
132
+ ```
133
+
134
+ ### E2EE-only
135
+
136
+ ```sql
137
+ DEFINE TABLE secret_note SCHEMAFULL;
138
+ DEFINE FIELD owner ON secret_note TYPE record<account>;
139
+ DEFINE FIELD updated_at ON secret_note TYPE datetime;
140
+ DEFINE FIELD version ON secret_note TYPE int;
141
+ DEFINE FIELD algorithm ON secret_note TYPE string;
142
+ DEFINE FIELD key_id ON secret_note TYPE string;
143
+ DEFINE FIELD nonce ON secret_note TYPE string;
144
+ DEFINE FIELD ciphertext ON secret_note TYPE string;
145
+ DEFINE INDEX secret_note_owner_updated ON secret_note FIELDS owner, updated_at;
146
+ ```
147
+
148
+ ### CRDT-only
149
+
150
+ ```sql
151
+ DEFINE TABLE doc SCHEMAFULL;
152
+ DEFINE FIELD owner ON doc TYPE record<account>;
153
+ DEFINE FIELD updated_at ON doc TYPE datetime;
154
+ DEFINE INDEX doc_owner_updated ON doc FIELDS owner, updated_at;
155
+
156
+ -- Necessary for CRDT updates
157
+ DEFINE TABLE crdt_update SCHEMAFULL;
158
+ DEFINE FIELD doc ON crdt_update TYPE record<doc>;
159
+ DEFINE FIELD ts ON crdt_update TYPE datetime;
160
+ DEFINE FIELD update_bytes ON crdt_update TYPE string;
161
+ DEFINE FIELD actor ON crdt_update TYPE string;
162
+ DEFINE INDEX crdt_doc_ts ON crdt_update FIELDS doc, ts;
163
+
164
+ DEFINE TABLE crdt_snapshot SCHEMAFULL;
165
+ DEFINE FIELD doc ON crdt_snapshot TYPE record<doc>;
166
+ DEFINE FIELD ts ON crdt_snapshot TYPE datetime;
167
+ DEFINE FIELD snapshot_bytes ON crdt_snapshot TYPE string;
168
+ DEFINE INDEX snap_doc_ts ON crdt_snapshot FIELDS doc, ts;
71
169
  ```
72
170
 
73
- `vite.config.ts`
171
+ ### CRDT + E2EE
172
+
173
+ ```sql
174
+ DEFINE TABLE secure_doc SCHEMAFULL;
175
+ DEFINE FIELD owner ON secure_doc TYPE record<account>;
176
+ DEFINE FIELD updated_at ON secure_doc TYPE datetime;
177
+ DEFINE INDEX secure_doc_owner_updated ON secure_doc FIELDS owner, updated_at;
178
+
179
+ DEFINE TABLE crdt_update SCHEMAFULL;
180
+ DEFINE FIELD doc ON crdt_update TYPE record<secure_doc>;
181
+ DEFINE FIELD ts ON crdt_update TYPE datetime;
182
+ DEFINE FIELD actor ON crdt_update TYPE string;
183
+ DEFINE FIELD version ON crdt_update TYPE int;
184
+ DEFINE FIELD algorithm ON crdt_update TYPE string;
185
+ DEFINE FIELD key_id ON crdt_update TYPE string;
186
+ DEFINE FIELD nonce ON crdt_update TYPE string;
187
+ DEFINE FIELD ciphertext ON crdt_update TYPE string;
188
+ DEFINE INDEX crdt_doc_ts ON crdt_update FIELDS doc, ts;
189
+ ```
190
+
191
+ If a single `crdt_update` table is shared across multiple base tables, use a union type such as `record<doc> | record<sheet>`.
192
+
193
+ ## Usage Snippets
194
+
195
+ ### E2EE-only secret table
74
196
 
75
197
  ```ts
76
- // Plugins
77
- import wasm from 'vite-plugin-wasm';
78
- import topLevelAwait from 'vite-plugin-top-level-await';
198
+ const provider = await WebCryptoAESGCM.fromRawKey(rawKey, { kid: 'org-key-2026-01' });
79
199
 
80
- export default defineConfig({
81
- plugins: [...otherConfigures, wasm(), topLevelAwait()],
82
- });
200
+ const secrets = createCollection(
201
+ surrealCollectionOptions<{ id: string; title: string; body: string }>({
202
+ db,
203
+ table: { name: 'secret_note' },
204
+ queryClient,
205
+ queryKey: ['secret-note'],
206
+ syncMode: 'eager',
207
+ e2ee: { enabled: true, crypto: provider },
208
+ }),
209
+ );
83
210
  ```
84
211
 
85
- ### NextJS
86
- `next.config.js`
212
+ ### CRDT richtext docs
87
213
 
88
214
  ```ts
89
- module.exports = {
90
- webpack: function (config) {
91
- config.experiments = {
92
- layers: true,
93
- asyncWebAssembly: true,
94
- };
95
- return config;
96
- },
215
+ const docs = createCollection(
216
+ surrealCollectionOptions<{ id: string; content: string; title?: string }>({
217
+ db,
218
+ table: { name: 'doc' },
219
+ queryClient,
220
+ queryKey: ['doc'],
221
+ syncMode: 'on-demand',
222
+ crdt: {
223
+ enabled: true,
224
+ profile: 'richtext',
225
+ updatesTable: { name: 'crdt_update' },
226
+ snapshotsTable: { name: 'crdt_snapshot' },
227
+ actor: ({ id }) => id.startsWith('team-a') ? 'device:team-a:abc' : 'device:team-b:abc',
228
+ },
229
+ }),
230
+ );
231
+ ```
232
+
233
+ ### RecordId model example
234
+
235
+ ```ts
236
+ import { RecordId } from 'surrealdb';
237
+
238
+ type CalendarEvent = {
239
+ id: RecordId<'calendar_event'>;
240
+ owner: RecordId<'account'>;
241
+ title: string;
242
+ start_at: string;
97
243
  };
244
+
245
+ await calendarEvents.insert({
246
+ id: new RecordId('calendar_event', 'evt-001'),
247
+ owner: new RecordId('account', 'user-123'),
248
+ title: 'Planning',
249
+ start_at: '2026-02-23T10:00:00.000Z',
250
+ });
98
251
  ```
99
252
 
100
- ## CRDTs
253
+ Full runnable example: `examples/record-id.ts`.
101
254
 
102
- If you need to use CRDTs for your application consider adding the following fields to the specific tables and set `useLoro: true` for the respective table. Please note these fields are opinionated, therefore fixed and required:
255
+ ### On-demand drive listing (query-driven)
103
256
 
104
- ```sql
105
- DEFINE FIELD OVERWRITE sync_deleted ON <table>
106
- TYPE bool
107
- DEFAULT false
108
- COMMENT 'Tombstone for CRDTs';
109
-
110
- DEFINE FIELD OVERWRITE updated_at ON <table>
111
- TYPE datetime
112
- VALUE time::now();
257
+ ```ts
258
+ import { createLiveQueryCollection, eq } from '@tanstack/db';
259
+
260
+ const files = createCollection(
261
+ surrealCollectionOptions<{ id: string; owner: string; updated_at: string; name: string }>({
262
+ db,
263
+ table: { name: 'file' },
264
+ queryClient,
265
+ queryKey: ['file'],
266
+ syncMode: 'on-demand',
267
+ }),
268
+ );
269
+
270
+ const ownerFiles = createLiveQueryCollection((q) =>
271
+ q
272
+ .from({ files })
273
+ .where(({ files }) => eq(files.owner, 'account:abc'))
274
+ .select(({ files }) => files),
275
+ );
276
+
277
+ await ownerFiles.preload();
113
278
  ```
114
279
 
115
- > While using SurrealDB as a Web Database, please remember to allow `SELECT` & `UPDATE` permissions for the `sync_deleted` and `updated_at` fields for the respective access.
280
+ ## Key Wrapping / Multi-Principal Access
281
+
282
+ This adapter expects key management to be provided by your app or KMS. For production shared access (users, teams, orgs), keep using wrapped keys:
283
+
284
+ - Encrypt entity data with a data key.
285
+ - Wrap that data key for each authorized principal (user/team/service/device).
286
+ - Resolve the active key by `kid` at decrypt time.
287
+ - Rotate by issuing a new `kid` and re-wrapping/re-encrypting progressively.
288
+
289
+ The adapter consumes derived keys through `CryptoProvider`; it does not manage wrapping policy for you.
290
+
291
+ ## Testing
292
+
293
+ Unit tests (`bun test`) cover:
294
+
295
+ - id/query translation behavior
296
+ - modern eager + on-demand sync controls
297
+ - E2EE envelope/AAD behavior
298
+ - CRDT update append, snapshot hydration, and actor loop prevention
116
299
 
117
- ## FAQ
300
+ Real SurrealDB integration tests are available and run against a live instance:
118
301
 
119
- <details>
120
- <summary><strong>When do I need CRDTs?</strong></summary>
121
- <p>In most cases Tanstack DB is sufficient to handle CRUD operations. However, if you need to implement a distributed system that is offline first, CRDTs are the way to go. Think: Google Docs, Figma Pages, Notion Blocks etc. We recommend you check out <a href='https://www.loro.dev/' target='_blank'>Loro</a> for a deeper understanding.</p>
122
- </details>
302
+ 1. Copy `.env.example` to `.env` and fill connection/auth values.
303
+ 2. Run `bun run test:integration`.
123
304
 
124
- <details>
125
- <summary><strong>How do I achieve type safety?</strong></summary>
126
- <p>Using Codegen tools that generate types from your SurrealDB Schema, this means you don't have to manually maintain types for each Collection.</p>
127
- </details>
305
+ Required env:
128
306
 
129
- <details>
130
- <summary><strong>Can I use GraphQL alongside this Library?</strong></summary>
131
- <p>GraphQL workflow is in the works as SurrealDB's own implementation of the GraphQL protocol matures, we'll be able to provide a seamless integration. Since this library only targets TanstackDB, you can also use GraphQL for direct querying through Tanstack Query.</p>
132
- </details>
307
+ - `SURREAL_URL`
308
+ - `SURREAL_NAMESPACE`
309
+ - `SURREAL_DATABASE`
310
+ - `SURREAL_USERNAME`
311
+ - `SURREAL_PASSWORD`
133
312
 
134
- <details>
135
- <summary><strong>Can I reduce the package sizes?</strong></summary>
136
- <p>They can be reduced, but these steps are very unique based on use-case. Loro ships a WASM binary thats 1 MB Gzipped in size, it's one of the tradeoffs of using this approach. The maintainers up-stream are working on reducing the size of the WASM binary.</p>
137
- </details>
313
+ `SURREAL_REQUIRE_LIVE=true` (default) enforces LIVE query assertions; set it to `false` if you intentionally use a connection without LIVE support.
package/dist/index.d.mts CHANGED
@@ -1,12 +1,67 @@
1
1
  import { StandardSchemaV1 } from '@standard-schema/spec';
2
- import { IR, UtilsRecord, OperationConfig, Transaction, CollectionConfig } from '@tanstack/db';
3
- import { Container } from 'loro-crdt';
4
- import { RecordId, Surreal } from 'surrealdb';
2
+ import { CollectionConfig, UtilsRecord, LoadSubsetOptions, OperationConfig, Transaction } from '@tanstack/db';
3
+ import { Table, Surreal, RecordId } from 'surrealdb';
5
4
  import { QueryClient } from '@tanstack/query-core';
5
+ import { LoroDoc } from 'loro-crdt';
6
6
 
7
- type RecordIdLike = RecordId | string;
8
- declare const eqRecordId: (field: unknown, value: RecordIdLike) => IR.BasicExpression<boolean>;
7
+ type EncryptInput = {
8
+ plaintext: Bytes;
9
+ aad?: Bytes;
10
+ v?: number;
11
+ alg?: string;
12
+ kid?: string;
13
+ };
14
+ type DecryptInput = {
15
+ envelope: EncryptedEnvelope;
16
+ aad?: Bytes;
17
+ };
18
+ interface CryptoProvider {
19
+ encrypt(input: EncryptInput): Promise<EncryptedEnvelope>;
20
+ decrypt(input: DecryptInput): Promise<Bytes>;
21
+ }
22
+
23
+ /**
24
+ * @deprecated Use SurrealE2EEOptions from ../types.
25
+ */
26
+ interface E2EEConfig<TItem extends object> {
27
+ enabled: boolean;
28
+ serialize?: (item: TItem) => Bytes;
29
+ deserialize?: (bytes: Bytes) => TItem;
30
+ crypto: CryptoProvider;
31
+ fields?: {
32
+ ciphertext: string;
33
+ nonce: string;
34
+ version: string;
35
+ };
36
+ aad?: (ctx: {
37
+ table: string;
38
+ id: string;
39
+ }) => Bytes;
40
+ }
41
+ type EncryptionEnvelope = EncryptedEnvelope;
42
+ type EncryptionAADContext = AADContext;
43
+ type AdapterE2EEOptions = SurrealE2EEOptions;
44
+
45
+ type KeyResolver = (kid: string) => Promise<CryptoKey> | CryptoKey;
46
+ type WebCryptoAESGCMOptions = {
47
+ alg?: string;
48
+ version?: number;
49
+ kid?: string;
50
+ resolveKey?: KeyResolver;
51
+ };
52
+ declare class WebCryptoAESGCM implements CryptoProvider {
53
+ private readonly alg;
54
+ private readonly version;
55
+ private readonly kid;
56
+ private readonly resolveKey;
57
+ constructor(key: CryptoKey, options?: WebCryptoAESGCMOptions);
58
+ static fromRawKey(rawKey: Bytes, options?: Omit<WebCryptoAESGCMOptions, 'resolveKey'>): Promise<WebCryptoAESGCM>;
59
+ private keyFor;
60
+ encrypt(input: EncryptInput): Promise<EncryptedEnvelope>;
61
+ decrypt({ envelope, aad }: DecryptInput): Promise<Bytes>;
62
+ }
9
63
 
64
+ type Bytes = Uint8Array;
10
65
  type WithId<T> = T & {
11
66
  id: string | RecordId;
12
67
  };
@@ -18,36 +73,96 @@ type TableOptions = {
18
73
  name: string;
19
74
  relation?: boolean;
20
75
  };
21
- type SyncMode = 'eager' | 'on-demand';
22
- type SurrealCollectionConfig = {
23
- id?: string;
76
+ type TableLike = Table | TableOptions | string;
77
+ type SurrealSubset = LoadSubsetOptions;
78
+ type EncryptedEnvelope = {
79
+ v: number;
80
+ alg: string;
81
+ kid: string;
82
+ n: string;
83
+ ct: string;
84
+ };
85
+ type EnvelopeKind = 'base' | 'update' | 'snapshot';
86
+ type AADContext = {
87
+ table: string;
88
+ id: string;
89
+ kind: EnvelopeKind;
90
+ baseTable?: string;
91
+ };
92
+ type SurrealE2EEOptions = {
93
+ enabled: boolean;
94
+ crypto: CryptoProvider;
95
+ aad?: (ctx: AADContext) => Bytes;
96
+ };
97
+ type LocalChange<T> = {
98
+ type: 'insert' | 'update' | 'delete';
99
+ value: T;
100
+ };
101
+ type CRDTActorContext<T> = {
102
+ id: string;
103
+ change?: LocalChange<T>;
104
+ };
105
+ type SurrealCRDTOptions<T extends object> = {
106
+ enabled: boolean;
107
+ profile: 'json' | 'richtext';
108
+ updatesTable: TableLike;
109
+ snapshotsTable?: TableLike;
110
+ materialize?: (doc: LoroDoc, id: string) => T;
111
+ applyLocalChange?: (doc: LoroDoc, change: LocalChange<T>) => void;
112
+ persistMaterializedView?: boolean;
113
+ actor?: string | ((ctx: CRDTActorContext<T>) => string | undefined);
114
+ /** @deprecated Use `actor` instead. */
115
+ localActorId?: string;
116
+ };
117
+ type AdapterSyncMode = 'eager' | 'on-demand' | 'progressive';
118
+ type SurrealCollectionOptions<T extends object> = Omit<CollectionConfig<T>, 'onInsert' | 'onUpdate' | 'onDelete' | 'sync' | 'getKey' | 'syncMode'> & {
24
119
  db: Surreal;
25
- table: TableOptions;
26
- syncMode?: SyncMode;
120
+ table: TableLike;
27
121
  queryKey: readonly unknown[];
28
122
  queryClient: QueryClient;
29
- useLoro?: boolean;
30
- onError?: (e: unknown) => void;
123
+ syncMode?: AdapterSyncMode;
124
+ e2ee?: SurrealE2EEOptions;
125
+ crdt?: SurrealCRDTOptions<T>;
126
+ onError?: (error: unknown) => void;
127
+ };
128
+ type SurrealCollectionOptionsReturn<T extends {
129
+ id: string | RecordId;
130
+ }> = CollectionConfig<T, string, StandardSchemaV1<Omit<T, 'id'> & {
131
+ id?: T['id'];
132
+ }, T>, UtilsRecord> & {
133
+ schema: StandardSchemaV1<Omit<T, 'id'> & {
134
+ id?: T['id'];
135
+ }, T>;
136
+ utils: UtilsRecord;
31
137
  };
32
138
 
33
139
  declare const toRecordKeyString: (rid: RecordId | string) => string;
34
140
 
141
+ interface CRDTProfileAdapter<T extends object> {
142
+ materialize: (doc: LoroDoc, id: string) => T;
143
+ applyLocalChange: (doc: LoroDoc, change: LocalChange<T>) => void;
144
+ }
145
+
146
+ type LoroProfile = 'json' | 'richtext';
147
+ declare const materializeLoroJson: <T extends object>(doc: LoroDoc, id: string) => T;
148
+ declare const applyLoroJsonChange: <T extends object>(doc: LoroDoc, change: LocalChange<T>) => void;
149
+ declare const materializeLoroRichtext: <T extends object>(doc: LoroDoc, id: string) => T;
150
+ declare const applyLoroRichtextChange: <T extends object>(doc: LoroDoc, change: LocalChange<T>) => void;
151
+ declare const createLoroProfile: <T extends object = Record<string, unknown>>(profile: LoroProfile) => CRDTProfileAdapter<T>;
152
+
35
153
  type MutationInput<T extends {
36
154
  id: string | RecordId;
37
155
  }> = Omit<T, 'id'> & {
38
156
  id?: T['id'];
39
157
  };
40
-
158
+ declare function surrealCollectionOptions<T extends SyncedTable<object>>(config: SurrealCollectionOptions<T>): CollectionConfig<T, string, StandardSchemaV1<MutationInput<T>, T>, UtilsRecord> & {
159
+ schema: StandardSchemaV1<MutationInput<T>, T>;
160
+ utils: UtilsRecord;
161
+ };
41
162
  declare module '@tanstack/db' {
42
163
  interface Collection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInsertInput extends object = T> {
43
164
  delete(keys: Array<TKey | RecordId | string> | TKey | RecordId | string, config?: OperationConfig): Transaction<any>;
44
165
  }
45
166
  }
46
- declare function surrealCollectionOptions<T extends SyncedTable<object>, S extends Record<string, Container> = {
47
- [k: string]: never;
48
- }>({ id, useLoro, onError, db, queryClient, queryKey, syncMode, ...config }: SurrealCollectionConfig): CollectionConfig<T, string, StandardSchemaV1<MutationInput<T>, T>, UtilsRecord> & {
49
- schema: StandardSchemaV1<MutationInput<T>, T>;
50
- utils: UtilsRecord;
51
- };
52
167
 
53
- export { eqRecordId, surrealCollectionOptions, toRecordKeyString };
168
+ export { type AADContext, type AdapterE2EEOptions, type AdapterSyncMode, type Bytes, type CRDTActorContext, type CryptoProvider, type DecryptInput, type E2EEConfig, type EncryptInput, type EncryptedEnvelope, type EncryptionAADContext, type EncryptionEnvelope, type EnvelopeKind, type LocalChange, type LoroProfile, type SurrealCRDTOptions, type SurrealCollectionOptions, type SurrealCollectionOptionsReturn, type SurrealE2EEOptions, type SurrealSubset, type SyncedTable, type TableLike, type TableOptions, WebCryptoAESGCM, type WithId, applyLoroJsonChange, applyLoroRichtextChange, createLoroProfile, materializeLoroJson, materializeLoroRichtext, surrealCollectionOptions, toRecordKeyString };