fauxbase 0.5.0 → 0.5.2
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 +585 -30
- package/dist/index.cjs +34 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +34 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -5,7 +5,17 @@
|
|
|
5
5
|
Fauxbase is a frontend data layer that simulates your backend during development, then connects to your real API without changing your components.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
npm install fauxbase
|
|
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
|
|
9
19
|
```
|
|
10
20
|
|
|
11
21
|
---
|
|
@@ -59,7 +69,7 @@ That's it.
|
|
|
59
69
|
Fauxbase Fauxbase
|
|
60
70
|
↓ ↓
|
|
61
71
|
Local Driver HTTP Driver
|
|
62
|
-
(memory
|
|
72
|
+
(memory/localStorage/IndexedDB) ↓
|
|
63
73
|
Your Backend API
|
|
64
74
|
```
|
|
65
75
|
|
|
@@ -108,22 +118,52 @@ During development, it runs locally. When your backend is ready, it forwards the
|
|
|
108
118
|
- Entity system with decorators (`@field`, `@relation`, `@computed`)
|
|
109
119
|
- Service layer with lifecycle hooks (`@beforeCreate`, `@afterUpdate`, ...)
|
|
110
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`)
|
|
111
128
|
- Seed data with deterministic IDs
|
|
112
|
-
- Local driver (memory / localStorage)
|
|
113
|
-
- HTTP driver for real backends
|
|
129
|
+
- Local driver (memory / localStorage / IndexedDB)
|
|
130
|
+
- HTTP driver for real backends (with retry, timeout, error mapping)
|
|
114
131
|
- Hybrid mode for gradual migration
|
|
115
|
-
- Backend presets (Spring Boot, NestJS, Laravel, Django,
|
|
132
|
+
- Backend presets (Spring Boot, NestJS, Laravel, Django, Express)
|
|
133
|
+
- DevTools panel for inspecting data, auth, and requests
|
|
116
134
|
- Zero runtime dependencies (~8KB gzipped)
|
|
117
135
|
|
|
118
136
|
---
|
|
119
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
|
+
|
|
120
159
|
## Hybrid Mode
|
|
121
160
|
|
|
122
|
-
|
|
161
|
+
Migrate one service at a time:
|
|
123
162
|
|
|
124
163
|
```ts
|
|
125
164
|
const fb = createClient({
|
|
126
165
|
driver: { type: 'local' },
|
|
166
|
+
services: { product: ProductService, order: OrderService, cart: CartService },
|
|
127
167
|
|
|
128
168
|
overrides: {
|
|
129
169
|
product: { driver: { type: 'http', baseUrl: '/api', preset: 'spring-boot' } },
|
|
@@ -131,7 +171,87 @@ const fb = createClient({
|
|
|
131
171
|
});
|
|
132
172
|
```
|
|
133
173
|
|
|
134
|
-
Products use the real API.
|
|
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` |
|
|
135
255
|
|
|
136
256
|
---
|
|
137
257
|
|
|
@@ -224,6 +344,460 @@ Every service gets: `list`, `get`, `create`, `update`, `delete`, `count`, `bulk.
|
|
|
224
344
|
|
|
225
345
|
---
|
|
226
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
|
+
|
|
227
801
|
## Seeding
|
|
228
802
|
|
|
229
803
|
Seed data has deterministic IDs. Runtime data has UUIDs. They never collide.
|
|
@@ -242,26 +816,6 @@ const productSeed = seed(Product, [
|
|
|
242
816
|
|
|
243
817
|
---
|
|
244
818
|
|
|
245
|
-
## Backend Presets
|
|
246
|
-
|
|
247
|
-
Works with any REST backend. Presets tell the HTTP driver how to serialize queries and parse responses.
|
|
248
|
-
|
|
249
|
-
| Preset | Framework | Filter Style |
|
|
250
|
-
|--------|-----------|-------------|
|
|
251
|
-
| `lightwind` | Lightwind (Quarkus) | `?price__gte=100` |
|
|
252
|
-
| `spring-boot` | Spring Boot | `?price.gte=100` |
|
|
253
|
-
| `nestjs` | NestJS | `?filter.price.$gte=100` |
|
|
254
|
-
| `laravel` | Laravel | `?filter[price_gte]=100` |
|
|
255
|
-
| `django` | Django REST Framework | `?price__gte=100` |
|
|
256
|
-
| `express` | Express.js | `?price__gte=100` |
|
|
257
|
-
| `fastapi` | FastAPI | `?price__gte=100` |
|
|
258
|
-
| `rails` | Ruby on Rails | `?q[price_gteq]=100` |
|
|
259
|
-
| `go-gin` | Go (Gin) | `?price__gte=100` |
|
|
260
|
-
|
|
261
|
-
Custom presets supported via `definePreset()`.
|
|
262
|
-
|
|
263
|
-
---
|
|
264
|
-
|
|
265
819
|
## Migration Timeline
|
|
266
820
|
|
|
267
821
|
```
|
|
@@ -293,9 +847,10 @@ Week 6 "All APIs ready"
|
|
|
293
847
|
## Roadmap
|
|
294
848
|
|
|
295
849
|
- [x] **v0.1** — Core: Entity, Service, QueryEngine, LocalDriver, Seeds
|
|
296
|
-
- [
|
|
297
|
-
- [
|
|
298
|
-
- [
|
|
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
|
|
299
854
|
|
|
300
855
|
---
|
|
301
856
|
|