@void-snippets/react 0.3.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +452 -51
- package/dist/index.d.ts +452 -51
- package/dist/index.js +590 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +584 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +26 -5
- package/README.md +0 -192
package/dist/index.mjs
CHANGED
|
@@ -6,19 +6,212 @@ import {
|
|
|
6
6
|
useQueryClient
|
|
7
7
|
} from "@tanstack/react-query";
|
|
8
8
|
import { createDefaultAdapters } from "@void-snippets/core";
|
|
9
|
-
var DEFAULT_PAGINATION = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
9
|
+
var DEFAULT_PAGINATION = { page: 1, limit: 10 };
|
|
10
|
+
function toOperation(op) {
|
|
11
|
+
if (op.kind === "create") return { kind: "create", payload: op.payload, tempId: op.tempId };
|
|
12
|
+
if (op.kind === "update") return { kind: "update", _id: op._id, payload: op.payload };
|
|
13
|
+
return { kind: "remove", _id: op._id };
|
|
14
|
+
}
|
|
15
|
+
var _stacks = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
function getStack(client, prefix) {
|
|
17
|
+
if (!_stacks.has(client)) _stacks.set(client, /* @__PURE__ */ new Map());
|
|
18
|
+
const map = _stacks.get(client);
|
|
19
|
+
if (!map.has(prefix)) {
|
|
20
|
+
map.set(prefix, {
|
|
21
|
+
pendingOps: [],
|
|
22
|
+
effectiveBaseListSnapshots: [],
|
|
23
|
+
effectiveBaseInfiniteSnapshots: [],
|
|
24
|
+
effectiveBaseGet: /* @__PURE__ */ new Map()
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return map.get(prefix);
|
|
28
|
+
}
|
|
29
|
+
function isListQueryKey(query) {
|
|
30
|
+
const key = query.queryKey;
|
|
31
|
+
return key.length === 2 && key[1] !== null && typeof key[1] === "object" && !Array.isArray(key[1]);
|
|
32
|
+
}
|
|
33
|
+
function generateTempId() {
|
|
34
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
35
|
+
return crypto.randomUUID();
|
|
36
|
+
}
|
|
37
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
38
|
+
}
|
|
39
|
+
function patchPagination(pagination, delta) {
|
|
40
|
+
if (delta === 0) return pagination;
|
|
41
|
+
const newTotal = Math.max(0, pagination.totalDocuments + delta);
|
|
42
|
+
return {
|
|
43
|
+
...pagination,
|
|
44
|
+
totalDocuments: newTotal,
|
|
45
|
+
totalPages: Math.ceil(newTotal / pagination.limit)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function applyUpdateToAllCaches(client, prefix, _id, payload, handler) {
|
|
49
|
+
client.setQueriesData(
|
|
50
|
+
{ queryKey: [prefix], predicate: isListQueryKey },
|
|
51
|
+
(old) => old ? { ...old, items: handler(old.items, { _id, payload }) } : old
|
|
52
|
+
);
|
|
53
|
+
client.setQueriesData(
|
|
54
|
+
{ queryKey: [prefix, "INFINITE"] },
|
|
55
|
+
(old) => old ? { ...old, pages: old.pages.map((p) => ({ ...p, items: handler(p.items, { _id, payload }) })) } : old
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function applyRemoveFromAllCaches(client, prefix, _id, handler) {
|
|
59
|
+
client.setQueriesData(
|
|
60
|
+
{ queryKey: [prefix], predicate: isListQueryKey },
|
|
61
|
+
(old) => {
|
|
62
|
+
if (!old) return old;
|
|
63
|
+
const newItems = handler(old.items, _id);
|
|
64
|
+
return { ...old, items: newItems, pagination: patchPagination(old.pagination, newItems.length - old.items.length) };
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
client.setQueriesData(
|
|
68
|
+
{ queryKey: [prefix, "INFINITE"] },
|
|
69
|
+
(old) => old ? {
|
|
70
|
+
...old,
|
|
71
|
+
pages: old.pages.map((p) => {
|
|
72
|
+
const newItems = handler(p.items, _id);
|
|
73
|
+
return { ...p, items: newItems, pagination: patchPagination(p.pagination, newItems.length - p.items.length) };
|
|
74
|
+
})
|
|
75
|
+
} : old
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function applyCreateToAllCaches(client, prefix, payload, tempId, handler) {
|
|
79
|
+
client.setQueriesData(
|
|
80
|
+
{ queryKey: [prefix], predicate: isListQueryKey },
|
|
81
|
+
(old) => {
|
|
82
|
+
if (!old) return old;
|
|
83
|
+
const newItems = handler(old.items, { payload, tempId });
|
|
84
|
+
return { ...old, items: newItems, pagination: patchPagination(old.pagination, newItems.length - old.items.length) };
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
client.setQueriesData(
|
|
88
|
+
{ queryKey: [prefix, "INFINITE"] },
|
|
89
|
+
(old) => {
|
|
90
|
+
if (!old) return old;
|
|
91
|
+
return {
|
|
92
|
+
...old,
|
|
93
|
+
pages: old.pages.map((p, i) => {
|
|
94
|
+
if (i !== 0) return p;
|
|
95
|
+
const newItems = handler(p.items, { payload, tempId });
|
|
96
|
+
return { ...p, items: newItems, pagination: patchPagination(p.pagination, newItems.length - p.items.length) };
|
|
97
|
+
})
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
function restoreEffectiveBase(client, prefix, stack) {
|
|
103
|
+
stack.effectiveBaseListSnapshots.forEach(([key, data]) => client.setQueryData(key, data));
|
|
104
|
+
stack.effectiveBaseInfiniteSnapshots.forEach(([key, data]) => client.setQueryData(key, data));
|
|
105
|
+
stack.effectiveBaseGet.forEach((data, idStr) => client.setQueryData([prefix, idStr], data));
|
|
106
|
+
}
|
|
107
|
+
function advanceEffectiveBase(stack, op, optimistic) {
|
|
108
|
+
if (op.kind === "update" && optimistic.update) {
|
|
109
|
+
stack.effectiveBaseListSnapshots = stack.effectiveBaseListSnapshots.map(
|
|
110
|
+
([key, data]) => data ? [key, { ...data, items: optimistic.update(data.items, { _id: op._id, payload: op.payload }) }] : [key, data]
|
|
111
|
+
);
|
|
112
|
+
stack.effectiveBaseInfiniteSnapshots = stack.effectiveBaseInfiniteSnapshots.map(
|
|
113
|
+
([key, data]) => data ? [key, { ...data, pages: data.pages.map((p) => ({ ...p, items: optimistic.update(p.items, { _id: op._id, payload: op.payload }) })) }] : [key, data]
|
|
114
|
+
);
|
|
115
|
+
const baseEntry = stack.effectiveBaseGet.get(String(op._id));
|
|
116
|
+
if (baseEntry !== void 0) {
|
|
117
|
+
const advanced = optimistic.updateSingle ? optimistic.updateSingle(baseEntry, op.payload) : { ...baseEntry, ...op.payload };
|
|
118
|
+
stack.effectiveBaseGet.set(String(op._id), advanced);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (op.kind === "remove" && optimistic.remove) {
|
|
123
|
+
stack.effectiveBaseListSnapshots = stack.effectiveBaseListSnapshots.map(([key, data]) => {
|
|
124
|
+
if (!data) return [key, data];
|
|
125
|
+
const newItems = optimistic.remove(data.items, op._id);
|
|
126
|
+
return [key, { ...data, items: newItems, pagination: patchPagination(data.pagination, newItems.length - data.items.length) }];
|
|
127
|
+
});
|
|
128
|
+
stack.effectiveBaseInfiniteSnapshots = stack.effectiveBaseInfiniteSnapshots.map(
|
|
129
|
+
([key, data]) => data ? [
|
|
130
|
+
key,
|
|
131
|
+
{
|
|
132
|
+
...data,
|
|
133
|
+
pages: data.pages.map((p) => {
|
|
134
|
+
const newItems = optimistic.remove(p.items, op._id);
|
|
135
|
+
return { ...p, items: newItems, pagination: patchPagination(p.pagination, newItems.length - p.items.length) };
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
] : [key, data]
|
|
139
|
+
);
|
|
140
|
+
stack.effectiveBaseGet.delete(String(op._id));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function replayPendingOps(client, prefix, stack, optimistic) {
|
|
145
|
+
for (const op of stack.pendingOps) {
|
|
146
|
+
if (op.kind === "update" && optimistic.update) {
|
|
147
|
+
applyUpdateToAllCaches(client, prefix, op._id, op.payload, optimistic.update);
|
|
148
|
+
const current = client.getQueryData([prefix, String(op._id)]);
|
|
149
|
+
if (current !== void 0) {
|
|
150
|
+
const updated = optimistic.updateSingle ? optimistic.updateSingle(current, op.payload) : { ...current, ...op.payload };
|
|
151
|
+
client.setQueryData([prefix, String(op._id)], updated);
|
|
152
|
+
}
|
|
153
|
+
} else if (op.kind === "remove" && optimistic.remove) {
|
|
154
|
+
applyRemoveFromAllCaches(client, prefix, op._id, optimistic.remove);
|
|
155
|
+
client.invalidateQueries({ queryKey: [prefix, String(op._id)], refetchType: "none" });
|
|
156
|
+
} else if (op.kind === "create" && optimistic.create) {
|
|
157
|
+
applyCreateToAllCaches(client, prefix, op.payload, op.tempId, optimistic.create);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function flushStack(client, prefix, stack) {
|
|
162
|
+
stack.pendingOps = [];
|
|
163
|
+
stack.effectiveBaseListSnapshots = [];
|
|
164
|
+
stack.effectiveBaseInfiniteSnapshots = [];
|
|
165
|
+
stack.effectiveBaseGet.clear();
|
|
166
|
+
client.invalidateQueries({ queryKey: [prefix] });
|
|
167
|
+
}
|
|
168
|
+
function handleOptimisticError(client, prefix, error, context, optimistic) {
|
|
169
|
+
if (!context) return;
|
|
170
|
+
const stack = getStack(client, prefix);
|
|
171
|
+
const failedOp = stack.pendingOps.find((op) => op.id === context.operationId);
|
|
172
|
+
stack.pendingOps = stack.pendingOps.filter((op) => op.id !== context.operationId);
|
|
173
|
+
restoreEffectiveBase(client, prefix, stack);
|
|
174
|
+
replayPendingOps(client, prefix, stack, optimistic);
|
|
175
|
+
if (failedOp && optimistic.onError) {
|
|
176
|
+
try {
|
|
177
|
+
optimistic.onError(error, toOperation(failedOp));
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function handleOptimisticSettled(client, prefix, error, context, optimistic) {
|
|
183
|
+
if (!context) {
|
|
184
|
+
client.invalidateQueries({ queryKey: [prefix] });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const stack = getStack(client, prefix);
|
|
188
|
+
if (!error) {
|
|
189
|
+
const settledOp = stack.pendingOps.find((op) => op.id === context.operationId);
|
|
190
|
+
if (settledOp) {
|
|
191
|
+
advanceEffectiveBase(stack, settledOp, optimistic);
|
|
192
|
+
if (optimistic.onSuccess) {
|
|
193
|
+
try {
|
|
194
|
+
optimistic.onSuccess(toOperation(settledOp));
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
stack.pendingOps = stack.pendingOps.filter((op) => op.id !== context.operationId);
|
|
201
|
+
if (stack.pendingOps.length === 0) {
|
|
202
|
+
flushStack(client, prefix, stack);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
13
205
|
function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
|
|
14
206
|
const service = apiService;
|
|
15
207
|
const {
|
|
16
208
|
adapters = createDefaultAdapters(),
|
|
17
|
-
defaultParams = DEFAULT_PAGINATION
|
|
209
|
+
defaultParams = DEFAULT_PAGINATION,
|
|
210
|
+
optimistic
|
|
18
211
|
} = options;
|
|
19
212
|
return {
|
|
20
213
|
// -------------------------------------------------------------------------
|
|
21
|
-
// useList
|
|
214
|
+
// useList
|
|
22
215
|
// -------------------------------------------------------------------------
|
|
23
216
|
useList: (params = defaultParams) => {
|
|
24
217
|
var _a, _b, _c, _d, _e;
|
|
@@ -38,13 +231,17 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
|
|
|
38
231
|
totalPages: 0,
|
|
39
232
|
totalDocuments: 0
|
|
40
233
|
},
|
|
41
|
-
isLoading: query.isLoading
|
|
234
|
+
isLoading: query.isLoading,
|
|
235
|
+
isFetching: query.isFetching,
|
|
236
|
+
isRefetching: query.isRefetching,
|
|
237
|
+
isError: query.isError,
|
|
42
238
|
error: query.error,
|
|
239
|
+
refetch: query.refetch,
|
|
43
240
|
invalidate: () => queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] })
|
|
44
241
|
};
|
|
45
242
|
},
|
|
46
243
|
// -------------------------------------------------------------------------
|
|
47
|
-
// useGet
|
|
244
|
+
// useGet
|
|
48
245
|
// -------------------------------------------------------------------------
|
|
49
246
|
useGet: (id, staleTime = 3e4) => {
|
|
50
247
|
const query = useQuery({
|
|
@@ -58,46 +255,173 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
|
|
|
58
255
|
});
|
|
59
256
|
return {
|
|
60
257
|
item: query.data,
|
|
61
|
-
isLoading: query.isLoading
|
|
258
|
+
isLoading: query.isLoading,
|
|
259
|
+
isFetching: query.isFetching,
|
|
260
|
+
isRefetching: query.isRefetching,
|
|
261
|
+
isError: query.isError,
|
|
62
262
|
error: query.error,
|
|
63
263
|
refetch: query.refetch
|
|
64
264
|
};
|
|
65
265
|
},
|
|
66
266
|
// -------------------------------------------------------------------------
|
|
67
|
-
// useMutations
|
|
267
|
+
// useMutations
|
|
68
268
|
// -------------------------------------------------------------------------
|
|
69
269
|
useMutations: () => {
|
|
70
270
|
const queryClient = useQueryClient();
|
|
71
271
|
const invalidate = () => queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] });
|
|
272
|
+
function captureBaseIfFirstOp(stack) {
|
|
273
|
+
if (stack.pendingOps.length === 0) {
|
|
274
|
+
stack.effectiveBaseListSnapshots = queryClient.getQueriesData({
|
|
275
|
+
queryKey: [queryKeyPrefix],
|
|
276
|
+
predicate: isListQueryKey
|
|
277
|
+
});
|
|
278
|
+
stack.effectiveBaseInfiniteSnapshots = queryClient.getQueriesData({
|
|
279
|
+
queryKey: [queryKeyPrefix, "INFINITE"]
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
72
283
|
const createMutation = useMutation({
|
|
73
284
|
mutationFn: async (payload) => {
|
|
74
285
|
const raw = await service.create(payload);
|
|
75
286
|
return adapters.fromSingle(raw);
|
|
76
287
|
},
|
|
77
|
-
|
|
288
|
+
onMutate: async (payload) => {
|
|
289
|
+
if (!(optimistic == null ? void 0 : optimistic.create)) return void 0;
|
|
290
|
+
await queryClient.cancelQueries({ queryKey: [queryKeyPrefix] });
|
|
291
|
+
const stack = getStack(
|
|
292
|
+
queryClient,
|
|
293
|
+
queryKeyPrefix
|
|
294
|
+
);
|
|
295
|
+
captureBaseIfFirstOp(stack);
|
|
296
|
+
const tempId = generateTempId();
|
|
297
|
+
const op = {
|
|
298
|
+
id: /* @__PURE__ */ Symbol(),
|
|
299
|
+
kind: "create",
|
|
300
|
+
payload,
|
|
301
|
+
tempId
|
|
302
|
+
};
|
|
303
|
+
stack.pendingOps.push(op);
|
|
304
|
+
try {
|
|
305
|
+
applyCreateToAllCaches(queryClient, queryKeyPrefix, payload, tempId, optimistic.create);
|
|
306
|
+
} catch {
|
|
307
|
+
stack.pendingOps = stack.pendingOps.filter((o) => o.id !== op.id);
|
|
308
|
+
throw new Error("[@void-snippets/react] Optimistic create setup failed.");
|
|
309
|
+
}
|
|
310
|
+
return { operationId: op.id };
|
|
311
|
+
},
|
|
312
|
+
onError: (_err, _vars, context) => {
|
|
313
|
+
if (!(optimistic == null ? void 0 : optimistic.create)) return;
|
|
314
|
+
handleOptimisticError(queryClient, queryKeyPrefix, _err, context, optimistic);
|
|
315
|
+
},
|
|
316
|
+
onSettled: (_data, error, _vars, context) => {
|
|
317
|
+
if (!(optimistic == null ? void 0 : optimistic.create)) {
|
|
318
|
+
invalidate();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
handleOptimisticSettled(queryClient, queryKeyPrefix, error != null ? error : null, context, optimistic);
|
|
322
|
+
}
|
|
78
323
|
});
|
|
79
324
|
const updateMutation = useMutation({
|
|
80
325
|
mutationFn: async ({ _id, payload }) => {
|
|
81
326
|
const raw = await service.update(_id, payload);
|
|
82
327
|
return adapters.fromSingle(raw);
|
|
83
328
|
},
|
|
84
|
-
|
|
329
|
+
onMutate: async ({ _id, payload }) => {
|
|
330
|
+
if (!(optimistic == null ? void 0 : optimistic.update)) return void 0;
|
|
331
|
+
await queryClient.cancelQueries({ queryKey: [queryKeyPrefix] });
|
|
332
|
+
const stack = getStack(
|
|
333
|
+
queryClient,
|
|
334
|
+
queryKeyPrefix
|
|
335
|
+
);
|
|
336
|
+
captureBaseIfFirstOp(stack);
|
|
337
|
+
const currentSingle = queryClient.getQueryData([queryKeyPrefix, String(_id)]);
|
|
338
|
+
if (currentSingle !== void 0) {
|
|
339
|
+
stack.effectiveBaseGet.set(String(_id), currentSingle);
|
|
340
|
+
}
|
|
341
|
+
const op = {
|
|
342
|
+
id: /* @__PURE__ */ Symbol(),
|
|
343
|
+
kind: "update",
|
|
344
|
+
_id,
|
|
345
|
+
payload
|
|
346
|
+
};
|
|
347
|
+
stack.pendingOps.push(op);
|
|
348
|
+
try {
|
|
349
|
+
applyUpdateToAllCaches(queryClient, queryKeyPrefix, _id, payload, optimistic.update);
|
|
350
|
+
if (currentSingle !== void 0) {
|
|
351
|
+
const updated = optimistic.updateSingle ? optimistic.updateSingle(currentSingle, payload) : { ...currentSingle, ...payload };
|
|
352
|
+
queryClient.setQueryData([queryKeyPrefix, String(_id)], updated);
|
|
353
|
+
}
|
|
354
|
+
} catch {
|
|
355
|
+
stack.pendingOps = stack.pendingOps.filter((o) => o.id !== op.id);
|
|
356
|
+
throw new Error("[@void-snippets/react] Optimistic update setup failed.");
|
|
357
|
+
}
|
|
358
|
+
return { operationId: op.id };
|
|
359
|
+
},
|
|
360
|
+
onError: (_err, _vars, context) => {
|
|
361
|
+
if (!(optimistic == null ? void 0 : optimistic.update)) return;
|
|
362
|
+
handleOptimisticError(queryClient, queryKeyPrefix, _err, context, optimistic);
|
|
363
|
+
},
|
|
364
|
+
onSettled: (_data, error, _vars, context) => {
|
|
365
|
+
if (!(optimistic == null ? void 0 : optimistic.update)) {
|
|
366
|
+
invalidate();
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
handleOptimisticSettled(queryClient, queryKeyPrefix, error != null ? error : null, context, optimistic);
|
|
370
|
+
}
|
|
85
371
|
});
|
|
86
372
|
const removeMutation = useMutation({
|
|
87
373
|
mutationFn: async (_id) => {
|
|
88
374
|
const raw = await service.delete(_id);
|
|
89
375
|
return adapters.fromSingle(raw);
|
|
90
376
|
},
|
|
91
|
-
|
|
377
|
+
onMutate: async (_id) => {
|
|
378
|
+
if (!(optimistic == null ? void 0 : optimistic.remove)) return void 0;
|
|
379
|
+
await queryClient.cancelQueries({ queryKey: [queryKeyPrefix] });
|
|
380
|
+
const stack = getStack(
|
|
381
|
+
queryClient,
|
|
382
|
+
queryKeyPrefix
|
|
383
|
+
);
|
|
384
|
+
captureBaseIfFirstOp(stack);
|
|
385
|
+
const currentSingle = queryClient.getQueryData([queryKeyPrefix, String(_id)]);
|
|
386
|
+
if (currentSingle !== void 0) {
|
|
387
|
+
stack.effectiveBaseGet.set(String(_id), currentSingle);
|
|
388
|
+
}
|
|
389
|
+
const op = {
|
|
390
|
+
id: /* @__PURE__ */ Symbol(),
|
|
391
|
+
kind: "remove",
|
|
392
|
+
_id
|
|
393
|
+
};
|
|
394
|
+
stack.pendingOps.push(op);
|
|
395
|
+
try {
|
|
396
|
+
applyRemoveFromAllCaches(queryClient, queryKeyPrefix, _id, optimistic.remove);
|
|
397
|
+
if (currentSingle !== void 0) {
|
|
398
|
+
queryClient.invalidateQueries({
|
|
399
|
+
queryKey: [queryKeyPrefix, String(_id)],
|
|
400
|
+
refetchType: "none"
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
stack.pendingOps = stack.pendingOps.filter((o) => o.id !== op.id);
|
|
405
|
+
throw new Error("[@void-snippets/react] Optimistic remove setup failed.");
|
|
406
|
+
}
|
|
407
|
+
return { operationId: op.id };
|
|
408
|
+
},
|
|
409
|
+
onError: (_err, _vars, context) => {
|
|
410
|
+
if (!(optimistic == null ? void 0 : optimistic.remove)) return;
|
|
411
|
+
handleOptimisticError(queryClient, queryKeyPrefix, _err, context, optimistic);
|
|
412
|
+
},
|
|
413
|
+
onSettled: (_data, error, _vars, context) => {
|
|
414
|
+
if (!(optimistic == null ? void 0 : optimistic.remove)) {
|
|
415
|
+
invalidate();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
handleOptimisticSettled(queryClient, queryKeyPrefix, error != null ? error : null, context, optimistic);
|
|
419
|
+
}
|
|
92
420
|
});
|
|
93
|
-
return {
|
|
94
|
-
create: createMutation,
|
|
95
|
-
update: updateMutation,
|
|
96
|
-
remove: removeMutation
|
|
97
|
-
};
|
|
421
|
+
return { create: createMutation, update: updateMutation, remove: removeMutation };
|
|
98
422
|
},
|
|
99
423
|
// -------------------------------------------------------------------------
|
|
100
|
-
// useInfinite
|
|
424
|
+
// useInfinite
|
|
101
425
|
// -------------------------------------------------------------------------
|
|
102
426
|
useInfinite: (params = defaultParams) => {
|
|
103
427
|
return useInfiniteQuery({
|
|
@@ -121,15 +445,224 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
|
|
|
121
445
|
};
|
|
122
446
|
}
|
|
123
447
|
|
|
448
|
+
// src/socket/createSocketHooks.ts
|
|
449
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
450
|
+
function createSocketHooks(socket) {
|
|
451
|
+
function useSocketEmit() {
|
|
452
|
+
const emit = useCallback(
|
|
453
|
+
(event, ...args) => {
|
|
454
|
+
if (!socket.connected) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`[@void-snippets/react] Cannot emit "${String(event)}" \u2014 socket is not connected.`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
socket.emit(event, ...args);
|
|
460
|
+
},
|
|
461
|
+
[]
|
|
462
|
+
);
|
|
463
|
+
const emitWithAck = useCallback(
|
|
464
|
+
(event, ...args) => {
|
|
465
|
+
if (!socket.connected) {
|
|
466
|
+
return Promise.reject(
|
|
467
|
+
new Error(
|
|
468
|
+
`[@void-snippets/react] Cannot emit "${String(event)}" \u2014 socket is not connected.`
|
|
469
|
+
)
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
return socket.emitWithAck(
|
|
473
|
+
event,
|
|
474
|
+
...args
|
|
475
|
+
);
|
|
476
|
+
},
|
|
477
|
+
[]
|
|
478
|
+
);
|
|
479
|
+
return { emit, emitWithAck };
|
|
480
|
+
}
|
|
481
|
+
function useSocketListener(event, handler, options) {
|
|
482
|
+
var _a;
|
|
483
|
+
const enabled = (_a = options == null ? void 0 : options.enabled) != null ? _a : true;
|
|
484
|
+
const savedHandler = useRef(handler);
|
|
485
|
+
useEffect(() => {
|
|
486
|
+
savedHandler.current = handler;
|
|
487
|
+
}, [handler]);
|
|
488
|
+
useEffect(() => {
|
|
489
|
+
if (!enabled) return;
|
|
490
|
+
const listener = ((...args) => {
|
|
491
|
+
savedHandler.current(...args);
|
|
492
|
+
});
|
|
493
|
+
socket.on(event, listener);
|
|
494
|
+
return () => {
|
|
495
|
+
socket.off(event, listener);
|
|
496
|
+
};
|
|
497
|
+
}, [event, enabled]);
|
|
498
|
+
}
|
|
499
|
+
function useSocketConnection() {
|
|
500
|
+
const [isConnected, setIsConnected] = useState(socket.connected);
|
|
501
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
502
|
+
const [socketId, setSocketId] = useState(socket.id);
|
|
503
|
+
const [error, setError] = useState(null);
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
function onConnect() {
|
|
506
|
+
setIsConnected(true);
|
|
507
|
+
setIsConnecting(false);
|
|
508
|
+
setSocketId(socket.id);
|
|
509
|
+
setError(null);
|
|
510
|
+
}
|
|
511
|
+
function onDisconnect() {
|
|
512
|
+
setIsConnected(false);
|
|
513
|
+
setIsConnecting(false);
|
|
514
|
+
setSocketId(void 0);
|
|
515
|
+
}
|
|
516
|
+
function onConnectError(err) {
|
|
517
|
+
setIsConnected(false);
|
|
518
|
+
setIsConnecting(false);
|
|
519
|
+
setError(err);
|
|
520
|
+
}
|
|
521
|
+
function onReconnectAttempt() {
|
|
522
|
+
setIsConnecting(true);
|
|
523
|
+
}
|
|
524
|
+
function onReconnectFailed() {
|
|
525
|
+
setIsConnecting(false);
|
|
526
|
+
setError(
|
|
527
|
+
new Error(
|
|
528
|
+
"[@void-snippets/react] Socket reconnection failed \u2014 maximum attempts exceeded."
|
|
529
|
+
)
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
socket.on("connect", onConnect);
|
|
533
|
+
socket.on("disconnect", onDisconnect);
|
|
534
|
+
socket.on("connect_error", onConnectError);
|
|
535
|
+
socket.io.on("reconnect_attempt", onReconnectAttempt);
|
|
536
|
+
socket.io.on("reconnect_failed", onReconnectFailed);
|
|
537
|
+
return () => {
|
|
538
|
+
socket.off("connect", onConnect);
|
|
539
|
+
socket.off("disconnect", onDisconnect);
|
|
540
|
+
socket.off("connect_error", onConnectError);
|
|
541
|
+
socket.io.off("reconnect_attempt", onReconnectAttempt);
|
|
542
|
+
socket.io.off("reconnect_failed", onReconnectFailed);
|
|
543
|
+
};
|
|
544
|
+
}, []);
|
|
545
|
+
const connect = useCallback(() => {
|
|
546
|
+
if (!socket.connected) {
|
|
547
|
+
setIsConnecting(true);
|
|
548
|
+
socket.connect();
|
|
549
|
+
}
|
|
550
|
+
}, []);
|
|
551
|
+
const disconnect = useCallback(() => {
|
|
552
|
+
socket.disconnect();
|
|
553
|
+
}, []);
|
|
554
|
+
return {
|
|
555
|
+
isConnected,
|
|
556
|
+
isConnecting,
|
|
557
|
+
socketId,
|
|
558
|
+
error,
|
|
559
|
+
connect,
|
|
560
|
+
disconnect
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
useSocketEmit,
|
|
565
|
+
useSocketListener,
|
|
566
|
+
useSocketConnection
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/routing/createRouteContract.ts
|
|
571
|
+
import { generatePath } from "react-router";
|
|
572
|
+
import { useSearchParams } from "react-router";
|
|
573
|
+
function isRouteDefinition(node) {
|
|
574
|
+
if (node === null || typeof node !== "object") return false;
|
|
575
|
+
const n = node;
|
|
576
|
+
return typeof n["path"] === "string" && "_search" in n && typeof n["search"] === "function";
|
|
577
|
+
}
|
|
578
|
+
function defineRoute(path, config) {
|
|
579
|
+
const definition = {
|
|
580
|
+
...config,
|
|
581
|
+
path,
|
|
582
|
+
// Phantom anchor — undefined at runtime, typed as `never` for the base definition.
|
|
583
|
+
// The search<S>() method changes this to `S` at the TypeScript level only.
|
|
584
|
+
_search: void 0,
|
|
585
|
+
search() {
|
|
586
|
+
return definition;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
return definition;
|
|
590
|
+
}
|
|
591
|
+
function createRouteContract(tree) {
|
|
592
|
+
const result = {};
|
|
593
|
+
for (const key in tree) {
|
|
594
|
+
const node = tree[key];
|
|
595
|
+
if (isRouteDefinition(node)) {
|
|
596
|
+
const {
|
|
597
|
+
_search: _phantom,
|
|
598
|
+
search: _searchFn,
|
|
599
|
+
...metadata
|
|
600
|
+
} = node;
|
|
601
|
+
result[key] = {
|
|
602
|
+
...metadata,
|
|
603
|
+
// Preserve the phantom anchor on the ProcessedRoute for useTypedSearchParams.
|
|
604
|
+
_search: void 0,
|
|
605
|
+
build(options = {}) {
|
|
606
|
+
const { params, search } = options;
|
|
607
|
+
const pathname = params ? generatePath(
|
|
608
|
+
node.path,
|
|
609
|
+
Object.fromEntries(
|
|
610
|
+
Object.entries(params).map(([k, v]) => [k, String(v)])
|
|
611
|
+
)
|
|
612
|
+
) : node.path;
|
|
613
|
+
if (search) {
|
|
614
|
+
const defined = Object.entries(search).filter(
|
|
615
|
+
([, v]) => v !== void 0 && v !== null
|
|
616
|
+
);
|
|
617
|
+
if (defined.length > 0) {
|
|
618
|
+
const qs = new URLSearchParams(
|
|
619
|
+
defined.map(([k, v]) => [k, String(v)])
|
|
620
|
+
).toString();
|
|
621
|
+
return `${pathname}?${qs}`;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return pathname;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
} else {
|
|
628
|
+
result[key] = createRouteContract(node);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
function useTypedSearchParams(_route) {
|
|
634
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
635
|
+
const search = Object.fromEntries(searchParams.entries());
|
|
636
|
+
function setSearch(update) {
|
|
637
|
+
setSearchParams((prev) => {
|
|
638
|
+
const next = Object.fromEntries(prev.entries());
|
|
639
|
+
for (const [k, v] of Object.entries(
|
|
640
|
+
update
|
|
641
|
+
)) {
|
|
642
|
+
if (v === void 0 || v === null) {
|
|
643
|
+
delete next[k];
|
|
644
|
+
} else {
|
|
645
|
+
next[k] = String(v);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return next;
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function clearSearch() {
|
|
652
|
+
setSearchParams({});
|
|
653
|
+
}
|
|
654
|
+
return { search, setSearch, clearSearch };
|
|
655
|
+
}
|
|
656
|
+
|
|
124
657
|
// src/hooks/useAlertMessage.ts
|
|
125
|
-
import { useCallback, useState } from "react";
|
|
658
|
+
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
126
659
|
function useAlertMessage(autoHideDuration = 3e3) {
|
|
127
|
-
const [alert, setAlert] =
|
|
660
|
+
const [alert, setAlert] = useState2({
|
|
128
661
|
message: null,
|
|
129
662
|
type: "info",
|
|
130
663
|
isVisible: false
|
|
131
664
|
});
|
|
132
|
-
const showAlert =
|
|
665
|
+
const showAlert = useCallback2(
|
|
133
666
|
(message, type = "info") => {
|
|
134
667
|
setAlert({ message, type, isVisible: true });
|
|
135
668
|
if (autoHideDuration) {
|
|
@@ -140,31 +673,31 @@ function useAlertMessage(autoHideDuration = 3e3) {
|
|
|
140
673
|
},
|
|
141
674
|
[autoHideDuration]
|
|
142
675
|
);
|
|
143
|
-
const hideAlert =
|
|
676
|
+
const hideAlert = useCallback2(() => {
|
|
144
677
|
setAlert((prev) => ({ ...prev, isVisible: false }));
|
|
145
678
|
}, []);
|
|
146
679
|
return { alert, showAlert, hideAlert };
|
|
147
680
|
}
|
|
148
681
|
|
|
149
682
|
// src/hooks/useAsyncState.ts
|
|
150
|
-
import { useCallback as
|
|
683
|
+
import { useCallback as useCallback3, useMemo, useState as useState3 } from "react";
|
|
151
684
|
import { catchError } from "@void-snippets/core";
|
|
152
685
|
function useAsyncState(initialData = null) {
|
|
153
|
-
const [state, setState] =
|
|
686
|
+
const [state, setState] = useState3({
|
|
154
687
|
data: initialData,
|
|
155
688
|
status: "idle",
|
|
156
689
|
error: null
|
|
157
690
|
});
|
|
158
|
-
const setData =
|
|
691
|
+
const setData = useCallback3((data) => {
|
|
159
692
|
setState({ data, status: "success", error: null });
|
|
160
693
|
}, []);
|
|
161
|
-
const setError =
|
|
694
|
+
const setError = useCallback3((error) => {
|
|
162
695
|
setState((prev) => ({ ...prev, error, status: "error" }));
|
|
163
696
|
}, []);
|
|
164
|
-
const reset =
|
|
697
|
+
const reset = useCallback3(() => {
|
|
165
698
|
setState({ data: initialData, status: "idle", error: null });
|
|
166
699
|
}, [initialData]);
|
|
167
|
-
const execute =
|
|
700
|
+
const execute = useCallback3(
|
|
168
701
|
async (asyncFn, options) => {
|
|
169
702
|
var _a, _b;
|
|
170
703
|
setState((prev) => ({ ...prev, status: "pending", error: null }));
|
|
@@ -192,10 +725,10 @@ function useAsyncState(initialData = null) {
|
|
|
192
725
|
}
|
|
193
726
|
|
|
194
727
|
// src/hooks/useCallTimer.ts
|
|
195
|
-
import { useEffect, useState as
|
|
728
|
+
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
196
729
|
function useCallTimer(startedAt) {
|
|
197
|
-
const [duration, setDuration] =
|
|
198
|
-
|
|
730
|
+
const [duration, setDuration] = useState4("00:00");
|
|
731
|
+
useEffect2(() => {
|
|
199
732
|
if (!startedAt) {
|
|
200
733
|
setDuration("00:00");
|
|
201
734
|
return;
|
|
@@ -212,27 +745,27 @@ function useCallTimer(startedAt) {
|
|
|
212
745
|
}
|
|
213
746
|
|
|
214
747
|
// src/hooks/useModal.ts
|
|
215
|
-
import { useCallback as
|
|
748
|
+
import { useCallback as useCallback4, useState as useState5 } from "react";
|
|
216
749
|
function useModal() {
|
|
217
|
-
const [isOpen, setIsOpen] =
|
|
218
|
-
const [data, setData] =
|
|
219
|
-
const [isLoading, setIsLoading] =
|
|
220
|
-
const openCreateModal =
|
|
750
|
+
const [isOpen, setIsOpen] = useState5(false);
|
|
751
|
+
const [data, setData] = useState5(null);
|
|
752
|
+
const [isLoading, setIsLoading] = useState5(false);
|
|
753
|
+
const openCreateModal = useCallback4(() => {
|
|
221
754
|
setIsOpen(true);
|
|
222
755
|
setData(null);
|
|
223
756
|
}, []);
|
|
224
|
-
const openEditModal =
|
|
757
|
+
const openEditModal = useCallback4((editData) => {
|
|
225
758
|
setIsOpen(true);
|
|
226
759
|
setData(editData);
|
|
227
760
|
}, []);
|
|
228
|
-
const setLoading =
|
|
761
|
+
const setLoading = useCallback4((loading) => {
|
|
229
762
|
setIsLoading(loading);
|
|
230
763
|
}, []);
|
|
231
|
-
const closeModal =
|
|
764
|
+
const closeModal = useCallback4(() => {
|
|
232
765
|
setIsOpen(false);
|
|
233
766
|
setData(null);
|
|
234
767
|
}, []);
|
|
235
|
-
const setModal =
|
|
768
|
+
const setModal = useCallback4((open, editData) => {
|
|
236
769
|
setIsOpen(open);
|
|
237
770
|
setData(editData != null ? editData : null);
|
|
238
771
|
}, []);
|
|
@@ -249,18 +782,18 @@ function useModal() {
|
|
|
249
782
|
}
|
|
250
783
|
|
|
251
784
|
// src/hooks/usePagination.ts
|
|
252
|
-
import { useCallback as
|
|
785
|
+
import { useCallback as useCallback5, useState as useState6 } from "react";
|
|
253
786
|
function usePagination(initialPage = 1, initialLimit = 10) {
|
|
254
|
-
const [page, setPage] =
|
|
255
|
-
const [limit, setLimit] =
|
|
256
|
-
const onPaginationChange =
|
|
787
|
+
const [page, setPage] = useState6(initialPage);
|
|
788
|
+
const [limit, setLimit] = useState6(initialLimit);
|
|
789
|
+
const onPaginationChange = useCallback5(
|
|
257
790
|
(newPage, newLimit) => {
|
|
258
791
|
setPage(newPage);
|
|
259
792
|
setLimit(newLimit);
|
|
260
793
|
},
|
|
261
794
|
[]
|
|
262
795
|
);
|
|
263
|
-
const resetPagination =
|
|
796
|
+
const resetPagination = useCallback5(() => {
|
|
264
797
|
setPage(1);
|
|
265
798
|
}, []);
|
|
266
799
|
return {
|
|
@@ -275,10 +808,14 @@ function usePagination(initialPage = 1, initialLimit = 10) {
|
|
|
275
808
|
}
|
|
276
809
|
export {
|
|
277
810
|
createResourceHooks,
|
|
811
|
+
createRouteContract,
|
|
812
|
+
createSocketHooks,
|
|
813
|
+
defineRoute,
|
|
278
814
|
useAlertMessage,
|
|
279
815
|
useAsyncState,
|
|
280
816
|
useCallTimer,
|
|
281
817
|
useModal,
|
|
282
|
-
usePagination
|
|
818
|
+
usePagination,
|
|
819
|
+
useTypedSearchParams
|
|
283
820
|
};
|
|
284
821
|
//# sourceMappingURL=index.mjs.map
|