query-optimistic 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.mts +39 -4
- package/dist/core/index.d.ts +39 -4
- package/dist/core/index.js +77 -11
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +77 -11
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +94 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -19
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +5 -0
- package/dist/react/index.d.ts +5 -0
- package/dist/react/index.js +84 -10
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +84 -10
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/core/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { I as IdGetter, C as CollectionDef, E as EntityDef, M as MutationDef, O as Optimistic } from '../types-BOq5W_Qm.mjs';
|
|
2
2
|
export { A as AnyDef, a as OptimisticAction, b as OptimisticInstruction, c as OptimisticStatus, P as PaginatedOptions, Q as QueryOptions } from '../types-BOq5W_Qm.mjs';
|
|
3
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Define a collection query for fetching arrays of items
|
|
@@ -86,19 +87,32 @@ type RegisteredEntry = RegisteredCollection | RegisteredEntity | RegisteredPagin
|
|
|
86
87
|
*/
|
|
87
88
|
declare class QueryRegistry {
|
|
88
89
|
private entries;
|
|
90
|
+
private queryClient;
|
|
91
|
+
private collectionDefs;
|
|
92
|
+
/** Set the query client for direct cache access */
|
|
93
|
+
setQueryClient(client: QueryClient): void;
|
|
94
|
+
/** Register a collection definition for direct cache updates */
|
|
95
|
+
registerDef(def: CollectionDef<any, any>): void;
|
|
89
96
|
/** Register an active query */
|
|
90
97
|
register(entry: RegisteredEntry): void;
|
|
91
98
|
/** Unregister a query when component unmounts */
|
|
92
99
|
unregister(entry: RegisteredEntry): void;
|
|
93
100
|
/** Get all registered entries for a query name */
|
|
94
101
|
getByName(name: string): RegisteredEntry[];
|
|
102
|
+
/**
|
|
103
|
+
* Check if params partially match the given scope object.
|
|
104
|
+
* Returns true if all key-value pairs in scope exist in params.
|
|
105
|
+
*/
|
|
106
|
+
private matchesScope;
|
|
95
107
|
/** Apply an optimistic update to all queries with given name */
|
|
96
108
|
applyUpdate<T>(name: string, action: 'prepend' | 'append' | 'update' | 'delete' | 'replace', payload: {
|
|
97
109
|
data?: Partial<Optimistic<T>>;
|
|
98
110
|
id?: string;
|
|
99
111
|
where?: (item: T) => boolean;
|
|
100
112
|
update?: (item: T) => T;
|
|
101
|
-
}): (() => void)[];
|
|
113
|
+
}, scope?: Record<string, unknown>): (() => void)[];
|
|
114
|
+
/** Apply update directly to query cache (used when scope is provided) */
|
|
115
|
+
private applyDirectCacheUpdate;
|
|
102
116
|
private applyCollectionUpdate;
|
|
103
117
|
}
|
|
104
118
|
/** Singleton registry instance */
|
|
@@ -115,11 +129,23 @@ interface OptimisticTransaction {
|
|
|
115
129
|
sync?: boolean;
|
|
116
130
|
rollback: () => void;
|
|
117
131
|
}
|
|
132
|
+
/** Options for channel operations */
|
|
133
|
+
interface ChannelOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Only apply updates to queries whose params partially match this object.
|
|
136
|
+
* All key-value pairs in scope must exist in the query's params.
|
|
137
|
+
* @example
|
|
138
|
+
* channel(ordersCollection, { scope: { chain: 'solana' } }).delete(id)
|
|
139
|
+
* // Only affects queries with params containing chain: 'solana'
|
|
140
|
+
*/
|
|
141
|
+
scope?: Record<string, unknown>;
|
|
142
|
+
}
|
|
118
143
|
/** Channel for a collection - provides typed optimistic mutation methods */
|
|
119
144
|
declare class CollectionChannel<TEntity> {
|
|
120
145
|
private readonly target;
|
|
146
|
+
private readonly options?;
|
|
121
147
|
private readonly optimisticId;
|
|
122
|
-
constructor(target: CollectionDef<TEntity, any
|
|
148
|
+
constructor(target: CollectionDef<TEntity, any>, options?: ChannelOptions | undefined);
|
|
123
149
|
/**
|
|
124
150
|
* Prepend an item to the collection
|
|
125
151
|
* @returns Rollback function to undo the change
|
|
@@ -189,9 +215,14 @@ declare class EntityChannel<TEntity> {
|
|
|
189
215
|
* @example
|
|
190
216
|
* // Update an entity
|
|
191
217
|
* channel(userEntity).update(user => ({ ...user, name: 'Jane' }));
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* // Scoped update
|
|
221
|
+
* channel(ordersCollection, { scope: { chain: 'solana' } }).delete(id);
|
|
222
|
+
* // Only affects queries with params containing chain: 'solana'
|
|
192
223
|
*/
|
|
193
224
|
interface Channel {
|
|
194
|
-
<TEntity>(target: CollectionDef<TEntity, any
|
|
225
|
+
<TEntity>(target: CollectionDef<TEntity, any>, options?: ChannelOptions): CollectionChannel<TEntity>;
|
|
195
226
|
<TEntity>(target: EntityDef<TEntity, any>): EntityChannel<TEntity>;
|
|
196
227
|
}
|
|
197
228
|
/**
|
|
@@ -205,7 +236,11 @@ interface Channel {
|
|
|
205
236
|
* } catch (error) {
|
|
206
237
|
* rollback(); // Undo the optimistic update
|
|
207
238
|
* }
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* // Scoped update - only affects queries with matching params
|
|
242
|
+
* channel(ordersCollection, { scope: { chain: 'solana', status: 'pending' } }).delete(id);
|
|
208
243
|
*/
|
|
209
244
|
declare const channel: Channel;
|
|
210
245
|
|
|
211
|
-
export { type Channel, CollectionChannel, CollectionDef, EntityChannel, EntityDef, IdGetter, MutationDef, Optimistic, type OptimisticTransaction, type RegisteredCollection, type RegisteredEntity, type RegisteredEntry, type RegisteredPaginatedCollection, channel, defineCollection, defineEntity, defineMutation, registry };
|
|
246
|
+
export { type Channel, type ChannelOptions, CollectionChannel, CollectionDef, EntityChannel, EntityDef, IdGetter, MutationDef, Optimistic, type OptimisticTransaction, type RegisteredCollection, type RegisteredEntity, type RegisteredEntry, type RegisteredPaginatedCollection, channel, defineCollection, defineEntity, defineMutation, registry };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { I as IdGetter, C as CollectionDef, E as EntityDef, M as MutationDef, O as Optimistic } from '../types-BOq5W_Qm.js';
|
|
2
2
|
export { A as AnyDef, a as OptimisticAction, b as OptimisticInstruction, c as OptimisticStatus, P as PaginatedOptions, Q as QueryOptions } from '../types-BOq5W_Qm.js';
|
|
3
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Define a collection query for fetching arrays of items
|
|
@@ -86,19 +87,32 @@ type RegisteredEntry = RegisteredCollection | RegisteredEntity | RegisteredPagin
|
|
|
86
87
|
*/
|
|
87
88
|
declare class QueryRegistry {
|
|
88
89
|
private entries;
|
|
90
|
+
private queryClient;
|
|
91
|
+
private collectionDefs;
|
|
92
|
+
/** Set the query client for direct cache access */
|
|
93
|
+
setQueryClient(client: QueryClient): void;
|
|
94
|
+
/** Register a collection definition for direct cache updates */
|
|
95
|
+
registerDef(def: CollectionDef<any, any>): void;
|
|
89
96
|
/** Register an active query */
|
|
90
97
|
register(entry: RegisteredEntry): void;
|
|
91
98
|
/** Unregister a query when component unmounts */
|
|
92
99
|
unregister(entry: RegisteredEntry): void;
|
|
93
100
|
/** Get all registered entries for a query name */
|
|
94
101
|
getByName(name: string): RegisteredEntry[];
|
|
102
|
+
/**
|
|
103
|
+
* Check if params partially match the given scope object.
|
|
104
|
+
* Returns true if all key-value pairs in scope exist in params.
|
|
105
|
+
*/
|
|
106
|
+
private matchesScope;
|
|
95
107
|
/** Apply an optimistic update to all queries with given name */
|
|
96
108
|
applyUpdate<T>(name: string, action: 'prepend' | 'append' | 'update' | 'delete' | 'replace', payload: {
|
|
97
109
|
data?: Partial<Optimistic<T>>;
|
|
98
110
|
id?: string;
|
|
99
111
|
where?: (item: T) => boolean;
|
|
100
112
|
update?: (item: T) => T;
|
|
101
|
-
}): (() => void)[];
|
|
113
|
+
}, scope?: Record<string, unknown>): (() => void)[];
|
|
114
|
+
/** Apply update directly to query cache (used when scope is provided) */
|
|
115
|
+
private applyDirectCacheUpdate;
|
|
102
116
|
private applyCollectionUpdate;
|
|
103
117
|
}
|
|
104
118
|
/** Singleton registry instance */
|
|
@@ -115,11 +129,23 @@ interface OptimisticTransaction {
|
|
|
115
129
|
sync?: boolean;
|
|
116
130
|
rollback: () => void;
|
|
117
131
|
}
|
|
132
|
+
/** Options for channel operations */
|
|
133
|
+
interface ChannelOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Only apply updates to queries whose params partially match this object.
|
|
136
|
+
* All key-value pairs in scope must exist in the query's params.
|
|
137
|
+
* @example
|
|
138
|
+
* channel(ordersCollection, { scope: { chain: 'solana' } }).delete(id)
|
|
139
|
+
* // Only affects queries with params containing chain: 'solana'
|
|
140
|
+
*/
|
|
141
|
+
scope?: Record<string, unknown>;
|
|
142
|
+
}
|
|
118
143
|
/** Channel for a collection - provides typed optimistic mutation methods */
|
|
119
144
|
declare class CollectionChannel<TEntity> {
|
|
120
145
|
private readonly target;
|
|
146
|
+
private readonly options?;
|
|
121
147
|
private readonly optimisticId;
|
|
122
|
-
constructor(target: CollectionDef<TEntity, any
|
|
148
|
+
constructor(target: CollectionDef<TEntity, any>, options?: ChannelOptions | undefined);
|
|
123
149
|
/**
|
|
124
150
|
* Prepend an item to the collection
|
|
125
151
|
* @returns Rollback function to undo the change
|
|
@@ -189,9 +215,14 @@ declare class EntityChannel<TEntity> {
|
|
|
189
215
|
* @example
|
|
190
216
|
* // Update an entity
|
|
191
217
|
* channel(userEntity).update(user => ({ ...user, name: 'Jane' }));
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* // Scoped update
|
|
221
|
+
* channel(ordersCollection, { scope: { chain: 'solana' } }).delete(id);
|
|
222
|
+
* // Only affects queries with params containing chain: 'solana'
|
|
192
223
|
*/
|
|
193
224
|
interface Channel {
|
|
194
|
-
<TEntity>(target: CollectionDef<TEntity, any
|
|
225
|
+
<TEntity>(target: CollectionDef<TEntity, any>, options?: ChannelOptions): CollectionChannel<TEntity>;
|
|
195
226
|
<TEntity>(target: EntityDef<TEntity, any>): EntityChannel<TEntity>;
|
|
196
227
|
}
|
|
197
228
|
/**
|
|
@@ -205,7 +236,11 @@ interface Channel {
|
|
|
205
236
|
* } catch (error) {
|
|
206
237
|
* rollback(); // Undo the optimistic update
|
|
207
238
|
* }
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* // Scoped update - only affects queries with matching params
|
|
242
|
+
* channel(ordersCollection, { scope: { chain: 'solana', status: 'pending' } }).delete(id);
|
|
208
243
|
*/
|
|
209
244
|
declare const channel: Channel;
|
|
210
245
|
|
|
211
|
-
export { type Channel, CollectionChannel, CollectionDef, EntityChannel, EntityDef, IdGetter, MutationDef, Optimistic, type OptimisticTransaction, type RegisteredCollection, type RegisteredEntity, type RegisteredEntry, type RegisteredPaginatedCollection, channel, defineCollection, defineEntity, defineMutation, registry };
|
|
246
|
+
export { type Channel, type ChannelOptions, CollectionChannel, CollectionDef, EntityChannel, EntityDef, IdGetter, MutationDef, Optimistic, type OptimisticTransaction, type RegisteredCollection, type RegisteredEntity, type RegisteredEntry, type RegisteredPaginatedCollection, channel, defineCollection, defineEntity, defineMutation, registry };
|
package/dist/core/index.js
CHANGED
|
@@ -30,6 +30,16 @@ function defineMutation(config) {
|
|
|
30
30
|
var QueryRegistry = class {
|
|
31
31
|
constructor() {
|
|
32
32
|
this.entries = /* @__PURE__ */ new Map();
|
|
33
|
+
this.queryClient = null;
|
|
34
|
+
this.collectionDefs = /* @__PURE__ */ new Map();
|
|
35
|
+
}
|
|
36
|
+
/** Set the query client for direct cache access */
|
|
37
|
+
setQueryClient(client) {
|
|
38
|
+
this.queryClient = client;
|
|
39
|
+
}
|
|
40
|
+
/** Register a collection definition for direct cache updates */
|
|
41
|
+
registerDef(def) {
|
|
42
|
+
this.collectionDefs.set(def.name, def);
|
|
33
43
|
}
|
|
34
44
|
/** Register an active query */
|
|
35
45
|
register(entry) {
|
|
@@ -37,6 +47,9 @@ var QueryRegistry = class {
|
|
|
37
47
|
this.entries.set(entry.name, /* @__PURE__ */ new Set());
|
|
38
48
|
}
|
|
39
49
|
this.entries.get(entry.name).add(entry);
|
|
50
|
+
if (entry.kind === "collection" || entry.kind === "paginated") {
|
|
51
|
+
this.collectionDefs.set(entry.name, entry.def);
|
|
52
|
+
}
|
|
40
53
|
}
|
|
41
54
|
/** Unregister a query when component unmounts */
|
|
42
55
|
unregister(entry) {
|
|
@@ -52,8 +65,20 @@ var QueryRegistry = class {
|
|
|
52
65
|
getByName(name) {
|
|
53
66
|
return Array.from(this.entries.get(name) ?? []);
|
|
54
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if params partially match the given scope object.
|
|
70
|
+
* Returns true if all key-value pairs in scope exist in params.
|
|
71
|
+
*/
|
|
72
|
+
matchesScope(params, scope) {
|
|
73
|
+
if (!scope) return true;
|
|
74
|
+
if (!params) return false;
|
|
75
|
+
return Object.entries(scope).every(([key, value]) => params[key] === value);
|
|
76
|
+
}
|
|
55
77
|
/** Apply an optimistic update to all queries with given name */
|
|
56
|
-
applyUpdate(name, action, payload) {
|
|
78
|
+
applyUpdate(name, action, payload, scope) {
|
|
79
|
+
if (scope && this.queryClient) {
|
|
80
|
+
return this.applyDirectCacheUpdate(name, action, payload, scope);
|
|
81
|
+
}
|
|
57
82
|
const entries = this.getByName(name);
|
|
58
83
|
const rollbacks = [];
|
|
59
84
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
@@ -61,7 +86,8 @@ var QueryRegistry = class {
|
|
|
61
86
|
const key = JSON.stringify(entry.queryKey);
|
|
62
87
|
if (seenKeys.has(key)) return false;
|
|
63
88
|
seenKeys.add(key);
|
|
64
|
-
|
|
89
|
+
const params = entry.queryKey[1];
|
|
90
|
+
return this.matchesScope(params, scope);
|
|
65
91
|
});
|
|
66
92
|
for (const entry of uniqueEntries) {
|
|
67
93
|
if (entry.kind === "collection") {
|
|
@@ -98,6 +124,45 @@ var QueryRegistry = class {
|
|
|
98
124
|
}
|
|
99
125
|
return rollbacks;
|
|
100
126
|
}
|
|
127
|
+
/** Apply update directly to query cache (used when scope is provided) */
|
|
128
|
+
applyDirectCacheUpdate(name, action, payload, scope) {
|
|
129
|
+
if (!this.queryClient) return [];
|
|
130
|
+
const def = this.collectionDefs.get(name);
|
|
131
|
+
if (!def) return [];
|
|
132
|
+
const rollbacks = [];
|
|
133
|
+
const queries = this.queryClient.getQueriesData({
|
|
134
|
+
queryKey: [name]
|
|
135
|
+
});
|
|
136
|
+
for (const [queryKey, data] of queries) {
|
|
137
|
+
if (!data) continue;
|
|
138
|
+
const params = queryKey[1];
|
|
139
|
+
if (!this.matchesScope(params, scope)) continue;
|
|
140
|
+
const isPaginated = data && typeof data === "object" && "pages" in data;
|
|
141
|
+
if (isPaginated) {
|
|
142
|
+
const paginatedData = data;
|
|
143
|
+
const previous = paginatedData;
|
|
144
|
+
rollbacks.push(() => this.queryClient.setQueryData(queryKey, previous));
|
|
145
|
+
this.queryClient.setQueryData(queryKey, (prev) => {
|
|
146
|
+
if (!prev) return prev;
|
|
147
|
+
return {
|
|
148
|
+
...prev,
|
|
149
|
+
pages: prev.pages.map(
|
|
150
|
+
(page, i) => i === 0 ? this.applyCollectionUpdate(page, action, payload, def.id) : page
|
|
151
|
+
)
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
const arrayData = data;
|
|
156
|
+
const previous = arrayData;
|
|
157
|
+
rollbacks.push(() => this.queryClient.setQueryData(queryKey, previous));
|
|
158
|
+
this.queryClient.setQueryData(queryKey, (prev) => {
|
|
159
|
+
if (!prev) return prev;
|
|
160
|
+
return this.applyCollectionUpdate(prev, action, payload, def.id);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return rollbacks;
|
|
165
|
+
}
|
|
101
166
|
applyCollectionUpdate(items, action, payload, getId) {
|
|
102
167
|
switch (action) {
|
|
103
168
|
case "prepend":
|
|
@@ -133,8 +198,9 @@ var QueryRegistry = class {
|
|
|
133
198
|
};
|
|
134
199
|
var registry = new QueryRegistry();
|
|
135
200
|
var CollectionChannel = class {
|
|
136
|
-
constructor(target) {
|
|
201
|
+
constructor(target, options) {
|
|
137
202
|
this.target = target;
|
|
203
|
+
this.options = options;
|
|
138
204
|
this.optimisticId = nanoid.nanoid();
|
|
139
205
|
}
|
|
140
206
|
/**
|
|
@@ -148,7 +214,7 @@ var CollectionChannel = class {
|
|
|
148
214
|
};
|
|
149
215
|
const rollbacks = registry.applyUpdate(this.target.name, "prepend", {
|
|
150
216
|
data: optimisticData
|
|
151
|
-
});
|
|
217
|
+
}, this.options?.scope);
|
|
152
218
|
return () => rollbacks.forEach((rb) => rb());
|
|
153
219
|
}
|
|
154
220
|
/**
|
|
@@ -162,7 +228,7 @@ var CollectionChannel = class {
|
|
|
162
228
|
};
|
|
163
229
|
const rollbacks = registry.applyUpdate(this.target.name, "append", {
|
|
164
230
|
data: optimisticData
|
|
165
|
-
});
|
|
231
|
+
}, this.options?.scope);
|
|
166
232
|
return () => rollbacks.forEach((rb) => rb());
|
|
167
233
|
}
|
|
168
234
|
/**
|
|
@@ -173,7 +239,7 @@ var CollectionChannel = class {
|
|
|
173
239
|
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
174
240
|
id,
|
|
175
241
|
update: updateFn
|
|
176
|
-
});
|
|
242
|
+
}, this.options?.scope);
|
|
177
243
|
return () => rollbacks.forEach((rb) => rb());
|
|
178
244
|
}
|
|
179
245
|
/**
|
|
@@ -184,7 +250,7 @@ var CollectionChannel = class {
|
|
|
184
250
|
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
185
251
|
where,
|
|
186
252
|
update: updateFn
|
|
187
|
-
});
|
|
253
|
+
}, this.options?.scope);
|
|
188
254
|
return () => rollbacks.forEach((rb) => rb());
|
|
189
255
|
}
|
|
190
256
|
/**
|
|
@@ -194,7 +260,7 @@ var CollectionChannel = class {
|
|
|
194
260
|
delete(id) {
|
|
195
261
|
const rollbacks = registry.applyUpdate(this.target.name, "delete", {
|
|
196
262
|
id
|
|
197
|
-
});
|
|
263
|
+
}, this.options?.scope);
|
|
198
264
|
return () => rollbacks.forEach((rb) => rb());
|
|
199
265
|
}
|
|
200
266
|
/**
|
|
@@ -204,7 +270,7 @@ var CollectionChannel = class {
|
|
|
204
270
|
deleteWhere(where) {
|
|
205
271
|
const rollbacks = registry.applyUpdate(this.target.name, "delete", {
|
|
206
272
|
where
|
|
207
|
-
});
|
|
273
|
+
}, this.options?.scope);
|
|
208
274
|
return () => rollbacks.forEach((rb) => rb());
|
|
209
275
|
}
|
|
210
276
|
};
|
|
@@ -233,9 +299,9 @@ var EntityChannel = class {
|
|
|
233
299
|
return () => rollbacks.forEach((rb) => rb());
|
|
234
300
|
}
|
|
235
301
|
};
|
|
236
|
-
var channel = ((target) => {
|
|
302
|
+
var channel = ((target, options) => {
|
|
237
303
|
if (target._type === "collection") {
|
|
238
|
-
return new CollectionChannel(target);
|
|
304
|
+
return new CollectionChannel(target, options);
|
|
239
305
|
} else {
|
|
240
306
|
return new EntityChannel(target);
|
|
241
307
|
}
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/define.ts","../../src/core/registry.ts","../../src/core/channel.ts"],"names":["nanoid"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n CollectionDef,\n EntityDef,\n MutationDef,\n IdGetter,\n} from './types';\n\n/**\n * Define a collection query for fetching arrays of items\n *\n * @example\n * const postsQuery = defineCollection({\n * name: 'posts',\n * id: (post) => post._id,\n * fetch: ({ page }) => api.get(`/posts?page=${page}`).json()\n * })\n */\nexport function defineCollection<TData, TParams = void>(config: {\n name: string;\n id: IdGetter<TData>;\n fetch: (params: TParams) => Promise<TData[]>;\n}): CollectionDef<TData, TParams> {\n return {\n _type: 'collection',\n name: config.name,\n id: config.id,\n fetch: config.fetch,\n };\n}\n\n/**\n * Define an entity for fetching single items\n *\n * @example\n * const userEntity = defineEntity({\n * name: 'user',\n * fetch: (userId) => api.get(`/users/${userId}`).json()\n * })\n */\nexport function defineEntity<TData, TParams = void>(config: {\n name: string;\n fetch: (params: TParams) => Promise<TData>;\n}): EntityDef<TData, TParams> {\n return {\n _type: 'entity',\n name: config.name,\n fetch: config.fetch,\n };\n}\n\n/**\n * Define a mutation for writing data\n *\n * @example\n * const createPost = defineMutation({\n * name: 'createPost',\n * mutate: (data) => api.post('/posts', { json: data }).json()\n * })\n */\nexport function defineMutation<TParams, TResponse = void>(config: {\n name?: string;\n mutate: (params: TParams) => Promise<TResponse>;\n}): MutationDef<TParams, TResponse> {\n return {\n _type: 'mutation',\n name: config.name,\n mutate: config.mutate,\n };\n}\n","import type { CollectionDef, EntityDef, Optimistic } from './types';\n\n/** Registered collection entry */\nexport interface RegisteredCollection<T = any> {\n kind: 'collection';\n name: string;\n queryKey: readonly unknown[];\n def: CollectionDef<T, any>;\n getData: () => T[] | undefined;\n setData: (updater: (prev: T[] | undefined) => T[] | undefined) => void;\n}\n\n/** Registered entity entry */\nexport interface RegisteredEntity<T = any> {\n kind: 'entity';\n name: string;\n queryKey: readonly unknown[];\n def: EntityDef<T, any>;\n getData: () => T | undefined;\n setData: (updater: (prev: T | undefined) => T | undefined) => void;\n}\n\n/** Registered paginated collection entry */\nexport interface RegisteredPaginatedCollection<T = any> {\n kind: 'paginated';\n name: string;\n queryKey: readonly unknown[];\n def: CollectionDef<T, any>;\n getData: () => { pages: T[][]; pageParams: unknown[] } | undefined;\n setData: (\n updater: (\n prev: { pages: T[][]; pageParams: unknown[] } | undefined\n ) => { pages: T[][]; pageParams: unknown[] } | undefined\n ) => void;\n}\n\nexport type RegisteredEntry =\n | RegisteredCollection\n | RegisteredEntity\n | RegisteredPaginatedCollection;\n\n/**\n * Internal registry for tracking active queries\n * Used by optimistic updates to broadcast changes\n */\nclass QueryRegistry {\n private entries = new Map<string, Set<RegisteredEntry>>();\n\n /** Register an active query */\n register(entry: RegisteredEntry): void {\n if (!this.entries.has(entry.name)) {\n this.entries.set(entry.name, new Set());\n }\n this.entries.get(entry.name)!.add(entry);\n }\n\n /** Unregister a query when component unmounts */\n unregister(entry: RegisteredEntry): void {\n const set = this.entries.get(entry.name);\n if (set) {\n set.delete(entry);\n if (set.size === 0) {\n this.entries.delete(entry.name);\n }\n }\n }\n\n /** Get all registered entries for a query name */\n getByName(name: string): RegisteredEntry[] {\n return Array.from(this.entries.get(name) ?? []);\n }\n\n /** Apply an optimistic update to all queries with given name */\n applyUpdate<T>(\n name: string,\n action: 'prepend' | 'append' | 'update' | 'delete' | 'replace',\n payload: {\n data?: Partial<Optimistic<T>>;\n id?: string;\n where?: (item: T) => boolean;\n update?: (item: T) => T;\n }\n ): (() => void)[] {\n const entries = this.getByName(name);\n const rollbacks: (() => void)[] = [];\n\n // Deduplicate by queryKey to avoid updating the same cache entry multiple times\n const seenKeys = new Set<string>();\n const uniqueEntries = entries.filter((entry) => {\n const key = JSON.stringify(entry.queryKey);\n if (seenKeys.has(key)) return false;\n seenKeys.add(key);\n return true;\n });\n\n for (const entry of uniqueEntries) {\n if (entry.kind === 'collection') {\n const previous = entry.getData();\n const rollback = () => entry.setData(() => previous);\n rollbacks.push(rollback);\n\n entry.setData((prev) => {\n if (!prev) return prev;\n return this.applyCollectionUpdate(prev, action, payload, entry.def.id);\n });\n } else if (entry.kind === 'paginated') {\n const previous = entry.getData();\n const rollback = () => entry.setData(() => previous);\n rollbacks.push(rollback);\n\n entry.setData((prev) => {\n if (!prev) return prev;\n return {\n ...prev,\n pages: prev.pages.map((page, i) =>\n i === 0\n ? this.applyCollectionUpdate(page, action, payload, entry.def.id)\n : page\n ),\n };\n });\n } else if (entry.kind === 'entity') {\n const previous = entry.getData();\n const rollback = () => entry.setData(() => previous);\n rollbacks.push(rollback);\n\n if (action === 'update' && payload.update) {\n entry.setData((prev) => (prev ? payload.update!(prev as T) : prev));\n } else if (action === 'replace' && payload.data) {\n entry.setData(() => payload.data as T);\n }\n }\n }\n\n return rollbacks;\n }\n\n private applyCollectionUpdate<T>(\n items: T[],\n action: string,\n payload: {\n data?: Partial<Optimistic<T>>;\n id?: string;\n where?: (item: T) => boolean;\n update?: (item: T) => T;\n },\n getId: (item: T) => string\n ): T[] {\n switch (action) {\n case 'prepend':\n return payload.data ? [payload.data as T, ...items] : items;\n\n case 'append':\n return payload.data ? [...items, payload.data as T] : items;\n\n case 'update':\n return items.map((item) => {\n const matches = payload.id\n ? getId(item) === payload.id\n : payload.where?.(item);\n if (matches && payload.update) {\n return payload.update(item);\n }\n if (matches && payload.data) {\n return { ...item, ...payload.data };\n }\n return item;\n });\n\n case 'delete':\n return items.filter((item) => {\n if (payload.id) return getId(item) !== payload.id;\n if (payload.where) return !payload.where(item);\n return true;\n });\n\n case 'replace':\n return items.map((item) => {\n const matches = payload.id\n ? getId(item) === payload.id\n : payload.where?.(item);\n return matches && payload.data ? (payload.data as T) : item;\n });\n\n default:\n return items;\n }\n }\n}\n\n/** Singleton registry instance */\nexport const registry = new QueryRegistry();\n","import { nanoid } from 'nanoid';\nimport type { CollectionDef, EntityDef, Optimistic } from './types';\nimport { registry } from './registry';\n\n/** Transaction returned from channel methods */\nexport interface OptimisticTransaction {\n target: CollectionDef<any, any> | EntityDef<any, any>;\n action: 'prepend' | 'append' | 'update' | 'delete' | 'replace';\n data?: any;\n id?: string;\n where?: (item: any) => boolean;\n update?: (item: any) => any;\n sync?: boolean;\n rollback: () => void;\n}\n\n/** Channel for a collection - provides typed optimistic mutation methods */\nexport class CollectionChannel<TEntity> {\n private readonly optimisticId = nanoid();\n\n constructor(private readonly target: CollectionDef<TEntity, any>) {}\n\n /**\n * Prepend an item to the collection\n * @returns Rollback function to undo the change\n */\n prepend(data: TEntity, options?: { sync?: boolean }): () => void {\n const optimisticData = {\n ...data,\n _optimistic: { id: this.optimisticId, status: 'pending' as const },\n };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'prepend', {\n data: optimisticData,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Append an item to the collection\n * @returns Rollback function to undo the change\n */\n append(data: TEntity, options?: { sync?: boolean }): () => void {\n const optimisticData = {\n ...data,\n _optimistic: { id: this.optimisticId, status: 'pending' as const },\n };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'append', {\n data: optimisticData,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Update an item in the collection by ID\n * @returns Rollback function to undo the change\n */\n update(\n id: string,\n updateFn: (item: TEntity) => TEntity,\n options?: { sync?: boolean }\n ): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'update', {\n id,\n update: updateFn,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Update items matching a predicate\n * @returns Rollback function to undo the change\n */\n updateWhere(\n where: (item: TEntity) => boolean,\n updateFn: (item: TEntity) => TEntity\n ): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'update', {\n where,\n update: updateFn,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Delete an item from the collection by ID\n * @returns Rollback function to undo the change\n */\n delete(id: string): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'delete', {\n id,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Delete items matching a predicate\n * @returns Rollback function to undo the change\n */\n deleteWhere(where: (item: TEntity) => boolean): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'delete', {\n where,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n}\n\n/** Channel for an entity - provides typed optimistic mutation methods */\nexport class EntityChannel<TEntity> {\n constructor(private readonly target: EntityDef<TEntity, any>) {}\n\n /**\n * Update the entity\n * @returns Rollback function to undo the change\n */\n update(updateFn: (item: TEntity) => TEntity, options?: { sync?: boolean }): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'update', {\n update: updateFn,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Replace the entity with new data\n * @returns Rollback function to undo the change\n */\n replace(data: TEntity, options?: { sync?: boolean }): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'replace', {\n data: data as any,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n}\n\n/**\n * Channel function for optimistic mutations.\n * Call with a collection or entity to get typed mutation methods.\n *\n * @example\n * // Standalone usage\n * const rollback = channel(usersCollection).prepend({ id: '1', name: 'John' });\n * // Later, to undo:\n * rollback();\n *\n * @example\n * // Update an entity\n * channel(userEntity).update(user => ({ ...user, name: 'Jane' }));\n */\nexport interface Channel {\n <TEntity>(target: CollectionDef<TEntity, any>): CollectionChannel<TEntity>;\n <TEntity>(target: EntityDef<TEntity, any>): EntityChannel<TEntity>;\n}\n\n/**\n * Create a channel for optimistic mutations.\n * Use this to apply immediate UI updates that can be rolled back.\n *\n * @example\n * const rollback = channel(usersCollection).prepend(newUser);\n * try {\n * await api.createUser(newUser);\n * } catch (error) {\n * rollback(); // Undo the optimistic update\n * }\n */\nexport const channel: Channel = (<TEntity>(\n target: CollectionDef<TEntity, any> | EntityDef<TEntity, any>\n): CollectionChannel<TEntity> | EntityChannel<TEntity> => {\n if (target._type === 'collection') {\n return new CollectionChannel(target);\n } else {\n return new EntityChannel(target);\n }\n}) as Channel;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/define.ts","../../src/core/registry.ts","../../src/core/channel.ts"],"names":["nanoid"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n CollectionDef,\n EntityDef,\n MutationDef,\n IdGetter,\n} from './types';\n\n/**\n * Define a collection query for fetching arrays of items\n *\n * @example\n * const postsQuery = defineCollection({\n * name: 'posts',\n * id: (post) => post._id,\n * fetch: ({ page }) => api.get(`/posts?page=${page}`).json()\n * })\n */\nexport function defineCollection<TData, TParams = void>(config: {\n name: string;\n id: IdGetter<TData>;\n fetch: (params: TParams) => Promise<TData[]>;\n}): CollectionDef<TData, TParams> {\n return {\n _type: 'collection',\n name: config.name,\n id: config.id,\n fetch: config.fetch,\n };\n}\n\n/**\n * Define an entity for fetching single items\n *\n * @example\n * const userEntity = defineEntity({\n * name: 'user',\n * fetch: (userId) => api.get(`/users/${userId}`).json()\n * })\n */\nexport function defineEntity<TData, TParams = void>(config: {\n name: string;\n fetch: (params: TParams) => Promise<TData>;\n}): EntityDef<TData, TParams> {\n return {\n _type: 'entity',\n name: config.name,\n fetch: config.fetch,\n };\n}\n\n/**\n * Define a mutation for writing data\n *\n * @example\n * const createPost = defineMutation({\n * name: 'createPost',\n * mutate: (data) => api.post('/posts', { json: data }).json()\n * })\n */\nexport function defineMutation<TParams, TResponse = void>(config: {\n name?: string;\n mutate: (params: TParams) => Promise<TResponse>;\n}): MutationDef<TParams, TResponse> {\n return {\n _type: 'mutation',\n name: config.name,\n mutate: config.mutate,\n };\n}\n","import type { QueryClient } from '@tanstack/react-query';\nimport type { CollectionDef, EntityDef, Optimistic } from './types';\n\n/** Registered collection entry */\nexport interface RegisteredCollection<T = any> {\n kind: 'collection';\n name: string;\n queryKey: readonly unknown[];\n def: CollectionDef<T, any>;\n getData: () => T[] | undefined;\n setData: (updater: (prev: T[] | undefined) => T[] | undefined) => void;\n}\n\n/** Registered entity entry */\nexport interface RegisteredEntity<T = any> {\n kind: 'entity';\n name: string;\n queryKey: readonly unknown[];\n def: EntityDef<T, any>;\n getData: () => T | undefined;\n setData: (updater: (prev: T | undefined) => T | undefined) => void;\n}\n\n/** Registered paginated collection entry */\nexport interface RegisteredPaginatedCollection<T = any> {\n kind: 'paginated';\n name: string;\n queryKey: readonly unknown[];\n def: CollectionDef<T, any>;\n getData: () => { pages: T[][]; pageParams: unknown[] } | undefined;\n setData: (\n updater: (\n prev: { pages: T[][]; pageParams: unknown[] } | undefined\n ) => { pages: T[][]; pageParams: unknown[] } | undefined\n ) => void;\n}\n\nexport type RegisteredEntry =\n | RegisteredCollection\n | RegisteredEntity\n | RegisteredPaginatedCollection;\n\n/**\n * Internal registry for tracking active queries\n * Used by optimistic updates to broadcast changes\n */\nclass QueryRegistry {\n private entries = new Map<string, Set<RegisteredEntry>>();\n private queryClient: QueryClient | null = null;\n private collectionDefs = new Map<string, CollectionDef<any, any>>();\n\n /** Set the query client for direct cache access */\n setQueryClient(client: QueryClient): void {\n this.queryClient = client;\n }\n\n /** Register a collection definition for direct cache updates */\n registerDef(def: CollectionDef<any, any>): void {\n this.collectionDefs.set(def.name, def);\n }\n\n /** Register an active query */\n register(entry: RegisteredEntry): void {\n if (!this.entries.has(entry.name)) {\n this.entries.set(entry.name, new Set());\n }\n this.entries.get(entry.name)!.add(entry);\n\n // Also store the def for direct cache access\n if (entry.kind === 'collection' || entry.kind === 'paginated') {\n this.collectionDefs.set(entry.name, entry.def);\n }\n }\n\n /** Unregister a query when component unmounts */\n unregister(entry: RegisteredEntry): void {\n const set = this.entries.get(entry.name);\n if (set) {\n set.delete(entry);\n if (set.size === 0) {\n this.entries.delete(entry.name);\n }\n }\n }\n\n /** Get all registered entries for a query name */\n getByName(name: string): RegisteredEntry[] {\n return Array.from(this.entries.get(name) ?? []);\n }\n\n /**\n * Check if params partially match the given scope object.\n * Returns true if all key-value pairs in scope exist in params.\n */\n private matchesScope(params: Record<string, unknown> | undefined, scope?: Record<string, unknown>): boolean {\n if (!scope) return true;\n if (!params) return false;\n\n // Check if all scope keys exist in params with same value\n return Object.entries(scope).every(([key, value]) => params[key] === value);\n }\n\n /** Apply an optimistic update to all queries with given name */\n applyUpdate<T>(\n name: string,\n action: 'prepend' | 'append' | 'update' | 'delete' | 'replace',\n payload: {\n data?: Partial<Optimistic<T>>;\n id?: string;\n where?: (item: T) => boolean;\n update?: (item: T) => T;\n },\n scope?: Record<string, unknown>\n ): (() => void)[] {\n // When scope is provided and we have a queryClient, update cache directly\n if (scope && this.queryClient) {\n return this.applyDirectCacheUpdate(name, action, payload, scope);\n }\n\n // Otherwise, use registry-based updates\n const entries = this.getByName(name);\n const rollbacks: (() => void)[] = [];\n\n // Deduplicate by queryKey to avoid updating the same cache entry multiple times\n const seenKeys = new Set<string>();\n const uniqueEntries = entries.filter((entry) => {\n const key = JSON.stringify(entry.queryKey);\n if (seenKeys.has(key)) return false;\n seenKeys.add(key);\n // Also filter by scope if provided\n const params = entry.queryKey[1] as Record<string, unknown> | undefined;\n return this.matchesScope(params, scope);\n });\n\n for (const entry of uniqueEntries) {\n if (entry.kind === 'collection') {\n const previous = entry.getData();\n const rollback = () => entry.setData(() => previous);\n rollbacks.push(rollback);\n\n entry.setData((prev) => {\n if (!prev) return prev;\n return this.applyCollectionUpdate(prev, action, payload, entry.def.id);\n });\n } else if (entry.kind === 'paginated') {\n const previous = entry.getData();\n const rollback = () => entry.setData(() => previous);\n rollbacks.push(rollback);\n\n entry.setData((prev) => {\n if (!prev) return prev;\n return {\n ...prev,\n pages: prev.pages.map((page, i) =>\n i === 0\n ? this.applyCollectionUpdate(page, action, payload, entry.def.id)\n : page\n ),\n };\n });\n } else if (entry.kind === 'entity') {\n const previous = entry.getData();\n const rollback = () => entry.setData(() => previous);\n rollbacks.push(rollback);\n\n if (action === 'update' && payload.update) {\n entry.setData((prev) => (prev ? payload.update!(prev as T) : prev));\n } else if (action === 'replace' && payload.data) {\n entry.setData(() => payload.data as T);\n }\n }\n }\n\n return rollbacks;\n }\n\n /** Apply update directly to query cache (used when scope is provided) */\n private applyDirectCacheUpdate<T>(\n name: string,\n action: 'prepend' | 'append' | 'update' | 'delete' | 'replace',\n payload: {\n data?: Partial<Optimistic<T>>;\n id?: string;\n where?: (item: T) => boolean;\n update?: (item: T) => T;\n },\n scope: Record<string, unknown>\n ): (() => void)[] {\n if (!this.queryClient) return [];\n\n const def = this.collectionDefs.get(name);\n if (!def) return [];\n\n const rollbacks: (() => void)[] = [];\n\n // Get all queries with this name from the cache\n const queries = this.queryClient.getQueriesData<T[] | { pages: T[][]; pageParams: unknown[] }>({\n queryKey: [name],\n });\n\n for (const [queryKey, data] of queries) {\n if (!data) continue;\n\n // Extract params from queryKey [name, params]\n const params = queryKey[1] as Record<string, unknown> | undefined;\n if (!this.matchesScope(params, scope)) continue;\n\n // Check if this is a paginated query\n const isPaginated = data && typeof data === 'object' && 'pages' in data;\n\n if (isPaginated) {\n const paginatedData = data as { pages: T[][]; pageParams: unknown[] };\n const previous = paginatedData;\n rollbacks.push(() => this.queryClient!.setQueryData(queryKey, previous));\n\n this.queryClient.setQueryData<{ pages: T[][]; pageParams: unknown[] }>(queryKey, (prev) => {\n if (!prev) return prev;\n return {\n ...prev,\n pages: prev.pages.map((page, i) =>\n i === 0\n ? this.applyCollectionUpdate(page, action, payload, def.id)\n : page\n ),\n };\n });\n } else {\n const arrayData = data as T[];\n const previous = arrayData;\n rollbacks.push(() => this.queryClient!.setQueryData(queryKey, previous));\n\n this.queryClient.setQueryData<T[]>(queryKey, (prev) => {\n if (!prev) return prev;\n return this.applyCollectionUpdate(prev, action, payload, def.id);\n });\n }\n }\n\n return rollbacks;\n }\n\n private applyCollectionUpdate<T>(\n items: T[],\n action: string,\n payload: {\n data?: Partial<Optimistic<T>>;\n id?: string;\n where?: (item: T) => boolean;\n update?: (item: T) => T;\n },\n getId: (item: T) => string\n ): T[] {\n switch (action) {\n case 'prepend':\n return payload.data ? [payload.data as T, ...items] : items;\n\n case 'append':\n return payload.data ? [...items, payload.data as T] : items;\n\n case 'update':\n return items.map((item) => {\n const matches = payload.id\n ? getId(item) === payload.id\n : payload.where?.(item);\n if (matches && payload.update) {\n return payload.update(item);\n }\n if (matches && payload.data) {\n return { ...item, ...payload.data };\n }\n return item;\n });\n\n case 'delete':\n return items.filter((item) => {\n if (payload.id) return getId(item) !== payload.id;\n if (payload.where) return !payload.where(item);\n return true;\n });\n\n case 'replace':\n return items.map((item) => {\n const matches = payload.id\n ? getId(item) === payload.id\n : payload.where?.(item);\n return matches && payload.data ? (payload.data as T) : item;\n });\n\n default:\n return items;\n }\n }\n}\n\n/** Singleton registry instance */\nexport const registry = new QueryRegistry();\n","import { nanoid } from 'nanoid';\nimport type { CollectionDef, EntityDef, Optimistic } from './types';\nimport { registry } from './registry';\n\n/** Transaction returned from channel methods */\nexport interface OptimisticTransaction {\n target: CollectionDef<any, any> | EntityDef<any, any>;\n action: 'prepend' | 'append' | 'update' | 'delete' | 'replace';\n data?: any;\n id?: string;\n where?: (item: any) => boolean;\n update?: (item: any) => any;\n sync?: boolean;\n rollback: () => void;\n}\n\n/** Options for channel operations */\nexport interface ChannelOptions {\n /**\n * Only apply updates to queries whose params partially match this object.\n * All key-value pairs in scope must exist in the query's params.\n * @example\n * channel(ordersCollection, { scope: { chain: 'solana' } }).delete(id)\n * // Only affects queries with params containing chain: 'solana'\n */\n scope?: Record<string, unknown>;\n}\n\n/** Channel for a collection - provides typed optimistic mutation methods */\nexport class CollectionChannel<TEntity> {\n private readonly optimisticId = nanoid();\n\n constructor(\n private readonly target: CollectionDef<TEntity, any>,\n private readonly options?: ChannelOptions\n ) {}\n\n /**\n * Prepend an item to the collection\n * @returns Rollback function to undo the change\n */\n prepend(data: TEntity, options?: { sync?: boolean }): () => void {\n const optimisticData = {\n ...data,\n _optimistic: { id: this.optimisticId, status: 'pending' as const },\n };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'prepend', {\n data: optimisticData,\n }, this.options?.scope);\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Append an item to the collection\n * @returns Rollback function to undo the change\n */\n append(data: TEntity, options?: { sync?: boolean }): () => void {\n const optimisticData = {\n ...data,\n _optimistic: { id: this.optimisticId, status: 'pending' as const },\n };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'append', {\n data: optimisticData,\n }, this.options?.scope);\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Update an item in the collection by ID\n * @returns Rollback function to undo the change\n */\n update(\n id: string,\n updateFn: (item: TEntity) => TEntity,\n options?: { sync?: boolean }\n ): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'update', {\n id,\n update: updateFn,\n }, this.options?.scope);\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Update items matching a predicate\n * @returns Rollback function to undo the change\n */\n updateWhere(\n where: (item: TEntity) => boolean,\n updateFn: (item: TEntity) => TEntity\n ): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'update', {\n where,\n update: updateFn,\n }, this.options?.scope);\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Delete an item from the collection by ID\n * @returns Rollback function to undo the change\n */\n delete(id: string): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'delete', {\n id,\n }, this.options?.scope);\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Delete items matching a predicate\n * @returns Rollback function to undo the change\n */\n deleteWhere(where: (item: TEntity) => boolean): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'delete', {\n where,\n }, this.options?.scope);\n\n return () => rollbacks.forEach((rb) => rb());\n }\n}\n\n/** Channel for an entity - provides typed optimistic mutation methods */\nexport class EntityChannel<TEntity> {\n constructor(private readonly target: EntityDef<TEntity, any>) {}\n\n /**\n * Update the entity\n * @returns Rollback function to undo the change\n */\n update(updateFn: (item: TEntity) => TEntity, options?: { sync?: boolean }): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'update', {\n update: updateFn,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n\n /**\n * Replace the entity with new data\n * @returns Rollback function to undo the change\n */\n replace(data: TEntity, options?: { sync?: boolean }): () => void {\n const rollbacks = registry.applyUpdate(this.target.name, 'replace', {\n data: data as any,\n });\n\n return () => rollbacks.forEach((rb) => rb());\n }\n}\n\n/**\n * Channel function for optimistic mutations.\n * Call with a collection or entity to get typed mutation methods.\n *\n * @example\n * // Standalone usage\n * const rollback = channel(usersCollection).prepend({ id: '1', name: 'John' });\n * // Later, to undo:\n * rollback();\n *\n * @example\n * // Update an entity\n * channel(userEntity).update(user => ({ ...user, name: 'Jane' }));\n *\n * @example\n * // Scoped update\n * channel(ordersCollection, { scope: { chain: 'solana' } }).delete(id);\n * // Only affects queries with params containing chain: 'solana'\n */\nexport interface Channel {\n <TEntity>(target: CollectionDef<TEntity, any>, options?: ChannelOptions): CollectionChannel<TEntity>;\n <TEntity>(target: EntityDef<TEntity, any>): EntityChannel<TEntity>;\n}\n\n/**\n * Create a channel for optimistic mutations.\n * Use this to apply immediate UI updates that can be rolled back.\n *\n * @example\n * const rollback = channel(usersCollection).prepend(newUser);\n * try {\n * await api.createUser(newUser);\n * } catch (error) {\n * rollback(); // Undo the optimistic update\n * }\n *\n * @example\n * // Scoped update - only affects queries with matching params\n * channel(ordersCollection, { scope: { chain: 'solana', status: 'pending' } }).delete(id);\n */\nexport const channel: Channel = (<TEntity>(\n target: CollectionDef<TEntity, any> | EntityDef<TEntity, any>,\n options?: ChannelOptions\n): CollectionChannel<TEntity> | EntityChannel<TEntity> => {\n if (target._type === 'collection') {\n return new CollectionChannel(target, options);\n } else {\n return new EntityChannel(target);\n }\n}) as Channel;\n"]}
|
package/dist/core/index.mjs
CHANGED
|
@@ -28,6 +28,16 @@ function defineMutation(config) {
|
|
|
28
28
|
var QueryRegistry = class {
|
|
29
29
|
constructor() {
|
|
30
30
|
this.entries = /* @__PURE__ */ new Map();
|
|
31
|
+
this.queryClient = null;
|
|
32
|
+
this.collectionDefs = /* @__PURE__ */ new Map();
|
|
33
|
+
}
|
|
34
|
+
/** Set the query client for direct cache access */
|
|
35
|
+
setQueryClient(client) {
|
|
36
|
+
this.queryClient = client;
|
|
37
|
+
}
|
|
38
|
+
/** Register a collection definition for direct cache updates */
|
|
39
|
+
registerDef(def) {
|
|
40
|
+
this.collectionDefs.set(def.name, def);
|
|
31
41
|
}
|
|
32
42
|
/** Register an active query */
|
|
33
43
|
register(entry) {
|
|
@@ -35,6 +45,9 @@ var QueryRegistry = class {
|
|
|
35
45
|
this.entries.set(entry.name, /* @__PURE__ */ new Set());
|
|
36
46
|
}
|
|
37
47
|
this.entries.get(entry.name).add(entry);
|
|
48
|
+
if (entry.kind === "collection" || entry.kind === "paginated") {
|
|
49
|
+
this.collectionDefs.set(entry.name, entry.def);
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
52
|
/** Unregister a query when component unmounts */
|
|
40
53
|
unregister(entry) {
|
|
@@ -50,8 +63,20 @@ var QueryRegistry = class {
|
|
|
50
63
|
getByName(name) {
|
|
51
64
|
return Array.from(this.entries.get(name) ?? []);
|
|
52
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if params partially match the given scope object.
|
|
68
|
+
* Returns true if all key-value pairs in scope exist in params.
|
|
69
|
+
*/
|
|
70
|
+
matchesScope(params, scope) {
|
|
71
|
+
if (!scope) return true;
|
|
72
|
+
if (!params) return false;
|
|
73
|
+
return Object.entries(scope).every(([key, value]) => params[key] === value);
|
|
74
|
+
}
|
|
53
75
|
/** Apply an optimistic update to all queries with given name */
|
|
54
|
-
applyUpdate(name, action, payload) {
|
|
76
|
+
applyUpdate(name, action, payload, scope) {
|
|
77
|
+
if (scope && this.queryClient) {
|
|
78
|
+
return this.applyDirectCacheUpdate(name, action, payload, scope);
|
|
79
|
+
}
|
|
55
80
|
const entries = this.getByName(name);
|
|
56
81
|
const rollbacks = [];
|
|
57
82
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
@@ -59,7 +84,8 @@ var QueryRegistry = class {
|
|
|
59
84
|
const key = JSON.stringify(entry.queryKey);
|
|
60
85
|
if (seenKeys.has(key)) return false;
|
|
61
86
|
seenKeys.add(key);
|
|
62
|
-
|
|
87
|
+
const params = entry.queryKey[1];
|
|
88
|
+
return this.matchesScope(params, scope);
|
|
63
89
|
});
|
|
64
90
|
for (const entry of uniqueEntries) {
|
|
65
91
|
if (entry.kind === "collection") {
|
|
@@ -96,6 +122,45 @@ var QueryRegistry = class {
|
|
|
96
122
|
}
|
|
97
123
|
return rollbacks;
|
|
98
124
|
}
|
|
125
|
+
/** Apply update directly to query cache (used when scope is provided) */
|
|
126
|
+
applyDirectCacheUpdate(name, action, payload, scope) {
|
|
127
|
+
if (!this.queryClient) return [];
|
|
128
|
+
const def = this.collectionDefs.get(name);
|
|
129
|
+
if (!def) return [];
|
|
130
|
+
const rollbacks = [];
|
|
131
|
+
const queries = this.queryClient.getQueriesData({
|
|
132
|
+
queryKey: [name]
|
|
133
|
+
});
|
|
134
|
+
for (const [queryKey, data] of queries) {
|
|
135
|
+
if (!data) continue;
|
|
136
|
+
const params = queryKey[1];
|
|
137
|
+
if (!this.matchesScope(params, scope)) continue;
|
|
138
|
+
const isPaginated = data && typeof data === "object" && "pages" in data;
|
|
139
|
+
if (isPaginated) {
|
|
140
|
+
const paginatedData = data;
|
|
141
|
+
const previous = paginatedData;
|
|
142
|
+
rollbacks.push(() => this.queryClient.setQueryData(queryKey, previous));
|
|
143
|
+
this.queryClient.setQueryData(queryKey, (prev) => {
|
|
144
|
+
if (!prev) return prev;
|
|
145
|
+
return {
|
|
146
|
+
...prev,
|
|
147
|
+
pages: prev.pages.map(
|
|
148
|
+
(page, i) => i === 0 ? this.applyCollectionUpdate(page, action, payload, def.id) : page
|
|
149
|
+
)
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
const arrayData = data;
|
|
154
|
+
const previous = arrayData;
|
|
155
|
+
rollbacks.push(() => this.queryClient.setQueryData(queryKey, previous));
|
|
156
|
+
this.queryClient.setQueryData(queryKey, (prev) => {
|
|
157
|
+
if (!prev) return prev;
|
|
158
|
+
return this.applyCollectionUpdate(prev, action, payload, def.id);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return rollbacks;
|
|
163
|
+
}
|
|
99
164
|
applyCollectionUpdate(items, action, payload, getId) {
|
|
100
165
|
switch (action) {
|
|
101
166
|
case "prepend":
|
|
@@ -131,8 +196,9 @@ var QueryRegistry = class {
|
|
|
131
196
|
};
|
|
132
197
|
var registry = new QueryRegistry();
|
|
133
198
|
var CollectionChannel = class {
|
|
134
|
-
constructor(target) {
|
|
199
|
+
constructor(target, options) {
|
|
135
200
|
this.target = target;
|
|
201
|
+
this.options = options;
|
|
136
202
|
this.optimisticId = nanoid();
|
|
137
203
|
}
|
|
138
204
|
/**
|
|
@@ -146,7 +212,7 @@ var CollectionChannel = class {
|
|
|
146
212
|
};
|
|
147
213
|
const rollbacks = registry.applyUpdate(this.target.name, "prepend", {
|
|
148
214
|
data: optimisticData
|
|
149
|
-
});
|
|
215
|
+
}, this.options?.scope);
|
|
150
216
|
return () => rollbacks.forEach((rb) => rb());
|
|
151
217
|
}
|
|
152
218
|
/**
|
|
@@ -160,7 +226,7 @@ var CollectionChannel = class {
|
|
|
160
226
|
};
|
|
161
227
|
const rollbacks = registry.applyUpdate(this.target.name, "append", {
|
|
162
228
|
data: optimisticData
|
|
163
|
-
});
|
|
229
|
+
}, this.options?.scope);
|
|
164
230
|
return () => rollbacks.forEach((rb) => rb());
|
|
165
231
|
}
|
|
166
232
|
/**
|
|
@@ -171,7 +237,7 @@ var CollectionChannel = class {
|
|
|
171
237
|
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
172
238
|
id,
|
|
173
239
|
update: updateFn
|
|
174
|
-
});
|
|
240
|
+
}, this.options?.scope);
|
|
175
241
|
return () => rollbacks.forEach((rb) => rb());
|
|
176
242
|
}
|
|
177
243
|
/**
|
|
@@ -182,7 +248,7 @@ var CollectionChannel = class {
|
|
|
182
248
|
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
183
249
|
where,
|
|
184
250
|
update: updateFn
|
|
185
|
-
});
|
|
251
|
+
}, this.options?.scope);
|
|
186
252
|
return () => rollbacks.forEach((rb) => rb());
|
|
187
253
|
}
|
|
188
254
|
/**
|
|
@@ -192,7 +258,7 @@ var CollectionChannel = class {
|
|
|
192
258
|
delete(id) {
|
|
193
259
|
const rollbacks = registry.applyUpdate(this.target.name, "delete", {
|
|
194
260
|
id
|
|
195
|
-
});
|
|
261
|
+
}, this.options?.scope);
|
|
196
262
|
return () => rollbacks.forEach((rb) => rb());
|
|
197
263
|
}
|
|
198
264
|
/**
|
|
@@ -202,7 +268,7 @@ var CollectionChannel = class {
|
|
|
202
268
|
deleteWhere(where) {
|
|
203
269
|
const rollbacks = registry.applyUpdate(this.target.name, "delete", {
|
|
204
270
|
where
|
|
205
|
-
});
|
|
271
|
+
}, this.options?.scope);
|
|
206
272
|
return () => rollbacks.forEach((rb) => rb());
|
|
207
273
|
}
|
|
208
274
|
};
|
|
@@ -231,9 +297,9 @@ var EntityChannel = class {
|
|
|
231
297
|
return () => rollbacks.forEach((rb) => rb());
|
|
232
298
|
}
|
|
233
299
|
};
|
|
234
|
-
var channel = ((target) => {
|
|
300
|
+
var channel = ((target, options) => {
|
|
235
301
|
if (target._type === "collection") {
|
|
236
|
-
return new CollectionChannel(target);
|
|
302
|
+
return new CollectionChannel(target, options);
|
|
237
303
|
} else {
|
|
238
304
|
return new EntityChannel(target);
|
|
239
305
|
}
|