@kennofizet/apphub-frontend 0.1.0 → 0.1.2

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.0",
3
+ "version": "0.1.2",
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",
@@ -15,6 +15,12 @@ export function isBackendReadyForApp(app) {
15
15
  return !!(store?.credentials?.backendUrl && store?.credentials?.token)
16
16
  }
17
17
 
18
+ export function isHostApiReadyForApp(app) {
19
+ const store = getAppHubStore(app)
20
+ if (!store?.credentials?.backendUrl || !store?.credentials?.token) return false
21
+ return store.facade?.hasImpl?.() === true
22
+ }
23
+
18
24
  /**
19
25
  * Host-only API — use in Hub shell components. Not provided via inject
20
26
  * so publisher app code cannot access grantBridgeScope or internal docs.
package/src/index.js CHANGED
@@ -120,7 +120,6 @@ function applyBootstrapOrigins(store, bootstrapResponse, { fromCache = false } =
120
120
  }
121
121
 
122
122
  store.options.serverOriginsResolved = true
123
- store.options.originBootstrapLoading = false
124
123
 
125
124
  if (!fromCache) {
126
125
  saveBootstrapCache(store.credentials.backendUrl, bootstrapResponse)
@@ -132,7 +131,14 @@ function applyBootstrapOrigins(store, bootstrapResponse, { fromCache = false } =
132
131
  store.zoneContext.state.user.name = user.name
133
132
  }
134
133
 
134
+ // Reconcile while originBootstrapLoading may still be true so enableModuleApi runs
135
+ // after disableModuleServices (wasLoading must be captured before clearing the flag).
135
136
  reconcileOriginSafety(store)
137
+ store.options.originBootstrapLoading = false
138
+ applyOriginSafety(store.options)
139
+ if (!store.options.originBlocked) {
140
+ enableModuleApi(store)
141
+ }
136
142
  }
137
143
 
138
144
  function applyCachedBootstrapIfAny(store) {
@@ -153,9 +159,12 @@ async function fetchBootstrapSession(store) {
153
159
  const res = await store.facade.bootstrap()
154
160
  if (res) applyBootstrapOrigins(store, res)
155
161
  })().catch(() => {
162
+ const wasLoading = store.options.originBootstrapLoading === true
156
163
  store.options.originBootstrapLoading = false
157
164
  if (!store.options.serverOriginsResolved) {
158
165
  reconcileOriginSafety(store)
166
+ } else if (!store.options.originBlocked && (wasLoading || !store.facade?.hasImpl?.())) {
167
+ enableModuleApi(store)
159
168
  }
160
169
  }).finally(() => {
161
170
  bootstrapInflight.delete(store)
@@ -240,6 +249,9 @@ function createApiFacade() {
240
249
  setImpl(next) {
241
250
  impl = next
242
251
  },
252
+ hasImpl() {
253
+ return impl != null
254
+ },
243
255
  bootstrap: (...args) => impl?.bootstrap?.(...args),
244
256
  apps: (...args) => impl?.apps?.(...args),
245
257
  launch: (...args) => impl?.launch?.(...args),
@@ -281,13 +293,13 @@ function applyModuleOptions(store, options = {}) {
281
293
  hostedSandboxSameOrigin: nextPublic.hostedSandboxSameOrigin,
282
294
  enforceDevFriendlyOrigins: nextPublic.enforceDevFriendlyOrigins,
283
295
  })
296
+ Object.assign(store.credentials, buildCredentials(options))
284
297
  applyOriginSafety(store.options, options)
285
298
  if (store.credentials.backendUrl && store.credentials.token) {
286
299
  void startBootstrapSession(store)
287
300
  } else {
288
301
  reconcileOriginSafety(store)
289
302
  }
290
- Object.assign(store.credentials, buildCredentials(options))
291
303
  }
292
304
 
293
305
  function syncApi(store) {
@@ -378,7 +390,11 @@ export function installAppHubModule(vueApp, options = {}) {
378
390
  vueApp.component('AppHubRunner', AppHubRunner)
379
391
 
380
392
  ensureModuleInfrastructure(vueApp, store)
381
- void startBootstrapSession(store)
393
+ void startBootstrapSession(store).then(() => {
394
+ if (!store.options.originBlocked) {
395
+ enableModuleApi(store)
396
+ }
397
+ })
382
398
 
383
399
  return facade
384
400
  }
@@ -100,7 +100,7 @@
100
100
  import { computed, getCurrentInstance, inject, onMounted, ref, watch } from 'vue'
101
101
  import {
102
102
  getHostApiForApp,
103
- isBackendReadyForApp,
103
+ isHostApiReadyForApp,
104
104
  resolveRootApp,
105
105
  } from '../../../composables/useAppHubHostApi.js'
106
106
  import { useAppHubZoneContext } from '../../../composables/useAppHubZoneContext.js'
@@ -152,11 +152,19 @@ const labels = computed(() => ({
152
152
 
153
153
  function hostApiOptions() {
154
154
  return {
155
- backendReady: isBackendReadyForApp(rootApp),
155
+ backendReady: isHostApiReadyForApp(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
+
160
168
  async function reloadCatalog() {
161
169
  if (!rootApp) return
162
170
  await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
@@ -197,14 +205,26 @@ onMounted(() => {
197
205
  watch(
198
206
  () => moduleOptions?.hasToken,
199
207
  (hasToken) => {
200
- if (hasToken && !catalog.loaded) reloadCatalog()
208
+ if (hasToken && canLoadCatalog() && !catalog.loaded) reloadCatalog()
209
+ },
210
+ )
211
+
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()
201
221
  },
202
222
  )
203
223
 
204
224
  watch(
205
225
  () => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
206
226
  () => {
207
- if (moduleOptions?.hasToken) reloadCatalog()
227
+ if (canLoadCatalog()) reloadCatalog()
208
228
  },
209
229
  )
210
230
  </script>
@@ -65,7 +65,7 @@
65
65
  import { computed, getCurrentInstance, inject, onMounted, reactive, ref, watch } from 'vue'
66
66
  import {
67
67
  getHostApiForApp,
68
- isBackendReadyForApp,
68
+ isHostApiReadyForApp,
69
69
  resolveRootApp,
70
70
  } from '../../../composables/useAppHubHostApi.js'
71
71
  import { useAppHubZoneContext } from '../../../composables/useAppHubZoneContext.js'
@@ -119,11 +119,19 @@ const labels = computed(() => ({
119
119
 
120
120
  function hostApiOptions() {
121
121
  return {
122
- backendReady: isBackendReadyForApp(rootApp),
122
+ backendReady: isHostApiReadyForApp(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
+
127
135
  async function reloadCatalog() {
128
136
  if (!rootApp) return
129
137
  await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
@@ -171,14 +179,26 @@ onMounted(() => {
171
179
  watch(
172
180
  () => moduleOptions?.hasToken,
173
181
  (hasToken) => {
174
- if (hasToken && !catalog.loaded) reloadCatalog()
182
+ if (hasToken && canLoadCatalog() && !catalog.loaded) reloadCatalog()
183
+ },
184
+ )
185
+
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()
175
195
  },
176
196
  )
177
197
 
178
198
  watch(
179
199
  () => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
180
200
  () => {
181
- if (moduleOptions?.hasToken) reloadCatalog()
201
+ if (canLoadCatalog()) reloadCatalog()
182
202
  },
183
203
  )
184
204
  </script>
@@ -102,7 +102,7 @@ export function createAppStoreState(options = {}) {
102
102
  const append = options.append === true
103
103
  const backendReady = options.backendReady !== false
104
104
 
105
- if (!backendReady || !hostApi?.apps) {
105
+ if (!backendReady || !hostApi?.hasImpl?.()) {
106
106
  if (!append) {
107
107
  bucket.items = []
108
108
  bucket.error = 'no_api'