query-optimistic 0.1.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/README.md +371 -0
- package/dist/core/index.d.mts +211 -0
- package/dist/core/index.d.ts +211 -0
- package/dist/core/index.js +245 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +237 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +513 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +503 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +213 -0
- package/dist/react/index.d.ts +213 -0
- package/dist/react/index.js +378 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +375 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/types-BRxQA1mR.d.mts +63 -0
- package/dist/types-BRxQA1mR.d.ts +63 -0
- package/package.json +76 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
|
|
3
|
+
// src/core/define.ts
|
|
4
|
+
function defineCollection(config) {
|
|
5
|
+
return {
|
|
6
|
+
_type: "collection",
|
|
7
|
+
name: config.name,
|
|
8
|
+
id: config.id,
|
|
9
|
+
fetch: config.fetch
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function defineEntity(config) {
|
|
13
|
+
return {
|
|
14
|
+
_type: "entity",
|
|
15
|
+
name: config.name,
|
|
16
|
+
fetch: config.fetch
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function defineMutation(config) {
|
|
20
|
+
return {
|
|
21
|
+
_type: "mutation",
|
|
22
|
+
name: config.name,
|
|
23
|
+
mutate: config.mutate
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/core/registry.ts
|
|
28
|
+
var QueryRegistry = class {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
31
|
+
}
|
|
32
|
+
/** Register an active query */
|
|
33
|
+
register(entry) {
|
|
34
|
+
if (!this.entries.has(entry.name)) {
|
|
35
|
+
this.entries.set(entry.name, /* @__PURE__ */ new Set());
|
|
36
|
+
}
|
|
37
|
+
this.entries.get(entry.name).add(entry);
|
|
38
|
+
}
|
|
39
|
+
/** Unregister a query when component unmounts */
|
|
40
|
+
unregister(entry) {
|
|
41
|
+
const set = this.entries.get(entry.name);
|
|
42
|
+
if (set) {
|
|
43
|
+
set.delete(entry);
|
|
44
|
+
if (set.size === 0) {
|
|
45
|
+
this.entries.delete(entry.name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Get all registered entries for a query name */
|
|
50
|
+
getByName(name) {
|
|
51
|
+
return Array.from(this.entries.get(name) ?? []);
|
|
52
|
+
}
|
|
53
|
+
/** Apply an optimistic update to all queries with given name */
|
|
54
|
+
applyUpdate(name, action, payload) {
|
|
55
|
+
const entries = this.getByName(name);
|
|
56
|
+
const rollbacks = [];
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
if (entry.kind === "collection") {
|
|
59
|
+
const previous = entry.getData();
|
|
60
|
+
const rollback = () => entry.setData(() => previous);
|
|
61
|
+
rollbacks.push(rollback);
|
|
62
|
+
entry.setData((prev) => {
|
|
63
|
+
if (!prev) return prev;
|
|
64
|
+
return this.applyCollectionUpdate(prev, action, payload, entry.def.id);
|
|
65
|
+
});
|
|
66
|
+
} else if (entry.kind === "paginated") {
|
|
67
|
+
const previous = entry.getData();
|
|
68
|
+
const rollback = () => entry.setData(() => previous);
|
|
69
|
+
rollbacks.push(rollback);
|
|
70
|
+
entry.setData((prev) => {
|
|
71
|
+
if (!prev) return prev;
|
|
72
|
+
return {
|
|
73
|
+
...prev,
|
|
74
|
+
pages: prev.pages.map(
|
|
75
|
+
(page, i) => i === 0 ? this.applyCollectionUpdate(page, action, payload, entry.def.id) : page
|
|
76
|
+
)
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
} else if (entry.kind === "entity") {
|
|
80
|
+
const previous = entry.getData();
|
|
81
|
+
const rollback = () => entry.setData(() => previous);
|
|
82
|
+
rollbacks.push(rollback);
|
|
83
|
+
if (action === "update" && payload.update) {
|
|
84
|
+
entry.setData((prev) => prev ? payload.update(prev) : prev);
|
|
85
|
+
} else if (action === "replace" && payload.data) {
|
|
86
|
+
entry.setData(() => payload.data);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return rollbacks;
|
|
91
|
+
}
|
|
92
|
+
applyCollectionUpdate(items, action, payload, getId) {
|
|
93
|
+
switch (action) {
|
|
94
|
+
case "prepend":
|
|
95
|
+
return payload.data ? [payload.data, ...items] : items;
|
|
96
|
+
case "append":
|
|
97
|
+
return payload.data ? [...items, payload.data] : items;
|
|
98
|
+
case "update":
|
|
99
|
+
return items.map((item) => {
|
|
100
|
+
const matches = payload.id ? getId(item) === payload.id : payload.where?.(item);
|
|
101
|
+
if (matches && payload.update) {
|
|
102
|
+
return payload.update(item);
|
|
103
|
+
}
|
|
104
|
+
if (matches && payload.data) {
|
|
105
|
+
return { ...item, ...payload.data };
|
|
106
|
+
}
|
|
107
|
+
return item;
|
|
108
|
+
});
|
|
109
|
+
case "delete":
|
|
110
|
+
return items.filter((item) => {
|
|
111
|
+
if (payload.id) return getId(item) !== payload.id;
|
|
112
|
+
if (payload.where) return !payload.where(item);
|
|
113
|
+
return true;
|
|
114
|
+
});
|
|
115
|
+
case "replace":
|
|
116
|
+
return items.map((item) => {
|
|
117
|
+
const matches = payload.id ? getId(item) === payload.id : payload.where?.(item);
|
|
118
|
+
return matches && payload.data ? payload.data : item;
|
|
119
|
+
});
|
|
120
|
+
default:
|
|
121
|
+
return items;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var registry = new QueryRegistry();
|
|
126
|
+
var CollectionChannel = class {
|
|
127
|
+
constructor(target) {
|
|
128
|
+
this.target = target;
|
|
129
|
+
this.optimisticId = nanoid();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Prepend an item to the collection
|
|
133
|
+
* @returns Rollback function to undo the change
|
|
134
|
+
*/
|
|
135
|
+
prepend(data, options) {
|
|
136
|
+
const optimisticData = {
|
|
137
|
+
...data,
|
|
138
|
+
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
139
|
+
};
|
|
140
|
+
const rollbacks = registry.applyUpdate(this.target.name, "prepend", {
|
|
141
|
+
data: optimisticData
|
|
142
|
+
});
|
|
143
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Append an item to the collection
|
|
147
|
+
* @returns Rollback function to undo the change
|
|
148
|
+
*/
|
|
149
|
+
append(data, options) {
|
|
150
|
+
const optimisticData = {
|
|
151
|
+
...data,
|
|
152
|
+
_optimistic: { id: this.optimisticId, status: "pending" }
|
|
153
|
+
};
|
|
154
|
+
const rollbacks = registry.applyUpdate(this.target.name, "append", {
|
|
155
|
+
data: optimisticData
|
|
156
|
+
});
|
|
157
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Update an item in the collection by ID
|
|
161
|
+
* @returns Rollback function to undo the change
|
|
162
|
+
*/
|
|
163
|
+
update(id, updateFn, options) {
|
|
164
|
+
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
165
|
+
id,
|
|
166
|
+
update: updateFn
|
|
167
|
+
});
|
|
168
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Update items matching a predicate
|
|
172
|
+
* @returns Rollback function to undo the change
|
|
173
|
+
*/
|
|
174
|
+
updateWhere(where, updateFn) {
|
|
175
|
+
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
176
|
+
where,
|
|
177
|
+
update: updateFn
|
|
178
|
+
});
|
|
179
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Delete an item from the collection by ID
|
|
183
|
+
* @returns Rollback function to undo the change
|
|
184
|
+
*/
|
|
185
|
+
delete(id) {
|
|
186
|
+
const rollbacks = registry.applyUpdate(this.target.name, "delete", {
|
|
187
|
+
id
|
|
188
|
+
});
|
|
189
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Delete items matching a predicate
|
|
193
|
+
* @returns Rollback function to undo the change
|
|
194
|
+
*/
|
|
195
|
+
deleteWhere(where) {
|
|
196
|
+
const rollbacks = registry.applyUpdate(this.target.name, "delete", {
|
|
197
|
+
where
|
|
198
|
+
});
|
|
199
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var EntityChannel = class {
|
|
203
|
+
constructor(target) {
|
|
204
|
+
this.target = target;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Update the entity
|
|
208
|
+
* @returns Rollback function to undo the change
|
|
209
|
+
*/
|
|
210
|
+
update(updateFn, options) {
|
|
211
|
+
const rollbacks = registry.applyUpdate(this.target.name, "update", {
|
|
212
|
+
update: updateFn
|
|
213
|
+
});
|
|
214
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Replace the entity with new data
|
|
218
|
+
* @returns Rollback function to undo the change
|
|
219
|
+
*/
|
|
220
|
+
replace(data, options) {
|
|
221
|
+
const rollbacks = registry.applyUpdate(this.target.name, "replace", {
|
|
222
|
+
data
|
|
223
|
+
});
|
|
224
|
+
return () => rollbacks.forEach((rb) => rb());
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
var channel = ((target) => {
|
|
228
|
+
if (target._type === "collection") {
|
|
229
|
+
return new CollectionChannel(target);
|
|
230
|
+
} else {
|
|
231
|
+
return new EntityChannel(target);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
export { CollectionChannel, EntityChannel, channel, defineCollection, defineEntity, defineMutation, registry };
|
|
236
|
+
//# sourceMappingURL=index.mjs.map
|
|
237
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 { 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 for (const entry of entries) {\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"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AnyDef, C as CollectionDef, E as EntityDef, I as IdGetter, M as MutationDef, a as Optimistic, b as OptimisticAction, c as OptimisticInstruction, O as OptimisticStatus, P as PaginatedOptions, Q as QueryOptions } from './types-BRxQA1mR.mjs';
|
|
2
|
+
export { Channel, CollectionChannel, EntityChannel, OptimisticTransaction, RegisteredCollection, RegisteredEntity, RegisteredEntry, RegisteredPaginatedCollection, channel, defineCollection, defineEntity, defineMutation, registry } from './core/index.mjs';
|
|
3
|
+
export { EntityResult, MutationResult, OptimisticConfig, PaginatedQueryResult, PaginationState, QueryResult, QueryState, UseMutationOptions, UseQueryHookOptions, useMutation, useQuery } from './react/index.mjs';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AnyDef, C as CollectionDef, E as EntityDef, I as IdGetter, M as MutationDef, a as Optimistic, b as OptimisticAction, c as OptimisticInstruction, O as OptimisticStatus, P as PaginatedOptions, Q as QueryOptions } from './types-BRxQA1mR.js';
|
|
2
|
+
export { Channel, CollectionChannel, EntityChannel, OptimisticTransaction, RegisteredCollection, RegisteredEntity, RegisteredEntry, RegisteredPaginatedCollection, channel, defineCollection, defineEntity, defineMutation, registry } from './core/index.js';
|
|
3
|
+
export { EntityResult, MutationResult, OptimisticConfig, PaginatedQueryResult, PaginationState, QueryResult, QueryState, UseMutationOptions, UseQueryHookOptions, useMutation, useQuery } from './react/index.js';
|