query-optimistic 0.11.2 → 0.11.4
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 +7 -1
- package/dist/core/index.d.ts +7 -1
- package/dist/core/index.js +19 -13
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +19 -13
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.js +25 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +25 -18
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +2 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +21 -8
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +21 -8
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/core/index.d.mts
CHANGED
|
@@ -99,6 +99,12 @@ declare class QueryRegistry {
|
|
|
99
99
|
unregister(entry: RegisteredEntry): void;
|
|
100
100
|
/** Get all registered entries for a query name */
|
|
101
101
|
getByName(name: string): RegisteredEntry[];
|
|
102
|
+
/**
|
|
103
|
+
* Extract params from a queryKey.
|
|
104
|
+
* Params are always the last element if it's a plain object (not an array).
|
|
105
|
+
* Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].
|
|
106
|
+
*/
|
|
107
|
+
private extractParams;
|
|
102
108
|
/**
|
|
103
109
|
* Check if params partially match the given scope object.
|
|
104
110
|
* Returns true if all key-value pairs in scope exist in params.
|
|
@@ -106,7 +112,7 @@ declare class QueryRegistry {
|
|
|
106
112
|
private matchesScope;
|
|
107
113
|
/** Apply an optimistic update to all queries with given name */
|
|
108
114
|
applyUpdate<T>(name: string, action: 'prepend' | 'append' | 'update' | 'delete' | 'replace', payload: {
|
|
109
|
-
data?: Partial<Optimistic<T>>;
|
|
115
|
+
data?: T | Partial<Optimistic<T>>;
|
|
110
116
|
id?: string;
|
|
111
117
|
where?: (item: T) => boolean;
|
|
112
118
|
update?: (item: T) => T;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -99,6 +99,12 @@ declare class QueryRegistry {
|
|
|
99
99
|
unregister(entry: RegisteredEntry): void;
|
|
100
100
|
/** Get all registered entries for a query name */
|
|
101
101
|
getByName(name: string): RegisteredEntry[];
|
|
102
|
+
/**
|
|
103
|
+
* Extract params from a queryKey.
|
|
104
|
+
* Params are always the last element if it's a plain object (not an array).
|
|
105
|
+
* Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].
|
|
106
|
+
*/
|
|
107
|
+
private extractParams;
|
|
102
108
|
/**
|
|
103
109
|
* Check if params partially match the given scope object.
|
|
104
110
|
* Returns true if all key-value pairs in scope exist in params.
|
|
@@ -106,7 +112,7 @@ declare class QueryRegistry {
|
|
|
106
112
|
private matchesScope;
|
|
107
113
|
/** Apply an optimistic update to all queries with given name */
|
|
108
114
|
applyUpdate<T>(name: string, action: 'prepend' | 'append' | 'update' | 'delete' | 'replace', payload: {
|
|
109
|
-
data?: Partial<Optimistic<T>>;
|
|
115
|
+
data?: T | Partial<Optimistic<T>>;
|
|
110
116
|
id?: string;
|
|
111
117
|
where?: (item: T) => boolean;
|
|
112
118
|
update?: (item: T) => T;
|
package/dist/core/index.js
CHANGED
|
@@ -65,6 +65,18 @@ var QueryRegistry = class {
|
|
|
65
65
|
getByName(name) {
|
|
66
66
|
return Array.from(this.entries.get(name) ?? []);
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Extract params from a queryKey.
|
|
70
|
+
* Params are always the last element if it's a plain object (not an array).
|
|
71
|
+
* Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].
|
|
72
|
+
*/
|
|
73
|
+
extractParams(queryKey) {
|
|
74
|
+
const last = queryKey[queryKey.length - 1];
|
|
75
|
+
if (last && typeof last === "object" && !Array.isArray(last)) {
|
|
76
|
+
return last;
|
|
77
|
+
}
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
68
80
|
/**
|
|
69
81
|
* Check if params partially match the given scope object.
|
|
70
82
|
* Returns true if all key-value pairs in scope exist in params.
|
|
@@ -86,7 +98,7 @@ var QueryRegistry = class {
|
|
|
86
98
|
const key = JSON.stringify(entry.queryKey);
|
|
87
99
|
if (seenKeys.has(key)) return false;
|
|
88
100
|
seenKeys.add(key);
|
|
89
|
-
const params = entry.queryKey
|
|
101
|
+
const params = this.extractParams(entry.queryKey);
|
|
90
102
|
return this.matchesScope(params, scope);
|
|
91
103
|
});
|
|
92
104
|
for (const entry of uniqueEntries) {
|
|
@@ -130,11 +142,11 @@ var QueryRegistry = class {
|
|
|
130
142
|
const collectionDef = this.collectionDefs.get(name);
|
|
131
143
|
const rollbacks = [];
|
|
132
144
|
const queries = this.queryClient.getQueriesData({
|
|
133
|
-
|
|
145
|
+
predicate: (query) => query.queryKey.includes(name)
|
|
134
146
|
});
|
|
135
147
|
for (const [queryKey, data] of queries) {
|
|
136
148
|
if (!data) continue;
|
|
137
|
-
const params = queryKey
|
|
149
|
+
const params = this.extractParams(queryKey);
|
|
138
150
|
if (!this.matchesScope(params, scope)) continue;
|
|
139
151
|
if (collectionDef) {
|
|
140
152
|
const isPaginated = data && typeof data === "object" && "pages" in data;
|
|
@@ -219,12 +231,9 @@ var CollectionChannel = class {
|
|
|
219
231
|
* @returns Rollback function to undo the change
|
|
220
232
|
*/
|
|
221
233
|
prepend(data, options) {
|
|
222
|
-
const
|
|
223
|
-
...data,
|
|
224
|
-
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
225
|
-
};
|
|
234
|
+
const payload = options?.sync ? data : { ...data, _optimistic: { id: this.optimisticId, status: "pending" } };
|
|
226
235
|
const rollbacks = registry.applyUpdate(this.target.name, "prepend", {
|
|
227
|
-
data:
|
|
236
|
+
data: payload
|
|
228
237
|
}, this.options?.scope);
|
|
229
238
|
return () => rollbacks.forEach((rb) => rb());
|
|
230
239
|
}
|
|
@@ -233,12 +242,9 @@ var CollectionChannel = class {
|
|
|
233
242
|
* @returns Rollback function to undo the change
|
|
234
243
|
*/
|
|
235
244
|
append(data, options) {
|
|
236
|
-
const
|
|
237
|
-
...data,
|
|
238
|
-
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
239
|
-
};
|
|
245
|
+
const payload = options?.sync ? data : { ...data, _optimistic: { id: this.optimisticId, status: "pending" } };
|
|
240
246
|
const rollbacks = registry.applyUpdate(this.target.name, "append", {
|
|
241
|
-
data:
|
|
247
|
+
data: payload
|
|
242
248
|
}, this.options?.scope);
|
|
243
249
|
return () => rollbacks.forEach((rb) => rb());
|
|
244
250
|
}
|
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 { 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 collectionDef = this.collectionDefs.get(name);\n const rollbacks: (() => void)[] = [];\n\n // Get all queries with this name from the cache\n const queries = this.queryClient.getQueriesData<any>({\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 if (collectionDef) {\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, collectionDef.id)\n : page\n ),\n };\n });\n } else {\n const previous = data as T[];\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, collectionDef.id);\n });\n }\n } else {\n // Entity: apply update/replace directly\n const previous = data as T;\n rollbacks.push(() => this.queryClient!.setQueryData(queryKey, previous));\n\n if (action === 'update' && payload.update) {\n this.queryClient.setQueryData<T>(queryKey, (prev) =>\n prev ? payload.update!(prev) : prev\n );\n } else if (action === 'replace' && payload.data) {\n this.queryClient.setQueryData(queryKey, payload.data);\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(\n private readonly target: EntityDef<TEntity, any>,\n private readonly options?: ChannelOptions\n ) {}\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 }, this.options?.scope);\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 }, this.options?.scope);\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>, options?: ChannelOptions): 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, options);\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 * Extract params from a queryKey.\n * Params are always the last element if it's a plain object (not an array).\n * Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].\n */\n private extractParams(queryKey: readonly unknown[]): Record<string, unknown> | undefined {\n const last = queryKey[queryKey.length - 1];\n if (last && typeof last === 'object' && !Array.isArray(last)) {\n return last as Record<string, unknown>;\n }\n return undefined;\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?: T | 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 = this.extractParams(entry.queryKey);\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?: T | 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 collectionDef = this.collectionDefs.get(name);\n const rollbacks: (() => void)[] = [];\n\n // Get all queries with this name from the cache\n // Use predicate to support both standard [name, params] and prefixed [...prefix, name, params] keys\n const queries = this.queryClient.getQueriesData<any>({\n predicate: (query) => query.queryKey.includes(name),\n });\n\n for (const [queryKey, data] of queries) {\n if (!data) continue;\n\n // Extract params from queryKey (last element if it's an object)\n const params = this.extractParams(queryKey);\n if (!this.matchesScope(params, scope)) continue;\n\n if (collectionDef) {\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, collectionDef.id)\n : page\n ),\n };\n });\n } else {\n const previous = data as T[];\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, collectionDef.id);\n });\n }\n } else {\n // Entity: apply update/replace directly\n const previous = data as T;\n rollbacks.push(() => this.queryClient!.setQueryData(queryKey, previous));\n\n if (action === 'update' && payload.update) {\n this.queryClient.setQueryData<T>(queryKey, (prev) =>\n prev ? payload.update!(prev) : prev\n );\n } else if (action === 'replace' && payload.data) {\n this.queryClient.setQueryData(queryKey, payload.data);\n }\n }\n }\n\n return rollbacks;\n }\n\n private applyCollectionUpdate<T>(\n items: T[],\n action: string,\n payload: {\n data?: T | 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 payload = options?.sync\n ? data\n : { ...data, _optimistic: { id: this.optimisticId, status: 'pending' as const } };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'prepend', {\n data: payload,\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 payload = options?.sync\n ? data\n : { ...data, _optimistic: { id: this.optimisticId, status: 'pending' as const } };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'append', {\n data: payload,\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(\n private readonly target: EntityDef<TEntity, any>,\n private readonly options?: ChannelOptions\n ) {}\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 }, this.options?.scope);\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 }, this.options?.scope);\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>, options?: ChannelOptions): 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, options);\n }\n}) as Channel;\n"]}
|
package/dist/core/index.mjs
CHANGED
|
@@ -63,6 +63,18 @@ var QueryRegistry = class {
|
|
|
63
63
|
getByName(name) {
|
|
64
64
|
return Array.from(this.entries.get(name) ?? []);
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Extract params from a queryKey.
|
|
68
|
+
* Params are always the last element if it's a plain object (not an array).
|
|
69
|
+
* Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].
|
|
70
|
+
*/
|
|
71
|
+
extractParams(queryKey) {
|
|
72
|
+
const last = queryKey[queryKey.length - 1];
|
|
73
|
+
if (last && typeof last === "object" && !Array.isArray(last)) {
|
|
74
|
+
return last;
|
|
75
|
+
}
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
66
78
|
/**
|
|
67
79
|
* Check if params partially match the given scope object.
|
|
68
80
|
* Returns true if all key-value pairs in scope exist in params.
|
|
@@ -84,7 +96,7 @@ var QueryRegistry = class {
|
|
|
84
96
|
const key = JSON.stringify(entry.queryKey);
|
|
85
97
|
if (seenKeys.has(key)) return false;
|
|
86
98
|
seenKeys.add(key);
|
|
87
|
-
const params = entry.queryKey
|
|
99
|
+
const params = this.extractParams(entry.queryKey);
|
|
88
100
|
return this.matchesScope(params, scope);
|
|
89
101
|
});
|
|
90
102
|
for (const entry of uniqueEntries) {
|
|
@@ -128,11 +140,11 @@ var QueryRegistry = class {
|
|
|
128
140
|
const collectionDef = this.collectionDefs.get(name);
|
|
129
141
|
const rollbacks = [];
|
|
130
142
|
const queries = this.queryClient.getQueriesData({
|
|
131
|
-
|
|
143
|
+
predicate: (query) => query.queryKey.includes(name)
|
|
132
144
|
});
|
|
133
145
|
for (const [queryKey, data] of queries) {
|
|
134
146
|
if (!data) continue;
|
|
135
|
-
const params = queryKey
|
|
147
|
+
const params = this.extractParams(queryKey);
|
|
136
148
|
if (!this.matchesScope(params, scope)) continue;
|
|
137
149
|
if (collectionDef) {
|
|
138
150
|
const isPaginated = data && typeof data === "object" && "pages" in data;
|
|
@@ -217,12 +229,9 @@ var CollectionChannel = class {
|
|
|
217
229
|
* @returns Rollback function to undo the change
|
|
218
230
|
*/
|
|
219
231
|
prepend(data, options) {
|
|
220
|
-
const
|
|
221
|
-
...data,
|
|
222
|
-
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
223
|
-
};
|
|
232
|
+
const payload = options?.sync ? data : { ...data, _optimistic: { id: this.optimisticId, status: "pending" } };
|
|
224
233
|
const rollbacks = registry.applyUpdate(this.target.name, "prepend", {
|
|
225
|
-
data:
|
|
234
|
+
data: payload
|
|
226
235
|
}, this.options?.scope);
|
|
227
236
|
return () => rollbacks.forEach((rb) => rb());
|
|
228
237
|
}
|
|
@@ -231,12 +240,9 @@ var CollectionChannel = class {
|
|
|
231
240
|
* @returns Rollback function to undo the change
|
|
232
241
|
*/
|
|
233
242
|
append(data, options) {
|
|
234
|
-
const
|
|
235
|
-
...data,
|
|
236
|
-
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
237
|
-
};
|
|
243
|
+
const payload = options?.sync ? data : { ...data, _optimistic: { id: this.optimisticId, status: "pending" } };
|
|
238
244
|
const rollbacks = registry.applyUpdate(this.target.name, "append", {
|
|
239
|
-
data:
|
|
245
|
+
data: payload
|
|
240
246
|
}, this.options?.scope);
|
|
241
247
|
return () => rollbacks.forEach((rb) => rb());
|
|
242
248
|
}
|
package/dist/core/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/define.ts","../../src/core/registry.ts","../../src/core/channel.ts"],"names":[],"mappings":"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","file":"index.mjs","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 collectionDef = this.collectionDefs.get(name);\n const rollbacks: (() => void)[] = [];\n\n // Get all queries with this name from the cache\n const queries = this.queryClient.getQueriesData<any>({\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 if (collectionDef) {\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, collectionDef.id)\n : page\n ),\n };\n });\n } else {\n const previous = data as T[];\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, collectionDef.id);\n });\n }\n } else {\n // Entity: apply update/replace directly\n const previous = data as T;\n rollbacks.push(() => this.queryClient!.setQueryData(queryKey, previous));\n\n if (action === 'update' && payload.update) {\n this.queryClient.setQueryData<T>(queryKey, (prev) =>\n prev ? payload.update!(prev) : prev\n );\n } else if (action === 'replace' && payload.data) {\n this.queryClient.setQueryData(queryKey, payload.data);\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(\n private readonly target: EntityDef<TEntity, any>,\n private readonly options?: ChannelOptions\n ) {}\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 }, this.options?.scope);\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 }, this.options?.scope);\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>, options?: ChannelOptions): 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, options);\n }\n}) as Channel;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/define.ts","../../src/core/registry.ts","../../src/core/channel.ts"],"names":[],"mappings":"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","file":"index.mjs","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 * Extract params from a queryKey.\n * Params are always the last element if it's a plain object (not an array).\n * Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].\n */\n private extractParams(queryKey: readonly unknown[]): Record<string, unknown> | undefined {\n const last = queryKey[queryKey.length - 1];\n if (last && typeof last === 'object' && !Array.isArray(last)) {\n return last as Record<string, unknown>;\n }\n return undefined;\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?: T | 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 = this.extractParams(entry.queryKey);\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?: T | 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 collectionDef = this.collectionDefs.get(name);\n const rollbacks: (() => void)[] = [];\n\n // Get all queries with this name from the cache\n // Use predicate to support both standard [name, params] and prefixed [...prefix, name, params] keys\n const queries = this.queryClient.getQueriesData<any>({\n predicate: (query) => query.queryKey.includes(name),\n });\n\n for (const [queryKey, data] of queries) {\n if (!data) continue;\n\n // Extract params from queryKey (last element if it's an object)\n const params = this.extractParams(queryKey);\n if (!this.matchesScope(params, scope)) continue;\n\n if (collectionDef) {\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, collectionDef.id)\n : page\n ),\n };\n });\n } else {\n const previous = data as T[];\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, collectionDef.id);\n });\n }\n } else {\n // Entity: apply update/replace directly\n const previous = data as T;\n rollbacks.push(() => this.queryClient!.setQueryData(queryKey, previous));\n\n if (action === 'update' && payload.update) {\n this.queryClient.setQueryData<T>(queryKey, (prev) =>\n prev ? payload.update!(prev) : prev\n );\n } else if (action === 'replace' && payload.data) {\n this.queryClient.setQueryData(queryKey, payload.data);\n }\n }\n }\n\n return rollbacks;\n }\n\n private applyCollectionUpdate<T>(\n items: T[],\n action: string,\n payload: {\n data?: T | 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 payload = options?.sync\n ? data\n : { ...data, _optimistic: { id: this.optimisticId, status: 'pending' as const } };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'prepend', {\n data: payload,\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 payload = options?.sync\n ? data\n : { ...data, _optimistic: { id: this.optimisticId, status: 'pending' as const } };\n\n const rollbacks = registry.applyUpdate(this.target.name, 'append', {\n data: payload,\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(\n private readonly target: EntityDef<TEntity, any>,\n private readonly options?: ChannelOptions\n ) {}\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 }, this.options?.scope);\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 }, this.options?.scope);\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>, options?: ChannelOptions): 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, options);\n }\n}) as Channel;\n"]}
|
package/dist/index.js
CHANGED
|
@@ -68,6 +68,18 @@ var QueryRegistry = class {
|
|
|
68
68
|
getByName(name) {
|
|
69
69
|
return Array.from(this.entries.get(name) ?? []);
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract params from a queryKey.
|
|
73
|
+
* Params are always the last element if it's a plain object (not an array).
|
|
74
|
+
* Supports both standard keys [name, params] and prefixed keys [...prefix, name, params].
|
|
75
|
+
*/
|
|
76
|
+
extractParams(queryKey) {
|
|
77
|
+
const last = queryKey[queryKey.length - 1];
|
|
78
|
+
if (last && typeof last === "object" && !Array.isArray(last)) {
|
|
79
|
+
return last;
|
|
80
|
+
}
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
71
83
|
/**
|
|
72
84
|
* Check if params partially match the given scope object.
|
|
73
85
|
* Returns true if all key-value pairs in scope exist in params.
|
|
@@ -89,7 +101,7 @@ var QueryRegistry = class {
|
|
|
89
101
|
const key = JSON.stringify(entry.queryKey);
|
|
90
102
|
if (seenKeys.has(key)) return false;
|
|
91
103
|
seenKeys.add(key);
|
|
92
|
-
const params = entry.queryKey
|
|
104
|
+
const params = this.extractParams(entry.queryKey);
|
|
93
105
|
return this.matchesScope(params, scope);
|
|
94
106
|
});
|
|
95
107
|
for (const entry of uniqueEntries) {
|
|
@@ -133,11 +145,11 @@ var QueryRegistry = class {
|
|
|
133
145
|
const collectionDef = this.collectionDefs.get(name);
|
|
134
146
|
const rollbacks = [];
|
|
135
147
|
const queries = this.queryClient.getQueriesData({
|
|
136
|
-
|
|
148
|
+
predicate: (query) => query.queryKey.includes(name)
|
|
137
149
|
});
|
|
138
150
|
for (const [queryKey, data] of queries) {
|
|
139
151
|
if (!data) continue;
|
|
140
|
-
const params = queryKey
|
|
152
|
+
const params = this.extractParams(queryKey);
|
|
141
153
|
if (!this.matchesScope(params, scope)) continue;
|
|
142
154
|
if (collectionDef) {
|
|
143
155
|
const isPaginated = data && typeof data === "object" && "pages" in data;
|
|
@@ -222,12 +234,9 @@ var CollectionChannel = class {
|
|
|
222
234
|
* @returns Rollback function to undo the change
|
|
223
235
|
*/
|
|
224
236
|
prepend(data, options) {
|
|
225
|
-
const
|
|
226
|
-
...data,
|
|
227
|
-
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
228
|
-
};
|
|
237
|
+
const payload = options?.sync ? data : { ...data, _optimistic: { id: this.optimisticId, status: "pending" } };
|
|
229
238
|
const rollbacks = registry.applyUpdate(this.target.name, "prepend", {
|
|
230
|
-
data:
|
|
239
|
+
data: payload
|
|
231
240
|
}, this.options?.scope);
|
|
232
241
|
return () => rollbacks.forEach((rb) => rb());
|
|
233
242
|
}
|
|
@@ -236,12 +245,9 @@ var CollectionChannel = class {
|
|
|
236
245
|
* @returns Rollback function to undo the change
|
|
237
246
|
*/
|
|
238
247
|
append(data, options) {
|
|
239
|
-
const
|
|
240
|
-
...data,
|
|
241
|
-
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
242
|
-
};
|
|
248
|
+
const payload = options?.sync ? data : { ...data, _optimistic: { id: this.optimisticId, status: "pending" } };
|
|
243
249
|
const rollbacks = registry.applyUpdate(this.target.name, "append", {
|
|
244
|
-
data:
|
|
250
|
+
data: payload
|
|
245
251
|
}, this.options?.scope);
|
|
246
252
|
return () => rollbacks.forEach((rb) => rb());
|
|
247
253
|
}
|
|
@@ -333,13 +339,14 @@ function useStableValue(value) {
|
|
|
333
339
|
}
|
|
334
340
|
function useQuery(def, options) {
|
|
335
341
|
const queryClient = reactQuery.useQueryClient();
|
|
336
|
-
const { params, paginated, getPageParams, getNextPageParam, select, queryKey: customQueryKey, syncInBackground, disableOptimistic, ...queryOptions } = options ?? {};
|
|
342
|
+
const { params, paginated, getPageParams, getNextPageParam, select, prefixQueryKey, queryKey: customQueryKey, syncInBackground, disableOptimistic, ...queryOptions } = options ?? {};
|
|
337
343
|
registry.setQueryClient(queryClient);
|
|
338
344
|
const stableParams = useStableValue(params);
|
|
339
345
|
const stableCustomQueryKey = useStableValue(customQueryKey);
|
|
346
|
+
const stablePrefixQueryKey = useStableValue(prefixQueryKey);
|
|
340
347
|
const queryKey = react.useMemo(
|
|
341
|
-
() => stableCustomQueryKey ?? [def.name, stableParams].filter(Boolean),
|
|
342
|
-
[stableCustomQueryKey, def.name, stableParams]
|
|
348
|
+
() => stableCustomQueryKey ?? [...stablePrefixQueryKey ?? [], def.name, stableParams].filter(Boolean),
|
|
349
|
+
[stableCustomQueryKey, stablePrefixQueryKey, def.name, stableParams]
|
|
343
350
|
);
|
|
344
351
|
if (def._type === "entity") {
|
|
345
352
|
const entityDef = def;
|
|
@@ -471,8 +478,8 @@ function useQueries(def, optionsArray) {
|
|
|
471
478
|
registry.setQueryClient(queryClient);
|
|
472
479
|
const queries = react.useMemo(
|
|
473
480
|
() => optionsArray.map((opts) => {
|
|
474
|
-
const { params, queryKey: customQueryKey, ...queryOptions } = opts;
|
|
475
|
-
const queryKey = customQueryKey ?? [def.name, params].filter(Boolean);
|
|
481
|
+
const { params, prefixQueryKey, queryKey: customQueryKey, ...queryOptions } = opts;
|
|
482
|
+
const queryKey = customQueryKey ?? [...prefixQueryKey ?? [], def.name, params].filter(Boolean);
|
|
476
483
|
return {
|
|
477
484
|
queryKey,
|
|
478
485
|
queryFn: () => def.fetch(params),
|