@datafn/client 0.0.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 +199 -0
- package/dist/index.cjs +1216 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +248 -0
- package/dist/index.d.ts +248 -0
- package/dist/index.js +1183 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# @datafn/client
|
|
2
|
+
|
|
3
|
+
A comprehensive, offline-first client for DataFn with reactive signals, event bus, and synchronization support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @datafn/client @datafn/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Fluent Table API**: Ergonomic interface for querying and mutating specific resources.
|
|
14
|
+
- **Reactive Signals**: Subscribable data sources that automatically update on changes (perfect for UI binding).
|
|
15
|
+
- **Offline Storage**: Built-in support for persistent storage (IndexedDB, Memory) with hydration.
|
|
16
|
+
- **Synchronization**: Integrated `clone`, `pull`, and `push` mechanisms that automatically update local storage.
|
|
17
|
+
- **Event Bus**: Global event system for mutations, sync lifecycle, and error handling.
|
|
18
|
+
- **Transaction Support**: Atomic operations across multiple resources.
|
|
19
|
+
- **Plugins**: Extensible architecture for custom behavior.
|
|
20
|
+
- **Type-Safe**: Built with TypeScript for full type inference.
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { createDatafnClient, IndexedDbStorageAdapter } from "@datafn/client";
|
|
26
|
+
|
|
27
|
+
// 1. Configure the client
|
|
28
|
+
const client = createDatafnClient({
|
|
29
|
+
clientId: "device-uuid-123", // Required for offline/sync
|
|
30
|
+
schema: mySchema, // Your DataFn schema definition
|
|
31
|
+
storage: new IndexedDbStorageAdapter("my-app-db"), // Persist data locally
|
|
32
|
+
remote: {
|
|
33
|
+
// Your network layer (fetch, axios, etc.) to talk to DataFn server
|
|
34
|
+
async query(q) { /* ... */ },
|
|
35
|
+
async mutation(m) { /* ... */ },
|
|
36
|
+
async pull(p) { /* ... */ },
|
|
37
|
+
// ... other methods
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 2. Work with a specific table (Resource)
|
|
42
|
+
const tasks = client.table("task");
|
|
43
|
+
|
|
44
|
+
// 3. Create a reactive signal (Auto-updates when data changes)
|
|
45
|
+
const activeTasksSignal = tasks.signal({
|
|
46
|
+
select: ["id", "title"],
|
|
47
|
+
filters: { completed: false }
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Subscribe to changes
|
|
51
|
+
const unsubscribe = activeTasksSignal.subscribe((data) => {
|
|
52
|
+
console.log("Active tasks:", data);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 4. Mutate data (Optimistic updates & Event emission)
|
|
56
|
+
await tasks.mutate({
|
|
57
|
+
operation: "insert",
|
|
58
|
+
record: { title: "New Task", completed: false }
|
|
59
|
+
});
|
|
60
|
+
// -> activeTasksSignal listeners are automatically notified!
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Core Concepts
|
|
64
|
+
|
|
65
|
+
### 1. The Client Instance
|
|
66
|
+
|
|
67
|
+
The client is the central hub. It coordinates the **Schema**, **Storage**, **Remote** adapter, and **Event Bus**.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const client = createDatafnClient({
|
|
71
|
+
schema: DatafnSchema;
|
|
72
|
+
remote: DatafnRemoteAdapter;
|
|
73
|
+
storage?: DatafnStorageAdapter; // Optional: Enable offline mode
|
|
74
|
+
plugins?: DatafnPlugin[]; // Optional: Custom logic
|
|
75
|
+
clientId?: string; // Optional: Unique ID for this client
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Table API
|
|
80
|
+
|
|
81
|
+
The `client.table(name)` method provides a scoped interface for a specific resource defined in your schema. It delegates to the main client but automatically handles resource naming and versioning.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const users = client.table("user");
|
|
85
|
+
|
|
86
|
+
// Query
|
|
87
|
+
const allUsers = await users.query({ select: ["id", "name"] });
|
|
88
|
+
|
|
89
|
+
// Mutate
|
|
90
|
+
await users.mutate({
|
|
91
|
+
operation: "merge",
|
|
92
|
+
id: "user:123",
|
|
93
|
+
record: { name: "New Name" }
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Subscribe to events for this table only
|
|
97
|
+
users.subscribe(event => console.log("User changed:", event));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. Reactive Signals
|
|
101
|
+
|
|
102
|
+
Signals are the bridge between your data and your UI. A signal represents a live query. When data changes (locally or via sync), signals automatically re-run and notify subscribers.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const query = { select: ["id"], filters: { status: "active" } };
|
|
106
|
+
const signal = client.table("todo").signal(query);
|
|
107
|
+
|
|
108
|
+
// Get current value
|
|
109
|
+
console.log(signal.get());
|
|
110
|
+
|
|
111
|
+
// Subscribe
|
|
112
|
+
const unsub = signal.subscribe(newValue => {
|
|
113
|
+
// Update UI efficiently
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 4. Offline & Storage
|
|
118
|
+
|
|
119
|
+
Pass a `storage` adapter to enable offline capabilities. The client will automatically:
|
|
120
|
+
- Hydrate signals from local storage on load.
|
|
121
|
+
- Apply `clone` and `pull` results to local storage.
|
|
122
|
+
- (Future) Queue mutations when offline.
|
|
123
|
+
|
|
124
|
+
**Available Adapters:**
|
|
125
|
+
- `MemoryStorageAdapter`: Transient, in-memory storage (great for testing).
|
|
126
|
+
- `IndexedDbStorageAdapter`: Persistent browser storage.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { IndexedDbStorageAdapter } from "@datafn/client";
|
|
130
|
+
|
|
131
|
+
const storage = new IndexedDbStorageAdapter("my-db");
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 5. Synchronization
|
|
135
|
+
|
|
136
|
+
The `client.sync` facade manages data consistency with the server.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Initial data load
|
|
140
|
+
await client.sync.clone({ ... });
|
|
141
|
+
|
|
142
|
+
// Fetch incremental updates
|
|
143
|
+
await client.sync.pull({ ... });
|
|
144
|
+
|
|
145
|
+
// Push local changes (if using offline queue)
|
|
146
|
+
await client.sync.push({ ... });
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If `storage` is configured, `clone` and `pull` operations automatically update the local database, which in turn triggers all relevant reactive signals.
|
|
150
|
+
|
|
151
|
+
### 6. Event Bus
|
|
152
|
+
|
|
153
|
+
Listen to global or scoped events.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
client.subscribe((event) => {
|
|
157
|
+
if (event.type === "mutation_applied") {
|
|
158
|
+
console.log(`Resource ${event.resource} updated!`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### `DatafnClient`
|
|
166
|
+
|
|
167
|
+
- `table(name: string): DatafnTable` - Get a table handle.
|
|
168
|
+
- `query(q: unknown): Promise<unknown>` - Execute a raw query.
|
|
169
|
+
- `mutate(m: unknown): Promise<unknown>` - Execute a raw mutation.
|
|
170
|
+
- `transact(t: unknown): Promise<unknown>` - Execute a transaction.
|
|
171
|
+
- `sync`: Sync facade (`clone`, `pull`, `push`, `seed`).
|
|
172
|
+
- `subscribe(handler, filter?)`: Global event subscription.
|
|
173
|
+
|
|
174
|
+
### `DatafnTable`
|
|
175
|
+
|
|
176
|
+
- `query(fragment)`: Execute query for this resource.
|
|
177
|
+
- `mutate(fragment)`: Execute mutation for this resource.
|
|
178
|
+
- `signal(fragment)`: Create a reactive signal.
|
|
179
|
+
- `subscribe(handler)`: Subscribe to events for this resource.
|
|
180
|
+
|
|
181
|
+
### `DatafnRemoteAdapter`
|
|
182
|
+
|
|
183
|
+
Interface for your network layer. You must implement this to connect `datafn/client` to your backend.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
interface DatafnRemoteAdapter {
|
|
187
|
+
query(q: unknown): Promise<unknown>;
|
|
188
|
+
mutation(m: unknown): Promise<unknown>;
|
|
189
|
+
transact(t: unknown): Promise<unknown>;
|
|
190
|
+
seed(p: unknown): Promise<unknown>;
|
|
191
|
+
clone(p: unknown): Promise<unknown>;
|
|
192
|
+
pull(p: unknown): Promise<unknown>;
|
|
193
|
+
push(p: unknown): Promise<unknown>;
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT
|