@void-snippets/react 0.3.0 → 0.6.0

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