@dragonmastery/dragoncore-vue 0.0.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 (138) hide show
  1. package/LICENSE +75 -0
  2. package/dist/AppLink-CHMMrSFI.js +54 -0
  3. package/dist/AppLink-CHMMrSFI.js.map +1 -0
  4. package/dist/Appearance-BfPdKMXw.js +70 -0
  5. package/dist/Appearance-BfPdKMXw.js.map +1 -0
  6. package/dist/Appearance-C3WguxT-.js +3 -0
  7. package/dist/ChangePasswordPage-Btu5lf-r.js +86 -0
  8. package/dist/ChangePasswordPage-Btu5lf-r.js.map +1 -0
  9. package/dist/ChangePasswordPage-mBBuQMkT.js +6 -0
  10. package/dist/CreateTeamForm-n2ut93vM.js +43 -0
  11. package/dist/CreateTeamMemberForm-CcH3AxNL.js +43 -0
  12. package/dist/CreateUserPage-CDrGuW9B.js +6 -0
  13. package/dist/CreateUserPage-Cmx8xjjv.js +76 -0
  14. package/dist/CreateUserPage-Cmx8xjjv.js.map +1 -0
  15. package/dist/CreditBalanceDashboard-DLz0ioP3.js +43 -0
  16. package/dist/CreditManagement-D3q5S-qc.js +43 -0
  17. package/dist/CustomerCreateSupportTicketForm-Ci7QYkG-.js +43 -0
  18. package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js +159 -0
  19. package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js.map +1 -0
  20. package/dist/CustomerEditSupportTicketForm-lLchVjnw.js +9 -0
  21. package/dist/CustomerSupportTicketAttachmentsTab-gBrVO97t.js +43 -0
  22. package/dist/CustomerSupportTicketCustomerNotesTab-D0jhzbOY.js +8 -0
  23. package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js +23 -0
  24. package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js.map +1 -0
  25. package/dist/CustomerSupportTicketHistoryTab-BNTf8EZq.js +6 -0
  26. package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js +17 -0
  27. package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js.map +1 -0
  28. package/dist/CustomerSupportTicketList-BkOzFxMP.js +6 -0
  29. package/dist/CustomerSupportTicketList-C2nUPawb.js +166 -0
  30. package/dist/CustomerSupportTicketList-C2nUPawb.js.map +1 -0
  31. package/dist/CustomerSupportTicketParent-2mONd9kL.js +66 -0
  32. package/dist/CustomerSupportTicketParent-2mONd9kL.js.map +1 -0
  33. package/dist/CustomerSupportTicketParent-N8ko1yFE.js +7 -0
  34. package/dist/CustomerSupportTicketSuccess-w_-9NXT4.js +43 -0
  35. package/dist/CustomerViewSupportTicket-CVwNH0lS.js +11 -0
  36. package/dist/CustomerViewSupportTicket-tZkxragu.js +363 -0
  37. package/dist/CustomerViewSupportTicket-tZkxragu.js.map +1 -0
  38. package/dist/EditTeamForm-BioqiTWE.js +43 -0
  39. package/dist/EditTeamMemberForm-DCq0Gsn_.js +7 -0
  40. package/dist/EditTeamMemberForm-ru4WgLz-.js +169 -0
  41. package/dist/EditTeamMemberForm-ru4WgLz-.js.map +1 -0
  42. package/dist/EditUserPage-BxJ5QvIM.js +112 -0
  43. package/dist/EditUserPage-BxJ5QvIM.js.map +1 -0
  44. package/dist/EditUserPage-XOBuxUxd.js +7 -0
  45. package/dist/FieldsetSection-CsHN38_o.js +27 -0
  46. package/dist/FieldsetSection-CsHN38_o.js.map +1 -0
  47. package/dist/ForgotPassword-CpqvcSFg.js +7 -0
  48. package/dist/ForgotPassword-CqhenzUG.js +73 -0
  49. package/dist/ForgotPassword-CqhenzUG.js.map +1 -0
  50. package/dist/InlineAttachments-I39rOvip.js +1351 -0
  51. package/dist/InlineAttachments-I39rOvip.js.map +1 -0
  52. package/dist/LoginForm-AM0qkfbU.js +7 -0
  53. package/dist/LoginForm-_PZ51Uwe.js +116 -0
  54. package/dist/LoginForm-_PZ51Uwe.js.map +1 -0
  55. package/dist/Logout-BMjiqHnS.js +38 -0
  56. package/dist/Logout-BMjiqHnS.js.map +1 -0
  57. package/dist/Logout-BfiBjlaH.js +6 -0
  58. package/dist/NoteList-C0hRPNMO.js +497 -0
  59. package/dist/NoteList-C0hRPNMO.js.map +1 -0
  60. package/dist/NotificationEmailsPage-BjRqtW95.js +141 -0
  61. package/dist/NotificationEmailsPage-BjRqtW95.js.map +1 -0
  62. package/dist/NotificationEmailsPage-bx-9rg3x.js +7 -0
  63. package/dist/ResetPassword-BQLkR9TZ.js +43 -0
  64. package/dist/Signup-CnCcQlB8.js +7 -0
  65. package/dist/Signup-c2-_yMOM.js +106 -0
  66. package/dist/Signup-c2-_yMOM.js.map +1 -0
  67. package/dist/StaffCreateSupportTicketForm-ChVFDJdA.js +43 -0
  68. package/dist/StaffEditSupportTicketForm-DY1Zkf5k.js +9 -0
  69. package/dist/StaffEditSupportTicketForm-DuUKuIGg.js +263 -0
  70. package/dist/StaffEditSupportTicketForm-DuUKuIGg.js.map +1 -0
  71. package/dist/StaffSupportTicketAttachmentsTab-DpDXsHXP.js +43 -0
  72. package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js +23 -0
  73. package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js.map +1 -0
  74. package/dist/StaffSupportTicketCustomerNotesTab-rbJHJ0_V.js +8 -0
  75. package/dist/StaffSupportTicketHistoryTab-D24myEm3.js +17 -0
  76. package/dist/StaffSupportTicketHistoryTab-D24myEm3.js.map +1 -0
  77. package/dist/StaffSupportTicketHistoryTab-nmVma5vp.js +6 -0
  78. package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js +23 -0
  79. package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js.map +1 -0
  80. package/dist/StaffSupportTicketInternalNotesTab-DihYd5XI.js +8 -0
  81. package/dist/StaffSupportTicketList-DelptSmK.js +43 -0
  82. package/dist/StaffSupportTicketParent-BCrj3ckV.js +7 -0
  83. package/dist/StaffSupportTicketParent-Cx1buQZw.js +66 -0
  84. package/dist/StaffSupportTicketParent-Cx1buQZw.js.map +1 -0
  85. package/dist/StaffSupportTicketSuccess-BYxtY5wZ.js +43 -0
  86. package/dist/StaffSupportTicketWorkflowTab-BrDDBeK9.js +9 -0
  87. package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js +1234 -0
  88. package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js.map +1 -0
  89. package/dist/SupportTicketHistoryTab-CLMopA7a.js +220 -0
  90. package/dist/SupportTicketHistoryTab-CLMopA7a.js.map +1 -0
  91. package/dist/SupportTicketStatusBadge-YdZzjvkh.js +163 -0
  92. package/dist/SupportTicketStatusBadge-YdZzjvkh.js.map +1 -0
  93. package/dist/TeamAttachmentsTab-BxUpTWYh.js +43 -0
  94. package/dist/TeamHistoryTab-CUCT9MRG.js +5 -0
  95. package/dist/TeamHistoryTab-gB3H2KZv.js +219 -0
  96. package/dist/TeamHistoryTab-gB3H2KZv.js.map +1 -0
  97. package/dist/TeamList-By6pzWm5.js +43 -0
  98. package/dist/TeamMemberList-CYV9fWEb.js +43 -0
  99. package/dist/TeamMemberParent-CVvGqpxD.js +43 -0
  100. package/dist/TeamMembersTab-4gmnP9sD.js +21 -0
  101. package/dist/TeamMembersTab-4gmnP9sD.js.map +1 -0
  102. package/dist/TeamMembersTab-CpE9BaCi.js +3 -0
  103. package/dist/TeamNotesTab-pfXTDhg6.js +23 -0
  104. package/dist/TeamNotesTab-pfXTDhg6.js.map +1 -0
  105. package/dist/TeamNotesTab-u4cDC67X.js +8 -0
  106. package/dist/TeamParent-BxT1KubK.js +43 -0
  107. package/dist/UserListPage-DsQdH2Sm.js +4 -0
  108. package/dist/UserListPage-WU56KiWj.js +153 -0
  109. package/dist/UserListPage-WU56KiWj.js.map +1 -0
  110. package/dist/UserProfilePage-B73JhjUu.js +7 -0
  111. package/dist/UserProfilePage-BtLUY1kt.js +125 -0
  112. package/dist/UserProfilePage-BtLUY1kt.js.map +1 -0
  113. package/dist/ViewTeam-DzX-obEl.js +43 -0
  114. package/dist/ViewTeamMember-PF6S_4Pb.js +43 -0
  115. package/dist/ZiniaContainer-C7c7Vwkh.js +18 -0
  116. package/dist/ZiniaContainer-C7c7Vwkh.js.map +1 -0
  117. package/dist/convertToLocalDateTime-D4IoNvRj.js +111 -0
  118. package/dist/convertToLocalDateTime-D4IoNvRj.js.map +1 -0
  119. package/dist/creditValueFormatter-DftEzu8d.js +128 -0
  120. package/dist/creditValueFormatter-DftEzu8d.js.map +1 -0
  121. package/dist/displayIdFormatter-Dz900Awr.js +13 -0
  122. package/dist/displayIdFormatter-Dz900Awr.js.map +1 -0
  123. package/dist/index.d.ts +6068 -0
  124. package/dist/index.js +45 -0
  125. package/dist/src-o5fMIo5_.js +6649 -0
  126. package/dist/src-o5fMIo5_.js.map +1 -0
  127. package/dist/useBreadcrumbs-DmgSucoe.js +41 -0
  128. package/dist/useBreadcrumbs-DmgSucoe.js.map +1 -0
  129. package/dist/useMutation-CFwe7H9j.js +50 -0
  130. package/dist/useMutation-CFwe7H9j.js.map +1 -0
  131. package/dist/useQuery-p7oJO7OD.js +107 -0
  132. package/dist/useQuery-p7oJO7OD.js.map +1 -0
  133. package/dist/useQueryCache-ByayvZgZ.js +254 -0
  134. package/dist/useQueryCache-ByayvZgZ.js.map +1 -0
  135. package/dist/useRpcAuth-BLlRSHy8.js +722 -0
  136. package/dist/useRpcAuth-BLlRSHy8.js.map +1 -0
  137. package/package.json +62 -0
  138. package/src/daisyui.css +63 -0
@@ -0,0 +1,722 @@
1
+ import { computed, readonly, ref, watchEffect } from "vue";
2
+ import { newHttpBatchRpcSession } from "capnweb";
3
+ import { jwtDecode } from "jwt-decode";
4
+ import { defineStore } from "pinia";
5
+
6
+ //#region src/composables/useEnv.ts
7
+ function useEnv() {
8
+ return readonly({
9
+ restApiClient: {
10
+ apiUrl: import.meta.env.VITE_API_CLIENT_URL,
11
+ authOptions: {
12
+ userDetails: import.meta.env.VITE_CF_APP_USER_DETAILS,
13
+ accessToken: import.meta.env.VITE_CF_APP_ACCESS_TOKEN,
14
+ decodedToken: import.meta.env.VITE_CF_APP_DECODED_TOKEN
15
+ }
16
+ },
17
+ allowUserSignup: import.meta.env.VITE_ALLOW_USER_SIGNUP === "true",
18
+ maxAttachmentFileSize: import.meta.env.VITE_MAX_ATTACHMENT_FILE_SIZE_MB ? parseInt(import.meta.env.VITE_MAX_ATTACHMENT_FILE_SIZE_MB, 10) * 1024 * 1024 : 50 * 1024 * 1024
19
+ });
20
+ }
21
+
22
+ //#endregion
23
+ //#region src/utils/logger.ts
24
+ /**
25
+ * Log levels as string literals
26
+ */
27
+ const LOG_LEVEL = {
28
+ NONE: "NONE",
29
+ ERROR: "ERROR",
30
+ WARN: "WARN",
31
+ INFO: "INFO",
32
+ DEBUG: "DEBUG",
33
+ TRACE: "TRACE"
34
+ };
35
+ /**
36
+ * Logger utility that conditionally logs based on configured log level
37
+ */
38
+ var Logger = class Logger {
39
+ static instance;
40
+ logLevel = LOG_LEVEL.INFO;
41
+ constructor() {
42
+ this.setLogLevelFromEnv();
43
+ }
44
+ /**
45
+ * Get the singleton logger instance
46
+ */
47
+ static getInstance() {
48
+ if (!Logger.instance) Logger.instance = new Logger();
49
+ return Logger.instance;
50
+ }
51
+ /**
52
+ * Set the log level from environment variables
53
+ */
54
+ setLogLevelFromEnv() {
55
+ const logLevelStr = import.meta.env.VITE_LOG_LEVEL || "";
56
+ if (logLevelStr) switch (logLevelStr.toUpperCase()) {
57
+ case "NONE":
58
+ this.logLevel = LOG_LEVEL.NONE;
59
+ break;
60
+ case "ERROR":
61
+ this.logLevel = LOG_LEVEL.ERROR;
62
+ break;
63
+ case "WARN":
64
+ this.logLevel = LOG_LEVEL.WARN;
65
+ break;
66
+ case "INFO":
67
+ this.logLevel = LOG_LEVEL.INFO;
68
+ break;
69
+ case "DEBUG":
70
+ this.logLevel = LOG_LEVEL.DEBUG;
71
+ break;
72
+ case "TRACE":
73
+ this.logLevel = LOG_LEVEL.TRACE;
74
+ break;
75
+ }
76
+ else this.logLevel = import.meta.env.DEV === true || import.meta.env.MODE === "development" || import.meta.env.VITE_APP_ENV === "local" || import.meta.env.VITE_APP_ENV === "dev" ? LOG_LEVEL.DEBUG : LOG_LEVEL.INFO;
77
+ console.log(`[Logger] Log level set to: ${this.logLevel.toUpperCase()}`);
78
+ }
79
+ /**
80
+ * Set the log level programmatically
81
+ */
82
+ setLogLevel(level) {
83
+ this.logLevel = level;
84
+ console.log(`[Logger] Log level changed to: ${this.logLevel.toUpperCase()}`);
85
+ }
86
+ /**
87
+ * Get the current log level
88
+ */
89
+ getLogLevel() {
90
+ return this.logLevel;
91
+ }
92
+ /**
93
+ * Determine if a message at the given level should be logged
94
+ * based on the current log level setting
95
+ */
96
+ shouldLog(level) {
97
+ const levelPriority = {
98
+ [LOG_LEVEL.NONE]: 0,
99
+ [LOG_LEVEL.ERROR]: 1,
100
+ [LOG_LEVEL.WARN]: 2,
101
+ [LOG_LEVEL.INFO]: 3,
102
+ [LOG_LEVEL.DEBUG]: 4,
103
+ [LOG_LEVEL.TRACE]: 5
104
+ };
105
+ return levelPriority[this.logLevel] >= levelPriority[level];
106
+ }
107
+ /**
108
+ * Log a trace message (most verbose)
109
+ */
110
+ trace(message, ...args) {
111
+ if (this.shouldLog(LOG_LEVEL.TRACE)) console.log(`[TRACE] ${message}`, ...args);
112
+ }
113
+ /**
114
+ * Log a debug message
115
+ */
116
+ debug(message, ...args) {
117
+ if (this.shouldLog(LOG_LEVEL.DEBUG)) console.debug(`[DEBUG] ${message}`, ...args);
118
+ }
119
+ /**
120
+ * Log an info message
121
+ */
122
+ info(message, ...args) {
123
+ if (this.shouldLog(LOG_LEVEL.INFO)) console.log(`[INFO] ${message}`, ...args);
124
+ }
125
+ /**
126
+ * Log a warning message
127
+ */
128
+ warn(message, ...args) {
129
+ if (this.shouldLog(LOG_LEVEL.WARN)) console.warn(`[WARN] ${message}`, ...args);
130
+ }
131
+ /**
132
+ * Log an error message
133
+ */
134
+ error(message, ...args) {
135
+ if (this.shouldLog(LOG_LEVEL.ERROR)) console.error(`[ERROR] ${message}`, ...args);
136
+ }
137
+ };
138
+ const logger = Logger.getInstance();
139
+ /**
140
+ * Conditionally log based on per-instance debug flag and global log level
141
+ * - If debug === false: never log (explicitly disabled)
142
+ * - If debug === true: always log (bypasses global log level)
143
+ * - If debug === undefined: use global log level (default behavior)
144
+ */
145
+ function logIfEnabled(level, message, debug, ...args) {
146
+ if (debug === false) return;
147
+ if (debug === true) {
148
+ const prefix = `[${level.toUpperCase()}]`;
149
+ if (level === "error") console.error(prefix, message, ...args);
150
+ else if (level === "warn") console.warn(prefix, message, ...args);
151
+ else console.log(prefix, message, ...args);
152
+ return;
153
+ }
154
+ if (level === "debug") logger.debug(message, ...args);
155
+ else if (level === "error") logger.error(message, ...args);
156
+ else if (level === "warn") logger.warn(message, ...args);
157
+ else logger.info(message, ...args);
158
+ }
159
+
160
+ //#endregion
161
+ //#region src/composables/useUserSessionStore.ts
162
+ /**
163
+ * Generic user session store for Dragoncore applications.
164
+ *
165
+ * Responsibilities:
166
+ * - Store and manage user session state (accessToken, userSession)
167
+ * - Sync with localStorage
168
+ * - Provide token refresh capability
169
+ * - Provide token expiration checking
170
+ *
171
+ * Note: This store is intentionally NOT generic on TApi.
172
+ * The refresh method uses createAppBatch which can be typed on a per-call basis.
173
+ */
174
+ const useUserSessionStore = defineStore("userSession", () => {
175
+ const env = useEnv().restApiClient;
176
+ const userSession = ref(null);
177
+ const accessToken = ref(null);
178
+ const decodedToken = ref(null);
179
+ const isRefreshing = ref(false);
180
+ const debug = ref(false);
181
+ watchEffect(() => {
182
+ if (!env.authOptions.accessToken || !env.authOptions.userDetails) {
183
+ logIfEnabled("error", "[UserSessionStore] localStorage keys are undefined - check environment variables", debug.value);
184
+ return;
185
+ }
186
+ const storedAccessToken = localStorage.getItem(env.authOptions.accessToken);
187
+ if (storedAccessToken !== accessToken.value) {
188
+ accessToken.value = storedAccessToken;
189
+ try {
190
+ decodedToken.value = storedAccessToken ? jwtDecode(storedAccessToken) : null;
191
+ } catch (error) {
192
+ logIfEnabled("error", "[UserSessionStore] Failed to decode access token:", debug.value, error);
193
+ decodedToken.value = null;
194
+ }
195
+ }
196
+ const storedUserSession = localStorage.getItem(env.authOptions.userDetails);
197
+ if (storedUserSession) try {
198
+ const sessionData = jwtDecode(storedUserSession).details;
199
+ if (JSON.stringify(sessionData) !== JSON.stringify(userSession.value)) userSession.value = sessionData;
200
+ } catch (error) {
201
+ logIfEnabled("error", "[UserSessionStore] Failed to decode user session:", debug.value, error);
202
+ if (userSession.value !== null) userSession.value = null;
203
+ }
204
+ else if (userSession.value !== null) userSession.value = null;
205
+ });
206
+ const currentSession = computed(() => {
207
+ if (!userSession.value) return null;
208
+ return userSession.value;
209
+ });
210
+ const clientHeaders = computed(() => ({
211
+ "Content-Type": "application/json",
212
+ ...accessToken.value ? { Authorization: `Bearer ${accessToken.value}` } : {}
213
+ }));
214
+ /**
215
+ * Refresh the access token using the refresh token (from HTTP-only cookie).
216
+ * This method uses createAppBatch directly to avoid circular dependencies.
217
+ *
218
+ * @returns true if refresh was successful, false otherwise
219
+ */
220
+ async function refreshToken() {
221
+ try {
222
+ if (isRefreshing.value) {
223
+ logIfEnabled("debug", "[RefreshToken] Token refresh already in progress", debug.value);
224
+ return false;
225
+ }
226
+ isRefreshing.value = true;
227
+ logIfEnabled("debug", "[RefreshToken] Starting token refresh", debug.value);
228
+ if (userSession.value) {
229
+ if (isTokenExpired().refreshTokenExpired) {
230
+ logIfEnabled("warn", "[RefreshToken] Refresh token is already expired, cannot refresh", debug.value);
231
+ return false;
232
+ }
233
+ }
234
+ const tokens = await createAppBatch$1({ credentials: "include" }).userSessions.refreshToken();
235
+ if (!tokens || !tokens.access_token || !tokens.user_details_token) {
236
+ logIfEnabled("error", "[RefreshToken] Invalid response from refresh endpoint", debug.value);
237
+ return false;
238
+ }
239
+ setSession(tokens.user_details_token);
240
+ setAccessToken(tokens.access_token);
241
+ if (!(!!userSession.value && !!accessToken.value)) {
242
+ logIfEnabled("error", "[RefreshToken] Failed to update all session components", debug.value);
243
+ return false;
244
+ }
245
+ if (isTokenExpired().accessTokenExpired) {
246
+ logIfEnabled("error", "[RefreshToken] New access token is already expired!", debug.value);
247
+ return false;
248
+ }
249
+ logIfEnabled("debug", "[RefreshToken] Token refresh successful", debug.value);
250
+ return true;
251
+ } catch (refreshError) {
252
+ logIfEnabled("error", "[RefreshToken] Token refresh failed:", debug.value, refreshError);
253
+ return false;
254
+ } finally {
255
+ isRefreshing.value = false;
256
+ }
257
+ }
258
+ /**
259
+ * Check if tokens are expired
260
+ */
261
+ function isTokenExpired() {
262
+ const now = Date.now() / 1e3;
263
+ return {
264
+ accessTokenExpired: decodedToken.value?.exp ? decodedToken.value.exp < now : true,
265
+ refreshTokenExpired: userSession.value?.expires_at ? new Date(userSession.value.expires_at).getTime() / 1e3 < now : true
266
+ };
267
+ }
268
+ /**
269
+ * Set the user session (stored in localStorage as JWT)
270
+ */
271
+ function setSession(token) {
272
+ if (token) try {
273
+ userSession.value = jwtDecode(token).details;
274
+ localStorage.setItem(env.authOptions.userDetails, token);
275
+ logIfEnabled("debug", "[SetSession] User session set successfully", debug.value);
276
+ } catch (error) {
277
+ logger.error("[SetSession] Failed to decode user session token:", error);
278
+ userSession.value = null;
279
+ localStorage.removeItem(env.authOptions.userDetails);
280
+ }
281
+ else {
282
+ userSession.value = null;
283
+ localStorage.removeItem(env.authOptions.userDetails);
284
+ logIfEnabled("debug", "[SetSession] User session cleared", debug.value);
285
+ }
286
+ }
287
+ /**
288
+ * Set the access token (stored in localStorage as JWT string)
289
+ */
290
+ function setAccessToken(token) {
291
+ if (token) try {
292
+ const decoded = jwtDecode(token);
293
+ accessToken.value = token;
294
+ decodedToken.value = decoded;
295
+ localStorage.setItem(env.authOptions.accessToken, token);
296
+ logIfEnabled("debug", "[SetAccessToken] Access token set successfully", debug.value);
297
+ } catch (error) {
298
+ logger.error("[SetAccessToken] Failed to decode access token:", error);
299
+ accessToken.value = null;
300
+ decodedToken.value = null;
301
+ localStorage.removeItem(env.authOptions.accessToken);
302
+ if (userSession.value) {
303
+ userSession.value = null;
304
+ localStorage.removeItem(env.authOptions.userDetails);
305
+ }
306
+ }
307
+ else {
308
+ accessToken.value = null;
309
+ decodedToken.value = null;
310
+ localStorage.removeItem(env.authOptions.accessToken);
311
+ logIfEnabled("debug", "[SetAccessToken] Access token cleared", debug.value);
312
+ }
313
+ }
314
+ /**
315
+ * Clear all session data
316
+ */
317
+ function clearSession() {
318
+ setSession(null);
319
+ setAccessToken(null);
320
+ }
321
+ return {
322
+ userSession,
323
+ currentSession,
324
+ accessToken,
325
+ clientHeaders,
326
+ debug,
327
+ setSession,
328
+ setAccessToken,
329
+ clearSession,
330
+ refreshToken,
331
+ isTokenExpired
332
+ };
333
+ });
334
+
335
+ //#endregion
336
+ //#region src/composables/useRpc.ts
337
+ /**
338
+ * Creates a fresh RPC batch session with authentication headers.
339
+ * Each call creates a new session, which is the correct pattern for Cap'n Web RPC.
340
+ *
341
+ * This is the simple version - just creates a batch with auth headers.
342
+ * For advanced features (token refresh, retry, tracked sessions), use executeWithAuth from useRpcAuth.
343
+ *
344
+ * @param options - Optional configuration for the RPC batch
345
+ * @param options.credentials - Credentials mode for the request. Use 'include' for login/logout/refreshToken to allow cookies.
346
+ */
347
+ function createAppBatch$1(options) {
348
+ const userStore = useUserSessionStore();
349
+ const env = useEnv();
350
+ const headers = {};
351
+ const accessToken = userStore.accessToken;
352
+ if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
353
+ return newHttpBatchRpcSession(new Request(env.restApiClient.apiUrl + "/rpc", {
354
+ method: "POST",
355
+ headers,
356
+ credentials: options?.credentials ?? "omit"
357
+ }));
358
+ }
359
+
360
+ //#endregion
361
+ //#region src/utils/EnhancedRefreshTokenHandler.ts
362
+ var EnhancedRefreshTokenHandler = class {
363
+ refreshState = {
364
+ isRefreshing: false,
365
+ pendingPromise: null,
366
+ lastAttempt: 0,
367
+ failureCount: 0
368
+ };
369
+ async performRefreshWithStrategies() {
370
+ if (this.refreshState.isRefreshing && this.refreshState.pendingPromise) {
371
+ logger.debug("[EnhancedRefresh] Refresh already in progress, returning existing promise");
372
+ return this.refreshState.pendingPromise;
373
+ }
374
+ this.refreshState.isRefreshing = true;
375
+ this.refreshState.pendingPromise = this.executeRefreshStrategies();
376
+ try {
377
+ const tokens = await this.refreshState.pendingPromise;
378
+ this.refreshState.failureCount = 0;
379
+ logger.debug("[EnhancedRefresh] Refresh completed successfully");
380
+ return tokens;
381
+ } catch (error) {
382
+ this.refreshState.failureCount++;
383
+ logger.error("[EnhancedRefresh] All refresh strategies failed:", error);
384
+ throw error;
385
+ } finally {
386
+ this.refreshState.isRefreshing = false;
387
+ this.refreshState.pendingPromise = null;
388
+ this.refreshState.lastAttempt = Date.now();
389
+ }
390
+ }
391
+ async executeRefreshStrategies() {
392
+ const strategies = [
393
+ {
394
+ name: "Direct RefreshToken",
395
+ execute: () => this.directRefreshToken()
396
+ },
397
+ {
398
+ name: "Warmup + Retry RefreshToken",
399
+ execute: () => this.warmupAndRetryRefresh()
400
+ },
401
+ {
402
+ name: "Alternative Domain RefreshToken",
403
+ execute: () => this.alternativeDomainRefresh()
404
+ }
405
+ ];
406
+ let lastError;
407
+ let partialSuccess = false;
408
+ for (const [index, strategy] of strategies.entries()) try {
409
+ logger.debug(`[EnhancedRefresh] Attempting strategy ${index + 1}: ${strategy.name}`);
410
+ const tokens = await strategy.execute();
411
+ logger.debug(`[EnhancedRefresh] Strategy ${index + 1} (${strategy.name}) succeeded`);
412
+ return tokens;
413
+ } catch (error) {
414
+ lastError = error;
415
+ logger.debug(`[EnhancedRefresh] Strategy ${index + 1} (${strategy.name}) failed:`, error);
416
+ const errorMessage = error instanceof Error ? error.message : String(error);
417
+ if (!errorMessage.includes("No refresh_token") && !errorMessage.includes("REFRESH_TOKEN_EXPIRED")) partialSuccess = true;
418
+ if (index < strategies.length - 1) {
419
+ const backoffDelay = 100 * (index + 1);
420
+ await this.delay(backoffDelay);
421
+ }
422
+ }
423
+ if (lastError && partialSuccess) {
424
+ const enhancedError = lastError;
425
+ enhancedError.partialSuccess = true;
426
+ enhancedError.isTemporaryFailure = true;
427
+ }
428
+ throw lastError;
429
+ }
430
+ async directRefreshToken() {
431
+ try {
432
+ const tokens = await createAppBatch$1({ credentials: "include" }).userSessions.refreshToken();
433
+ if (!tokens || !tokens.access_token || !tokens.user_details_token) throw new Error("No refresh_token in response");
434
+ return {
435
+ access_token: tokens.access_token,
436
+ user_details_token: tokens.user_details_token
437
+ };
438
+ } catch (error) {
439
+ logger.error("[EnhancedRefresh] Refresh token call failed:", {
440
+ error,
441
+ name: error?.name,
442
+ message: error?.message,
443
+ code: error?.code,
444
+ originalError: error?.originalError,
445
+ stack: error?.stack
446
+ });
447
+ if (error?.name === "InternalServerError" && error?.originalError) throw error.originalError;
448
+ throw error;
449
+ }
450
+ }
451
+ async warmupAndRetryRefresh() {
452
+ logger.debug("[EnhancedRefresh] Warming up cookie context");
453
+ await this.warmupCookieContext();
454
+ await this.delay(150);
455
+ return this.directRefreshToken();
456
+ }
457
+ async alternativeDomainRefresh() {
458
+ await this.warmupCookieContext();
459
+ await this.delay(300);
460
+ return this.directRefreshToken();
461
+ }
462
+ async warmupCookieContext() {
463
+ const warmupStrategies = [
464
+ () => fetch(window.location.origin + "/favicon.ico", {
465
+ method: "HEAD",
466
+ credentials: "include",
467
+ cache: "no-cache",
468
+ mode: "same-origin"
469
+ }),
470
+ () => {
471
+ const rpcEndpoint = useEnv().restApiClient.apiUrl + "/rpc";
472
+ return fetch(rpcEndpoint, {
473
+ method: "OPTIONS",
474
+ credentials: "include",
475
+ cache: "no-cache"
476
+ });
477
+ },
478
+ () => fetch(window.location.origin + "/", {
479
+ method: "HEAD",
480
+ credentials: "include",
481
+ cache: "no-cache",
482
+ mode: "same-origin"
483
+ })
484
+ ];
485
+ try {
486
+ await Promise.race(warmupStrategies.map((strategy) => strategy().catch(() => {})));
487
+ logger.debug("[EnhancedRefresh] Cookie context warmed up");
488
+ } catch (error) {
489
+ logger.debug("[EnhancedRefresh] Cookie warmup failed, continuing:", error);
490
+ }
491
+ }
492
+ delay(ms) {
493
+ return new Promise((resolve) => setTimeout(resolve, ms));
494
+ }
495
+ shouldAttemptRefresh() {
496
+ return Date.now() - this.refreshState.lastAttempt > Math.min(5e3, 1e3 * Math.pow(2, this.refreshState.failureCount));
497
+ }
498
+ getRefreshState() {
499
+ return { ...this.refreshState };
500
+ }
501
+ };
502
+ /**
503
+ * Create an instance of the refresh token handler for your API type.
504
+ * Your API must extend AuthenticatableApi (must have userSessions.refreshToken method).
505
+ *
506
+ * @example
507
+ * ```typescript
508
+ * // In your app's setup/config
509
+ * import type { AppApi } from '@follow-zap/shared';
510
+ * import { createRefreshTokenHandler, setRefreshTokenHandler } from '@dragonmastery/dragoncore-vue';
511
+ *
512
+ * // Initialize during app setup
513
+ * setRefreshTokenHandler(createRefreshTokenHandler<AppApi>());
514
+ * ```
515
+ */
516
+ function createRefreshTokenHandler() {
517
+ return new EnhancedRefreshTokenHandler();
518
+ }
519
+ /**
520
+ * Global refresh token handler instance.
521
+ * Set this once in your app's initialization.
522
+ */
523
+ let globalRefreshTokenHandler = null;
524
+ /**
525
+ * Set the global refresh token handler for your application.
526
+ * Call this once during app initialization.
527
+ */
528
+ function setRefreshTokenHandler(handler) {
529
+ globalRefreshTokenHandler = handler;
530
+ }
531
+ /**
532
+ * Get the global refresh token handler.
533
+ * This is used internally by useQuery and useMutation.
534
+ *
535
+ * @throws Error if handler is not set
536
+ */
537
+ function getRefreshTokenHandler() {
538
+ if (!globalRefreshTokenHandler) throw new Error("Refresh token handler not initialized. Call setRefreshTokenHandler() in your app initialization.");
539
+ return globalRefreshTokenHandler;
540
+ }
541
+
542
+ //#endregion
543
+ //#region src/composables/useRpcAuth.ts
544
+ /**
545
+ * Auth error codes that should trigger token refresh
546
+ */
547
+ const AUTH_ERROR_CODES = [
548
+ "ACCESS_TOKEN_EXPIRED",
549
+ "ACCESS_TOKEN_REVOKED",
550
+ "NO_ACCESS_TOKEN",
551
+ "NO_REFRESH_TOKEN"
552
+ ];
553
+ const BATCH_MODE = {
554
+ batch: "batch",
555
+ tracked: "tracked"
556
+ };
557
+ /**
558
+ * Check if an error is an auth error that should trigger refresh
559
+ */
560
+ function isAuthError(error) {
561
+ if (!error) return false;
562
+ let actualError = error;
563
+ if (error.name === "InternalServerError" && error.originalError) actualError = error.originalError;
564
+ if (actualError.name === "AuthenticationError" || error.name === "AuthenticationError") return true;
565
+ if (actualError.code && AUTH_ERROR_CODES.includes(actualError.code)) return true;
566
+ if (error.code && AUTH_ERROR_CODES.includes(error.code)) return true;
567
+ const errorMessage = actualError.message || error.message || String(error);
568
+ if (AUTH_ERROR_CODES.some((code) => errorMessage.includes(code))) return true;
569
+ if (errorMessage.includes("Unauthorized") || errorMessage.includes("authentication") || errorMessage.includes("token")) return errorMessage.toLowerCase().includes("expired") || errorMessage.toLowerCase().includes("revoked") || errorMessage.toLowerCase().includes("invalid token") || errorMessage.toLowerCase().includes("no refresh token");
570
+ return false;
571
+ }
572
+ /**
573
+ * Global router instance.
574
+ * Set this once in your app's initialization.
575
+ */
576
+ let globalRouter = null;
577
+ /**
578
+ * Set the global router instance for your application.
579
+ * Call this once during app initialization.
580
+ */
581
+ function setRouter(router) {
582
+ globalRouter = router;
583
+ }
584
+ /**
585
+ * Get the global router instance.
586
+ * This is used internally by executeWithAuth for navigation.
587
+ *
588
+ * @throws Error if router is not set
589
+ */
590
+ function getRouter() {
591
+ if (!globalRouter) throw new Error("Router not initialized. Call setRouter() in your app initialization.");
592
+ return globalRouter;
593
+ }
594
+ /**
595
+ * Check if token needs refresh before making a request
596
+ */
597
+ function needsRefresh(userStore) {
598
+ if (!userStore.accessToken) return true;
599
+ const { accessTokenExpired, refreshTokenExpired } = userStore.isTokenExpired();
600
+ return accessTokenExpired || refreshTokenExpired;
601
+ }
602
+ /**
603
+ * Attempt to refresh the token using the provided handler
604
+ */
605
+ async function attemptRefresh(userStore, refreshTokenHandler) {
606
+ const router = getRouter();
607
+ try {
608
+ if (!userStore.currentSession && !userStore.accessToken) return false;
609
+ if (userStore.currentSession) {
610
+ const { refreshTokenExpired } = userStore.isTokenExpired();
611
+ if (refreshTokenExpired) {
612
+ await router.push("/auth/logout");
613
+ return false;
614
+ }
615
+ }
616
+ if (!refreshTokenHandler.shouldAttemptRefresh()) return false;
617
+ const tokens = await refreshTokenHandler.performRefreshWithStrategies();
618
+ userStore.setSession(tokens.user_details_token);
619
+ userStore.setAccessToken(tokens.access_token);
620
+ return true;
621
+ } catch (error) {
622
+ console.error("[RPC Auth] Refresh attempt failed:", {
623
+ error,
624
+ name: error?.name,
625
+ message: error?.message,
626
+ code: error?.code,
627
+ originalError: error?.originalError
628
+ });
629
+ const combinedMessage = `${((error?.originalError || error)?.message || error?.message || "").toUpperCase()} ${(error?.originalError?.message || "").toUpperCase()}`;
630
+ if (combinedMessage.includes("NO_REFRESH_TOKEN") || combinedMessage.includes("REFRESH_TOKEN_EXPIRED") || combinedMessage.includes("REFRESH_TOKEN_REVOKED") || combinedMessage.includes("INVALID REFRESH TOKEN") || combinedMessage.includes("NO REFRESH TOKEN FOUND")) await router.push("/auth/logout");
631
+ return false;
632
+ }
633
+ }
634
+ /**
635
+ * Create a base RPC batch with current auth token
636
+ */
637
+ function createAppBatch(options) {
638
+ const userStore = useUserSessionStore();
639
+ const env = useEnv();
640
+ const headers = {};
641
+ const accessToken = userStore.accessToken;
642
+ if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
643
+ const request = new Request(env.restApiClient.apiUrl + "/rpc", {
644
+ method: "POST",
645
+ headers,
646
+ credentials: options?.credentials ?? "omit"
647
+ });
648
+ if (options?.mode === BATCH_MODE.batch && !options?.trackedSegment) return newHttpBatchRpcSession(request);
649
+ return createTrackedSession(request, options?.trackedSegment);
650
+ }
651
+ /**
652
+ * Execute an RPC call with automatic auth handling
653
+ * - Checks token expiration before call
654
+ * - Handles auth errors and retries after refresh
655
+ *
656
+ * @param fn - Function that receives an RPC batch and returns a promise
657
+ * @param options - Configuration options
658
+ * @param options.refreshTokenHandler - Instance of refresh token handler for your API type
659
+ */
660
+ async function executeWithAuth(fn, options) {
661
+ const userStore = useUserSessionStore();
662
+ const router = getRouter();
663
+ if (options?.skipAuthCheck) return await fn(createAppBatch(options));
664
+ if (needsRefresh(userStore)) {
665
+ if (!await attemptRefresh(userStore, options.refreshTokenHandler)) {
666
+ if (!userStore.accessToken) {
667
+ await router.push("/auth/logout");
668
+ throw new Error("Authentication required");
669
+ }
670
+ } else if (!userStore.accessToken) {
671
+ await router.push("/auth/logout");
672
+ throw new Error("Token refresh failed to set access token");
673
+ }
674
+ }
675
+ try {
676
+ if (!userStore.accessToken) {
677
+ await router.push("/auth/logout");
678
+ throw new Error("Authentication required");
679
+ }
680
+ return await fn(createAppBatch(options));
681
+ } catch (error) {
682
+ if (isAuthError(error)) {
683
+ if (await attemptRefresh(userStore, options.refreshTokenHandler)) return await fn(createAppBatch(options));
684
+ await router.push("/auth/logout");
685
+ throw error;
686
+ }
687
+ throw error;
688
+ }
689
+ }
690
+ /**
691
+ * Create a tracked RPC session that appends method names to URL
692
+ */
693
+ function createTrackedSession(request, trackedSegment) {
694
+ const baseUrl = request.url;
695
+ if (trackedSegment) return newHttpBatchRpcSession(new Request(`${baseUrl}/${trackedSegment}`, {
696
+ method: request.method,
697
+ headers: request.headers,
698
+ credentials: request.credentials
699
+ }));
700
+ function createProxy(path = []) {
701
+ return new Proxy(() => {}, {
702
+ get(_target, prop) {
703
+ if (typeof prop === "string") return createProxy([...path, prop]);
704
+ },
705
+ apply(_target, _thisArg, args) {
706
+ const fullPath = path.join(".");
707
+ let method = newHttpBatchRpcSession(new Request(`${baseUrl}/${fullPath}`, {
708
+ method: request.method,
709
+ headers: request.headers,
710
+ credentials: request.credentials
711
+ }));
712
+ for (const segment of path) method = method[segment];
713
+ return method(...args);
714
+ }
715
+ });
716
+ }
717
+ return createProxy();
718
+ }
719
+
720
+ //#endregion
721
+ export { EnhancedRefreshTokenHandler as a, setRefreshTokenHandler as c, Logger as d, logIfEnabled as f, setRouter as i, useUserSessionStore as l, useEnv as m, createAppBatch as n, createRefreshTokenHandler as o, logger as p, executeWithAuth as r, getRefreshTokenHandler as s, BATCH_MODE as t, LOG_LEVEL as u };
722
+ //# sourceMappingURL=useRpcAuth-BLlRSHy8.js.map