@kennofizet/apphub-frontend 0.1.7 → 0.1.9
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 +1 -1
- package/src/composables/useAppHubHostApi.js +14 -3
- package/src/index.js +449 -507
- package/src/moduleStore.js +15 -2
- package/src/modules/app-store/components/AppHubAppStoreApp.vue +15 -40
- package/src/modules/app-store/components/AppHubDraftStoreApp.vue +11 -11
- package/src/modules/app-store/composables/useAppStore.js +0 -22
- package/src/utils/apphubDebug.js +0 -47
package/package.json
CHANGED
|
@@ -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
|
|
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
|
@@ -1,507 +1,449 @@
|
|
|
1
|
-
import './modules/desktop/styles/desktop.css'
|
|
2
|
-
import './modules/desktop/styles/theme.css'
|
|
3
|
-
import { reactive } from 'vue'
|
|
4
|
-
import { createAppHubApi } from './api/index.js'
|
|
5
|
-
import { createCoreApi } from './api/coreApi.js'
|
|
6
|
-
import { createZoneContextState } from './composables/createZoneContext.js'
|
|
7
|
-
import { APPHUB_ZONE_CONTEXT_KEY } from './composables/useAppHubZoneContext.js'
|
|
8
|
-
import { createAppStoreState, provideAppStore } from './modules/app-store/index.js'
|
|
9
|
-
import { AppHubDesktop } from './modules/desktop/index.js'
|
|
10
|
-
import { AppHubRunner } from './modules/runner/index.js'
|
|
11
|
-
import { getAppHubStore, registerAppHubStore } from './moduleStore.js'
|
|
12
|
-
import {
|
|
13
|
-
createWindowManagerState,
|
|
14
|
-
provideWindowManager,
|
|
15
|
-
} from './modules/window-manager/index.js'
|
|
16
|
-
import {
|
|
17
|
-
evaluateOriginSafety,
|
|
18
|
-
parseDevUserFromBootstrap,
|
|
19
|
-
parseOriginsFromBootstrap,
|
|
20
|
-
} from './utils/originSafety.js'
|
|
21
|
-
import { loadDevFriendlyOriginsPreference } from './utils/devOriginSettings.js'
|
|
22
|
-
import {
|
|
23
|
-
bootstrapResponseFromCache,
|
|
24
|
-
loadBootstrapCache,
|
|
25
|
-
saveBootstrapCache,
|
|
26
|
-
} from './utils/bootstrapCache.js'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
target.
|
|
80
|
-
target.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
store.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
store
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
export
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
store =
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
windowManager: null,
|
|
451
|
-
appStore: null,
|
|
452
|
-
}
|
|
453
|
-
registerAppHubStore(vueApp, store)
|
|
454
|
-
|
|
455
|
-
vueApp.provide('apphubOptions', moduleOptions)
|
|
456
|
-
vueApp.provide('apphubHostApp', vueApp)
|
|
457
|
-
vueApp.component('AppHubDesktop', AppHubDesktop)
|
|
458
|
-
vueApp.component('AppHubRunner', AppHubRunner)
|
|
459
|
-
|
|
460
|
-
ensureModuleInfrastructure(vueApp, store)
|
|
461
|
-
void startBootstrapSession(store)
|
|
462
|
-
|
|
463
|
-
return facade
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/** Whether installAppHubModule has been called for this Vue app instance. */
|
|
467
|
-
export function isAppHubModuleInstalled(vueApp) {
|
|
468
|
-
return vueApp != null && getAppHubStore(vueApp) != null
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
export { useAppHubHostApi } from './composables/useAppHubHostApi.js'
|
|
472
|
-
export { useAppHubZoneContext } from './composables/useAppHubZoneContext.js'
|
|
473
|
-
export { createAppHubApi } from './api/index.js'
|
|
474
|
-
export { createCoreApi } from './api/coreApi.js'
|
|
475
|
-
export { AppHubDesktop } from './modules/desktop/index.js'
|
|
476
|
-
export {
|
|
477
|
-
createDesktopNotificationsState,
|
|
478
|
-
useDesktopNotifications,
|
|
479
|
-
AppHubDesktopNotifications,
|
|
480
|
-
parseApiError,
|
|
481
|
-
} from './modules/notifications/index.js'
|
|
482
|
-
export { AppHubRunner } from './modules/runner/index.js'
|
|
483
|
-
export { resolveLang } from './i18n/resolveLang.js'
|
|
484
|
-
export { resolveTheme, normalizeTheme, isThemeLocked } from './i18n/resolveTheme.js'
|
|
485
|
-
export { t } from './i18n/index.js'
|
|
486
|
-
export {
|
|
487
|
-
evaluateOriginSafety,
|
|
488
|
-
isEmbeddedFrame,
|
|
489
|
-
isLocalDevOrigin,
|
|
490
|
-
parseOriginsFromBootstrap,
|
|
491
|
-
resolveEffectiveOrigins,
|
|
492
|
-
ORIGIN_UNSAFE_SAME_ORIGIN_EMBED,
|
|
493
|
-
ORIGIN_UNSAFE_NOT_CONFIGURED,
|
|
494
|
-
ORIGIN_UNSAFE_WRONG_ORIGIN,
|
|
495
|
-
ORIGIN_UNSAFE_RUNTIME_NOT_CONFIGURED,
|
|
496
|
-
ORIGIN_UNSAFE_RUNTIME_SAME_ORIGIN,
|
|
497
|
-
resolveRuntimeApiBase,
|
|
498
|
-
} from './utils/originSafety.js'
|
|
499
|
-
|
|
500
|
-
/** True when installAppHubModule blocked due to unsafe same-origin embed. */
|
|
501
|
-
export function isAppHubOriginBlocked(vueApp) {
|
|
502
|
-
const store = getAppHubStore(vueApp)
|
|
503
|
-
return store?.options?.originBlocked === true
|
|
504
|
-
}
|
|
505
|
-
export * from './modules/app-store/index.js'
|
|
506
|
-
export * from './modules/window-manager/index.js'
|
|
507
|
-
export * from './modules/runner/index.js'
|
|
1
|
+
import './modules/desktop/styles/desktop.css'
|
|
2
|
+
import './modules/desktop/styles/theme.css'
|
|
3
|
+
import { reactive } from 'vue'
|
|
4
|
+
import { createAppHubApi } from './api/index.js'
|
|
5
|
+
import { createCoreApi } from './api/coreApi.js'
|
|
6
|
+
import { createZoneContextState } from './composables/createZoneContext.js'
|
|
7
|
+
import { APPHUB_ZONE_CONTEXT_KEY } from './composables/useAppHubZoneContext.js'
|
|
8
|
+
import { createAppStoreState, provideAppStore } from './modules/app-store/index.js'
|
|
9
|
+
import { AppHubDesktop } from './modules/desktop/index.js'
|
|
10
|
+
import { AppHubRunner } from './modules/runner/index.js'
|
|
11
|
+
import { getAppHubStore, registerAppHubStore, APPHUB_MODULE_STORE_KEY } from './moduleStore.js'
|
|
12
|
+
import {
|
|
13
|
+
createWindowManagerState,
|
|
14
|
+
provideWindowManager,
|
|
15
|
+
} from './modules/window-manager/index.js'
|
|
16
|
+
import {
|
|
17
|
+
evaluateOriginSafety,
|
|
18
|
+
parseDevUserFromBootstrap,
|
|
19
|
+
parseOriginsFromBootstrap,
|
|
20
|
+
} from './utils/originSafety.js'
|
|
21
|
+
import { loadDevFriendlyOriginsPreference } from './utils/devOriginSettings.js'
|
|
22
|
+
import {
|
|
23
|
+
bootstrapResponseFromCache,
|
|
24
|
+
loadBootstrapCache,
|
|
25
|
+
saveBootstrapCache,
|
|
26
|
+
} from './utils/bootstrapCache.js'
|
|
27
|
+
|
|
28
|
+
const bootstrapInflight = new WeakMap()
|
|
29
|
+
|
|
30
|
+
function buildPublicOptions(options = {}) {
|
|
31
|
+
const origins = Array.isArray(options.allowedRuntimeOrigins)
|
|
32
|
+
? options.allowedRuntimeOrigins.filter((o) => typeof o === 'string')
|
|
33
|
+
: []
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
language: options.language || 'vi',
|
|
37
|
+
theme: options.theme ?? 'auto',
|
|
38
|
+
themeToggle: options.themeToggle,
|
|
39
|
+
openAppStoreOnMount: options.openAppStoreOnMount !== false,
|
|
40
|
+
allowedRuntimeOrigins: origins,
|
|
41
|
+
coreUrl: options.coreUrl || '',
|
|
42
|
+
backendUrl: (options.backendUrl || '').replace(/\/$/, ''),
|
|
43
|
+
hasToken: !!(options.token),
|
|
44
|
+
allowSameOriginEmbed: options.allowSameOriginEmbed === true,
|
|
45
|
+
allowUnsafeOrigin: options.allowUnsafeOrigin === true,
|
|
46
|
+
hubOrigin: typeof options.hubOrigin === 'string' ? options.hubOrigin.trim() : '',
|
|
47
|
+
runtimePublicUrl: typeof options.runtimePublicUrl === 'string' ? options.runtimePublicUrl.trim() : '',
|
|
48
|
+
enforceDedicatedHubOrigin: options.enforceDedicatedHubOrigin !== false,
|
|
49
|
+
enforceIsolatedHostedRuntime: options.enforceIsolatedHostedRuntime !== false,
|
|
50
|
+
allowSameOriginHostedRuntime: options.allowSameOriginHostedRuntime === true,
|
|
51
|
+
hostedSandboxSameOrigin: options.hostedSandboxSameOrigin === true,
|
|
52
|
+
enforceDevFriendlyOrigins: typeof options.enforceDevFriendlyOrigins === 'boolean'
|
|
53
|
+
? options.enforceDevFriendlyOrigins
|
|
54
|
+
: true,
|
|
55
|
+
isDevUser: options.isDevUser === true,
|
|
56
|
+
serverHubPublicUrl: typeof options.serverHubPublicUrl === 'string' ? options.serverHubPublicUrl.trim() : '',
|
|
57
|
+
serverFrontendOrigin: typeof options.serverFrontendOrigin === 'string' ? options.serverFrontendOrigin.trim() : '',
|
|
58
|
+
serverRuntimePublicUrl: typeof options.serverRuntimePublicUrl === 'string' ? options.serverRuntimePublicUrl.trim() : '',
|
|
59
|
+
serverOriginsAuto: options.serverOriginsAuto === true,
|
|
60
|
+
serverOriginsResolved: options.serverOriginsResolved === true,
|
|
61
|
+
originBootstrapLoading: options.originBootstrapLoading === true,
|
|
62
|
+
originBlocked: false,
|
|
63
|
+
originCheckPending: false,
|
|
64
|
+
originBlockReason: null,
|
|
65
|
+
originBlockParentOrigin: null,
|
|
66
|
+
originBlockExpectedHubOrigin: null,
|
|
67
|
+
originBlockExpectedRuntimeOrigin: null,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** @param {Record<string, unknown>} target */
|
|
72
|
+
function applyOriginSafety(target, sourceOptions = {}) {
|
|
73
|
+
const check = evaluateOriginSafety({ ...target, ...sourceOptions })
|
|
74
|
+
target.originBootstrapLoading = check.loading === true
|
|
75
|
+
target.originCheckPending = check.pending === true
|
|
76
|
+
target.originBlocked = !check.safe && check.loading !== true
|
|
77
|
+
target.originBlockReason = check.reason
|
|
78
|
+
target.originBlockParentOrigin = check.parentOrigin
|
|
79
|
+
target.originBlockExpectedHubOrigin = check.expectedHubOrigin
|
|
80
|
+
target.originBlockExpectedRuntimeOrigin = check.expectedRuntimeOrigin
|
|
81
|
+
return check
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseUserFromBootstrap(resp) {
|
|
85
|
+
const data = resp?.data?.data ?? resp?.data ?? {}
|
|
86
|
+
const user = data.user
|
|
87
|
+
if (!user || user.id == null) return null
|
|
88
|
+
return {
|
|
89
|
+
id: user.id,
|
|
90
|
+
name: user.name ?? String(user.id),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function applyBootstrapOrigins(store, bootstrapResponse, { fromCache = false } = {}) {
|
|
95
|
+
const {
|
|
96
|
+
hubPublicUrl,
|
|
97
|
+
frontendOrigin,
|
|
98
|
+
runtimePublicUrl,
|
|
99
|
+
originsAuto,
|
|
100
|
+
} = parseOriginsFromBootstrap(bootstrapResponse)
|
|
101
|
+
|
|
102
|
+
store.options.isDevUser = parseDevUserFromBootstrap(bootstrapResponse)
|
|
103
|
+
store.options.serverOriginsAuto = originsAuto
|
|
104
|
+
|
|
105
|
+
if (!store.options.isDevUser) {
|
|
106
|
+
store.options.enforceDevFriendlyOrigins = true
|
|
107
|
+
} else {
|
|
108
|
+
store.options.enforceDevFriendlyOrigins = loadDevFriendlyOriginsPreference()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (hubPublicUrl) {
|
|
112
|
+
store.options.serverHubPublicUrl = hubPublicUrl
|
|
113
|
+
}
|
|
114
|
+
if (frontendOrigin) {
|
|
115
|
+
store.options.serverFrontendOrigin = frontendOrigin
|
|
116
|
+
}
|
|
117
|
+
if (runtimePublicUrl) {
|
|
118
|
+
store.options.serverRuntimePublicUrl = runtimePublicUrl
|
|
119
|
+
if (!store.options.runtimePublicUrl) store.options.runtimePublicUrl = runtimePublicUrl
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
store.options.serverOriginsResolved = true
|
|
123
|
+
|
|
124
|
+
if (!fromCache) {
|
|
125
|
+
saveBootstrapCache(store.credentials.backendUrl, bootstrapResponse)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const user = parseUserFromBootstrap(bootstrapResponse)
|
|
129
|
+
if (user && store.zoneContext?.state) {
|
|
130
|
+
store.zoneContext.state.user.id = user.id
|
|
131
|
+
store.zoneContext.state.user.name = user.name
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Reconcile while originBootstrapLoading may still be true so enableModuleApi runs
|
|
135
|
+
// after disableModuleServices (wasLoading must be captured before clearing the flag).
|
|
136
|
+
reconcileOriginSafety(store)
|
|
137
|
+
store.options.originBootstrapLoading = false
|
|
138
|
+
applyOriginSafety(store.options)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function applyCachedBootstrapIfAny(store) {
|
|
142
|
+
const cached = loadBootstrapCache(store.credentials.backendUrl)
|
|
143
|
+
if (!cached) return false
|
|
144
|
+
applyBootstrapOrigins(store, bootstrapResponseFromCache(cached), { fromCache: true })
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function fetchBootstrapSession(store) {
|
|
149
|
+
let promise = bootstrapInflight.get(store)
|
|
150
|
+
if (promise) return promise
|
|
151
|
+
|
|
152
|
+
promise = (async () => {
|
|
153
|
+
syncApi(store)
|
|
154
|
+
if (!store.facade?.bootstrap) return
|
|
155
|
+
|
|
156
|
+
const res = await store.facade.bootstrap()
|
|
157
|
+
if (res) applyBootstrapOrigins(store, res)
|
|
158
|
+
})().catch(() => {
|
|
159
|
+
store.options.originBootstrapLoading = false
|
|
160
|
+
if (!store.options.serverOriginsResolved) {
|
|
161
|
+
reconcileOriginSafety(store)
|
|
162
|
+
}
|
|
163
|
+
}).finally(() => {
|
|
164
|
+
bootstrapInflight.delete(store)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
bootstrapInflight.set(store, promise)
|
|
168
|
+
return promise
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function startBootstrapSession(store) {
|
|
172
|
+
const { backendUrl, token } = store.credentials
|
|
173
|
+
if (!backendUrl || !token) {
|
|
174
|
+
store.options.originBootstrapLoading = false
|
|
175
|
+
reconcileOriginSafety(store)
|
|
176
|
+
return Promise.resolve()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
syncApi(store)
|
|
180
|
+
applyOriginSafety(store.options)
|
|
181
|
+
|
|
182
|
+
const hadCache = applyCachedBootstrapIfAny(store)
|
|
183
|
+
if (!hadCache && !store.options.originBlocked) {
|
|
184
|
+
store.options.originBootstrapLoading = true
|
|
185
|
+
store.options.originBlocked = false
|
|
186
|
+
applyOriginSafety(store.options)
|
|
187
|
+
disableModuleServices(store)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return fetchBootstrapSession(store)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function reconcileOriginSafety(store) {
|
|
194
|
+
const wasBlocked = store.options.originBlocked === true
|
|
195
|
+
const wasLoading = store.options.originBootstrapLoading === true
|
|
196
|
+
applyOriginSafety(store.options)
|
|
197
|
+
|
|
198
|
+
if (store.options.originBootstrapLoading) {
|
|
199
|
+
disableModuleServices(store)
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (store.options.originBlocked && !wasBlocked) {
|
|
204
|
+
disableModuleServices(store)
|
|
205
|
+
} else if (!store.options.originBlocked && (wasBlocked || wasLoading)) {
|
|
206
|
+
enableModuleApi(store)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function disableModuleServices(store) {
|
|
211
|
+
store.facade.setImpl(null)
|
|
212
|
+
store.coreApi = null
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Window manager + app store — required even when origin is blocked (AppHubDesktop setup). */
|
|
216
|
+
function ensureModuleInfrastructure(vueApp, store) {
|
|
217
|
+
ensureZoneContext(vueApp, store)
|
|
218
|
+
ensureModuleState(vueApp, store)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function enableModuleApi(store) {
|
|
222
|
+
syncApi(store)
|
|
223
|
+
syncZoneContext(store)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function enableModuleServices(vueApp, store) {
|
|
227
|
+
ensureModuleInfrastructure(vueApp, store)
|
|
228
|
+
enableModuleApi(store)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function buildCredentials(options = {}) {
|
|
232
|
+
return {
|
|
233
|
+
coreUrl: options.coreUrl || '',
|
|
234
|
+
backendUrl: options.backendUrl || '',
|
|
235
|
+
token: options.token || '',
|
|
236
|
+
hostAccessSecret: options.hostAccessSecret || '',
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function createApiFacade() {
|
|
241
|
+
let impl = null
|
|
242
|
+
return {
|
|
243
|
+
setImpl(next) {
|
|
244
|
+
impl = next
|
|
245
|
+
},
|
|
246
|
+
hasImpl() {
|
|
247
|
+
return impl != null
|
|
248
|
+
},
|
|
249
|
+
bootstrap: (...args) => impl?.bootstrap?.(...args),
|
|
250
|
+
apps: (...args) => impl?.apps?.(...args),
|
|
251
|
+
launch: (...args) => impl?.launch?.(...args),
|
|
252
|
+
ping: (...args) => impl?.ping?.(...args),
|
|
253
|
+
verifyLaunchToken: (...args) => impl?.verifyLaunchToken?.(...args),
|
|
254
|
+
usage: (...args) => impl?.usage?.(...args),
|
|
255
|
+
devApps: (...args) => impl?.devApps?.(...args),
|
|
256
|
+
devInspectBundle: (...args) => impl?.devInspectBundle?.(...args),
|
|
257
|
+
devDisableApp: (...args) => impl?.devDisableApp?.(...args),
|
|
258
|
+
devSetAppStatus: (...args) => impl?.devSetAppStatus?.(...args),
|
|
259
|
+
registerApp: (...args) => impl?.registerApp?.(...args),
|
|
260
|
+
appVersions: (...args) => impl?.appVersions?.(...args),
|
|
261
|
+
integrationDocs: (...args) => impl?.integrationDocs?.(...args),
|
|
262
|
+
integrationDocsInternal: (...args) => impl?.integrationDocsInternal?.(...args),
|
|
263
|
+
grantBridgeScope: (...args) => impl?.grantBridgeScope?.(...args),
|
|
264
|
+
bridgeUser: (...args) => impl?.bridgeUser?.(...args),
|
|
265
|
+
bridgeDesktopMessage: (...args) => impl?.bridgeDesktopMessage?.(...args),
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function credentialsUnchanged(prev, next) {
|
|
270
|
+
return prev.backendUrl === next.backendUrl
|
|
271
|
+
&& prev.token === next.token
|
|
272
|
+
&& prev.coreUrl === next.coreUrl
|
|
273
|
+
&& prev.hostAccessSecret === next.hostAccessSecret
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function applyModuleOptions(store, options = {}) {
|
|
277
|
+
const prevCredentials = { ...store.credentials }
|
|
278
|
+
const nextCredentials = buildCredentials(options)
|
|
279
|
+
Object.assign(store.credentials, nextCredentials)
|
|
280
|
+
const credsUnchanged = credentialsUnchanged(prevCredentials, nextCredentials)
|
|
281
|
+
|
|
282
|
+
const nextPublic = buildPublicOptions({ ...options, token: options.token ?? store.credentials.token })
|
|
283
|
+
Object.assign(store.options, {
|
|
284
|
+
language: nextPublic.language,
|
|
285
|
+
theme: nextPublic.theme,
|
|
286
|
+
themeToggle: nextPublic.themeToggle,
|
|
287
|
+
openAppStoreOnMount: nextPublic.openAppStoreOnMount,
|
|
288
|
+
allowedRuntimeOrigins: nextPublic.allowedRuntimeOrigins,
|
|
289
|
+
coreUrl: nextPublic.coreUrl,
|
|
290
|
+
backendUrl: nextPublic.backendUrl,
|
|
291
|
+
hasToken: nextPublic.hasToken,
|
|
292
|
+
allowSameOriginEmbed: nextPublic.allowSameOriginEmbed,
|
|
293
|
+
allowUnsafeOrigin: nextPublic.allowUnsafeOrigin,
|
|
294
|
+
hubOrigin: nextPublic.hubOrigin,
|
|
295
|
+
runtimePublicUrl: nextPublic.runtimePublicUrl,
|
|
296
|
+
enforceDedicatedHubOrigin: nextPublic.enforceDedicatedHubOrigin,
|
|
297
|
+
enforceIsolatedHostedRuntime: nextPublic.enforceIsolatedHostedRuntime,
|
|
298
|
+
allowSameOriginHostedRuntime: nextPublic.allowSameOriginHostedRuntime,
|
|
299
|
+
hostedSandboxSameOrigin: nextPublic.hostedSandboxSameOrigin,
|
|
300
|
+
enforceDevFriendlyOrigins: nextPublic.enforceDevFriendlyOrigins,
|
|
301
|
+
})
|
|
302
|
+
applyOriginSafety(store.options, options)
|
|
303
|
+
if (store.credentials.backendUrl && store.credentials.token) {
|
|
304
|
+
if (credsUnchanged) {
|
|
305
|
+
reconcileOriginSafety(store)
|
|
306
|
+
} else {
|
|
307
|
+
void startBootstrapSession(store)
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
reconcileOriginSafety(store)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function syncApi(store) {
|
|
315
|
+
const { credentials, facade, zoneContext } = store
|
|
316
|
+
const api = credentials.backendUrl && credentials.token
|
|
317
|
+
? createAppHubApi(credentials.backendUrl, credentials.token, {
|
|
318
|
+
hostAccessSecret: credentials.hostAccessSecret,
|
|
319
|
+
getZoneHeaderId: () => zoneContext?.getZoneHeaderId?.() ?? null,
|
|
320
|
+
})
|
|
321
|
+
: null
|
|
322
|
+
facade.setImpl(api)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function syncCoreApi(store) {
|
|
326
|
+
const { credentials } = store
|
|
327
|
+
store.coreApi = credentials.coreUrl && credentials.token
|
|
328
|
+
? createCoreApi(credentials.coreUrl, credentials.token)
|
|
329
|
+
: null
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function ensureZoneContext(app, store) {
|
|
333
|
+
if (store.zoneContext) return
|
|
334
|
+
store.zoneContext = createZoneContextState(
|
|
335
|
+
() => store.coreApi,
|
|
336
|
+
() => store.facade,
|
|
337
|
+
{
|
|
338
|
+
ensureBootstrapSession: () => fetchBootstrapSession(store),
|
|
339
|
+
},
|
|
340
|
+
)
|
|
341
|
+
app.provide(APPHUB_ZONE_CONTEXT_KEY, store.zoneContext)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function syncZoneContext(store) {
|
|
345
|
+
syncCoreApi(store)
|
|
346
|
+
if (store.zoneContext) {
|
|
347
|
+
store.zoneContext.refresh({ skipBootstrap: true })
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function ensureModuleState(app, store) {
|
|
352
|
+
if (store.windowManager && store.appStore) {
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
store.windowManager = createWindowManagerState()
|
|
357
|
+
store.appStore = createAppStoreState()
|
|
358
|
+
provideWindowManager(app, store.windowManager)
|
|
359
|
+
provideAppStore(app, store.appStore)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Install App Hub — Windows desktop shell + modular apps (App Store default).
|
|
364
|
+
* Returns host API facade — keep in host app code, not publisher apps.
|
|
365
|
+
*/
|
|
366
|
+
export function installAppHubModule(vueApp, options = {}) {
|
|
367
|
+
let store = getAppHubStore(vueApp)
|
|
368
|
+
|
|
369
|
+
if (store) {
|
|
370
|
+
vueApp.provide('apphubHostApp', vueApp)
|
|
371
|
+
vueApp.provide(APPHUB_MODULE_STORE_KEY, store)
|
|
372
|
+
applyModuleOptions(store, options)
|
|
373
|
+
ensureModuleInfrastructure(vueApp, store)
|
|
374
|
+
reconcileOriginSafety(store)
|
|
375
|
+
return store.facade
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const facade = createApiFacade()
|
|
379
|
+
const moduleOptions = reactive(buildPublicOptions(options))
|
|
380
|
+
applyOriginSafety(moduleOptions, options)
|
|
381
|
+
const credentials = buildCredentials(options)
|
|
382
|
+
|
|
383
|
+
store = {
|
|
384
|
+
app: vueApp,
|
|
385
|
+
facade,
|
|
386
|
+
options: moduleOptions,
|
|
387
|
+
credentials,
|
|
388
|
+
coreApi: null,
|
|
389
|
+
zoneContext: null,
|
|
390
|
+
windowManager: null,
|
|
391
|
+
appStore: null,
|
|
392
|
+
}
|
|
393
|
+
registerAppHubStore(vueApp, store)
|
|
394
|
+
|
|
395
|
+
vueApp.provide('apphubOptions', moduleOptions)
|
|
396
|
+
vueApp.provide('apphubHostApp', vueApp)
|
|
397
|
+
vueApp.provide(APPHUB_MODULE_STORE_KEY, store)
|
|
398
|
+
vueApp.component('AppHubDesktop', AppHubDesktop)
|
|
399
|
+
vueApp.component('AppHubRunner', AppHubRunner)
|
|
400
|
+
|
|
401
|
+
ensureModuleInfrastructure(vueApp, store)
|
|
402
|
+
void startBootstrapSession(store)
|
|
403
|
+
|
|
404
|
+
return facade
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Whether installAppHubModule has been called for this Vue app instance. */
|
|
408
|
+
export function isAppHubModuleInstalled(vueApp) {
|
|
409
|
+
return vueApp != null && getAppHubStore(vueApp) != null
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export { useAppHubHostApi, useAppHubModuleStore } from './composables/useAppHubHostApi.js'
|
|
413
|
+
export { useAppHubZoneContext } from './composables/useAppHubZoneContext.js'
|
|
414
|
+
export { createAppHubApi } from './api/index.js'
|
|
415
|
+
export { createCoreApi } from './api/coreApi.js'
|
|
416
|
+
export { AppHubDesktop } from './modules/desktop/index.js'
|
|
417
|
+
export {
|
|
418
|
+
createDesktopNotificationsState,
|
|
419
|
+
useDesktopNotifications,
|
|
420
|
+
AppHubDesktopNotifications,
|
|
421
|
+
parseApiError,
|
|
422
|
+
} from './modules/notifications/index.js'
|
|
423
|
+
export { AppHubRunner } from './modules/runner/index.js'
|
|
424
|
+
export { resolveLang } from './i18n/resolveLang.js'
|
|
425
|
+
export { resolveTheme, normalizeTheme, isThemeLocked } from './i18n/resolveTheme.js'
|
|
426
|
+
export { t } from './i18n/index.js'
|
|
427
|
+
export {
|
|
428
|
+
evaluateOriginSafety,
|
|
429
|
+
isEmbeddedFrame,
|
|
430
|
+
isLocalDevOrigin,
|
|
431
|
+
parseOriginsFromBootstrap,
|
|
432
|
+
resolveEffectiveOrigins,
|
|
433
|
+
ORIGIN_UNSAFE_SAME_ORIGIN_EMBED,
|
|
434
|
+
ORIGIN_UNSAFE_NOT_CONFIGURED,
|
|
435
|
+
ORIGIN_UNSAFE_WRONG_ORIGIN,
|
|
436
|
+
ORIGIN_UNSAFE_RUNTIME_NOT_CONFIGURED,
|
|
437
|
+
ORIGIN_UNSAFE_RUNTIME_SAME_ORIGIN,
|
|
438
|
+
resolveRuntimeApiBase,
|
|
439
|
+
} from './utils/originSafety.js'
|
|
440
|
+
|
|
441
|
+
/** True when installAppHubModule blocked due to unsafe same-origin embed. */
|
|
442
|
+
export function isAppHubOriginBlocked(vueApp) {
|
|
443
|
+
const store = getAppHubStore(vueApp)
|
|
444
|
+
return store?.options?.originBlocked === true
|
|
445
|
+
}
|
|
446
|
+
export * from './modules/app-store/index.js'
|
|
447
|
+
export * from './modules/window-manager/index.js'
|
|
448
|
+
export * from './modules/runner/index.js'
|
|
449
|
+
|
package/src/moduleStore.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
const
|
|
1
|
+
/** Internal inject key — package 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,
|
|
100
|
+
import { computed, inject, onMounted, ref, watch } from 'vue'
|
|
101
101
|
import {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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,6 @@ 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'
|
|
113
112
|
import AppHubAppStoreCard from './AppHubAppStoreCard.vue'
|
|
114
113
|
import AppHubAppStoreSettingsPanel from './AppHubAppStoreSettingsPanel.vue'
|
|
115
114
|
|
|
@@ -123,8 +122,9 @@ const props = defineProps({
|
|
|
123
122
|
const settingsOpen = ref(false)
|
|
124
123
|
const appStore = useAppStore()
|
|
125
124
|
const catalog = appStore.catalogs.store
|
|
126
|
-
const
|
|
127
|
-
const
|
|
125
|
+
const hubStore = useAppHubModuleStore()
|
|
126
|
+
const hostApi = useAppHubHostApi()
|
|
127
|
+
const rootApp = inject('apphubHostApp', null)
|
|
128
128
|
const zone = useAppHubZoneContext()
|
|
129
129
|
const moduleOptions = inject('apphubOptions', {})
|
|
130
130
|
const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
|
|
@@ -154,35 +154,17 @@ const labels = computed(() => ({
|
|
|
154
154
|
|
|
155
155
|
function hostApiOptions() {
|
|
156
156
|
return {
|
|
157
|
-
backendReady:
|
|
157
|
+
backendReady: isBackendReadyFromStore(hubStore),
|
|
158
158
|
mode: CATALOG_MODE_STORE,
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
async function reloadCatalog(
|
|
163
|
-
|
|
164
|
-
apphubDebug('app-store', 'reloadCatalog skipped — no rootApp', { source, rootAppSource })
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
const hostApi = getHostApiForApp(rootApp)
|
|
168
|
-
const opts = hostApiOptions()
|
|
169
|
-
apphubDebug('app-store', 'reloadCatalog', {
|
|
170
|
-
source,
|
|
171
|
-
rootAppSource,
|
|
172
|
-
...describeApp(rootApp),
|
|
173
|
-
...describeHostApi(hostApi),
|
|
174
|
-
backendReady: opts.backendReady,
|
|
175
|
-
originBootstrapLoading: moduleOptions?.originBootstrapLoading,
|
|
176
|
-
originBlocked: moduleOptions?.originBlocked,
|
|
177
|
-
catalogLoaded: catalog.loaded,
|
|
178
|
-
catalogError: catalog.error,
|
|
179
|
-
})
|
|
180
|
-
await appStore.loadCatalog(hostApi, opts)
|
|
162
|
+
async function reloadCatalog() {
|
|
163
|
+
await appStore.loadCatalog(hostApi, hostApiOptions())
|
|
181
164
|
}
|
|
182
165
|
|
|
183
166
|
async function loadMore() {
|
|
184
|
-
|
|
185
|
-
await appStore.loadMoreCatalog(getHostApiForApp(rootApp), CATALOG_MODE_STORE, hostApiOptions())
|
|
167
|
+
await appStore.loadMoreCatalog(hostApi, CATALOG_MODE_STORE, hostApiOptions())
|
|
186
168
|
}
|
|
187
169
|
|
|
188
170
|
const { rootRef: scrollRoot, sentinelRef: scrollSentinel } = useCatalogInfiniteScroll({
|
|
@@ -209,28 +191,21 @@ async function onUninstall(app) {
|
|
|
209
191
|
}
|
|
210
192
|
|
|
211
193
|
onMounted(() => {
|
|
212
|
-
if (!catalog.loaded) reloadCatalog(
|
|
194
|
+
if (!catalog.loaded) reloadCatalog()
|
|
213
195
|
})
|
|
214
196
|
|
|
215
197
|
watch(
|
|
216
198
|
() => moduleOptions?.hasToken,
|
|
217
199
|
(hasToken) => {
|
|
218
|
-
if (hasToken && !catalog.loaded) reloadCatalog(
|
|
200
|
+
if (hasToken && !catalog.loaded) reloadCatalog()
|
|
219
201
|
},
|
|
220
202
|
)
|
|
221
203
|
|
|
222
204
|
watch(
|
|
223
205
|
() => moduleOptions?.originBootstrapLoading,
|
|
224
206
|
(loading, wasLoading) => {
|
|
225
|
-
apphubDebug('app-store', 'watch originBootstrapLoading', {
|
|
226
|
-
loading,
|
|
227
|
-
wasLoading,
|
|
228
|
-
originBlocked: moduleOptions?.originBlocked,
|
|
229
|
-
catalogLoaded: catalog.loaded,
|
|
230
|
-
catalogError: catalog.error,
|
|
231
|
-
})
|
|
232
207
|
if (wasLoading && !loading && !moduleOptions?.originBlocked) {
|
|
233
|
-
if (!catalog.loaded || catalog.error === 'no_api') reloadCatalog(
|
|
208
|
+
if (!catalog.loaded || catalog.error === 'no_api') reloadCatalog()
|
|
234
209
|
}
|
|
235
210
|
},
|
|
236
211
|
)
|
|
@@ -238,7 +213,7 @@ watch(
|
|
|
238
213
|
watch(
|
|
239
214
|
() => [zone?.state?.selectedZoneId, zone?.state?.viewAllZones],
|
|
240
215
|
() => {
|
|
241
|
-
if (moduleOptions?.hasToken) reloadCatalog(
|
|
216
|
+
if (moduleOptions?.hasToken) reloadCatalog()
|
|
242
217
|
},
|
|
243
218
|
)
|
|
244
219
|
</script>
|
|
@@ -62,11 +62,11 @@
|
|
|
62
62
|
</template>
|
|
63
63
|
|
|
64
64
|
<script setup>
|
|
65
|
-
import { computed,
|
|
65
|
+
import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
|
|
66
66
|
import {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
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:
|
|
124
|
+
backendReady: isBackendReadyFromStore(hubStore),
|
|
123
125
|
mode: CATALOG_MODE_DRAFT,
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
async function reloadCatalog() {
|
|
128
|
-
|
|
129
|
-
await appStore.loadCatalog(getHostApiForApp(rootApp), hostApiOptions())
|
|
130
|
+
await appStore.loadCatalog(hostApi, hostApiOptions())
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
async function loadMore() {
|
|
133
|
-
|
|
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 =
|
|
153
|
+
const api = hostApi
|
|
154
154
|
if (!api?.ping || !app?.slug) return
|
|
155
155
|
pingingSlug.value = app.slug
|
|
156
156
|
delete pingResults[app.slug]
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { computed, inject, reactive } from 'vue'
|
|
2
2
|
import { CATALOG_MODE_DRAFT, CATALOG_MODE_STORE } from '../constants/catalogModes.js'
|
|
3
3
|
import { normalizeCatalogList } from '../utils/normalizeCatalogApp.js'
|
|
4
|
-
import { apphubDebug, describeHostApi } from '../../../utils/apphubDebug.js'
|
|
5
4
|
|
|
6
5
|
const APP_STORE_KEY = 'apphubAppStore'
|
|
7
6
|
|
|
@@ -109,20 +108,7 @@ export function createAppStoreState(options = {}) {
|
|
|
109
108
|
const append = options.append === true
|
|
110
109
|
const backendReady = options.backendReady !== false
|
|
111
110
|
|
|
112
|
-
apphubDebug('catalog', 'loadCatalog called', {
|
|
113
|
-
mode,
|
|
114
|
-
append,
|
|
115
|
-
backendReady,
|
|
116
|
-
...describeHostApi(hostApi),
|
|
117
|
-
})
|
|
118
|
-
|
|
119
111
|
if (!backendReady || !hostApiReady(hostApi)) {
|
|
120
|
-
apphubDebug('catalog', 'loadCatalog → no_api (not ready)', {
|
|
121
|
-
mode,
|
|
122
|
-
append,
|
|
123
|
-
backendReady,
|
|
124
|
-
...describeHostApi(hostApi),
|
|
125
|
-
})
|
|
126
112
|
if (!append) {
|
|
127
113
|
bucket.items = []
|
|
128
114
|
bucket.error = 'no_api'
|
|
@@ -151,16 +137,8 @@ export function createAppStoreState(options = {}) {
|
|
|
151
137
|
}
|
|
152
138
|
|
|
153
139
|
const res = await hostApi.apps(params)
|
|
154
|
-
apphubDebug('catalog', 'loadCatalog apps() response', {
|
|
155
|
-
mode,
|
|
156
|
-
append,
|
|
157
|
-
...describeHostApi(hostApi),
|
|
158
|
-
resUndefined: res === undefined,
|
|
159
|
-
resNull: res === null,
|
|
160
|
-
})
|
|
161
140
|
if (res === undefined || res === null) {
|
|
162
141
|
if (!append) {
|
|
163
|
-
apphubDebug('catalog', 'loadCatalog → no_api (undefined response)', { mode })
|
|
164
142
|
bucket.items = []
|
|
165
143
|
bucket.error = 'no_api'
|
|
166
144
|
bucket.loaded = false
|
package/src/utils/apphubDebug.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/** Temporary diagnostics — filter console with `[apphub:debug]`. Disable: localStorage.setItem('apphub:debug','0') */
|
|
2
|
-
export function isAppHubDebugEnabled() {
|
|
3
|
-
try {
|
|
4
|
-
if (typeof localStorage !== 'undefined' && localStorage.getItem('apphub:debug') === '0') {
|
|
5
|
-
return false
|
|
6
|
-
}
|
|
7
|
-
if (typeof window !== 'undefined' && window.__APPHUB_DEBUG__ === false) {
|
|
8
|
-
return false
|
|
9
|
-
}
|
|
10
|
-
} catch {
|
|
11
|
-
/* ignore */
|
|
12
|
-
}
|
|
13
|
-
return true
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function apphubDebug(scope, message, data) {
|
|
17
|
-
if (!isAppHubDebugEnabled()) return
|
|
18
|
-
const payload = data !== undefined ? { ts: Date.now(), ...data } : { ts: Date.now() }
|
|
19
|
-
console.info(`[apphub:debug] ${scope}: ${message}`, payload)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function describeHostApi(hostApi) {
|
|
23
|
-
if (!hostApi) return { hostApi: null }
|
|
24
|
-
return {
|
|
25
|
-
hasImpl: typeof hostApi.hasImpl === 'function' ? hostApi.hasImpl() : null,
|
|
26
|
-
hasAppsFn: typeof hostApi.apps === 'function',
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function describeStore(store) {
|
|
31
|
-
if (!store) return { store: null }
|
|
32
|
-
return {
|
|
33
|
-
hasImpl: store.facade?.hasImpl?.() ?? null,
|
|
34
|
-
originBootstrapLoading: store.options?.originBootstrapLoading ?? null,
|
|
35
|
-
originBlocked: store.options?.originBlocked ?? null,
|
|
36
|
-
originBlockReason: store.options?.originBlockReason ?? null,
|
|
37
|
-
hasToken: store.options?.hasToken ?? null,
|
|
38
|
-
backendUrl: store.credentials?.backendUrl ? '(set)' : '(empty)',
|
|
39
|
-
token: store.credentials?.token ? `(len ${store.credentials.token.length})` : '(empty)',
|
|
40
|
-
serverOriginsResolved: store.options?.serverOriginsResolved ?? null,
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function describeApp(app) {
|
|
45
|
-
if (!app) return { app: null }
|
|
46
|
-
return { appUid: app._uid ?? null }
|
|
47
|
-
}
|