void-snippets-monorepo 0.1.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.
Files changed (32) hide show
  1. package/README.md +2261 -0
  2. package/package.json +18 -0
  3. package/packages/client/package.json +47 -0
  4. package/packages/client/src/configure.ts +34 -0
  5. package/packages/client/src/index.ts +4 -0
  6. package/packages/client/src/services/base-api.service.ts +26 -0
  7. package/packages/client/src/services/resource-api.service.ts +117 -0
  8. package/packages/client/src/utils/handle-api-error.ts +20 -0
  9. package/packages/client/tsconfig.json +13 -0
  10. package/packages/client/tsup.config.ts +10 -0
  11. package/packages/core/package.json +41 -0
  12. package/packages/core/src/id.ts +19 -0
  13. package/packages/core/src/index.ts +4 -0
  14. package/packages/core/src/string-to-id.ts +22 -0
  15. package/packages/core/src/types/index.ts +86 -0
  16. package/packages/core/src/utils/catch-error.ts +20 -0
  17. package/packages/core/tsconfig.json +13 -0
  18. package/packages/core/tsup.config.ts +9 -0
  19. package/packages/react/package.json +80 -0
  20. package/packages/react/src/hooks/createResourceHooks.ts +872 -0
  21. package/packages/react/src/hooks/useAlertMessage.ts +45 -0
  22. package/packages/react/src/hooks/useAsyncState.ts +110 -0
  23. package/packages/react/src/hooks/useCallTimer.ts +37 -0
  24. package/packages/react/src/hooks/useModal.ts +71 -0
  25. package/packages/react/src/hooks/usePagination.ts +57 -0
  26. package/packages/react/src/index.ts +43 -0
  27. package/packages/react/src/routing/createRouteContract.ts +483 -0
  28. package/packages/react/src/socket/createSocketHooks.ts +351 -0
  29. package/packages/react/tsconfig.json +14 -0
  30. package/packages/react/tsup.config.ts +10 -0
  31. package/pnpm-workspace.yaml +2 -0
  32. package/tsconfig.base.json +12 -0
@@ -0,0 +1,45 @@
1
+ import { type ReactNode, useCallback, useState } from "react";
2
+
3
+ export type VSAlertVariant = "success" | "info" | "error";
4
+
5
+ export interface VSAlertState {
6
+ message: ReactNode | string;
7
+ type: VSAlertVariant;
8
+ isVisible: boolean;
9
+ }
10
+
11
+ /**
12
+ * Manages alert/toast message state with optional auto-hide.
13
+ *
14
+ * @param autoHideDuration - ms before alert hides automatically. Pass 0 to disable. Default: 3000
15
+ *
16
+ * @example
17
+ * const { alert, showAlert, hideAlert } = useAlertMessage();
18
+ * showAlert('Saved successfully!', 'success');
19
+ * showAlert(<b>Something went wrong</b>, 'error');
20
+ */
21
+ export function useAlertMessage(autoHideDuration = 3000) {
22
+ const [alert, setAlert] = useState<VSAlertState>({
23
+ message: null,
24
+ type: "info",
25
+ isVisible: false,
26
+ });
27
+
28
+ const showAlert = useCallback(
29
+ (message: ReactNode | string, type: VSAlertVariant = "info") => {
30
+ setAlert({ message, type, isVisible: true });
31
+ if (autoHideDuration) {
32
+ setTimeout(() => {
33
+ setAlert((prev) => ({ ...prev, isVisible: false }));
34
+ }, autoHideDuration);
35
+ }
36
+ },
37
+ [autoHideDuration]
38
+ );
39
+
40
+ const hideAlert = useCallback(() => {
41
+ setAlert((prev) => ({ ...prev, isVisible: false }));
42
+ }, []);
43
+
44
+ return { alert, showAlert, hideAlert };
45
+ }
@@ -0,0 +1,110 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import { catchError } from "@void-snippets/core";
3
+
4
+ export type VSAsyncStatus = "idle" | "pending" | "success" | "error";
5
+
6
+ interface VSAsyncState<T> {
7
+ data: T | null;
8
+ status: VSAsyncStatus;
9
+ error: Error | null;
10
+ }
11
+
12
+ export interface VSUseAsyncStateReturn<T> extends VSAsyncState<T> {
13
+ isLoading: boolean;
14
+ isSuccess: boolean;
15
+ isError: boolean;
16
+
17
+ setData: (data: T | null) => void;
18
+ setError: (error: Error | null) => void;
19
+ reset: () => void;
20
+
21
+ /**
22
+ * Executes an async function, updates state, and returns a [err, data] tuple.
23
+ * Allows immediate result handling without try/catch.
24
+ *
25
+ * @example
26
+ * const [err, data] = await execute(() => ContactsApis.create(payload));
27
+ * if (err) return showAlert(err.message, 'error');
28
+ * showAlert('Created!', 'success');
29
+ */
30
+ execute: (
31
+ asyncFn: () => Promise<T>,
32
+ options?: {
33
+ onSuccess?: (data: T) => void;
34
+ onError?: (error: Error) => void;
35
+ }
36
+ ) => Promise<[Error, null] | [null, T]>;
37
+ }
38
+
39
+ /**
40
+ * Generic async state machine — tracks data, status, and error for any async operation.
41
+ * Pair with any async function: API calls, file reads, timers, etc.
42
+ *
43
+ * @param initialData - Optional initial data value. Default: null
44
+ *
45
+ * @example
46
+ * const { data, isLoading, isError, execute } = useAsyncState<User>();
47
+ *
48
+ * const handleSubmit = async () => {
49
+ * const [err, user] = await execute(() => fetchUser(id));
50
+ * if (err) return;
51
+ * console.log(user.name);
52
+ * };
53
+ */
54
+ export function useAsyncState<T>(
55
+ initialData: T | null = null
56
+ ): VSUseAsyncStateReturn<T> {
57
+ const [state, setState] = useState<VSAsyncState<T>>({
58
+ data: initialData,
59
+ status: "idle",
60
+ error: null,
61
+ });
62
+
63
+ const setData = useCallback((data: T | null) => {
64
+ setState({ data, status: "success", error: null });
65
+ }, []);
66
+
67
+ const setError = useCallback((error: Error | null) => {
68
+ setState((prev) => ({ ...prev, error, status: "error" }));
69
+ }, []);
70
+
71
+ const reset = useCallback(() => {
72
+ setState({ data: initialData, status: "idle", error: null });
73
+ }, [initialData]);
74
+
75
+ const execute = useCallback(
76
+ async (
77
+ asyncFn: () => Promise<T>,
78
+ options?: {
79
+ onSuccess?: (data: T) => void;
80
+ onError?: (error: Error) => void;
81
+ }
82
+ ): Promise<[Error, null] | [null, T]> => {
83
+ setState((prev) => ({ ...prev, status: "pending", error: null }));
84
+
85
+ const [err, res] = await catchError(asyncFn());
86
+
87
+ if (err) {
88
+ setState((prev) => ({ ...prev, status: "error", error: err }));
89
+ options?.onError?.(err);
90
+ return [err, null];
91
+ }
92
+
93
+ setState({ data: res as T, status: "success", error: null });
94
+ options?.onSuccess?.(res as T);
95
+ return [null, res as T];
96
+ },
97
+ []
98
+ );
99
+
100
+ const flags = useMemo(
101
+ () => ({
102
+ isLoading: state.status === "pending",
103
+ isSuccess: state.status === "success",
104
+ isError: state.status === "error",
105
+ }),
106
+ [state.status]
107
+ );
108
+
109
+ return { ...state, ...flags, setData, setError, reset, execute };
110
+ }
@@ -0,0 +1,37 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ /**
4
+ * Tracks elapsed time from a given start timestamp — useful for call durations,
5
+ * countdowns, or any elapsed-time display.
6
+ *
7
+ * @param startedAt - Unix timestamp in ms (e.g. Date.now()). Pass null/undefined to reset.
8
+ * @returns Formatted duration string "MM:SS"
9
+ *
10
+ * @example
11
+ * const duration = useCallTimer(call.startedAt);
12
+ * // duration → "02:45"
13
+ *
14
+ * // Reset when no active call
15
+ * const duration = useCallTimer(activeCall ? activeCall.startedAt : null);
16
+ */
17
+ export function useCallTimer(startedAt?: number | null): string {
18
+ const [duration, setDuration] = useState("00:00");
19
+
20
+ useEffect(() => {
21
+ if (!startedAt) {
22
+ setDuration("00:00");
23
+ return;
24
+ }
25
+
26
+ const interval = setInterval(() => {
27
+ const diffInSeconds = Math.floor((Date.now() - startedAt) / 1000);
28
+ const minutes = Math.floor(diffInSeconds / 60).toString().padStart(2, "0");
29
+ const seconds = (diffInSeconds % 60).toString().padStart(2, "0");
30
+ setDuration(`${minutes}:${seconds}`);
31
+ }, 1000);
32
+
33
+ return () => clearInterval(interval);
34
+ }, [startedAt]);
35
+
36
+ return duration;
37
+ }
@@ -0,0 +1,71 @@
1
+ import { useCallback, useState } from "react";
2
+
3
+ export interface VSModalReturn<T> {
4
+ isOpen: boolean;
5
+ data: T | null;
6
+ isLoading: boolean;
7
+ openCreateModal: () => void;
8
+ openEditModal: (editData: T) => void;
9
+ setLoading: (loading: boolean) => void;
10
+ closeModal: () => void;
11
+ setModal: (open: boolean, editData?: T | null) => void;
12
+ }
13
+
14
+ /**
15
+ * Manages modal open/close state with optional data payload and loading state.
16
+ * Works for both create and edit modals — pass data to distinguish the mode.
17
+ *
18
+ * @typeParam T - The type of data the modal operates on (e.g. a Contact, User, etc.)
19
+ *
20
+ * @example
21
+ * const modal = useModal<Contact.Base>();
22
+ *
23
+ * modal.openCreateModal(); // data → null (create mode)
24
+ * modal.openEditModal(contact); // data → contact (edit mode)
25
+ *
26
+ * if (modal.data) {
27
+ * // Edit mode — modal.data is Contact.Base
28
+ * } else {
29
+ * // Create mode
30
+ * }
31
+ */
32
+ export function useModal<T = unknown>(): VSModalReturn<T> {
33
+ const [isOpen, setIsOpen] = useState(false);
34
+ const [data, setData] = useState<T | null>(null);
35
+ const [isLoading, setIsLoading] = useState(false);
36
+
37
+ const openCreateModal = useCallback(() => {
38
+ setIsOpen(true);
39
+ setData(null);
40
+ }, []);
41
+
42
+ const openEditModal = useCallback((editData: T) => {
43
+ setIsOpen(true);
44
+ setData(editData);
45
+ }, []);
46
+
47
+ const setLoading = useCallback((loading: boolean) => {
48
+ setIsLoading(loading);
49
+ }, []);
50
+
51
+ const closeModal = useCallback(() => {
52
+ setIsOpen(false);
53
+ setData(null);
54
+ }, []);
55
+
56
+ const setModal = useCallback((open: boolean, editData?: T | null) => {
57
+ setIsOpen(open);
58
+ setData(editData ?? null);
59
+ }, []);
60
+
61
+ return {
62
+ isOpen,
63
+ data,
64
+ isLoading,
65
+ openCreateModal,
66
+ openEditModal,
67
+ setLoading,
68
+ closeModal,
69
+ setModal,
70
+ };
71
+ }
@@ -0,0 +1,57 @@
1
+ import { useCallback, useState } from "react";
2
+ import type { VSQueryParams } from "@void-snippets/core";
3
+
4
+ export interface VSPaginationReturn {
5
+ page: number;
6
+ limit: number;
7
+ onPaginationChange: (newPage: number, newLimit: number) => void;
8
+ resetPagination: () => void;
9
+ setPage: (page: number) => void;
10
+ setLimit: (limit: number) => void;
11
+ /** Ready-to-use query params object — pass directly to useList() */
12
+ queryParams: VSQueryParams;
13
+ }
14
+
15
+ /**
16
+ * Manages pagination state and produces a ready-to-use queryParams object
17
+ * compatible with createResourceHooks' useList() and useInfinite().
18
+ *
19
+ * @param initialPage - Starting page. Default: 1
20
+ * @param initialLimit - Items per page. Default: 10
21
+ *
22
+ * @example
23
+ * const { queryParams, onPaginationChange } = usePagination(1, 20);
24
+ *
25
+ * const { list, isLoading } = contactHooks.useList(queryParams);
26
+ *
27
+ * <Pagination onChange={onPaginationChange} total={pagination.totalDocuments} />
28
+ */
29
+ export function usePagination(
30
+ initialPage = 1,
31
+ initialLimit = 10
32
+ ): VSPaginationReturn {
33
+ const [page, setPage] = useState(initialPage);
34
+ const [limit, setLimit] = useState(initialLimit);
35
+
36
+ const onPaginationChange = useCallback(
37
+ (newPage: number, newLimit: number) => {
38
+ setPage(newPage);
39
+ setLimit(newLimit);
40
+ },
41
+ []
42
+ );
43
+
44
+ const resetPagination = useCallback(() => {
45
+ setPage(1);
46
+ }, []);
47
+
48
+ return {
49
+ page,
50
+ limit,
51
+ onPaginationChange,
52
+ resetPagination,
53
+ setPage,
54
+ setLimit,
55
+ queryParams: { page, limit },
56
+ };
57
+ }
@@ -0,0 +1,43 @@
1
+ // Resource hooks factory
2
+ export { createResourceHooks } from "./hooks/createResourceHooks";
3
+ export type {
4
+ VSUseListReturn,
5
+ VSUseGetReturn,
6
+ VSResourceHooksOptions,
7
+ VSOptimisticHandlers,
8
+ VSOptimisticOperation,
9
+ } from "./hooks/createResourceHooks";
10
+
11
+ // Socket.IO hooks factory
12
+ export { createSocketHooks } from "./socket/createSocketHooks";
13
+ export type { VSSocketConnectionReturn } from "./socket/createSocketHooks";
14
+
15
+ // Route contract factory
16
+ export {
17
+ createRouteContract,
18
+ defineRoute,
19
+ useTypedSearchParams,
20
+ } from "./routing/createRouteContract";
21
+ export type {
22
+ RouteMetadata,
23
+ RouteDefinition,
24
+ ProcessedRoute,
25
+ } from "./routing/createRouteContract";
26
+
27
+ // General-purpose React hooks
28
+ export { useAlertMessage } from "./hooks/useAlertMessage";
29
+ export type { VSAlertVariant, VSAlertState } from "./hooks/useAlertMessage";
30
+
31
+ export { useAsyncState } from "./hooks/useAsyncState";
32
+ export type {
33
+ VSAsyncStatus,
34
+ VSUseAsyncStateReturn,
35
+ } from "./hooks/useAsyncState";
36
+
37
+ export { useCallTimer } from "./hooks/useCallTimer";
38
+
39
+ export { useModal } from "./hooks/useModal";
40
+ export type { VSModalReturn } from "./hooks/useModal";
41
+
42
+ export { usePagination } from "./hooks/usePagination";
43
+ export type { VSPaginationReturn } from "./hooks/usePagination";