@withvlibe/base-sdk 1.0.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.
- package/README.md +369 -0
- package/dist/VlibeBaseAuth-ChuzgzDl.d.mts +562 -0
- package/dist/VlibeBaseAuth-ChuzgzDl.d.ts +562 -0
- package/dist/index.d.mts +158 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.js +702 -0
- package/dist/index.mjs +673 -0
- package/dist/react.d.mts +96 -0
- package/dist/react.d.ts +96 -0
- package/dist/react.js +273 -0
- package/dist/react.mjs +245 -0
- package/package.json +73 -0
package/dist/react.d.mts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { j as BaseRecord, V as VlibeBaseDatabase, w as UseCollectionOptions, v as UseCollectionReturn, x as UseKVReturn, c as VlibeBaseAuth, y as UseAuthReturn } from './VlibeBaseAuth-ChuzgzDl.mjs';
|
|
2
|
+
export { z as UsePaymentsReturn, l as VlibeUser } from './VlibeBaseAuth-ChuzgzDl.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* React hook for working with a database collection
|
|
6
|
+
*
|
|
7
|
+
* @param db - VlibeBaseDatabase instance
|
|
8
|
+
* @param collection - Collection name
|
|
9
|
+
* @param options - Query options
|
|
10
|
+
* @returns Collection data and operations
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* function TodoList() {
|
|
15
|
+
* const { data, loading, insert, remove } = useCollection(db, 'todos', {
|
|
16
|
+
* orderBy: 'created_at',
|
|
17
|
+
* orderDirection: 'desc',
|
|
18
|
+
* realtime: true,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* if (loading) return <div>Loading...</div>;
|
|
22
|
+
*
|
|
23
|
+
* return (
|
|
24
|
+
* <ul>
|
|
25
|
+
* {data.map(todo => (
|
|
26
|
+
* <li key={todo.id}>
|
|
27
|
+
* {todo.title}
|
|
28
|
+
* <button onClick={() => remove(todo.id)}>Delete</button>
|
|
29
|
+
* </li>
|
|
30
|
+
* ))}
|
|
31
|
+
* </ul>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function useCollection<T extends BaseRecord = BaseRecord>(db: VlibeBaseDatabase, collection: string, options?: UseCollectionOptions): UseCollectionReturn<T>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* React hook for working with key-value storage
|
|
40
|
+
*
|
|
41
|
+
* @param db - VlibeBaseDatabase instance
|
|
42
|
+
* @param key - The key to read/write
|
|
43
|
+
* @returns KV data and operations
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* function Settings() {
|
|
48
|
+
* const { data, loading, set } = useKV<{ theme: string }>(db, 'user-settings');
|
|
49
|
+
*
|
|
50
|
+
* if (loading) return <div>Loading...</div>;
|
|
51
|
+
*
|
|
52
|
+
* return (
|
|
53
|
+
* <select
|
|
54
|
+
* value={data?.theme || 'light'}
|
|
55
|
+
* onChange={(e) => set({ ...data, theme: e.target.value })}
|
|
56
|
+
* >
|
|
57
|
+
* <option value="light">Light</option>
|
|
58
|
+
* <option value="dark">Dark</option>
|
|
59
|
+
* </select>
|
|
60
|
+
* );
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function useKV<T = unknown>(db: VlibeBaseDatabase, key: string): UseKVReturn<T>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* React hook for authentication
|
|
68
|
+
*
|
|
69
|
+
* @param auth - VlibeBaseAuth instance
|
|
70
|
+
* @param initialToken - Optional initial token (from SSO callback)
|
|
71
|
+
* @returns Auth state and operations
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* function App() {
|
|
76
|
+
* const { user, loading, login, logout, hasFeature } = useAuth(auth);
|
|
77
|
+
*
|
|
78
|
+
* if (loading) return <div>Loading...</div>;
|
|
79
|
+
*
|
|
80
|
+
* if (!user) {
|
|
81
|
+
* return <button onClick={() => login()}>Login</button>;
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* return (
|
|
85
|
+
* <div>
|
|
86
|
+
* <p>Welcome, {user.name}!</p>
|
|
87
|
+
* {hasFeature('premium') && <PremiumFeature />}
|
|
88
|
+
* <button onClick={() => logout()}>Logout</button>
|
|
89
|
+
* </div>
|
|
90
|
+
* );
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
declare function useAuth(auth: VlibeBaseAuth, initialToken?: string | null): UseAuthReturn;
|
|
95
|
+
|
|
96
|
+
export { BaseRecord, UseAuthReturn, UseCollectionOptions, UseCollectionReturn, UseKVReturn, useAuth, useCollection, useKV };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { j as BaseRecord, V as VlibeBaseDatabase, w as UseCollectionOptions, v as UseCollectionReturn, x as UseKVReturn, c as VlibeBaseAuth, y as UseAuthReturn } from './VlibeBaseAuth-ChuzgzDl.js';
|
|
2
|
+
export { z as UsePaymentsReturn, l as VlibeUser } from './VlibeBaseAuth-ChuzgzDl.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* React hook for working with a database collection
|
|
6
|
+
*
|
|
7
|
+
* @param db - VlibeBaseDatabase instance
|
|
8
|
+
* @param collection - Collection name
|
|
9
|
+
* @param options - Query options
|
|
10
|
+
* @returns Collection data and operations
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* function TodoList() {
|
|
15
|
+
* const { data, loading, insert, remove } = useCollection(db, 'todos', {
|
|
16
|
+
* orderBy: 'created_at',
|
|
17
|
+
* orderDirection: 'desc',
|
|
18
|
+
* realtime: true,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* if (loading) return <div>Loading...</div>;
|
|
22
|
+
*
|
|
23
|
+
* return (
|
|
24
|
+
* <ul>
|
|
25
|
+
* {data.map(todo => (
|
|
26
|
+
* <li key={todo.id}>
|
|
27
|
+
* {todo.title}
|
|
28
|
+
* <button onClick={() => remove(todo.id)}>Delete</button>
|
|
29
|
+
* </li>
|
|
30
|
+
* ))}
|
|
31
|
+
* </ul>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function useCollection<T extends BaseRecord = BaseRecord>(db: VlibeBaseDatabase, collection: string, options?: UseCollectionOptions): UseCollectionReturn<T>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* React hook for working with key-value storage
|
|
40
|
+
*
|
|
41
|
+
* @param db - VlibeBaseDatabase instance
|
|
42
|
+
* @param key - The key to read/write
|
|
43
|
+
* @returns KV data and operations
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* function Settings() {
|
|
48
|
+
* const { data, loading, set } = useKV<{ theme: string }>(db, 'user-settings');
|
|
49
|
+
*
|
|
50
|
+
* if (loading) return <div>Loading...</div>;
|
|
51
|
+
*
|
|
52
|
+
* return (
|
|
53
|
+
* <select
|
|
54
|
+
* value={data?.theme || 'light'}
|
|
55
|
+
* onChange={(e) => set({ ...data, theme: e.target.value })}
|
|
56
|
+
* >
|
|
57
|
+
* <option value="light">Light</option>
|
|
58
|
+
* <option value="dark">Dark</option>
|
|
59
|
+
* </select>
|
|
60
|
+
* );
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function useKV<T = unknown>(db: VlibeBaseDatabase, key: string): UseKVReturn<T>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* React hook for authentication
|
|
68
|
+
*
|
|
69
|
+
* @param auth - VlibeBaseAuth instance
|
|
70
|
+
* @param initialToken - Optional initial token (from SSO callback)
|
|
71
|
+
* @returns Auth state and operations
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* function App() {
|
|
76
|
+
* const { user, loading, login, logout, hasFeature } = useAuth(auth);
|
|
77
|
+
*
|
|
78
|
+
* if (loading) return <div>Loading...</div>;
|
|
79
|
+
*
|
|
80
|
+
* if (!user) {
|
|
81
|
+
* return <button onClick={() => login()}>Login</button>;
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* return (
|
|
85
|
+
* <div>
|
|
86
|
+
* <p>Welcome, {user.name}!</p>
|
|
87
|
+
* {hasFeature('premium') && <PremiumFeature />}
|
|
88
|
+
* <button onClick={() => logout()}>Logout</button>
|
|
89
|
+
* </div>
|
|
90
|
+
* );
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
declare function useAuth(auth: VlibeBaseAuth, initialToken?: string | null): UseAuthReturn;
|
|
95
|
+
|
|
96
|
+
export { BaseRecord, UseAuthReturn, UseCollectionOptions, UseCollectionReturn, UseKVReturn, useAuth, useCollection, useKV };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/react.ts
|
|
22
|
+
var react_exports = {};
|
|
23
|
+
__export(react_exports, {
|
|
24
|
+
useAuth: () => useAuth,
|
|
25
|
+
useCollection: () => useCollection,
|
|
26
|
+
useKV: () => useKV
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(react_exports);
|
|
29
|
+
|
|
30
|
+
// src/hooks/useCollection.ts
|
|
31
|
+
var import_react = require("react");
|
|
32
|
+
function useCollection(db, collection, options = {}) {
|
|
33
|
+
const [data, setData] = (0, import_react.useState)([]);
|
|
34
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
35
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
36
|
+
const { realtime = false, ...queryOptions } = options;
|
|
37
|
+
const refresh = (0, import_react.useCallback)(async () => {
|
|
38
|
+
setLoading(true);
|
|
39
|
+
setError(null);
|
|
40
|
+
try {
|
|
41
|
+
const result = await db.query(collection, queryOptions);
|
|
42
|
+
setData(result);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch data"));
|
|
45
|
+
} finally {
|
|
46
|
+
setLoading(false);
|
|
47
|
+
}
|
|
48
|
+
}, [db, collection, JSON.stringify(queryOptions)]);
|
|
49
|
+
const insert = (0, import_react.useCallback)(
|
|
50
|
+
async (doc) => {
|
|
51
|
+
try {
|
|
52
|
+
const result = await db.insert(collection, doc);
|
|
53
|
+
if (!realtime) {
|
|
54
|
+
setData((prev) => [result, ...prev]);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
setError(err instanceof Error ? err : new Error("Failed to insert"));
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[db, collection, realtime]
|
|
63
|
+
);
|
|
64
|
+
const update = (0, import_react.useCallback)(
|
|
65
|
+
async (id, updates) => {
|
|
66
|
+
try {
|
|
67
|
+
const result = await db.update(collection, id, updates);
|
|
68
|
+
if (!realtime) {
|
|
69
|
+
setData(
|
|
70
|
+
(prev) => prev.map((item) => item.id === id ? result : item)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
setError(err instanceof Error ? err : new Error("Failed to update"));
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[db, collection, realtime]
|
|
80
|
+
);
|
|
81
|
+
const remove = (0, import_react.useCallback)(
|
|
82
|
+
async (id) => {
|
|
83
|
+
try {
|
|
84
|
+
await db.delete(collection, id);
|
|
85
|
+
if (!realtime) {
|
|
86
|
+
setData((prev) => prev.filter((item) => item.id !== id));
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
setError(err instanceof Error ? err : new Error("Failed to delete"));
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[db, collection, realtime]
|
|
95
|
+
);
|
|
96
|
+
(0, import_react.useEffect)(() => {
|
|
97
|
+
refresh();
|
|
98
|
+
}, [refresh]);
|
|
99
|
+
(0, import_react.useEffect)(() => {
|
|
100
|
+
if (!realtime) return;
|
|
101
|
+
const subscription = db.subscribe(
|
|
102
|
+
collection,
|
|
103
|
+
(payload) => {
|
|
104
|
+
switch (payload.eventType) {
|
|
105
|
+
case "INSERT":
|
|
106
|
+
if (payload.new) {
|
|
107
|
+
setData((prev) => [payload.new, ...prev]);
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
case "UPDATE":
|
|
111
|
+
if (payload.new) {
|
|
112
|
+
setData(
|
|
113
|
+
(prev) => prev.map(
|
|
114
|
+
(item) => item.id === payload.new.id ? payload.new : item
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case "DELETE":
|
|
120
|
+
if (payload.old) {
|
|
121
|
+
setData(
|
|
122
|
+
(prev) => prev.filter((item) => item.id !== payload.old.id)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
return () => {
|
|
130
|
+
subscription.unsubscribe();
|
|
131
|
+
};
|
|
132
|
+
}, [db, collection, realtime]);
|
|
133
|
+
return {
|
|
134
|
+
data,
|
|
135
|
+
loading,
|
|
136
|
+
error,
|
|
137
|
+
refresh,
|
|
138
|
+
insert,
|
|
139
|
+
update,
|
|
140
|
+
remove
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/hooks/useKV.ts
|
|
145
|
+
var import_react2 = require("react");
|
|
146
|
+
function useKV(db, key) {
|
|
147
|
+
const [data, setData] = (0, import_react2.useState)(null);
|
|
148
|
+
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
149
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
150
|
+
const refresh = (0, import_react2.useCallback)(async () => {
|
|
151
|
+
setLoading(true);
|
|
152
|
+
setError(null);
|
|
153
|
+
try {
|
|
154
|
+
const result = await db.getKV(key);
|
|
155
|
+
setData(result);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch KV"));
|
|
158
|
+
} finally {
|
|
159
|
+
setLoading(false);
|
|
160
|
+
}
|
|
161
|
+
}, [db, key]);
|
|
162
|
+
const set = (0, import_react2.useCallback)(
|
|
163
|
+
async (value) => {
|
|
164
|
+
try {
|
|
165
|
+
await db.setKV(key, value);
|
|
166
|
+
setData(value);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
setError(err instanceof Error ? err : new Error("Failed to set KV"));
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
[db, key]
|
|
173
|
+
);
|
|
174
|
+
(0, import_react2.useEffect)(() => {
|
|
175
|
+
refresh();
|
|
176
|
+
}, [refresh]);
|
|
177
|
+
return {
|
|
178
|
+
data,
|
|
179
|
+
loading,
|
|
180
|
+
error,
|
|
181
|
+
set,
|
|
182
|
+
refresh
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/hooks/useAuth.ts
|
|
187
|
+
var import_react3 = require("react");
|
|
188
|
+
function useAuth(auth, initialToken) {
|
|
189
|
+
const [user, setUser] = (0, import_react3.useState)(null);
|
|
190
|
+
const [loading, setLoading] = (0, import_react3.useState)(true);
|
|
191
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
192
|
+
(0, import_react3.useEffect)(() => {
|
|
193
|
+
const verifySession = async () => {
|
|
194
|
+
setLoading(true);
|
|
195
|
+
setError(null);
|
|
196
|
+
try {
|
|
197
|
+
let token = initialToken;
|
|
198
|
+
if (!token && typeof window !== "undefined") {
|
|
199
|
+
const params = new URLSearchParams(window.location.search);
|
|
200
|
+
token = params.get("vlibe_token");
|
|
201
|
+
}
|
|
202
|
+
if (!token && typeof window !== "undefined") {
|
|
203
|
+
token = localStorage.getItem("vlibe_base_token");
|
|
204
|
+
}
|
|
205
|
+
if (token) {
|
|
206
|
+
const verifiedUser = await auth.verifySession(token);
|
|
207
|
+
if (verifiedUser) {
|
|
208
|
+
setUser(verifiedUser);
|
|
209
|
+
if (typeof window !== "undefined") {
|
|
210
|
+
localStorage.setItem("vlibe_base_token", token);
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
if (typeof window !== "undefined") {
|
|
214
|
+
localStorage.removeItem("vlibe_base_token");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} catch (err) {
|
|
219
|
+
setError(err instanceof Error ? err : new Error("Auth verification failed"));
|
|
220
|
+
} finally {
|
|
221
|
+
setLoading(false);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
verifySession();
|
|
225
|
+
}, [auth, initialToken]);
|
|
226
|
+
const login = (0, import_react3.useCallback)(
|
|
227
|
+
(redirectPath) => {
|
|
228
|
+
const currentPath = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
229
|
+
const loginUrl = auth.getLoginUrl(redirectPath || currentPath);
|
|
230
|
+
if (typeof window !== "undefined") {
|
|
231
|
+
window.location.href = loginUrl;
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
[auth]
|
|
235
|
+
);
|
|
236
|
+
const logout = (0, import_react3.useCallback)(
|
|
237
|
+
(redirectPath) => {
|
|
238
|
+
if (typeof window !== "undefined") {
|
|
239
|
+
localStorage.removeItem("vlibe_base_token");
|
|
240
|
+
}
|
|
241
|
+
setUser(null);
|
|
242
|
+
const logoutUrl = auth.getLogoutUrl(redirectPath || "/");
|
|
243
|
+
if (typeof window !== "undefined") {
|
|
244
|
+
window.location.href = logoutUrl;
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
[auth]
|
|
248
|
+
);
|
|
249
|
+
const hasFeature = (0, import_react3.useCallback)(
|
|
250
|
+
(feature) => {
|
|
251
|
+
return auth.hasFeature(user, feature);
|
|
252
|
+
},
|
|
253
|
+
[auth, user]
|
|
254
|
+
);
|
|
255
|
+
const hasSubscription = (0, import_react3.useCallback)(() => {
|
|
256
|
+
return auth.hasSubscription(user);
|
|
257
|
+
}, [auth, user]);
|
|
258
|
+
return {
|
|
259
|
+
user,
|
|
260
|
+
loading,
|
|
261
|
+
error,
|
|
262
|
+
login,
|
|
263
|
+
logout,
|
|
264
|
+
hasFeature,
|
|
265
|
+
hasSubscription
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
269
|
+
0 && (module.exports = {
|
|
270
|
+
useAuth,
|
|
271
|
+
useCollection,
|
|
272
|
+
useKV
|
|
273
|
+
});
|
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/useCollection.ts
|
|
4
|
+
import { useState, useEffect, useCallback } from "react";
|
|
5
|
+
function useCollection(db, collection, options = {}) {
|
|
6
|
+
const [data, setData] = useState([]);
|
|
7
|
+
const [loading, setLoading] = useState(true);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const { realtime = false, ...queryOptions } = options;
|
|
10
|
+
const refresh = useCallback(async () => {
|
|
11
|
+
setLoading(true);
|
|
12
|
+
setError(null);
|
|
13
|
+
try {
|
|
14
|
+
const result = await db.query(collection, queryOptions);
|
|
15
|
+
setData(result);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch data"));
|
|
18
|
+
} finally {
|
|
19
|
+
setLoading(false);
|
|
20
|
+
}
|
|
21
|
+
}, [db, collection, JSON.stringify(queryOptions)]);
|
|
22
|
+
const insert = useCallback(
|
|
23
|
+
async (doc) => {
|
|
24
|
+
try {
|
|
25
|
+
const result = await db.insert(collection, doc);
|
|
26
|
+
if (!realtime) {
|
|
27
|
+
setData((prev) => [result, ...prev]);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
setError(err instanceof Error ? err : new Error("Failed to insert"));
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
[db, collection, realtime]
|
|
36
|
+
);
|
|
37
|
+
const update = useCallback(
|
|
38
|
+
async (id, updates) => {
|
|
39
|
+
try {
|
|
40
|
+
const result = await db.update(collection, id, updates);
|
|
41
|
+
if (!realtime) {
|
|
42
|
+
setData(
|
|
43
|
+
(prev) => prev.map((item) => item.id === id ? result : item)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
setError(err instanceof Error ? err : new Error("Failed to update"));
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[db, collection, realtime]
|
|
53
|
+
);
|
|
54
|
+
const remove = useCallback(
|
|
55
|
+
async (id) => {
|
|
56
|
+
try {
|
|
57
|
+
await db.delete(collection, id);
|
|
58
|
+
if (!realtime) {
|
|
59
|
+
setData((prev) => prev.filter((item) => item.id !== id));
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
setError(err instanceof Error ? err : new Error("Failed to delete"));
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
[db, collection, realtime]
|
|
68
|
+
);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
refresh();
|
|
71
|
+
}, [refresh]);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!realtime) return;
|
|
74
|
+
const subscription = db.subscribe(
|
|
75
|
+
collection,
|
|
76
|
+
(payload) => {
|
|
77
|
+
switch (payload.eventType) {
|
|
78
|
+
case "INSERT":
|
|
79
|
+
if (payload.new) {
|
|
80
|
+
setData((prev) => [payload.new, ...prev]);
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
case "UPDATE":
|
|
84
|
+
if (payload.new) {
|
|
85
|
+
setData(
|
|
86
|
+
(prev) => prev.map(
|
|
87
|
+
(item) => item.id === payload.new.id ? payload.new : item
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
case "DELETE":
|
|
93
|
+
if (payload.old) {
|
|
94
|
+
setData(
|
|
95
|
+
(prev) => prev.filter((item) => item.id !== payload.old.id)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
return () => {
|
|
103
|
+
subscription.unsubscribe();
|
|
104
|
+
};
|
|
105
|
+
}, [db, collection, realtime]);
|
|
106
|
+
return {
|
|
107
|
+
data,
|
|
108
|
+
loading,
|
|
109
|
+
error,
|
|
110
|
+
refresh,
|
|
111
|
+
insert,
|
|
112
|
+
update,
|
|
113
|
+
remove
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/hooks/useKV.ts
|
|
118
|
+
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
|
|
119
|
+
function useKV(db, key) {
|
|
120
|
+
const [data, setData] = useState2(null);
|
|
121
|
+
const [loading, setLoading] = useState2(true);
|
|
122
|
+
const [error, setError] = useState2(null);
|
|
123
|
+
const refresh = useCallback2(async () => {
|
|
124
|
+
setLoading(true);
|
|
125
|
+
setError(null);
|
|
126
|
+
try {
|
|
127
|
+
const result = await db.getKV(key);
|
|
128
|
+
setData(result);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch KV"));
|
|
131
|
+
} finally {
|
|
132
|
+
setLoading(false);
|
|
133
|
+
}
|
|
134
|
+
}, [db, key]);
|
|
135
|
+
const set = useCallback2(
|
|
136
|
+
async (value) => {
|
|
137
|
+
try {
|
|
138
|
+
await db.setKV(key, value);
|
|
139
|
+
setData(value);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
setError(err instanceof Error ? err : new Error("Failed to set KV"));
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
[db, key]
|
|
146
|
+
);
|
|
147
|
+
useEffect2(() => {
|
|
148
|
+
refresh();
|
|
149
|
+
}, [refresh]);
|
|
150
|
+
return {
|
|
151
|
+
data,
|
|
152
|
+
loading,
|
|
153
|
+
error,
|
|
154
|
+
set,
|
|
155
|
+
refresh
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/hooks/useAuth.ts
|
|
160
|
+
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3 } from "react";
|
|
161
|
+
function useAuth(auth, initialToken) {
|
|
162
|
+
const [user, setUser] = useState3(null);
|
|
163
|
+
const [loading, setLoading] = useState3(true);
|
|
164
|
+
const [error, setError] = useState3(null);
|
|
165
|
+
useEffect3(() => {
|
|
166
|
+
const verifySession = async () => {
|
|
167
|
+
setLoading(true);
|
|
168
|
+
setError(null);
|
|
169
|
+
try {
|
|
170
|
+
let token = initialToken;
|
|
171
|
+
if (!token && typeof window !== "undefined") {
|
|
172
|
+
const params = new URLSearchParams(window.location.search);
|
|
173
|
+
token = params.get("vlibe_token");
|
|
174
|
+
}
|
|
175
|
+
if (!token && typeof window !== "undefined") {
|
|
176
|
+
token = localStorage.getItem("vlibe_base_token");
|
|
177
|
+
}
|
|
178
|
+
if (token) {
|
|
179
|
+
const verifiedUser = await auth.verifySession(token);
|
|
180
|
+
if (verifiedUser) {
|
|
181
|
+
setUser(verifiedUser);
|
|
182
|
+
if (typeof window !== "undefined") {
|
|
183
|
+
localStorage.setItem("vlibe_base_token", token);
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
if (typeof window !== "undefined") {
|
|
187
|
+
localStorage.removeItem("vlibe_base_token");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch (err) {
|
|
192
|
+
setError(err instanceof Error ? err : new Error("Auth verification failed"));
|
|
193
|
+
} finally {
|
|
194
|
+
setLoading(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
verifySession();
|
|
198
|
+
}, [auth, initialToken]);
|
|
199
|
+
const login = useCallback3(
|
|
200
|
+
(redirectPath) => {
|
|
201
|
+
const currentPath = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
202
|
+
const loginUrl = auth.getLoginUrl(redirectPath || currentPath);
|
|
203
|
+
if (typeof window !== "undefined") {
|
|
204
|
+
window.location.href = loginUrl;
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
[auth]
|
|
208
|
+
);
|
|
209
|
+
const logout = useCallback3(
|
|
210
|
+
(redirectPath) => {
|
|
211
|
+
if (typeof window !== "undefined") {
|
|
212
|
+
localStorage.removeItem("vlibe_base_token");
|
|
213
|
+
}
|
|
214
|
+
setUser(null);
|
|
215
|
+
const logoutUrl = auth.getLogoutUrl(redirectPath || "/");
|
|
216
|
+
if (typeof window !== "undefined") {
|
|
217
|
+
window.location.href = logoutUrl;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
[auth]
|
|
221
|
+
);
|
|
222
|
+
const hasFeature = useCallback3(
|
|
223
|
+
(feature) => {
|
|
224
|
+
return auth.hasFeature(user, feature);
|
|
225
|
+
},
|
|
226
|
+
[auth, user]
|
|
227
|
+
);
|
|
228
|
+
const hasSubscription = useCallback3(() => {
|
|
229
|
+
return auth.hasSubscription(user);
|
|
230
|
+
}, [auth, user]);
|
|
231
|
+
return {
|
|
232
|
+
user,
|
|
233
|
+
loading,
|
|
234
|
+
error,
|
|
235
|
+
login,
|
|
236
|
+
logout,
|
|
237
|
+
hasFeature,
|
|
238
|
+
hasSubscription
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
export {
|
|
242
|
+
useAuth,
|
|
243
|
+
useCollection,
|
|
244
|
+
useKV
|
|
245
|
+
};
|