@fatagnus/dink-sync 1.0.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 (186) hide show
  1. package/README.md +312 -0
  2. package/dist/client/attachment.d.ts +225 -0
  3. package/dist/client/attachment.d.ts.map +1 -0
  4. package/dist/client/attachment.js +402 -0
  5. package/dist/client/attachment.js.map +1 -0
  6. package/dist/client/binary-encoding.d.ts +45 -0
  7. package/dist/client/binary-encoding.d.ts.map +1 -0
  8. package/dist/client/binary-encoding.js +90 -0
  9. package/dist/client/binary-encoding.js.map +1 -0
  10. package/dist/client/collection.d.ts +10 -0
  11. package/dist/client/collection.d.ts.map +1 -0
  12. package/dist/client/collection.js +924 -0
  13. package/dist/client/collection.js.map +1 -0
  14. package/dist/client/compression.d.ts +56 -0
  15. package/dist/client/compression.d.ts.map +1 -0
  16. package/dist/client/compression.js +173 -0
  17. package/dist/client/compression.js.map +1 -0
  18. package/dist/client/crdt/index.d.ts +2 -0
  19. package/dist/client/crdt/index.d.ts.map +1 -0
  20. package/dist/client/crdt/index.js +2 -0
  21. package/dist/client/crdt/index.js.map +1 -0
  22. package/dist/client/crdt/yjs-doc.d.ts +88 -0
  23. package/dist/client/crdt/yjs-doc.d.ts.map +1 -0
  24. package/dist/client/crdt/yjs-doc.js +123 -0
  25. package/dist/client/crdt/yjs-doc.js.map +1 -0
  26. package/dist/client/index.d.ts +66 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +233 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client/mock-transport.d.ts +155 -0
  31. package/dist/client/mock-transport.d.ts.map +1 -0
  32. package/dist/client/mock-transport.js +292 -0
  33. package/dist/client/mock-transport.js.map +1 -0
  34. package/dist/client/network-detector.d.ts +65 -0
  35. package/dist/client/network-detector.d.ts.map +1 -0
  36. package/dist/client/network-detector.js +147 -0
  37. package/dist/client/network-detector.js.map +1 -0
  38. package/dist/client/provisioning.d.ts +126 -0
  39. package/dist/client/provisioning.d.ts.map +1 -0
  40. package/dist/client/provisioning.js +125 -0
  41. package/dist/client/provisioning.js.map +1 -0
  42. package/dist/client/signal.d.ts +13 -0
  43. package/dist/client/signal.d.ts.map +1 -0
  44. package/dist/client/signal.js +27 -0
  45. package/dist/client/signal.js.map +1 -0
  46. package/dist/client/sync-engine.d.ts +298 -0
  47. package/dist/client/sync-engine.d.ts.map +1 -0
  48. package/dist/client/sync-engine.js +904 -0
  49. package/dist/client/sync-engine.js.map +1 -0
  50. package/dist/client/synced-edge.d.ts +109 -0
  51. package/dist/client/synced-edge.d.ts.map +1 -0
  52. package/dist/client/synced-edge.js +179 -0
  53. package/dist/client/synced-edge.js.map +1 -0
  54. package/dist/client/synced-offline-edge-types.d.ts +540 -0
  55. package/dist/client/synced-offline-edge-types.d.ts.map +1 -0
  56. package/dist/client/synced-offline-edge-types.js +10 -0
  57. package/dist/client/synced-offline-edge-types.js.map +1 -0
  58. package/dist/client/synced-offline-edge.d.ts +54 -0
  59. package/dist/client/synced-offline-edge.d.ts.map +1 -0
  60. package/dist/client/synced-offline-edge.js +731 -0
  61. package/dist/client/synced-offline-edge.js.map +1 -0
  62. package/dist/client/transport.d.ts +202 -0
  63. package/dist/client/transport.d.ts.map +1 -0
  64. package/dist/client/transport.js +409 -0
  65. package/dist/client/transport.js.map +1 -0
  66. package/dist/client/types.d.ts +622 -0
  67. package/dist/client/types.d.ts.map +1 -0
  68. package/dist/client/types.js +60 -0
  69. package/dist/client/types.js.map +1 -0
  70. package/dist/client/validation.d.ts +61 -0
  71. package/dist/client/validation.d.ts.map +1 -0
  72. package/dist/client/validation.js +57 -0
  73. package/dist/client/validation.js.map +1 -0
  74. package/dist/client/versioning.d.ts +134 -0
  75. package/dist/client/versioning.d.ts.map +1 -0
  76. package/dist/client/versioning.js +304 -0
  77. package/dist/client/versioning.js.map +1 -0
  78. package/dist/index.d.ts +40 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +51 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/persistence/encryption.d.ts +114 -0
  83. package/dist/persistence/encryption.d.ts.map +1 -0
  84. package/dist/persistence/encryption.js +286 -0
  85. package/dist/persistence/encryption.js.map +1 -0
  86. package/dist/persistence/index.d.ts +21 -0
  87. package/dist/persistence/index.d.ts.map +1 -0
  88. package/dist/persistence/index.js +20 -0
  89. package/dist/persistence/index.js.map +1 -0
  90. package/dist/persistence/memory.d.ts +32 -0
  91. package/dist/persistence/memory.d.ts.map +1 -0
  92. package/dist/persistence/memory.js +57 -0
  93. package/dist/persistence/memory.js.map +1 -0
  94. package/dist/persistence/migrations.d.ts +106 -0
  95. package/dist/persistence/migrations.d.ts.map +1 -0
  96. package/dist/persistence/migrations.js +176 -0
  97. package/dist/persistence/migrations.js.map +1 -0
  98. package/dist/persistence/pending-queue.d.ts +109 -0
  99. package/dist/persistence/pending-queue.d.ts.map +1 -0
  100. package/dist/persistence/pending-queue.js +249 -0
  101. package/dist/persistence/pending-queue.js.map +1 -0
  102. package/dist/persistence/pglite.d.ts +72 -0
  103. package/dist/persistence/pglite.d.ts.map +1 -0
  104. package/dist/persistence/pglite.js +126 -0
  105. package/dist/persistence/pglite.js.map +1 -0
  106. package/dist/persistence/quota-manager.d.ts +134 -0
  107. package/dist/persistence/quota-manager.d.ts.map +1 -0
  108. package/dist/persistence/quota-manager.js +242 -0
  109. package/dist/persistence/quota-manager.js.map +1 -0
  110. package/dist/persistence/types.d.ts +54 -0
  111. package/dist/persistence/types.d.ts.map +1 -0
  112. package/dist/persistence/types.js +2 -0
  113. package/dist/persistence/types.js.map +1 -0
  114. package/dist/react/OfflineEdgeProvider.d.ts +91 -0
  115. package/dist/react/OfflineEdgeProvider.d.ts.map +1 -0
  116. package/dist/react/OfflineEdgeProvider.js +127 -0
  117. package/dist/react/OfflineEdgeProvider.js.map +1 -0
  118. package/dist/react/SyncedOfflineEdgeProvider.d.ts +105 -0
  119. package/dist/react/SyncedOfflineEdgeProvider.d.ts.map +1 -0
  120. package/dist/react/SyncedOfflineEdgeProvider.js +138 -0
  121. package/dist/react/SyncedOfflineEdgeProvider.js.map +1 -0
  122. package/dist/react/index.d.ts +50 -0
  123. package/dist/react/index.d.ts.map +1 -0
  124. package/dist/react/index.js +51 -0
  125. package/dist/react/index.js.map +1 -0
  126. package/dist/react/useCollection.d.ts +77 -0
  127. package/dist/react/useCollection.d.ts.map +1 -0
  128. package/dist/react/useCollection.js +113 -0
  129. package/dist/react/useCollection.js.map +1 -0
  130. package/dist/react/useCollectionSyncMode.d.ts +61 -0
  131. package/dist/react/useCollectionSyncMode.d.ts.map +1 -0
  132. package/dist/react/useCollectionSyncMode.js +93 -0
  133. package/dist/react/useCollectionSyncMode.js.map +1 -0
  134. package/dist/react/useConnectionState.d.ts +44 -0
  135. package/dist/react/useConnectionState.d.ts.map +1 -0
  136. package/dist/react/useConnectionState.js +46 -0
  137. package/dist/react/useConnectionState.js.map +1 -0
  138. package/dist/react/useDocumentSyncStatus.d.ts +72 -0
  139. package/dist/react/useDocumentSyncStatus.d.ts.map +1 -0
  140. package/dist/react/useDocumentSyncStatus.js +110 -0
  141. package/dist/react/useDocumentSyncStatus.js.map +1 -0
  142. package/dist/react/useOfflineEdge.d.ts +58 -0
  143. package/dist/react/useOfflineEdge.d.ts.map +1 -0
  144. package/dist/react/useOfflineEdge.js +54 -0
  145. package/dist/react/useOfflineEdge.js.map +1 -0
  146. package/dist/react/usePendingChanges.d.ts +67 -0
  147. package/dist/react/usePendingChanges.d.ts.map +1 -0
  148. package/dist/react/usePendingChanges.js +90 -0
  149. package/dist/react/usePendingChanges.js.map +1 -0
  150. package/dist/react/useRejectedDocuments.d.ts +112 -0
  151. package/dist/react/useRejectedDocuments.d.ts.map +1 -0
  152. package/dist/react/useRejectedDocuments.js +213 -0
  153. package/dist/react/useRejectedDocuments.js.map +1 -0
  154. package/dist/react/useSyncControls.d.ts +96 -0
  155. package/dist/react/useSyncControls.d.ts.map +1 -0
  156. package/dist/react/useSyncControls.js +112 -0
  157. package/dist/react/useSyncControls.js.map +1 -0
  158. package/dist/react/useSyncProgress.d.ts +78 -0
  159. package/dist/react/useSyncProgress.d.ts.map +1 -0
  160. package/dist/react/useSyncProgress.js +90 -0
  161. package/dist/react/useSyncProgress.js.map +1 -0
  162. package/dist/react/useSyncRejected.d.ts +47 -0
  163. package/dist/react/useSyncRejected.d.ts.map +1 -0
  164. package/dist/react/useSyncRejected.js +55 -0
  165. package/dist/react/useSyncRejected.js.map +1 -0
  166. package/dist/react/useSyncStatus.d.ts +56 -0
  167. package/dist/react/useSyncStatus.d.ts.map +1 -0
  168. package/dist/react/useSyncStatus.js +59 -0
  169. package/dist/react/useSyncStatus.js.map +1 -0
  170. package/dist/react/useSyncedOfflineEdge.d.ts +69 -0
  171. package/dist/react/useSyncedOfflineEdge.d.ts.map +1 -0
  172. package/dist/react/useSyncedOfflineEdge.js +65 -0
  173. package/dist/react/useSyncedOfflineEdge.js.map +1 -0
  174. package/dist/service-worker/index.d.ts +7 -0
  175. package/dist/service-worker/index.d.ts.map +1 -0
  176. package/dist/service-worker/index.js +7 -0
  177. package/dist/service-worker/index.js.map +1 -0
  178. package/dist/service-worker/sync-worker.d.ts +230 -0
  179. package/dist/service-worker/sync-worker.d.ts.map +1 -0
  180. package/dist/service-worker/sync-worker.js +471 -0
  181. package/dist/service-worker/sync-worker.js.map +1 -0
  182. package/dist/types.d.ts +6 -0
  183. package/dist/types.d.ts.map +1 -0
  184. package/dist/types.js +3 -0
  185. package/dist/types.js.map +1 -0
  186. package/package.json +95 -0
package/README.md ADDED
@@ -0,0 +1,312 @@
1
+ # @fatagnus/dink-sync
2
+
3
+ Offline-first sync SDK for Dink edge platform with Effect.ts.
4
+
5
+ This SDK powers the **Offline-First Edge** type - edges that work offline and automatically sync when connectivity is restored.
6
+
7
+ | Edge Type | SDK | Use Case |
8
+ |-----------|-----|----------|
9
+ | **Lite Edge** | `@fatagnus/dink-sdk` | Always-connected IoT, RPC services |
10
+ | **Offline-First Edge** | ✅ This SDK | Offline data with auto-sync |
11
+ | **Full Edge** | Not yet available | Local NATS services (planned) |
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @fatagnus/dink-sync
17
+ # or
18
+ pnpm add @fatagnus/dink-sync
19
+ ```
20
+
21
+ ## Features
22
+
23
+ - **Offline-first**: Queue changes locally and sync when online
24
+ - **CRDT-based**: Conflict-free replicated data types for automatic conflict resolution
25
+ - **Real-time sync**: Bidirectional sync with dinkd server via NATS
26
+ - **React hooks**: Ready-to-use hooks for React applications
27
+ - **Persistence**: Pluggable persistence providers (memory, PGlite)
28
+ - **Type-safe**: Full TypeScript support with strict typing
29
+ - **Typed Client Factory**: Generate type-safe collection access from Convex schema
30
+
31
+ ## Quick Start — ⭐ Typed Client (STRONGLY PREFERRED)
32
+
33
+ **Always use typed clients** generated from your Convex schema. This is the only recommended approach for production applications.
34
+
35
+ > ⚠️ Do not use the low-level sync engine or generic collection APIs in production. They should only be used for quick prototyping.
36
+
37
+ ### 1. Define Your Schema
38
+
39
+ ```typescript
40
+ // convex/schema.ts
41
+ import { defineSchema, defineTable } from "convex/server";
42
+ import { v } from "convex/values";
43
+
44
+ export default defineSchema({
45
+ tasks: defineTable({
46
+ text: v.string(),
47
+ isCompleted: v.boolean(),
48
+ priority: v.optional(v.number()),
49
+ }),
50
+ users: defineTable({
51
+ name: v.string(),
52
+ email: v.string(),
53
+ }),
54
+ });
55
+ ```
56
+
57
+ ### 2. Generate Typed Client
58
+
59
+ ```bash
60
+ # Generate TypeScript types, validators, and typed client
61
+ dink codegen --convex-schema ./convex --convex-output ./src/generated --zod
62
+ ```
63
+
64
+ ### 3. Use the Typed Client
65
+
66
+ ```typescript
67
+ import { offlineEdge } from '@fatagnus/dink-sync';
68
+ import { PGlitePersistence } from '@fatagnus/dink-sync/persistence';
69
+ import { createTypedClient } from './generated';
70
+
71
+ // Initialize the offline edge client
72
+ const edge = offlineEdge.create({
73
+ persistence: new PGlitePersistence(),
74
+ config: {
75
+ serverUrl: 'nats://localhost:4222',
76
+ apiKey: 'your-edge-api-key',
77
+ },
78
+ });
79
+ await edge.init();
80
+
81
+ // Create the typed client - this is the recommended way!
82
+ const db = createTypedClient(edge.get());
83
+
84
+ // Type-safe collection access with full IDE autocompletion
85
+ const task = await db.tasks.insert({
86
+ text: 'Buy milk',
87
+ isCompleted: false,
88
+ });
89
+ console.log(`Created task: ${task.id}`);
90
+
91
+ // List all tasks
92
+ const allTasks = await db.tasks.list();
93
+
94
+ // Update a task
95
+ const updated = await db.tasks.update(task.id, { isCompleted: true });
96
+
97
+ // Delete a task
98
+ await db.tasks.delete(task.id);
99
+
100
+ // Validation is automatic!
101
+ try {
102
+ await db.tasks.insert({ text: '', isCompleted: false });
103
+ } catch (err) {
104
+ console.error('Validation failed:', err.message); // "text is required"
105
+ }
106
+ ```
107
+
108
+ ### Why Typed Client?
109
+
110
+ | Feature | Typed Client | Generic Collection |
111
+ |---------|--------------|--------------------|
112
+ | Type Safety | ✅ Compile-time checks | ⚠️ Runtime only |
113
+ | IDE Support | ✅ Full autocompletion | ⚠️ Limited |
114
+ | Validation | ✅ Automatic | ❌ Manual setup |
115
+ | Refactoring | ✅ Safe renames | ⚠️ String-based |
116
+
117
+ ## Alternative: Low-Level Sync Engine ⚠️ Advanced/Prototyping Only
118
+
119
+ For advanced use cases or quick prototyping, you can use the sync engine directly. **Migrate to typed clients before production:**
120
+
121
+ ```typescript
122
+ import { createSyncEngine, NatsTransport } from '@fatagnus/dink-sync/client';
123
+
124
+ // Create transport and engine
125
+ const transport = new NatsTransport({
126
+ serverUrl: 'nats://localhost:4222',
127
+ apiKey: 'your-edge-api-key',
128
+ appId: 'your-app-id',
129
+ edgeId: 'your-edge-id',
130
+ });
131
+
132
+ const engine = createSyncEngine({
133
+ serverUrl: 'nats://localhost:4222',
134
+ apiKey: 'your-edge-api-key',
135
+ transport,
136
+ });
137
+
138
+ // Connect and register documents
139
+ await engine.connect();
140
+ const actor = await engine.registerDocument('tasks', 'task-1');
141
+
142
+ // Queue changes
143
+ actor.queueChange(new Uint8Array([1, 2, 3]));
144
+
145
+ // Listen for sync events
146
+ engine.onSyncComplete((event) => {
147
+ console.log(`Synced ${event.documentId}`);
148
+ });
149
+ ```
150
+
151
+ ## React Integration
152
+
153
+ ```tsx
154
+ import { OfflineEdgeProvider, useCollection } from '@fatagnus/dink-sync/react';
155
+
156
+ function App() {
157
+ return (
158
+ <OfflineEdgeProvider config={{ serverUrl, apiKey, edgeId }}>
159
+ <TaskList />
160
+ </OfflineEdgeProvider>
161
+ );
162
+ }
163
+
164
+ function TaskList() {
165
+ const { items, insert, update, delete: remove } = useCollection('tasks');
166
+ // ...
167
+ }
168
+ ```
169
+
170
+ ## Testing
171
+
172
+ ### Unit Tests
173
+
174
+ Run unit tests (uses mock transport):
175
+
176
+ ```bash
177
+ pnpm test
178
+ ```
179
+
180
+ ### E2E Tests
181
+
182
+ E2E tests use testcontainers to spin up a real dinkd server.
183
+
184
+ ### Browser Tests
185
+
186
+ Browser tests verify PGlite persistence with IndexedDB in a real browser environment.
187
+
188
+ #### Prerequisites
189
+
190
+ 1. **Docker**: Install and ensure Docker daemon is running
191
+
192
+ 2. **dinkd image**: Build the dinkd Docker image from the project root:
193
+ ```bash
194
+ # From the project root directory
195
+ docker build -t dinkd:latest .
196
+ ```
197
+
198
+ #### Running E2E Tests
199
+
200
+ ```bash
201
+ pnpm test:e2e
202
+ ```
203
+
204
+ #### Running Browser Tests
205
+
206
+ **Prerequisites:**
207
+
208
+ 1. **Playwright browsers**: Install Chromium for browser testing:
209
+ ```bash
210
+ npx playwright install chromium
211
+ ```
212
+
213
+ **Run browser tests:**
214
+
215
+ ```bash
216
+ pnpm test:browser
217
+ ```
218
+
219
+ **Browser test coverage:**
220
+
221
+ - Data persistence with IndexedDB backend
222
+ - Data persistence across page reloads/sessions
223
+ - Large dataset handling (1000+ documents)
224
+ - Concurrent read/write operations
225
+ - Binary data integrity
226
+ - Special character handling in document IDs
227
+
228
+ #### E2E Test Coverage
229
+
230
+ The E2E tests verify:
231
+
232
+ - Edge connection to real dinkd server
233
+ - Document registration and sync operations
234
+ - External update subscription
235
+ - Concurrent sync from multiple edges
236
+ - Reconnection handling
237
+ - Error handling and event emission
238
+
239
+ #### Test Environment Variables
240
+
241
+ | Variable | Description | Default |
242
+ |----------|-------------|--------|
243
+ | `DINK_TEST_IMAGE` | Docker image for dinkd | `dinkd:latest` |
244
+
245
+ #### Troubleshooting Browser Tests
246
+
247
+ **Playwright not installed:**
248
+ - Run `npx playwright install chromium` to install the browser
249
+
250
+ **Tests timing out:**
251
+ - Large dataset tests may take longer; timeout is set to 60 seconds
252
+ - Consider running browser tests separately from unit tests
253
+
254
+ **IndexedDB errors:**
255
+ - Ensure you're running in a supported browser (Chromium)
256
+ - Check for quota limits in browser settings
257
+
258
+ #### Troubleshooting E2E Tests
259
+
260
+ **Container startup timeout**:
261
+ - Ensure Docker is running
262
+ - Check that the `dinkd:latest` image exists: `docker images | grep dinkd`
263
+ - Increase startup timeout in test configuration if needed
264
+
265
+ **Connection refused**:
266
+ - Container may not be fully started
267
+ - Check container logs for errors
268
+
269
+ **Port conflicts**:
270
+ - Testcontainers automatically maps to random ports, but conflicts can occur
271
+ - Stop any running dinkd containers manually if needed
272
+
273
+ ## API Reference
274
+
275
+ ### SyncEngine
276
+
277
+ - `connect()`: Connect to dinkd server
278
+ - `disconnect()`: Disconnect from server
279
+ - `destroy()`: Clean up all resources
280
+ - `registerDocument(collection, docId)`: Register a document for sync
281
+ - `unregisterDocument(collection, docId)`: Unregister a document
282
+ - `getConnectionState()`: Get current connection state
283
+ - `onStateChange(callback)`: Subscribe to connection state changes
284
+ - `onSyncStarted(callback)`: Subscribe to sync start events
285
+ - `onSyncComplete(callback)`: Subscribe to sync completion events
286
+ - `onSyncError(callback)`: Subscribe to sync error events
287
+ - `onSyncRejected(callback)`: Subscribe to sync rejection events
288
+ - `goOffline()`: Manually force offline mode
289
+ - `goOnline()`: Exit manual offline mode
290
+ - `discardLocalChanges(collection, docId)`: Discard pending changes for a document
291
+ - `forcePush(collection, docId)`: Force push local state to server
292
+
293
+ ### DocumentActor
294
+
295
+ - `queueChange(delta)`: Queue a change for sync
296
+ - `hasPendingChanges()`: Check if there are pending changes
297
+ - `onPendingChange(callback)`: Subscribe to pending state changes
298
+ - `getStateVector()`: Get current state vector
299
+ - `setStateVector(vector)`: Set state vector
300
+ - `applyExternalUpdate(update)`: Apply an external update
301
+ - `onExternalUpdate(callback)`: Subscribe to external updates
302
+
303
+ ### ConnectionState
304
+
305
+ - `Offline`: Not connected to server
306
+ - `Connecting`: Connection in progress
307
+ - `Online`: Connected and ready
308
+ - `Reconnecting`: Connection lost, attempting to reconnect
309
+
310
+ ## License
311
+
312
+ Apache-2.0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Attachment support for offline-first sync.
3
+ *
4
+ * Provides:
5
+ * - Separate storage for binary attachments
6
+ * - Chunked upload for large files
7
+ * - Upload progress tracking
8
+ * - On-demand vs eager download policies
9
+ * - LRU caching with eviction
10
+ */
11
+ import type { PersistenceProvider } from '../persistence/types.js';
12
+ /**
13
+ * Download policy for attachments.
14
+ */
15
+ export declare const DownloadPolicy: {
16
+ /** Download attachment data immediately when fetched */
17
+ readonly Eager: "eager";
18
+ /** Only download metadata; data loaded on explicit request */
19
+ readonly OnDemand: "on-demand";
20
+ };
21
+ export type DownloadPolicy = typeof DownloadPolicy[keyof typeof DownloadPolicy];
22
+ /**
23
+ * Metadata for an attachment.
24
+ */
25
+ export interface AttachmentMetadata {
26
+ /** Unique attachment ID */
27
+ id: string;
28
+ /** Original filename */
29
+ filename: string;
30
+ /** MIME type of the file */
31
+ mimeType: string;
32
+ /** Size in bytes */
33
+ size: number;
34
+ /** Checksum for integrity verification */
35
+ checksum: string;
36
+ /** Timestamp when the attachment was created */
37
+ createdAt: number;
38
+ /** ID of the document this attachment belongs to */
39
+ documentId: string;
40
+ /** Collection the document belongs to */
41
+ collection: string;
42
+ }
43
+ /**
44
+ * Upload progress information.
45
+ */
46
+ export interface UploadProgress {
47
+ /** Percentage complete (0-100) */
48
+ percent: number;
49
+ /** Bytes uploaded so far */
50
+ uploadedBytes: number;
51
+ /** Total bytes to upload */
52
+ totalBytes: number;
53
+ /** Number of chunks completed */
54
+ chunksCompleted: number;
55
+ /** Total number of chunks */
56
+ totalChunks: number;
57
+ }
58
+ /**
59
+ * Options for uploading an attachment.
60
+ */
61
+ export interface UploadOptions {
62
+ /** Original filename */
63
+ filename: string;
64
+ /** MIME type */
65
+ mimeType: string;
66
+ /** Binary data to upload */
67
+ data: Uint8Array;
68
+ /** Document ID to associate with */
69
+ documentId: string;
70
+ /** Collection name */
71
+ collection: string;
72
+ /** Progress callback */
73
+ onProgress?: (progress: UploadProgress) => void;
74
+ }
75
+ /**
76
+ * Result of an upload operation.
77
+ */
78
+ export interface UploadResult {
79
+ /** Attachment metadata */
80
+ metadata: AttachmentMetadata;
81
+ }
82
+ /**
83
+ * Configuration for AttachmentManager.
84
+ */
85
+ export interface AttachmentConfig {
86
+ /** Chunk size for large file uploads (default: 256KB) */
87
+ chunkSize?: number;
88
+ /** Maximum cache size in bytes (default: 50MB) */
89
+ maxCacheSize?: number;
90
+ /** Download policy (default: Eager) */
91
+ downloadPolicy?: DownloadPolicy;
92
+ }
93
+ /**
94
+ * Cache statistics.
95
+ */
96
+ export interface CacheStats {
97
+ /** Number of cache hits */
98
+ hits: number;
99
+ /** Number of cache misses */
100
+ misses: number;
101
+ /** Number of evictions */
102
+ evictions: number;
103
+ /** Current cache size in bytes */
104
+ currentSize: number;
105
+ /** IDs of cached attachments */
106
+ cachedIds: string[];
107
+ }
108
+ /**
109
+ * An attachment with lazy-loadable data.
110
+ */
111
+ export interface Attachment {
112
+ /** Attachment metadata */
113
+ metadata: AttachmentMetadata;
114
+ /** Binary data (may be undefined if not loaded) */
115
+ data: Uint8Array | undefined;
116
+ /** Check if data has been loaded */
117
+ isDataLoaded(): boolean;
118
+ /** Load the data (no-op if already loaded) */
119
+ loadData(): Promise<void>;
120
+ }
121
+ /**
122
+ * Stored attachment with metadata and data.
123
+ */
124
+ interface StoredAttachment {
125
+ metadata: AttachmentMetadata;
126
+ data: Uint8Array;
127
+ }
128
+ /**
129
+ * Low-level storage for attachments.
130
+ * Stores metadata and data separately for efficient access.
131
+ */
132
+ export declare class AttachmentStorage {
133
+ private readonly persistence;
134
+ constructor(persistence: PersistenceProvider);
135
+ /**
136
+ * Save an attachment.
137
+ */
138
+ save(metadata: AttachmentMetadata, data: Uint8Array): Promise<void>;
139
+ /**
140
+ * Load an attachment with its data.
141
+ */
142
+ load(attachmentId: string): Promise<StoredAttachment | undefined>;
143
+ /**
144
+ * Load just the data for an attachment.
145
+ */
146
+ loadData(attachmentId: string): Promise<Uint8Array | undefined>;
147
+ /**
148
+ * Get metadata for an attachment without loading data.
149
+ */
150
+ getMetadata(attachmentId: string): Promise<AttachmentMetadata | undefined>;
151
+ /**
152
+ * List all attachments for a document.
153
+ */
154
+ listByDocument(collection: string, documentId: string): Promise<AttachmentMetadata[]>;
155
+ /**
156
+ * Delete an attachment.
157
+ */
158
+ delete(attachmentId: string): Promise<void>;
159
+ /**
160
+ * Update the document-to-attachment index.
161
+ */
162
+ private updateIndex;
163
+ /**
164
+ * Remove an attachment from the document index.
165
+ */
166
+ private removeFromIndex;
167
+ }
168
+ /**
169
+ * Manages attachments with caching, chunked upload, and progress tracking.
170
+ */
171
+ export declare class AttachmentManager {
172
+ private readonly storage;
173
+ private readonly chunkSize;
174
+ private readonly maxCacheSize;
175
+ private readonly downloadPolicy;
176
+ private readonly cache;
177
+ private currentCacheSize;
178
+ private cacheHits;
179
+ private cacheMisses;
180
+ private cacheEvictions;
181
+ constructor(storage: AttachmentStorage, config?: AttachmentConfig);
182
+ /**
183
+ * Upload an attachment.
184
+ */
185
+ upload(options: UploadOptions): Promise<UploadResult>;
186
+ /**
187
+ * Get an attachment by ID.
188
+ */
189
+ get(attachmentId: string): Promise<Attachment | undefined>;
190
+ /**
191
+ * Split data into chunks.
192
+ */
193
+ splitIntoChunks(data: Uint8Array): Uint8Array[];
194
+ /**
195
+ * Reassemble chunks into original data.
196
+ */
197
+ reassembleChunks(chunks: Uint8Array[]): Uint8Array;
198
+ /**
199
+ * Calculate checksum for data.
200
+ * Uses a simple hash in browser environments.
201
+ */
202
+ calculateChecksum(data: Uint8Array): Promise<string>;
203
+ /**
204
+ * Get cache statistics.
205
+ */
206
+ getCacheStats(): CacheStats;
207
+ /**
208
+ * Clear the cache.
209
+ */
210
+ clearCache(): void;
211
+ /**
212
+ * Add an attachment to the cache, evicting old entries if needed.
213
+ */
214
+ private addToCache;
215
+ /**
216
+ * Evict the least recently used cache entry.
217
+ */
218
+ private evictLRU;
219
+ /**
220
+ * Create an Attachment object with lazy loading support.
221
+ */
222
+ private createAttachment;
223
+ }
224
+ export {};
225
+ //# sourceMappingURL=attachment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment.d.ts","sourceRoot":"","sources":["../../src/client/attachment.ts"],"names":[],"mappings":"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"}