@exyconn/common 1.0.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.
Files changed (88) hide show
  1. package/README.md +259 -0
  2. package/dist/client/http/index.d.mts +85 -0
  3. package/dist/client/http/index.d.ts +85 -0
  4. package/dist/client/http/index.js +127 -0
  5. package/dist/client/http/index.js.map +1 -0
  6. package/dist/client/http/index.mjs +109 -0
  7. package/dist/client/http/index.mjs.map +1 -0
  8. package/dist/client/index.d.mts +7 -0
  9. package/dist/client/index.d.ts +7 -0
  10. package/dist/client/index.js +964 -0
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/client/index.mjs +889 -0
  13. package/dist/client/index.mjs.map +1 -0
  14. package/dist/client/logger/index.d.mts +53 -0
  15. package/dist/client/logger/index.d.ts +53 -0
  16. package/dist/client/logger/index.js +120 -0
  17. package/dist/client/logger/index.js.map +1 -0
  18. package/dist/client/logger/index.mjs +116 -0
  19. package/dist/client/logger/index.mjs.map +1 -0
  20. package/dist/client/utils/index.d.mts +285 -0
  21. package/dist/client/utils/index.d.ts +285 -0
  22. package/dist/client/utils/index.js +403 -0
  23. package/dist/client/utils/index.js.map +1 -0
  24. package/dist/client/utils/index.mjs +362 -0
  25. package/dist/client/utils/index.mjs.map +1 -0
  26. package/dist/index-BNdT-2X4.d.ts +229 -0
  27. package/dist/index-CcrANHAQ.d.mts +59 -0
  28. package/dist/index-ClWtDfwk.d.ts +833 -0
  29. package/dist/index-DSW6JfD-.d.mts +833 -0
  30. package/dist/index-Du0LLt9f.d.mts +229 -0
  31. package/dist/index-iTKxFa78.d.ts +59 -0
  32. package/dist/index.d.mts +171 -0
  33. package/dist/index.d.ts +171 -0
  34. package/dist/index.js +3806 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/index.mjs +3792 -0
  37. package/dist/index.mjs.map +1 -0
  38. package/dist/response.types-D--UhLJq.d.mts +67 -0
  39. package/dist/response.types-D--UhLJq.d.ts +67 -0
  40. package/dist/server/db/index.d.mts +38 -0
  41. package/dist/server/db/index.d.ts +38 -0
  42. package/dist/server/db/index.js +68 -0
  43. package/dist/server/db/index.js.map +1 -0
  44. package/dist/server/db/index.mjs +60 -0
  45. package/dist/server/db/index.mjs.map +1 -0
  46. package/dist/server/enums/index.d.mts +46 -0
  47. package/dist/server/enums/index.d.ts +46 -0
  48. package/dist/server/enums/index.js +48 -0
  49. package/dist/server/enums/index.js.map +1 -0
  50. package/dist/server/enums/index.mjs +43 -0
  51. package/dist/server/enums/index.mjs.map +1 -0
  52. package/dist/server/index.d.mts +9 -0
  53. package/dist/server/index.d.ts +9 -0
  54. package/dist/server/index.js +569 -0
  55. package/dist/server/index.js.map +1 -0
  56. package/dist/server/index.mjs +523 -0
  57. package/dist/server/index.mjs.map +1 -0
  58. package/dist/server/logger/index.d.mts +34 -0
  59. package/dist/server/logger/index.d.ts +34 -0
  60. package/dist/server/logger/index.js +125 -0
  61. package/dist/server/logger/index.js.map +1 -0
  62. package/dist/server/logger/index.mjs +113 -0
  63. package/dist/server/logger/index.mjs.map +1 -0
  64. package/dist/server/middleware/index.d.mts +56 -0
  65. package/dist/server/middleware/index.d.ts +56 -0
  66. package/dist/server/middleware/index.js +128 -0
  67. package/dist/server/middleware/index.js.map +1 -0
  68. package/dist/server/middleware/index.mjs +118 -0
  69. package/dist/server/middleware/index.mjs.map +1 -0
  70. package/dist/server/response/index.d.mts +86 -0
  71. package/dist/server/response/index.d.ts +86 -0
  72. package/dist/server/response/index.js +140 -0
  73. package/dist/server/response/index.js.map +1 -0
  74. package/dist/server/response/index.mjs +126 -0
  75. package/dist/server/response/index.mjs.map +1 -0
  76. package/dist/server/utils/index.d.mts +69 -0
  77. package/dist/server/utils/index.d.ts +69 -0
  78. package/dist/server/utils/index.js +114 -0
  79. package/dist/server/utils/index.js.map +1 -0
  80. package/dist/server/utils/index.mjs +106 -0
  81. package/dist/server/utils/index.mjs.map +1 -0
  82. package/dist/shared/index.d.mts +4 -0
  83. package/dist/shared/index.d.ts +4 -0
  84. package/dist/shared/index.js +933 -0
  85. package/dist/shared/index.js.map +1 -0
  86. package/dist/shared/index.mjs +612 -0
  87. package/dist/shared/index.mjs.map +1 -0
  88. package/package.json +202 -0
@@ -0,0 +1,889 @@
1
+ import axios from 'axios';
2
+ import { useCallback, useState, useEffect, useRef } from 'react';
3
+
4
+ // src/client/http/axios-instance.ts
5
+ var createHttpClient = (options) => {
6
+ const {
7
+ baseURL,
8
+ timeout = 3e4,
9
+ withCredentials = true,
10
+ getAuthToken,
11
+ onUnauthorized,
12
+ onServerError
13
+ } = options;
14
+ const instance = axios.create({
15
+ baseURL,
16
+ timeout,
17
+ withCredentials,
18
+ headers: {
19
+ "Content-Type": "application/json"
20
+ }
21
+ });
22
+ instance.interceptors.request.use(
23
+ (config) => {
24
+ if (getAuthToken) {
25
+ const token = getAuthToken();
26
+ if (token && config.headers) {
27
+ config.headers.Authorization = `Bearer ${token}`;
28
+ }
29
+ }
30
+ return config;
31
+ },
32
+ (error) => Promise.reject(error)
33
+ );
34
+ instance.interceptors.response.use(
35
+ (response) => response,
36
+ (error) => {
37
+ if (error.response) {
38
+ const status = error.response.status;
39
+ if (status === 401 && onUnauthorized) {
40
+ onUnauthorized();
41
+ }
42
+ if (status >= 500 && onServerError) {
43
+ onServerError(error);
44
+ }
45
+ }
46
+ return Promise.reject(error);
47
+ }
48
+ );
49
+ return instance;
50
+ };
51
+ var withFormData = () => ({
52
+ headers: {
53
+ "Content-Type": "multipart/form-data"
54
+ }
55
+ });
56
+ var withTimeout = (ms) => ({
57
+ timeout: ms
58
+ });
59
+ var withAbortSignal = (signal) => ({
60
+ signal
61
+ });
62
+
63
+ // src/client/http/response-parser.ts
64
+ var parseResponse = (response) => {
65
+ if (response.data?.success && response.data?.data !== void 0) {
66
+ return response.data.data;
67
+ }
68
+ return null;
69
+ };
70
+ var parseFullResponse = (response) => {
71
+ return response.data;
72
+ };
73
+ var parseError = (error) => {
74
+ if (error.response?.data?.message) {
75
+ return error.response.data.message;
76
+ }
77
+ if (error.response?.data?.error) {
78
+ return error.response.data.error;
79
+ }
80
+ if (error.code === "ERR_NETWORK") {
81
+ return "Network error. Please check your connection.";
82
+ }
83
+ if (error.code === "ECONNABORTED") {
84
+ return "Request timed out. Please try again.";
85
+ }
86
+ return error.message || "An unexpected error occurred.";
87
+ };
88
+ var isSuccess = (response) => {
89
+ return response.data?.success === true;
90
+ };
91
+ var isStatusError = (error, statusCode) => {
92
+ return error.response?.status === statusCode;
93
+ };
94
+ var isUnauthorized = (error) => {
95
+ return isStatusError(error, 401);
96
+ };
97
+ var isForbidden = (error) => {
98
+ return isStatusError(error, 403);
99
+ };
100
+ var isNotFound = (error) => {
101
+ return isStatusError(error, 404);
102
+ };
103
+ var isServerError = (error) => {
104
+ const status = error.response?.status;
105
+ return status !== void 0 && status >= 500;
106
+ };
107
+
108
+ // src/client/logger/client-logger.ts
109
+ var LOG_LEVELS = {
110
+ debug: 0,
111
+ info: 1,
112
+ warn: 2,
113
+ error: 3
114
+ };
115
+ var ClientLogger = class {
116
+ constructor(config = {}) {
117
+ this.buffer = [];
118
+ this.config = {
119
+ enabled: config.enabled ?? process.env.NODE_ENV !== "production",
120
+ minLevel: config.minLevel ?? "debug",
121
+ prefix: config.prefix ?? "[App]",
122
+ includeTimestamp: config.includeTimestamp ?? true,
123
+ remoteLogging: config.remoteLogging
124
+ };
125
+ if (this.config.remoteLogging?.enabled) {
126
+ const interval = this.config.remoteLogging.flushInterval ?? 3e4;
127
+ this.flushTimer = setInterval(() => this.flush(), interval);
128
+ }
129
+ }
130
+ shouldLog(level) {
131
+ if (!this.config.enabled) return false;
132
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.config.minLevel];
133
+ }
134
+ formatMessage(level, message) {
135
+ const parts = [];
136
+ if (this.config.includeTimestamp) {
137
+ parts.push(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
138
+ }
139
+ parts.push(this.config.prefix);
140
+ parts.push(`[${level.toUpperCase()}]`);
141
+ parts.push(message);
142
+ return parts.join(" ");
143
+ }
144
+ log(level, message, data) {
145
+ if (!this.shouldLog(level)) return;
146
+ const formattedMessage = this.formatMessage(level, message);
147
+ const entry = {
148
+ level,
149
+ message,
150
+ data,
151
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
152
+ prefix: this.config.prefix
153
+ };
154
+ switch (level) {
155
+ case "debug":
156
+ console.debug(formattedMessage, data ?? "");
157
+ break;
158
+ case "info":
159
+ console.info(formattedMessage, data ?? "");
160
+ break;
161
+ case "warn":
162
+ console.warn(formattedMessage, data ?? "");
163
+ break;
164
+ case "error":
165
+ console.error(formattedMessage, data ?? "");
166
+ break;
167
+ }
168
+ if (this.config.remoteLogging?.enabled) {
169
+ this.buffer.push(entry);
170
+ const batchSize = this.config.remoteLogging.batchSize ?? 10;
171
+ if (this.buffer.length >= batchSize) {
172
+ this.flush();
173
+ }
174
+ }
175
+ }
176
+ debug(message, data) {
177
+ this.log("debug", message, data);
178
+ }
179
+ info(message, data) {
180
+ this.log("info", message, data);
181
+ }
182
+ warn(message, data) {
183
+ this.log("warn", message, data);
184
+ }
185
+ error(message, data) {
186
+ this.log("error", message, data);
187
+ }
188
+ /**
189
+ * Flush buffered logs to remote endpoint
190
+ */
191
+ async flush() {
192
+ if (!this.config.remoteLogging?.enabled || this.buffer.length === 0) return;
193
+ const logs = [...this.buffer];
194
+ this.buffer = [];
195
+ try {
196
+ await fetch(this.config.remoteLogging.endpoint, {
197
+ method: "POST",
198
+ headers: { "Content-Type": "application/json" },
199
+ body: JSON.stringify({ logs })
200
+ });
201
+ } catch (error) {
202
+ this.buffer = [...logs, ...this.buffer].slice(0, 100);
203
+ console.error("Failed to send logs to remote endpoint", error);
204
+ }
205
+ }
206
+ /**
207
+ * Cleanup logger (clear intervals)
208
+ */
209
+ destroy() {
210
+ if (this.flushTimer) {
211
+ clearInterval(this.flushTimer);
212
+ }
213
+ this.flush();
214
+ }
215
+ };
216
+ var createClientLogger = (config) => {
217
+ return new ClientLogger(config);
218
+ };
219
+ var clientLogger = new ClientLogger();
220
+
221
+ // src/client/utils/date.ts
222
+ var formatDate = (date, locale = "en-US") => {
223
+ const dateObj = new Date(date);
224
+ return dateObj.toLocaleDateString(locale, {
225
+ year: "numeric",
226
+ month: "long",
227
+ day: "numeric"
228
+ });
229
+ };
230
+ var formatDateTime = (date, locale = "en-US") => {
231
+ const dateObj = new Date(date);
232
+ return dateObj.toLocaleDateString(locale, {
233
+ year: "numeric",
234
+ month: "short",
235
+ day: "numeric",
236
+ hour: "2-digit",
237
+ minute: "2-digit"
238
+ });
239
+ };
240
+ var formatRelativeTime = (date) => {
241
+ const dateObj = new Date(date);
242
+ const now = /* @__PURE__ */ new Date();
243
+ const diffInSeconds = Math.floor((now.getTime() - dateObj.getTime()) / 1e3);
244
+ const intervals = [
245
+ { label: "year", seconds: 31536e3 },
246
+ { label: "month", seconds: 2592e3 },
247
+ { label: "week", seconds: 604800 },
248
+ { label: "day", seconds: 86400 },
249
+ { label: "hour", seconds: 3600 },
250
+ { label: "minute", seconds: 60 },
251
+ { label: "second", seconds: 1 }
252
+ ];
253
+ for (const interval of intervals) {
254
+ const count = Math.floor(diffInSeconds / interval.seconds);
255
+ if (count >= 1) {
256
+ return `${count} ${interval.label}${count !== 1 ? "s" : ""} ago`;
257
+ }
258
+ }
259
+ return "just now";
260
+ };
261
+ var formatDateForInput = (date) => {
262
+ const dateObj = new Date(date);
263
+ return dateObj.toISOString().split("T")[0];
264
+ };
265
+ var formatDateTimeForInput = (date) => {
266
+ const dateObj = new Date(date);
267
+ return dateObj.toISOString().slice(0, 16);
268
+ };
269
+ var isToday = (date) => {
270
+ const dateObj = new Date(date);
271
+ const today = /* @__PURE__ */ new Date();
272
+ return dateObj.getDate() === today.getDate() && dateObj.getMonth() === today.getMonth() && dateObj.getFullYear() === today.getFullYear();
273
+ };
274
+ var isPast = (date) => {
275
+ return new Date(date).getTime() < Date.now();
276
+ };
277
+ var isFuture = (date) => {
278
+ return new Date(date).getTime() > Date.now();
279
+ };
280
+ var addDays = (date, days) => {
281
+ const dateObj = new Date(date);
282
+ dateObj.setDate(dateObj.getDate() + days);
283
+ return dateObj;
284
+ };
285
+ var startOfDay = (date) => {
286
+ const dateObj = new Date(date);
287
+ dateObj.setHours(0, 0, 0, 0);
288
+ return dateObj;
289
+ };
290
+ var endOfDay = (date) => {
291
+ const dateObj = new Date(date);
292
+ dateObj.setHours(23, 59, 59, 999);
293
+ return dateObj;
294
+ };
295
+
296
+ // src/client/utils/clipboard.ts
297
+ var copyToClipboard = async (text) => {
298
+ try {
299
+ if (navigator.clipboard && window.isSecureContext) {
300
+ await navigator.clipboard.writeText(text);
301
+ return true;
302
+ }
303
+ const textArea = document.createElement("textarea");
304
+ textArea.value = text;
305
+ textArea.style.position = "fixed";
306
+ textArea.style.left = "-999999px";
307
+ textArea.style.top = "-999999px";
308
+ document.body.appendChild(textArea);
309
+ textArea.focus();
310
+ textArea.select();
311
+ const success = document.execCommand("copy");
312
+ document.body.removeChild(textArea);
313
+ return success;
314
+ } catch (error) {
315
+ console.error("Failed to copy to clipboard:", error);
316
+ return false;
317
+ }
318
+ };
319
+ var readFromClipboard = async () => {
320
+ try {
321
+ if (navigator.clipboard && window.isSecureContext) {
322
+ return await navigator.clipboard.readText();
323
+ }
324
+ return null;
325
+ } catch (error) {
326
+ console.error("Failed to read from clipboard:", error);
327
+ return null;
328
+ }
329
+ };
330
+ var isClipboardAvailable = () => {
331
+ return !!(navigator.clipboard && window.isSecureContext);
332
+ };
333
+
334
+ // src/client/utils/slug.ts
335
+ var slugify = (text) => {
336
+ return text.toString().toLowerCase().trim().replace(/\s+/g, "-").replace(/[^\w\-]+/g, "").replace(/\-\-+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
337
+ };
338
+ var slugifyUnique = (text) => {
339
+ const baseSlug = slugify(text);
340
+ const uniqueSuffix = Date.now().toString(36) + Math.random().toString(36).substring(2, 5);
341
+ return `${baseSlug}-${uniqueSuffix}`;
342
+ };
343
+ var unslugify = (slug) => {
344
+ return slug.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
345
+ };
346
+ var truncate = (text, maxLength, suffix = "...") => {
347
+ if (text.length <= maxLength) return text;
348
+ return text.substring(0, maxLength - suffix.length).trim() + suffix;
349
+ };
350
+ var truncateWords = (text, maxWords, suffix = "...") => {
351
+ const words = text.split(/\s+/);
352
+ if (words.length <= maxWords) return text;
353
+ return words.slice(0, maxWords).join(" ") + suffix;
354
+ };
355
+ var capitalizeWords = (text) => {
356
+ return text.replace(/\b\w/g, (char) => char.toUpperCase());
357
+ };
358
+ var capitalize = (text) => {
359
+ if (!text) return "";
360
+ return text.charAt(0).toUpperCase() + text.slice(1);
361
+ };
362
+ var camelToKebab = (text) => {
363
+ return text.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
364
+ };
365
+ var kebabToCamel = (text) => {
366
+ return text.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
367
+ };
368
+
369
+ // src/client/utils/events.ts
370
+ var EventEmitter = class {
371
+ constructor() {
372
+ this.handlers = /* @__PURE__ */ new Map();
373
+ }
374
+ /**
375
+ * Subscribe to an event
376
+ * @returns Unsubscribe function
377
+ */
378
+ on(event, handler) {
379
+ if (!this.handlers.has(event)) {
380
+ this.handlers.set(event, /* @__PURE__ */ new Set());
381
+ }
382
+ this.handlers.get(event).add(handler);
383
+ return () => this.off(event, handler);
384
+ }
385
+ /**
386
+ * Subscribe to an event once
387
+ */
388
+ once(event, handler) {
389
+ const wrappedHandler = (data) => {
390
+ this.off(event, wrappedHandler);
391
+ handler(data);
392
+ };
393
+ return this.on(event, wrappedHandler);
394
+ }
395
+ /**
396
+ * Unsubscribe from an event
397
+ */
398
+ off(event, handler) {
399
+ const eventHandlers = this.handlers.get(event);
400
+ if (eventHandlers) {
401
+ eventHandlers.delete(handler);
402
+ }
403
+ }
404
+ /**
405
+ * Emit an event
406
+ */
407
+ emit(event, data) {
408
+ const eventHandlers = this.handlers.get(event);
409
+ if (eventHandlers) {
410
+ eventHandlers.forEach((handler) => {
411
+ try {
412
+ handler(data);
413
+ } catch (error) {
414
+ console.error(`Error in event handler for "${String(event)}":`, error);
415
+ }
416
+ });
417
+ }
418
+ }
419
+ /**
420
+ * Remove all handlers for an event (or all events)
421
+ */
422
+ removeAllListeners(event) {
423
+ if (event) {
424
+ this.handlers.delete(event);
425
+ } else {
426
+ this.handlers.clear();
427
+ }
428
+ }
429
+ /**
430
+ * Get count of listeners for an event
431
+ */
432
+ listenerCount(event) {
433
+ return this.handlers.get(event)?.size ?? 0;
434
+ }
435
+ };
436
+ var createEventEmitter = () => {
437
+ return new EventEmitter();
438
+ };
439
+ var appEvents = new EventEmitter();
440
+
441
+ // src/client/utils/api-urls.ts
442
+ var ApiUrlBuilder = class {
443
+ constructor(config) {
444
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
445
+ this.version = config.version || "";
446
+ }
447
+ /**
448
+ * Build full URL from path
449
+ */
450
+ build(path) {
451
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
452
+ const versionPath = this.version ? `/${this.version}` : "";
453
+ return `${this.baseUrl}${versionPath}${normalizedPath}`;
454
+ }
455
+ /**
456
+ * Build URL with query parameters
457
+ */
458
+ buildWithParams(path, params) {
459
+ const url = this.build(path);
460
+ const filteredParams = Object.entries(params).filter(([, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`).join("&");
461
+ return filteredParams ? `${url}?${filteredParams}` : url;
462
+ }
463
+ /**
464
+ * Build URL with path parameters
465
+ */
466
+ buildWithPathParams(template, params) {
467
+ let path = template;
468
+ Object.entries(params).forEach(([key, value]) => {
469
+ path = path.replace(`:${key}`, String(value));
470
+ path = path.replace(`{${key}}`, String(value));
471
+ });
472
+ return this.build(path);
473
+ }
474
+ /**
475
+ * Get base URL
476
+ */
477
+ getBaseUrl() {
478
+ return this.baseUrl;
479
+ }
480
+ /**
481
+ * Set new base URL
482
+ */
483
+ setBaseUrl(baseUrl) {
484
+ this.baseUrl = baseUrl.replace(/\/$/, "");
485
+ }
486
+ };
487
+ var createApiUrlBuilder = (config) => {
488
+ return new ApiUrlBuilder(config);
489
+ };
490
+ var createApiEndpoints = (builder) => ({
491
+ // Auth endpoints
492
+ auth: {
493
+ login: () => builder.build("/auth/login"),
494
+ register: () => builder.build("/auth/register"),
495
+ logout: () => builder.build("/auth/logout"),
496
+ refresh: () => builder.build("/auth/refresh"),
497
+ me: () => builder.build("/auth/me"),
498
+ forgotPassword: () => builder.build("/auth/forgot-password"),
499
+ resetPassword: () => builder.build("/auth/reset-password")
500
+ },
501
+ // User endpoints
502
+ users: {
503
+ list: () => builder.build("/users"),
504
+ get: (id) => builder.buildWithPathParams("/users/:id", { id }),
505
+ create: () => builder.build("/users"),
506
+ update: (id) => builder.buildWithPathParams("/users/:id", { id }),
507
+ delete: (id) => builder.buildWithPathParams("/users/:id", { id })
508
+ },
509
+ // Generic CRUD factory
510
+ crud: (resource) => ({
511
+ list: () => builder.build(`/${resource}`),
512
+ get: (id) => builder.buildWithPathParams(`/${resource}/:id`, { id }),
513
+ create: () => builder.build(`/${resource}`),
514
+ update: (id) => builder.buildWithPathParams(`/${resource}/:id`, { id }),
515
+ delete: (id) => builder.buildWithPathParams(`/${resource}/:id`, { id })
516
+ })
517
+ });
518
+
519
+ // src/client/utils/response-parser.ts
520
+ var isSuccessResponse = (response) => {
521
+ return response.success === true;
522
+ };
523
+ var isErrorResponse = (response) => {
524
+ return response.success === false;
525
+ };
526
+ var getResponseData = (response, defaultValue) => {
527
+ if (isSuccessResponse(response) && response.data !== void 0) {
528
+ return response.data;
529
+ }
530
+ return defaultValue;
531
+ };
532
+ var getErrorMessage = (response, defaultMessage = "An error occurred") => {
533
+ if (response.error) {
534
+ return response.error;
535
+ }
536
+ if (response.message) {
537
+ return response.message;
538
+ }
539
+ return defaultMessage;
540
+ };
541
+ var hasData = (response) => {
542
+ return response.data !== null && response.data !== void 0;
543
+ };
544
+ var hasMorePages = (response) => {
545
+ return response.pagination.hasNextPage;
546
+ };
547
+ var getNextPage = (response) => {
548
+ if (response.pagination.hasNextPage) {
549
+ return response.pagination.page + 1;
550
+ }
551
+ return null;
552
+ };
553
+ var getPrevPage = (response) => {
554
+ if (response.pagination.hasPrevPage) {
555
+ return response.pagination.page - 1;
556
+ }
557
+ return null;
558
+ };
559
+ var createEmptyPaginationMeta = () => ({
560
+ total: 0,
561
+ page: 1,
562
+ limit: 10,
563
+ totalPages: 0,
564
+ hasNextPage: false,
565
+ hasPrevPage: false
566
+ });
567
+ var createSuccessResponse = (data, message = "Success") => ({
568
+ success: true,
569
+ message,
570
+ data,
571
+ statusCode: 200
572
+ });
573
+ var createErrorResponse = (message, statusCode = 400, error) => ({
574
+ success: false,
575
+ message,
576
+ error,
577
+ statusCode
578
+ });
579
+ function useLocalStorage(key, initialValue, options = {}) {
580
+ const {
581
+ serializer = JSON.stringify,
582
+ deserializer = JSON.parse,
583
+ syncTabs = true,
584
+ debug = false
585
+ } = options;
586
+ const log = useCallback(
587
+ (...args) => {
588
+ if (debug) console.log(`[useLocalStorage:${key}]`, ...args);
589
+ },
590
+ [debug, key]
591
+ );
592
+ const readValue = useCallback(() => {
593
+ if (typeof window === "undefined") {
594
+ return initialValue;
595
+ }
596
+ try {
597
+ const item = window.localStorage.getItem(key);
598
+ if (item === null) {
599
+ return initialValue;
600
+ }
601
+ const parsed = deserializer(item);
602
+ log("Read value:", parsed);
603
+ return parsed;
604
+ } catch (error) {
605
+ console.warn(`Error reading localStorage key "${key}":`, error);
606
+ return initialValue;
607
+ }
608
+ }, [key, initialValue, deserializer, log]);
609
+ const [storedValue, setStoredValue] = useState(readValue);
610
+ const setValue = useCallback(
611
+ (value) => {
612
+ if (typeof window === "undefined") {
613
+ console.warn(`Cannot set localStorage key "${key}" in non-browser environment`);
614
+ return;
615
+ }
616
+ try {
617
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
618
+ setStoredValue(valueToStore);
619
+ window.localStorage.setItem(key, serializer(valueToStore));
620
+ log("Set value:", valueToStore);
621
+ window.dispatchEvent(new StorageEvent("storage", { key, newValue: serializer(valueToStore) }));
622
+ } catch (error) {
623
+ console.warn(`Error setting localStorage key "${key}":`, error);
624
+ }
625
+ },
626
+ [key, storedValue, serializer, log]
627
+ );
628
+ const removeValue = useCallback(() => {
629
+ if (typeof window === "undefined") {
630
+ return;
631
+ }
632
+ try {
633
+ window.localStorage.removeItem(key);
634
+ setStoredValue(initialValue);
635
+ log("Removed value");
636
+ window.dispatchEvent(new StorageEvent("storage", { key, newValue: null }));
637
+ } catch (error) {
638
+ console.warn(`Error removing localStorage key "${key}":`, error);
639
+ }
640
+ }, [key, initialValue, log]);
641
+ useEffect(() => {
642
+ if (!syncTabs || typeof window === "undefined") {
643
+ return;
644
+ }
645
+ const handleStorageChange = (event) => {
646
+ if (event.key !== key) {
647
+ return;
648
+ }
649
+ log("Storage event received:", event.newValue);
650
+ if (event.newValue === null) {
651
+ setStoredValue(initialValue);
652
+ } else {
653
+ try {
654
+ setStoredValue(deserializer(event.newValue));
655
+ } catch {
656
+ console.warn(`Error parsing localStorage change for key "${key}"`);
657
+ }
658
+ }
659
+ };
660
+ window.addEventListener("storage", handleStorageChange);
661
+ return () => window.removeEventListener("storage", handleStorageChange);
662
+ }, [key, initialValue, syncTabs, deserializer, log]);
663
+ return [storedValue, setValue, removeValue];
664
+ }
665
+ var useLocalStorage_default = useLocalStorage;
666
+ function useDebounce(value, delay = 500) {
667
+ const [debouncedValue, setDebouncedValue] = useState(value);
668
+ useEffect(() => {
669
+ const timer = setTimeout(() => {
670
+ setDebouncedValue(value);
671
+ }, delay);
672
+ return () => {
673
+ clearTimeout(timer);
674
+ };
675
+ }, [value, delay]);
676
+ return debouncedValue;
677
+ }
678
+ var useDebounce_default = useDebounce;
679
+ function useCopyToClipboard(resetDelay = 2e3) {
680
+ const [copied, setCopied] = useState(false);
681
+ const [error, setError] = useState(null);
682
+ const reset = useCallback(() => {
683
+ setCopied(false);
684
+ setError(null);
685
+ }, []);
686
+ const copy = useCallback(
687
+ async (text) => {
688
+ if (!navigator?.clipboard) {
689
+ try {
690
+ const textarea = document.createElement("textarea");
691
+ textarea.value = text;
692
+ textarea.style.position = "fixed";
693
+ textarea.style.left = "-999999px";
694
+ textarea.style.top = "-999999px";
695
+ document.body.appendChild(textarea);
696
+ textarea.focus();
697
+ textarea.select();
698
+ const successful = document.execCommand("copy");
699
+ document.body.removeChild(textarea);
700
+ if (successful) {
701
+ setCopied(true);
702
+ setError(null);
703
+ setTimeout(reset, resetDelay);
704
+ return true;
705
+ } else {
706
+ throw new Error("execCommand failed");
707
+ }
708
+ } catch (err) {
709
+ const message = err instanceof Error ? err.message : "Failed to copy to clipboard";
710
+ setError(message);
711
+ setCopied(false);
712
+ return false;
713
+ }
714
+ }
715
+ try {
716
+ await navigator.clipboard.writeText(text);
717
+ setCopied(true);
718
+ setError(null);
719
+ setTimeout(reset, resetDelay);
720
+ return true;
721
+ } catch (err) {
722
+ const message = err instanceof Error ? err.message : "Failed to copy to clipboard";
723
+ setError(message);
724
+ setCopied(false);
725
+ return false;
726
+ }
727
+ },
728
+ [resetDelay, reset]
729
+ );
730
+ return { copy, copied, error, reset };
731
+ }
732
+ var useCopyToClipboard_default = useCopyToClipboard;
733
+ function usePageTitle(title, options = {}) {
734
+ const { suffix, separator = " | ", restoreOnUnmount = true } = options;
735
+ useEffect(() => {
736
+ const originalTitle = document.title;
737
+ const newTitle = suffix ? `${title}${separator}${suffix}` : title;
738
+ document.title = newTitle;
739
+ return () => {
740
+ if (restoreOnUnmount) {
741
+ document.title = originalTitle;
742
+ }
743
+ };
744
+ }, [title, suffix, separator, restoreOnUnmount]);
745
+ }
746
+ var usePageTitle_default = usePageTitle;
747
+ function useInterval(callback, delay) {
748
+ const savedCallback = useRef(callback);
749
+ useEffect(() => {
750
+ savedCallback.current = callback;
751
+ }, [callback]);
752
+ useEffect(() => {
753
+ if (delay === null) {
754
+ return;
755
+ }
756
+ const tick = () => savedCallback.current();
757
+ const id = setInterval(tick, delay);
758
+ return () => clearInterval(id);
759
+ }, [delay]);
760
+ }
761
+ var useInterval_default = useInterval;
762
+ function useThemeDetector() {
763
+ const getCurrentTheme = () => {
764
+ if (typeof window === "undefined") return "light";
765
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
766
+ };
767
+ const [theme, setTheme] = useState(getCurrentTheme);
768
+ useEffect(() => {
769
+ if (typeof window === "undefined") return;
770
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
771
+ const handleChange = (e) => {
772
+ setTheme(e.matches ? "dark" : "light");
773
+ };
774
+ mediaQuery.addEventListener("change", handleChange);
775
+ return () => mediaQuery.removeEventListener("change", handleChange);
776
+ }, []);
777
+ return theme;
778
+ }
779
+ var useThemeDetector_default = useThemeDetector;
780
+ var DEFAULT_DURATION = 4e3;
781
+ function useSnackbar(defaultDuration = DEFAULT_DURATION) {
782
+ const [state, setState] = useState({
783
+ open: false,
784
+ message: "",
785
+ severity: "info",
786
+ autoHideDuration: defaultDuration
787
+ });
788
+ const show = useCallback(
789
+ (message, severity = "info", duration) => {
790
+ setState({
791
+ open: true,
792
+ message,
793
+ severity,
794
+ autoHideDuration: duration ?? defaultDuration
795
+ });
796
+ },
797
+ [defaultDuration]
798
+ );
799
+ const success = useCallback(
800
+ (message, duration) => show(message, "success", duration),
801
+ [show]
802
+ );
803
+ const error = useCallback(
804
+ (message, duration) => show(message, "error", duration),
805
+ [show]
806
+ );
807
+ const warning = useCallback(
808
+ (message, duration) => show(message, "warning", duration),
809
+ [show]
810
+ );
811
+ const info = useCallback(
812
+ (message, duration) => show(message, "info", duration),
813
+ [show]
814
+ );
815
+ const close = useCallback(() => {
816
+ setState((prev) => ({ ...prev, open: false }));
817
+ }, []);
818
+ return { state, show, success, error, warning, info, close };
819
+ }
820
+ var useSnackbar_default = useSnackbar;
821
+ function useMediaQuery(query) {
822
+ const getMatches = (query2) => {
823
+ if (typeof window === "undefined") return false;
824
+ return window.matchMedia(query2).matches;
825
+ };
826
+ const [matches, setMatches] = useState(getMatches(query));
827
+ useEffect(() => {
828
+ if (typeof window === "undefined") return;
829
+ const mediaQuery = window.matchMedia(query);
830
+ const handleChange = () => setMatches(mediaQuery.matches);
831
+ handleChange();
832
+ mediaQuery.addEventListener("change", handleChange);
833
+ return () => mediaQuery.removeEventListener("change", handleChange);
834
+ }, [query]);
835
+ return matches;
836
+ }
837
+ var useIsMobile = () => useMediaQuery("(max-width: 767px)");
838
+ var useIsTablet = () => useMediaQuery("(min-width: 768px) and (max-width: 1023px)");
839
+ var useIsDesktop = () => useMediaQuery("(min-width: 1024px)");
840
+ var useIsMobileOrTablet = () => useMediaQuery("(max-width: 1023px)");
841
+ var useMediaQuery_default = useMediaQuery;
842
+ function useOnClickOutside(ref, handler, enabled = true) {
843
+ useEffect(() => {
844
+ if (!enabled) return;
845
+ const listener = (event) => {
846
+ const el = ref?.current;
847
+ if (!el || el.contains(event.target)) {
848
+ return;
849
+ }
850
+ handler(event);
851
+ };
852
+ document.addEventListener("mousedown", listener);
853
+ document.addEventListener("touchstart", listener);
854
+ return () => {
855
+ document.removeEventListener("mousedown", listener);
856
+ document.removeEventListener("touchstart", listener);
857
+ };
858
+ }, [ref, handler, enabled]);
859
+ }
860
+ var useOnClickOutside_default = useOnClickOutside;
861
+ function useWindowSize(debounceMs = 100) {
862
+ const getSize = () => ({
863
+ width: typeof window !== "undefined" ? window.innerWidth : 0,
864
+ height: typeof window !== "undefined" ? window.innerHeight : 0
865
+ });
866
+ const [windowSize, setWindowSize] = useState(getSize);
867
+ useEffect(() => {
868
+ if (typeof window === "undefined") return;
869
+ let timeoutId;
870
+ const handleResize = () => {
871
+ clearTimeout(timeoutId);
872
+ timeoutId = setTimeout(() => {
873
+ setWindowSize(getSize());
874
+ }, debounceMs);
875
+ };
876
+ setWindowSize(getSize());
877
+ window.addEventListener("resize", handleResize);
878
+ return () => {
879
+ clearTimeout(timeoutId);
880
+ window.removeEventListener("resize", handleResize);
881
+ };
882
+ }, [debounceMs]);
883
+ return windowSize;
884
+ }
885
+ var useWindowSize_default = useWindowSize;
886
+
887
+ export { ApiUrlBuilder, ClientLogger, EventEmitter, addDays, appEvents, camelToKebab, capitalize, capitalizeWords, clientLogger, copyToClipboard, createApiEndpoints, createApiUrlBuilder, createClientLogger, createEmptyPaginationMeta, createErrorResponse, createEventEmitter, createHttpClient, createSuccessResponse, endOfDay, formatDate, formatDateForInput, formatDateTime, formatDateTimeForInput, formatRelativeTime, getErrorMessage, getNextPage, getPrevPage, getResponseData, hasData, hasMorePages, isClipboardAvailable, isErrorResponse, isForbidden, isFuture, isNotFound, isPast, isServerError, isStatusError, isSuccess, isSuccessResponse, isToday, isUnauthorized, kebabToCamel, parseError, parseFullResponse, parseResponse, readFromClipboard, slugify, slugifyUnique, startOfDay, truncate, truncateWords, unslugify, useCopyToClipboard_default as useCopyToClipboard, useDebounce_default as useDebounce, useInterval_default as useInterval, useIsDesktop, useIsMobile, useIsMobileOrTablet, useIsTablet, useLocalStorage_default as useLocalStorage, useMediaQuery_default as useMediaQuery, useOnClickOutside_default as useOnClickOutside, usePageTitle_default as usePageTitle, useSnackbar_default as useSnackbar, useThemeDetector_default as useThemeDetector, useWindowSize_default as useWindowSize, withAbortSignal, withFormData, withTimeout };
888
+ //# sourceMappingURL=index.mjs.map
889
+ //# sourceMappingURL=index.mjs.map