@revealui/sync 0.2.1 → 0.3.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 (52) hide show
  1. package/README.md +82 -112
  2. package/dist/collab/agent-client-factory.d.ts.map +1 -1
  3. package/dist/collab/agent-client.d.ts +1 -0
  4. package/dist/collab/agent-client.d.ts.map +1 -1
  5. package/dist/collab/agent-client.js +16 -7
  6. package/dist/collab/index.d.ts +1 -0
  7. package/dist/collab/index.d.ts.map +1 -1
  8. package/dist/collab/index.js +1 -0
  9. package/dist/collab/protocol-constants.d.ts +5 -0
  10. package/dist/collab/protocol-constants.d.ts.map +1 -0
  11. package/dist/collab/protocol-constants.js +4 -0
  12. package/dist/collab/server-index.d.ts +1 -0
  13. package/dist/collab/server-index.d.ts.map +1 -1
  14. package/dist/collab/server-index.js +1 -0
  15. package/dist/collab/use-collab-document.d.ts +1 -1
  16. package/dist/collab/use-collab-document.d.ts.map +1 -1
  17. package/dist/collab/use-collab-document.js +7 -4
  18. package/dist/collab/use-collaboration.d.ts.map +1 -1
  19. package/dist/collab/yjs-websocket-provider.d.ts.map +1 -1
  20. package/dist/collab/yjs-websocket-provider.js +1 -2
  21. package/dist/fetch-with-timeout.d.ts +7 -0
  22. package/dist/fetch-with-timeout.d.ts.map +1 -0
  23. package/dist/fetch-with-timeout.js +27 -0
  24. package/dist/hooks/useAgentContexts.d.ts +29 -0
  25. package/dist/hooks/useAgentContexts.d.ts.map +1 -0
  26. package/dist/hooks/useAgentContexts.js +22 -0
  27. package/dist/hooks/useAgentMemory.d.ts +34 -0
  28. package/dist/hooks/useAgentMemory.d.ts.map +1 -0
  29. package/dist/hooks/useAgentMemory.js +37 -0
  30. package/dist/hooks/useConversations.d.ts +26 -6
  31. package/dist/hooks/useConversations.d.ts.map +1 -1
  32. package/dist/hooks/useConversations.js +13 -3
  33. package/dist/hooks/useCoordinationSessions.d.ts +35 -0
  34. package/dist/hooks/useCoordinationSessions.d.ts.map +1 -0
  35. package/dist/hooks/useCoordinationSessions.js +22 -0
  36. package/dist/hooks/useCoordinationWorkItems.d.ts +41 -0
  37. package/dist/hooks/useCoordinationWorkItems.d.ts.map +1 -0
  38. package/dist/hooks/useCoordinationWorkItems.js +22 -0
  39. package/dist/index.d.ts +20 -4
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +14 -4
  42. package/dist/mutations.d.ts +14 -0
  43. package/dist/mutations.d.ts.map +1 -0
  44. package/dist/mutations.js +53 -0
  45. package/dist/provider/index.d.ts +29 -5
  46. package/dist/provider/index.d.ts.map +1 -1
  47. package/dist/provider/index.js +25 -7
  48. package/dist/shape-utils.d.ts +11 -0
  49. package/dist/shape-utils.d.ts.map +1 -0
  50. package/dist/shape-utils.js +14 -0
  51. package/dist/test-setup.d.ts.map +1 -1
  52. package/package.json +7 -5
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @revealui/sync
2
2
 
3
- ElectricSQL sync utilities for RevealUI - real-time data synchronization with local-first architecture.
3
+ ElectricSQL sync utilities for RevealUI real-time data synchronization with local-first architecture.
4
4
 
5
5
  ## Features
6
6
 
7
- - **ElectricSQL Integration**: Real-time sync with ElectricSQL
8
- - **React Hooks**: Use sync data in React components
7
+ - **ElectricSQL Integration**: Real-time sync via shape subscriptions
8
+ - **React Hooks**: Subscribe to synced data in React components
9
+ - **Mutations**: Create, update, and delete records via authenticated REST endpoints
9
10
  - **Type-safe**: Full TypeScript support with database types
10
- - **Local-first**: Works offline, syncs when online
11
11
  - **React Provider**: Easy setup with `ElectricProvider`
12
- - **Optimistic Updates**: Instant UI updates with server reconciliation
12
+ - **Yjs Collaboration**: CRDT-based real-time collaborative editing
13
13
 
14
14
  ## Installation
15
15
 
@@ -28,27 +28,29 @@ import { ElectricProvider } from '@revealui/sync/provider'
28
28
 
29
29
  export default function App() {
30
30
  return (
31
- <ElectricProvider>
31
+ <ElectricProvider proxyBaseUrl="https://cms.revealui.com">
32
32
  <YourComponents />
33
33
  </ElectricProvider>
34
34
  )
35
35
  }
36
36
  ```
37
37
 
38
- ### Use Synced Data
38
+ ### Read Synced Data
39
+
40
+ Hooks subscribe to ElectricSQL shapes via authenticated proxy routes. Data updates in real-time as the database changes.
39
41
 
40
42
  ```typescript
41
43
  import { useAgentContexts } from '@revealui/sync'
42
44
 
43
45
  function MyComponent() {
44
- const { data: contexts, isLoading } = useAgentContexts()
46
+ const { contexts, isLoading } = useAgentContexts()
45
47
 
46
48
  if (isLoading) return <div>Loading...</div>
47
49
 
48
50
  return (
49
51
  <ul>
50
52
  {contexts.map(context => (
51
- <li key={context.id}>{context.name}</li>
53
+ <li key={context.id}>{JSON.stringify(context.context)}</li>
52
54
  ))}
53
55
  </ul>
54
56
  )
@@ -57,18 +59,29 @@ function MyComponent() {
57
59
 
58
60
  ### Mutations
59
61
 
62
+ Each hook returns `create`, `update`, and `remove` functions. Mutations go through authenticated REST endpoints at `/api/sync/*`. ElectricSQL picks up the database changes and pushes updates to all subscribers automatically.
63
+
60
64
  ```typescript
61
65
  import { useAgentContexts } from '@revealui/sync'
62
66
 
63
67
  function CreateContext() {
64
- const { create } = useAgentContexts()
68
+ const { contexts, create, update, remove } = useAgentContexts()
65
69
 
66
70
  const handleCreate = async () => {
67
- await create({
68
- name: 'New Context',
69
- agent_id: 'agent-123',
70
- // ... other fields
71
+ const result = await create({
72
+ agent_id: 'assistant',
73
+ context: { theme: 'dark', language: 'en' },
74
+ priority: 0.8,
71
75
  })
76
+ if (!result.success) console.error(result.error)
77
+ }
78
+
79
+ const handleUpdate = async (id: string) => {
80
+ await update(id, { context: { theme: 'light' } })
81
+ }
82
+
83
+ const handleDelete = async (id: string) => {
84
+ await remove(id)
72
85
  }
73
86
 
74
87
  return <button onClick={handleCreate}>Create</button>
@@ -79,139 +92,96 @@ function CreateContext() {
79
92
 
80
93
  ### `useAgentContexts()`
81
94
 
82
- Sync agent contexts (task context, working memory, etc.)
95
+ Subscribe to agent contexts (task context, working memory).
83
96
 
84
97
  ```typescript
85
98
  const {
86
- data, // Agent contexts array
87
- isLoading, // Loading state
88
- error, // Error state
89
- create, // Create function
90
- update, // Update function
91
- remove // Delete function
99
+ contexts, // AgentContextRecord[]
100
+ isLoading, // boolean
101
+ error, // Error | null
102
+ create, // (data: CreateAgentContextInput) => Promise<MutationResult>
103
+ update, // (id: string, data: UpdateAgentContextInput) => Promise<MutationResult>
104
+ remove, // (id: string) => Promise<MutationResult>
92
105
  } = useAgentContexts()
93
106
  ```
94
107
 
95
- ### `useAgentMemory()`
108
+ ### `useAgentMemory(agentId)`
96
109
 
97
- Sync agent memory (episodic, semantic, working)
110
+ Subscribe to agent memory (episodic, semantic, working) filtered by agent ID.
98
111
 
99
112
  ```typescript
100
113
  const {
101
- data, // Memory entries array
102
- isLoading,
103
- error,
104
- create,
105
- update,
106
- remove
107
- } = useAgentMemory()
114
+ memories, // AgentMemoryRecord[]
115
+ isLoading, // boolean
116
+ error, // Error | null
117
+ create, // (data: CreateAgentMemoryInput) => Promise<MutationResult>
118
+ update, // (id: string, data: UpdateAgentMemoryInput) => Promise<MutationResult>
119
+ remove, // (id: string) => Promise<MutationResult>
120
+ } = useAgentMemory('assistant')
108
121
  ```
109
122
 
110
- ### `useConversations()`
123
+ ### `useConversations(userId)`
111
124
 
112
- Sync conversation history
125
+ Subscribe to conversation history. Server-side proxy enforces row-level filtering by session — the `userId` parameter is for API compatibility but filtering is handled server-side.
113
126
 
114
127
  ```typescript
115
128
  const {
116
- data, // Conversations array
117
- isLoading,
118
- error,
119
- create,
120
- update,
121
- remove
122
- } = useConversations()
129
+ conversations, // ConversationRecord[]
130
+ isLoading, // boolean
131
+ error, // Error | null
132
+ create, // (data: CreateConversationInput) => Promise<MutationResult>
133
+ update, // (id: string, data: UpdateConversationInput) => Promise<MutationResult>
134
+ remove, // (id: string) => Promise<MutationResult>
135
+ } = useConversations(userId)
123
136
  ```
124
137
 
125
138
  ## How It Works
126
139
 
127
- 1. **ElectricSQL Service**: Runs as a sync service between Postgres and clients
128
- 2. **Shape Subscriptions**: Subscribe to "shapes" of data (queries)
129
- 3. **Local Cache**: Data cached locally in browser
130
- 4. **Real-time Updates**: Changes propagate instantly to all connected clients
131
- 5. **Conflict Resolution**: CRDT-based conflict resolution for offline edits
140
+ 1. **Reads**: ElectricSQL shape subscriptions via authenticated CMS proxy (`/api/shapes/*`)
141
+ 2. **Writes**: REST mutations via CMS API (`/api/sync/*`) → Postgres → ElectricSQL replication
142
+ 3. **Real-time**: Database changes propagate to all shape subscribers automatically
143
+ 4. **Auth**: All endpoints require a valid session cookie
132
144
 
133
- ## Environment Variables
145
+ ## Collaboration (Yjs)
134
146
 
135
- ```env
136
- # ElectricSQL service URL
137
- NEXT_PUBLIC_ELECTRIC_SERVICE_URL=http://localhost:5133
147
+ The collab layer provides CRDT-based collaborative editing:
138
148
 
139
- # Optional: Server-side Electric URL (if different)
140
- ELECTRIC_SERVICE_URL=http://localhost:5133
149
+ ```typescript
150
+ import { useCollaboration } from '@revealui/sync'
151
+
152
+ function Editor() {
153
+ const { doc, synced, connectedUsers } = useCollaboration({
154
+ documentId: 'doc-uuid',
155
+ serverUrl: 'ws://localhost:4000',
156
+ })
157
+ // ...
158
+ }
141
159
  ```
142
160
 
143
- ## Development
161
+ Server-side agents can use `AgentCollabClient` from `@revealui/sync/collab/server`.
144
162
 
145
- ```bash
146
- # Build package
147
- pnpm --filter @revealui/sync build
163
+ ## Environment Variables
148
164
 
149
- # Watch mode
150
- pnpm --filter @revealui/sync dev
165
+ ```env
166
+ # ElectricSQL service URL (used by CMS proxy)
167
+ ELECTRIC_SERVICE_URL=http://localhost:5133
151
168
 
152
- # Run tests
153
- pnpm --filter @revealui/sync test
169
+ # Optional: Electric auth secret
170
+ ELECTRIC_SECRET=your-secret
154
171
 
155
- # Type check
156
- pnpm --filter @revealui/sync typecheck
172
+ # Client-side (stored in provider context)
173
+ NEXT_PUBLIC_ELECTRIC_SERVICE_URL=http://localhost:5133
157
174
  ```
158
175
 
159
- ## Testing
176
+ ## Development
160
177
 
161
178
  ```bash
162
- # Run all tests
163
- pnpm --filter @revealui/sync test
164
-
165
- # Watch mode
166
- pnpm --filter @revealui/sync test:watch
167
-
168
- # Coverage
169
- pnpm --filter @revealui/sync test:coverage
179
+ pnpm --filter @revealui/sync build # Build
180
+ pnpm --filter @revealui/sync dev # Watch mode
181
+ pnpm --filter @revealui/sync test # Run tests
182
+ pnpm --filter @revealui/sync typecheck # Type check
170
183
  ```
171
184
 
172
- ## Architecture
173
-
174
- ```
175
- ┌─────────────────┐
176
- │ React Component │
177
- │ (useAgentContexts) │
178
- └────────┬────────┘
179
-
180
-
181
- ┌─────────────────┐
182
- │ ElectricSQL │
183
- │ Shape Hook │
184
- └────────┬────────┘
185
-
186
-
187
- ┌─────────────────┐
188
- │ Electric Sync │
189
- │ Service │
190
- └────────┬────────┘
191
-
192
-
193
- ┌─────────────────┐
194
- │ PostgreSQL │
195
- │ Database │
196
- └─────────────────┘
197
- ```
198
-
199
- ## Limitations
200
-
201
- ⚠️ **CRITICAL**: ElectricSQL API endpoints need independent verification before production use.
202
-
203
- **Status:** ⚠️ NEEDS VERIFICATION
204
- - ElectricSQL integration exists but requires testing
205
- - API endpoints based on assumptions
206
- - No integration tests performed yet
207
- - Verify against your deployment before relying on sync in production
208
-
209
- ## Related Documentation
210
-
211
- - [ElectricSQL Documentation](https://electric-sql.com/docs) - Official ElectricSQL docs
212
- - [Architecture](../../docs/ARCHITECTURE.md) - System architecture overview
213
- - [Database Guide](../../docs/DATABASE.md) - Database setup
214
-
215
185
  ## License
216
186
 
217
187
  MIT
@@ -1 +1 @@
1
- {"version":3,"file":"agent-client-factory.d.ts","sourceRoot":"","sources":["../../src/collab/agent-client-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAsB,MAAM,mBAAmB,CAAA;AAIzE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAgBtF;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,wBAAwB,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAAC,iBAAiB,CAAC,CAK5B"}
1
+ {"version":3,"file":"agent-client-factory.d.ts","sourceRoot":"","sources":["../../src/collab/agent-client-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAsB,MAAM,mBAAmB,CAAC;AAI1E,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAgBtF;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,wBAAwB,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAAC,iBAAiB,CAAC,CAK5B"}
@@ -31,6 +31,7 @@ export declare class AgentCollabClient extends Observable<string> {
31
31
  private updateHandler;
32
32
  private awarenessUpdateHandler;
33
33
  private destroyed;
34
+ private pendingSyncAbort;
34
35
  constructor(options: AgentCollabClientOptions);
35
36
  connect(): void;
36
37
  disconnect(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-client.d.ts","sourceRoot":"","sources":["../../src/collab/agent-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAA;AAE1D,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAQxB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,aAAa,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,qBAAa,iBAAkB,SAAQ,UAAU,CAAC,MAAM,CAAC;IACvD,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAA;IACnB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC,SAAS,CAAA;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAExC,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,aAAa,CAA+D;IACpF,OAAO,CAAC,sBAAsB,CAGrB;IACT,OAAO,CAAC,SAAS,CAAQ;gBAEb,OAAO,EAAE,wBAAwB;IA0B7C,OAAO,IAAI,IAAI;IAcf,UAAU,IAAI,IAAI;IAkBlB,WAAW,IAAI,CAAC,CAAC,GAAG;IAIpB,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI;IAI9B,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAMrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAI9D,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAQhD,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAU5D,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAUzD,WAAW,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5C,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;IAoClB,OAAO,CAAC,aAAa;IA+BrB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,eAAe;CAOxB"}
1
+ {"version":3,"file":"agent-client.d.ts","sourceRoot":"","sources":["../../src/collab/agent-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAOzB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,iBAAkB,SAAQ,UAAU,CAAC,MAAM,CAAC;IACvD,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC,SAAS,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,sBAAsB,CAGpB;IACV,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,OAAO,EAAE,wBAAwB;IA0B7C,OAAO,IAAI,IAAI;IAcf,UAAU,IAAI,IAAI;IAkBlB,WAAW,IAAI,CAAC,CAAC,GAAG;IAIpB,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI;IAI9B,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAI9D,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAQhD,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAU5D,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAUzD,WAAW,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B5C,OAAO,IAAI,IAAI;IAUf,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;IAoClB,OAAO,CAAC,aAAa;IAoCrB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,eAAe;CAOxB"}
@@ -5,8 +5,7 @@ import WebSocket from 'ws';
5
5
  import * as awarenessProtocol from 'y-protocols/awareness';
6
6
  import * as syncProtocol from 'y-protocols/sync';
7
7
  import * as Y from 'yjs';
8
- const MESSAGE_SYNC = 0;
9
- const MESSAGE_AWARENESS = 1;
8
+ import { MESSAGE_AWARENESS, MESSAGE_SYNC } from './protocol-constants.js';
10
9
  const MAX_RECONNECT_ATTEMPTS = 5;
11
10
  const BASE_RECONNECT_DELAY = 1000;
12
11
  const RECONNECT_MULTIPLIER = 2;
@@ -26,6 +25,7 @@ export class AgentCollabClient extends Observable {
26
25
  updateHandler = null;
27
26
  awarenessUpdateHandler;
28
27
  destroyed = false;
28
+ pendingSyncAbort = null;
29
29
  constructor(options) {
30
30
  super();
31
31
  this.serverUrl = options.serverUrl;
@@ -83,8 +83,6 @@ export class AgentCollabClient extends Observable {
83
83
  return this.doc.getText(name ?? this.defaultTextName);
84
84
  }
85
85
  getTextContent(name) {
86
- // Y.Text.toString() returns the text content — ESLint doesn't recognize this
87
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
88
86
  return this.getText(name).toString();
89
87
  }
90
88
  insertText(index, content, name) {
@@ -122,22 +120,33 @@ export class AgentCollabClient extends Observable {
122
120
  if (this.synced)
123
121
  return Promise.resolve();
124
122
  return new Promise((resolve, reject) => {
125
- const timeout = setTimeout(() => {
123
+ const cleanup = () => {
124
+ clearTimeout(timeout);
126
125
  this.off('sync', handler);
126
+ if (this.pendingSyncAbort === abort)
127
+ this.pendingSyncAbort = null;
128
+ };
129
+ const timeout = setTimeout(() => {
130
+ cleanup();
127
131
  reject(new Error(`Sync timeout after ${timeoutMs}ms`));
128
132
  }, timeoutMs);
129
133
  const handler = (isSynced) => {
130
134
  if (isSynced) {
131
- clearTimeout(timeout);
132
- this.off('sync', handler);
135
+ cleanup();
133
136
  resolve();
134
137
  }
135
138
  };
139
+ const abort = () => {
140
+ cleanup();
141
+ reject(new Error('Client destroyed while waiting for sync'));
142
+ };
143
+ this.pendingSyncAbort = abort;
136
144
  this.on('sync', handler);
137
145
  });
138
146
  }
139
147
  destroy() {
140
148
  this.destroyed = true;
149
+ this.pendingSyncAbort?.();
141
150
  this.awareness.off('update', this.awarenessUpdateHandler);
142
151
  this.disconnect();
143
152
  this.awareness.destroy();
@@ -1,3 +1,4 @@
1
+ export { MESSAGE_AWARENESS, MESSAGE_SYNC } from './protocol-constants.js';
1
2
  export type { CollabDocumentState } from './use-collab-document.js';
2
3
  export { useCollabDocument } from './use-collab-document.js';
3
4
  export type { CollaborationIdentity, UseCollaborationOptions, UseCollaborationResult, } from './use-collaboration.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collab/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collab/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC1E,YAAY,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC"}
@@ -1,3 +1,4 @@
1
+ export { MESSAGE_AWARENESS, MESSAGE_SYNC } from './protocol-constants.js';
1
2
  export { useCollabDocument } from './use-collab-document.js';
2
3
  export { useCollaboration } from './use-collaboration.js';
3
4
  export { CollabProvider } from './yjs-websocket-provider.js';
@@ -0,0 +1,5 @@
1
+ /** Yjs sync protocol message type */
2
+ export declare const MESSAGE_SYNC = 0;
3
+ /** Yjs awareness protocol message type */
4
+ export declare const MESSAGE_AWARENESS = 1;
5
+ //# sourceMappingURL=protocol-constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-constants.d.ts","sourceRoot":"","sources":["../../src/collab/protocol-constants.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,0CAA0C;AAC1C,eAAO,MAAM,iBAAiB,IAAI,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** Yjs sync protocol message type */
2
+ export const MESSAGE_SYNC = 0;
3
+ /** Yjs awareness protocol message type */
4
+ export const MESSAGE_AWARENESS = 1;
@@ -2,4 +2,5 @@ export type { AgentCollabClientOptions, AgentIdentity } from './agent-client.js'
2
2
  export { AgentCollabClient } from './agent-client.js';
3
3
  export type { CreateAgentClientOptions } from './agent-client-factory.js';
4
4
  export { createAgentClient, createAndConnectAgentClient } from './agent-client-factory.js';
5
+ export { MESSAGE_AWARENESS, MESSAGE_SYNC } from './protocol-constants.js';
5
6
  //# sourceMappingURL=server-index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-index.d.ts","sourceRoot":"","sources":["../../src/collab/server-index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAA"}
1
+ {"version":3,"file":"server-index.d.ts","sourceRoot":"","sources":["../../src/collab/server-index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { AgentCollabClient } from './agent-client.js';
2
2
  export { createAgentClient, createAndConnectAgentClient } from './agent-client-factory.js';
3
+ export { MESSAGE_AWARENESS, MESSAGE_SYNC } from './protocol-constants.js';
@@ -4,5 +4,5 @@ export interface CollabDocumentState {
4
4
  isLoading: boolean;
5
5
  error: Error | null;
6
6
  }
7
- export declare function useCollabDocument(documentId: string, electricUrl: string): CollabDocumentState;
7
+ export declare function useCollabDocument(documentId: string): CollabDocumentState;
8
8
  //# sourceMappingURL=use-collab-document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-collab-document.d.ts","sourceRoot":"","sources":["../../src/collab/use-collab-document.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,UAAU,GAAG,IAAI,CAAA;IAC/B,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAKD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,mBAAmB,CA+C9F"}
1
+ {"version":3,"file":"use-collab-document.d.ts","sourceRoot":"","sources":["../../src/collab/use-collab-document.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAKD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,CAiDzE"}
@@ -1,18 +1,21 @@
1
1
  import { useShape } from '@electric-sql/react';
2
+ import { fetchWithTimeout } from '../fetch-with-timeout.js';
3
+ import { useElectricConfig } from '../provider/index.js';
2
4
  // UUID v4 pattern — only format accepted as a yjs_documents PK
3
5
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
- export function useCollabDocument(documentId, electricUrl) {
6
+ export function useCollabDocument(documentId) {
7
+ const { proxyBaseUrl } = useElectricConfig();
5
8
  // Validate before interpolating into the WHERE clause. yjs_documents PKs are
6
9
  // always UUIDs — reject anything else so no untrusted string enters the query.
7
10
  const isValidId = UUID_RE.test(documentId);
8
11
  // Hook must always be called (Rules of Hooks). Pass an impossible WHERE when
9
12
  // the ID is invalid so the shape returns no rows but the hook still runs.
10
13
  const { data, isLoading, error } = useShape({
11
- url: `${electricUrl}/v1/shape`,
14
+ url: `${proxyBaseUrl}/api/shapes/yjs-documents`,
12
15
  params: {
13
- table: 'yjs_documents',
14
- where: isValidId ? `id = '${documentId}'` : `id = 'invalid'`,
16
+ document_id: isValidId ? documentId : '__invalid__',
15
17
  },
18
+ fetchClient: fetchWithTimeout,
16
19
  });
17
20
  if (!isValidId) {
18
21
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"use-collaboration.d.ts","sourceRoot":"","sources":["../../src/collab/use-collaboration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,YAAY,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;IAChC,QAAQ,CAAC,EAAE,qBAAqB,CAAA;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,IAAI,CAAA;IACjB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAC1C;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,SAAS,EACT,OAAc,EACd,YAAmB,EACnB,QAAQ,GACT,EAAE,uBAAuB,GAAG,sBAAsB,CAqElD"}
1
+ {"version":3,"file":"use-collaboration.d.ts","sourceRoot":"","sources":["../../src/collab/use-collaboration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC;IAClB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC3C;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,SAAS,EACT,OAAc,EACd,YAAmB,EACnB,QAAQ,GACT,EAAE,uBAAuB,GAAG,sBAAsB,CAqElD"}
@@ -1 +1 @@
1
- {"version":3,"file":"yjs-websocket-provider.d.ts","sourceRoot":"","sources":["../../src/collab/yjs-websocket-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAA;AAE1D,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAQxB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C;AAED,qBAAa,cAAe,SAAQ,UAAU,CAAC,MAAM,CAAC;IACpD,GAAG,EAAE,CAAC,CAAC,GAAG,CAAA;IACV,SAAS,EAAE,iBAAiB,CAAC,SAAS,CAAA;IACtC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,aAAa,CAA+D;IACpF,OAAO,CAAC,sBAAsB,CAGrB;gBAGP,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,CAAC,CAAC,GAAG,EACV,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE;IA2BhD,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAI9C,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC;IAU9C,OAAO,IAAI,IAAI;IAuFf,UAAU,IAAI,IAAI;IAmBlB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,eAAe;IAQvB,OAAO,IAAI,IAAI;CAMhB"}
1
+ {"version":3,"file":"yjs-websocket-provider.d.ts","sourceRoot":"","sources":["../../src/collab/yjs-websocket-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAOzB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAED,qBAAa,cAAe,SAAQ,UAAU,CAAC,MAAM,CAAC;IACpD,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACX,SAAS,EAAE,iBAAiB,CAAC,SAAS,CAAC;IACvC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,sBAAsB,CAGpB;gBAGR,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,CAAC,CAAC,GAAG,EACV,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE;IA2BhD,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAI9C,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC;IAU9C,OAAO,IAAI,IAAI;IAuFf,UAAU,IAAI,IAAI;IAmBlB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,eAAe;IAQvB,OAAO,IAAI,IAAI;CAMhB"}
@@ -4,8 +4,7 @@ import { Observable } from 'lib0/observable';
4
4
  import * as awarenessProtocol from 'y-protocols/awareness';
5
5
  import * as syncProtocol from 'y-protocols/sync';
6
6
  import * as Y from 'yjs';
7
- const MESSAGE_SYNC = 0;
8
- const MESSAGE_AWARENESS = 1;
7
+ import { MESSAGE_AWARENESS, MESSAGE_SYNC } from './protocol-constants.js';
9
8
  const MAX_RECONNECT_ATTEMPTS = 10;
10
9
  const BASE_RECONNECT_DELAY = 1000;
11
10
  const RECONNECT_MULTIPLIER = 1.5;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A fetch wrapper that aborts requests after {@link SHAPE_FETCH_TIMEOUT_MS} (10 s).
3
+ * Passed as `fetchClient` to ElectricSQL `useShape` so that shape subscription
4
+ * requests to the CMS proxy do not hang indefinitely.
5
+ */
6
+ export declare function fetchWithTimeout(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
7
+ //# sourceMappingURL=fetch-with-timeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-with-timeout.d.ts","sourceRoot":"","sources":["../src/fetch-with-timeout.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoBhG"}
@@ -0,0 +1,27 @@
1
+ /** Default timeout for Electric shape proxy fetch requests (milliseconds). */
2
+ const SHAPE_FETCH_TIMEOUT_MS = 10_000;
3
+ /**
4
+ * A fetch wrapper that aborts requests after {@link SHAPE_FETCH_TIMEOUT_MS} (10 s).
5
+ * Passed as `fetchClient` to ElectricSQL `useShape` so that shape subscription
6
+ * requests to the CMS proxy do not hang indefinitely.
7
+ */
8
+ export function fetchWithTimeout(input, init) {
9
+ // If the caller already provides a signal, respect it and compose with our timeout.
10
+ const externalSignal = init?.signal;
11
+ const controller = new AbortController();
12
+ const timeout = setTimeout(() => controller.abort(), SHAPE_FETCH_TIMEOUT_MS);
13
+ // If the external signal aborts, forward to our controller.
14
+ if (externalSignal) {
15
+ if (externalSignal.aborted) {
16
+ controller.abort(externalSignal.reason);
17
+ }
18
+ else {
19
+ externalSignal.addEventListener('abort', () => controller.abort(externalSignal.reason), {
20
+ once: true,
21
+ });
22
+ }
23
+ }
24
+ return fetch(input, { ...init, signal: controller.signal }).finally(() => {
25
+ clearTimeout(timeout);
26
+ });
27
+ }
@@ -0,0 +1,29 @@
1
+ import type { MutationResult } from '../mutations.js';
2
+ export interface AgentContextRecord {
3
+ id: string;
4
+ session_id: string;
5
+ agent_id: string;
6
+ context: Record<string, unknown>;
7
+ priority: number;
8
+ created_at: string;
9
+ updated_at: string;
10
+ }
11
+ export interface CreateAgentContextInput {
12
+ agent_id: string;
13
+ context?: Record<string, unknown>;
14
+ priority?: number;
15
+ }
16
+ export interface UpdateAgentContextInput {
17
+ context?: Record<string, unknown>;
18
+ priority?: number;
19
+ }
20
+ export interface UseAgentContextsResult {
21
+ contexts: AgentContextRecord[];
22
+ isLoading: boolean;
23
+ error: Error | null;
24
+ create: (data: CreateAgentContextInput) => Promise<MutationResult<AgentContextRecord>>;
25
+ update: (id: string, data: UpdateAgentContextInput) => Promise<MutationResult<AgentContextRecord>>;
26
+ remove: (id: string) => Promise<MutationResult<void>>;
27
+ }
28
+ export declare function useAgentContexts(): UseAgentContextsResult;
29
+ //# sourceMappingURL=useAgentContexts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAgentContexts.d.ts","sourceRoot":"","sources":["../../src/hooks/useAgentContexts.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACvF,MAAM,EAAE,CACN,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,uBAAuB,KAC1B,OAAO,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;CACvD;AAED,wBAAgB,gBAAgB,IAAI,sBAAsB,CAqBzD"}
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { useShape } from '@electric-sql/react';
3
+ import { fetchWithTimeout } from '../fetch-with-timeout.js';
4
+ import { useSyncMutations } from '../mutations.js';
5
+ import { useElectricConfig } from '../provider/index.js';
6
+ import { toRecords } from '../shape-utils.js';
7
+ export function useAgentContexts() {
8
+ const { proxyBaseUrl } = useElectricConfig();
9
+ const { data, isLoading, error } = useShape({
10
+ url: `${proxyBaseUrl}/api/shapes/agent-contexts`,
11
+ fetchClient: fetchWithTimeout,
12
+ });
13
+ const { create, update, remove } = useSyncMutations('agent-contexts');
14
+ return {
15
+ contexts: toRecords(data),
16
+ isLoading,
17
+ error: error || null,
18
+ create,
19
+ update,
20
+ remove,
21
+ };
22
+ }
@@ -0,0 +1,34 @@
1
+ import type { MutationResult } from '../mutations.js';
2
+ export interface AgentMemoryRecord {
3
+ id: string;
4
+ agent_id: string;
5
+ content: string;
6
+ type: string;
7
+ metadata: Record<string, unknown>;
8
+ created_at: string;
9
+ expires_at: string | null;
10
+ }
11
+ export interface CreateAgentMemoryInput {
12
+ agent_id: string;
13
+ content: string;
14
+ type: string;
15
+ source: Record<string, unknown>;
16
+ metadata?: Record<string, unknown>;
17
+ expires_at?: string | null;
18
+ }
19
+ export interface UpdateAgentMemoryInput {
20
+ content?: string;
21
+ type?: string;
22
+ metadata?: Record<string, unknown>;
23
+ expires_at?: string | null;
24
+ }
25
+ export interface UseAgentMemoryResult {
26
+ memories: AgentMemoryRecord[];
27
+ isLoading: boolean;
28
+ error: Error | null;
29
+ create: (data: CreateAgentMemoryInput) => Promise<MutationResult<AgentMemoryRecord>>;
30
+ update: (id: string, data: UpdateAgentMemoryInput) => Promise<MutationResult<AgentMemoryRecord>>;
31
+ remove: (id: string) => Promise<MutationResult<void>>;
32
+ }
33
+ export declare function useAgentMemory(agentId: string): UseAgentMemoryResult;
34
+ //# sourceMappingURL=useAgentMemory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAgentMemory.d.ts","sourceRoot":"","sources":["../../src/hooks/useAgentMemory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,sBAAsB,KAAK,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACrF,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,KAAK,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACjG,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;CACvD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAuCpE"}
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+ import { useShape } from '@electric-sql/react';
3
+ import { fetchWithTimeout } from '../fetch-with-timeout.js';
4
+ import { useSyncMutations } from '../mutations.js';
5
+ import { useElectricConfig } from '../provider/index.js';
6
+ import { toRecords } from '../shape-utils.js';
7
+ const AGENT_ID_RE = /^[a-zA-Z0-9_-]+$/;
8
+ export function useAgentMemory(agentId) {
9
+ const { proxyBaseUrl } = useElectricConfig();
10
+ const isValid = agentId.length > 0 && AGENT_ID_RE.test(agentId);
11
+ // Hook must always be called (Rules of Hooks). Pass an impossible WHERE when
12
+ // the ID is invalid so the shape returns no rows but the hook still runs.
13
+ const { data, isLoading, error } = useShape({
14
+ url: `${proxyBaseUrl}/api/shapes/agent-memories`,
15
+ params: { agent_id: isValid ? agentId : '00000000-0000-0000-0000-000000000000' },
16
+ fetchClient: fetchWithTimeout,
17
+ });
18
+ const { create, update, remove } = useSyncMutations('agent-memories');
19
+ if (!isValid) {
20
+ return {
21
+ memories: [],
22
+ isLoading: false,
23
+ error: new Error('Invalid agentId: must be non-empty alphanumeric, hyphens, underscores only'),
24
+ create,
25
+ update,
26
+ remove,
27
+ };
28
+ }
29
+ return {
30
+ memories: toRecords(data),
31
+ isLoading,
32
+ error: error || null,
33
+ create,
34
+ update,
35
+ remove,
36
+ };
37
+ }
@@ -1,11 +1,31 @@
1
- type ConversationRecord = {
2
- id: string | number;
1
+ import type { MutationResult } from '../mutations.js';
2
+ export interface ConversationRecord {
3
+ id: string;
4
+ user_id: string;
5
+ agent_id: string;
3
6
  title?: string | null;
4
- };
5
- export declare function useConversations(_userId: string): {
7
+ status: string;
8
+ device_id?: string | null;
9
+ version: number;
10
+ created_at: string;
11
+ updated_at: string;
12
+ }
13
+ export interface CreateConversationInput {
14
+ agent_id: string;
15
+ title?: string;
16
+ device_id?: string;
17
+ }
18
+ export interface UpdateConversationInput {
19
+ title?: string;
20
+ status?: string;
21
+ }
22
+ export interface UseConversationsResult {
6
23
  conversations: ConversationRecord[];
7
24
  isLoading: boolean;
8
25
  error: Error | null;
9
- };
10
- export {};
26
+ create: (data: CreateConversationInput) => Promise<MutationResult<ConversationRecord>>;
27
+ update: (id: string, data: UpdateConversationInput) => Promise<MutationResult<ConversationRecord>>;
28
+ remove: (id: string) => Promise<MutationResult<void>>;
29
+ }
30
+ export declare function useConversations(_userId: string): UseConversationsResult;
11
31
  //# sourceMappingURL=useConversations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useConversations.d.ts","sourceRoot":"","sources":["../../src/hooks/useConversations.ts"],"names":[],"mappings":"AAIA,KAAK,kBAAkB,GAAG;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAAA;AAID,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM;;;WAU5B,KAAK,GAAG,IAAI;EAE/B"}
1
+ {"version":3,"file":"useConversations.d.ts","sourceRoot":"","sources":["../../src/hooks/useConversations.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACvF,MAAM,EAAE,CACN,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,uBAAuB,KAC1B,OAAO,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;CACvD;AAID,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB,CAuBxE"}
@@ -1,16 +1,26 @@
1
1
  'use client';
2
2
  import { useShape } from '@electric-sql/react';
3
+ import { fetchWithTimeout } from '../fetch-with-timeout.js';
4
+ import { useSyncMutations } from '../mutations.js';
5
+ import { useElectricConfig } from '../provider/index.js';
6
+ import { toRecords } from '../shape-utils.js';
3
7
  // _userId kept for API compatibility — filtering is enforced by the server-side
4
8
  // proxy at /api/shapes/conversations, which reads the session cookie directly.
5
9
  export function useConversations(_userId) {
10
+ const { proxyBaseUrl } = useElectricConfig();
6
11
  // The proxy validates the session and enforces row-level filtering server-side.
7
12
  // Client-provided params are not forwarded — the proxy overrides them.
8
13
  const { data, isLoading, error } = useShape({
9
- url: `/api/shapes/conversations`,
14
+ url: `${proxyBaseUrl}/api/shapes/conversations`,
15
+ fetchClient: fetchWithTimeout,
10
16
  });
17
+ const { create, update, remove } = useSyncMutations('conversations');
11
18
  return {
12
- conversations: Array.isArray(data) ? data : [],
19
+ conversations: toRecords(data),
13
20
  isLoading,
14
- error: error,
21
+ error: error || null,
22
+ create,
23
+ update,
24
+ remove,
15
25
  };
16
26
  }
@@ -0,0 +1,35 @@
1
+ import type { MutationResult } from '../mutations.js';
2
+ export interface CoordinationSessionRecord {
3
+ id: string;
4
+ agent_id: string;
5
+ started_at: string;
6
+ ended_at: string | null;
7
+ task: string;
8
+ status: string;
9
+ pid: number | null;
10
+ tools: Record<string, number> | null;
11
+ metadata: Record<string, unknown> | null;
12
+ }
13
+ export interface CreateCoordinationSessionInput {
14
+ agent_id: string;
15
+ task?: string;
16
+ pid?: number;
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface UpdateCoordinationSessionInput {
20
+ task?: string;
21
+ status?: string;
22
+ ended_at?: string;
23
+ tools?: Record<string, number>;
24
+ metadata?: Record<string, unknown>;
25
+ }
26
+ export interface UseCoordinationSessionsResult {
27
+ sessions: CoordinationSessionRecord[];
28
+ isLoading: boolean;
29
+ error: Error | null;
30
+ create: (data: CreateCoordinationSessionInput) => Promise<MutationResult<CoordinationSessionRecord>>;
31
+ update: (id: string, data: UpdateCoordinationSessionInput) => Promise<MutationResult<CoordinationSessionRecord>>;
32
+ remove: (id: string) => Promise<MutationResult<void>>;
33
+ }
34
+ export declare function useCoordinationSessions(): UseCoordinationSessionsResult;
35
+ //# sourceMappingURL=useCoordinationSessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCoordinationSessions.d.ts","sourceRoot":"","sources":["../../src/hooks/useCoordinationSessions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,8BAA8B;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,CACN,IAAI,EAAE,8BAA8B,KACjC,OAAO,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC;IACxD,MAAM,EAAE,CACN,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,8BAA8B,KACjC,OAAO,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC;IACxD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;CACvD;AAED,wBAAgB,uBAAuB,IAAI,6BAA6B,CAqBvE"}
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { useShape } from '@electric-sql/react';
3
+ import { fetchWithTimeout } from '../fetch-with-timeout.js';
4
+ import { useSyncMutations } from '../mutations.js';
5
+ import { useElectricConfig } from '../provider/index.js';
6
+ import { toRecords } from '../shape-utils.js';
7
+ export function useCoordinationSessions() {
8
+ const { proxyBaseUrl } = useElectricConfig();
9
+ const { data, isLoading, error } = useShape({
10
+ url: `${proxyBaseUrl}/api/shapes/coordination-sessions`,
11
+ fetchClient: fetchWithTimeout,
12
+ });
13
+ const { create, update, remove } = useSyncMutations('coordination-sessions');
14
+ return {
15
+ sessions: toRecords(data),
16
+ isLoading,
17
+ error: error || null,
18
+ create,
19
+ update,
20
+ remove,
21
+ };
22
+ }
@@ -0,0 +1,41 @@
1
+ import type { MutationResult } from '../mutations.js';
2
+ export interface CoordinationWorkItemRecord {
3
+ id: string;
4
+ title: string;
5
+ description: string | null;
6
+ status: string;
7
+ priority: number;
8
+ owner_agent: string | null;
9
+ owner_session: string | null;
10
+ parent_id: string | null;
11
+ metadata: Record<string, unknown> | null;
12
+ created_at: string;
13
+ updated_at: string;
14
+ completed_at: string | null;
15
+ }
16
+ export interface CreateCoordinationWorkItemInput {
17
+ title: string;
18
+ description?: string;
19
+ priority?: number;
20
+ owner_agent?: string;
21
+ parent_id?: string;
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ export interface UpdateCoordinationWorkItemInput {
25
+ title?: string;
26
+ description?: string;
27
+ status?: string;
28
+ priority?: number;
29
+ owner_agent?: string;
30
+ metadata?: Record<string, unknown>;
31
+ }
32
+ export interface UseCoordinationWorkItemsResult {
33
+ workItems: CoordinationWorkItemRecord[];
34
+ isLoading: boolean;
35
+ error: Error | null;
36
+ create: (data: CreateCoordinationWorkItemInput) => Promise<MutationResult<CoordinationWorkItemRecord>>;
37
+ update: (id: string, data: UpdateCoordinationWorkItemInput) => Promise<MutationResult<CoordinationWorkItemRecord>>;
38
+ remove: (id: string) => Promise<MutationResult<void>>;
39
+ }
40
+ export declare function useCoordinationWorkItems(): UseCoordinationWorkItemsResult;
41
+ //# sourceMappingURL=useCoordinationWorkItems.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCoordinationWorkItems.d.ts","sourceRoot":"","sources":["../../src/hooks/useCoordinationWorkItems.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,+BAA+B;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,0BAA0B,EAAE,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,CACN,IAAI,EAAE,+BAA+B,KAClC,OAAO,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC;IACzD,MAAM,EAAE,CACN,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,+BAA+B,KAClC,OAAO,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC;IACzD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;CACvD;AAED,wBAAgB,wBAAwB,IAAI,8BAA8B,CAqBzE"}
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { useShape } from '@electric-sql/react';
3
+ import { fetchWithTimeout } from '../fetch-with-timeout.js';
4
+ import { useSyncMutations } from '../mutations.js';
5
+ import { useElectricConfig } from '../provider/index.js';
6
+ import { toRecords } from '../shape-utils.js';
7
+ export function useCoordinationWorkItems() {
8
+ const { proxyBaseUrl } = useElectricConfig();
9
+ const { data, isLoading, error } = useShape({
10
+ url: `${proxyBaseUrl}/api/shapes/coordination-work-items`,
11
+ fetchClient: fetchWithTimeout,
12
+ });
13
+ const { create, update, remove } = useSyncMutations('coordination-work-items');
14
+ return {
15
+ workItems: toRecords(data),
16
+ isLoading,
17
+ error: error || null,
18
+ create,
19
+ update,
20
+ remove,
21
+ };
22
+ }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,26 @@
1
1
  /**
2
- * @experimental ElectricSQL sync is not yet functional.
3
- * The ElectricProvider is currently a passthrough that renders children without
4
- * establishing any sync connection. Do not use in production.
2
+ * @revealui/sync Real-time collaboration and sync primitives.
3
+ *
4
+ * The collab layer (Yjs-based) is fully functional.
5
+ * ElectricProvider provides proxyBaseUrl config to child hooks. All hooks route
6
+ * through the authenticated CMS proxy at /api/shapes/* — no direct Electric client.
7
+ *
8
+ * Reads use ElectricSQL shape subscriptions for real-time updates.
9
+ * Writes use REST mutations via /api/sync/* — changes propagate to all
10
+ * subscribers automatically through ElectricSQL replication.
5
11
  */
6
12
  export type { CollabDocumentState, UseCollaborationOptions, UseCollaborationResult, } from './collab/index.js';
7
13
  export { CollabProvider, useCollabDocument, useCollaboration, } from './collab/index.js';
14
+ export type { AgentContextRecord, CreateAgentContextInput, UpdateAgentContextInput, UseAgentContextsResult, } from './hooks/useAgentContexts.js';
15
+ export { useAgentContexts } from './hooks/useAgentContexts.js';
16
+ export type { AgentMemoryRecord, CreateAgentMemoryInput, UpdateAgentMemoryInput, UseAgentMemoryResult, } from './hooks/useAgentMemory.js';
17
+ export { useAgentMemory } from './hooks/useAgentMemory.js';
18
+ export type { ConversationRecord, CreateConversationInput, UpdateConversationInput, UseConversationsResult, } from './hooks/useConversations.js';
8
19
  export { useConversations } from './hooks/useConversations.js';
9
- export { ElectricProvider } from './provider/index.js';
20
+ export type { CoordinationSessionRecord, CreateCoordinationSessionInput, UpdateCoordinationSessionInput, UseCoordinationSessionsResult, } from './hooks/useCoordinationSessions.js';
21
+ export { useCoordinationSessions } from './hooks/useCoordinationSessions.js';
22
+ export type { CoordinationWorkItemRecord, CreateCoordinationWorkItemInput, UpdateCoordinationWorkItemInput, UseCoordinationWorkItemsResult, } from './hooks/useCoordinationWorkItems.js';
23
+ export { useCoordinationWorkItems } from './hooks/useCoordinationWorkItems.js';
24
+ export type { MutationResult } from './mutations.js';
25
+ export { ElectricProvider, useElectricConfig } from './provider/index.js';
10
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EACV,yBAAyB,EACzB,8BAA8B,EAC9B,8BAA8B,EAC9B,6BAA6B,GAC9B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,YAAY,EACV,0BAA0B,EAC1B,+BAA+B,EAC/B,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,18 @@
1
1
  /**
2
- * @experimental ElectricSQL sync is not yet functional.
3
- * The ElectricProvider is currently a passthrough that renders children without
4
- * establishing any sync connection. Do not use in production.
2
+ * @revealui/sync Real-time collaboration and sync primitives.
3
+ *
4
+ * The collab layer (Yjs-based) is fully functional.
5
+ * ElectricProvider provides proxyBaseUrl config to child hooks. All hooks route
6
+ * through the authenticated CMS proxy at /api/shapes/* — no direct Electric client.
7
+ *
8
+ * Reads use ElectricSQL shape subscriptions for real-time updates.
9
+ * Writes use REST mutations via /api/sync/* — changes propagate to all
10
+ * subscribers automatically through ElectricSQL replication.
5
11
  */
6
12
  export { CollabProvider, useCollabDocument, useCollaboration, } from './collab/index.js';
13
+ export { useAgentContexts } from './hooks/useAgentContexts.js';
14
+ export { useAgentMemory } from './hooks/useAgentMemory.js';
7
15
  export { useConversations } from './hooks/useConversations.js';
8
- export { ElectricProvider } from './provider/index.js';
16
+ export { useCoordinationSessions } from './hooks/useCoordinationSessions.js';
17
+ export { useCoordinationWorkItems } from './hooks/useCoordinationWorkItems.js';
18
+ export { ElectricProvider, useElectricConfig } from './provider/index.js';
@@ -0,0 +1,14 @@
1
+ export interface MutationResult<T = unknown> {
2
+ success: boolean;
3
+ data?: T;
4
+ error?: string;
5
+ }
6
+ /**
7
+ * Hook that returns mutation functions for a given sync API endpoint.
8
+ */
9
+ export declare function useSyncMutations<TCreate, TUpdate, TRecord>(endpoint: string): {
10
+ create: (data: TCreate) => Promise<MutationResult<TRecord>>;
11
+ update: (id: string, data: TUpdate) => Promise<MutationResult<TRecord>>;
12
+ remove: (id: string) => Promise<MutationResult<void>>;
13
+ };
14
+ //# sourceMappingURL=mutations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../src/mutations.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0CD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;mBAK3D,OAAO,KAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;iBAO5C,MAAM,QAAQ,OAAO,KAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;iBAOxD,MAAM,KAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;EAOpD"}
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+ import { useCallback } from 'react';
3
+ import { useElectricConfig } from './provider/index.js';
4
+ /** Default timeout for mutation fetch requests (milliseconds). */
5
+ const MUTATION_FETCH_TIMEOUT_MS = 10_000;
6
+ /**
7
+ * Make an authenticated mutation request to the CMS API.
8
+ * Credentials are included so the session cookie is sent cross-origin.
9
+ * Requests are aborted after {@link MUTATION_FETCH_TIMEOUT_MS} (10 s).
10
+ */
11
+ async function mutationFetch(url, method, body) {
12
+ const controller = new AbortController();
13
+ const timeout = setTimeout(() => controller.abort(), MUTATION_FETCH_TIMEOUT_MS);
14
+ let response;
15
+ try {
16
+ response = await fetch(url, {
17
+ method,
18
+ credentials: 'include',
19
+ signal: controller.signal,
20
+ headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined,
21
+ body: body !== undefined ? JSON.stringify(body) : undefined,
22
+ });
23
+ }
24
+ finally {
25
+ clearTimeout(timeout);
26
+ }
27
+ if (!response.ok) {
28
+ const errorData = (await response.json().catch(() => null));
29
+ return {
30
+ success: false,
31
+ error: errorData?.error ?? `Request failed with status ${response.status}`,
32
+ };
33
+ }
34
+ const data = (await response.json());
35
+ return { success: true, data };
36
+ }
37
+ /**
38
+ * Hook that returns mutation functions for a given sync API endpoint.
39
+ */
40
+ export function useSyncMutations(endpoint) {
41
+ const { proxyBaseUrl } = useElectricConfig();
42
+ const baseUrl = `${proxyBaseUrl}/api/sync/${endpoint}`;
43
+ const create = useCallback(async (data) => {
44
+ return mutationFetch(baseUrl, 'POST', data);
45
+ }, [baseUrl]);
46
+ const update = useCallback(async (id, data) => {
47
+ return mutationFetch(`${baseUrl}/${encodeURIComponent(id)}`, 'PATCH', data);
48
+ }, [baseUrl]);
49
+ const remove = useCallback(async (id) => {
50
+ return mutationFetch(`${baseUrl}/${encodeURIComponent(id)}`, 'DELETE');
51
+ }, [baseUrl]);
52
+ return { create, update, remove };
53
+ }
@@ -1,11 +1,35 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
+ interface ElectricContextValue {
3
+ /**
4
+ * Direct Electric service URL (e.g. the Railway instance).
5
+ * Stored in context for future use — not consumed by the current proxy-based hooks.
6
+ * All hooks use proxyBaseUrl + /api/shapes/* instead.
7
+ */
8
+ serviceUrl: string | null;
9
+ /**
10
+ * Base URL prefix for authenticated CMS shape proxy routes.
11
+ * Default '' keeps all hook URLs relative (works for same-origin apps).
12
+ * Set to 'https://cms.revealui.com' when consuming from a different origin.
13
+ */
14
+ proxyBaseUrl: string;
15
+ debug: boolean;
16
+ }
2
17
  /**
3
- * @experimental This provider is a passthrough ElectricSQL integration is not yet implemented.
4
- * Children are rendered without any sync functionality.
18
+ * Provides ElectricSQL configuration to child hooks (`useConversations`, `useCollabDocument`).
19
+ *
20
+ * Provides proxyBaseUrl (and optional serviceUrl/debug) to child hooks via context.
21
+ * All hooks use the CMS proxy pattern — no direct Electric connection is established here.
5
22
  */
6
- export declare function ElectricProvider({ children, }: {
23
+ export declare function ElectricProvider(props: {
7
24
  children: ReactNode;
8
25
  serviceUrl?: string;
26
+ proxyBaseUrl?: string;
9
27
  debug?: boolean;
10
- }): import("react/jsx-runtime").JSX.Element;
28
+ }): ReactNode;
29
+ /**
30
+ * Access the ElectricSQL configuration provided by `ElectricProvider`.
31
+ * Returns `{ serviceUrl: null, debug: false }` if no provider is present.
32
+ */
33
+ export declare function useElectricConfig(): ElectricContextValue;
34
+ export {};
11
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,2CAIA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,SAAS,EAAgB,MAAM,OAAO,CAAC;AAEpE,UAAU,oBAAoB;IAC5B;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;CAChB;AAQD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,SAAS,CAWZ;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,oBAAoB,CAExD"}
@@ -1,11 +1,29 @@
1
1
  'use client';
2
- import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, use, useMemo } from 'react';
4
+ const ElectricContext = createContext({
5
+ serviceUrl: null,
6
+ proxyBaseUrl: '',
7
+ debug: false,
8
+ });
3
9
  /**
4
- * @experimental This provider is a passthrough ElectricSQL integration is not yet implemented.
5
- * Children are rendered without any sync functionality.
10
+ * Provides ElectricSQL configuration to child hooks (`useConversations`, `useCollabDocument`).
11
+ *
12
+ * Provides proxyBaseUrl (and optional serviceUrl/debug) to child hooks via context.
13
+ * All hooks use the CMS proxy pattern — no direct Electric connection is established here.
6
14
  */
7
- export function ElectricProvider({ children, }) {
8
- // For now, just pass through children
9
- // useShape hooks work directly with proxy API endpoints
10
- return _jsx(_Fragment, { children: children });
15
+ export function ElectricProvider(props) {
16
+ const value = useMemo(() => ({
17
+ serviceUrl: props.serviceUrl ?? null,
18
+ proxyBaseUrl: props.proxyBaseUrl ?? '',
19
+ debug: props.debug ?? false,
20
+ }), [props.serviceUrl, props.proxyBaseUrl, props.debug]);
21
+ return _jsx(ElectricContext, { value: value, children: props.children });
22
+ }
23
+ /**
24
+ * Access the ElectricSQL configuration provided by `ElectricProvider`.
25
+ * Returns `{ serviceUrl: null, debug: false }` if no provider is present.
26
+ */
27
+ export function useElectricConfig() {
28
+ return use(ElectricContext);
11
29
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cast ElectricSQL shape data to a typed record array.
3
+ *
4
+ * ElectricSQL's `useShape` returns `Row[]` (i.e. `Record<string, Value>[]`).
5
+ * Our record interfaces describe the exact same shape but lack the index
6
+ * signature that `Row` carries, so TypeScript rejects a direct assignment.
7
+ * This helper encapsulates the boundary cast in one place and returns an
8
+ * empty array when data is falsy.
9
+ */
10
+ export declare function toRecords<T>(data: unknown[] | undefined): T[];
11
+ //# sourceMappingURL=shape-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-utils.d.ts","sourceRoot":"","sources":["../src/shape-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,CAG7D"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Cast ElectricSQL shape data to a typed record array.
3
+ *
4
+ * ElectricSQL's `useShape` returns `Row[]` (i.e. `Record<string, Value>[]`).
5
+ * Our record interfaces describe the exact same shape but lack the index
6
+ * signature that `Row` carries, so TypeScript rejects a direct assignment.
7
+ * This helper encapsulates the boundary cast in one place and returns an
8
+ * empty array when data is falsy.
9
+ */
10
+ export function toRecords(data) {
11
+ if (!Array.isArray(data))
12
+ return [];
13
+ return data;
14
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAA"}
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revealui/sync",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "experimental": true,
5
5
  "license": "MIT",
6
6
  "files": [
@@ -13,8 +13,8 @@
13
13
  "ws": "^8.18.0",
14
14
  "y-protocols": "^1.0.7",
15
15
  "yjs": "^13.6.29",
16
- "@revealui/contracts": "1.1.0",
17
- "@revealui/db": "0.2.1"
16
+ "@revealui/db": "0.3.0",
17
+ "@revealui/contracts": "1.2.0"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@testing-library/jest-dom": "^6.6.4",
@@ -29,6 +29,9 @@
29
29
  "vitest": "^4.0.18",
30
30
  "dev": "0.0.1"
31
31
  },
32
+ "engines": {
33
+ "node": ">=24.13.0"
34
+ },
32
35
  "exports": {
33
36
  ".": {
34
37
  "types": "./dist/index.d.ts",
@@ -59,9 +62,8 @@
59
62
  "clean": "rm -rf dist",
60
63
  "dev": "tsc --watch",
61
64
  "lint": "biome check .",
62
- "lint:eslint": "eslint .",
63
65
  "test": "vitest run",
64
- "test:coverage": "vitest run --coverage",
66
+ "test:coverage": "vitest run --coverage --coverage.reporter=json-summary --coverage.reporter=html --coverage.reporter=text",
65
67
  "test:watch": "vitest",
66
68
  "typecheck": "tsc --noEmit"
67
69
  }