@zveltio/react 3.0.0-beta.4 → 3.0.0-beta.68

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/README.md CHANGED
@@ -12,14 +12,16 @@ bun add @zveltio/react @zveltio/sdk
12
12
 
13
13
  ## Setup
14
14
 
15
- Wrap your app with `ZveltioProvider`:
15
+ Create a client and wrap your app with `ZveltioProvider`:
16
16
 
17
17
  ```tsx
18
- import { ZveltioProvider } from '@zveltio/react';
18
+ import { ZveltioProvider, createZveltioClient } from '@zveltio/react';
19
+
20
+ const client = createZveltioClient({ baseUrl: 'https://your-engine.example.com' });
19
21
 
20
22
  export default function App() {
21
23
  return (
22
- <ZveltioProvider url="https://your-engine.example.com">
24
+ <ZveltioProvider client={client}>
23
25
  <YourApp />
24
26
  </ZveltioProvider>
25
27
  );
@@ -34,17 +36,20 @@ export default function App() {
34
36
  import { useCollection, useRecord } from '@zveltio/react';
35
37
 
36
38
  function ProductList() {
37
- const { data, loading, error, refresh } = useCollection('products', {
39
+ const { data, loading, error, refetch } = useCollection('products', {
38
40
  filter: { status: 'active' },
39
- sort: '-createdAt',
41
+ sort: 'created_at',
42
+ order: 'desc',
40
43
  limit: 20,
41
44
  });
42
45
 
43
46
  if (loading) return <Spinner />;
44
- return data.map((p) => <ProductCard key={p.id} product={p} />);
47
+ if (error) return <p>{error.message}</p>;
48
+ return (data ?? []).map((p) => <ProductCard key={p.id} product={p} />);
45
49
  }
46
50
 
47
51
  function ProductDetail({ id }: { id: string }) {
52
+ // Re-fetches when `id` changes.
48
53
  const { data: product } = useRecord('products', id);
49
54
  return <div>{product?.name}</div>;
50
55
  }
@@ -55,26 +60,39 @@ function ProductDetail({ id }: { id: string }) {
55
60
  ```tsx
56
61
  import { useRealtime } from '@zveltio/react';
57
62
 
58
- function LiveFeed() {
59
- const event = useRealtime('orders');
60
- // event: { action: 'create'|'update'|'delete', record: {...} } | null
61
- return <div>Last event: {event?.action}</div>;
63
+ function LiveOrders() {
64
+ const [orders, setOrders] = useState([]);
65
+
66
+ useRealtime(
67
+ 'https://your-engine.example.com',
68
+ 'orders',
69
+ 'insert', // 'insert' | 'update' | 'delete', or null for every event
70
+ (event) => setOrders((prev) => [event.data, ...prev]),
71
+ );
72
+
73
+ return <OrderList orders={orders} />;
62
74
  }
63
75
  ```
64
76
 
65
- ### Offline Sync
77
+ The socket authenticates with the session cookie, so the engine must be same-site
78
+ with your app (or behind the same reverse proxy).
79
+
80
+ ### Offline sync
66
81
 
67
82
  ```tsx
68
83
  import { useSyncCollection, useSyncStatus } from '@zveltio/react';
69
84
 
70
85
  function OfflineProducts() {
71
- const { records, pending, conflicts } = useSyncCollection('products');
72
- const { isOnline } = useSyncStatus();
86
+ const { data } = useSyncCollection('products', {
87
+ realtimeUrl: 'https://your-engine.example.com',
88
+ syncInterval: 30_000,
89
+ });
90
+ const { status, pendingCount } = useSyncStatus();
73
91
 
74
92
  return (
75
93
  <>
76
- {!isOnline && <Banner>Offline — syncing when reconnected</Banner>}
77
- {records.map((p) => (
94
+ {status === 'offline' && <Banner>Offline — {pendingCount} changes waiting</Banner>}
95
+ {(data ?? []).map((p) => (
78
96
  <div key={p.id} style={{ opacity: p._syncStatus === 'pending' ? 0.6 : 1 }}>
79
97
  {p.name}
80
98
  </div>
@@ -84,37 +102,42 @@ function OfflineProducts() {
84
102
  }
85
103
  ```
86
104
 
105
+ Pass a `SyncManager` to `useSyncStatus(syncManager)` to see its pending queue;
106
+ without one it reports only the browser's online state.
107
+
87
108
  ### Auth
88
109
 
89
110
  ```tsx
90
111
  import { useAuth } from '@zveltio/react';
91
112
 
92
113
  function Header() {
93
- const { user, signIn, signOut, loading } = useAuth();
114
+ const { data, loading, login, logout } = useAuth();
94
115
 
95
116
  if (loading) return null;
96
- if (!user) return <button onClick={() => signIn({ email, password })}>Sign In</button>;
117
+ if (!data?.user) return <button onClick={() => login(email, password)}>Sign In</button>;
97
118
  return (
98
119
  <div>
99
- {user.email} <button onClick={signOut}>Sign Out</button>
120
+ {data.user.email} <button onClick={logout}>Sign Out</button>
100
121
  </div>
101
122
  );
102
123
  }
103
124
  ```
104
125
 
126
+ `signup(email, password, name)` is returned alongside `login` and `logout`.
127
+
105
128
  ### Storage
106
129
 
107
130
  ```tsx
108
131
  import { useStorage } from '@zveltio/react';
109
132
 
110
- function FileUpload() {
111
- const { upload, uploading, progress } = useStorage();
133
+ function FileUpload({ folderId }: { folderId?: string }) {
134
+ const { upload, uploading, error } = useStorage();
112
135
 
113
136
  const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
114
137
  const file = e.target.files?.[0];
115
138
  if (file) {
116
- const url = await upload(file);
117
- console.log('Uploaded:', url);
139
+ const stored = await upload(file, folderId);
140
+ console.log('Uploaded:', stored.url);
118
141
  }
119
142
  };
120
143
 
@@ -122,6 +145,9 @@ function FileUpload() {
122
145
  }
123
146
  ```
124
147
 
148
+ `list(folderId?)` and `remove(fileId)` are returned as well. The folder argument is a
149
+ folder **id**; omit it for the root.
150
+
125
151
  ## Links
126
152
 
127
153
  - [Documentation](https://zveltio.com/docs)
package/dist/context.d.ts CHANGED
@@ -4,6 +4,6 @@ export interface ZveltioProviderProps {
4
4
  client: ZveltioClient;
5
5
  children: React.ReactNode;
6
6
  }
7
- export declare function ZveltioProvider({ client, children }: ZveltioProviderProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function ZveltioProvider({ client, children }: ZveltioProviderProps): React.JSX.Element;
8
8
  export declare function useZveltioClient(): ZveltioClient;
9
9
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,oBAAoB,2CAEzE;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAMhD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,oBAAoB,qBAEzE;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAMhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCollection.d.ts","sourceRoot":"","sources":["../../src/hooks/useCollection.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EACnC,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,UAAU,CAAC,CAAC,EAAE,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAuB3C"}
1
+ {"version":3,"file":"useCollection.d.ts","sourceRoot":"","sources":["../../src/hooks/useCollection.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EACnC,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,UAAU,CAAC,CAAC,EAAE,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAuB3C"}
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect, useCallback } from 'react';
2
2
  import { fetchCollection } from '@zveltio/sdk';
3
3
  import { useZveltioClient } from '../context.js';
4
+ // biome-ignore lint/suspicious/noExplicitAny: legacy any; tracked in hardening plan item H-01
4
5
  export function useCollection(collectionName, options) {
5
6
  const client = useZveltioClient();
6
7
  const [data, setData] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"file":"useRealtime.d.ts","sourceRoot":"","sources":["../../src/hooks/useRealtime.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAC5B,IAAI,CAwBN"}
1
+ {"version":3,"file":"useRealtime.d.ts","sourceRoot":"","sources":["../../src/hooks/useRealtime.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,EAEpB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAC5B,IAAI,CAyBN"}
@@ -1,6 +1,8 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { ZveltioRealtime } from '@zveltio/sdk';
3
- export function useRealtime(baseUrl, collection, event, callback) {
3
+ export function useRealtime(baseUrl, collection, event,
4
+ // biome-ignore lint/suspicious/noExplicitAny: legacy any; tracked in hardening plan item H-01
5
+ callback) {
4
6
  const realtimeRef = useRef(null);
5
7
  const callbackRef = useRef(callback);
6
8
  callbackRef.current = callback;
@@ -10,6 +12,7 @@ export function useRealtime(baseUrl, collection, event, callback) {
10
12
  const realtime = new ZveltioRealtime(baseUrl);
11
13
  realtimeRef.current = realtime;
12
14
  realtime.connect();
15
+ // biome-ignore lint/suspicious/noExplicitAny: legacy any; tracked in hardening plan item H-01
13
16
  const unsub = realtime.subscribe(collection, (data) => {
14
17
  if (!event || data.event === event) {
15
18
  callbackRef.current(data);
@@ -1 +1 @@
1
- {"version":3,"file":"useRecord.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecord.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,SAAS,CAAC,CAAC,GAAG,GAAG,EAC/B,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC5B,UAAU,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA4BzC"}
1
+ {"version":3,"file":"useRecord.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecord.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,SAAS,CAAC,CAAC,GAAG,GAAG,EAC/B,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC5B,UAAU,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA4BzC"}
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect, useCallback } from 'react';
2
2
  import { fetchRecord } from '@zveltio/sdk';
3
3
  import { useZveltioClient } from '../context.js';
4
+ // biome-ignore lint/suspicious/noExplicitAny: legacy any; tracked in hardening plan item H-01
4
5
  export function useRecord(collectionName, id) {
5
6
  const client = useZveltioClient();
6
7
  const [data, setData] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"file":"useSyncCollection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSyncCollection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,GAAG,EACvC,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,wBAAwB,GACjC,UAAU,CAAC,CAAC,EAAE,CAAC,CAkCjB"}
1
+ {"version":3,"file":"useSyncCollection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSyncCollection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAGD,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,GAAG,EACvC,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,wBAAwB,GACjC,UAAU,CAAC,CAAC,EAAE,CAAC,CAuCjB"}
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect, useRef } from 'react';
2
2
  import { SyncManager } from '@zveltio/sdk';
3
3
  import { useZveltioClient } from '../context.js';
4
+ // biome-ignore lint/suspicious/noExplicitAny: legacy any; tracked in hardening plan item H-01
4
5
  export function useSyncCollection(collectionName, options) {
5
6
  const client = useZveltioClient();
6
7
  const [data, setData] = useState(null);
@@ -12,23 +13,30 @@ export function useSyncCollection(collectionName, options) {
12
13
  syncInterval: options?.syncInterval,
13
14
  });
14
15
  syncRef.current = sync;
16
+ let unsub;
17
+ let cancelled = false;
15
18
  sync
16
19
  .start(options?.realtimeUrl)
17
20
  .then(() => {
18
- const unsub = sync.collection(collectionName).subscribe((records) => {
21
+ if (cancelled)
22
+ return;
23
+ unsub = sync.collection(collectionName).subscribe((records) => {
19
24
  setData(records);
20
25
  setLoading(false);
21
26
  });
22
- return unsub;
23
27
  })
24
28
  .catch((err) => {
29
+ if (cancelled)
30
+ return;
25
31
  setError(err instanceof Error ? err : new Error(String(err)));
26
32
  setLoading(false);
27
33
  });
28
34
  return () => {
35
+ cancelled = true;
36
+ unsub?.();
29
37
  sync.stop();
30
38
  syncRef.current = null;
31
39
  };
32
- }, [client, collectionName]);
40
+ }, [client, collectionName, options?.realtimeUrl, options?.syncInterval]);
33
41
  return { data, loading, error };
34
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useSyncStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useSyncStatus.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,aAAa,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,cAAc,SAAO,GAAG,UAAU,CA+C1F"}
1
+ {"version":3,"file":"useSyncStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useSyncStatus.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,aAAa,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,cAAc,SAAO,GAAG,UAAU,CA8C1F"}
@@ -1,7 +1,5 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { useZveltioClient } from '../context.js';
3
2
  export function useSyncStatus(syncManager, pollIntervalMs = 2000) {
4
- const client = useZveltioClient();
5
3
  const [status, setStatus] = useState({
6
4
  status: 'online',
7
5
  pendingCount: 0,
package/dist/types.d.ts CHANGED
@@ -1,11 +1,4 @@
1
- export interface CollectionOptions {
2
- filter?: Record<string, any>;
3
- sort?: string;
4
- order?: 'asc' | 'desc';
5
- limit?: number;
6
- page?: number;
7
- search?: string;
8
- }
1
+ export type { CollectionOptions } from '@zveltio/sdk';
9
2
  export interface HookResult<T> {
10
3
  data: T | null;
11
4
  loading: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,YAAY,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,YAAY,EAAE,MAAM,CAAC;CACtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zveltio/react",
3
- "version": "3.0.0-beta.4",
3
+ "version": "3.0.0-beta.68",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -15,20 +15,21 @@
15
15
  "README.md"
16
16
  ],
17
17
  "scripts": {
18
- "build": "tsc",
18
+ "build": "tsc -p tsconfig.build.json",
19
19
  "dev": "tsc --watch",
20
- "typecheck": "tsc --noEmit"
20
+ "typecheck": "tsc --noEmit",
21
+ "test": "bun test"
21
22
  },
22
23
  "dependencies": {
23
- "@zveltio/sdk": "3.0.0-beta.4"
24
+ "@zveltio/sdk": "3.0.0-beta.68"
24
25
  },
25
26
  "peerDependencies": {
26
27
  "react": ">=18",
27
28
  "react-dom": ">=18"
28
29
  },
29
30
  "devDependencies": {
30
- "@types/react": "^18.0.0",
31
- "@types/react-dom": "^18.0.0",
31
+ "@types/react": "^18.3.31",
32
+ "@types/react-dom": "^18.3.7",
32
33
  "typescript": "^5.4.0"
33
34
  },
34
35
  "repository": {