@kennofizet/apphub-frontend 0.1.2 → 0.1.4

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.2",
3
+ "version": "0.1.4",
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",
@@ -6,19 +6,34 @@ export function resolveRootApp(instance = getCurrentInstance()) {
6
6
  return instance.appContext?.app ?? instance.app ?? null
7
7
  }
8
8
 
9
+ export function resolveHostApp(app) {
10
+ if (app != null) return app
11
+ const instance = getCurrentInstance()
12
+ if (!instance) return null
13
+ return instance.appContext?.app ?? instance.app ?? null
14
+ }
15
+
16
+ export function getAppHubStoreForApp(app) {
17
+ const resolved = resolveHostApp(app)
18
+ return resolved != null ? getAppHubStore(resolved) : null
19
+ }
20
+
9
21
  export function getHostApiForApp(app) {
10
- return getAppHubStore(app)?.facade ?? null
22
+ return getAppHubStoreForApp(app)?.facade ?? null
11
23
  }
12
24
 
13
25
  export function isBackendReadyForApp(app) {
14
- const store = getAppHubStore(app)
26
+ const store = getAppHubStoreForApp(app)
15
27
  return !!(store?.credentials?.backendUrl && store?.credentials?.token)
16
28
  }
17
29
 
18
30
  export function isHostApiReadyForApp(app) {
19
- const store = getAppHubStore(app)
31
+ const store = getAppHubStoreForApp(app)
20
32
  if (!store?.credentials?.backendUrl || !store?.credentials?.token) return false
21
- return store.facade?.hasImpl?.() === true
33
+ if (typeof store.facade?.hasImpl === 'function') {
34
+ return store.facade.hasImpl()
35
+ }
36
+ return !!store.facade
22
37
  }
23
38
 
24
39
  /**
package/src/index.js CHANGED
@@ -208,8 +208,6 @@ function reconcileOriginSafety(store) {
208
208
 
209
209
  if (store.options.originBlocked && !wasBlocked) {
210
210
  disableModuleServices(store)
211
- } else if (!store.options.originBlocked && (wasBlocked || wasLoading)) {
212
- enableModuleApi(store)
213
211
  }
214
212
  }
215
213
 
@@ -294,6 +292,7 @@ function applyModuleOptions(store, options = {}) {
294
292
  enforceDevFriendlyOrigins: nextPublic.enforceDevFriendlyOrigins,
295
293
  })
296
294
  Object.assign(store.credentials, buildCredentials(options))
295
+ store._zonesSyncKey = null
297
296
  applyOriginSafety(store.options, options)
298
297
  if (store.credentials.backendUrl && store.credentials.token) {
299
298
  void startBootstrapSession(store)
@@ -332,11 +331,29 @@ function ensureZoneContext(app, store) {
332
331
  app.provide(APPHUB_ZONE_CONTEXT_KEY, store.zoneContext)
333
332
  }
334
333
 
335
- function syncZoneContext(store) {
334
+ function zoneSyncKey(store) {
335
+ const { coreUrl, token } = store.credentials
336
+ return `${coreUrl}::${token}`
337
+ }
338
+
339
+ function syncZoneContext(store, { force = false } = {}) {
336
340
  syncCoreApi(store)
337
- if (store.zoneContext) {
338
- store.zoneContext.refresh({ skipBootstrap: true })
341
+ if (!store.zoneContext || !store.coreApi) return
342
+
343
+ const key = zoneSyncKey(store)
344
+ if (!force && store._zonesSyncKey === key && store.zoneContext.state.zones.length > 0) {
345
+ return store._zonesRefreshInflight ?? undefined
346
+ }
347
+ if (store._zonesRefreshInflight) {
348
+ return store._zonesRefreshInflight
339
349
  }
350
+
351
+ store._zonesSyncKey = key
352
+ store._zonesRefreshInflight = store.zoneContext.refresh({ skipBootstrap: true })
353
+ .finally(() => {
354
+ store._zonesRefreshInflight = null
355
+ })
356
+ return store._zonesRefreshInflight
340
357
  }
341
358
 
342
359
  function ensureModuleState(app, store) {
@@ -358,11 +375,14 @@ export function installAppHubModule(vueApp, options = {}) {
358
375
  let store = getAppHubStore(vueApp)
359
376
 
360
377
  if (store) {
378
+ vueApp.provide('apphubHostApp', vueApp)
361
379
  applyModuleOptions(store, options)
362
380
  ensureModuleInfrastructure(vueApp, store)
363
- if (store.options.originBootstrapLoading || store.options.originBlocked) {
381
+ // While bootstrap is in flight, leave API state to startBootstrapSession — do not
382
+ // disable again (hub-host-starter may re-apply config when parent re-sends postMessage).
383
+ if (store.options.originBlocked) {
364
384
  disableModuleServices(store)
365
- } else {
385
+ } else if (!store.options.originBootstrapLoading) {
366
386
  enableModuleApi(store)
367
387
  }
368
388
  return store.facade
@@ -385,16 +405,13 @@ export function installAppHubModule(vueApp, options = {}) {
385
405
  }
386
406
  registerAppHubStore(vueApp, store)
387
407
 
408
+ vueApp.provide('apphubHostApp', vueApp)
388
409
  vueApp.provide('apphubOptions', moduleOptions)
389
410
  vueApp.component('AppHubDesktop', AppHubDesktop)
390
411
  vueApp.component('AppHubRunner', AppHubRunner)
391
412
 
392
413
  ensureModuleInfrastructure(vueApp, store)
393
- void startBootstrapSession(store).then(() => {
394
- if (!store.options.originBlocked) {
395
- enableModuleApi(store)
396
- }
397
- })
414
+ void startBootstrapSession(store)
398
415
 
399
416
  return facade
400
417
  }
@@ -122,7 +122,7 @@ const props = defineProps({
122
122
  const settingsOpen = ref(false)
123
123
  const appStore = useAppStore()
124
124
  const catalog = appStore.catalogs.store
125
- const rootApp = resolveRootApp(getCurrentInstance())
125
+ const rootApp = inject('apphubHostApp', null) ?? resolveRootApp(getCurrentInstance())
126
126
  const zone = useAppHubZoneContext()
127
127
  const moduleOptions = inject('apphubOptions', {})
128
128
  const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
@@ -210,21 +210,21 @@ watch(
210
210
  )
211
211
 
212
212
  watch(
213
- () => [
214
- moduleOptions?.originBootstrapLoading,
215
- moduleOptions?.originBlocked,
216
- isHostApiReadyForApp(rootApp),
217
- ],
218
- () => {
219
- if (!canLoadCatalog()) return
220
- if (!catalog.loaded || catalog.error === 'no_api') reloadCatalog()
213
+ () => moduleOptions?.originBootstrapLoading,
214
+ (loading, prevLoading) => {
215
+ if (prevLoading && !loading && canLoadCatalog()) {
216
+ reloadCatalog()
217
+ }
221
218
  },
222
219
  )
223
220
 
224
221
  watch(
225
222
  () => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
226
- () => {
227
- if (canLoadCatalog()) reloadCatalog()
223
+ (current, previous) => {
224
+ if (!previous) return
225
+ if (current[0] === previous[0] && current[1] === previous[1]) return
226
+ if (!canLoadCatalog() || !catalog.loaded) return
227
+ reloadCatalog()
228
228
  },
229
229
  )
230
230
  </script>
@@ -83,7 +83,7 @@ const props = defineProps({
83
83
 
84
84
  const appStore = useAppStore()
85
85
  const catalog = appStore.catalogs.draft
86
- const rootApp = resolveRootApp(getCurrentInstance())
86
+ const rootApp = inject('apphubHostApp', null) ?? resolveRootApp(getCurrentInstance())
87
87
  const zone = useAppHubZoneContext()
88
88
  const moduleOptions = inject('apphubOptions', {})
89
89
  const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
@@ -184,21 +184,21 @@ watch(
184
184
  )
185
185
 
186
186
  watch(
187
- () => [
188
- moduleOptions?.originBootstrapLoading,
189
- moduleOptions?.originBlocked,
190
- isHostApiReadyForApp(rootApp),
191
- ],
192
- () => {
193
- if (!canLoadCatalog()) return
194
- if (!catalog.loaded || catalog.error === 'no_api') reloadCatalog()
187
+ () => moduleOptions?.originBootstrapLoading,
188
+ (loading, prevLoading) => {
189
+ if (prevLoading && !loading && canLoadCatalog()) {
190
+ reloadCatalog()
191
+ }
195
192
  },
196
193
  )
197
194
 
198
195
  watch(
199
196
  () => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
200
- () => {
201
- if (canLoadCatalog()) reloadCatalog()
197
+ (current, previous) => {
198
+ if (!previous) return
199
+ if (current[0] === previous[0] && current[1] === previous[1]) return
200
+ if (!canLoadCatalog() || !catalog.loaded) return
201
+ reloadCatalog()
202
202
  },
203
203
  )
204
204
  </script>
@@ -102,7 +102,10 @@ export function createAppStoreState(options = {}) {
102
102
  const append = options.append === true
103
103
  const backendReady = options.backendReady !== false
104
104
 
105
- if (!backendReady || !hostApi?.hasImpl?.()) {
105
+ const facadeReady = hostApi && (
106
+ typeof hostApi.hasImpl === 'function' ? hostApi.hasImpl() : true
107
+ )
108
+ if (!backendReady || !facadeReady) {
106
109
  if (!append) {
107
110
  bucket.items = []
108
111
  bucket.error = 'no_api'