fauxbase-react 0.4.0 → 0.5.1
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 +859 -0
- package/dist/index.cjs +20 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
# Fauxbase
|
|
2
|
+
|
|
3
|
+
**Start with fake. Ship with real. Change nothing.**
|
|
4
|
+
|
|
5
|
+
Fauxbase is a frontend data layer that simulates your backend during development, then connects to your real API without changing your components.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install fauxbase # core
|
|
9
|
+
npm install fauxbase-react # react hooks (optional)
|
|
10
|
+
npm install fauxbase-vue # vue composables (optional)
|
|
11
|
+
npm install fauxbase-svelte # svelte stores (optional)
|
|
12
|
+
npm install fauxbase-devtools # devtools panel (optional)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or scaffold a new project instantly:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npx fauxbase-cli init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 15-Second Example
|
|
24
|
+
|
|
25
|
+
Define your data:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
class Product extends Entity {
|
|
29
|
+
@field({ required: true }) name!: string;
|
|
30
|
+
@field({ min: 0 }) price!: number;
|
|
31
|
+
@field({ default: 0 }) stock!: number;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use it:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const fb = createClient({
|
|
39
|
+
services: { product: ProductService },
|
|
40
|
+
seeds: [seed(Product, [
|
|
41
|
+
{ name: 'Hair Clay', price: 185000, stock: 50 },
|
|
42
|
+
{ name: 'Beard Oil', price: 125000, stock: 30 },
|
|
43
|
+
])],
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Filter, sort, paginate — all work locally
|
|
47
|
+
const result = await fb.product.list({
|
|
48
|
+
filter: { price__gte: 100000 },
|
|
49
|
+
sort: { field: 'price', direction: 'desc' },
|
|
50
|
+
page: 1, size: 20,
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
That's it.
|
|
55
|
+
|
|
56
|
+
- Works locally with fake data
|
|
57
|
+
- Switch to real backend later
|
|
58
|
+
- No component changes
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## How It Works
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Development Production
|
|
66
|
+
|
|
67
|
+
Your App Your App
|
|
68
|
+
↓ ↓
|
|
69
|
+
Fauxbase Fauxbase
|
|
70
|
+
↓ ↓
|
|
71
|
+
Local Driver HTTP Driver
|
|
72
|
+
(memory/localStorage/IndexedDB) ↓
|
|
73
|
+
Your Backend API
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Components always talk to Fauxbase. Fauxbase talks to a driver. The driver is swappable. Your components never know whether they're hitting localStorage or a REST API.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## The Problem
|
|
81
|
+
|
|
82
|
+
Every frontend project does this:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Step 1: Backend not ready → hardcode mock data
|
|
86
|
+
Step 2: Mock data grows → copy-paste everywhere
|
|
87
|
+
Step 3: Need filtering → hack together Array.filter()
|
|
88
|
+
Step 4: Need pagination → hack together Array.slice()
|
|
89
|
+
Step 5: Need auth → fake login with useState
|
|
90
|
+
Step 6: Backend arrives → rewrite everything
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Existing tools don't solve this:
|
|
94
|
+
|
|
95
|
+
| Tool | What it does | The gap |
|
|
96
|
+
|------|-------------|---------|
|
|
97
|
+
| **MSW** | Intercepts HTTP | No query engine, no auth, mocks transport not data |
|
|
98
|
+
| **json-server** | Fake REST from JSON | No query operators, no hooks, separate process |
|
|
99
|
+
| **MirageJS** | In-browser server | Limited operators, no typed entities, largely abandoned |
|
|
100
|
+
| **Zustand/Redux** | State management | No CRUD contract, no query engine, no migration path |
|
|
101
|
+
|
|
102
|
+
**Fauxbase removes the rewrite.**
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## The Key Idea
|
|
107
|
+
|
|
108
|
+
Fauxbase runs your backend contract in the browser.
|
|
109
|
+
|
|
110
|
+
Entities define your data model. Services define business logic. A query engine handles filtering, sorting, and pagination.
|
|
111
|
+
|
|
112
|
+
During development, it runs locally. When your backend is ready, it forwards the same calls to your API.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Core Features
|
|
117
|
+
|
|
118
|
+
- Entity system with decorators (`@field`, `@relation`, `@computed`)
|
|
119
|
+
- Service layer with lifecycle hooks (`@beforeCreate`, `@afterUpdate`, ...)
|
|
120
|
+
- 13 query operators (`eq`, `gte`, `contains`, `between`, `in`, ...)
|
|
121
|
+
- Auth simulation (`AuthService`, login/register/logout, role checks)
|
|
122
|
+
- Auto-injection of `createdById`/`updatedById` when authenticated
|
|
123
|
+
- React hooks (`useList`, `useGet`, `useMutation`, `useAuth`, `useEvent`)
|
|
124
|
+
- Vue 3 composables (same API as React hooks)
|
|
125
|
+
- Svelte stores (same API, uses `writable`/`readable`)
|
|
126
|
+
- Real-time events (EventBus, SSE, STOMP/WebSocket)
|
|
127
|
+
- CLI scaffolder (`npx fauxbase-cli init`)
|
|
128
|
+
- Seed data with deterministic IDs
|
|
129
|
+
- Local driver (memory / localStorage / IndexedDB)
|
|
130
|
+
- HTTP driver for real backends (with retry, timeout, error mapping)
|
|
131
|
+
- Hybrid mode for gradual migration
|
|
132
|
+
- Backend presets (Spring Boot, NestJS, Laravel, Django, Express)
|
|
133
|
+
- DevTools panel for inspecting data, auth, and requests
|
|
134
|
+
- Zero runtime dependencies (~8KB gzipped)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Switching to Real Backend
|
|
139
|
+
|
|
140
|
+
When your API is ready, change one line:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// Before: local driver (default)
|
|
144
|
+
const fb = createClient({
|
|
145
|
+
services: { product: ProductService },
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// After: HTTP driver
|
|
149
|
+
const fb = createClient({
|
|
150
|
+
driver: { type: 'http', baseUrl: '/api', preset: 'spring-boot' },
|
|
151
|
+
services: { product: ProductService },
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Every component stays the same. Every query operator maps to your backend's filter syntax.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Hybrid Mode
|
|
160
|
+
|
|
161
|
+
Migrate one service at a time:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const fb = createClient({
|
|
165
|
+
driver: { type: 'local' },
|
|
166
|
+
services: { product: ProductService, order: OrderService, cart: CartService },
|
|
167
|
+
|
|
168
|
+
overrides: {
|
|
169
|
+
product: { driver: { type: 'http', baseUrl: '/api', preset: 'spring-boot' } },
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Products use the real API. Orders and cart stay local. Migrate at your own pace.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Backend Presets
|
|
179
|
+
|
|
180
|
+
Presets tell the HTTP driver how to serialize queries and parse responses for your backend framework.
|
|
181
|
+
|
|
182
|
+
| Preset | Framework | Filter Style | Page Indexing |
|
|
183
|
+
|--------|-----------|-------------|---------------|
|
|
184
|
+
| `default` | Generic REST | `?price__gte=100` | 1-indexed |
|
|
185
|
+
| `spring-boot` | Spring Boot | `?price.gte=100` | 0-indexed |
|
|
186
|
+
| `nestjs` | NestJS | `?filter.price.$gte=100` | 1-indexed |
|
|
187
|
+
| `laravel` | Laravel | `?filter[price_gte]=100` | 1-indexed |
|
|
188
|
+
| `django` | Django REST | `?price__gte=100` | 1-indexed |
|
|
189
|
+
| `express` | Express.js | `?price__gte=100` | 1-indexed |
|
|
190
|
+
|
|
191
|
+
### Custom Presets
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { definePreset } from 'fauxbase';
|
|
195
|
+
|
|
196
|
+
const myPreset = definePreset({
|
|
197
|
+
name: 'my-backend',
|
|
198
|
+
response: {
|
|
199
|
+
single: (raw) => ({ data: raw.result }),
|
|
200
|
+
list: (raw) => ({ items: raw.results, meta: raw.pagination }),
|
|
201
|
+
error: (raw) => ({ error: raw.message, code: raw.code }),
|
|
202
|
+
},
|
|
203
|
+
meta: { page: 'page', size: 'limit', totalItems: 'total', totalPages: 'pages' },
|
|
204
|
+
query: {
|
|
205
|
+
filterStyle: 'django',
|
|
206
|
+
pageParam: 'page',
|
|
207
|
+
sizeParam: 'limit',
|
|
208
|
+
sortParam: 'order_by',
|
|
209
|
+
sortFormat: 'field,direction',
|
|
210
|
+
},
|
|
211
|
+
auth: {
|
|
212
|
+
loginUrl: '/auth/login',
|
|
213
|
+
registerUrl: '/auth/register',
|
|
214
|
+
tokenField: 'access_token',
|
|
215
|
+
userField: 'user',
|
|
216
|
+
headerFormat: 'Bearer {token}',
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const fb = createClient({
|
|
221
|
+
driver: { type: 'http', baseUrl: '/api', preset: myPreset },
|
|
222
|
+
services: { product: ProductService },
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## HTTP Driver Options
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
const fb = createClient({
|
|
232
|
+
driver: {
|
|
233
|
+
type: 'http',
|
|
234
|
+
baseUrl: 'https://api.example.com',
|
|
235
|
+
preset: 'spring-boot',
|
|
236
|
+
timeout: 10000, // 10s (default: 30s)
|
|
237
|
+
retry: { maxRetries: 3, baseDelay: 300 }, // exponential backoff for 5xx
|
|
238
|
+
headers: { 'X-API-Key': 'my-key' }, // custom headers on every request
|
|
239
|
+
},
|
|
240
|
+
services: { product: ProductService },
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Error mapping:
|
|
245
|
+
|
|
246
|
+
| HTTP Status | Fauxbase Error |
|
|
247
|
+
|-------------|---------------|
|
|
248
|
+
| 400, 422 | `ValidationError` |
|
|
249
|
+
| 401, 403 | `ForbiddenError` |
|
|
250
|
+
| 404 | `NotFoundError` |
|
|
251
|
+
| 409 | `ConflictError` |
|
|
252
|
+
| 5xx | `HttpError` (with retry) |
|
|
253
|
+
| Network failure | `NetworkError` |
|
|
254
|
+
| Timeout | `TimeoutError` |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## AI Prototypes → Production
|
|
259
|
+
|
|
260
|
+
Many AI-generated prototypes hardcode arrays:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const products = [
|
|
264
|
+
{ name: 'Hair Clay', price: 185000 },
|
|
265
|
+
{ name: 'Beard Oil', price: 125000 },
|
|
266
|
+
];
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
When the prototype becomes real, engineers must rewrite the data layer.
|
|
270
|
+
|
|
271
|
+
Fauxbase lets prototypes start with a real data contract:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Claude / Cursor prototype
|
|
275
|
+
↓
|
|
276
|
+
Fauxbase local driver (works immediately)
|
|
277
|
+
↓
|
|
278
|
+
Real backend later (no rewrite)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Query Engine — 13 Operators
|
|
284
|
+
|
|
285
|
+
Every operator works identically on local and HTTP drivers.
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
const result = await fb.product.list({
|
|
289
|
+
filter: {
|
|
290
|
+
price__gte: 100000,
|
|
291
|
+
name__contains: 'pomade',
|
|
292
|
+
categoryId__in: ['cat-1', 'cat-2'],
|
|
293
|
+
stock__between: [10, 100],
|
|
294
|
+
isActive: true,
|
|
295
|
+
},
|
|
296
|
+
sort: { field: 'price', direction: 'desc' },
|
|
297
|
+
page: 1,
|
|
298
|
+
size: 20,
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
| Operator | Syntax | Example |
|
|
303
|
+
|----------|--------|---------|
|
|
304
|
+
| `eq` | `field` or `field__eq` | `{ isActive: true }` |
|
|
305
|
+
| `ne` | `field__ne` | `{ status__ne: 'deleted' }` |
|
|
306
|
+
| `gt` | `field__gt` | `{ price__gt: 100 }` |
|
|
307
|
+
| `gte` | `field__gte` | `{ price__gte: 100 }` |
|
|
308
|
+
| `lt` | `field__lt` | `{ stock__lt: 10 }` |
|
|
309
|
+
| `lte` | `field__lte` | `{ stock__lte: 100 }` |
|
|
310
|
+
| `like` | `field__like` | `{ name__like: 'hair' }` |
|
|
311
|
+
| `contains` | `field__contains` | `{ name__contains: 'hair' }` |
|
|
312
|
+
| `startswith` | `field__startswith` | `{ name__startswith: 'ha' }` |
|
|
313
|
+
| `endswith` | `field__endswith` | `{ email__endswith: '@gmail.com' }` |
|
|
314
|
+
| `between` | `field__between` | `{ price__between: [100, 500] }` |
|
|
315
|
+
| `in` | `field__in` | `{ status__in: ['active', 'pending'] }` |
|
|
316
|
+
| `isnull` | `field__isnull` | `{ deletedAt__isnull: true }` |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Services & Hooks
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
class ProductService extends Service<Product> {
|
|
324
|
+
entity = Product;
|
|
325
|
+
endpoint = '/products';
|
|
326
|
+
|
|
327
|
+
@beforeCreate()
|
|
328
|
+
ensureUniqueName(data: Partial<Product>, existing: Product[]) {
|
|
329
|
+
if (existing.some(p => p.name === data.name)) {
|
|
330
|
+
throw new ConflictError(`Product "${data.name}" already exists`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async getByCategory(categoryId: string) {
|
|
335
|
+
return this.list({
|
|
336
|
+
filter: { categoryId, isActive: true },
|
|
337
|
+
sort: { field: 'name', direction: 'asc' },
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Every service gets: `list`, `get`, `create`, `update`, `delete`, `count`, `bulk.create`, `bulk.update`, `bulk.delete`.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Auth
|
|
348
|
+
|
|
349
|
+
Simulate login, registration, and role-based access during development. When your real auth backend is ready, the same API works over HTTP.
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
class User extends Entity {
|
|
353
|
+
@field({ required: true }) name!: string;
|
|
354
|
+
@field({ required: true }) email!: string;
|
|
355
|
+
@field({ required: true }) password!: string;
|
|
356
|
+
@field({ default: 'user' }) role!: string;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
class UserAuth extends AuthService<User> {
|
|
360
|
+
entity = User;
|
|
361
|
+
endpoint = '/users';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const fb = createClient({
|
|
365
|
+
services: { product: ProductService },
|
|
366
|
+
auth: UserAuth,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Register & login
|
|
370
|
+
await fb.auth.register({ name: 'Alice', email: 'alice@test.com', password: 'secret' });
|
|
371
|
+
await fb.auth.login({ email: 'alice@test.com', password: 'secret' });
|
|
372
|
+
|
|
373
|
+
// Check state
|
|
374
|
+
fb.auth.isLoggedIn; // true
|
|
375
|
+
fb.auth.currentUser; // { id, email }
|
|
376
|
+
fb.auth.hasRole('admin'); // false
|
|
377
|
+
fb.auth.token; // mock JWT (base64)
|
|
378
|
+
|
|
379
|
+
// Auto-injection — createdById/updatedById set automatically
|
|
380
|
+
const { data } = await fb.product.create({ name: 'Pomade', price: 150000 });
|
|
381
|
+
data.createdById; // → user's ID
|
|
382
|
+
data.createdByName; // → 'Alice'
|
|
383
|
+
|
|
384
|
+
fb.auth.logout();
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
With HTTP driver, `login()` and `register()` POST to the preset's auth endpoints. The token from the server response is injected into all subsequent requests as `Authorization: Bearer <token>`.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## React Hooks
|
|
392
|
+
|
|
393
|
+
`fauxbase-react` provides hooks that connect your React components to Fauxbase services.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
npm install fauxbase-react
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Setup
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
import { FauxbaseProvider } from 'fauxbase-react';
|
|
403
|
+
|
|
404
|
+
function App() {
|
|
405
|
+
return (
|
|
406
|
+
<FauxbaseProvider client={fb}>
|
|
407
|
+
<ProductList />
|
|
408
|
+
</FauxbaseProvider>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### useList — fetch collections
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
import { useList } from 'fauxbase-react';
|
|
417
|
+
|
|
418
|
+
function ProductList() {
|
|
419
|
+
const { items, loading, error, meta, refetch } = useList(fb.product, {
|
|
420
|
+
filter: { isActive: true },
|
|
421
|
+
sort: { field: 'price', direction: 'desc' },
|
|
422
|
+
page: 1,
|
|
423
|
+
size: 20,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
if (loading) return <p>Loading...</p>;
|
|
427
|
+
return items.map(p => <div key={p.id}>{p.name}</div>);
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Options: `enabled` (skip fetch), `refetchInterval` (polling in ms).
|
|
432
|
+
|
|
433
|
+
### useGet — fetch single record
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
import { useGet } from 'fauxbase-react';
|
|
437
|
+
|
|
438
|
+
function ProductDetail({ id }: { id: string }) {
|
|
439
|
+
const { data, loading, error } = useGet(fb.product, id);
|
|
440
|
+
if (loading) return <p>Loading...</p>;
|
|
441
|
+
return <h1>{data?.name}</h1>;
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Pass `null` as id to skip fetching.
|
|
446
|
+
|
|
447
|
+
### useMutation — create, update, delete
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
import { useMutation } from 'fauxbase-react';
|
|
451
|
+
|
|
452
|
+
function CreateProduct() {
|
|
453
|
+
const { create, loading, error } = useMutation(fb.product);
|
|
454
|
+
|
|
455
|
+
const handleSubmit = async () => {
|
|
456
|
+
await create({ name: 'New Product', price: 100000 });
|
|
457
|
+
// useList hooks on the same service auto-refetch
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Returns `{ create, update, remove, loading, error }`. Mutations automatically invalidate all `useList` subscribers on the same service.
|
|
463
|
+
|
|
464
|
+
### useAuth — auth state in React
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
import { useAuth } from 'fauxbase-react';
|
|
468
|
+
|
|
469
|
+
function LoginPage() {
|
|
470
|
+
const { user, isLoggedIn, login, logout, register, hasRole, loading } = useAuth();
|
|
471
|
+
|
|
472
|
+
const handleLogin = async () => {
|
|
473
|
+
await login({ email: 'alice@test.com', password: 'secret' });
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
if (isLoggedIn) return <p>Welcome, {user.email}</p>;
|
|
477
|
+
return <button onClick={handleLogin}>Login</button>;
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### useFauxbase — raw client access
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
import { useFauxbase } from 'fauxbase-react';
|
|
485
|
+
|
|
486
|
+
function Dashboard() {
|
|
487
|
+
const fb = useFauxbase();
|
|
488
|
+
// fb.product, fb.auth, etc.
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## DevTools
|
|
495
|
+
|
|
496
|
+
`fauxbase-devtools` provides a floating panel for inspecting your Fauxbase instance during development.
|
|
497
|
+
|
|
498
|
+
```
|
|
499
|
+
npm install fauxbase-devtools
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
```tsx
|
|
503
|
+
import { FauxbaseDevtools } from 'fauxbase-devtools';
|
|
504
|
+
|
|
505
|
+
function App() {
|
|
506
|
+
return (
|
|
507
|
+
<FauxbaseProvider client={fb}>
|
|
508
|
+
<ProductList />
|
|
509
|
+
{process.env.NODE_ENV === 'development' && (
|
|
510
|
+
<FauxbaseDevtools client={fb} />
|
|
511
|
+
)}
|
|
512
|
+
</FauxbaseProvider>
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Panels
|
|
518
|
+
|
|
519
|
+
| Panel | What it shows |
|
|
520
|
+
|-------|--------------|
|
|
521
|
+
| **Data** | Browse records per service |
|
|
522
|
+
| **Auth** | Current auth state + logout button |
|
|
523
|
+
| **Requests** | Chronological log of all service method calls with timing |
|
|
524
|
+
| **Seeds** | Reset seed data per resource (LocalDriver only) |
|
|
525
|
+
|
|
526
|
+
### Configuration
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
<FauxbaseDevtools
|
|
530
|
+
client={fb}
|
|
531
|
+
config={{
|
|
532
|
+
position: 'bottom-left', // default: 'bottom-right'
|
|
533
|
+
defaultOpen: true, // default: false
|
|
534
|
+
maxLogEntries: 200, // default: 100
|
|
535
|
+
}}
|
|
536
|
+
/>
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
The request logger uses `Proxy` to wrap service methods — zero changes to the Service class, zero overhead when devtools is not rendered.
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## IndexedDB Storage
|
|
544
|
+
|
|
545
|
+
For persistent local data that survives page refreshes without needing `localStorage` size limits:
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
const fb = createClient({
|
|
549
|
+
driver: { type: 'local', persist: 'indexeddb' },
|
|
550
|
+
services: { product: ProductService },
|
|
551
|
+
});
|
|
552
|
+
await fb.ready; // Wait for IndexedDB to load
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Options:
|
|
556
|
+
|
|
557
|
+
| Option | Default | Description |
|
|
558
|
+
|--------|---------|-------------|
|
|
559
|
+
| `persist` | `'memory'` | `'memory'`, `'localStorage'`, or `'indexeddb'` |
|
|
560
|
+
| `dbName` | `'fauxbase'` | Custom IndexedDB database name |
|
|
561
|
+
|
|
562
|
+
The IndexedDB backend uses a memory-cached, write-through strategy: all data is loaded into memory on init (behind `fb.ready`), then writes are synchronously reflected in memory and asynchronously persisted to IndexedDB. This means reads are always fast and the synchronous `StorageBackend` interface is preserved.
|
|
563
|
+
|
|
564
|
+
For memory and localStorage drivers, `fb.ready` resolves immediately.
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Vue Composables
|
|
569
|
+
|
|
570
|
+
`fauxbase-vue` provides Vue 3 Composition API equivalents of the React hooks.
|
|
571
|
+
|
|
572
|
+
```
|
|
573
|
+
npm install fauxbase-vue
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Setup
|
|
577
|
+
|
|
578
|
+
```ts
|
|
579
|
+
// main.ts
|
|
580
|
+
import { FauxbasePlugin } from 'fauxbase-vue';
|
|
581
|
+
import { fb } from './fauxbase';
|
|
582
|
+
|
|
583
|
+
const app = createApp(App);
|
|
584
|
+
app.use(FauxbasePlugin, { client: fb });
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Usage
|
|
588
|
+
|
|
589
|
+
```vue
|
|
590
|
+
<script setup>
|
|
591
|
+
import { useList, useMutation, useAuth } from 'fauxbase-vue';
|
|
592
|
+
|
|
593
|
+
const { items, loading, error, meta, refetch } = useList(fb.product, {
|
|
594
|
+
filter: { isActive: true },
|
|
595
|
+
sort: { field: 'price', direction: 'desc' },
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
const { create, update, remove } = useMutation(fb.product);
|
|
599
|
+
|
|
600
|
+
// items, loading, error, meta are Vue Ref<> values
|
|
601
|
+
</script>
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
All hooks: `useList`, `useGet`, `useMutation`, `useAuth`, `useFauxbase`.
|
|
605
|
+
|
|
606
|
+
Returns are `Ref<>` values — use `.value` in script, automatic unwrapping in templates. Mutations auto-invalidate `useList` subscribers.
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Svelte Stores
|
|
611
|
+
|
|
612
|
+
`fauxbase-svelte` provides Svelte store-based equivalents. Compatible with Svelte 4 and 5.
|
|
613
|
+
|
|
614
|
+
```
|
|
615
|
+
npm install fauxbase-svelte
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Setup
|
|
619
|
+
|
|
620
|
+
```svelte
|
|
621
|
+
<!-- +layout.svelte -->
|
|
622
|
+
<script>
|
|
623
|
+
import { setFauxbaseContext } from 'fauxbase-svelte';
|
|
624
|
+
import { fb } from './fauxbase';
|
|
625
|
+
|
|
626
|
+
setFauxbaseContext(fb);
|
|
627
|
+
</script>
|
|
628
|
+
|
|
629
|
+
<slot />
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### Usage
|
|
633
|
+
|
|
634
|
+
```svelte
|
|
635
|
+
<script>
|
|
636
|
+
import { useList, useMutation } from 'fauxbase-svelte';
|
|
637
|
+
|
|
638
|
+
const { items, loading, error } = useList(fb.product, {
|
|
639
|
+
filter: { isActive: true },
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
const { create } = useMutation(fb.product);
|
|
643
|
+
</script>
|
|
644
|
+
|
|
645
|
+
{#if $loading}
|
|
646
|
+
<p>Loading...</p>
|
|
647
|
+
{:else}
|
|
648
|
+
{#each $items as product}
|
|
649
|
+
<div>{product.name}</div>
|
|
650
|
+
{/each}
|
|
651
|
+
{/if}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
All hooks: `useList`, `useGet`, `useMutation`, `useAuth`, `useFauxbase`.
|
|
655
|
+
|
|
656
|
+
Returns are Svelte `Readable<>` stores — use `$store` syntax in templates. Mutations auto-invalidate `useList` subscribers.
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## Real-Time Events
|
|
661
|
+
|
|
662
|
+
Fauxbase includes an opt-in event system. Local mutations auto-emit events. For server-pushed events, connect via SSE or STOMP (WebSocket).
|
|
663
|
+
|
|
664
|
+
### Enable local events
|
|
665
|
+
|
|
666
|
+
```ts
|
|
667
|
+
const fb = createClient({
|
|
668
|
+
services: { todo: TodoService },
|
|
669
|
+
events: true,
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// Listen to events
|
|
673
|
+
fb._eventBus.on('todo', (event) => {
|
|
674
|
+
console.log(event.action, event.data); // 'created', { id, ... }
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// Or listen to all events
|
|
678
|
+
fb._eventBus.onAny((event) => {
|
|
679
|
+
console.log(event.resource, event.action);
|
|
680
|
+
});
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### SSE (Server-Sent Events)
|
|
684
|
+
|
|
685
|
+
```ts
|
|
686
|
+
const fb = createClient({
|
|
687
|
+
driver: { type: 'http', baseUrl: '/api' },
|
|
688
|
+
services: { todo: TodoService },
|
|
689
|
+
events: {
|
|
690
|
+
source: {
|
|
691
|
+
type: 'sse',
|
|
692
|
+
url: '/api/events',
|
|
693
|
+
eventMap: { 'todo-changed': 'todo' },
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
});
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### STOMP (WebSocket)
|
|
700
|
+
|
|
701
|
+
Requires `@stomp/stompjs` as a peer dependency.
|
|
702
|
+
|
|
703
|
+
```ts
|
|
704
|
+
const fb = createClient({
|
|
705
|
+
driver: { type: 'http', baseUrl: '/api' },
|
|
706
|
+
services: { todo: TodoService },
|
|
707
|
+
events: {
|
|
708
|
+
source: {
|
|
709
|
+
type: 'stomp',
|
|
710
|
+
brokerUrl: 'wss://api.example.com/ws',
|
|
711
|
+
subscriptions: { '/topic/todos': 'todo' },
|
|
712
|
+
},
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### Custom handlers
|
|
718
|
+
|
|
719
|
+
```ts
|
|
720
|
+
const fb = createClient({
|
|
721
|
+
services: { todo: TodoService },
|
|
722
|
+
events: {
|
|
723
|
+
handlers: {
|
|
724
|
+
todo: (event) => console.log('Todo changed:', event),
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### useEvent hook (React / Vue / Svelte)
|
|
731
|
+
|
|
732
|
+
```tsx
|
|
733
|
+
import { useEvent } from 'fauxbase-react'; // or fauxbase-vue, fauxbase-svelte
|
|
734
|
+
|
|
735
|
+
function TodoList() {
|
|
736
|
+
useEvent('todo', (event) => {
|
|
737
|
+
toast(`Todo ${event.action}!`);
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// Also accepts a service instance
|
|
741
|
+
useEvent(fb.todo, (event) => { ... });
|
|
742
|
+
}
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Auto-invalidation
|
|
746
|
+
|
|
747
|
+
Remote events (from SSE/STOMP) automatically trigger refetches in `useList`/`useGet` hooks. No manual wiring needed.
|
|
748
|
+
|
|
749
|
+
### Event type
|
|
750
|
+
|
|
751
|
+
```ts
|
|
752
|
+
interface FauxbaseEvent<T = any> {
|
|
753
|
+
action: 'created' | 'updated' | 'deleted' | 'bulkCreated' | 'bulkUpdated' | 'bulkDeleted';
|
|
754
|
+
resource: string;
|
|
755
|
+
data?: T;
|
|
756
|
+
id?: string;
|
|
757
|
+
ids?: string[];
|
|
758
|
+
timestamp: number;
|
|
759
|
+
source: 'local' | 'remote';
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Cleanup
|
|
764
|
+
|
|
765
|
+
```ts
|
|
766
|
+
fb.disconnect(); // Closes SSE/STOMP connection and clears all listeners
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
## CLI
|
|
772
|
+
|
|
773
|
+
`fauxbase-cli` scaffolds a new Fauxbase project with entities, services, seeds, and framework-specific setup.
|
|
774
|
+
|
|
775
|
+
```
|
|
776
|
+
npx fauxbase-cli init
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Interactive prompts
|
|
780
|
+
|
|
781
|
+
1. **Framework?** — React / Vue / Svelte / None
|
|
782
|
+
2. **Output directory?** — default `src/fauxbase/`
|
|
783
|
+
3. **Sample entity (Todo)?** — Y/N
|
|
784
|
+
4. **Authentication?** — Y/N
|
|
785
|
+
5. **Storage?** — memory / localStorage / IndexedDB
|
|
786
|
+
|
|
787
|
+
### Generated structure
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
src/fauxbase/
|
|
791
|
+
entities/todo.ts, user.ts
|
|
792
|
+
services/todo.ts, user.ts
|
|
793
|
+
seeds/todo.ts, user.ts
|
|
794
|
+
index.ts # createClient call
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
After scaffolding, the CLI prints the `npm install` command and framework-specific setup instructions.
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
## Seeding
|
|
802
|
+
|
|
803
|
+
Seed data has deterministic IDs. Runtime data has UUIDs. They never collide.
|
|
804
|
+
|
|
805
|
+
```ts
|
|
806
|
+
const productSeed = seed(Product, [
|
|
807
|
+
{ name: 'Hair Clay', price: 185000, stock: 50 }, // → seed:product:0
|
|
808
|
+
{ name: 'Beard Oil', price: 125000, stock: 30 }, // → seed:product:1
|
|
809
|
+
]);
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
- Seeds auto-apply on first load
|
|
813
|
+
- Fauxbase tracks a version hash — if seeds change, only seed records are re-applied
|
|
814
|
+
- Runtime records are never touched during re-seeding
|
|
815
|
+
- On HTTP driver, seeding is disabled — the backend owns the data
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
## Migration Timeline
|
|
820
|
+
|
|
821
|
+
```
|
|
822
|
+
Week 1 Install Fauxbase, define entities/services/seeds.
|
|
823
|
+
Build UI with local driver. Everything works.
|
|
824
|
+
|
|
825
|
+
Week 2-4 Build features at full speed.
|
|
826
|
+
No blocking on backend. No mock data spaghetti.
|
|
827
|
+
|
|
828
|
+
Week 5 "Products API is ready"
|
|
829
|
+
→ Switch products to HTTP driver (hybrid mode)
|
|
830
|
+
→ Zero component changes
|
|
831
|
+
|
|
832
|
+
Week 6 "All APIs ready"
|
|
833
|
+
→ Set VITE_API_URL → done
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
---
|
|
837
|
+
|
|
838
|
+
## Who This Is For
|
|
839
|
+
|
|
840
|
+
- Frontend teams waiting for backend APIs
|
|
841
|
+
- Solo devs building full-stack apps
|
|
842
|
+
- Prototypers using AI coding tools
|
|
843
|
+
- Teams building UI before backend is ready
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## Roadmap
|
|
848
|
+
|
|
849
|
+
- [x] **v0.1** — Core: Entity, Service, QueryEngine, LocalDriver, Seeds
|
|
850
|
+
- [x] **v0.2** — React hooks (`useList`, `useGet`, `useMutation`, `useAuth`) + Auth simulation
|
|
851
|
+
- [x] **v0.3** — HTTP Driver + Backend Presets + Hybrid Mode + DevTools
|
|
852
|
+
- [x] **v0.4** — IndexedDB + CLI (`npx fauxbase-cli init`) + Vue/Svelte adapters
|
|
853
|
+
- [x] **v0.5** — Real-Time Events (EventBus, SSE, STOMP) + `useEvent` hook + auto-invalidation
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
## License
|
|
858
|
+
|
|
859
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,16 @@ function FauxbaseProvider(props) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
+
const eventBus = props.client._eventBus;
|
|
30
|
+
if (eventBus) {
|
|
31
|
+
eventBus.onAny((event) => {
|
|
32
|
+
if (event.source !== "remote") return;
|
|
33
|
+
const svc = props.client[event.resource];
|
|
34
|
+
if (svc) {
|
|
35
|
+
value.invalidate(svc);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
29
39
|
return react.createElement(FauxbaseContext.Provider, { value }, props.children);
|
|
30
40
|
}
|
|
31
41
|
function useFauxbaseContext() {
|
|
@@ -182,6 +192,15 @@ function useMutation(service) {
|
|
|
182
192
|
}, [service, ctx]);
|
|
183
193
|
return { create, update, remove, loading, error };
|
|
184
194
|
}
|
|
195
|
+
function useEvent(serviceOrResource, handler) {
|
|
196
|
+
const ctx = useFauxbaseContext();
|
|
197
|
+
react.useEffect(() => {
|
|
198
|
+
const eventBus = ctx.client._eventBus;
|
|
199
|
+
if (!eventBus) return;
|
|
200
|
+
const resource = typeof serviceOrResource === "string" ? serviceOrResource : serviceOrResource.resourceName;
|
|
201
|
+
return eventBus.on(resource, handler);
|
|
202
|
+
}, [serviceOrResource, handler, ctx.client]);
|
|
203
|
+
}
|
|
185
204
|
function useAuth() {
|
|
186
205
|
const ctx = useFauxbaseContext();
|
|
187
206
|
const authService = ctx.client.auth;
|
|
@@ -245,6 +264,7 @@ function useAuth() {
|
|
|
245
264
|
exports.FauxbaseContext = FauxbaseContext;
|
|
246
265
|
exports.FauxbaseProvider = FauxbaseProvider;
|
|
247
266
|
exports.useAuth = useAuth;
|
|
267
|
+
exports.useEvent = useEvent;
|
|
248
268
|
exports.useFauxbase = useFauxbase;
|
|
249
269
|
exports.useFauxbaseContext = useFauxbaseContext;
|
|
250
270
|
exports.useGet = useGet;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/use-fauxbase.ts","../src/use-list.ts","../src/use-get.ts","../src/use-mutation.ts","../src/use-auth.ts"],"names":["createContext","createElement","useContext","useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, createElement, useContext } from 'react';\nimport type { Service } from 'fauxbase';\nimport type { FauxbaseContextValue } from './types';\n\n// --- Context ---\n\nexport const FauxbaseContext = createContext<FauxbaseContextValue | null>(null);\n\n// --- Provider ---\n\nexport function FauxbaseProvider(props: {\n client: any;\n children: React.ReactNode;\n}) {\n // Invalidation registry: Service → Set of refetch callbacks\n const registryRef = { current: new Map<Service<any>, Set<() => void>>() };\n\n const value: FauxbaseContextValue = {\n client: props.client,\n invalidate: (service: Service<any>) => {\n const subscribers = registryRef.current.get(service);\n if (subscribers) {\n for (const fn of subscribers) {\n fn();\n }\n }\n },\n subscribe: (service: Service<any>, refetchFn: () => void) => {\n if (!registryRef.current.has(service)) {\n registryRef.current.set(service, new Set());\n }\n registryRef.current.get(service)!.add(refetchFn);\n return () => {\n registryRef.current.get(service)?.delete(refetchFn);\n };\n },\n };\n\n return createElement(FauxbaseContext.Provider, { value }, props.children);\n}\n\n// --- Hook ---\n\nexport function useFauxbaseContext(): FauxbaseContextValue {\n const ctx = useContext(FauxbaseContext);\n if (!ctx) {\n throw new Error('useFauxbaseContext must be used within a FauxbaseProvider');\n }\n return ctx;\n}\n","import { useFauxbaseContext } from './context';\n\nexport function useFauxbase() {\n const ctx = useFauxbaseContext();\n return ctx.client;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity, QueryParams, PageMeta } from 'fauxbase';\nimport type { UseListResult, UseListOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useList<T extends Entity>(\n service: Service<T>,\n query?: QueryParams,\n options?: UseListOptions,\n): UseListResult<T> {\n const ctx = useFauxbaseContext();\n const [items, setItems] = useState<T[]>([]);\n const [meta, setMeta] = useState<PageMeta | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false;\n\n const fetch = useCallback(async () => {\n if (!enabled) return;\n setLoading(true);\n setError(null);\n try {\n const result = await service.list(query);\n if (mountedRef.current) {\n setItems(result.items);\n setMeta(result.meta);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, JSON.stringify(query), enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and query change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n // Refetch interval\n useEffect(() => {\n if (!options?.refetchInterval || !enabled) return;\n const id = setInterval(fetch, options.refetchInterval);\n return () => clearInterval(id);\n }, [fetch, options?.refetchInterval, enabled]);\n\n return { items, meta, loading, error, refetch: fetch };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseGetResult, UseGetOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useGet<T extends Entity>(\n service: Service<T>,\n id: string | null | undefined,\n options?: UseGetOptions,\n): UseGetResult<T> {\n const ctx = useFauxbaseContext();\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false && id != null;\n\n const fetch = useCallback(async () => {\n if (!enabled || !id) {\n setData(null);\n setLoading(false);\n return;\n }\n setLoading(true);\n setError(null);\n try {\n const result = await service.get(id);\n if (mountedRef.current) {\n setData(result.data);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setData(null);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, id, enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and id change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n return { data, loading, error, refetch: fetch };\n}\n","import { useState, useCallback } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseMutationResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useMutation<T extends Entity>(\n service: Service<T>,\n): UseMutationResult<T> {\n const ctx = useFauxbaseContext();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const create = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.create(data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const update = useCallback(async (id: string, data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.update(id, data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const remove = useCallback(async (id: string): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.delete(id);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n return { create, update, remove, loading, error };\n}\n","import { useState, useCallback } from 'react';\nimport type { Entity } from 'fauxbase';\nimport type { UseAuthResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useAuth<T extends Entity>(): UseAuthResult<T> {\n const ctx = useFauxbaseContext();\n const authService = ctx.client.auth;\n\n if (!authService) {\n throw new Error('useAuth requires auth to be configured in createClient');\n }\n\n const [, forceUpdate] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rerender = useCallback(() => forceUpdate(n => n + 1), []);\n\n const login = useCallback(async (credentials: { email: string; password: string }): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.login(credentials);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const register = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.register(data);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const logout = useCallback(() => {\n authService.logout();\n rerender();\n }, [authService, rerender]);\n\n const hasRole = useCallback((role: string): boolean => {\n return authService.hasRole(role);\n }, [authService]);\n\n return {\n user: authService.currentUser as T | null,\n isLoggedIn: authService.isLoggedIn,\n token: authService.token,\n login,\n register,\n logout,\n hasRole,\n loading,\n error,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/use-fauxbase.ts","../src/use-list.ts","../src/use-get.ts","../src/use-mutation.ts","../src/use-event.ts","../src/use-auth.ts"],"names":["createContext","createElement","useContext","useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, createElement, useContext } from 'react';\nimport type { Service, EventBus } from 'fauxbase';\nimport type { FauxbaseContextValue } from './types';\n\n// --- Context ---\n\nexport const FauxbaseContext = createContext<FauxbaseContextValue | null>(null);\n\n// --- Provider ---\n\nexport function FauxbaseProvider(props: {\n client: any;\n children: React.ReactNode;\n}) {\n // Invalidation registry: Service → Set of refetch callbacks\n const registryRef = { current: new Map<Service<any>, Set<() => void>>() };\n\n const value: FauxbaseContextValue = {\n client: props.client,\n invalidate: (service: Service<any>) => {\n const subscribers = registryRef.current.get(service);\n if (subscribers) {\n for (const fn of subscribers) {\n fn();\n }\n }\n },\n subscribe: (service: Service<any>, refetchFn: () => void) => {\n if (!registryRef.current.has(service)) {\n registryRef.current.set(service, new Set());\n }\n registryRef.current.get(service)!.add(refetchFn);\n return () => {\n registryRef.current.get(service)?.delete(refetchFn);\n };\n },\n };\n\n // Bridge remote events → auto-invalidation\n const eventBus: EventBus | undefined = props.client._eventBus;\n if (eventBus) {\n eventBus.onAny((event) => {\n if (event.source !== 'remote') return;\n const svc = props.client[event.resource];\n if (svc) {\n value.invalidate(svc);\n }\n });\n }\n\n return createElement(FauxbaseContext.Provider, { value }, props.children);\n}\n\n// --- Hook ---\n\nexport function useFauxbaseContext(): FauxbaseContextValue {\n const ctx = useContext(FauxbaseContext);\n if (!ctx) {\n throw new Error('useFauxbaseContext must be used within a FauxbaseProvider');\n }\n return ctx;\n}\n","import { useFauxbaseContext } from './context';\n\nexport function useFauxbase() {\n const ctx = useFauxbaseContext();\n return ctx.client;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity, QueryParams, PageMeta } from 'fauxbase';\nimport type { UseListResult, UseListOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useList<T extends Entity>(\n service: Service<T>,\n query?: QueryParams,\n options?: UseListOptions,\n): UseListResult<T> {\n const ctx = useFauxbaseContext();\n const [items, setItems] = useState<T[]>([]);\n const [meta, setMeta] = useState<PageMeta | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false;\n\n const fetch = useCallback(async () => {\n if (!enabled) return;\n setLoading(true);\n setError(null);\n try {\n const result = await service.list(query);\n if (mountedRef.current) {\n setItems(result.items);\n setMeta(result.meta);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, JSON.stringify(query), enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and query change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n // Refetch interval\n useEffect(() => {\n if (!options?.refetchInterval || !enabled) return;\n const id = setInterval(fetch, options.refetchInterval);\n return () => clearInterval(id);\n }, [fetch, options?.refetchInterval, enabled]);\n\n return { items, meta, loading, error, refetch: fetch };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseGetResult, UseGetOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useGet<T extends Entity>(\n service: Service<T>,\n id: string | null | undefined,\n options?: UseGetOptions,\n): UseGetResult<T> {\n const ctx = useFauxbaseContext();\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false && id != null;\n\n const fetch = useCallback(async () => {\n if (!enabled || !id) {\n setData(null);\n setLoading(false);\n return;\n }\n setLoading(true);\n setError(null);\n try {\n const result = await service.get(id);\n if (mountedRef.current) {\n setData(result.data);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setData(null);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, id, enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and id change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n return { data, loading, error, refetch: fetch };\n}\n","import { useState, useCallback } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseMutationResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useMutation<T extends Entity>(\n service: Service<T>,\n): UseMutationResult<T> {\n const ctx = useFauxbaseContext();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const create = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.create(data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const update = useCallback(async (id: string, data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.update(id, data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const remove = useCallback(async (id: string): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.delete(id);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n return { create, update, remove, loading, error };\n}\n","import { useEffect } from 'react';\nimport type { Service, Entity, EventBus, EventHandler } from 'fauxbase';\nimport { useFauxbaseContext } from './context';\n\nexport function useEvent<T extends Entity>(\n serviceOrResource: Service<T> | string,\n handler: EventHandler<T>,\n): void {\n const ctx = useFauxbaseContext();\n\n useEffect(() => {\n const eventBus: EventBus | undefined = ctx.client._eventBus;\n if (!eventBus) return;\n\n const resource = typeof serviceOrResource === 'string'\n ? serviceOrResource\n : (serviceOrResource as any).resourceName;\n\n return eventBus.on(resource, handler);\n }, [serviceOrResource, handler, ctx.client]);\n}\n","import { useState, useCallback } from 'react';\nimport type { Entity } from 'fauxbase';\nimport type { UseAuthResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useAuth<T extends Entity>(): UseAuthResult<T> {\n const ctx = useFauxbaseContext();\n const authService = ctx.client.auth;\n\n if (!authService) {\n throw new Error('useAuth requires auth to be configured in createClient');\n }\n\n const [, forceUpdate] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rerender = useCallback(() => forceUpdate(n => n + 1), []);\n\n const login = useCallback(async (credentials: { email: string; password: string }): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.login(credentials);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const register = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.register(data);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const logout = useCallback(() => {\n authService.logout();\n rerender();\n }, [authService, rerender]);\n\n const hasRole = useCallback((role: string): boolean => {\n return authService.hasRole(role);\n }, [authService]);\n\n return {\n user: authService.currentUser as T | null,\n isLoggedIn: authService.isLoggedIn,\n token: authService.token,\n login,\n register,\n logout,\n hasRole,\n loading,\n error,\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { Service, PageMeta, Entity, QueryParams } from 'fauxbase';
|
|
2
|
+
import { Service, PageMeta, Entity, QueryParams, EventHandler } from 'fauxbase';
|
|
3
3
|
|
|
4
4
|
interface UseListResult<T> {
|
|
5
5
|
items: T[];
|
|
@@ -63,6 +63,8 @@ declare function useGet<T extends Entity>(service: Service<T>, id: string | null
|
|
|
63
63
|
|
|
64
64
|
declare function useMutation<T extends Entity>(service: Service<T>): UseMutationResult<T>;
|
|
65
65
|
|
|
66
|
+
declare function useEvent<T extends Entity>(serviceOrResource: Service<T> | string, handler: EventHandler<T>): void;
|
|
67
|
+
|
|
66
68
|
declare function useAuth<T extends Entity>(): UseAuthResult<T>;
|
|
67
69
|
|
|
68
|
-
export { FauxbaseContext, type FauxbaseContextValue, FauxbaseProvider, type UseAuthResult, type UseGetOptions, type UseGetResult, type UseListOptions, type UseListResult, type UseMutationResult, useAuth, useFauxbase, useFauxbaseContext, useGet, useList, useMutation };
|
|
70
|
+
export { FauxbaseContext, type FauxbaseContextValue, FauxbaseProvider, type UseAuthResult, type UseGetOptions, type UseGetResult, type UseListOptions, type UseListResult, type UseMutationResult, useAuth, useEvent, useFauxbase, useFauxbaseContext, useGet, useList, useMutation };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { Service, PageMeta, Entity, QueryParams } from 'fauxbase';
|
|
2
|
+
import { Service, PageMeta, Entity, QueryParams, EventHandler } from 'fauxbase';
|
|
3
3
|
|
|
4
4
|
interface UseListResult<T> {
|
|
5
5
|
items: T[];
|
|
@@ -63,6 +63,8 @@ declare function useGet<T extends Entity>(service: Service<T>, id: string | null
|
|
|
63
63
|
|
|
64
64
|
declare function useMutation<T extends Entity>(service: Service<T>): UseMutationResult<T>;
|
|
65
65
|
|
|
66
|
+
declare function useEvent<T extends Entity>(serviceOrResource: Service<T> | string, handler: EventHandler<T>): void;
|
|
67
|
+
|
|
66
68
|
declare function useAuth<T extends Entity>(): UseAuthResult<T>;
|
|
67
69
|
|
|
68
|
-
export { FauxbaseContext, type FauxbaseContextValue, FauxbaseProvider, type UseAuthResult, type UseGetOptions, type UseGetResult, type UseListOptions, type UseListResult, type UseMutationResult, useAuth, useFauxbase, useFauxbaseContext, useGet, useList, useMutation };
|
|
70
|
+
export { FauxbaseContext, type FauxbaseContextValue, FauxbaseProvider, type UseAuthResult, type UseGetOptions, type UseGetResult, type UseListOptions, type UseListResult, type UseMutationResult, useAuth, useEvent, useFauxbase, useFauxbaseContext, useGet, useList, useMutation };
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,16 @@ function FauxbaseProvider(props) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
+
const eventBus = props.client._eventBus;
|
|
28
|
+
if (eventBus) {
|
|
29
|
+
eventBus.onAny((event) => {
|
|
30
|
+
if (event.source !== "remote") return;
|
|
31
|
+
const svc = props.client[event.resource];
|
|
32
|
+
if (svc) {
|
|
33
|
+
value.invalidate(svc);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
27
37
|
return createElement(FauxbaseContext.Provider, { value }, props.children);
|
|
28
38
|
}
|
|
29
39
|
function useFauxbaseContext() {
|
|
@@ -180,6 +190,15 @@ function useMutation(service) {
|
|
|
180
190
|
}, [service, ctx]);
|
|
181
191
|
return { create, update, remove, loading, error };
|
|
182
192
|
}
|
|
193
|
+
function useEvent(serviceOrResource, handler) {
|
|
194
|
+
const ctx = useFauxbaseContext();
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
const eventBus = ctx.client._eventBus;
|
|
197
|
+
if (!eventBus) return;
|
|
198
|
+
const resource = typeof serviceOrResource === "string" ? serviceOrResource : serviceOrResource.resourceName;
|
|
199
|
+
return eventBus.on(resource, handler);
|
|
200
|
+
}, [serviceOrResource, handler, ctx.client]);
|
|
201
|
+
}
|
|
183
202
|
function useAuth() {
|
|
184
203
|
const ctx = useFauxbaseContext();
|
|
185
204
|
const authService = ctx.client.auth;
|
|
@@ -240,6 +259,6 @@ function useAuth() {
|
|
|
240
259
|
};
|
|
241
260
|
}
|
|
242
261
|
|
|
243
|
-
export { FauxbaseContext, FauxbaseProvider, useAuth, useFauxbase, useFauxbaseContext, useGet, useList, useMutation };
|
|
262
|
+
export { FauxbaseContext, FauxbaseProvider, useAuth, useEvent, useFauxbase, useFauxbaseContext, useGet, useList, useMutation };
|
|
244
263
|
//# sourceMappingURL=index.js.map
|
|
245
264
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/use-fauxbase.ts","../src/use-list.ts","../src/use-get.ts","../src/use-mutation.ts","../src/use-auth.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, createElement, useContext } from 'react';\nimport type { Service } from 'fauxbase';\nimport type { FauxbaseContextValue } from './types';\n\n// --- Context ---\n\nexport const FauxbaseContext = createContext<FauxbaseContextValue | null>(null);\n\n// --- Provider ---\n\nexport function FauxbaseProvider(props: {\n client: any;\n children: React.ReactNode;\n}) {\n // Invalidation registry: Service → Set of refetch callbacks\n const registryRef = { current: new Map<Service<any>, Set<() => void>>() };\n\n const value: FauxbaseContextValue = {\n client: props.client,\n invalidate: (service: Service<any>) => {\n const subscribers = registryRef.current.get(service);\n if (subscribers) {\n for (const fn of subscribers) {\n fn();\n }\n }\n },\n subscribe: (service: Service<any>, refetchFn: () => void) => {\n if (!registryRef.current.has(service)) {\n registryRef.current.set(service, new Set());\n }\n registryRef.current.get(service)!.add(refetchFn);\n return () => {\n registryRef.current.get(service)?.delete(refetchFn);\n };\n },\n };\n\n return createElement(FauxbaseContext.Provider, { value }, props.children);\n}\n\n// --- Hook ---\n\nexport function useFauxbaseContext(): FauxbaseContextValue {\n const ctx = useContext(FauxbaseContext);\n if (!ctx) {\n throw new Error('useFauxbaseContext must be used within a FauxbaseProvider');\n }\n return ctx;\n}\n","import { useFauxbaseContext } from './context';\n\nexport function useFauxbase() {\n const ctx = useFauxbaseContext();\n return ctx.client;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity, QueryParams, PageMeta } from 'fauxbase';\nimport type { UseListResult, UseListOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useList<T extends Entity>(\n service: Service<T>,\n query?: QueryParams,\n options?: UseListOptions,\n): UseListResult<T> {\n const ctx = useFauxbaseContext();\n const [items, setItems] = useState<T[]>([]);\n const [meta, setMeta] = useState<PageMeta | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false;\n\n const fetch = useCallback(async () => {\n if (!enabled) return;\n setLoading(true);\n setError(null);\n try {\n const result = await service.list(query);\n if (mountedRef.current) {\n setItems(result.items);\n setMeta(result.meta);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, JSON.stringify(query), enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and query change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n // Refetch interval\n useEffect(() => {\n if (!options?.refetchInterval || !enabled) return;\n const id = setInterval(fetch, options.refetchInterval);\n return () => clearInterval(id);\n }, [fetch, options?.refetchInterval, enabled]);\n\n return { items, meta, loading, error, refetch: fetch };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseGetResult, UseGetOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useGet<T extends Entity>(\n service: Service<T>,\n id: string | null | undefined,\n options?: UseGetOptions,\n): UseGetResult<T> {\n const ctx = useFauxbaseContext();\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false && id != null;\n\n const fetch = useCallback(async () => {\n if (!enabled || !id) {\n setData(null);\n setLoading(false);\n return;\n }\n setLoading(true);\n setError(null);\n try {\n const result = await service.get(id);\n if (mountedRef.current) {\n setData(result.data);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setData(null);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, id, enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and id change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n return { data, loading, error, refetch: fetch };\n}\n","import { useState, useCallback } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseMutationResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useMutation<T extends Entity>(\n service: Service<T>,\n): UseMutationResult<T> {\n const ctx = useFauxbaseContext();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const create = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.create(data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const update = useCallback(async (id: string, data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.update(id, data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const remove = useCallback(async (id: string): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.delete(id);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n return { create, update, remove, loading, error };\n}\n","import { useState, useCallback } from 'react';\nimport type { Entity } from 'fauxbase';\nimport type { UseAuthResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useAuth<T extends Entity>(): UseAuthResult<T> {\n const ctx = useFauxbaseContext();\n const authService = ctx.client.auth;\n\n if (!authService) {\n throw new Error('useAuth requires auth to be configured in createClient');\n }\n\n const [, forceUpdate] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rerender = useCallback(() => forceUpdate(n => n + 1), []);\n\n const login = useCallback(async (credentials: { email: string; password: string }): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.login(credentials);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const register = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.register(data);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const logout = useCallback(() => {\n authService.logout();\n rerender();\n }, [authService, rerender]);\n\n const hasRole = useCallback((role: string): boolean => {\n return authService.hasRole(role);\n }, [authService]);\n\n return {\n user: authService.currentUser as T | null,\n isLoggedIn: authService.isLoggedIn,\n token: authService.token,\n login,\n register,\n logout,\n hasRole,\n loading,\n error,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/use-fauxbase.ts","../src/use-list.ts","../src/use-get.ts","../src/use-mutation.ts","../src/use-event.ts","../src/use-auth.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, createElement, useContext } from 'react';\nimport type { Service, EventBus } from 'fauxbase';\nimport type { FauxbaseContextValue } from './types';\n\n// --- Context ---\n\nexport const FauxbaseContext = createContext<FauxbaseContextValue | null>(null);\n\n// --- Provider ---\n\nexport function FauxbaseProvider(props: {\n client: any;\n children: React.ReactNode;\n}) {\n // Invalidation registry: Service → Set of refetch callbacks\n const registryRef = { current: new Map<Service<any>, Set<() => void>>() };\n\n const value: FauxbaseContextValue = {\n client: props.client,\n invalidate: (service: Service<any>) => {\n const subscribers = registryRef.current.get(service);\n if (subscribers) {\n for (const fn of subscribers) {\n fn();\n }\n }\n },\n subscribe: (service: Service<any>, refetchFn: () => void) => {\n if (!registryRef.current.has(service)) {\n registryRef.current.set(service, new Set());\n }\n registryRef.current.get(service)!.add(refetchFn);\n return () => {\n registryRef.current.get(service)?.delete(refetchFn);\n };\n },\n };\n\n // Bridge remote events → auto-invalidation\n const eventBus: EventBus | undefined = props.client._eventBus;\n if (eventBus) {\n eventBus.onAny((event) => {\n if (event.source !== 'remote') return;\n const svc = props.client[event.resource];\n if (svc) {\n value.invalidate(svc);\n }\n });\n }\n\n return createElement(FauxbaseContext.Provider, { value }, props.children);\n}\n\n// --- Hook ---\n\nexport function useFauxbaseContext(): FauxbaseContextValue {\n const ctx = useContext(FauxbaseContext);\n if (!ctx) {\n throw new Error('useFauxbaseContext must be used within a FauxbaseProvider');\n }\n return ctx;\n}\n","import { useFauxbaseContext } from './context';\n\nexport function useFauxbase() {\n const ctx = useFauxbaseContext();\n return ctx.client;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity, QueryParams, PageMeta } from 'fauxbase';\nimport type { UseListResult, UseListOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useList<T extends Entity>(\n service: Service<T>,\n query?: QueryParams,\n options?: UseListOptions,\n): UseListResult<T> {\n const ctx = useFauxbaseContext();\n const [items, setItems] = useState<T[]>([]);\n const [meta, setMeta] = useState<PageMeta | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false;\n\n const fetch = useCallback(async () => {\n if (!enabled) return;\n setLoading(true);\n setError(null);\n try {\n const result = await service.list(query);\n if (mountedRef.current) {\n setItems(result.items);\n setMeta(result.meta);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, JSON.stringify(query), enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and query change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n // Refetch interval\n useEffect(() => {\n if (!options?.refetchInterval || !enabled) return;\n const id = setInterval(fetch, options.refetchInterval);\n return () => clearInterval(id);\n }, [fetch, options?.refetchInterval, enabled]);\n\n return { items, meta, loading, error, refetch: fetch };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseGetResult, UseGetOptions } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useGet<T extends Entity>(\n service: Service<T>,\n id: string | null | undefined,\n options?: UseGetOptions,\n): UseGetResult<T> {\n const ctx = useFauxbaseContext();\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false && id != null;\n\n const fetch = useCallback(async () => {\n if (!enabled || !id) {\n setData(null);\n setLoading(false);\n return;\n }\n setLoading(true);\n setError(null);\n try {\n const result = await service.get(id);\n if (mountedRef.current) {\n setData(result.data);\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setData(null);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, [service, id, enabled]);\n\n // Subscribe for invalidation\n useEffect(() => {\n const unsub = ctx.subscribe(service, fetch);\n return unsub;\n }, [ctx, service, fetch]);\n\n // Fetch on mount and id change\n useEffect(() => {\n mountedRef.current = true;\n fetch();\n return () => { mountedRef.current = false; };\n }, [fetch]);\n\n return { data, loading, error, refetch: fetch };\n}\n","import { useState, useCallback } from 'react';\nimport type { Service, Entity } from 'fauxbase';\nimport type { UseMutationResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useMutation<T extends Entity>(\n service: Service<T>,\n): UseMutationResult<T> {\n const ctx = useFauxbaseContext();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const create = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.create(data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const update = useCallback(async (id: string, data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.update(id, data);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n const remove = useCallback(async (id: string): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const result = await service.delete(id);\n ctx.invalidate(service);\n return result.data;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [service, ctx]);\n\n return { create, update, remove, loading, error };\n}\n","import { useEffect } from 'react';\nimport type { Service, Entity, EventBus, EventHandler } from 'fauxbase';\nimport { useFauxbaseContext } from './context';\n\nexport function useEvent<T extends Entity>(\n serviceOrResource: Service<T> | string,\n handler: EventHandler<T>,\n): void {\n const ctx = useFauxbaseContext();\n\n useEffect(() => {\n const eventBus: EventBus | undefined = ctx.client._eventBus;\n if (!eventBus) return;\n\n const resource = typeof serviceOrResource === 'string'\n ? serviceOrResource\n : (serviceOrResource as any).resourceName;\n\n return eventBus.on(resource, handler);\n }, [serviceOrResource, handler, ctx.client]);\n}\n","import { useState, useCallback } from 'react';\nimport type { Entity } from 'fauxbase';\nimport type { UseAuthResult } from './types';\nimport { useFauxbaseContext } from './context';\n\nexport function useAuth<T extends Entity>(): UseAuthResult<T> {\n const ctx = useFauxbaseContext();\n const authService = ctx.client.auth;\n\n if (!authService) {\n throw new Error('useAuth requires auth to be configured in createClient');\n }\n\n const [, forceUpdate] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rerender = useCallback(() => forceUpdate(n => n + 1), []);\n\n const login = useCallback(async (credentials: { email: string; password: string }): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.login(credentials);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const register = useCallback(async (data: Partial<T>): Promise<T> => {\n setLoading(true);\n setError(null);\n try {\n const user = await authService.register(data);\n rerender();\n return user;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw e;\n } finally {\n setLoading(false);\n }\n }, [authService, rerender]);\n\n const logout = useCallback(() => {\n authService.logout();\n rerender();\n }, [authService, rerender]);\n\n const hasRole = useCallback((role: string): boolean => {\n return authService.hasRole(role);\n }, [authService]);\n\n return {\n user: authService.currentUser as T | null,\n isLoggedIn: authService.isLoggedIn,\n token: authService.token,\n login,\n register,\n logout,\n hasRole,\n loading,\n error,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fauxbase-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"require": "./dist/index.cjs"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"files": ["dist"],
|
|
15
|
+
"files": ["dist", "README.md"],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsup",
|
|
18
18
|
"test": "vitest run",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"react": ">=18",
|
|
25
|
-
"fauxbase": ">=0.
|
|
25
|
+
"fauxbase": ">=0.5.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"react": "^18.3.0",
|