@kennofizet/apphub-frontend 0.1.4 → 0.1.5

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.4",
3
+ "version": "0.1.5",
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,36 +6,15 @@ 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
-
21
9
  export function getHostApiForApp(app) {
22
- return getAppHubStoreForApp(app)?.facade ?? null
10
+ return getAppHubStore(app)?.facade ?? null
23
11
  }
24
12
 
25
13
  export function isBackendReadyForApp(app) {
26
- const store = getAppHubStoreForApp(app)
14
+ const store = getAppHubStore(app)
27
15
  return !!(store?.credentials?.backendUrl && store?.credentials?.token)
28
16
  }
29
17
 
30
- export function isHostApiReadyForApp(app) {
31
- const store = getAppHubStoreForApp(app)
32
- if (!store?.credentials?.backendUrl || !store?.credentials?.token) return false
33
- if (typeof store.facade?.hasImpl === 'function') {
34
- return store.facade.hasImpl()
35
- }
36
- return !!store.facade
37
- }
38
-
39
18
  /**
40
19
  * Host-only API — use in Hub shell components. Not provided via inject
41
20
  * so publisher app code cannot access grantBridgeScope or internal docs.
package/src/index.js CHANGED
@@ -136,9 +136,6 @@ function applyBootstrapOrigins(store, bootstrapResponse, { fromCache = false } =
136
136
  reconcileOriginSafety(store)
137
137
  store.options.originBootstrapLoading = false
138
138
  applyOriginSafety(store.options)
139
- if (!store.options.originBlocked) {
140
- enableModuleApi(store)
141
- }
142
139
  }
143
140
 
144
141
  function applyCachedBootstrapIfAny(store) {
@@ -159,12 +156,9 @@ async function fetchBootstrapSession(store) {
159
156
  const res = await store.facade.bootstrap()
160
157
  if (res) applyBootstrapOrigins(store, res)
161
158
  })().catch(() => {
162
- const wasLoading = store.options.originBootstrapLoading === true
163
159
  store.options.originBootstrapLoading = false
164
160
  if (!store.options.serverOriginsResolved) {
165
161
  reconcileOriginSafety(store)
166
- } else if (!store.options.originBlocked && (wasLoading || !store.facade?.hasImpl?.())) {
167
- enableModuleApi(store)
168
162
  }
169
163
  }).finally(() => {
170
164
  bootstrapInflight.delete(store)
@@ -208,6 +202,8 @@ function reconcileOriginSafety(store) {
208
202
 
209
203
  if (store.options.originBlocked && !wasBlocked) {
210
204
  disableModuleServices(store)
205
+ } else if (!store.options.originBlocked && (wasBlocked || wasLoading)) {
206
+ enableModuleApi(store)
211
207
  }
212
208
  }
213
209
 
@@ -247,9 +243,6 @@ function createApiFacade() {
247
243
  setImpl(next) {
248
244
  impl = next
249
245
  },
250
- hasImpl() {
251
- return impl != null
252
- },
253
246
  bootstrap: (...args) => impl?.bootstrap?.(...args),
254
247
  apps: (...args) => impl?.apps?.(...args),
255
248
  launch: (...args) => impl?.launch?.(...args),
@@ -291,14 +284,13 @@ function applyModuleOptions(store, options = {}) {
291
284
  hostedSandboxSameOrigin: nextPublic.hostedSandboxSameOrigin,
292
285
  enforceDevFriendlyOrigins: nextPublic.enforceDevFriendlyOrigins,
293
286
  })
294
- Object.assign(store.credentials, buildCredentials(options))
295
- store._zonesSyncKey = null
296
287
  applyOriginSafety(store.options, options)
297
288
  if (store.credentials.backendUrl && store.credentials.token) {
298
289
  void startBootstrapSession(store)
299
290
  } else {
300
291
  reconcileOriginSafety(store)
301
292
  }
293
+ Object.assign(store.credentials, buildCredentials(options))
302
294
  }
303
295
 
304
296
  function syncApi(store) {
@@ -331,29 +323,11 @@ function ensureZoneContext(app, store) {
331
323
  app.provide(APPHUB_ZONE_CONTEXT_KEY, store.zoneContext)
332
324
  }
333
325
 
334
- function zoneSyncKey(store) {
335
- const { coreUrl, token } = store.credentials
336
- return `${coreUrl}::${token}`
337
- }
338
-
339
- function syncZoneContext(store, { force = false } = {}) {
326
+ function syncZoneContext(store) {
340
327
  syncCoreApi(store)
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
328
+ if (store.zoneContext) {
329
+ store.zoneContext.refresh({ skipBootstrap: true })
349
330
  }
350
-
351
- store._zonesSyncKey = key
352
- store._zonesRefreshInflight = store.zoneContext.refresh({ skipBootstrap: true })
353
- .finally(() => {
354
- store._zonesRefreshInflight = null
355
- })
356
- return store._zonesRefreshInflight
357
331
  }
358
332
 
359
333
  function ensureModuleState(app, store) {
@@ -375,14 +349,11 @@ export function installAppHubModule(vueApp, options = {}) {
375
349
  let store = getAppHubStore(vueApp)
376
350
 
377
351
  if (store) {
378
- vueApp.provide('apphubHostApp', vueApp)
379
352
  applyModuleOptions(store, options)
380
353
  ensureModuleInfrastructure(vueApp, store)
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) {
354
+ if (store.options.originBootstrapLoading || store.options.originBlocked) {
384
355
  disableModuleServices(store)
385
- } else if (!store.options.originBootstrapLoading) {
356
+ } else {
386
357
  enableModuleApi(store)
387
358
  }
388
359
  return store.facade
@@ -405,7 +376,6 @@ export function installAppHubModule(vueApp, options = {}) {
405
376
  }
406
377
  registerAppHubStore(vueApp, store)
407
378
 
408
- vueApp.provide('apphubHostApp', vueApp)
409
379
  vueApp.provide('apphubOptions', moduleOptions)
410
380
  vueApp.component('AppHubDesktop', AppHubDesktop)
411
381
  vueApp.component('AppHubRunner', AppHubRunner)
@@ -100,7 +100,7 @@
100
100
  import { computed, getCurrentInstance, inject, onMounted, ref, watch } from 'vue'
101
101
  import {
102
102
  getHostApiForApp,
103
- isHostApiReadyForApp,
103
+ isBackendReadyForApp,
104
104
  resolveRootApp,
105
105
  } from '../../../composables/useAppHubHostApi.js'
106
106
  import { useAppHubZoneContext } from '../../../composables/useAppHubZoneContext.js'
@@ -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 = inject('apphubHostApp', null) ?? resolveRootApp(getCurrentInstance())
125
+ const rootApp = resolveRootApp(getCurrentInstance())
126
126
  const zone = useAppHubZoneContext()
127
127
  const moduleOptions = inject('apphubOptions', {})
128
128
  const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
@@ -152,19 +152,11 @@ const labels = computed(() => ({
152
152
 
153
153
  function hostApiOptions() {
154
154
  return {
155
- backendReady: isHostApiReadyForApp(rootApp),
155
+ backendReady: isBackendReadyForApp(rootApp),
156
156
  mode: CATALOG_MODE_STORE,
157
157
  }
158
158
  }
159
159
 
160
- function canLoadCatalog() {
161
- return (
162
- !moduleOptions?.originBootstrapLoading
163
- && !moduleOptions?.originBlocked
164
- && isHostApiReadyForApp(rootApp)
165
- )
166
- }
167
-
168
160
  async function reloadCatalog() {
169
161
  if (!rootApp) return
170
162
  await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
@@ -205,26 +197,14 @@ onMounted(() => {
205
197
  watch(
206
198
  () => moduleOptions?.hasToken,
207
199
  (hasToken) => {
208
- if (hasToken && canLoadCatalog() && !catalog.loaded) reloadCatalog()
209
- },
210
- )
211
-
212
- watch(
213
- () => moduleOptions?.originBootstrapLoading,
214
- (loading, prevLoading) => {
215
- if (prevLoading && !loading && canLoadCatalog()) {
216
- reloadCatalog()
217
- }
200
+ if (hasToken && !catalog.loaded) reloadCatalog()
218
201
  },
219
202
  )
220
203
 
221
204
  watch(
222
205
  () => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
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()
206
+ () => {
207
+ if (moduleOptions?.hasToken) reloadCatalog()
228
208
  },
229
209
  )
230
210
  </script>
@@ -65,7 +65,7 @@
65
65
  import { computed, getCurrentInstance, inject, onMounted, reactive, ref, watch } from 'vue'
66
66
  import {
67
67
  getHostApiForApp,
68
- isHostApiReadyForApp,
68
+ isBackendReadyForApp,
69
69
  resolveRootApp,
70
70
  } from '../../../composables/useAppHubHostApi.js'
71
71
  import { useAppHubZoneContext } from '../../../composables/useAppHubZoneContext.js'
@@ -83,7 +83,7 @@ const props = defineProps({
83
83
 
84
84
  const appStore = useAppStore()
85
85
  const catalog = appStore.catalogs.draft
86
- const rootApp = inject('apphubHostApp', null) ?? resolveRootApp(getCurrentInstance())
86
+ const rootApp = resolveRootApp(getCurrentInstance())
87
87
  const zone = useAppHubZoneContext()
88
88
  const moduleOptions = inject('apphubOptions', {})
89
89
  const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
@@ -119,19 +119,11 @@ const labels = computed(() => ({
119
119
 
120
120
  function hostApiOptions() {
121
121
  return {
122
- backendReady: isHostApiReadyForApp(rootApp),
122
+ backendReady: isBackendReadyForApp(rootApp),
123
123
  mode: CATALOG_MODE_DRAFT,
124
124
  }
125
125
  }
126
126
 
127
- function canLoadCatalog() {
128
- return (
129
- !moduleOptions?.originBootstrapLoading
130
- && !moduleOptions?.originBlocked
131
- && isHostApiReadyForApp(rootApp)
132
- )
133
- }
134
-
135
127
  async function reloadCatalog() {
136
128
  if (!rootApp) return
137
129
  await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
@@ -179,26 +171,14 @@ onMounted(() => {
179
171
  watch(
180
172
  () => moduleOptions?.hasToken,
181
173
  (hasToken) => {
182
- if (hasToken && canLoadCatalog() && !catalog.loaded) reloadCatalog()
183
- },
184
- )
185
-
186
- watch(
187
- () => moduleOptions?.originBootstrapLoading,
188
- (loading, prevLoading) => {
189
- if (prevLoading && !loading && canLoadCatalog()) {
190
- reloadCatalog()
191
- }
174
+ if (hasToken && !catalog.loaded) reloadCatalog()
192
175
  },
193
176
  )
194
177
 
195
178
  watch(
196
179
  () => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
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()
180
+ () => {
181
+ if (moduleOptions?.hasToken) reloadCatalog()
202
182
  },
203
183
  )
204
184
  </script>
@@ -102,10 +102,7 @@ export function createAppStoreState(options = {}) {
102
102
  const append = options.append === true
103
103
  const backendReady = options.backendReady !== false
104
104
 
105
- const facadeReady = hostApi && (
106
- typeof hostApi.hasImpl === 'function' ? hostApi.hasImpl() : true
107
- )
108
- if (!backendReady || !facadeReady) {
105
+ if (!backendReady || !hostApi?.apps) {
109
106
  if (!append) {
110
107
  bucket.items = []
111
108
  bucket.error = 'no_api'