@wishbone-media/spark 0.13.1 → 0.14.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/dist/index.js +795 -753
- package/package.json +1 -1
- package/src/containers/SparkDefaultContainer.vue +6 -1
- package/src/plugins/app-bootstrap.js +91 -0
- package/src/plugins/axios.js +42 -1
- package/src/plugins/index.js +3 -2
- package/src/plugins/router.js +34 -0
- package/src/stores/auth.js +4 -0
package/package.json
CHANGED
|
@@ -220,7 +220,11 @@ const toggleAppSelector = () => {
|
|
|
220
220
|
slotProps.footerSlot = props.appSelectorSlots.footerSlot
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
sparkOverlayService.showRight(SparkAppSelector, slotProps
|
|
223
|
+
sparkOverlayService.showRight(SparkAppSelector, slotProps, {
|
|
224
|
+
select: (brand) => {
|
|
225
|
+
sparkOverlayService.closeRight()
|
|
226
|
+
},
|
|
227
|
+
})
|
|
224
228
|
}
|
|
225
229
|
|
|
226
230
|
const toggleBrandSelector = () => {
|
|
@@ -230,6 +234,7 @@ const toggleBrandSelector = () => {
|
|
|
230
234
|
{
|
|
231
235
|
select: (brand) => {
|
|
232
236
|
sparkBrandFilterStore.toggleBrand(brand)
|
|
237
|
+
sparkOverlayService.closeLeft()
|
|
233
238
|
},
|
|
234
239
|
},
|
|
235
240
|
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Global registry for bootstrap reset callbacks
|
|
2
|
+
const resetCallbacks = []
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register a reset callback to be called on logout
|
|
6
|
+
* This is used internally by createBootstrapService
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
function registerResetCallback(callback) {
|
|
10
|
+
resetCallbacks.push(callback)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Reset all registered bootstrap services
|
|
15
|
+
* Called automatically by the auth store on logout
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export function resetAllBootstrapServices() {
|
|
19
|
+
resetCallbacks.forEach(callback => callback())
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a bootstrap service for initializing app-level data after authentication
|
|
24
|
+
*
|
|
25
|
+
* This factory function provides a standardized pattern for loading user-dependent data
|
|
26
|
+
* in authenticated SPAs. It ensures initialization runs only once per session and provides
|
|
27
|
+
* automatic cleanup on logout (no manual reset needed).
|
|
28
|
+
*
|
|
29
|
+
* @param {Function} initFn - Async function that performs the initialization logic
|
|
30
|
+
* (e.g., loading stores, fetching user data)
|
|
31
|
+
* @returns {Object} Object containing bootstrapApp function
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // In consumer app: src/bootstrap.js
|
|
35
|
+
* import { createBootstrapService } from '@wishbone-media/spark'
|
|
36
|
+
* import { useGlobalStore } from '@/stores/global.js'
|
|
37
|
+
* import { useBrandFilterStore } from '@/stores/brand-filter.js'
|
|
38
|
+
*
|
|
39
|
+
* export const { bootstrapApp } = createBootstrapService(async () => {
|
|
40
|
+
* const globalStore = useGlobalStore()
|
|
41
|
+
* const brandStore = useBrandFilterStore()
|
|
42
|
+
*
|
|
43
|
+
* // Load stores in parallel for better performance
|
|
44
|
+
* await Promise.all([
|
|
45
|
+
* globalStore.loadGlobalData(),
|
|
46
|
+
* brandStore.loadBrands(),
|
|
47
|
+
* ])
|
|
48
|
+
* })
|
|
49
|
+
*/
|
|
50
|
+
export function createBootstrapService(initFn) {
|
|
51
|
+
let initialized = false
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reset the bootstrap state
|
|
55
|
+
* Called automatically by Spark's auth store on logout
|
|
56
|
+
* @private
|
|
57
|
+
*/
|
|
58
|
+
const resetBootstrap = () => {
|
|
59
|
+
initialized = false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Register this service's reset callback
|
|
63
|
+
registerResetCallback(resetBootstrap)
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Initialize application-level data after authentication
|
|
67
|
+
* Called automatically by the router guard when navigating to authenticated routes
|
|
68
|
+
*
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
71
|
+
const bootstrapApp = async () => {
|
|
72
|
+
// Only run once per session
|
|
73
|
+
if (initialized) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
// Execute the consumer-provided initialization logic
|
|
79
|
+
await initFn()
|
|
80
|
+
initialized = true
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Error during app bootstrap:', error)
|
|
83
|
+
// Don't set initialized = true on error, allow retry on next navigation
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
bootstrapApp,
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/plugins/axios.js
CHANGED
|
@@ -81,6 +81,9 @@ export function createAxiosInstance(config = {}) {
|
|
|
81
81
|
return instance
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// Module-level axios instance reference for consumer apps
|
|
85
|
+
let axiosInstance = null
|
|
86
|
+
|
|
84
87
|
/**
|
|
85
88
|
* Setup axios for a Vue app with global availability
|
|
86
89
|
* @param {App} app - Vue app instance
|
|
@@ -88,7 +91,7 @@ export function createAxiosInstance(config = {}) {
|
|
|
88
91
|
* @returns {AxiosInstance} Configured axios instance
|
|
89
92
|
*/
|
|
90
93
|
export function setupAxios(app, config = {}) {
|
|
91
|
-
|
|
94
|
+
axiosInstance = createAxiosInstance(config)
|
|
92
95
|
|
|
93
96
|
// Make axios available via injection (Composition API)
|
|
94
97
|
app.provide('axios', axiosInstance)
|
|
@@ -98,3 +101,41 @@ export function setupAxios(app, config = {}) {
|
|
|
98
101
|
|
|
99
102
|
return axiosInstance
|
|
100
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the configured axios instance
|
|
107
|
+
* This allows consumer apps to access the axios instance in non-component contexts
|
|
108
|
+
* (like Pinia stores) without needing to pass it as a parameter.
|
|
109
|
+
*
|
|
110
|
+
* @returns {AxiosInstance} The axios instance
|
|
111
|
+
* @throws {Error} If axios has not been initialized via setupAxios()
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // In consumer app: src/plugins/axios.js
|
|
115
|
+
* import { setupAxios, getAxiosInstance as getSparkAxiosInstance } from '@wishbone-media/spark'
|
|
116
|
+
*
|
|
117
|
+
* export function setupAppAxios(app) {
|
|
118
|
+
* return setupAxios(app, {
|
|
119
|
+
* baseURL: import.meta.env.VITE_APP_API_URL,
|
|
120
|
+
* })
|
|
121
|
+
* }
|
|
122
|
+
*
|
|
123
|
+
* // Re-export for convenience
|
|
124
|
+
* export const getAxiosInstance = getSparkAxiosInstance
|
|
125
|
+
*
|
|
126
|
+
* // In stores: src/stores/global.js
|
|
127
|
+
* import { getAxiosInstance } from '@/plugins/axios'
|
|
128
|
+
*
|
|
129
|
+
* export const useGlobalStore = defineStore('global', () => {
|
|
130
|
+
* const loadData = async () => {
|
|
131
|
+
* const axios = getAxiosInstance()
|
|
132
|
+
* const { data } = await axios.get('/data')
|
|
133
|
+
* }
|
|
134
|
+
* })
|
|
135
|
+
*/
|
|
136
|
+
export function getAxiosInstance() {
|
|
137
|
+
if (!axiosInstance) {
|
|
138
|
+
throw new Error('Axios instance not initialized. Call setupAxios() first.')
|
|
139
|
+
}
|
|
140
|
+
return axiosInstance
|
|
141
|
+
}
|
package/src/plugins/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { Icons, addIcons, setupFontAwesome } from './fontawesome.js'
|
|
2
|
-
export { createAuthRoutes, setupAuthGuards, create403Route, create404Route } from './router.js'
|
|
3
|
-
export { createAxiosInstance, setupAxios } from './axios.js'
|
|
2
|
+
export { createAuthRoutes, setupAuthGuards, create403Route, create404Route, setupBootstrapGuard } from './router.js'
|
|
3
|
+
export { createAxiosInstance, setupAxios, getAxiosInstance } from './axios.js'
|
|
4
|
+
export { createBootstrapService } from './app-bootstrap.js'
|
package/src/plugins/router.js
CHANGED
|
@@ -178,3 +178,37 @@ export function create404Route(options = {}) {
|
|
|
178
178
|
meta: { auth: false },
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Setup bootstrap guard to initialize app data after authentication
|
|
184
|
+
* This guard waits for authentication to be ready, then calls the provided bootstrap function
|
|
185
|
+
* to load user-dependent data before rendering authenticated routes.
|
|
186
|
+
*
|
|
187
|
+
* IMPORTANT: This MUST be called AFTER setupAuthGuards() to ensure auth is ready first
|
|
188
|
+
*
|
|
189
|
+
* @param {Router} router - Vue router instance
|
|
190
|
+
* @param {Function} bootstrapFn - Async function that initializes app data
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* import { setupAuthGuards, setupBootstrapGuard } from '@wishbone-media/spark'
|
|
194
|
+
* import { bootstrapApp } from '@/services/app-bootstrap'
|
|
195
|
+
*
|
|
196
|
+
* // Setup auth guards first
|
|
197
|
+
* setupAuthGuards(router)
|
|
198
|
+
*
|
|
199
|
+
* // Then setup bootstrap guard
|
|
200
|
+
* setupBootstrapGuard(router, bootstrapApp)
|
|
201
|
+
*/
|
|
202
|
+
export function setupBootstrapGuard(router, bootstrapFn) {
|
|
203
|
+
router.beforeResolve(async (to) => {
|
|
204
|
+
// Only bootstrap for authenticated routes
|
|
205
|
+
if (to.meta.auth !== false) {
|
|
206
|
+
const authStore = useSparkAuthStore()
|
|
207
|
+
|
|
208
|
+
// Wait for auth to be ready and bootstrap if authenticated
|
|
209
|
+
if (authStore.state.ready && authStore.check) {
|
|
210
|
+
await bootstrapFn()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
}
|
package/src/stores/auth.js
CHANGED
|
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
|
|
|
2
2
|
import { reactive, computed } from 'vue'
|
|
3
3
|
import axios from 'axios'
|
|
4
4
|
import { getCookie, setCookie, deleteCookie } from '../utils/cookies.js'
|
|
5
|
+
import { resetAllBootstrapServices } from '../plugins/app-bootstrap.js'
|
|
5
6
|
|
|
6
7
|
const TOKEN_NAME = 'bolt-next-token'
|
|
7
8
|
|
|
@@ -117,6 +118,9 @@ export const useSparkAuthStore = defineStore('auth', () => {
|
|
|
117
118
|
} finally {
|
|
118
119
|
clearTokenCookie()
|
|
119
120
|
state.user = null
|
|
121
|
+
|
|
122
|
+
// Reset all bootstrap services to allow re-initialization on next login
|
|
123
|
+
resetAllBootstrapServices()
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
|