repluggable-core 0.0.0-pr349.66 → 0.0.0-pr351.71

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 +20 -0
  5. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.d.ts.map +1 -1
  6. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.js +41 -42
  7. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.js.map +1 -1
  8. package/dist/es/test/repluggableAppDebug.spec.js +122 -0
  9. package/dist/es/test/repluggableAppDebug.spec.js.map +1 -1
  10. package/dist/es/test/testKit.spec.js +44 -2
  11. package/dist/es/test/testKit.spec.js.map +1 -1
  12. package/dist/es/testKit/index.d.ts +10 -1
  13. package/dist/es/testKit/index.d.ts.map +1 -1
  14. package/dist/es/testKit/index.js +56 -13
  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 +20 -0
  20. package/dist/src/repluggableAppDebug/repluggableAppDebug.d.ts.map +1 -1
  21. package/dist/src/repluggableAppDebug/repluggableAppDebug.js +41 -42
  22. package/dist/src/repluggableAppDebug/repluggableAppDebug.js.map +1 -1
  23. package/dist/test/repluggableAppDebug.spec.js +122 -0
  24. package/dist/test/repluggableAppDebug.spec.js.map +1 -1
  25. package/dist/test/testKit.spec.js +43 -1
  26. package/dist/test/testKit.spec.js.map +1 -1
  27. package/dist/testKit/index.d.ts +10 -1
  28. package/dist/testKit/index.d.ts.map +1 -1
  29. package/dist/testKit/index.js +58 -14
  30. package/dist/testKit/index.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/repluggableAppDebug/debug.d.ts +16 -1
  33. package/src/repluggableAppDebug/index.ts +1 -1
  34. package/src/repluggableAppDebug/repluggableAppDebug.ts +65 -43
  35. package/test/repluggableAppDebug.spec.ts +150 -0
  36. package/test/testKit.spec.tsx +57 -2
  37. package/testKit/index.ts +71 -18
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.mockObservable = exports.addMockShell = exports.createAppHostAndWaitForLoading = exports.createAppHostWithPacts = exports.getPackagesDependencies = exports.createAppHost = exports.createSignalItemsDataStructure = exports.withThrowOnError = exports.withConsoleErrors = exports.emptyLoggerOptions = exports.createShellLogger = void 0;
29
+ exports.mockObservable = exports.addMockShell = exports.createAppHostAndWaitForLoading = exports.AppHostLoadingTimeoutError = exports.createAppHostWithPacts = exports.getPackagesDependencies = exports.createAppHost = exports.createSignalItemsDataStructure = exports.withThrowOnError = exports.withConsoleErrors = exports.emptyLoggerOptions = exports.createShellLogger = void 0;
30
30
  const lodash_1 = __importDefault(require("lodash"));
31
31
  const index_1 = require("../src/index");
32
32
  const loggers_1 = require("../src/loggers");
@@ -88,28 +88,64 @@ function createAppHostWithPacts(packages, pacts) {
88
88
  return (0, exports.createAppHost)([...packages, pactsEntryPoint], Object.assign(Object.assign({}, emptyLoggerOptions_1.emptyLoggerOptions), { disableLayersValidation: true }));
89
89
  }
90
90
  exports.createAppHostWithPacts = createAppHostWithPacts;
91
- function createAppHostAndWaitForLoading(packages, pacts) {
92
- return __awaiter(this, void 0, void 0, function* () {
91
+ class AppHostLoadingTimeoutError extends Error {
92
+ constructor(message, report) {
93
+ super(message);
94
+ this.report = report;
95
+ this.name = 'AppHostLoadingTimeoutError';
96
+ }
97
+ }
98
+ exports.AppHostLoadingTimeoutError = AppHostLoadingTimeoutError;
99
+ const WAIT_FOR_LOADING_PROBE_SHELL = 'Depends on all declared APIs';
100
+ const formatUnreadyReport = (timeout, report) => {
101
+ const { utils } = globalThis.repluggableAppDebug;
102
+ const isProbeShell = (name) => name === WAIT_FOR_LOADING_PROBE_SHELL;
103
+ const header = `createAppHostAndWaitForLoading - host not ready within ${timeout}ms.`;
104
+ const unreadyEntryPoints = report.unreadyEntryPoints.filter(entryPoint => !isProbeShell(entryPoint.name));
105
+ const rootUnreadyEntryPoints = report.rootUnreadyEntryPoints.filter(entryPoint => !isProbeShell(entryPoint.name));
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
+ const roots = report.rootUnreadyAPIs.map((info, index) => {
112
+ const declaredBy = info.declaredBy === 'nobody'
113
+ ? 'declared by NO entry point (missing package or pact?)'
114
+ : `declared by "${info.declaredBy.entryPointName}" (installed, but its contribution never completed - hung async contributeAPI?)`;
115
+ const requiredBy = info.requiredBy.filter(name => !isProbeShell(name));
116
+ const lines = [`${index + 1}. "${info.key.name}" - ${declaredBy}`];
117
+ if (requiredBy.length) {
118
+ lines.push(` required by: ${requiredBy.join(', ')}`);
119
+ const tree = utils.visualizeDependencyTree(utils.traceAPIDependency(requiredBy[0], info.key.name));
120
+ lines.push(...tree.split('\n').map(line => ` ${line}`));
121
+ }
122
+ return lines.join('\n');
123
+ });
124
+ return `${header}
125
+
126
+ Root unready APIs (${report.rootUnreadyAPIs.length}):
127
+ ${roots.join('\n')}
128
+
129
+ Unready entry points: ${rootUnreadyEntryPoints.length} blocked directly on root APIs, ${unreadyEntryPoints.length} total.`;
130
+ };
131
+ function createAppHostAndWaitForLoading(packages_1, pacts_1) {
132
+ return __awaiter(this, arguments, void 0, function* (packages, pacts, { timeout = 3000 } = {}) {
93
133
  const appHost = createAppHostWithPacts(packages, pacts);
94
134
  const declaredAPIs = (0, lodash_1.default)(packages)
95
135
  .flatten()
96
136
  .value()
97
137
  .flatMap((entryPoint) => (entryPoint.declareAPIs ? entryPoint.declareAPIs() : []));
138
+ let timer;
98
139
  const timeoutPromise = new Promise((resolve, reject) => {
99
- setTimeout(() => {
100
- const readyAPIs = Array.from(globalThis.repluggableAppDebug.readyAPIs);
101
- const unreadyAPIs = declaredAPIs.filter(api => !readyAPIs.some(readyAPI => readyAPI.name === api.name));
102
- const rootUnreadyAPI = globalThis.repluggableAppDebug.utils.getRootUnreadyAPI();
103
- reject(new Error(`createAppHostAndWaitForLoading - waiting for loading timed out.
104
- there's a high chance this missing API is the main reason for it: ${JSON.stringify(rootUnreadyAPI)}
105
-
106
- in addition here's the full list of declared APIs that have not been contributed: ${JSON.stringify(unreadyAPIs)}`));
107
- }, 3000);
140
+ timer = setTimeout(() => {
141
+ const report = globalThis.repluggableAppDebug.utils.getUnreadyEntryPointsReport();
142
+ reject(new AppHostLoadingTimeoutError(formatUnreadyReport(timeout, report), report));
143
+ }, timeout);
108
144
  });
109
145
  const loadingPromise = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
110
146
  yield appHost.addShells([
111
147
  {
112
- name: 'Depends on all declared APIs',
148
+ name: WAIT_FOR_LOADING_PROBE_SHELL,
113
149
  getDependencyAPIs() {
114
150
  return declaredAPIs;
115
151
  },
@@ -119,7 +155,15 @@ in addition here's the full list of declared APIs that have not been contributed
119
155
  }
120
156
  ]);
121
157
  }));
122
- return Promise.race([timeoutPromise, loadingPromise]).then(() => appHost);
158
+ try {
159
+ yield Promise.race([timeoutPromise, loadingPromise]);
160
+ return appHost;
161
+ }
162
+ finally {
163
+ if (timer) {
164
+ clearTimeout(timer);
165
+ }
166
+ }
123
167
  });
124
168
  }
125
169
  exports.createAppHostAndWaitForLoading = createAppHostAndWaitForLoading;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../testKit/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../testKit/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repluggable-core",
3
- "version": "0.0.0-pr349.66",
3
+ "version": "0.0.0-pr351.71",
4
4
  "license": "MIT",
5
5
  "description": "Allows composition of a React-with-Redux application entirely from a list of pluggable packages",
6
6
  "main": "dist/src/index.js",
@@ -51,5 +51,5 @@
51
51
  "registry": "https://registry.npmjs.org/",
52
52
  "versionBumpStrategy": "patch"
53
53
  },
54
- "stableVersion": "1.302.85"
54
+ "stableVersion": "1.302.86"
55
55
  }
@@ -23,10 +23,25 @@ export interface DependencyTree {
23
23
  deps: { api: string; subtree: DependencyTree | null }[]
24
24
  }
25
25
 
26
+ export interface RootUnreadyAPIInfo {
27
+ key: AnySlotKey
28
+ declaredBy: 'nobody' | { entryPointName: string }
29
+ requiredBy: string[]
30
+ }
31
+
32
+ export interface UnreadyEntryPointsReport {
33
+ unreadyEntryPoints: EntryPoint[]
34
+ rootUnreadyAPIs: RootUnreadyAPIInfo[]
35
+ rootUnreadyEntryPoints: EntryPoint[]
36
+ probableDependencyCycle: boolean
37
+ }
38
+
26
39
  export interface RepluggableDebugUtils {
27
40
  apis(): APIDebugInfo[]
28
41
  unReadyEntryPoints(): EntryPoint[]
29
- getRootUnreadyAPI(): SlotKey<any>
42
+ getUnreadyEntryPointsReport(): UnreadyEntryPointsReport
43
+ /** @deprecated use getUnreadyEntryPointsReport() - this returns only the first of possibly many root unready APIs */
44
+ getRootUnreadyAPI(): SlotKey<any> | undefined
30
45
  whyEntryPointUnready(name: string): void
31
46
  findAPI(name: string): APIDebugInfo[]
32
47
  getAPIOrEntryPointsDependencies(
@@ -1 +1 @@
1
- export { setupDebugInfo } from './repluggableAppDebug'
1
+ export { setupDebugInfo, RootUnreadyAPIInfo, UnreadyEntryPointsReport } from './repluggableAppDebug'
@@ -36,54 +36,74 @@ function mapApiToEntryPoint(allPackages: EntryPoint[]) {
36
36
  return apiToEntryPoint
37
37
  }
38
38
 
39
- /**
40
- * a function that returns all the entry points in the system with their declared APIs and dependencies
41
- */
42
- const getAllEntryPoints = () => {
43
- return [
44
- ...globalThis.repluggableAppDebug.utils.unReadyEntryPoints(),
45
- ...[...globalThis.repluggableAppDebug.addedShells].map(([_, shell]) => shell.entryPoint)
46
- ]
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
47
57
  }
48
58
 
59
+ type UnreadyReportParams = Pick<SetupDebugInfoParams, 'readyAPIs' | 'getOwnSlotKey' | 'getUnreadyEntryPoints' | 'addedShells'>
60
+
49
61
  /**
50
- * this function is used to get the root unready API in case there are too many to understand.
51
- * for example if you have 200 unready entry points, running this function will give you the first unready API that
52
- * will unblock the rest of the entry point (note that there might be more than one)
53
- *
54
- * this function basically takes the first unready entry point, get its dependencies and iterates over them to find an API that is not ready
55
- * at this point it follows the same process recursively until it reaced the target API.
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).
56
64
  */
57
- const getRootUnreadyAPI = (host: AppHost) => {
58
- return () => {
59
- // get all unready entry points
60
- const allEntryPoints = getAllEntryPoints()
61
- const unReadyAPIsArray = []
62
- // get the depdenencies of the first unready entry point
63
- let dependenciesOfUnreadyEntryPoint = allEntryPoints?.[0]?.getDependencyAPIs?.()
64
-
65
- while (dependenciesOfUnreadyEntryPoint?.length) {
66
- const currentAPI = dependenciesOfUnreadyEntryPoint.pop()
67
-
68
- if (!currentAPI) {
69
- continue
70
- }
71
- // try to get the API from this host, we are looking for an API that is not ready
72
- try {
73
- const api = host.getAPI(currentAPI as SlotKey<any>)
74
- if (api) {
75
- continue
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
76
83
  }
77
- } catch (e) {
78
- unReadyAPIsArray.push(currentAPI)
79
- // we found an API that is unready, lets find which entry point declares it
80
- const declarer = allEntryPoints.find(entryPointData =>
81
- entryPointData.declareAPIs?.().some(api => currentAPI?.name === api.name)
82
- )
83
- dependenciesOfUnreadyEntryPoint = declarer?.getDependencyAPIs?.()
84
- }
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
85
106
  }
86
- return unReadyAPIsArray.reverse()[0]
87
107
  }
88
108
  }
89
109
 
@@ -207,7 +227,9 @@ export function setupDebugInfo({
207
227
  }
208
228
  })
209
229
  },
210
- getRootUnreadyAPI: getRootUnreadyAPI(host),
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,
211
233
  unReadyEntryPoints: (): EntryPoint[] => getUnreadyEntryPoints(),
212
234
  whyEntryPointUnready: (name: string) => {
213
235
  const unreadyEntryPoint = _.find(
@@ -245,4 +245,154 @@ describe('RepluggableAppDebug', () => {
245
245
  expect(unreadyAPIs).toEqual({ name: 'unreadyAPI' })
246
246
  })
247
247
  })
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 () => {
296
+ createAppHost([
297
+ {
298
+ name: 'entryPoint A',
299
+ getDependencyAPIs: () => [{ name: 'API B' }]
300
+ },
301
+ {
302
+ name: 'entryPoint B',
303
+ 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]
340
+ },
341
+ {
342
+ name: 'entryPoint B',
343
+ getDependencyAPIs: () => [unreadAPI]
344
+ }
345
+ ])
346
+
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)
374
+ })
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
+ })
248
398
  })
@@ -12,7 +12,8 @@ import {
12
12
  asyncLoadMockPackage,
13
13
  dependsOnMockPackageEntryPoint,
14
14
  MockPublicAPI,
15
- createAppHostAndWaitForLoading
15
+ createAppHostAndWaitForLoading,
16
+ AppHostLoadingTimeoutError
16
17
  } from '../testKit'
17
18
 
18
19
  interface APIKeys {
@@ -147,7 +148,61 @@ describe('App Host TestKit', () => {
147
148
  it('should throw if failed to load all packages', async () => {
148
149
  const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint], [])
149
150
  jest.runAllTimers()
150
- await expect(hostPromise).rejects.toThrow(new RegExp(MockPublicAPI.name))
151
+ await expect(hostPromise).rejects.toThrow(new RegExp(MockAPI.name))
152
+ })
153
+
154
+ it('should reject with a structured report of all root unready APIs', async () => {
155
+ const hostPromise = createAppHostAndWaitForLoading(
156
+ [
157
+ {
158
+ name: 'entryPoint A',
159
+ declareAPIs: () => [{ name: 'API A' }],
160
+ getDependencyAPIs: () => [{ name: 'missing A' }]
161
+ },
162
+ {
163
+ name: 'entryPoint B',
164
+ declareAPIs: () => [{ name: 'API B' }],
165
+ getDependencyAPIs: () => [{ name: 'missing B' }]
166
+ }
167
+ ],
168
+ []
169
+ )
170
+ jest.runAllTimers()
171
+ const error: AppHostLoadingTimeoutError = await hostPromise.then(
172
+ () => {
173
+ throw new Error('expected hostPromise to reject')
174
+ },
175
+ e => e
176
+ )
177
+
178
+ expect(error).toBeInstanceOf(AppHostLoadingTimeoutError)
179
+ expect(error.report.rootUnreadyAPIs.map(info => info.key.name)).toEqual(['missing A', 'missing B'])
180
+ expect(error.message).toContain('"missing A"')
181
+ 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
+ })
185
+
186
+ it('should respect a custom loading timeout', async () => {
187
+ let settled = false
188
+ const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint], [], { timeout: 10000 })
189
+ hostPromise.catch(() => (settled = true))
190
+
191
+ jest.advanceTimersByTime(9999)
192
+ await Promise.resolve()
193
+ await Promise.resolve()
194
+ expect(settled).toBe(false)
195
+
196
+ 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)
151
206
  })
152
207
  })
153
208
  })