@kennofizet/apphub-frontend 0.1.7 → 0.1.8

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.7",
3
+ "version": "0.1.8",
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",
@@ -1,11 +1,18 @@
1
- import { getCurrentInstance } from 'vue'
2
- import { getAppHubStore } from '../moduleStore.js'
1
+ import { getCurrentInstance, inject } from 'vue'
2
+ import { APPHUB_MODULE_STORE_KEY, getAppHubStore } from '../moduleStore.js'
3
3
 
4
4
  export function resolveRootApp(instance = getCurrentInstance()) {
5
5
  if (!instance) return null
6
6
  return instance.appContext?.app ?? instance.app ?? null
7
7
  }
8
8
 
9
+ /** Resolve module store — inject first (always same instance), then WeakMap by Vue app. */
10
+ export function useAppHubModuleStore() {
11
+ const fromInject = inject(APPHUB_MODULE_STORE_KEY, null)
12
+ if (fromInject) return fromInject
13
+ return getAppHubStore(resolveRootApp())
14
+ }
15
+
9
16
  export function getHostApiForApp(app) {
10
17
  return getAppHubStore(app)?.facade ?? null
11
18
  }
@@ -20,5 +27,9 @@ export function isBackendReadyForApp(app) {
20
27
  * so publisher app code cannot access grantBridgeScope or internal docs.
21
28
  */
22
29
  export function useAppHubHostApi() {
23
- return getHostApiForApp(resolveRootApp())
30
+ return useAppHubModuleStore()?.facade ?? null
31
+ }
32
+
33
+ export function isBackendReadyFromStore(store) {
34
+ return !!(store?.credentials?.backendUrl && store?.credentials?.token)
24
35
  }
package/src/index.js CHANGED
@@ -8,7 +8,7 @@ import { APPHUB_ZONE_CONTEXT_KEY } from './composables/useAppHubZoneContext.js'
8
8
  import { createAppStoreState, provideAppStore } from './modules/app-store/index.js'
9
9
  import { AppHubDesktop } from './modules/desktop/index.js'
10
10
  import { AppHubRunner } from './modules/runner/index.js'
11
- import { getAppHubStore, registerAppHubStore } from './moduleStore.js'
11
+ import { getAppHubStore, registerAppHubStore, APPHUB_MODULE_STORE_KEY } from './moduleStore.js'
12
12
  import {
13
13
  createWindowManagerState,
14
14
  provideWindowManager,
@@ -417,6 +417,7 @@ export function installAppHubModule(vueApp, options = {}) {
417
417
 
418
418
  if (store) {
419
419
  vueApp.provide('apphubHostApp', vueApp)
420
+ vueApp.provide(APPHUB_MODULE_STORE_KEY, store)
420
421
  apphubDebug('install', 'installAppHubModule re-install', {
421
422
  appUid: vueApp._uid ?? null,
422
423
  hasToken: !!(options.token),
@@ -454,6 +455,7 @@ export function installAppHubModule(vueApp, options = {}) {
454
455
 
455
456
  vueApp.provide('apphubOptions', moduleOptions)
456
457
  vueApp.provide('apphubHostApp', vueApp)
458
+ vueApp.provide(APPHUB_MODULE_STORE_KEY, store)
457
459
  vueApp.component('AppHubDesktop', AppHubDesktop)
458
460
  vueApp.component('AppHubRunner', AppHubRunner)
459
461
 
@@ -468,7 +470,7 @@ export function isAppHubModuleInstalled(vueApp) {
468
470
  return vueApp != null && getAppHubStore(vueApp) != null
469
471
  }
470
472
 
471
- export { useAppHubHostApi } from './composables/useAppHubHostApi.js'
473
+ export { useAppHubHostApi, useAppHubModuleStore } from './composables/useAppHubHostApi.js'
472
474
  export { useAppHubZoneContext } from './composables/useAppHubZoneContext.js'
473
475
  export { createAppHubApi } from './api/index.js'
474
476
  export { createCoreApi } from './api/coreApi.js'
@@ -1,5 +1,18 @@
1
- /** Private per-Vue-app store — not exposed via provide/inject. */
2
- const installedApps = new WeakMap()
1
+ /** Internal inject keypackage components only; not part of public host API. */
2
+ export const APPHUB_MODULE_STORE_KEY = Symbol('apphubModuleStore')
3
+
4
+ const REGISTRY_KEY = '__kennofizet_apphub_installed_apps__'
5
+
6
+ /** Shared WeakMap — Vite may load this module twice (host bundle vs SFC chunks). */
7
+ function getInstalledAppsRegistry() {
8
+ const root = typeof globalThis !== 'undefined' ? globalThis : global
9
+ if (!root[REGISTRY_KEY]) {
10
+ root[REGISTRY_KEY] = new WeakMap()
11
+ }
12
+ return root[REGISTRY_KEY]
13
+ }
14
+
15
+ const installedApps = getInstalledAppsRegistry()
3
16
 
4
17
  export function registerAppHubStore(app, store) {
5
18
  installedApps.set(app, store)
@@ -97,11 +97,11 @@
97
97
  </template>
98
98
 
99
99
  <script setup>
100
- import { computed, getCurrentInstance, inject, onMounted, ref, watch } from 'vue'
100
+ import { computed, inject, onMounted, ref, watch } from 'vue'
101
101
  import {
102
- getHostApiForApp,
103
- isBackendReadyForApp,
104
- resolveRootApp,
102
+ isBackendReadyFromStore,
103
+ useAppHubHostApi,
104
+ useAppHubModuleStore,
105
105
  } from '../../../composables/useAppHubHostApi.js'
106
106
  import { useAppHubZoneContext } from '../../../composables/useAppHubZoneContext.js'
107
107
  import { t } from '../../../i18n/index.js'
@@ -109,7 +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
+ import { apphubDebug, describeHostApi } from '../../../utils/apphubDebug.js'
113
113
  import AppHubAppStoreCard from './AppHubAppStoreCard.vue'
114
114
  import AppHubAppStoreSettingsPanel from './AppHubAppStoreSettingsPanel.vue'
115
115
 
@@ -123,8 +123,9 @@ const props = defineProps({
123
123
  const settingsOpen = ref(false)
124
124
  const appStore = useAppStore()
125
125
  const catalog = appStore.catalogs.store
126
- const rootApp = inject('apphubHostApp', null) ?? resolveRootApp(getCurrentInstance())
127
- const rootAppSource = inject('apphubHostApp', null) ? 'inject' : 'resolveRootApp'
126
+ const hubStore = useAppHubModuleStore()
127
+ const hostApi = useAppHubHostApi()
128
+ const rootApp = inject('apphubHostApp', null)
128
129
  const zone = useAppHubZoneContext()
129
130
  const moduleOptions = inject('apphubOptions', {})
130
131
  const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
@@ -154,35 +155,29 @@ const labels = computed(() => ({
154
155
 
155
156
  function hostApiOptions() {
156
157
  return {
157
- backendReady: isBackendReadyForApp(rootApp),
158
+ backendReady: isBackendReadyFromStore(hubStore),
158
159
  mode: CATALOG_MODE_STORE,
159
160
  }
160
161
  }
161
162
 
162
163
  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)
164
+ const api = hostApi
168
165
  const opts = hostApiOptions()
169
166
  apphubDebug('app-store', 'reloadCatalog', {
170
167
  source,
171
- rootAppSource,
172
- ...describeApp(rootApp),
173
- ...describeHostApi(hostApi),
168
+ storeFromInject: !!hubStore,
169
+ ...describeHostApi(api),
174
170
  backendReady: opts.backendReady,
175
171
  originBootstrapLoading: moduleOptions?.originBootstrapLoading,
176
172
  originBlocked: moduleOptions?.originBlocked,
177
173
  catalogLoaded: catalog.loaded,
178
174
  catalogError: catalog.error,
179
175
  })
180
- await appStore.loadCatalog(hostApi, opts)
176
+ await appStore.loadCatalog(api, opts)
181
177
  }
182
178
 
183
179
  async function loadMore() {
184
- if (!rootApp) return
185
- await appStore.loadMoreCatalog(getHostApiForApp(rootApp), CATALOG_MODE_STORE, hostApiOptions())
180
+ await appStore.loadMoreCatalog(hostApi, CATALOG_MODE_STORE, hostApiOptions())
186
181
  }
187
182
 
188
183
  const { rootRef: scrollRoot, sentinelRef: scrollSentinel } = useCatalogInfiniteScroll({
@@ -62,11 +62,11 @@
62
62
  </template>
63
63
 
64
64
  <script setup>
65
- import { computed, getCurrentInstance, inject, onMounted, reactive, ref, watch } from 'vue'
65
+ import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
66
66
  import {
67
- getHostApiForApp,
68
- isBackendReadyForApp,
69
- resolveRootApp,
67
+ isBackendReadyFromStore,
68
+ useAppHubHostApi,
69
+ useAppHubModuleStore,
70
70
  } from '../../../composables/useAppHubHostApi.js'
71
71
  import { useAppHubZoneContext } from '../../../composables/useAppHubZoneContext.js'
72
72
  import { t } from '../../../i18n/index.js'
@@ -83,7 +83,9 @@ 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 hubStore = useAppHubModuleStore()
87
+ const hostApi = useAppHubHostApi()
88
+ const rootApp = inject('apphubHostApp', null)
87
89
  const zone = useAppHubZoneContext()
88
90
  const moduleOptions = inject('apphubOptions', {})
89
91
  const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
@@ -119,19 +121,17 @@ const labels = computed(() => ({
119
121
 
120
122
  function hostApiOptions() {
121
123
  return {
122
- backendReady: isBackendReadyForApp(rootApp),
124
+ backendReady: isBackendReadyFromStore(hubStore),
123
125
  mode: CATALOG_MODE_DRAFT,
124
126
  }
125
127
  }
126
128
 
127
129
  async function reloadCatalog() {
128
- if (!rootApp) return
129
- await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
130
+ await appStore.loadCatalog(hostApi, hostApiOptions())
130
131
  }
131
132
 
132
133
  async function loadMore() {
133
- if (!rootApp) return
134
- await appStore.loadMoreCatalog(getHostApiForApp(rootApp), CATALOG_MODE_DRAFT, hostApiOptions())
134
+ await appStore.loadMoreCatalog(hostApi, CATALOG_MODE_DRAFT, hostApiOptions())
135
135
  }
136
136
 
137
137
  const { rootRef: scrollRoot, sentinelRef: scrollSentinel } = useCatalogInfiniteScroll({
@@ -150,7 +150,7 @@ async function onUninstall(app) {
150
150
  }
151
151
 
152
152
  async function onPing(app) {
153
- const api = getHostApiForApp(rootApp)
153
+ const api = hostApi
154
154
  if (!api?.ping || !app?.slug) return
155
155
  pingingSlug.value = app.slug
156
156
  delete pingResults[app.slug]