@kennofizet/apphub-frontend 0.1.1 → 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.1",
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
@@ -136,6 +136,9 @@ 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
+ }
139
142
  }
140
143
 
141
144
  function applyCachedBootstrapIfAny(store) {
@@ -156,9 +159,12 @@ async function fetchBootstrapSession(store) {
156
159
  const res = await store.facade.bootstrap()
157
160
  if (res) applyBootstrapOrigins(store, res)
158
161
  })().catch(() => {
162
+ const wasLoading = store.options.originBootstrapLoading === true
159
163
  store.options.originBootstrapLoading = false
160
164
  if (!store.options.serverOriginsResolved) {
161
165
  reconcileOriginSafety(store)
166
+ } else if (!store.options.originBlocked && (wasLoading || !store.facade?.hasImpl?.())) {
167
+ enableModuleApi(store)
162
168
  }
163
169
  }).finally(() => {
164
170
  bootstrapInflight.delete(store)
@@ -243,6 +249,9 @@ function createApiFacade() {
243
249
  setImpl(next) {
244
250
  impl = next
245
251
  },
252
+ hasImpl() {
253
+ return impl != null
254
+ },
246
255
  bootstrap: (...args) => impl?.bootstrap?.(...args),
247
256
  apps: (...args) => impl?.apps?.(...args),
248
257
  launch: (...args) => impl?.launch?.(...args),
@@ -284,13 +293,13 @@ function applyModuleOptions(store, options = {}) {
284
293
  hostedSandboxSameOrigin: nextPublic.hostedSandboxSameOrigin,
285
294
  enforceDevFriendlyOrigins: nextPublic.enforceDevFriendlyOrigins,
286
295
  })
296
+ Object.assign(store.credentials, buildCredentials(options))
287
297
  applyOriginSafety(store.options, options)
288
298
  if (store.credentials.backendUrl && store.credentials.token) {
289
299
  void startBootstrapSession(store)
290
300
  } else {
291
301
  reconcileOriginSafety(store)
292
302
  }
293
- Object.assign(store.credentials, buildCredentials(options))
294
303
  }
295
304
 
296
305
  function syncApi(store) {
@@ -381,7 +390,11 @@ export function installAppHubModule(vueApp, options = {}) {
381
390
  vueApp.component('AppHubRunner', AppHubRunner)
382
391
 
383
392
  ensureModuleInfrastructure(vueApp, store)
384
- void startBootstrapSession(store)
393
+ void startBootstrapSession(store).then(() => {
394
+ if (!store.options.originBlocked) {
395
+ enableModuleApi(store)
396
+ }
397
+ })
385
398
 
386
399
  return facade
387
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'