local-lite 0.0.1 → 0.0.3

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.
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useLiveQuery = useLiveQuery;
4
- exports.useOutboxStatus = useOutboxStatus;
4
+ exports.useSyncStatus = useSyncStatus;
5
+ exports.useSyncMetrics = useSyncMetrics;
5
6
  const react_1 = require("react");
6
7
  /**
7
8
  * Hook to execute a live reactive table query that automatically re-runs
@@ -11,9 +12,9 @@ const react_1 = require("react");
11
12
  * 1. Zero-SQL (all rows):
12
13
  * const { data: todos } = useLiveQuery<Todo>(client, 'todos');
13
14
  *
14
- * 2. Declarative options (filtering & sorting without SQL):
15
+ * 2. Declarative options (filtering with rich operators & sorting without SQL):
15
16
  * const { data: active } = useLiveQuery<Todo>(client, 'todos', {
16
- * where: { completed: 0 },
17
+ * where: { completed: 0, priority: { $gte: 2 } },
17
18
  * orderBy: 'created_at',
18
19
  * order: 'desc'
19
20
  * });
@@ -71,33 +72,114 @@ function useLiveQuery(client, tableName, queryOrOptions) {
71
72
  return { data, refetch, loading };
72
73
  }
73
74
  /**
74
- * Hook that tracks the status and pending items in sync_outbox in real time.
75
+ * Consolidated hook that tracks real-time sync connectivity, sync worker activity
76
+ * (pushing/pulling/syncing), and pending items in the local SQLite outbox queue.
75
77
  */
76
- function useOutboxStatus(client) {
78
+ function useSyncStatus(client) {
79
+ const [isOnline, setIsOnline] = (0, react_1.useState)(true);
80
+ const [isPushing, setIsPushing] = (0, react_1.useState)(false);
81
+ const [isPulling, setIsPulling] = (0, react_1.useState)(false);
77
82
  const [pendingCount, setPendingCount] = (0, react_1.useState)(0);
78
83
  const [pendingRecords, setPendingRecords] = (0, react_1.useState)([]);
84
+ const [lastSyncedAt, setLastSyncedAt] = (0, react_1.useState)(null);
79
85
  const refresh = (0, react_1.useCallback)(async () => {
80
86
  if (!client || !client.db)
81
87
  return;
82
88
  try {
83
- const records = await client.db.execute('SELECT * FROM sync_outbox ORDER BY timestamp DESC');
84
- setPendingRecords(records || []);
85
- setPendingCount((records || []).length);
89
+ const [metrics, records, lastSynced] = await Promise.all([
90
+ client.getMetrics().catch(() => null),
91
+ client.getOutboxRecords().catch(() => []),
92
+ client.getLastSyncedAt().catch(() => null),
93
+ ]);
94
+ if (metrics) {
95
+ setIsOnline(metrics.isOnline);
96
+ setIsPushing(metrics.isPushing);
97
+ setIsPulling(metrics.isPulling);
98
+ }
99
+ if (records) {
100
+ setPendingRecords(records);
101
+ setPendingCount(records.filter((r) => r.status === 'PENDING').length);
102
+ }
103
+ if (lastSynced !== null && lastSynced !== undefined) {
104
+ setLastSyncedAt(lastSynced > 0 ? lastSynced : null);
105
+ }
86
106
  }
87
107
  catch {
88
- // Ignored if table doesn't exist yet
108
+ // Ignored if client is stopping or tables initializing
89
109
  }
90
110
  }, [client]);
91
111
  (0, react_1.useEffect)(() => {
92
112
  if (!client || !client.db)
93
113
  return;
94
114
  refresh();
95
- const unsubscribe = client.events.on('sync_outbox', () => {
115
+ const unsubStatus = client.events.on('sync:status', (e) => {
116
+ setIsOnline(e.isOnline);
117
+ setIsPushing(e.isPushing);
118
+ setIsPulling(e.isPulling);
119
+ setPendingCount(e.pendingCount);
120
+ });
121
+ const unsubOutbox = client.events.on('sync_outbox', () => {
122
+ refresh();
123
+ });
124
+ const unsubPush = client.events.on('sync:push', () => {
125
+ refresh();
126
+ });
127
+ const unsubPull = client.events.on('sync:pull', () => {
96
128
  refresh();
97
129
  });
98
130
  return () => {
99
- unsubscribe();
131
+ unsubStatus();
132
+ unsubOutbox();
133
+ unsubPush();
134
+ unsubPull();
100
135
  };
101
136
  }, [client, refresh]);
102
- return { pendingCount, pendingRecords, refresh };
137
+ return {
138
+ isOnline,
139
+ isPushing,
140
+ isPulling,
141
+ isSyncing: isPushing || isPulling,
142
+ pendingCount,
143
+ pendingRecords,
144
+ lastSyncedAt,
145
+ refresh,
146
+ };
147
+ }
148
+ /**
149
+ * Hook that provides real-time sync telemetry and engine performance metrics.
150
+ */
151
+ function useSyncMetrics(client, pollIntervalMs = 3000) {
152
+ const [metrics, setMetrics] = (0, react_1.useState)(null);
153
+ const refresh = (0, react_1.useCallback)(async () => {
154
+ if (!client)
155
+ return;
156
+ try {
157
+ const data = await client.getMetrics();
158
+ setMetrics(data);
159
+ }
160
+ catch {
161
+ // Ignored
162
+ }
163
+ }, [client]);
164
+ (0, react_1.useEffect)(() => {
165
+ if (!client)
166
+ return;
167
+ refresh();
168
+ // Listen to sync events for immediate refresh
169
+ const unsubs = [
170
+ client.events.on('sync:push', () => refresh()),
171
+ client.events.on('sync:pull', () => refresh()),
172
+ client.events.on('sync:status', () => refresh()),
173
+ ];
174
+ let timer;
175
+ if (pollIntervalMs > 0) {
176
+ timer = setInterval(refresh, pollIntervalMs);
177
+ }
178
+ return () => {
179
+ unsubs.forEach((u) => u());
180
+ if (timer)
181
+ clearInterval(timer);
182
+ };
183
+ }, [client, refresh, pollIntervalMs]);
184
+ return { metrics, refresh };
103
185
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "local-lite",
3
- "version": "0.0.1",
4
- "description": "Vendor-agnostic local-first sync engine with outbox pattern, Supabase, Convex, and Firebase support",
3
+ "version": "0.0.3",
4
+ "description": "Never let a lost connection break a promise. Local-first sync that faithfully keeps data safe until you're back online.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -130,4 +130,4 @@
130
130
  "dependencies": {
131
131
  "uuid": "^14.0.3"
132
132
  }
133
- }
133
+ }