@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kennofizet/apphub-frontend",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "App Hub Vue 3 UI — Windows-style desktop shell and modular apps (App Store default).",
5
5
  "main": "./src/index.js",
6
6
  "module": "./src/index.js",
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) return
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
- return
201
- }
202
-
203
- if (store.options.originBlocked && !wasBlocked) {
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) return
162
- await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
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
- if (!backendReady || !hostApi?.apps) {
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
+ }