@revealui/sync 0.0.0-canary-20260409021642
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +201 -0
- package/dist/collab/agent-client-factory.d.ts +16 -0
- package/dist/collab/agent-client-factory.d.ts.map +1 -0
- package/dist/collab/agent-client-factory.js +24 -0
- package/dist/collab/agent-client.d.ts +56 -0
- package/dist/collab/agent-client.d.ts.map +1 -0
- package/dist/collab/agent-client.js +262 -0
- package/dist/collab/index.d.ts +8 -0
- package/dist/collab/index.d.ts.map +1 -0
- package/dist/collab/index.js +4 -0
- package/dist/collab/protocol-constants.d.ts +5 -0
- package/dist/collab/protocol-constants.d.ts.map +1 -0
- package/dist/collab/protocol-constants.js +4 -0
- package/dist/collab/server-index.d.ts +6 -0
- package/dist/collab/server-index.d.ts.map +1 -0
- package/dist/collab/server-index.js +3 -0
- package/dist/collab/use-collab-document.d.ts +8 -0
- package/dist/collab/use-collab-document.d.ts.map +1 -0
- package/dist/collab/use-collab-document.js +49 -0
- package/dist/collab/use-collaboration.d.ts +26 -0
- package/dist/collab/use-collaboration.d.ts.map +1 -0
- package/dist/collab/use-collaboration.js +65 -0
- package/dist/collab/yjs-websocket-provider.d.ts +36 -0
- package/dist/collab/yjs-websocket-provider.d.ts.map +1 -0
- package/dist/collab/yjs-websocket-provider.js +172 -0
- package/dist/components/SyncStatusIndicator.d.ts +17 -0
- package/dist/components/SyncStatusIndicator.d.ts.map +1 -0
- package/dist/components/SyncStatusIndicator.js +65 -0
- package/dist/fetch-with-timeout.d.ts +7 -0
- package/dist/fetch-with-timeout.d.ts.map +1 -0
- package/dist/fetch-with-timeout.js +27 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useAgentContexts.d.ts +29 -0
- package/dist/hooks/useAgentContexts.d.ts.map +1 -0
- package/dist/hooks/useAgentContexts.js +22 -0
- package/dist/hooks/useAgentMemory.d.ts +34 -0
- package/dist/hooks/useAgentMemory.d.ts.map +1 -0
- package/dist/hooks/useAgentMemory.js +37 -0
- package/dist/hooks/useConversations.d.ts +31 -0
- package/dist/hooks/useConversations.d.ts.map +1 -0
- package/dist/hooks/useConversations.js +26 -0
- package/dist/hooks/useCoordinationSessions.d.ts +35 -0
- package/dist/hooks/useCoordinationSessions.d.ts.map +1 -0
- package/dist/hooks/useCoordinationSessions.js +22 -0
- package/dist/hooks/useCoordinationWorkItems.d.ts +41 -0
- package/dist/hooks/useCoordinationWorkItems.d.ts.map +1 -0
- package/dist/hooks/useCoordinationWorkItems.js +22 -0
- package/dist/hooks/useOfflineCache.d.ts +32 -0
- package/dist/hooks/useOfflineCache.d.ts.map +1 -0
- package/dist/hooks/useOfflineCache.js +129 -0
- package/dist/hooks/useOnlineStatus.d.ts +21 -0
- package/dist/hooks/useOnlineStatus.d.ts.map +1 -0
- package/dist/hooks/useOnlineStatus.js +74 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/mutations.d.ts +14 -0
- package/dist/mutations.d.ts.map +1 -0
- package/dist/mutations.js +53 -0
- package/dist/offline-queue.d.ts +55 -0
- package/dist/offline-queue.d.ts.map +1 -0
- package/dist/offline-queue.js +126 -0
- package/dist/provider/index.d.ts +35 -0
- package/dist/provider/index.d.ts.map +1 -0
- package/dist/provider/index.js +29 -0
- package/dist/shape-utils.d.ts +11 -0
- package/dist/shape-utils.d.ts.map +1 -0
- package/dist/shape-utils.js +14 -0
- package/dist/test-setup.d.ts +2 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline mutation queue — stores pending mutations in localStorage
|
|
3
|
+
* so they survive page reloads and can be flushed when connectivity returns.
|
|
4
|
+
*/
|
|
5
|
+
/** localStorage key for the persisted queue. */
|
|
6
|
+
const STORAGE_KEY = 'revealui:offline-queue';
|
|
7
|
+
/**
|
|
8
|
+
* Check whether `localStorage` is available.
|
|
9
|
+
* Returns `false` during SSR or in private-browsing contexts that throw on access.
|
|
10
|
+
*/
|
|
11
|
+
function hasLocalStorage() {
|
|
12
|
+
if (typeof window === 'undefined') {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const testKey = '__revealui_ls_test__';
|
|
17
|
+
window.localStorage.setItem(testKey, '1');
|
|
18
|
+
window.localStorage.removeItem(testKey);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read the current queue from localStorage.
|
|
27
|
+
* Returns an empty array when storage is unavailable or the data is corrupt.
|
|
28
|
+
*/
|
|
29
|
+
function readQueue() {
|
|
30
|
+
if (!hasLocalStorage()) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
35
|
+
if (raw === null) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
if (!Array.isArray(parsed)) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return parsed;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Persist the queue to localStorage.
|
|
50
|
+
* Silently ignores errors (e.g. quota exceeded, private browsing).
|
|
51
|
+
*/
|
|
52
|
+
function writeQueue(queue) {
|
|
53
|
+
if (!hasLocalStorage()) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(queue));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Quota exceeded or private browsing — drop silently.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Manages a FIFO queue of mutations that were created while the browser
|
|
65
|
+
* was offline. Mutations are persisted in `localStorage` under the key
|
|
66
|
+
* `revealui:offline-queue`.
|
|
67
|
+
*
|
|
68
|
+
* All operations are no-ops when `localStorage` is unavailable (SSR,
|
|
69
|
+
* private browsing that throws, etc.).
|
|
70
|
+
*/
|
|
71
|
+
export class OfflineMutationQueue {
|
|
72
|
+
/**
|
|
73
|
+
* Add a mutation to the end of the queue.
|
|
74
|
+
*
|
|
75
|
+
* @param mutation - The mutation to enqueue (must include a unique `id`).
|
|
76
|
+
*/
|
|
77
|
+
enqueue(mutation) {
|
|
78
|
+
const queue = readQueue();
|
|
79
|
+
queue.push(mutation);
|
|
80
|
+
writeQueue(queue);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Execute all queued mutations in order via the provided executor.
|
|
84
|
+
* Each mutation is removed from the persisted queue only after the
|
|
85
|
+
* executor resolves successfully. Processing stops at the first failure
|
|
86
|
+
* so ordering is preserved.
|
|
87
|
+
*
|
|
88
|
+
* @param executor - Async function that sends a single mutation to the server.
|
|
89
|
+
*/
|
|
90
|
+
async flush(executor) {
|
|
91
|
+
const queue = readQueue();
|
|
92
|
+
for (const mutation of queue) {
|
|
93
|
+
await executor(mutation);
|
|
94
|
+
// Remove the successfully-processed mutation from storage.
|
|
95
|
+
const current = readQueue();
|
|
96
|
+
const updated = current.filter((m) => m.id !== mutation.id);
|
|
97
|
+
writeQueue(updated);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Return all pending mutations without removing them.
|
|
102
|
+
*/
|
|
103
|
+
peek() {
|
|
104
|
+
return readQueue();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Number of mutations currently in the queue.
|
|
108
|
+
*/
|
|
109
|
+
get size() {
|
|
110
|
+
return readQueue().length;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Remove all pending mutations from the queue.
|
|
114
|
+
*/
|
|
115
|
+
clear() {
|
|
116
|
+
if (!hasLocalStorage()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
window.localStorage.removeItem(STORAGE_KEY);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Ignore — same guard as writeQueue.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
}
|
|
17
|
+
/**
|
|
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.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ElectricProvider(props: {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
serviceUrl?: string;
|
|
26
|
+
proxyBaseUrl?: string;
|
|
27
|
+
debug?: boolean;
|
|
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 {};
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
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
|
+
});
|
|
9
|
+
/**
|
|
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.
|
|
14
|
+
*/
|
|
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);
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@revealui/sync",
|
|
3
|
+
"version": "0.0.0-canary-20260409021642",
|
|
4
|
+
"description": "ElectricSQL sync utilities for RevealUI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@electric-sql/react": "^1.0.27",
|
|
8
|
+
"lib0": "^0.2.117",
|
|
9
|
+
"ws": "^8.18.0",
|
|
10
|
+
"y-protocols": "^1.0.7",
|
|
11
|
+
"yjs": "^13.6.29",
|
|
12
|
+
"@revealui/db": "0.0.0-canary-20260409021642",
|
|
13
|
+
"@revealui/contracts": "0.0.0-canary-20260409021642"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@testing-library/jest-dom": "^6.6.4",
|
|
17
|
+
"@testing-library/react": "^16.3.0",
|
|
18
|
+
"@types/ws": "^8.5.14",
|
|
19
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
20
|
+
"jsdom": "^29.0.1",
|
|
21
|
+
"react": "^19.2.3",
|
|
22
|
+
"react-dom": "^19.2.3",
|
|
23
|
+
"typescript": "^6.0.2",
|
|
24
|
+
"vitest": "^4.1.0",
|
|
25
|
+
"dev": "0.0.1"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=24.13.0"
|
|
29
|
+
},
|
|
30
|
+
"experimental": true,
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./provider": {
|
|
37
|
+
"types": "./dist/provider/index.d.ts",
|
|
38
|
+
"import": "./dist/provider/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./collab": {
|
|
41
|
+
"types": "./dist/collab/index.d.ts",
|
|
42
|
+
"import": "./dist/collab/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./collab/server": {
|
|
45
|
+
"types": "./dist/collab/server-index.d.ts",
|
|
46
|
+
"import": "./dist/collab/server-index.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist"
|
|
51
|
+
],
|
|
52
|
+
"main": "./dist/index.js",
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public",
|
|
55
|
+
"registry": "https://registry.npmjs.org"
|
|
56
|
+
},
|
|
57
|
+
"type": "module",
|
|
58
|
+
"types": "./dist/index.d.ts",
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc",
|
|
61
|
+
"clean": "rm -rf dist",
|
|
62
|
+
"dev": "tsc --watch",
|
|
63
|
+
"lint": "biome check .",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=json-summary --coverage.reporter=html --coverage.reporter=text",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"typecheck": "tsc --noEmit"
|
|
68
|
+
}
|
|
69
|
+
}
|