@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.
- package/LICENSE +75 -0
- package/dist/AppLink-CHMMrSFI.js +54 -0
- package/dist/AppLink-CHMMrSFI.js.map +1 -0
- package/dist/Appearance-BfPdKMXw.js +70 -0
- package/dist/Appearance-BfPdKMXw.js.map +1 -0
- package/dist/Appearance-C3WguxT-.js +3 -0
- package/dist/ChangePasswordPage-Btu5lf-r.js +86 -0
- package/dist/ChangePasswordPage-Btu5lf-r.js.map +1 -0
- package/dist/ChangePasswordPage-mBBuQMkT.js +6 -0
- package/dist/CreateTeamForm-n2ut93vM.js +43 -0
- package/dist/CreateTeamMemberForm-CcH3AxNL.js +43 -0
- package/dist/CreateUserPage-CDrGuW9B.js +6 -0
- package/dist/CreateUserPage-Cmx8xjjv.js +76 -0
- package/dist/CreateUserPage-Cmx8xjjv.js.map +1 -0
- package/dist/CreditBalanceDashboard-DLz0ioP3.js +43 -0
- package/dist/CreditManagement-D3q5S-qc.js +43 -0
- package/dist/CustomerCreateSupportTicketForm-Ci7QYkG-.js +43 -0
- package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js +159 -0
- package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js.map +1 -0
- package/dist/CustomerEditSupportTicketForm-lLchVjnw.js +9 -0
- package/dist/CustomerSupportTicketAttachmentsTab-gBrVO97t.js +43 -0
- package/dist/CustomerSupportTicketCustomerNotesTab-D0jhzbOY.js +8 -0
- package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js +23 -0
- package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js.map +1 -0
- package/dist/CustomerSupportTicketHistoryTab-BNTf8EZq.js +6 -0
- package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js +17 -0
- package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js.map +1 -0
- package/dist/CustomerSupportTicketList-BkOzFxMP.js +6 -0
- package/dist/CustomerSupportTicketList-C2nUPawb.js +166 -0
- package/dist/CustomerSupportTicketList-C2nUPawb.js.map +1 -0
- package/dist/CustomerSupportTicketParent-2mONd9kL.js +66 -0
- package/dist/CustomerSupportTicketParent-2mONd9kL.js.map +1 -0
- package/dist/CustomerSupportTicketParent-N8ko1yFE.js +7 -0
- package/dist/CustomerSupportTicketSuccess-w_-9NXT4.js +43 -0
- package/dist/CustomerViewSupportTicket-CVwNH0lS.js +11 -0
- package/dist/CustomerViewSupportTicket-tZkxragu.js +363 -0
- package/dist/CustomerViewSupportTicket-tZkxragu.js.map +1 -0
- package/dist/EditTeamForm-BioqiTWE.js +43 -0
- package/dist/EditTeamMemberForm-DCq0Gsn_.js +7 -0
- package/dist/EditTeamMemberForm-ru4WgLz-.js +169 -0
- package/dist/EditTeamMemberForm-ru4WgLz-.js.map +1 -0
- package/dist/EditUserPage-BxJ5QvIM.js +112 -0
- package/dist/EditUserPage-BxJ5QvIM.js.map +1 -0
- package/dist/EditUserPage-XOBuxUxd.js +7 -0
- package/dist/FieldsetSection-CsHN38_o.js +27 -0
- package/dist/FieldsetSection-CsHN38_o.js.map +1 -0
- package/dist/ForgotPassword-CpqvcSFg.js +7 -0
- package/dist/ForgotPassword-CqhenzUG.js +73 -0
- package/dist/ForgotPassword-CqhenzUG.js.map +1 -0
- package/dist/InlineAttachments-I39rOvip.js +1351 -0
- package/dist/InlineAttachments-I39rOvip.js.map +1 -0
- package/dist/LoginForm-AM0qkfbU.js +7 -0
- package/dist/LoginForm-_PZ51Uwe.js +116 -0
- package/dist/LoginForm-_PZ51Uwe.js.map +1 -0
- package/dist/Logout-BMjiqHnS.js +38 -0
- package/dist/Logout-BMjiqHnS.js.map +1 -0
- package/dist/Logout-BfiBjlaH.js +6 -0
- package/dist/NoteList-C0hRPNMO.js +497 -0
- package/dist/NoteList-C0hRPNMO.js.map +1 -0
- package/dist/NotificationEmailsPage-BjRqtW95.js +141 -0
- package/dist/NotificationEmailsPage-BjRqtW95.js.map +1 -0
- package/dist/NotificationEmailsPage-bx-9rg3x.js +7 -0
- package/dist/ResetPassword-BQLkR9TZ.js +43 -0
- package/dist/Signup-CnCcQlB8.js +7 -0
- package/dist/Signup-c2-_yMOM.js +106 -0
- package/dist/Signup-c2-_yMOM.js.map +1 -0
- package/dist/StaffCreateSupportTicketForm-ChVFDJdA.js +43 -0
- package/dist/StaffEditSupportTicketForm-DY1Zkf5k.js +9 -0
- package/dist/StaffEditSupportTicketForm-DuUKuIGg.js +263 -0
- package/dist/StaffEditSupportTicketForm-DuUKuIGg.js.map +1 -0
- package/dist/StaffSupportTicketAttachmentsTab-DpDXsHXP.js +43 -0
- package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js +23 -0
- package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js.map +1 -0
- package/dist/StaffSupportTicketCustomerNotesTab-rbJHJ0_V.js +8 -0
- package/dist/StaffSupportTicketHistoryTab-D24myEm3.js +17 -0
- package/dist/StaffSupportTicketHistoryTab-D24myEm3.js.map +1 -0
- package/dist/StaffSupportTicketHistoryTab-nmVma5vp.js +6 -0
- package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js +23 -0
- package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js.map +1 -0
- package/dist/StaffSupportTicketInternalNotesTab-DihYd5XI.js +8 -0
- package/dist/StaffSupportTicketList-DelptSmK.js +43 -0
- package/dist/StaffSupportTicketParent-BCrj3ckV.js +7 -0
- package/dist/StaffSupportTicketParent-Cx1buQZw.js +66 -0
- package/dist/StaffSupportTicketParent-Cx1buQZw.js.map +1 -0
- package/dist/StaffSupportTicketSuccess-BYxtY5wZ.js +43 -0
- package/dist/StaffSupportTicketWorkflowTab-BrDDBeK9.js +9 -0
- package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js +1234 -0
- package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js.map +1 -0
- package/dist/SupportTicketHistoryTab-CLMopA7a.js +220 -0
- package/dist/SupportTicketHistoryTab-CLMopA7a.js.map +1 -0
- package/dist/SupportTicketStatusBadge-YdZzjvkh.js +163 -0
- package/dist/SupportTicketStatusBadge-YdZzjvkh.js.map +1 -0
- package/dist/TeamAttachmentsTab-BxUpTWYh.js +43 -0
- package/dist/TeamHistoryTab-CUCT9MRG.js +5 -0
- package/dist/TeamHistoryTab-gB3H2KZv.js +219 -0
- package/dist/TeamHistoryTab-gB3H2KZv.js.map +1 -0
- package/dist/TeamList-By6pzWm5.js +43 -0
- package/dist/TeamMemberList-CYV9fWEb.js +43 -0
- package/dist/TeamMemberParent-CVvGqpxD.js +43 -0
- package/dist/TeamMembersTab-4gmnP9sD.js +21 -0
- package/dist/TeamMembersTab-4gmnP9sD.js.map +1 -0
- package/dist/TeamMembersTab-CpE9BaCi.js +3 -0
- package/dist/TeamNotesTab-pfXTDhg6.js +23 -0
- package/dist/TeamNotesTab-pfXTDhg6.js.map +1 -0
- package/dist/TeamNotesTab-u4cDC67X.js +8 -0
- package/dist/TeamParent-BxT1KubK.js +43 -0
- package/dist/UserListPage-DsQdH2Sm.js +4 -0
- package/dist/UserListPage-WU56KiWj.js +153 -0
- package/dist/UserListPage-WU56KiWj.js.map +1 -0
- package/dist/UserProfilePage-B73JhjUu.js +7 -0
- package/dist/UserProfilePage-BtLUY1kt.js +125 -0
- package/dist/UserProfilePage-BtLUY1kt.js.map +1 -0
- package/dist/ViewTeam-DzX-obEl.js +43 -0
- package/dist/ViewTeamMember-PF6S_4Pb.js +43 -0
- package/dist/ZiniaContainer-C7c7Vwkh.js +18 -0
- package/dist/ZiniaContainer-C7c7Vwkh.js.map +1 -0
- package/dist/convertToLocalDateTime-D4IoNvRj.js +111 -0
- package/dist/convertToLocalDateTime-D4IoNvRj.js.map +1 -0
- package/dist/creditValueFormatter-DftEzu8d.js +128 -0
- package/dist/creditValueFormatter-DftEzu8d.js.map +1 -0
- package/dist/displayIdFormatter-Dz900Awr.js +13 -0
- package/dist/displayIdFormatter-Dz900Awr.js.map +1 -0
- package/dist/index.d.ts +6068 -0
- package/dist/index.js +45 -0
- package/dist/src-o5fMIo5_.js +6649 -0
- package/dist/src-o5fMIo5_.js.map +1 -0
- package/dist/useBreadcrumbs-DmgSucoe.js +41 -0
- package/dist/useBreadcrumbs-DmgSucoe.js.map +1 -0
- package/dist/useMutation-CFwe7H9j.js +50 -0
- package/dist/useMutation-CFwe7H9j.js.map +1 -0
- package/dist/useQuery-p7oJO7OD.js +107 -0
- package/dist/useQuery-p7oJO7OD.js.map +1 -0
- package/dist/useQueryCache-ByayvZgZ.js +254 -0
- package/dist/useQueryCache-ByayvZgZ.js.map +1 -0
- package/dist/useRpcAuth-BLlRSHy8.js +722 -0
- package/dist/useRpcAuth-BLlRSHy8.js.map +1 -0
- package/package.json +62 -0
- package/src/daisyui.css +63 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { inject, onUnmounted, watchEffect } from "vue";
|
|
2
|
+
|
|
3
|
+
//#region src/composables/useBreadcrumbs.ts
|
|
4
|
+
const BREADCRUMB_KEY = Symbol("breadcrumbs");
|
|
5
|
+
let componentIdCounter = 0;
|
|
6
|
+
/**
|
|
7
|
+
* Composable for components to set their own breadcrumbs
|
|
8
|
+
* Accepts a function that returns breadcrumbs - handles reactivity automatically
|
|
9
|
+
* Automatically clears breadcrumbs when component unmounts
|
|
10
|
+
*
|
|
11
|
+
* @param getBreadcrumbs - Function that returns breadcrumb array (reactive)
|
|
12
|
+
* @param useSegmentMode - If true, uses the new segment-based system for nested breadcrumbs
|
|
13
|
+
*/
|
|
14
|
+
function useBreadcrumbs(getBreadcrumbs, useSegmentMode = false) {
|
|
15
|
+
const context = inject(BREADCRUMB_KEY);
|
|
16
|
+
if (!context) {
|
|
17
|
+
console.warn("useBreadcrumbs: No breadcrumb context found. Make sure InApp.vue provides it.");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (useSegmentMode) {
|
|
21
|
+
const componentId = Symbol(`breadcrumb-${componentIdCounter++}`);
|
|
22
|
+
watchEffect(() => {
|
|
23
|
+
const breadcrumbs = getBreadcrumbs();
|
|
24
|
+
context.setBreadcrumbSegment(componentId, breadcrumbs.length > 0 ? breadcrumbs : null);
|
|
25
|
+
});
|
|
26
|
+
onUnmounted(() => {
|
|
27
|
+
context.setBreadcrumbSegment(componentId, null);
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
watchEffect(() => {
|
|
31
|
+
context.setBreadcrumbs(getBreadcrumbs());
|
|
32
|
+
});
|
|
33
|
+
onUnmounted(() => {
|
|
34
|
+
context.setBreadcrumbs(null);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { useBreadcrumbs as n, BREADCRUMB_KEY as t };
|
|
41
|
+
//# sourceMappingURL=useBreadcrumbs-DmgSucoe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBreadcrumbs-DmgSucoe.js","names":[],"sources":["../src/composables/useBreadcrumbs.ts"],"sourcesContent":["import { inject, onUnmounted, watchEffect } from 'vue';\n\nexport const BREADCRUMB_KEY = Symbol('breadcrumbs');\n\nexport interface Breadcrumb {\n label: string;\n to: any;\n loading?: boolean;\n}\n\nexport interface BreadcrumbSegment {\n componentId: symbol;\n breadcrumbs: Breadcrumb[];\n}\n\nexport interface BreadcrumbContext {\n setBreadcrumbs: (breadcrumbs: Breadcrumb[] | null) => void;\n setBreadcrumbSegment: (componentId: symbol, breadcrumbs: Breadcrumb[] | null) => void;\n}\n\nlet componentIdCounter = 0;\n\n/**\n * Composable for components to set their own breadcrumbs\n * Accepts a function that returns breadcrumbs - handles reactivity automatically\n * Automatically clears breadcrumbs when component unmounts\n *\n * @param getBreadcrumbs - Function that returns breadcrumb array (reactive)\n * @param useSegmentMode - If true, uses the new segment-based system for nested breadcrumbs\n */\nexport function useBreadcrumbs(\n getBreadcrumbs: () => Breadcrumb[],\n useSegmentMode: boolean = false,\n) {\n const context = inject<BreadcrumbContext>(BREADCRUMB_KEY);\n\n if (!context) {\n console.warn(\n 'useBreadcrumbs: No breadcrumb context found. Make sure InApp.vue provides it.',\n );\n return;\n }\n\n if (useSegmentMode) {\n // Use segment-based mode for nested breadcrumbs\n const componentId = Symbol(`breadcrumb-${componentIdCounter++}`);\n\n watchEffect(() => {\n const breadcrumbs = getBreadcrumbs();\n context.setBreadcrumbSegment(componentId, breadcrumbs.length > 0 ? breadcrumbs : null);\n });\n\n onUnmounted(() => {\n context.setBreadcrumbSegment(componentId, null);\n });\n } else {\n // Legacy mode: single breadcrumb setter (for backward compatibility)\n watchEffect(() => {\n context.setBreadcrumbs(getBreadcrumbs());\n });\n\n onUnmounted(() => {\n context.setBreadcrumbs(null);\n });\n }\n}\n"],"mappings":";;;AAEA,MAAa,iBAAiB,OAAO,cAAc;AAkBnD,IAAI,qBAAqB;;;;;;;;;AAUzB,SAAgB,eACd,gBACA,iBAA0B,OAC1B;CACA,MAAM,UAAU,OAA0B,eAAe;AAEzD,KAAI,CAAC,SAAS;AACZ,UAAQ,KACN,gFACD;AACD;;AAGF,KAAI,gBAAgB;EAElB,MAAM,cAAc,OAAO,cAAc,uBAAuB;AAEhE,oBAAkB;GAChB,MAAM,cAAc,gBAAgB;AACpC,WAAQ,qBAAqB,aAAa,YAAY,SAAS,IAAI,cAAc,KAAK;IACtF;AAEF,oBAAkB;AAChB,WAAQ,qBAAqB,aAAa,KAAK;IAC/C;QACG;AAEL,oBAAkB;AAChB,WAAQ,eAAe,gBAAgB,CAAC;IACxC;AAEF,oBAAkB;AAChB,WAAQ,eAAe,KAAK;IAC5B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { r as executeWithAuth, s as getRefreshTokenHandler } from "./useRpcAuth-BLlRSHy8.js";
|
|
2
|
+
import { t as queryCache } from "./useQueryCache-ByayvZgZ.js";
|
|
3
|
+
import { shallowRef } from "vue";
|
|
4
|
+
|
|
5
|
+
//#region src/composables/useMutation.ts
|
|
6
|
+
function useMutation(mutationFn, options) {
|
|
7
|
+
const refreshTokenHandler = getRefreshTokenHandler();
|
|
8
|
+
const loading = shallowRef(false);
|
|
9
|
+
const error = shallowRef(null);
|
|
10
|
+
const data = shallowRef(null);
|
|
11
|
+
const mutate = async (input) => {
|
|
12
|
+
loading.value = true;
|
|
13
|
+
error.value = null;
|
|
14
|
+
try {
|
|
15
|
+
const skipAuthCheck = options?.skipAuthCheck || options?.credentials === "include";
|
|
16
|
+
const result = await executeWithAuth((batch) => mutationFn(batch, input), {
|
|
17
|
+
credentials: options?.credentials,
|
|
18
|
+
skipAuthCheck,
|
|
19
|
+
refreshTokenHandler
|
|
20
|
+
});
|
|
21
|
+
data.value = result;
|
|
22
|
+
if (options?.invalidate) if (Array.isArray(options.invalidate)) options.invalidate.forEach((key) => queryCache.invalidate(key));
|
|
23
|
+
else queryCache.invalidate(options.invalidate);
|
|
24
|
+
if (options?.onSuccess) options.onSuccess(result, input);
|
|
25
|
+
return result;
|
|
26
|
+
} catch (e) {
|
|
27
|
+
error.value = e instanceof Error ? e : /* @__PURE__ */ new Error("Unknown error");
|
|
28
|
+
if (options?.onError) options.onError(error.value, input);
|
|
29
|
+
throw e;
|
|
30
|
+
} finally {
|
|
31
|
+
loading.value = false;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const reset = () => {
|
|
35
|
+
data.value = null;
|
|
36
|
+
error.value = null;
|
|
37
|
+
loading.value = false;
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
mutate,
|
|
41
|
+
loading,
|
|
42
|
+
error,
|
|
43
|
+
data,
|
|
44
|
+
reset
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { useMutation as t };
|
|
50
|
+
//# sourceMappingURL=useMutation-CFwe7H9j.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMutation-CFwe7H9j.js","names":[],"sources":["../src/composables/useMutation.ts"],"sourcesContent":["import type { DragoncoreApi } from '@dragonmastery/dragoncore-shared';\nimport { shallowRef, type ShallowRef } from 'vue';\nimport { getRefreshTokenHandler } from '../utils/EnhancedRefreshTokenHandler';\nimport type { MutationFunction, UseMutationOptions } from './types';\nimport { queryCache } from './useQueryCache';\nimport { executeWithAuth } from './useRpcAuth';\n\nexport interface UseMutationReturn<TInput, TOutput> {\n data: ShallowRef<TOutput | null>;\n loading: ShallowRef<boolean>;\n error: ShallowRef<Error | null>;\n mutate: (input: TInput) => Promise<TOutput>;\n reset: () => void;\n}\n\nexport function useMutation<\n TApi extends Record<string, any> = DragoncoreApi,\n TInput = any,\n TOutput = any,\n>(\n mutationFn: MutationFunction<TApi, TInput, TOutput>,\n options?: UseMutationOptions<TInput, TOutput>,\n): UseMutationReturn<TInput, TOutput> {\n // Get the global refresh token handler - Dragoncore handles this automatically\n const refreshTokenHandler = getRefreshTokenHandler();\n const loading = shallowRef(false);\n const error = shallowRef<Error | null>(null);\n const data = shallowRef<TOutput | null>(null);\n\n const mutate = async (input: TInput): Promise<TOutput> => {\n loading.value = true;\n error.value = null;\n\n try {\n // Skip auth check for public endpoints (signup) or login/logout operations\n const skipAuthCheck = options?.skipAuthCheck || options?.credentials === 'include';\n const result = await executeWithAuth<TApi, TOutput>(\n (batch) => mutationFn(batch, input),\n {\n credentials: options?.credentials,\n skipAuthCheck,\n refreshTokenHandler,\n },\n );\n data.value = result;\n\n // Invalidate related queries\n if (options?.invalidate) {\n if (Array.isArray(options.invalidate)) {\n options.invalidate.forEach((key) => queryCache.invalidate(key));\n } else {\n queryCache.invalidate(options.invalidate);\n }\n }\n\n // Call success callback\n if (options?.onSuccess) {\n options.onSuccess(result, input);\n }\n\n return result;\n } catch (e) {\n error.value = e instanceof Error ? e : new Error('Unknown error');\n\n // Call error callback\n if (options?.onError) {\n options.onError(error.value, input);\n }\n\n throw e;\n } finally {\n loading.value = false;\n }\n };\n\n const reset = () => {\n data.value = null;\n error.value = null;\n loading.value = false;\n };\n\n return { mutate, loading, error, data, reset };\n}\n"],"mappings":";;;;;AAeA,SAAgB,YAKd,YACA,SACoC;CAEpC,MAAM,sBAAsB,wBAAwB;CACpD,MAAM,UAAU,WAAW,MAAM;CACjC,MAAM,QAAQ,WAAyB,KAAK;CAC5C,MAAM,OAAO,WAA2B,KAAK;CAE7C,MAAM,SAAS,OAAO,UAAoC;AACxD,UAAQ,QAAQ;AAChB,QAAM,QAAQ;AAEd,MAAI;GAEF,MAAM,gBAAgB,SAAS,iBAAiB,SAAS,gBAAgB;GACzE,MAAM,SAAS,MAAM,iBAClB,UAAU,WAAW,OAAO,MAAM,EACnC;IACE,aAAa,SAAS;IACtB;IACA;IACD,CACF;AACD,QAAK,QAAQ;AAGb,OAAI,SAAS,WACX,KAAI,MAAM,QAAQ,QAAQ,WAAW,CACnC,SAAQ,WAAW,SAAS,QAAQ,WAAW,WAAW,IAAI,CAAC;OAE/D,YAAW,WAAW,QAAQ,WAAW;AAK7C,OAAI,SAAS,UACX,SAAQ,UAAU,QAAQ,MAAM;AAGlC,UAAO;WACA,GAAG;AACV,SAAM,QAAQ,aAAa,QAAQ,oBAAI,IAAI,MAAM,gBAAgB;AAGjE,OAAI,SAAS,QACX,SAAQ,QAAQ,MAAM,OAAO,MAAM;AAGrC,SAAM;YACE;AACR,WAAQ,QAAQ;;;CAIpB,MAAM,cAAc;AAClB,OAAK,QAAQ;AACb,QAAM,QAAQ;AACd,UAAQ,QAAQ;;AAGlB,QAAO;EAAE;EAAQ;EAAS;EAAO;EAAM;EAAO"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { f as logIfEnabled, r as executeWithAuth, s as getRefreshTokenHandler } from "./useRpcAuth-BLlRSHy8.js";
|
|
2
|
+
import { t as queryCache } from "./useQueryCache-ByayvZgZ.js";
|
|
3
|
+
import { onMounted, onUnmounted, shallowRef, watch } from "vue";
|
|
4
|
+
|
|
5
|
+
//#region src/composables/useQuery.ts
|
|
6
|
+
function useQuery(queryFn, options) {
|
|
7
|
+
const refreshTokenHandler = getRefreshTokenHandler();
|
|
8
|
+
const data = shallowRef(null);
|
|
9
|
+
const loading = shallowRef(options?.immediate !== false);
|
|
10
|
+
const error = shallowRef(null);
|
|
11
|
+
const dataUpdatedAt = shallowRef(0);
|
|
12
|
+
let isBackgroundFetching = false;
|
|
13
|
+
const debug = options?.debug ?? false;
|
|
14
|
+
const persist = options?.persist ?? (options?.cacheKey ? true : false);
|
|
15
|
+
const execute = async (skipCache = false) => {
|
|
16
|
+
if (!(typeof options?.enabled === "boolean" ? options?.enabled : options?.enabled?.value ?? true)) {
|
|
17
|
+
loading.value = false;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
loading.value = true;
|
|
21
|
+
error.value = null;
|
|
22
|
+
if (options?.cacheKey && !skipCache) {
|
|
23
|
+
const cached = queryCache.get(options.cacheKey, {
|
|
24
|
+
staleTime: options?.staleTime,
|
|
25
|
+
persist,
|
|
26
|
+
debug
|
|
27
|
+
});
|
|
28
|
+
if (cached !== null) {
|
|
29
|
+
logIfEnabled("debug", `[Query] Cache HIT: ${options.cacheKey}`, debug);
|
|
30
|
+
data.value = cached;
|
|
31
|
+
loading.value = false;
|
|
32
|
+
if (options?.refetchOnWindowFocus) executeInBackground();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
logIfEnabled("debug", `[Query] Cache MISS: ${options.cacheKey}`, debug);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const result = await executeWithAuth((batch) => queryFn(batch), {
|
|
39
|
+
credentials: options?.credentials,
|
|
40
|
+
skipAuthCheck: false,
|
|
41
|
+
mode: options?.batchMode,
|
|
42
|
+
trackedSegment: options?.trackedSegment,
|
|
43
|
+
refreshTokenHandler
|
|
44
|
+
});
|
|
45
|
+
data.value = result;
|
|
46
|
+
dataUpdatedAt.value = Date.now();
|
|
47
|
+
if (options?.cacheKey) queryCache.set(options.cacheKey, result, {
|
|
48
|
+
staleTime: options?.staleTime,
|
|
49
|
+
persist,
|
|
50
|
+
debug: options?.debug
|
|
51
|
+
});
|
|
52
|
+
} catch (e) {
|
|
53
|
+
error.value = e instanceof Error ? e : /* @__PURE__ */ new Error("Unknown error");
|
|
54
|
+
data.value = null;
|
|
55
|
+
} finally {
|
|
56
|
+
loading.value = false;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const executeInBackground = async () => {
|
|
60
|
+
if (isBackgroundFetching || loading.value) return;
|
|
61
|
+
isBackgroundFetching = true;
|
|
62
|
+
try {
|
|
63
|
+
const result = await executeWithAuth((batch) => queryFn(batch), {
|
|
64
|
+
credentials: options?.credentials,
|
|
65
|
+
skipAuthCheck: false,
|
|
66
|
+
mode: options?.batchMode ? "batch" : "tracked",
|
|
67
|
+
trackedSegment: options?.trackedSegment,
|
|
68
|
+
refreshTokenHandler
|
|
69
|
+
});
|
|
70
|
+
data.value = result;
|
|
71
|
+
dataUpdatedAt.value = Date.now();
|
|
72
|
+
if (options?.cacheKey) queryCache.set(options.cacheKey, result, {
|
|
73
|
+
staleTime: options?.staleTime,
|
|
74
|
+
persist,
|
|
75
|
+
debug
|
|
76
|
+
});
|
|
77
|
+
} catch (e) {
|
|
78
|
+
logIfEnabled("error", "[Query] Background refetch failed:", debug, e);
|
|
79
|
+
} finally {
|
|
80
|
+
isBackgroundFetching = false;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
if (options?.immediate !== false) onMounted(execute);
|
|
84
|
+
if (options?.refetchOnWindowFocus) {
|
|
85
|
+
const handleFocus = () => {
|
|
86
|
+
if (data.value !== null) executeInBackground();
|
|
87
|
+
};
|
|
88
|
+
onMounted(() => window.addEventListener("focus", handleFocus));
|
|
89
|
+
onUnmounted(() => window.removeEventListener("focus", handleFocus));
|
|
90
|
+
}
|
|
91
|
+
if (options?.watch) watch(Array.isArray(options.watch) ? options.watch : [options.watch], () => execute());
|
|
92
|
+
if (options?.enabled && typeof options?.enabled !== "boolean") watch(options?.enabled, (isEnabled) => {
|
|
93
|
+
if (isEnabled) execute();
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
data,
|
|
97
|
+
loading,
|
|
98
|
+
error,
|
|
99
|
+
dataUpdatedAt,
|
|
100
|
+
execute: () => execute(),
|
|
101
|
+
refetch: () => execute(true)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
export { useQuery as t };
|
|
107
|
+
//# sourceMappingURL=useQuery-p7oJO7OD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQuery-p7oJO7OD.js","names":[],"sources":["../src/composables/useQuery.ts"],"sourcesContent":["import type { DragoncoreApi } from '@dragonmastery/dragoncore-shared';\nimport { onMounted, onUnmounted, shallowRef, type ShallowRef, watch } from 'vue';\nimport { getRefreshTokenHandler } from '../utils/EnhancedRefreshTokenHandler';\nimport { logIfEnabled } from '../utils/logger';\nimport type { QueryFunction, UseQueryOptions } from './types';\nimport { queryCache } from './useQueryCache';\nimport { executeWithAuth } from './useRpcAuth';\n\nexport interface UseQueryReturn<T> {\n data: ShallowRef<T | null>;\n loading: ShallowRef<boolean>;\n error: ShallowRef<Error | null>;\n dataUpdatedAt: ShallowRef<number>;\n execute: () => Promise<void>;\n refetch: () => Promise<void>;\n}\n\nexport function useQuery<TApi extends Record<string, any> = DragoncoreApi, T = any>(\n queryFn: QueryFunction<TApi, T>,\n options?: UseQueryOptions,\n): UseQueryReturn<T> {\n // Get the global refresh token handler - Dragoncore handles this automatically\n const refreshTokenHandler = getRefreshTokenHandler();\n\n const data = shallowRef<T | null>(null);\n const loading = shallowRef(options?.immediate !== false);\n const error = shallowRef<Error | null>(null);\n const dataUpdatedAt = shallowRef<number>(0);\n // Internal flag to prevent duplicate background fetches (not exposed to consumers)\n let isBackgroundFetching = false;\n // Default debug to false if not specified\n const debug = options?.debug ?? false;\n // Default persist to true when cacheKey is provided (localStorage persistence is default)\n // Set persist: false to disable localStorage persistence (in-memory caching still works)\n const persist = options?.persist ?? (options?.cacheKey ? true : false);\n\n const execute = async (skipCache = false) => {\n const isEnabled =\n typeof options?.enabled === 'boolean'\n ? options?.enabled\n : (options?.enabled?.value ?? true);\n\n if (!isEnabled) {\n loading.value = false;\n return;\n }\n\n loading.value = true;\n error.value = null;\n\n // Check cache first (unless explicitly skipping)\n if (options?.cacheKey && !skipCache) {\n const cached = queryCache.get<T>(options.cacheKey, {\n staleTime: options?.staleTime,\n persist: persist,\n debug: debug,\n });\n\n if (cached !== null) {\n logIfEnabled('debug', `[Query] Cache HIT: ${options.cacheKey}`, debug);\n data.value = cached;\n loading.value = false;\n\n // Optionally fetch fresh data in background if refetchOnWindowFocus\n if (options?.refetchOnWindowFocus) {\n executeInBackground();\n }\n return;\n }\n logIfEnabled('debug', `[Query] Cache MISS: ${options.cacheKey}`, debug);\n }\n\n try {\n const result = await executeWithAuth<TApi, T>((batch) => queryFn(batch), {\n credentials: options?.credentials,\n skipAuthCheck: false, // Queries always need auth\n mode: options?.batchMode,\n trackedSegment: options?.trackedSegment, // Pass through the tracked segment\n refreshTokenHandler,\n });\n data.value = result;\n dataUpdatedAt.value = Date.now();\n\n // Cache the result\n if (options?.cacheKey) {\n queryCache.set(options.cacheKey, result, {\n staleTime: options?.staleTime,\n persist: persist,\n debug: options?.debug,\n });\n }\n } catch (e) {\n error.value = e instanceof Error ? e : new Error('Unknown error');\n data.value = null;\n } finally {\n loading.value = false;\n }\n };\n\n const executeInBackground = async () => {\n if (isBackgroundFetching || loading.value) return; // Don't fetch if already fetching\n\n isBackgroundFetching = true;\n try {\n const result = await executeWithAuth<TApi, T>((batch) => queryFn(batch), {\n credentials: options?.credentials,\n skipAuthCheck: false, // Queries always need auth\n mode: options?.batchMode ? 'batch' : 'tracked',\n trackedSegment: options?.trackedSegment, // Pass through the tracked segment\n refreshTokenHandler,\n });\n data.value = result;\n dataUpdatedAt.value = Date.now();\n\n if (options?.cacheKey) {\n queryCache.set(options.cacheKey, result, {\n staleTime: options?.staleTime,\n persist: persist,\n debug: debug,\n });\n }\n } catch (e) {\n // Silent failure for background refetch\n logIfEnabled('error', '[Query] Background refetch failed:', debug, e);\n } finally {\n isBackgroundFetching = false;\n }\n };\n\n // Auto-execute on mount\n if (options?.immediate !== false) {\n onMounted(execute);\n }\n\n // Refetch on window focus\n if (options?.refetchOnWindowFocus) {\n const handleFocus = () => {\n if (data.value !== null) {\n executeInBackground();\n }\n };\n onMounted(() => window.addEventListener('focus', handleFocus));\n onUnmounted(() => window.removeEventListener('focus', handleFocus));\n }\n\n // Watch for changes\n if (options?.watch) {\n const watchTargets = Array.isArray(options.watch) ? options.watch : [options.watch];\n watch(watchTargets, () => execute());\n }\n\n // Watch enabled state\n if (options?.enabled && typeof options?.enabled !== 'boolean') {\n watch(options?.enabled, (isEnabled) => {\n if (isEnabled) execute();\n });\n }\n\n return {\n data,\n loading,\n error,\n dataUpdatedAt,\n execute: () => execute(),\n refetch: () => execute(true), // Skip cache on manual refetch\n };\n}\n"],"mappings":";;;;;AAiBA,SAAgB,SACd,SACA,SACmB;CAEnB,MAAM,sBAAsB,wBAAwB;CAEpD,MAAM,OAAO,WAAqB,KAAK;CACvC,MAAM,UAAU,WAAW,SAAS,cAAc,MAAM;CACxD,MAAM,QAAQ,WAAyB,KAAK;CAC5C,MAAM,gBAAgB,WAAmB,EAAE;CAE3C,IAAI,uBAAuB;CAE3B,MAAM,QAAQ,SAAS,SAAS;CAGhC,MAAM,UAAU,SAAS,YAAY,SAAS,WAAW,OAAO;CAEhE,MAAM,UAAU,OAAO,YAAY,UAAU;AAM3C,MAAI,EAJF,OAAO,SAAS,YAAY,YACxB,SAAS,UACR,SAAS,SAAS,SAAS,OAElB;AACd,WAAQ,QAAQ;AAChB;;AAGF,UAAQ,QAAQ;AAChB,QAAM,QAAQ;AAGd,MAAI,SAAS,YAAY,CAAC,WAAW;GACnC,MAAM,SAAS,WAAW,IAAO,QAAQ,UAAU;IACjD,WAAW,SAAS;IACX;IACF;IACR,CAAC;AAEF,OAAI,WAAW,MAAM;AACnB,iBAAa,SAAS,sBAAsB,QAAQ,YAAY,MAAM;AACtE,SAAK,QAAQ;AACb,YAAQ,QAAQ;AAGhB,QAAI,SAAS,qBACX,sBAAqB;AAEvB;;AAEF,gBAAa,SAAS,uBAAuB,QAAQ,YAAY,MAAM;;AAGzE,MAAI;GACF,MAAM,SAAS,MAAM,iBAA0B,UAAU,QAAQ,MAAM,EAAE;IACvE,aAAa,SAAS;IACtB,eAAe;IACf,MAAM,SAAS;IACf,gBAAgB,SAAS;IACzB;IACD,CAAC;AACF,QAAK,QAAQ;AACb,iBAAc,QAAQ,KAAK,KAAK;AAGhC,OAAI,SAAS,SACX,YAAW,IAAI,QAAQ,UAAU,QAAQ;IACvC,WAAW,SAAS;IACX;IACT,OAAO,SAAS;IACjB,CAAC;WAEG,GAAG;AACV,SAAM,QAAQ,aAAa,QAAQ,oBAAI,IAAI,MAAM,gBAAgB;AACjE,QAAK,QAAQ;YACL;AACR,WAAQ,QAAQ;;;CAIpB,MAAM,sBAAsB,YAAY;AACtC,MAAI,wBAAwB,QAAQ,MAAO;AAE3C,yBAAuB;AACvB,MAAI;GACF,MAAM,SAAS,MAAM,iBAA0B,UAAU,QAAQ,MAAM,EAAE;IACvE,aAAa,SAAS;IACtB,eAAe;IACf,MAAM,SAAS,YAAY,UAAU;IACrC,gBAAgB,SAAS;IACzB;IACD,CAAC;AACF,QAAK,QAAQ;AACb,iBAAc,QAAQ,KAAK,KAAK;AAEhC,OAAI,SAAS,SACX,YAAW,IAAI,QAAQ,UAAU,QAAQ;IACvC,WAAW,SAAS;IACX;IACF;IACR,CAAC;WAEG,GAAG;AAEV,gBAAa,SAAS,sCAAsC,OAAO,EAAE;YAC7D;AACR,0BAAuB;;;AAK3B,KAAI,SAAS,cAAc,MACzB,WAAU,QAAQ;AAIpB,KAAI,SAAS,sBAAsB;EACjC,MAAM,oBAAoB;AACxB,OAAI,KAAK,UAAU,KACjB,sBAAqB;;AAGzB,kBAAgB,OAAO,iBAAiB,SAAS,YAAY,CAAC;AAC9D,oBAAkB,OAAO,oBAAoB,SAAS,YAAY,CAAC;;AAIrE,KAAI,SAAS,MAEX,OADqB,MAAM,QAAQ,QAAQ,MAAM,GAAG,QAAQ,QAAQ,CAAC,QAAQ,MAAM,QACzD,SAAS,CAAC;AAItC,KAAI,SAAS,WAAW,OAAO,SAAS,YAAY,UAClD,OAAM,SAAS,UAAU,cAAc;AACrC,MAAI,UAAW,UAAS;GACxB;AAGJ,QAAO;EACL;EACA;EACA;EACA;EACA,eAAe,SAAS;EACxB,eAAe,QAAQ,KAAK;EAC7B"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { f as logIfEnabled, p as logger } from "./useRpcAuth-BLlRSHy8.js";
|
|
2
|
+
|
|
3
|
+
//#region src/composables/useQueryCache.ts
|
|
4
|
+
var QueryCache = class {
|
|
5
|
+
memoryCache = /* @__PURE__ */ new Map();
|
|
6
|
+
BUILD_TAG = import.meta.env.VITE_BUILD_TAG || "dev";
|
|
7
|
+
STORAGE_PREFIX = `rpc-cache:${this.BUILD_TAG}:`;
|
|
8
|
+
BUILD_TAG_KEY = "rpc-cache-build-tag";
|
|
9
|
+
cleanupTimers = /* @__PURE__ */ new Map();
|
|
10
|
+
constructor() {
|
|
11
|
+
this.handleDeploymentChange();
|
|
12
|
+
this.cleanExpiredStorage();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detect deployment changes and clear old cache
|
|
16
|
+
*/
|
|
17
|
+
handleDeploymentChange() {
|
|
18
|
+
const lastBuildTag = localStorage.getItem(this.BUILD_TAG_KEY);
|
|
19
|
+
if (!lastBuildTag) {
|
|
20
|
+
logger.debug("[Cache] Build tag missing - clearing all cache");
|
|
21
|
+
this.clearAllStorage();
|
|
22
|
+
} else if (lastBuildTag !== this.BUILD_TAG) {
|
|
23
|
+
logger.debug(`[Cache] New deployment detected: ${lastBuildTag} → ${this.BUILD_TAG}`);
|
|
24
|
+
logger.debug("[Cache] Clearing old cache from previous deployment");
|
|
25
|
+
this.clearAllOldDeploymentCache(lastBuildTag);
|
|
26
|
+
}
|
|
27
|
+
localStorage.setItem(this.BUILD_TAG_KEY, this.BUILD_TAG);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Clear cache from previous deployment
|
|
31
|
+
*/
|
|
32
|
+
clearAllOldDeploymentCache(oldBuildTag) {
|
|
33
|
+
const oldPrefix = `rpc-cache:${oldBuildTag}:`;
|
|
34
|
+
const keysToRemove = [];
|
|
35
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
36
|
+
const key = localStorage.key(i);
|
|
37
|
+
if (key?.startsWith(oldPrefix)) keysToRemove.push(key);
|
|
38
|
+
}
|
|
39
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
40
|
+
if (keysToRemove.length > 0) logger.debug(`[Cache] Removed ${keysToRemove.length} entries from previous deployment`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get data from cache (memory first, then localStorage if persist enabled)
|
|
44
|
+
*/
|
|
45
|
+
get(key, options = {}) {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const debug = options.debug ?? false;
|
|
48
|
+
const memoryEntry = this.memoryCache.get(key);
|
|
49
|
+
if (memoryEntry) {
|
|
50
|
+
if (now < memoryEntry.expiresAt) return memoryEntry.data;
|
|
51
|
+
this.memoryCache.delete(key);
|
|
52
|
+
}
|
|
53
|
+
if (options.persist) {
|
|
54
|
+
const storageEntry = this.getFromStorage(key, debug);
|
|
55
|
+
if (storageEntry) {
|
|
56
|
+
if (now < storageEntry.expiresAt) {
|
|
57
|
+
this.memoryCache.set(key, storageEntry);
|
|
58
|
+
return storageEntry.data;
|
|
59
|
+
}
|
|
60
|
+
this.removeFromStorage(key);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Set data in cache (memory and optionally localStorage)
|
|
67
|
+
*/
|
|
68
|
+
set(key, data, options = {}) {
|
|
69
|
+
const staleTime = options.staleTime ?? 3e5;
|
|
70
|
+
const debug = options.debug ?? false;
|
|
71
|
+
const entry = {
|
|
72
|
+
data,
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
expiresAt: Date.now() + staleTime
|
|
75
|
+
};
|
|
76
|
+
this.memoryCache.set(key, entry);
|
|
77
|
+
if (options.persist) this.setInStorage(key, entry, debug);
|
|
78
|
+
this.scheduleCleanup(key, staleTime);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Invalidate cache entries matching a pattern
|
|
82
|
+
*/
|
|
83
|
+
invalidate(pattern, debug = false) {
|
|
84
|
+
const keysToRemove = [];
|
|
85
|
+
for (const key of this.memoryCache.keys()) if (this.matchesPattern(key, pattern)) keysToRemove.push(key);
|
|
86
|
+
keysToRemove.forEach((key) => {
|
|
87
|
+
this.memoryCache.delete(key);
|
|
88
|
+
this.removeFromStorage(key);
|
|
89
|
+
this.cancelCleanup(key);
|
|
90
|
+
});
|
|
91
|
+
logIfEnabled("debug", `[Cache] Invalidated ${keysToRemove.length} entries matching:`, debug, pattern);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Clear all cache entries
|
|
95
|
+
*/
|
|
96
|
+
clear(debug = false) {
|
|
97
|
+
this.memoryCache.clear();
|
|
98
|
+
for (const timer of this.cleanupTimers.values()) clearTimeout(timer);
|
|
99
|
+
this.cleanupTimers.clear();
|
|
100
|
+
this.clearAllStorage();
|
|
101
|
+
logIfEnabled("debug", "[Cache] Cleared all entries", debug);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get cache statistics (for debugging)
|
|
105
|
+
*/
|
|
106
|
+
inspect() {
|
|
107
|
+
const entries = {};
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
for (const [key, value] of this.memoryCache.entries()) entries[key] = {
|
|
110
|
+
data: value.data,
|
|
111
|
+
ageMs: now - value.timestamp,
|
|
112
|
+
expiresInMs: value.expiresAt - now,
|
|
113
|
+
isExpired: now > value.expiresAt,
|
|
114
|
+
source: "memory"
|
|
115
|
+
};
|
|
116
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
117
|
+
const storageKey = localStorage.key(i);
|
|
118
|
+
if (storageKey?.startsWith(this.STORAGE_PREFIX)) {
|
|
119
|
+
const key = storageKey.substring(this.STORAGE_PREFIX.length);
|
|
120
|
+
if (!entries[key]) try {
|
|
121
|
+
const entry = JSON.parse(localStorage.getItem(storageKey));
|
|
122
|
+
entries[key] = {
|
|
123
|
+
data: entry.data,
|
|
124
|
+
ageMs: now - entry.timestamp,
|
|
125
|
+
expiresInMs: entry.expiresAt - now,
|
|
126
|
+
isExpired: now > entry.expiresAt,
|
|
127
|
+
source: "storage"
|
|
128
|
+
};
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
totalEntries: Object.keys(entries).length,
|
|
134
|
+
memoryEntries: Array.from(this.memoryCache.keys()).length,
|
|
135
|
+
storageEntries: this.countStorageEntries(),
|
|
136
|
+
entries
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get size of cache in localStorage (approximate)
|
|
141
|
+
*/
|
|
142
|
+
getStorageSize() {
|
|
143
|
+
let size = 0;
|
|
144
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
145
|
+
const key = localStorage.key(i);
|
|
146
|
+
if (key?.startsWith(this.STORAGE_PREFIX)) {
|
|
147
|
+
const value = localStorage.getItem(key);
|
|
148
|
+
if (value) size += key.length + value.length;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return size;
|
|
152
|
+
}
|
|
153
|
+
getFromStorage(key, debug = false) {
|
|
154
|
+
try {
|
|
155
|
+
const stored = localStorage.getItem(this.STORAGE_PREFIX + key);
|
|
156
|
+
if (!stored) return null;
|
|
157
|
+
return JSON.parse(stored);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
logIfEnabled("error", "[Cache] Error reading from storage:", debug, error);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
setInStorage(key, entry, debug = false) {
|
|
164
|
+
try {
|
|
165
|
+
localStorage.setItem(this.STORAGE_PREFIX + key, JSON.stringify(entry));
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof DOMException && error.name === "QuotaExceededError") {
|
|
168
|
+
logIfEnabled("warn", "[Cache] Storage quota exceeded, clearing old entries", debug);
|
|
169
|
+
this.clearOldestStorageEntries(debug);
|
|
170
|
+
try {
|
|
171
|
+
localStorage.setItem(this.STORAGE_PREFIX + key, JSON.stringify(entry));
|
|
172
|
+
} catch (retryError) {
|
|
173
|
+
logIfEnabled("error", "[Cache] Still cannot write to storage after cleanup", debug);
|
|
174
|
+
}
|
|
175
|
+
} else logIfEnabled("error", "[Cache] Error writing to storage:", debug, error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
removeFromStorage(key) {
|
|
179
|
+
localStorage.removeItem(this.STORAGE_PREFIX + key);
|
|
180
|
+
}
|
|
181
|
+
clearAllStorage() {
|
|
182
|
+
const keysToRemove = [];
|
|
183
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
184
|
+
const key = localStorage.key(i);
|
|
185
|
+
if (key?.startsWith(this.STORAGE_PREFIX)) keysToRemove.push(key);
|
|
186
|
+
}
|
|
187
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
188
|
+
}
|
|
189
|
+
cleanExpiredStorage() {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const keysToRemove = [];
|
|
192
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
193
|
+
const storageKey = localStorage.key(i);
|
|
194
|
+
if (storageKey?.startsWith(this.STORAGE_PREFIX)) try {
|
|
195
|
+
if (now > JSON.parse(localStorage.getItem(storageKey)).expiresAt) keysToRemove.push(storageKey);
|
|
196
|
+
} catch {
|
|
197
|
+
keysToRemove.push(storageKey);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
201
|
+
if (keysToRemove.length > 0) logger.debug(`[Cache] Cleaned up ${keysToRemove.length} expired storage entries`);
|
|
202
|
+
}
|
|
203
|
+
clearOldestStorageEntries(debug = false) {
|
|
204
|
+
const entries = [];
|
|
205
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
206
|
+
const key = localStorage.key(i);
|
|
207
|
+
if (key?.startsWith(this.STORAGE_PREFIX)) try {
|
|
208
|
+
const entry = JSON.parse(localStorage.getItem(key));
|
|
209
|
+
entries.push([key, entry.timestamp]);
|
|
210
|
+
} catch {
|
|
211
|
+
localStorage.removeItem(key);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
entries.sort((a, b) => a[1] - b[1]);
|
|
215
|
+
const toRemove = Math.ceil(entries.length * .25);
|
|
216
|
+
entries.slice(0, toRemove).forEach(([key]) => {
|
|
217
|
+
localStorage.removeItem(key);
|
|
218
|
+
});
|
|
219
|
+
logIfEnabled("debug", `[Cache] Removed ${toRemove} oldest entries to free space`, debug);
|
|
220
|
+
}
|
|
221
|
+
scheduleCleanup(key, delay) {
|
|
222
|
+
this.cancelCleanup(key);
|
|
223
|
+
const timer = setTimeout(() => {
|
|
224
|
+
this.memoryCache.delete(key);
|
|
225
|
+
this.cleanupTimers.delete(key);
|
|
226
|
+
}, delay);
|
|
227
|
+
this.cleanupTimers.set(key, timer);
|
|
228
|
+
}
|
|
229
|
+
cancelCleanup(key) {
|
|
230
|
+
const timer = this.cleanupTimers.get(key);
|
|
231
|
+
if (timer) {
|
|
232
|
+
clearTimeout(timer);
|
|
233
|
+
this.cleanupTimers.delete(key);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
matchesPattern(key, pattern) {
|
|
237
|
+
if (typeof pattern === "string") return key === pattern || key.startsWith(pattern);
|
|
238
|
+
return pattern.test(key);
|
|
239
|
+
}
|
|
240
|
+
countStorageEntries() {
|
|
241
|
+
let count = 0;
|
|
242
|
+
for (let i = 0; i < localStorage.length; i++) if (localStorage.key(i)?.startsWith(this.STORAGE_PREFIX)) count++;
|
|
243
|
+
return count;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const queryCache = new QueryCache();
|
|
247
|
+
if (import.meta.env.DEV) {
|
|
248
|
+
window.__queryCache = queryCache;
|
|
249
|
+
logger.debug("[Cache] Debug available: window.__queryCache");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
//#endregion
|
|
253
|
+
export { queryCache as t };
|
|
254
|
+
//# sourceMappingURL=useQueryCache-ByayvZgZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQueryCache-ByayvZgZ.js","names":["keysToRemove: string[]","entry: CacheEntry<T>","entries: Record<\n string,\n {\n data: any;\n ageMs: number;\n expiresInMs: number;\n isExpired: boolean;\n source: 'memory' | 'storage';\n }\n >","entries: [string, number][]"],"sources":["../src/composables/useQueryCache.ts"],"sourcesContent":["import { logger, logIfEnabled } from '../utils/logger';\n\ninterface CacheEntry<T> {\n data: T;\n timestamp: number;\n expiresAt: number;\n}\n\ninterface CacheOptions {\n staleTime?: number; // How long data is fresh (ms), default 5 minutes\n persist?: boolean; // Save to localStorage\n debug?: boolean; // Enable/disable debug logging for this cache operation (default: false)\n}\n\nclass QueryCache {\n private memoryCache = new Map<string, CacheEntry<any>>();\n private readonly BUILD_TAG = import.meta.env.VITE_BUILD_TAG || 'dev';\n private readonly STORAGE_PREFIX = `rpc-cache:${this.BUILD_TAG}:`;\n private readonly BUILD_TAG_KEY = 'rpc-cache-build-tag';\n private cleanupTimers = new Map<string, number>();\n\n constructor() {\n // Check if this is a new deployment and clear old cache\n this.handleDeploymentChange();\n\n // Clean up expired localStorage entries on init\n this.cleanExpiredStorage();\n }\n\n /**\n * Detect deployment changes and clear old cache\n */\n private handleDeploymentChange() {\n const lastBuildTag = localStorage.getItem(this.BUILD_TAG_KEY);\n\n // If build tag is missing, clear all cache (first load or manual clear)\n if (!lastBuildTag) {\n // Use global log level for deployment change detection\n logger.debug('[Cache] Build tag missing - clearing all cache');\n this.clearAllStorage();\n }\n // If deployment changed, clear old cache from previous build\n else if (lastBuildTag !== this.BUILD_TAG) {\n // Use global log level for deployment change detection\n logger.debug(`[Cache] New deployment detected: ${lastBuildTag} → ${this.BUILD_TAG}`);\n logger.debug('[Cache] Clearing old cache from previous deployment');\n\n // Clear all old cache entries (from previous build tag)\n this.clearAllOldDeploymentCache(lastBuildTag);\n }\n\n // Store current build tag\n localStorage.setItem(this.BUILD_TAG_KEY, this.BUILD_TAG);\n }\n\n /**\n * Clear cache from previous deployment\n */\n private clearAllOldDeploymentCache(oldBuildTag: string) {\n const oldPrefix = `rpc-cache:${oldBuildTag}:`;\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(oldPrefix)) {\n keysToRemove.push(key);\n }\n }\n\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n\n if (keysToRemove.length > 0) {\n // Use global log level for deployment change detection\n logger.debug(`[Cache] Removed ${keysToRemove.length} entries from previous deployment`);\n }\n }\n\n /**\n * Get data from cache (memory first, then localStorage if persist enabled)\n */\n get<T>(key: string, options: CacheOptions = {}): T | null {\n const now = Date.now();\n const debug = options.debug ?? false;\n\n // 1. Try memory cache first\n const memoryEntry = this.memoryCache.get(key);\n if (memoryEntry) {\n if (now < memoryEntry.expiresAt) {\n return memoryEntry.data as T;\n }\n // Expired in memory\n this.memoryCache.delete(key);\n }\n\n // 2. Try localStorage if persist is enabled\n if (options.persist) {\n const storageEntry = this.getFromStorage<T>(key, debug);\n if (storageEntry) {\n if (now < storageEntry.expiresAt) {\n // Rehydrate to memory cache\n this.memoryCache.set(key, storageEntry);\n return storageEntry.data;\n }\n // Expired in storage\n this.removeFromStorage(key);\n }\n }\n\n return null;\n }\n\n /**\n * Set data in cache (memory and optionally localStorage)\n */\n set<T>(key: string, data: T, options: CacheOptions = {}) {\n const staleTime = options.staleTime ?? 300000; // Default 5 minutes\n const debug = options.debug ?? false;\n const entry: CacheEntry<T> = {\n data,\n timestamp: Date.now(),\n expiresAt: Date.now() + staleTime,\n };\n\n // 1. Set in memory\n this.memoryCache.set(key, entry);\n\n // 2. Persist to localStorage if enabled\n if (options.persist) {\n this.setInStorage(key, entry, debug);\n }\n\n // 3. Schedule automatic cleanup\n this.scheduleCleanup(key, staleTime);\n }\n\n /**\n * Invalidate cache entries matching a pattern\n */\n invalidate(pattern: string | RegExp, debug: boolean = false) {\n const keysToRemove: string[] = [];\n\n // Find matching keys in memory\n for (const key of this.memoryCache.keys()) {\n if (this.matchesPattern(key, pattern)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove from memory and storage\n keysToRemove.forEach((key) => {\n this.memoryCache.delete(key);\n this.removeFromStorage(key);\n this.cancelCleanup(key);\n });\n\n logIfEnabled(\n 'debug',\n `[Cache] Invalidated ${keysToRemove.length} entries matching:`,\n debug,\n pattern,\n );\n }\n\n /**\n * Clear all cache entries\n */\n clear(debug: boolean = false) {\n // Clear memory\n this.memoryCache.clear();\n\n // Clear all cleanup timers\n for (const timer of this.cleanupTimers.values()) {\n clearTimeout(timer);\n }\n this.cleanupTimers.clear();\n\n // Clear localStorage\n this.clearAllStorage();\n\n logIfEnabled('debug', '[Cache] Cleared all entries', debug);\n }\n\n /**\n * Get cache statistics (for debugging)\n */\n inspect() {\n const entries: Record<\n string,\n {\n data: any;\n ageMs: number;\n expiresInMs: number;\n isExpired: boolean;\n source: 'memory' | 'storage';\n }\n > = {};\n\n const now = Date.now();\n\n // Memory entries\n for (const [key, value] of this.memoryCache.entries()) {\n entries[key] = {\n data: value.data,\n ageMs: now - value.timestamp,\n expiresInMs: value.expiresAt - now,\n isExpired: now > value.expiresAt,\n source: 'memory',\n };\n }\n\n // Storage entries not in memory\n for (let i = 0; i < localStorage.length; i++) {\n const storageKey = localStorage.key(i);\n if (storageKey?.startsWith(this.STORAGE_PREFIX)) {\n const key = storageKey.substring(this.STORAGE_PREFIX.length);\n if (!entries[key]) {\n try {\n const entry = JSON.parse(localStorage.getItem(storageKey)!) as CacheEntry<any>;\n entries[key] = {\n data: entry.data,\n ageMs: now - entry.timestamp,\n expiresInMs: entry.expiresAt - now,\n isExpired: now > entry.expiresAt,\n source: 'storage',\n };\n } catch {\n // Ignore parse errors\n }\n }\n }\n }\n\n return {\n totalEntries: Object.keys(entries).length,\n memoryEntries: Array.from(this.memoryCache.keys()).length,\n storageEntries: this.countStorageEntries(),\n entries,\n };\n }\n\n /**\n * Get size of cache in localStorage (approximate)\n */\n getStorageSize(): number {\n let size = 0;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n const value = localStorage.getItem(key);\n if (value) {\n size += key.length + value.length;\n }\n }\n }\n return size;\n }\n\n // Private methods\n\n private getFromStorage<T>(key: string, debug: boolean = false): CacheEntry<T> | null {\n try {\n const stored = localStorage.getItem(this.STORAGE_PREFIX + key);\n if (!stored) return null;\n\n const entry = JSON.parse(stored) as CacheEntry<T>;\n return entry;\n } catch (error) {\n logIfEnabled('error', '[Cache] Error reading from storage:', debug, error);\n return null;\n }\n }\n\n private setInStorage<T>(key: string, entry: CacheEntry<T>, debug: boolean = false) {\n try {\n localStorage.setItem(this.STORAGE_PREFIX + key, JSON.stringify(entry));\n } catch (error) {\n // Handle quota exceeded\n if (error instanceof DOMException && error.name === 'QuotaExceededError') {\n logIfEnabled('warn', '[Cache] Storage quota exceeded, clearing old entries', debug);\n this.clearOldestStorageEntries(debug);\n // Try again\n try {\n localStorage.setItem(this.STORAGE_PREFIX + key, JSON.stringify(entry));\n } catch (retryError) {\n logIfEnabled('error', '[Cache] Still cannot write to storage after cleanup', debug);\n }\n } else {\n logIfEnabled('error', '[Cache] Error writing to storage:', debug, error);\n }\n }\n }\n\n private removeFromStorage(key: string) {\n localStorage.removeItem(this.STORAGE_PREFIX + key);\n }\n\n private clearAllStorage() {\n const keysToRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n }\n\n private cleanExpiredStorage() {\n const now = Date.now();\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const storageKey = localStorage.key(i);\n if (storageKey?.startsWith(this.STORAGE_PREFIX)) {\n try {\n const entry = JSON.parse(localStorage.getItem(storageKey)!) as CacheEntry<any>;\n if (now > entry.expiresAt) {\n keysToRemove.push(storageKey);\n }\n } catch {\n // Remove corrupted entries\n keysToRemove.push(storageKey);\n }\n }\n }\n\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n if (keysToRemove.length > 0) {\n // Use global log level for internal cleanup operations\n logger.debug(`[Cache] Cleaned up ${keysToRemove.length} expired storage entries`);\n }\n }\n\n private clearOldestStorageEntries(debug: boolean = false) {\n const entries: [string, number][] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n try {\n const entry = JSON.parse(localStorage.getItem(key)!) as CacheEntry<any>;\n entries.push([key, entry.timestamp]);\n } catch {\n // Remove corrupted entries\n localStorage.removeItem(key);\n }\n }\n }\n\n // Sort by timestamp (oldest first) and remove oldest 25%\n entries.sort((a, b) => a[1] - b[1]);\n const toRemove = Math.ceil(entries.length * 0.25);\n entries.slice(0, toRemove).forEach(([key]) => {\n localStorage.removeItem(key);\n });\n\n logIfEnabled('debug', `[Cache] Removed ${toRemove} oldest entries to free space`, debug);\n }\n\n private scheduleCleanup(key: string, delay: number) {\n // Cancel existing cleanup if any\n this.cancelCleanup(key);\n\n // Schedule new cleanup\n const timer = setTimeout(() => {\n this.memoryCache.delete(key);\n this.cleanupTimers.delete(key);\n }, delay);\n\n this.cleanupTimers.set(key, timer);\n }\n\n private cancelCleanup(key: string) {\n const timer = this.cleanupTimers.get(key);\n if (timer) {\n clearTimeout(timer);\n this.cleanupTimers.delete(key);\n }\n }\n\n private matchesPattern(key: string, pattern: string | RegExp): boolean {\n if (typeof pattern === 'string') {\n // Exact match or prefix match\n return key === pattern || key.startsWith(pattern);\n }\n return pattern.test(key);\n }\n\n private countStorageEntries(): number {\n let count = 0;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n count++;\n }\n }\n return count;\n }\n}\n\n// Singleton instance\nexport const queryCache = new QueryCache();\n\n// Expose for debugging in dev mode\nif (import.meta.env.DEV) {\n (window as any).__queryCache = queryCache;\n // Always log this in dev mode\n logger.debug('[Cache] Debug available: window.__queryCache');\n}\n"],"mappings":"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"}
|