@trestleinc/replicate 1.1.2 → 1.2.0-preview.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 (51) hide show
  1. package/README.md +40 -41
  2. package/package.json +3 -1
  3. package/src/client/collection.ts +334 -523
  4. package/src/client/errors.ts +1 -1
  5. package/src/client/index.ts +4 -7
  6. package/src/client/merge.ts +2 -2
  7. package/src/client/persistence/indexeddb.ts +10 -14
  8. package/src/client/prose.ts +147 -203
  9. package/src/client/services/awareness.ts +373 -0
  10. package/src/client/services/context.ts +114 -0
  11. package/src/client/services/seq.ts +78 -0
  12. package/src/client/services/session.ts +20 -0
  13. package/src/client/services/sync.ts +122 -0
  14. package/src/client/subdocs.ts +263 -0
  15. package/src/component/_generated/api.ts +2 -2
  16. package/src/component/_generated/component.ts +73 -28
  17. package/src/component/mutations.ts +734 -0
  18. package/src/component/schema.ts +31 -14
  19. package/src/server/collection.ts +98 -0
  20. package/src/server/index.ts +2 -2
  21. package/src/server/{storage.ts → replicate.ts} +214 -75
  22. package/dist/client/index.d.ts +0 -314
  23. package/dist/client/index.js +0 -4027
  24. package/dist/component/_generated/api.d.ts +0 -31
  25. package/dist/component/_generated/api.js +0 -25
  26. package/dist/component/_generated/component.d.ts +0 -91
  27. package/dist/component/_generated/component.js +0 -1
  28. package/dist/component/_generated/dataModel.d.ts +0 -42
  29. package/dist/component/_generated/dataModel.js +0 -1
  30. package/dist/component/_generated/server.d.ts +0 -117
  31. package/dist/component/_generated/server.js +0 -73
  32. package/dist/component/_virtual/rolldown_runtime.js +0 -18
  33. package/dist/component/convex.config.d.ts +0 -6
  34. package/dist/component/convex.config.js +0 -8
  35. package/dist/component/logger.d.ts +0 -12
  36. package/dist/component/logger.js +0 -27
  37. package/dist/component/public.d.ts +0 -83
  38. package/dist/component/public.js +0 -325
  39. package/dist/component/schema.d.ts +0 -54
  40. package/dist/component/schema.js +0 -29
  41. package/dist/component/shared/types.d.ts +0 -9
  42. package/dist/component/shared/types.js +0 -15
  43. package/dist/server/index.d.ts +0 -135
  44. package/dist/server/index.js +0 -368
  45. package/dist/shared/index.d.ts +0 -29
  46. package/dist/shared/index.js +0 -1
  47. package/src/client/prose-schema.ts +0 -55
  48. package/src/client/services/cursor.ts +0 -109
  49. package/src/component/public.ts +0 -453
  50. package/src/server/builder.ts +0 -98
  51. /package/src/client/{replicate.ts → ops.ts} +0 -0
package/README.md CHANGED
@@ -127,16 +127,14 @@ export default defineSchema({
127
127
 
128
128
  ### Step 3: Create Replication Functions
129
129
 
130
- Use `replicate()` to bind your component and create collection functions:
130
+ Use `collection.create()` to create server-side collection functions:
131
131
 
132
132
  ```typescript
133
133
  // convex/tasks.ts
134
- import { replicate } from '@trestleinc/replicate/server';
134
+ import { collection } from '@trestleinc/replicate/server';
135
135
  import { components } from './_generated/api';
136
136
  import type { Task } from '../src/useTasks';
137
137
 
138
- const r = replicate(components.replicate);
139
-
140
138
  export const {
141
139
  stream,
142
140
  material,
@@ -144,18 +142,15 @@ export const {
144
142
  insert,
145
143
  update,
146
144
  remove,
147
- mark, // Peer sync progress tracking
148
- compact, // Manual compaction trigger
149
- } = r<Task>({
150
- collection: 'tasks',
151
- compaction: {
152
- sizeThreshold: "5mb", // Type-safe size: "100kb", "5mb", "1gb"
153
- peerTimeout: "24h", // Type-safe duration: "30m", "24h", "7d"
154
- },
155
- });
145
+ mark,
146
+ compact,
147
+ sessions,
148
+ cursors,
149
+ leave,
150
+ } = collection.create<Task>(components.replicate, 'tasks');
156
151
  ```
157
152
 
158
- **What `replicate()` generates:**
153
+ **What `collection.create()` generates:**
159
154
 
160
155
  - `stream` - Real-time CRDT stream query (cursor-based subscriptions with `seq` numbers)
161
156
  - `material` - SSR-friendly query (for server-side rendering)
@@ -165,6 +160,9 @@ export const {
165
160
  - `remove` - Dual-storage delete mutation (auto-compacts when threshold exceeded)
166
161
  - `mark` - Report sync progress to server (peer tracking for safe compaction)
167
162
  - `compact` - Manual compaction trigger (peer-aware, respects active peer sync state)
163
+ - `sessions` - Get connected sessions (presence query)
164
+ - `cursors` - Get cursor positions for collaborative editing
165
+ - `leave` - Explicit disconnect mutation
168
166
 
169
167
  ### Step 4: Define Your Collection
170
168
 
@@ -296,7 +294,7 @@ For frameworks that support SSR (TanStack Start, Next.js, Remix, SvelteKit), pre
296
294
 
297
295
  **Step 1: Prefetch material on the server**
298
296
 
299
- Use `ConvexHttpClient` to fetch data during SSR. The `material` query is generated by `replicate()`:
297
+ Use `ConvexHttpClient` to fetch data during SSR. The `material` query is generated by `collection.create()`:
300
298
 
301
299
  ```typescript
302
300
  // TanStack Start: src/routes/__root.tsx
@@ -471,12 +469,10 @@ You can customize the behavior of generated functions using optional hooks:
471
469
 
472
470
  ```typescript
473
471
  // convex/tasks.ts
474
- import { replicate } from '@trestleinc/replicate/server';
472
+ import { collection } from '@trestleinc/replicate/server';
475
473
  import { components } from './_generated/api';
476
474
  import type { Task } from '../src/useTasks';
477
475
 
478
- const r = replicate(components.replicate);
479
-
480
476
  export const {
481
477
  stream,
482
478
  material,
@@ -486,9 +482,10 @@ export const {
486
482
  remove,
487
483
  mark,
488
484
  compact,
489
- } = r<Task>({
490
- collection: 'tasks',
491
-
485
+ sessions,
486
+ cursors,
487
+ leave,
488
+ } = collection.create<Task>(components.replicate, 'tasks', {
492
489
  // Optional hooks for authorization and lifecycle events
493
490
  hooks: {
494
491
  // Permission checks (eval* hooks validate BEFORE execution, throw to deny)
@@ -500,7 +497,7 @@ export const {
500
497
  const userId = await ctx.auth.getUserIdentity();
501
498
  if (!userId) throw new Error('Unauthorized');
502
499
  },
503
- evalRemove: async (ctx, documentId) => {
500
+ evalRemove: async (ctx, document) => {
504
501
  const userId = await ctx.auth.getUserIdentity();
505
502
  if (!userId) throw new Error('Unauthorized');
506
503
  },
@@ -509,7 +506,7 @@ export const {
509
506
  const userId = await ctx.auth.getUserIdentity();
510
507
  if (!userId) throw new Error('Unauthorized');
511
508
  },
512
- evalCompact: async (ctx, documentId) => {
509
+ evalCompact: async (ctx, document) => {
513
510
  // Restrict compaction to admin users
514
511
  const userId = await ctx.auth.getUserIdentity();
515
512
  if (!userId) throw new Error('Unauthorized');
@@ -519,7 +516,7 @@ export const {
519
516
  onStream: async (ctx, result) => { /* after stream query */ },
520
517
  onInsert: async (ctx, doc) => { /* after insert */ },
521
518
  onUpdate: async (ctx, doc) => { /* after update */ },
522
- onRemove: async (ctx, documentId) => { /* after remove */ },
519
+ onRemove: async (ctx, document) => { /* after remove */ },
523
520
 
524
521
  // Transform hook (modify documents before returning)
525
522
  transform: async (docs) => docs.filter(d => d.isPublic),
@@ -711,7 +708,7 @@ interface CollectionConfig<T> {
711
708
  schema: ZodObject; // Required: Zod schema for type inference
712
709
  getKey: (item: T) => string | number; // Extract unique key from item
713
710
  convexClient: ConvexClient; // Convex client instance
714
- api: { // API from replicate()
711
+ api: { // API from collection.create()
715
712
  stream: FunctionReference; // Real-time subscription
716
713
  insert: FunctionReference; // Insert mutation
717
714
  update: FunctionReference; // Update mutation
@@ -816,34 +813,33 @@ errors.NonRetriable // Errors that should not be retried (auth, validation)
816
813
 
817
814
  ### Server-Side (`@trestleinc/replicate/server`)
818
815
 
819
- #### `replicate(component)`
816
+ #### `collection.create<T>(component, name, options?)`
820
817
 
821
- Factory function that creates a bound replicate function for your collections.
818
+ Creates server-side collection functions that mirror the client-side collection.
822
819
 
823
820
  **Parameters:**
824
821
  - `component` - Your Convex component reference (`components.replicate`)
825
-
826
- **Returns:** A function `<T>(config: ReplicateConfig<T>)` that generates collection operations.
822
+ - `name` - Collection name (e.g., `'tasks'`)
823
+ - `options` - Optional configuration for compaction and hooks
827
824
 
828
825
  **Example:**
829
826
  ```typescript
830
- import { replicate } from '@trestleinc/replicate/server';
827
+ import { collection } from '@trestleinc/replicate/server';
831
828
  import { components } from './_generated/api';
832
829
 
833
- const r = replicate(components.replicate);
834
- export const tasks = r<Task>({ collection: 'tasks' });
830
+ export const {
831
+ stream, material, insert, update, remove, recovery, mark, compact, sessions, cursors, leave,
832
+ } = collection.create<Task>(components.replicate, 'tasks');
835
833
  ```
836
834
 
837
- #### `ReplicateConfig<T>`
835
+ #### `CollectionOptions<T>`
838
836
 
839
- Configuration for the bound replicate function.
837
+ Optional configuration for `collection.create()`.
840
838
 
841
839
  **Config:**
842
840
  ```typescript
843
- interface ReplicateConfig<T> {
844
- collection: string; // Collection name (e.g., 'tasks')
845
-
846
- // Optional: Compaction settings with type-safe values
841
+ interface CollectionOptions<T> {
842
+ // Optional: Compaction settings
847
843
  compaction?: {
848
844
  sizeThreshold?: Size; // Size threshold: "100kb", "5mb", "1gb" (default: "5mb")
849
845
  peerTimeout?: Duration; // Peer timeout: "30m", "24h", "7d" (default: "24h")
@@ -854,15 +850,15 @@ interface ReplicateConfig<T> {
854
850
  // Permission checks (throw to reject)
855
851
  evalRead?: (ctx, collection) => Promise<void>;
856
852
  evalWrite?: (ctx, doc) => Promise<void>;
857
- evalRemove?: (ctx, documentId) => Promise<void>;
853
+ evalRemove?: (ctx, document) => Promise<void>;
858
854
  evalMark?: (ctx, peerId) => Promise<void>;
859
- evalCompact?: (ctx, documentId) => Promise<void>;
855
+ evalCompact?: (ctx, document) => Promise<void>;
860
856
 
861
857
  // Lifecycle callbacks (run after operation)
862
858
  onStream?: (ctx, result) => Promise<void>;
863
859
  onInsert?: (ctx, doc) => Promise<void>;
864
860
  onUpdate?: (ctx, doc) => Promise<void>;
865
- onRemove?: (ctx, documentId) => Promise<void>;
861
+ onRemove?: (ctx, document) => Promise<void>;
866
862
 
867
863
  // Transform hook (modify documents before returning)
868
864
  transform?: (docs) => Promise<T[]>;
@@ -883,6 +879,9 @@ interface ReplicateConfig<T> {
883
879
  - `remove` - Dual-storage delete mutation (auto-compacts when threshold exceeded)
884
880
  - `mark` - Peer sync tracking mutation (reports `syncedSeq` to server)
885
881
  - `compact` - Manual compaction mutation (peer-aware, safe for active clients)
882
+ - `sessions` - Get connected sessions (presence query)
883
+ - `cursors` - Get cursor positions for collaborative editing
884
+ - `leave` - Explicit disconnect mutation
886
885
 
887
886
  #### `schema.table(userFields, applyIndexes?)`
888
887
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trestleinc/replicate",
3
- "version": "1.1.2",
3
+ "version": "1.2.0-preview.0",
4
4
  "description": "Offline-first data replication with Yjs CRDTs and Convex",
5
5
  "repository": "github:trestleinc/replicate",
6
6
  "homepage": "https://github.com/trestleinc/replicate#readme",
@@ -82,6 +82,7 @@
82
82
  "@tanstack/db": "^0.5.15",
83
83
  "convex": "^1.31.0",
84
84
  "lib0": "^0.2.0",
85
+ "y-protocols": "^1.0.7",
85
86
  "yjs": "^13.6.0"
86
87
  },
87
88
  "peerDependenciesMeta": {
@@ -111,6 +112,7 @@
111
112
  "tsdown": "0.18.2",
112
113
  "typescript": "5.9.3",
113
114
  "typescript-eslint": "8.50.1",
115
+ "y-protocols": "1.0.7",
114
116
  "yjs": "13.6.28",
115
117
  "zod": "4.2.1"
116
118
  },