repluggable-core 0.0.0-pr351.71 → 0.0.0-pr351.73

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.
Files changed (37) hide show
  1. package/dist/es/src/repluggableAppDebug/index.d.ts +1 -1
  2. package/dist/es/src/repluggableAppDebug/index.d.ts.map +1 -1
  3. package/dist/es/src/repluggableAppDebug/index.js.map +1 -1
  4. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.d.ts +0 -20
  5. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.d.ts.map +1 -1
  6. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.js +13 -47
  7. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.js.map +1 -1
  8. package/dist/es/test/repluggableAppDebug.spec.js +7 -108
  9. package/dist/es/test/repluggableAppDebug.spec.js.map +1 -1
  10. package/dist/es/test/testKit.spec.js +5 -16
  11. package/dist/es/test/testKit.spec.js.map +1 -1
  12. package/dist/es/testKit/index.d.ts +1 -10
  13. package/dist/es/testKit/index.d.ts.map +1 -1
  14. package/dist/es/testKit/index.js +11 -54
  15. package/dist/es/testKit/index.js.map +1 -1
  16. package/dist/src/repluggableAppDebug/index.d.ts +1 -1
  17. package/dist/src/repluggableAppDebug/index.d.ts.map +1 -1
  18. package/dist/src/repluggableAppDebug/index.js.map +1 -1
  19. package/dist/src/repluggableAppDebug/repluggableAppDebug.d.ts +0 -20
  20. package/dist/src/repluggableAppDebug/repluggableAppDebug.d.ts.map +1 -1
  21. package/dist/src/repluggableAppDebug/repluggableAppDebug.js +13 -47
  22. package/dist/src/repluggableAppDebug/repluggableAppDebug.js.map +1 -1
  23. package/dist/test/repluggableAppDebug.spec.js +7 -108
  24. package/dist/test/repluggableAppDebug.spec.js.map +1 -1
  25. package/dist/test/testKit.spec.js +4 -15
  26. package/dist/test/testKit.spec.js.map +1 -1
  27. package/dist/testKit/index.d.ts +1 -10
  28. package/dist/testKit/index.d.ts.map +1 -1
  29. package/dist/testKit/index.js +12 -56
  30. package/dist/testKit/index.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/repluggableAppDebug/debug.d.ts +2 -16
  33. package/src/repluggableAppDebug/index.ts +1 -1
  34. package/src/repluggableAppDebug/repluggableAppDebug.ts +16 -74
  35. package/test/repluggableAppDebug.spec.ts +7 -134
  36. package/test/testKit.spec.tsx +6 -19
  37. package/testKit/index.ts +19 -66
@@ -1 +1 @@
1
- export { setupDebugInfo, RootUnreadyAPIInfo, UnreadyEntryPointsReport } from './repluggableAppDebug'
1
+ export { setupDebugInfo } from './repluggableAppDebug'
@@ -36,77 +36,6 @@ function mapApiToEntryPoint(allPackages: EntryPoint[]) {
36
36
  return apiToEntryPoint
37
37
  }
38
38
 
39
- export interface RootUnreadyAPIInfo {
40
- key: AnySlotKey
41
- /**
42
- * 'nobody' -> not declared by any entry point in the host (missing package / pact)
43
- * otherwise -> declared by an INSTALLED shell whose contributeAPI never completed (hung async contribution)
44
- */
45
- declaredBy: 'nobody' | { entryPointName: string }
46
- /** names of unready entry points directly blocked on this API */
47
- requiredBy: string[]
48
- }
49
-
50
- export interface UnreadyEntryPointsReport {
51
- unreadyEntryPoints: EntryPoint[]
52
- rootUnreadyAPIs: RootUnreadyAPIInfo[]
53
- /** unready entry points directly blocked on a root unready API */
54
- rootUnreadyEntryPoints: EntryPoint[]
55
- /** true when entry points are unready but no root unready API exists - their dependencies form a cycle */
56
- probableDependencyCycle: boolean
57
- }
58
-
59
- type UnreadyReportParams = Pick<SetupDebugInfoParams, 'readyAPIs' | 'getOwnSlotKey' | 'getUnreadyEntryPoints' | 'addedShells'>
60
-
61
- /**
62
- * Computes all root unready APIs at once: dependencies of unready entry points that are neither ready
63
- * nor declared by another unready entry point (i.e. the APIs actually blocking the host from loading).
64
- */
65
- const createGetUnreadyEntryPointsReport = ({ readyAPIs, getOwnSlotKey, getUnreadyEntryPoints, addedShells }: UnreadyReportParams) => {
66
- return (): UnreadyEntryPointsReport => {
67
- const unreadyEntryPoints = getUnreadyEntryPoints()
68
- const isReady = (key: AnySlotKey) => readyAPIs.has(getOwnSlotKey(key as SlotKey<any>))
69
-
70
- const declaredByUnready = new Set<string>()
71
- unreadyEntryPoints.forEach(entryPoint => entryPoint.declareAPIs?.().forEach(key => declaredByUnready.add(key.name)))
72
-
73
- const declaredByInstalled = new Map<string, string>()
74
- for (const shell of addedShells.values()) {
75
- shell.entryPoint.declareAPIs?.().forEach(key => declaredByInstalled.set(key.name, shell.entryPoint.name))
76
- }
77
-
78
- const rootByName = new Map<string, RootUnreadyAPIInfo>()
79
- unreadyEntryPoints.forEach(entryPoint => {
80
- entryPoint.getDependencyAPIs?.().forEach(key => {
81
- if (isReady(key) || declaredByUnready.has(key.name)) {
82
- return
83
- }
84
- const declarerName = declaredByInstalled.get(key.name)
85
- const info = rootByName.get(key.name) ?? {
86
- key,
87
- declaredBy: declarerName ? { entryPointName: declarerName } : ('nobody' as const),
88
- requiredBy: []
89
- }
90
- info.requiredBy.push(entryPoint.name)
91
- rootByName.set(key.name, info)
92
- })
93
- })
94
-
95
- const rootUnreadyAPIs = [...rootByName.values()]
96
- const rootAPINames = new Set(rootByName.keys())
97
- const rootUnreadyEntryPoints = unreadyEntryPoints.filter(entryPoint =>
98
- entryPoint.getDependencyAPIs?.().some(key => rootAPINames.has(key.name))
99
- )
100
-
101
- return {
102
- unreadyEntryPoints,
103
- rootUnreadyAPIs,
104
- rootUnreadyEntryPoints,
105
- probableDependencyCycle: unreadyEntryPoints.length > 0 && rootUnreadyAPIs.length === 0
106
- }
107
- }
108
- }
109
-
110
39
  export type DependencyTree = {
111
40
  entryPoint: string
112
41
  deps: Array<{ api: string; subtree: DependencyTree | null }>
@@ -227,9 +156,22 @@ export function setupDebugInfo({
227
156
  }
228
157
  })
229
158
  },
230
- getUnreadyEntryPointsReport: createGetUnreadyEntryPointsReport({ readyAPIs, getOwnSlotKey, getUnreadyEntryPoints, addedShells }),
231
- /** @deprecated use getUnreadyEntryPointsReport() - this returns only the first of possibly many root unready APIs */
232
- getRootUnreadyAPI: (): AnySlotKey | undefined => utils.getUnreadyEntryPointsReport().rootUnreadyAPIs[0]?.key,
159
+ /**
160
+ * dependencies of unready entry points that are neither ready nor declared by another
161
+ * unready entry point - the APIs actually blocking the host from loading
162
+ * (missing entry point / pact, or a contribution that never completed)
163
+ */
164
+ getRootUnreadyAPIs: (): AnySlotKey[] => {
165
+ const unreadyEntryPoints = getUnreadyEntryPoints()
166
+ const declaredByUnready = new Set(unreadyEntryPoints.flatMap(ep => (ep.declareAPIs?.() || []).map(key => key.name)))
167
+ return _.uniqBy(
168
+ unreadyEntryPoints
169
+ .flatMap(ep => ep.getDependencyAPIs?.() || [])
170
+ .filter(key => !readyAPIs.has(getOwnSlotKey(key)) && !declaredByUnready.has(key.name)),
171
+ 'name'
172
+ )
173
+ },
174
+ getRootUnreadyAPI: () => utils.getRootUnreadyAPIs()[0],
233
175
  unReadyEntryPoints: (): EntryPoint[] => getUnreadyEntryPoints(),
234
176
  whyEntryPointUnready: (name: string) => {
235
177
  const unreadyEntryPoint = _.find(
@@ -246,53 +246,8 @@ describe('RepluggableAppDebug', () => {
246
246
  })
247
247
  })
248
248
 
249
- describe('getUnreadyEntryPointsReport', () => {
250
- const getReport = async () => {
251
- await new Promise(resolve => setTimeout(resolve, 0))
252
- return globalThis.repluggableAppDebug.utils.getUnreadyEntryPointsReport()
253
- }
254
-
255
- it('should report no unready entry points when all are loaded', async () => {
256
- createAppHost([
257
- {
258
- name: 'entryPoint',
259
- declareAPIs: () => []
260
- }
261
- ])
262
-
263
- const report = await getReport()
264
-
265
- expect(report).toEqual({
266
- unreadyEntryPoints: [],
267
- rootUnreadyAPIs: [],
268
- rootUnreadyEntryPoints: [],
269
- probableDependencyCycle: false
270
- })
271
- })
272
-
273
- it('should report all root unready APIs of independent unready clusters', async () => {
274
- createAppHost([
275
- {
276
- name: 'entryPoint A',
277
- getDependencyAPIs: () => [{ name: 'missing A' }]
278
- },
279
- {
280
- name: 'entryPoint B',
281
- getDependencyAPIs: () => [{ name: 'missing B' }]
282
- }
283
- ])
284
-
285
- const report = await getReport()
286
-
287
- expect(report.rootUnreadyAPIs).toEqual([
288
- { key: { name: 'missing A' }, declaredBy: 'nobody', requiredBy: ['entryPoint A'] },
289
- { key: { name: 'missing B' }, declaredBy: 'nobody', requiredBy: ['entryPoint B'] }
290
- ])
291
- expect(report.rootUnreadyEntryPoints.map(entryPoint => entryPoint.name)).toEqual(['entryPoint A', 'entryPoint B'])
292
- expect(report.probableDependencyCycle).toBe(false)
293
- })
294
-
295
- it('should report only the root API of a transitively blocked chain', async () => {
249
+ describe('getRootUnreadyAPIs', () => {
250
+ it('should return all root unready APIs, excluding transitively blocked ones', async () => {
296
251
  createAppHost([
297
252
  {
298
253
  name: 'entryPoint A',
@@ -301,98 +256,16 @@ describe('RepluggableAppDebug', () => {
301
256
  {
302
257
  name: 'entryPoint B',
303
258
  declareAPIs: () => [{ name: 'API B' }],
304
- getDependencyAPIs: () => [unreadAPI]
305
- }
306
- ])
307
-
308
- const report = await getReport()
309
-
310
- expect(report.rootUnreadyAPIs).toEqual([{ key: unreadAPI, declaredBy: 'nobody', requiredBy: ['entryPoint B'] }])
311
- expect(report.rootUnreadyEntryPoints.map(entryPoint => entryPoint.name)).toEqual(['entryPoint B'])
312
- expect(report.unreadyEntryPoints.map(entryPoint => entryPoint.name)).toEqual(['entryPoint A', 'entryPoint B'])
313
- })
314
-
315
- it('should report roots of sibling branches, including APIs declared by an installed shell that never contributed them', async () => {
316
- createAppHost([
317
- {
318
- name: 'entryPoint 0',
319
- getDependencyAPIs: () => [{ name: 'missing API' }, { name: 'API B' }]
320
- },
321
- {
322
- name: 'entryPoint 1',
323
- declareAPIs: () => [{ name: 'API B' }]
324
- }
325
- ])
326
-
327
- const report = await getReport()
328
-
329
- expect(report.rootUnreadyAPIs).toEqual([
330
- { key: { name: 'missing API' }, declaredBy: 'nobody', requiredBy: ['entryPoint 0'] },
331
- { key: { name: 'API B' }, declaredBy: { entryPointName: 'entryPoint 1' }, requiredBy: ['entryPoint 0'] }
332
- ])
333
- })
334
-
335
- it('should aggregate all entry points blocked on the same root API', async () => {
336
- createAppHost([
337
- {
338
- name: 'entryPoint A',
339
- getDependencyAPIs: () => [unreadAPI]
259
+ getDependencyAPIs: () => [{ name: 'missing B' }]
340
260
  },
341
261
  {
342
- name: 'entryPoint B',
343
- getDependencyAPIs: () => [unreadAPI]
262
+ name: 'entryPoint C',
263
+ getDependencyAPIs: () => [{ name: 'missing C' }]
344
264
  }
345
265
  ])
266
+ await new Promise(resolve => setTimeout(resolve, 0))
346
267
 
347
- const report = await getReport()
348
-
349
- expect(report.rootUnreadyAPIs).toEqual([{ key: unreadAPI, declaredBy: 'nobody', requiredBy: ['entryPoint A', 'entryPoint B'] }])
350
- })
351
-
352
- it('should flag a probable dependency cycle when unready entry points have no root unready API', async () => {
353
- createAppHost(
354
- [
355
- {
356
- name: 'entryPoint A',
357
- declareAPIs: () => [{ name: 'API X' }],
358
- getDependencyAPIs: () => [{ name: 'API Y' }]
359
- },
360
- {
361
- name: 'entryPoint B',
362
- declareAPIs: () => [{ name: 'API Y' }],
363
- getDependencyAPIs: () => [{ name: 'API X' }]
364
- }
365
- ],
366
- { monitoring: {}, disableCheckCircularDependencies: true }
367
- )
368
-
369
- const report = await getReport()
370
-
371
- expect(report.rootUnreadyAPIs).toEqual([])
372
- expect(report.unreadyEntryPoints.map(entryPoint => entryPoint.name)).toEqual(['entryPoint A', 'entryPoint B'])
373
- expect(report.probableDependencyCycle).toBe(true)
268
+ expect(globalThis.repluggableAppDebug.utils.getRootUnreadyAPIs()).toEqual([{ name: 'missing B' }, { name: 'missing C' }])
374
269
  })
375
-
376
- it('should not hang getRootUnreadyAPI on a dependency cycle', async () => {
377
- createAppHost(
378
- [
379
- {
380
- name: 'entryPoint A',
381
- declareAPIs: () => [{ name: 'API X' }],
382
- getDependencyAPIs: () => [{ name: 'API Y' }]
383
- },
384
- {
385
- name: 'entryPoint B',
386
- declareAPIs: () => [{ name: 'API Y' }],
387
- getDependencyAPIs: () => [{ name: 'API X' }]
388
- }
389
- ],
390
- { monitoring: {}, disableCheckCircularDependencies: true }
391
- )
392
-
393
- const unreadyAPIs = await getUnreadyAPIs()
394
-
395
- expect(unreadyAPIs).toBeUndefined()
396
- }, 3000)
397
270
  })
398
271
  })
@@ -12,8 +12,7 @@ import {
12
12
  asyncLoadMockPackage,
13
13
  dependsOnMockPackageEntryPoint,
14
14
  MockPublicAPI,
15
- createAppHostAndWaitForLoading,
16
- AppHostLoadingTimeoutError
15
+ createAppHostAndWaitForLoading
17
16
  } from '../testKit'
18
17
 
19
18
  interface APIKeys {
@@ -148,10 +147,10 @@ describe('App Host TestKit', () => {
148
147
  it('should throw if failed to load all packages', async () => {
149
148
  const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint], [])
150
149
  jest.runAllTimers()
151
- await expect(hostPromise).rejects.toThrow(new RegExp(MockAPI.name))
150
+ await expect(hostPromise).rejects.toThrow(new RegExp(MockPublicAPI.name))
152
151
  })
153
152
 
154
- it('should reject with a structured report of all root unready APIs', async () => {
153
+ it('should report all root unready APIs of independent unready entry points', async () => {
155
154
  const hostPromise = createAppHostAndWaitForLoading(
156
155
  [
157
156
  {
@@ -168,24 +167,20 @@ describe('App Host TestKit', () => {
168
167
  []
169
168
  )
170
169
  jest.runAllTimers()
171
- const error: AppHostLoadingTimeoutError = await hostPromise.then(
170
+ const error: Error = await hostPromise.then(
172
171
  () => {
173
172
  throw new Error('expected hostPromise to reject')
174
173
  },
175
174
  e => e
176
175
  )
177
176
 
178
- expect(error).toBeInstanceOf(AppHostLoadingTimeoutError)
179
- expect(error.report.rootUnreadyAPIs.map(info => info.key.name)).toEqual(['missing A', 'missing B'])
180
177
  expect(error.message).toContain('"missing A"')
181
178
  expect(error.message).toContain('"missing B"')
182
- expect(error.message).toContain('required by: entryPoint A')
183
- expect(error.message).toContain('required by: entryPoint B')
184
179
  })
185
180
 
186
181
  it('should respect a custom loading timeout', async () => {
187
182
  let settled = false
188
- const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint], [], { timeout: 10000 })
183
+ const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint], [], 10000)
189
184
  hostPromise.catch(() => (settled = true))
190
185
 
191
186
  jest.advanceTimersByTime(9999)
@@ -194,15 +189,7 @@ describe('App Host TestKit', () => {
194
189
  expect(settled).toBe(false)
195
190
 
196
191
  jest.advanceTimersByTime(1)
197
- await expect(hostPromise).rejects.toBeInstanceOf(AppHostLoadingTimeoutError)
198
- })
199
-
200
- it('should not leave a pending timeout after successful loading', async () => {
201
- const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint, asyncLoadMockPackage], [])
202
- jest.advanceTimersByTime(500)
203
- const host = await hostPromise
204
- expect(host.getAPI(MockPublicAPI)).toBeDefined()
205
- expect(jest.getTimerCount()).toBe(0)
192
+ await expect(hostPromise).rejects.toThrow('timed out after 10000ms')
206
193
  })
207
194
  })
208
195
  })
package/testKit/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import _ from 'lodash'
2
2
  import { EntryPoint, ObservableState } from '../src/API'
3
3
  import { AnySlotKey, AppHost, createAppHost as _createAppHost, EntryPointOrPackage, Shell, SlotKey } from '../src/index'
4
- import { UnreadyEntryPointsReport } from '../src/repluggableAppDebug'
5
4
  import { createShellLogger } from '../src/loggers'
6
5
  import { emptyLoggerOptions } from './emptyLoggerOptions'
7
6
 
@@ -82,60 +81,10 @@ export function createAppHostWithPacts(packages: EntryPointOrPackage[], pacts: P
82
81
  })
83
82
  }
84
83
 
85
- export interface WaitForLoadingOptions {
86
- /** milliseconds to wait for host loading before rejecting with an unready entry points report (default: 3000) */
87
- timeout?: number
88
- }
89
-
90
- export class AppHostLoadingTimeoutError extends Error {
91
- constructor(message: string, public readonly report: UnreadyEntryPointsReport) {
92
- super(message)
93
- this.name = 'AppHostLoadingTimeoutError'
94
- }
95
- }
96
-
97
- const WAIT_FOR_LOADING_PROBE_SHELL = 'Depends on all declared APIs'
98
-
99
- const formatUnreadyReport = (timeout: number, report: UnreadyEntryPointsReport): string => {
100
- const { utils } = globalThis.repluggableAppDebug
101
- const isProbeShell = (name: string) => name === WAIT_FOR_LOADING_PROBE_SHELL
102
- const header = `createAppHostAndWaitForLoading - host not ready within ${timeout}ms.`
103
- const unreadyEntryPoints = report.unreadyEntryPoints.filter(entryPoint => !isProbeShell(entryPoint.name))
104
- const rootUnreadyEntryPoints = report.rootUnreadyEntryPoints.filter(entryPoint => !isProbeShell(entryPoint.name))
105
-
106
- if (report.probableDependencyCycle) {
107
- const names = unreadyEntryPoints.map(entryPoint => `"${entryPoint.name}"`).join(', ')
108
- return `${header}
109
- No root unready API found while ${unreadyEntryPoints.length} entry points are unready - probable dependency cycle between: ${names}`
110
- }
111
-
112
- const roots = report.rootUnreadyAPIs.map((info, index) => {
113
- const declaredBy =
114
- info.declaredBy === 'nobody'
115
- ? 'declared by NO entry point (missing package or pact?)'
116
- : `declared by "${info.declaredBy.entryPointName}" (installed, but its contribution never completed - hung async contributeAPI?)`
117
- const requiredBy = info.requiredBy.filter(name => !isProbeShell(name))
118
- const lines = [`${index + 1}. "${info.key.name}" - ${declaredBy}`]
119
- if (requiredBy.length) {
120
- lines.push(` required by: ${requiredBy.join(', ')}`)
121
- const tree = utils.visualizeDependencyTree(utils.traceAPIDependency(requiredBy[0], info.key.name))
122
- lines.push(...tree.split('\n').map(line => ` ${line}`))
123
- }
124
- return lines.join('\n')
125
- })
126
-
127
- return `${header}
128
-
129
- Root unready APIs (${report.rootUnreadyAPIs.length}):
130
- ${roots.join('\n')}
131
-
132
- Unready entry points: ${rootUnreadyEntryPoints.length} blocked directly on root APIs, ${unreadyEntryPoints.length} total.`
133
- }
134
-
135
84
  export async function createAppHostAndWaitForLoading(
136
85
  packages: EntryPointOrPackage[],
137
86
  pacts: PactAPIBase[],
138
- { timeout = 3000 }: WaitForLoadingOptions = {}
87
+ timeout: number = 3000
139
88
  ): Promise<AppHost> {
140
89
  const appHost = createAppHostWithPacts(packages, pacts)
141
90
  const declaredAPIs = _(packages)
@@ -143,18 +92,29 @@ export async function createAppHostAndWaitForLoading(
143
92
  .value()
144
93
  .flatMap((entryPoint: EntryPoint) => (entryPoint.declareAPIs ? entryPoint.declareAPIs() : []))
145
94
 
146
- let timer: ReturnType<typeof setTimeout> | undefined
147
- const timeoutPromise = new Promise<never>((resolve, reject) => {
148
- timer = setTimeout(() => {
149
- const report = globalThis.repluggableAppDebug.utils.getUnreadyEntryPointsReport()
150
- reject(new AppHostLoadingTimeoutError(formatUnreadyReport(timeout, report), report))
95
+ const timeoutPromise = new Promise((resolve, reject) => {
96
+ setTimeout(() => {
97
+ const readyAPIs = Array.from(globalThis.repluggableAppDebug.readyAPIs)
98
+ const unreadyAPIs = declaredAPIs.filter(api => !readyAPIs.some(readyAPI => readyAPI.name === api.name))
99
+ const rootUnreadyAPIs = globalThis.repluggableAppDebug.utils.getRootUnreadyAPIs()
100
+
101
+ reject(
102
+ new Error(
103
+ `createAppHostAndWaitForLoading - waiting for loading timed out after ${timeout}ms.
104
+ these root unready APIs are most likely the reason - unready entry points require them but nothing declares them (missing entry point or pact?): ${JSON.stringify(
105
+ rootUnreadyAPIs
106
+ )}
107
+
108
+ in addition here's the full list of declared APIs that have not been contributed: ${JSON.stringify(unreadyAPIs)}`
109
+ )
110
+ )
151
111
  }, timeout)
152
112
  })
153
113
 
154
114
  const loadingPromise = new Promise<void>(async resolve => {
155
115
  await appHost.addShells([
156
116
  {
157
- name: WAIT_FOR_LOADING_PROBE_SHELL,
117
+ name: 'Depends on all declared APIs',
158
118
  getDependencyAPIs() {
159
119
  return declaredAPIs
160
120
  },
@@ -165,14 +125,7 @@ export async function createAppHostAndWaitForLoading(
165
125
  ])
166
126
  })
167
127
 
168
- try {
169
- await Promise.race([timeoutPromise, loadingPromise])
170
- return appHost
171
- } finally {
172
- if (timer) {
173
- clearTimeout(timer)
174
- }
175
- }
128
+ return Promise.race([timeoutPromise, loadingPromise]).then(() => appHost)
176
129
  }
177
130
 
178
131
  type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>