@kennofizet/apphub-frontend 0.1.6 → 0.1.7
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/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
loadBootstrapCache,
|
|
25
25
|
saveBootstrapCache,
|
|
26
26
|
} from './utils/bootstrapCache.js'
|
|
27
|
+
import {
|
|
28
|
+
apphubDebug,
|
|
29
|
+
describeHostApi,
|
|
30
|
+
describeStore,
|
|
31
|
+
} from './utils/apphubDebug.js'
|
|
27
32
|
|
|
28
33
|
const bootstrapInflight = new WeakMap()
|
|
29
34
|
|
|
@@ -136,6 +141,7 @@ function applyBootstrapOrigins(store, bootstrapResponse, { fromCache = false } =
|
|
|
136
141
|
reconcileOriginSafety(store)
|
|
137
142
|
store.options.originBootstrapLoading = false
|
|
138
143
|
applyOriginSafety(store.options)
|
|
144
|
+
apphubDebug('bootstrap', 'applyBootstrapOrigins done', describeStore(store))
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
function applyCachedBootstrapIfAny(store) {
|
|
@@ -150,12 +156,24 @@ async function fetchBootstrapSession(store) {
|
|
|
150
156
|
if (promise) return promise
|
|
151
157
|
|
|
152
158
|
promise = (async () => {
|
|
159
|
+
apphubDebug('bootstrap', 'fetchBootstrapSession start', describeStore(store))
|
|
153
160
|
syncApi(store)
|
|
154
|
-
if (!store.facade?.bootstrap)
|
|
161
|
+
if (!store.facade?.bootstrap) {
|
|
162
|
+
apphubDebug('bootstrap', 'fetchBootstrapSession abort — no bootstrap fn', describeStore(store))
|
|
163
|
+
return
|
|
164
|
+
}
|
|
155
165
|
|
|
156
166
|
const res = await store.facade.bootstrap()
|
|
167
|
+
apphubDebug('bootstrap', 'fetchBootstrapSession bootstrap response', {
|
|
168
|
+
...describeStore(store),
|
|
169
|
+
hasResponse: !!res,
|
|
170
|
+
})
|
|
157
171
|
if (res) applyBootstrapOrigins(store, res)
|
|
158
|
-
})().catch(() => {
|
|
172
|
+
})().catch((err) => {
|
|
173
|
+
apphubDebug('bootstrap', 'fetchBootstrapSession failed', {
|
|
174
|
+
...describeStore(store),
|
|
175
|
+
error: err?.message ?? String(err),
|
|
176
|
+
})
|
|
159
177
|
store.options.originBootstrapLoading = false
|
|
160
178
|
if (!store.options.serverOriginsResolved) {
|
|
161
179
|
reconcileOriginSafety(store)
|
|
@@ -170,6 +188,11 @@ async function fetchBootstrapSession(store) {
|
|
|
170
188
|
|
|
171
189
|
function startBootstrapSession(store) {
|
|
172
190
|
const { backendUrl, token } = store.credentials
|
|
191
|
+
apphubDebug('bootstrap', 'startBootstrapSession', {
|
|
192
|
+
...describeStore(store),
|
|
193
|
+
hasBackendUrl: !!backendUrl,
|
|
194
|
+
hasToken: !!token,
|
|
195
|
+
})
|
|
173
196
|
if (!backendUrl || !token) {
|
|
174
197
|
store.options.originBootstrapLoading = false
|
|
175
198
|
reconcileOriginSafety(store)
|
|
@@ -180,11 +203,15 @@ function startBootstrapSession(store) {
|
|
|
180
203
|
applyOriginSafety(store.options)
|
|
181
204
|
|
|
182
205
|
const hadCache = applyCachedBootstrapIfAny(store)
|
|
206
|
+
apphubDebug('bootstrap', 'startBootstrapSession after cache', {
|
|
207
|
+
hadCache,
|
|
208
|
+
...describeStore(store),
|
|
209
|
+
})
|
|
183
210
|
if (!hadCache && !store.options.originBlocked) {
|
|
184
211
|
store.options.originBootstrapLoading = true
|
|
185
212
|
store.options.originBlocked = false
|
|
186
213
|
applyOriginSafety(store.options)
|
|
187
|
-
disableModuleServices(store)
|
|
214
|
+
disableModuleServices(store, 'startBootstrapSession:no-cache')
|
|
188
215
|
}
|
|
189
216
|
|
|
190
217
|
return fetchBootstrapSession(store)
|
|
@@ -195,21 +222,30 @@ function reconcileOriginSafety(store) {
|
|
|
195
222
|
const wasLoading = store.options.originBootstrapLoading === true
|
|
196
223
|
applyOriginSafety(store.options)
|
|
197
224
|
|
|
225
|
+
let action = 'noop'
|
|
198
226
|
if (store.options.originBootstrapLoading) {
|
|
199
|
-
disableModuleServices(store)
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
disableModuleServices(store)
|
|
227
|
+
disableModuleServices(store, 'reconcileOriginSafety:loading')
|
|
228
|
+
action = 'disable:loading'
|
|
229
|
+
} else if (store.options.originBlocked && !wasBlocked) {
|
|
230
|
+
disableModuleServices(store, 'reconcileOriginSafety:blocked')
|
|
231
|
+
action = 'disable:blocked'
|
|
205
232
|
} else if (!store.options.originBlocked && (wasBlocked || wasLoading)) {
|
|
206
233
|
enableModuleApi(store)
|
|
234
|
+
action = 'enable'
|
|
207
235
|
}
|
|
236
|
+
|
|
237
|
+
apphubDebug('origin', 'reconcileOriginSafety', {
|
|
238
|
+
wasLoading,
|
|
239
|
+
wasBlocked,
|
|
240
|
+
action,
|
|
241
|
+
...describeStore(store),
|
|
242
|
+
})
|
|
208
243
|
}
|
|
209
244
|
|
|
210
|
-
function disableModuleServices(store) {
|
|
245
|
+
function disableModuleServices(store, reason = 'unknown') {
|
|
211
246
|
store.facade.setImpl(null)
|
|
212
247
|
store.coreApi = null
|
|
248
|
+
apphubDebug('api', 'disableModuleServices', { reason, ...describeStore(store) })
|
|
213
249
|
}
|
|
214
250
|
|
|
215
251
|
/** Window manager + app store — required even when origin is blocked (AppHubDesktop setup). */
|
|
@@ -221,6 +257,7 @@ function ensureModuleInfrastructure(vueApp, store) {
|
|
|
221
257
|
function enableModuleApi(store) {
|
|
222
258
|
syncApi(store)
|
|
223
259
|
syncZoneContext(store)
|
|
260
|
+
apphubDebug('api', 'enableModuleApi', describeStore(store))
|
|
224
261
|
}
|
|
225
262
|
|
|
226
263
|
function enableModuleServices(vueApp, store) {
|
|
@@ -300,6 +337,14 @@ function applyModuleOptions(store, options = {}) {
|
|
|
300
337
|
enforceDevFriendlyOrigins: nextPublic.enforceDevFriendlyOrigins,
|
|
301
338
|
})
|
|
302
339
|
applyOriginSafety(store.options, options)
|
|
340
|
+
const branch = !store.credentials.backendUrl || !store.credentials.token
|
|
341
|
+
? 'no-credentials'
|
|
342
|
+
: (credsUnchanged ? 'creds-unchanged' : 'start-bootstrap')
|
|
343
|
+
apphubDebug('install', 'applyModuleOptions', {
|
|
344
|
+
branch,
|
|
345
|
+
credsUnchanged,
|
|
346
|
+
...describeStore(store),
|
|
347
|
+
})
|
|
303
348
|
if (store.credentials.backendUrl && store.credentials.token) {
|
|
304
349
|
if (credsUnchanged) {
|
|
305
350
|
reconcileOriginSafety(store)
|
|
@@ -320,6 +365,10 @@ function syncApi(store) {
|
|
|
320
365
|
})
|
|
321
366
|
: null
|
|
322
367
|
facade.setImpl(api)
|
|
368
|
+
apphubDebug('api', 'syncApi', {
|
|
369
|
+
implSet: api != null,
|
|
370
|
+
...describeStore(store),
|
|
371
|
+
})
|
|
323
372
|
}
|
|
324
373
|
|
|
325
374
|
function syncCoreApi(store) {
|
|
@@ -368,12 +417,24 @@ export function installAppHubModule(vueApp, options = {}) {
|
|
|
368
417
|
|
|
369
418
|
if (store) {
|
|
370
419
|
vueApp.provide('apphubHostApp', vueApp)
|
|
420
|
+
apphubDebug('install', 'installAppHubModule re-install', {
|
|
421
|
+
appUid: vueApp._uid ?? null,
|
|
422
|
+
hasToken: !!(options.token),
|
|
423
|
+
theme: options.theme,
|
|
424
|
+
language: options.language,
|
|
425
|
+
})
|
|
371
426
|
applyModuleOptions(store, options)
|
|
372
427
|
ensureModuleInfrastructure(vueApp, store)
|
|
373
428
|
reconcileOriginSafety(store)
|
|
374
429
|
return store.facade
|
|
375
430
|
}
|
|
376
431
|
|
|
432
|
+
apphubDebug('install', 'installAppHubModule first install', {
|
|
433
|
+
appUid: vueApp._uid ?? null,
|
|
434
|
+
hasToken: !!(options.token),
|
|
435
|
+
backendUrl: options.backendUrl ? '(set)' : '(empty)',
|
|
436
|
+
})
|
|
437
|
+
|
|
377
438
|
const facade = createApiFacade()
|
|
378
439
|
const moduleOptions = reactive(buildPublicOptions(options))
|
|
379
440
|
applyOriginSafety(moduleOptions, options)
|
|
@@ -109,6 +109,7 @@ import { resolveLang } from '../../../i18n/resolveLang.js'
|
|
|
109
109
|
import { CATALOG_MODE_STORE } from '../constants/catalogModes.js'
|
|
110
110
|
import { useCatalogInfiniteScroll } from '../composables/useCatalogInfiniteScroll.js'
|
|
111
111
|
import { useAppStore } from '../composables/useAppStore.js'
|
|
112
|
+
import { apphubDebug, describeApp, describeHostApi } from '../../../utils/apphubDebug.js'
|
|
112
113
|
import AppHubAppStoreCard from './AppHubAppStoreCard.vue'
|
|
113
114
|
import AppHubAppStoreSettingsPanel from './AppHubAppStoreSettingsPanel.vue'
|
|
114
115
|
|
|
@@ -123,6 +124,7 @@ const settingsOpen = ref(false)
|
|
|
123
124
|
const appStore = useAppStore()
|
|
124
125
|
const catalog = appStore.catalogs.store
|
|
125
126
|
const rootApp = inject('apphubHostApp', null) ?? resolveRootApp(getCurrentInstance())
|
|
127
|
+
const rootAppSource = inject('apphubHostApp', null) ? 'inject' : 'resolveRootApp'
|
|
126
128
|
const zone = useAppHubZoneContext()
|
|
127
129
|
const moduleOptions = inject('apphubOptions', {})
|
|
128
130
|
const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
|
|
@@ -157,9 +159,25 @@ function hostApiOptions() {
|
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
|
|
160
|
-
async function reloadCatalog() {
|
|
161
|
-
if (!rootApp)
|
|
162
|
-
|
|
162
|
+
async function reloadCatalog(source = 'manual') {
|
|
163
|
+
if (!rootApp) {
|
|
164
|
+
apphubDebug('app-store', 'reloadCatalog skipped — no rootApp', { source, rootAppSource })
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
const hostApi = getHostApiForApp(rootApp)
|
|
168
|
+
const opts = hostApiOptions()
|
|
169
|
+
apphubDebug('app-store', 'reloadCatalog', {
|
|
170
|
+
source,
|
|
171
|
+
rootAppSource,
|
|
172
|
+
...describeApp(rootApp),
|
|
173
|
+
...describeHostApi(hostApi),
|
|
174
|
+
backendReady: opts.backendReady,
|
|
175
|
+
originBootstrapLoading: moduleOptions?.originBootstrapLoading,
|
|
176
|
+
originBlocked: moduleOptions?.originBlocked,
|
|
177
|
+
catalogLoaded: catalog.loaded,
|
|
178
|
+
catalogError: catalog.error,
|
|
179
|
+
})
|
|
180
|
+
await appStore.loadCatalog(hostApi, opts)
|
|
163
181
|
}
|
|
164
182
|
|
|
165
183
|
async function loadMore() {
|
|
@@ -191,21 +209,28 @@ async function onUninstall(app) {
|
|
|
191
209
|
}
|
|
192
210
|
|
|
193
211
|
onMounted(() => {
|
|
194
|
-
if (!catalog.loaded) reloadCatalog()
|
|
212
|
+
if (!catalog.loaded) reloadCatalog('onMounted')
|
|
195
213
|
})
|
|
196
214
|
|
|
197
215
|
watch(
|
|
198
216
|
() => moduleOptions?.hasToken,
|
|
199
217
|
(hasToken) => {
|
|
200
|
-
if (hasToken && !catalog.loaded) reloadCatalog()
|
|
218
|
+
if (hasToken && !catalog.loaded) reloadCatalog('watch:hasToken')
|
|
201
219
|
},
|
|
202
220
|
)
|
|
203
221
|
|
|
204
222
|
watch(
|
|
205
223
|
() => moduleOptions?.originBootstrapLoading,
|
|
206
224
|
(loading, wasLoading) => {
|
|
225
|
+
apphubDebug('app-store', 'watch originBootstrapLoading', {
|
|
226
|
+
loading,
|
|
227
|
+
wasLoading,
|
|
228
|
+
originBlocked: moduleOptions?.originBlocked,
|
|
229
|
+
catalogLoaded: catalog.loaded,
|
|
230
|
+
catalogError: catalog.error,
|
|
231
|
+
})
|
|
207
232
|
if (wasLoading && !loading && !moduleOptions?.originBlocked) {
|
|
208
|
-
if (!catalog.loaded || catalog.error === 'no_api') reloadCatalog()
|
|
233
|
+
if (!catalog.loaded || catalog.error === 'no_api') reloadCatalog('watch:bootstrap-done')
|
|
209
234
|
}
|
|
210
235
|
},
|
|
211
236
|
)
|
|
@@ -213,7 +238,7 @@ watch(
|
|
|
213
238
|
watch(
|
|
214
239
|
() => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
|
|
215
240
|
() => {
|
|
216
|
-
if (moduleOptions?.hasToken) reloadCatalog()
|
|
241
|
+
if (moduleOptions?.hasToken) reloadCatalog('watch:zone')
|
|
217
242
|
},
|
|
218
243
|
)
|
|
219
244
|
</script>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computed, inject, reactive } from 'vue'
|
|
2
2
|
import { CATALOG_MODE_DRAFT, CATALOG_MODE_STORE } from '../constants/catalogModes.js'
|
|
3
3
|
import { normalizeCatalogList } from '../utils/normalizeCatalogApp.js'
|
|
4
|
+
import { apphubDebug, describeHostApi } from '../../../utils/apphubDebug.js'
|
|
4
5
|
|
|
5
6
|
const APP_STORE_KEY = 'apphubAppStore'
|
|
6
7
|
|
|
@@ -28,6 +29,12 @@ function filterItems(items, search) {
|
|
|
28
29
|
)
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
function hostApiReady(hostApi) {
|
|
33
|
+
if (!hostApi?.apps) return false
|
|
34
|
+
if (typeof hostApi.hasImpl === 'function') return hostApi.hasImpl()
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
/**
|
|
32
39
|
* Independent App Store module — separate catalog buckets per mode (store / draft).
|
|
33
40
|
*/
|
|
@@ -102,7 +109,20 @@ export function createAppStoreState(options = {}) {
|
|
|
102
109
|
const append = options.append === true
|
|
103
110
|
const backendReady = options.backendReady !== false
|
|
104
111
|
|
|
105
|
-
|
|
112
|
+
apphubDebug('catalog', 'loadCatalog called', {
|
|
113
|
+
mode,
|
|
114
|
+
append,
|
|
115
|
+
backendReady,
|
|
116
|
+
...describeHostApi(hostApi),
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (!backendReady || !hostApiReady(hostApi)) {
|
|
120
|
+
apphubDebug('catalog', 'loadCatalog → no_api (not ready)', {
|
|
121
|
+
mode,
|
|
122
|
+
append,
|
|
123
|
+
backendReady,
|
|
124
|
+
...describeHostApi(hostApi),
|
|
125
|
+
})
|
|
106
126
|
if (!append) {
|
|
107
127
|
bucket.items = []
|
|
108
128
|
bucket.error = 'no_api'
|
|
@@ -131,8 +151,16 @@ export function createAppStoreState(options = {}) {
|
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
const res = await hostApi.apps(params)
|
|
154
|
+
apphubDebug('catalog', 'loadCatalog apps() response', {
|
|
155
|
+
mode,
|
|
156
|
+
append,
|
|
157
|
+
...describeHostApi(hostApi),
|
|
158
|
+
resUndefined: res === undefined,
|
|
159
|
+
resNull: res === null,
|
|
160
|
+
})
|
|
134
161
|
if (res === undefined || res === null) {
|
|
135
162
|
if (!append) {
|
|
163
|
+
apphubDebug('catalog', 'loadCatalog → no_api (undefined response)', { mode })
|
|
136
164
|
bucket.items = []
|
|
137
165
|
bucket.error = 'no_api'
|
|
138
166
|
bucket.loaded = false
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Temporary diagnostics — filter console with `[apphub:debug]`. Disable: localStorage.setItem('apphub:debug','0') */
|
|
2
|
+
export function isAppHubDebugEnabled() {
|
|
3
|
+
try {
|
|
4
|
+
if (typeof localStorage !== 'undefined' && localStorage.getItem('apphub:debug') === '0') {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
if (typeof window !== 'undefined' && window.__APPHUB_DEBUG__ === false) {
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
} catch {
|
|
11
|
+
/* ignore */
|
|
12
|
+
}
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function apphubDebug(scope, message, data) {
|
|
17
|
+
if (!isAppHubDebugEnabled()) return
|
|
18
|
+
const payload = data !== undefined ? { ts: Date.now(), ...data } : { ts: Date.now() }
|
|
19
|
+
console.info(`[apphub:debug] ${scope}: ${message}`, payload)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function describeHostApi(hostApi) {
|
|
23
|
+
if (!hostApi) return { hostApi: null }
|
|
24
|
+
return {
|
|
25
|
+
hasImpl: typeof hostApi.hasImpl === 'function' ? hostApi.hasImpl() : null,
|
|
26
|
+
hasAppsFn: typeof hostApi.apps === 'function',
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function describeStore(store) {
|
|
31
|
+
if (!store) return { store: null }
|
|
32
|
+
return {
|
|
33
|
+
hasImpl: store.facade?.hasImpl?.() ?? null,
|
|
34
|
+
originBootstrapLoading: store.options?.originBootstrapLoading ?? null,
|
|
35
|
+
originBlocked: store.options?.originBlocked ?? null,
|
|
36
|
+
originBlockReason: store.options?.originBlockReason ?? null,
|
|
37
|
+
hasToken: store.options?.hasToken ?? null,
|
|
38
|
+
backendUrl: store.credentials?.backendUrl ? '(set)' : '(empty)',
|
|
39
|
+
token: store.credentials?.token ? `(len ${store.credentials.token.length})` : '(empty)',
|
|
40
|
+
serverOriginsResolved: store.options?.serverOriginsResolved ?? null,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function describeApp(app) {
|
|
45
|
+
if (!app) return { app: null }
|
|
46
|
+
return { appUid: app._uid ?? null }
|
|
47
|
+
}
|