@servicetitan/web-components 24.1.3 → 24.2.0
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/dist/delayed.d.ts +6 -0
- package/dist/delayed.d.ts.map +1 -0
- package/dist/delayed.js +12 -0
- package/dist/delayed.js.map +1 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +30 -18
- package/dist/loader.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/ttl-cache.d.ts +17 -0
- package/dist/utils/ttl-cache.d.ts.map +1 -0
- package/dist/utils/ttl-cache.js +65 -0
- package/dist/utils/ttl-cache.js.map +1 -0
- package/dist/utils/validate-bundle-info.d.ts +3 -1
- package/dist/utils/validate-bundle-info.d.ts.map +1 -1
- package/dist/utils/validate-bundle-info.js +15 -4
- package/dist/utils/validate-bundle-info.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/delayed.test.tsx +50 -0
- package/src/__tests__/loader.test.tsx +129 -22
- package/src/delayed.tsx +18 -0
- package/src/loader.tsx +53 -25
- package/src/utils/__tests__/ttl-cache.test.ts +98 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/ttl-cache.ts +64 -0
- package/src/utils/validate-bundle-info.ts +21 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delayed.d.ts","sourceRoot":"","sources":["../src/delayed.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAuB,MAAM,OAAO,CAAC;AAEnE,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,OAAO,EAAE,EAAE,CAAC,YAAY,CAWpC,CAAC"}
|
package/dist/delayed.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export const Delayed = ({ delayMs = 200, children }) => {
|
|
3
|
+
const [timedOut, setTimedOut] = useState(delayMs <= 0);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (delayMs > 0) {
|
|
6
|
+
const timer = setTimeout(() => setTimedOut(true), delayMs);
|
|
7
|
+
return () => clearTimeout(timer);
|
|
8
|
+
}
|
|
9
|
+
}, [delayMs]);
|
|
10
|
+
return timedOut ? children : null;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=delayed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delayed.js","sourceRoot":"","sources":["../src/delayed.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAMnE,MAAM,CAAC,MAAM,OAAO,GAAqB,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,QAAQ,EAAgB,EAAE,EAAE;IACnF,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IAEvD,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;YAC3D,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACL,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACtC,CAAC,CAAC"}
|
package/dist/loader.d.ts
CHANGED
|
@@ -11,9 +11,11 @@ export interface LoaderProps<T> {
|
|
|
11
11
|
data?: MFEContextData<T>;
|
|
12
12
|
basename?: string;
|
|
13
13
|
loadingFallback?: FallbackNode;
|
|
14
|
+
loadingFallbackDelayed?: boolean;
|
|
14
15
|
errorFallback?: FallbackNode;
|
|
15
16
|
className?: string;
|
|
16
17
|
retries?: number;
|
|
18
|
+
cache?: boolean | number;
|
|
17
19
|
onRequestError?(error: GetJsonError): boolean;
|
|
18
20
|
}
|
|
19
21
|
export type LoaderDataProps = Pick<LoaderProps<{}>, 'data' | 'className'> & {
|
package/dist/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEH,SAAS,EAOZ,MAAM,OAAO,CAAC;AAKf,OAAO,EAGH,YAAY,EAEf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEH,SAAS,EAOZ,MAAM,OAAO,CAAC;AAKf,OAAO,EAGH,YAAY,EAEf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAgC,MAAM,aAAa,CAAC;AAWrE,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;AAEjC,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;AAEzD,MAAM,WAAW,WAAW,CAAC,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uHAAuH;IACvH,IAAI,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,cAAc,CAAC,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,GAAG;IACxE,eAAe,EAAE,MAAM,GAAG,CAAC,OAAO,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,MAAM,sGA4DjB,CAAC"}
|
package/dist/loader.js
CHANGED
|
@@ -13,19 +13,22 @@ import { ErrorBoundary } from '@servicetitan/error-boundary';
|
|
|
13
13
|
import { Log } from '@servicetitan/log-service';
|
|
14
14
|
import { useOptionalDependencies } from '@servicetitan/react-ioc';
|
|
15
15
|
import { EXPOSED_DEPENDENCIES_TOKEN, useCancelation, } from './common';
|
|
16
|
+
import { Delayed } from './delayed';
|
|
16
17
|
import { EVENT_BUS_TOKEN, useEventBus } from './event-bus';
|
|
17
18
|
import { EXPOSED_DEPENDENCIES } from './globals';
|
|
18
|
-
import { getBundleInfo, validateBundleInfo, loadScripts, webComponent } from './utils';
|
|
19
|
+
import { TtlCache, getBundleInfo, validateBundleInfo, loadScripts, webComponent, } from './utils';
|
|
19
20
|
export const Loader = forwardRef((props, ref) => {
|
|
20
21
|
var _a;
|
|
21
|
-
const { basename, fallbackSrc, onRequestError, retries, src } = props;
|
|
22
|
+
const { cache = true, basename, fallbackSrc, onRequestError, retries, src } = props;
|
|
23
|
+
const { loadingFallback, loadingFallbackDelayed = true } = props;
|
|
22
24
|
const bus = useEventBus();
|
|
23
25
|
const [logService, exposedDependencies = EXPOSED_DEPENDENCIES, eventBus = bus] = useOptionalDependencies(Log, EXPOSED_DEPENDENCIES_TOKEN, EVENT_BUS_TOKEN);
|
|
24
26
|
const cancelation = useCancelation();
|
|
25
|
-
const LoadingFallback = useMemo(() => getFallback(
|
|
27
|
+
const LoadingFallback = useMemo(() => getFallback(loadingFallback, 'Loading...', { delayed: loadingFallbackDelayed }), [loadingFallback, loadingFallbackDelayed]);
|
|
26
28
|
const ErrorFallback = useMemo(() => getFallback(props.errorFallback, 'Something went wrong'), [props.errorFallback]);
|
|
27
29
|
const Loader = useMemo(() => lazyLoader({
|
|
28
30
|
basename,
|
|
31
|
+
cache,
|
|
29
32
|
cancelation,
|
|
30
33
|
exposedDependencies,
|
|
31
34
|
eventBus,
|
|
@@ -36,6 +39,7 @@ export const Loader = forwardRef((props, ref) => {
|
|
|
36
39
|
src,
|
|
37
40
|
}), [
|
|
38
41
|
basename,
|
|
42
|
+
cache,
|
|
39
43
|
cancelation,
|
|
40
44
|
exposedDependencies,
|
|
41
45
|
eventBus,
|
|
@@ -61,22 +65,27 @@ function lazyLoader(props) {
|
|
|
61
65
|
};
|
|
62
66
|
}
|
|
63
67
|
function lazyWebComponent(props) {
|
|
64
|
-
const { basename, cancelation, exposedDependencies, eventBus, fallbackSrc, logService, onRequestError, retries, src, } = props;
|
|
68
|
+
const { basename, cache, cancelation, exposedDependencies, eventBus, fallbackSrc, logService, onRequestError, retries, src, } = props;
|
|
65
69
|
return lazy(() => __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
let bundleInfo;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
let bundleInfo = cache ? bundleCache.get(src) : undefined;
|
|
71
|
+
if (!bundleInfo) {
|
|
72
|
+
try {
|
|
73
|
+
bundleInfo = yield getBundleInfo(src, fallbackSrc, exposedDependencies, cancelation.signal, retries, onRequestError);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
if (cancelation.signal.aborted) {
|
|
77
|
+
return {
|
|
78
|
+
default: () => null,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
if (cache) {
|
|
84
|
+
bundleCache.set(src, bundleInfo, typeof cache === 'number' ? cache : undefined);
|
|
75
85
|
}
|
|
76
|
-
|
|
86
|
+
validateBundleInfo(bundleInfo, { exposedDependencies, logService, packageName: src });
|
|
87
|
+
yield loadScripts(bundleInfo);
|
|
77
88
|
}
|
|
78
|
-
validateBundleInfo(bundleInfo, { logService, packageName: src });
|
|
79
|
-
yield loadScripts(bundleInfo);
|
|
80
89
|
return {
|
|
81
90
|
default: webComponent({
|
|
82
91
|
basename,
|
|
@@ -88,7 +97,10 @@ function lazyWebComponent(props) {
|
|
|
88
97
|
};
|
|
89
98
|
}));
|
|
90
99
|
}
|
|
91
|
-
function getFallback(node, message) {
|
|
92
|
-
|
|
100
|
+
function getFallback(node, message, { delayed = false } = {}) {
|
|
101
|
+
const fallbackNode = node !== undefined ? node : _jsx("h2", { children: message });
|
|
102
|
+
return delayed ? () => _jsx(Delayed, { children: fallbackNode }) : () => fallbackNode;
|
|
93
103
|
}
|
|
104
|
+
const MINUTE = 1000 * 60;
|
|
105
|
+
const bundleCache = new TtlCache({ ttlMs: 15 * MINUTE });
|
|
94
106
|
//# sourceMappingURL=loader.js.map
|
package/dist/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.tsx"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,EAGH,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,SAAS,EACT,mBAAmB,EACnB,OAAO,GACV,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,EACH,0BAA0B,EAG1B,cAAc,GACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAY,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.tsx"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './get-mfe-supports-rerendering';
|
|
|
4
4
|
export * from './get-version-mismatches';
|
|
5
5
|
export * from './is-versioned-url';
|
|
6
6
|
export * from './load-scripts';
|
|
7
|
+
export * from './ttl-cache';
|
|
7
8
|
export * from './validate-bundle-info';
|
|
8
9
|
export * from './web-component';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from './get-mfe-supports-rerendering';
|
|
|
4
4
|
export * from './get-version-mismatches';
|
|
5
5
|
export * from './is-versioned-url';
|
|
6
6
|
export * from './load-scripts';
|
|
7
|
+
export * from './ttl-cache';
|
|
7
8
|
export * from './validate-bundle-info';
|
|
8
9
|
export * from './web-component';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface TtlCacheOptions {
|
|
2
|
+
ttlMs?: number;
|
|
3
|
+
}
|
|
4
|
+
export declare class TtlCache<T = any> {
|
|
5
|
+
private data;
|
|
6
|
+
private timers;
|
|
7
|
+
private ttl;
|
|
8
|
+
constructor({ ttlMs }?: TtlCacheOptions);
|
|
9
|
+
set(key: string, value: T, ttl?: number): void;
|
|
10
|
+
get(key: string): T | undefined;
|
|
11
|
+
has(key: string): boolean;
|
|
12
|
+
delete(key: string): void;
|
|
13
|
+
clear(): void;
|
|
14
|
+
private cancelTimeout;
|
|
15
|
+
private setTimeout;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=ttl-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ttl-cache.d.ts","sourceRoot":"","sources":["../../src/utils/ttl-cache.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IACzB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,GAAG,CAAS;gBAER,EAAE,KAAmB,EAAE,GAAE,eAAoB;IAUzD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM;IAKvC,GAAG,CAAC,GAAG,EAAE,MAAM;IAIf,GAAG,CAAC,GAAG,EAAE,MAAM;IAIf,MAAM,CAAC,GAAG,EAAE,MAAM;IAKlB,KAAK;IAML,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,UAAU;CASrB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const MINUTE = 1000 * 60;
|
|
2
|
+
const DEFAULT_TTL = 5 * MINUTE;
|
|
3
|
+
export class TtlCache {
|
|
4
|
+
constructor({ ttlMs = DEFAULT_TTL } = {}) {
|
|
5
|
+
Object.defineProperty(this, "data", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "timers", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "ttl", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
if (ttlMs < 0) {
|
|
24
|
+
throw new Error('default TTL must be zero or greater');
|
|
25
|
+
}
|
|
26
|
+
this.data = new Map();
|
|
27
|
+
this.timers = new Map();
|
|
28
|
+
this.ttl = ttlMs;
|
|
29
|
+
}
|
|
30
|
+
set(key, value, ttl) {
|
|
31
|
+
this.setTimeout(key, ttl !== null && ttl !== void 0 ? ttl : this.ttl);
|
|
32
|
+
this.data.set(key, value);
|
|
33
|
+
}
|
|
34
|
+
get(key) {
|
|
35
|
+
return this.data.get(key);
|
|
36
|
+
}
|
|
37
|
+
has(key) {
|
|
38
|
+
return this.data.has(key);
|
|
39
|
+
}
|
|
40
|
+
delete(key) {
|
|
41
|
+
this.cancelTimeout(key);
|
|
42
|
+
this.data.delete(key);
|
|
43
|
+
}
|
|
44
|
+
clear() {
|
|
45
|
+
this.data.clear();
|
|
46
|
+
this.timers.forEach(value => clearTimeout(value));
|
|
47
|
+
this.timers.clear();
|
|
48
|
+
}
|
|
49
|
+
cancelTimeout(key) {
|
|
50
|
+
const id = this.timers.get(key);
|
|
51
|
+
if (id) {
|
|
52
|
+
clearTimeout(id);
|
|
53
|
+
this.timers.delete(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
setTimeout(key, ttl) {
|
|
57
|
+
this.cancelTimeout(key);
|
|
58
|
+
if (ttl < 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const id = setTimeout(() => this.delete(key), ttl);
|
|
62
|
+
this.timers.set(key, id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ttl-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ttl-cache.js","sourceRoot":"","sources":["../../src/utils/ttl-cache.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;AACzB,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC;AAM/B,MAAM,OAAO,QAAQ;IAKjB,YAAY,EAAE,KAAK,GAAG,WAAW,KAAsB,EAAE;QAJjD;;;;;WAAqB;QACrB;;;;;WAAmD;QACnD;;;;;WAAY;QAGhB,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAQ,EAAE,GAAY;QACnC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,aAAH,GAAG,cAAH,GAAG,GAAI,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,GAAG,CAAC,GAAW;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,GAAG,CAAC,GAAW;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,GAAW;QACd,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAEO,aAAa,CAAC,GAAW;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,EAAE,EAAE,CAAC;YACL,YAAY,CAAC,EAAE,CAAC,CAAC;YACjB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAEO,UAAU,CAAC,GAAW,EAAE,GAAW;QACvC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACV,OAAO;QACX,CAAC;QAED,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7B,CAAC;CACJ"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Log } from '@servicetitan/log-service';
|
|
2
|
+
import { ExposedDependencies } from '../common';
|
|
2
3
|
import { BundleInfo } from './get-bundle-info';
|
|
3
|
-
export declare function validateBundleInfo({ mismatch }: BundleInfo, { logService, packageName }: {
|
|
4
|
+
export declare function validateBundleInfo({ mismatch }: BundleInfo, { exposedDependencies, logService, packageName, }: {
|
|
5
|
+
exposedDependencies?: ExposedDependencies;
|
|
4
6
|
logService?: Log;
|
|
5
7
|
packageName: string;
|
|
6
8
|
}): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-bundle-info.d.ts","sourceRoot":"","sources":["../../src/utils/validate-bundle-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,wBAAgB,kBAAkB,CAC9B,EAAE,QAAQ,EAAE,EAAE,UAAU,EACxB,
|
|
1
|
+
{"version":3,"file":"validate-bundle-info.d.ts","sourceRoot":"","sources":["../../src/utils/validate-bundle-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,wBAAgB,kBAAkB,CAC9B,EAAE,QAAQ,EAAE,EAAE,UAAU,EACxB,EACI,mBAAwB,EACxB,UAAU,EACV,WAAW,GACd,EAAE;IAAE,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,QAqB1F"}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
export function validateBundleInfo({ mismatch }, { logService, packageName }) {
|
|
2
|
-
if (mismatch) {
|
|
3
|
-
|
|
1
|
+
export function validateBundleInfo({ mismatch }, { exposedDependencies = {}, logService, packageName, }) {
|
|
2
|
+
if (!mismatch || !logService) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
const data = { package: packageName, dependencies: JSON.stringify(mismatch) };
|
|
6
|
+
if (Object.keys(exposedDependencies).length === 0) {
|
|
7
|
+
logService.warning({
|
|
8
|
+
category: 'MicroFrontends.NoExposedDependencies',
|
|
9
|
+
message: 'Cannot use light bundle because the host is not exposing dependencies or the list of shared dependencies is empty.',
|
|
10
|
+
data,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
logService.warning({
|
|
4
15
|
category: 'MicroFrontends.DependenciesMismatch',
|
|
5
16
|
message: 'Some of the package dependencies have incompatible versions.',
|
|
6
|
-
data
|
|
17
|
+
data,
|
|
7
18
|
});
|
|
8
19
|
}
|
|
9
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-bundle-info.js","sourceRoot":"","sources":["../../src/utils/validate-bundle-info.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate-bundle-info.js","sourceRoot":"","sources":["../../src/utils/validate-bundle-info.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,kBAAkB,CAC9B,EAAE,QAAQ,EAAc,EACxB,EACI,mBAAmB,GAAG,EAAE,EACxB,UAAU,EACV,WAAW,GACwE;IAEvF,IAAI,CAAC,QAAQ,IAAI,CAAC,UAAU,EAAE,CAAC;QAC3B,OAAO;IACX,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC9E,IAAI,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,UAAU,CAAC,OAAO,CAAC;YACf,QAAQ,EAAE,sCAAsC;YAChD,OAAO,EACH,oHAAoH;YACxH,IAAI;SACP,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACJ,UAAU,CAAC,OAAO,CAAC;YACf,QAAQ,EAAE,qCAAqC;YAC/C,OAAO,EAAE,8DAA8D;YACvE,IAAI;SACP,CAAC,CAAC;IACP,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/web-components",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
"src"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@servicetitan/error-boundary": "24.
|
|
18
|
+
"@servicetitan/error-boundary": "24.2.0",
|
|
19
19
|
"lodash.get": "~4.4.2",
|
|
20
20
|
"mitt": "~3.0.1",
|
|
21
21
|
"semver": "~7.6.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@servicetitan/design-system": "~14.0.0",
|
|
25
|
-
"@servicetitan/log-service": "24.
|
|
26
|
-
"@servicetitan/react-ioc": "24.
|
|
27
|
-
"@testing-library/dom": "^10.
|
|
25
|
+
"@servicetitan/log-service": "24.2.0",
|
|
26
|
+
"@servicetitan/react-ioc": "24.2.0",
|
|
27
|
+
"@testing-library/dom": "^10.3.1",
|
|
28
28
|
"@testing-library/jest-dom": "^6.4.5",
|
|
29
29
|
"@testing-library/react": "^16.0.0",
|
|
30
30
|
"@types/history": "~4.7.10",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@servicetitan/design-system": ">=12.6.2",
|
|
42
|
-
"@servicetitan/log-service": "24.
|
|
43
|
-
"@servicetitan/react-ioc": "24.
|
|
42
|
+
"@servicetitan/log-service": "24.2.0",
|
|
43
|
+
"@servicetitan/react-ioc": "24.2.0",
|
|
44
44
|
"history": "~4.10.1",
|
|
45
45
|
"react": ">=17.0.2",
|
|
46
46
|
"react-dom": ">=17.0.2"
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"cli": {
|
|
52
52
|
"webpack": false
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "13848340f521a17e1e251fde0d031022910ca7ab"
|
|
55
55
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import { act, render, screen } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { Delayed, DelayedProps } from '../delayed';
|
|
5
|
+
|
|
6
|
+
describe(`${Delayed.name}`, () => {
|
|
7
|
+
const content = 'foo';
|
|
8
|
+
let props: DelayedProps;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.useFakeTimers();
|
|
12
|
+
props = {};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const subject = () => render(<Delayed {...props}>{content}</Delayed>);
|
|
16
|
+
|
|
17
|
+
test('renders content after 200ms delay', () => {
|
|
18
|
+
subject();
|
|
19
|
+
|
|
20
|
+
act(() => jest.advanceTimersByTime(199));
|
|
21
|
+
expect(screen.queryByText(content)).not.toBeInTheDocument();
|
|
22
|
+
|
|
23
|
+
act(() => jest.advanceTimersByTime(1));
|
|
24
|
+
expect(screen.getByText(content)).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('with zero delay', () => {
|
|
28
|
+
beforeEach(() => (props.delayMs = 0));
|
|
29
|
+
|
|
30
|
+
test('renders content immediately', () => {
|
|
31
|
+
subject();
|
|
32
|
+
|
|
33
|
+
expect(screen.getByText(content)).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('with custom delay', () => {
|
|
38
|
+
beforeEach(() => (props.delayMs = 1000));
|
|
39
|
+
|
|
40
|
+
test('renders content after specified delay', () => {
|
|
41
|
+
subject();
|
|
42
|
+
|
|
43
|
+
act(() => jest.advanceTimersByTime(props.delayMs! - 1));
|
|
44
|
+
expect(screen.queryByText(content)).not.toBeInTheDocument();
|
|
45
|
+
|
|
46
|
+
act(() => jest.advanceTimersByTime(1));
|
|
47
|
+
expect(screen.queryByText(content)).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -44,11 +44,7 @@ describe('[web-components] Loader', () => {
|
|
|
44
44
|
let metadata: Metadata;
|
|
45
45
|
|
|
46
46
|
function setStartupVersion(version: string | undefined) {
|
|
47
|
-
|
|
48
|
-
metadata.bundledWith = undefined;
|
|
49
|
-
} else {
|
|
50
|
-
metadata.bundledWith = { '@servicetitan/startup': version };
|
|
51
|
-
}
|
|
47
|
+
metadata.bundledWith = version ? { '@servicetitan/startup': version } : undefined;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
beforeAll(() => {
|
|
@@ -104,7 +100,7 @@ describe('[web-components] Loader', () => {
|
|
|
104
100
|
},
|
|
105
101
|
};
|
|
106
102
|
setStartupVersion('22.14.0');
|
|
107
|
-
props = { src: baseUrl };
|
|
103
|
+
props = { src: baseUrl, cache: false };
|
|
108
104
|
onloadRef = null;
|
|
109
105
|
singletons = [];
|
|
110
106
|
initializeGetJsonMockFns();
|
|
@@ -128,9 +124,10 @@ describe('[web-components] Loader', () => {
|
|
|
128
124
|
return render(<Root />);
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
test('renders default loading fallback', () => {
|
|
127
|
+
test('renders default loading fallback', async () => {
|
|
132
128
|
subject();
|
|
133
|
-
|
|
129
|
+
|
|
130
|
+
await waitFor(() => expect(screen.getByText(/loading/i)).toBeInTheDocument());
|
|
134
131
|
});
|
|
135
132
|
|
|
136
133
|
describe('with ref parameter', () => {
|
|
@@ -162,37 +159,38 @@ describe('[web-components] Loader', () => {
|
|
|
162
159
|
props.loadingFallback = <div>foo</div>;
|
|
163
160
|
});
|
|
164
161
|
|
|
165
|
-
test('renders provided loading fallback', () => {
|
|
162
|
+
test('renders provided loading fallback', async () => {
|
|
166
163
|
subject();
|
|
167
|
-
|
|
164
|
+
|
|
165
|
+
await waitFor(() => expect(screen.getByText('foo')).toBeInTheDocument());
|
|
168
166
|
});
|
|
169
167
|
|
|
170
168
|
test('preserves component state when loading fallback is updated', () => {
|
|
171
169
|
const renderResult = subject();
|
|
172
170
|
expect(common.getJson).toHaveBeenCalledTimes(1);
|
|
171
|
+
|
|
173
172
|
props.loadingFallback = <div>foo</div>;
|
|
174
173
|
renderResult.rerender(<Root />);
|
|
174
|
+
|
|
175
175
|
expect(common.getJson).toHaveBeenCalledTimes(1);
|
|
176
176
|
});
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
describe('when the loadingFallback throws an error', () => {
|
|
180
180
|
beforeEach(() => {
|
|
181
|
-
// suppress console
|
|
182
|
-
jest.spyOn(console, 'error').mockImplementation(jest.fn);
|
|
181
|
+
jest.spyOn(console, 'error').mockImplementation(jest.fn); // suppress console output
|
|
183
182
|
const ThrowError: FC = () => {
|
|
184
183
|
throw new Error('woops');
|
|
185
184
|
};
|
|
186
|
-
props.loadingFallback =
|
|
187
|
-
<div>
|
|
188
|
-
<ThrowError />
|
|
189
|
-
</div>
|
|
190
|
-
);
|
|
185
|
+
props.loadingFallback = <ThrowError />;
|
|
191
186
|
});
|
|
192
187
|
|
|
193
|
-
test('renders default error fallback', () => {
|
|
188
|
+
test('renders default error fallback', async () => {
|
|
194
189
|
subject();
|
|
195
|
-
|
|
190
|
+
|
|
191
|
+
await waitFor(() =>
|
|
192
|
+
expect(screen.getByText(/something went wrong/i)).toBeInTheDocument()
|
|
193
|
+
);
|
|
196
194
|
});
|
|
197
195
|
|
|
198
196
|
describe('when an errorFallback is provided', () => {
|
|
@@ -200,13 +198,24 @@ describe('[web-components] Loader', () => {
|
|
|
200
198
|
props.errorFallback = <div>bar</div>;
|
|
201
199
|
});
|
|
202
200
|
|
|
203
|
-
test('renders provided error fallback', () => {
|
|
201
|
+
test('renders provided error fallback', async () => {
|
|
204
202
|
subject();
|
|
205
|
-
|
|
203
|
+
|
|
204
|
+
await waitFor(() => expect(screen.getByText('bar')).toBeInTheDocument());
|
|
206
205
|
});
|
|
207
206
|
});
|
|
208
207
|
});
|
|
209
208
|
|
|
209
|
+
describe('when loadingFallback is not delayed', () => {
|
|
210
|
+
beforeEach(() => (props.loadingFallbackDelayed = false));
|
|
211
|
+
|
|
212
|
+
test('renders loading fallback immediately', () => {
|
|
213
|
+
subject();
|
|
214
|
+
|
|
215
|
+
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
210
219
|
describe('when a script tag (with a webComponent data attr) for this bundle is in the DOM before Loader is rendered', () => {
|
|
211
220
|
beforeEach(() => {
|
|
212
221
|
const script = document.createElement('script');
|
|
@@ -637,6 +646,10 @@ describe('[web-components] Loader', () => {
|
|
|
637
646
|
|
|
638
647
|
beforeEach(() => {
|
|
639
648
|
jest.mocked(getVersionMismatches).mockReturnValue(mismatchedDeps);
|
|
649
|
+
singletons?.push({
|
|
650
|
+
provide: EXPOSED_DEPENDENCIES_TOKEN,
|
|
651
|
+
useValue: { foo: 'bar' },
|
|
652
|
+
});
|
|
640
653
|
});
|
|
641
654
|
|
|
642
655
|
itAddsJsEntrypointToWebComponent(
|
|
@@ -656,7 +669,7 @@ describe('[web-components] Loader', () => {
|
|
|
656
669
|
singletons?.push({ provide: Log, useValue: logService });
|
|
657
670
|
});
|
|
658
671
|
|
|
659
|
-
test('logs
|
|
672
|
+
test('logs "mismatch" warning', async () => {
|
|
660
673
|
await webComponentReadySetup();
|
|
661
674
|
|
|
662
675
|
expect(logService.warning).toHaveBeenCalledWith(
|
|
@@ -669,6 +682,24 @@ describe('[web-components] Loader', () => {
|
|
|
669
682
|
})
|
|
670
683
|
);
|
|
671
684
|
});
|
|
685
|
+
|
|
686
|
+
describe('when host exposes no dependencies', () => {
|
|
687
|
+
beforeEach(() => {
|
|
688
|
+
singletons = singletons?.filter(
|
|
689
|
+
({ provide }: any) => provide !== EXPOSED_DEPENDENCIES_TOKEN
|
|
690
|
+
);
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
test('logs "no dependencies" warning', async () => {
|
|
694
|
+
await webComponentReadySetup();
|
|
695
|
+
|
|
696
|
+
expect(logService.warning).toHaveBeenCalledWith(
|
|
697
|
+
expect.objectContaining({
|
|
698
|
+
category: 'MicroFrontends.NoExposedDependencies',
|
|
699
|
+
})
|
|
700
|
+
);
|
|
701
|
+
});
|
|
702
|
+
});
|
|
672
703
|
});
|
|
673
704
|
});
|
|
674
705
|
});
|
|
@@ -678,4 +709,80 @@ describe('[web-components] Loader', () => {
|
|
|
678
709
|
expect(onloadRef).toBeNull();
|
|
679
710
|
});
|
|
680
711
|
});
|
|
712
|
+
|
|
713
|
+
describe('when cache is enabled', () => {
|
|
714
|
+
beforeAll(() => {
|
|
715
|
+
jest.useFakeTimers();
|
|
716
|
+
jest.spyOn(console, 'error').mockImplementation(jest.fn); // suppress "suspended resource finished loading inside a test" error
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
beforeEach(() => delete props.cache);
|
|
720
|
+
|
|
721
|
+
function preloadComponent({ id, cache }: { id?: string; cache?: boolean | number } = {}) {
|
|
722
|
+
if (id) {
|
|
723
|
+
props.src = `http://localhost/${id}`;
|
|
724
|
+
initializeGetJsonMockFns(props.src);
|
|
725
|
+
}
|
|
726
|
+
if (cache !== undefined) {
|
|
727
|
+
props.cache = cache;
|
|
728
|
+
}
|
|
729
|
+
subject();
|
|
730
|
+
jest.mocked(common.getJson).mockClear();
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function itReloadsMetadataAfter(delay: number, description: string) {
|
|
734
|
+
test(`reloads metadata after ${description}`, () => {
|
|
735
|
+
act(() => jest.advanceTimersByTime(delay - 1));
|
|
736
|
+
subject();
|
|
737
|
+
expect(common.getJson).toHaveBeenCalledTimes(0);
|
|
738
|
+
|
|
739
|
+
act(() => jest.advanceTimersByTime(1));
|
|
740
|
+
subject();
|
|
741
|
+
expect(common.getJson).toHaveBeenCalledTimes(1);
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
describe('when component was previously loaded', () => {
|
|
746
|
+
beforeEach(() => preloadComponent());
|
|
747
|
+
|
|
748
|
+
test('uses cached metadata', () => {
|
|
749
|
+
subject();
|
|
750
|
+
|
|
751
|
+
expect(common.getJson).toHaveBeenCalledTimes(0);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
itReloadsMetadataAfter(1000 * 60 * 15, 'fifteen minutes');
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
describe('when component has custom ttl', () => {
|
|
758
|
+
const customTtl = 1000 * 60; // one minute
|
|
759
|
+
|
|
760
|
+
beforeEach(() => preloadComponent({ id: 'mfe2', cache: customTtl }));
|
|
761
|
+
|
|
762
|
+
itReloadsMetadataAfter(customTtl, 'custom ttl');
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
describe('when component has indefinite ttl', () => {
|
|
766
|
+
beforeEach(() => preloadComponent({ id: 'mfe3', cache: -1 }));
|
|
767
|
+
|
|
768
|
+
test('never reloads metadata', () => {
|
|
769
|
+
jest.runOnlyPendingTimers();
|
|
770
|
+
subject();
|
|
771
|
+
|
|
772
|
+
expect(common.getJson).toHaveBeenCalledTimes(0);
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
describe('when component has zero ttl', () => {
|
|
777
|
+
beforeEach(() => preloadComponent({ id: 'mfe4', cache: 0 }));
|
|
778
|
+
|
|
779
|
+
test('always reloads metadata', () => {
|
|
780
|
+
subject();
|
|
781
|
+
expect(common.getJson).toHaveBeenCalledTimes(1);
|
|
782
|
+
|
|
783
|
+
subject();
|
|
784
|
+
expect(common.getJson).toHaveBeenCalledTimes(2);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
});
|
|
681
788
|
});
|
package/src/delayed.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FC, PropsWithChildren, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type DelayedProps = PropsWithChildren<{
|
|
4
|
+
delayMs?: number;
|
|
5
|
+
}>;
|
|
6
|
+
|
|
7
|
+
export const Delayed: FC<DelayedProps> = ({ delayMs = 200, children }: DelayedProps) => {
|
|
8
|
+
const [timedOut, setTimedOut] = useState(delayMs <= 0);
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (delayMs > 0) {
|
|
12
|
+
const timer = setTimeout(() => setTimedOut(true), delayMs);
|
|
13
|
+
return () => clearTimeout(timer);
|
|
14
|
+
}
|
|
15
|
+
}, [delayMs]);
|
|
16
|
+
|
|
17
|
+
return timedOut ? children : null;
|
|
18
|
+
};
|
package/src/loader.tsx
CHANGED
|
@@ -19,9 +19,17 @@ import {
|
|
|
19
19
|
useCancelation,
|
|
20
20
|
} from './common';
|
|
21
21
|
import { MFEContextData } from './contexts/mfe-data-context';
|
|
22
|
+
import { Delayed } from './delayed';
|
|
22
23
|
import { EventBus, EVENT_BUS_TOKEN, useEventBus } from './event-bus';
|
|
23
24
|
import { EXPOSED_DEPENDENCIES } from './globals';
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
BundleInfo,
|
|
27
|
+
TtlCache,
|
|
28
|
+
getBundleInfo,
|
|
29
|
+
validateBundleInfo,
|
|
30
|
+
loadScripts,
|
|
31
|
+
webComponent,
|
|
32
|
+
} from './utils';
|
|
25
33
|
|
|
26
34
|
export type LoaderRef = EventBus;
|
|
27
35
|
|
|
@@ -34,9 +42,11 @@ export interface LoaderProps<T> {
|
|
|
34
42
|
data?: MFEContextData<T>;
|
|
35
43
|
basename?: string;
|
|
36
44
|
loadingFallback?: FallbackNode;
|
|
45
|
+
loadingFallbackDelayed?: boolean;
|
|
37
46
|
errorFallback?: FallbackNode;
|
|
38
47
|
className?: string;
|
|
39
48
|
retries?: number;
|
|
49
|
+
cache?: boolean | number;
|
|
40
50
|
onRequestError?(error: GetJsonError): boolean;
|
|
41
51
|
}
|
|
42
52
|
|
|
@@ -45,7 +55,8 @@ export type LoaderDataProps = Pick<LoaderProps<{}>, 'data' | 'className'> & {
|
|
|
45
55
|
};
|
|
46
56
|
|
|
47
57
|
export const Loader = forwardRef<LoaderRef, LoaderProps<{}>>((props, ref) => {
|
|
48
|
-
const { basename, fallbackSrc, onRequestError, retries, src } = props;
|
|
58
|
+
const { cache = true, basename, fallbackSrc, onRequestError, retries, src } = props;
|
|
59
|
+
const { loadingFallback, loadingFallbackDelayed = true } = props;
|
|
49
60
|
|
|
50
61
|
const bus = useEventBus();
|
|
51
62
|
const [logService, exposedDependencies = EXPOSED_DEPENDENCIES, eventBus = bus] =
|
|
@@ -53,8 +64,8 @@ export const Loader = forwardRef<LoaderRef, LoaderProps<{}>>((props, ref) => {
|
|
|
53
64
|
const cancelation = useCancelation();
|
|
54
65
|
|
|
55
66
|
const LoadingFallback = useMemo(
|
|
56
|
-
() => getFallback(
|
|
57
|
-
[
|
|
67
|
+
() => getFallback(loadingFallback, 'Loading...', { delayed: loadingFallbackDelayed }),
|
|
68
|
+
[loadingFallback, loadingFallbackDelayed]
|
|
58
69
|
);
|
|
59
70
|
|
|
60
71
|
const ErrorFallback = useMemo(
|
|
@@ -66,6 +77,7 @@ export const Loader = forwardRef<LoaderRef, LoaderProps<{}>>((props, ref) => {
|
|
|
66
77
|
() =>
|
|
67
78
|
lazyLoader({
|
|
68
79
|
basename,
|
|
80
|
+
cache,
|
|
69
81
|
cancelation,
|
|
70
82
|
exposedDependencies,
|
|
71
83
|
eventBus,
|
|
@@ -77,6 +89,7 @@ export const Loader = forwardRef<LoaderRef, LoaderProps<{}>>((props, ref) => {
|
|
|
77
89
|
}),
|
|
78
90
|
[
|
|
79
91
|
basename,
|
|
92
|
+
cache,
|
|
80
93
|
cancelation,
|
|
81
94
|
exposedDependencies,
|
|
82
95
|
eventBus,
|
|
@@ -135,6 +148,7 @@ function lazyLoader(props: LazyLoaderProps) {
|
|
|
135
148
|
function lazyWebComponent(props: LazyLoaderProps) {
|
|
136
149
|
const {
|
|
137
150
|
basename,
|
|
151
|
+
cache,
|
|
138
152
|
cancelation,
|
|
139
153
|
exposedDependencies,
|
|
140
154
|
eventBus,
|
|
@@ -146,29 +160,35 @@ function lazyWebComponent(props: LazyLoaderProps) {
|
|
|
146
160
|
} = props;
|
|
147
161
|
|
|
148
162
|
return lazy<(props: LoaderDataProps) => JSX.Element | null>(async () => {
|
|
149
|
-
let bundleInfo: BundleInfo;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
let bundleInfo: BundleInfo | undefined = cache ? bundleCache.get(src) : undefined;
|
|
164
|
+
if (!bundleInfo) {
|
|
165
|
+
try {
|
|
166
|
+
bundleInfo = await getBundleInfo(
|
|
167
|
+
src,
|
|
168
|
+
fallbackSrc,
|
|
169
|
+
exposedDependencies,
|
|
170
|
+
cancelation.signal,
|
|
171
|
+
retries,
|
|
172
|
+
onRequestError
|
|
173
|
+
);
|
|
174
|
+
} catch (e) {
|
|
175
|
+
if (cancelation.signal.aborted) {
|
|
176
|
+
return {
|
|
177
|
+
default: () => null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
throw e;
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
|
|
167
|
-
|
|
184
|
+
if (cache) {
|
|
185
|
+
bundleCache.set(src, bundleInfo, typeof cache === 'number' ? cache : undefined);
|
|
186
|
+
}
|
|
168
187
|
|
|
169
|
-
|
|
188
|
+
validateBundleInfo(bundleInfo, { exposedDependencies, logService, packageName: src });
|
|
170
189
|
|
|
171
|
-
|
|
190
|
+
await loadScripts(bundleInfo);
|
|
191
|
+
}
|
|
172
192
|
|
|
173
193
|
return {
|
|
174
194
|
default: webComponent({
|
|
@@ -182,6 +202,14 @@ function lazyWebComponent(props: LazyLoaderProps) {
|
|
|
182
202
|
});
|
|
183
203
|
}
|
|
184
204
|
|
|
185
|
-
function getFallback(
|
|
186
|
-
|
|
205
|
+
function getFallback(
|
|
206
|
+
node: FallbackNode | undefined,
|
|
207
|
+
message: string,
|
|
208
|
+
{ delayed = false }: { delayed?: boolean } = {}
|
|
209
|
+
) {
|
|
210
|
+
const fallbackNode = node !== undefined ? (node as ReactElement) : <h2>{message}</h2>;
|
|
211
|
+
return delayed ? () => <Delayed>{fallbackNode}</Delayed> : () => fallbackNode;
|
|
187
212
|
}
|
|
213
|
+
|
|
214
|
+
const MINUTE = 1000 * 60;
|
|
215
|
+
const bundleCache = new TtlCache<BundleInfo>({ ttlMs: 15 * MINUTE });
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { TtlCache } from '../ttl-cache';
|
|
2
|
+
|
|
3
|
+
describe(`${TtlCache.name}`, () => {
|
|
4
|
+
const key = 'foo';
|
|
5
|
+
const value = 'bar';
|
|
6
|
+
|
|
7
|
+
let cache: TtlCache<string>;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.useFakeTimers();
|
|
11
|
+
cache = new TtlCache();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('cache rejects invalid default ttl', () => {
|
|
15
|
+
expect(() => new TtlCache({ ttlMs: -1 })).toThrow();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('when cache omits key', () => {
|
|
19
|
+
test('has returns false', () => {
|
|
20
|
+
expect(cache.has(key)).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('get returns undefined', () => {
|
|
24
|
+
expect(cache.get(key)).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('when cache contains key', () => {
|
|
29
|
+
const elements = {
|
|
30
|
+
[key]: value,
|
|
31
|
+
[`other-${key}`]: `other-${value}`,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
Object.entries(elements).map(([key, value]) => cache.set(key, value));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('has returns true', () => {
|
|
39
|
+
expect(cache.has(key)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('get returns value', () => {
|
|
43
|
+
expect(cache.get(key)).toBe(value);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('set replaces value', () => {
|
|
47
|
+
const newValue = `new-${value}`;
|
|
48
|
+
|
|
49
|
+
cache.set(key, newValue);
|
|
50
|
+
|
|
51
|
+
expect(cache.get(key)).toBe(newValue);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('delete removes key', () => {
|
|
55
|
+
cache.delete(key);
|
|
56
|
+
|
|
57
|
+
expect(cache.has(key)).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('clear removes all keys', () => {
|
|
61
|
+
cache.clear();
|
|
62
|
+
|
|
63
|
+
Object.keys(elements).forEach(key => expect(cache.has(key)).toBe(false));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('removes keys after five minutes', () => {
|
|
67
|
+
jest.advanceTimersByTime(1000 * 60 * 5 - 1);
|
|
68
|
+
Object.keys(elements).forEach(key => expect(cache.has(key)).toBe(true));
|
|
69
|
+
|
|
70
|
+
jest.advanceTimersByTime(1);
|
|
71
|
+
Object.keys(elements).forEach(key => expect(cache.has(key)).toBe(false));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('when key has custom ttl', () => {
|
|
75
|
+
const ttl = 1000 * 60 * 10;
|
|
76
|
+
|
|
77
|
+
beforeEach(() => cache.set(key, value, ttl));
|
|
78
|
+
|
|
79
|
+
test('removes key after custom ttl', () => {
|
|
80
|
+
jest.advanceTimersByTime(ttl - 1);
|
|
81
|
+
expect(cache.has(key)).toBe(true);
|
|
82
|
+
|
|
83
|
+
jest.advanceTimersByTime(1);
|
|
84
|
+
expect(cache.has(key)).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('when key has negative ttl', () => {
|
|
89
|
+
beforeEach(() => cache.set(key, value, -1));
|
|
90
|
+
|
|
91
|
+
test('never removes key', () => {
|
|
92
|
+
jest.runOnlyPendingTimers();
|
|
93
|
+
|
|
94
|
+
expect(cache.has(key)).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const MINUTE = 1000 * 60;
|
|
2
|
+
const DEFAULT_TTL = 5 * MINUTE;
|
|
3
|
+
|
|
4
|
+
export interface TtlCacheOptions {
|
|
5
|
+
ttlMs?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class TtlCache<T = any> {
|
|
9
|
+
private data: Map<string, T>;
|
|
10
|
+
private timers: Map<string, ReturnType<typeof setTimeout>>;
|
|
11
|
+
private ttl: number;
|
|
12
|
+
|
|
13
|
+
constructor({ ttlMs = DEFAULT_TTL }: TtlCacheOptions = {}) {
|
|
14
|
+
if (ttlMs < 0) {
|
|
15
|
+
throw new Error('default TTL must be zero or greater');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.data = new Map();
|
|
19
|
+
this.timers = new Map();
|
|
20
|
+
this.ttl = ttlMs;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set(key: string, value: T, ttl?: number) {
|
|
24
|
+
this.setTimeout(key, ttl ?? this.ttl);
|
|
25
|
+
this.data.set(key, value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get(key: string) {
|
|
29
|
+
return this.data.get(key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
has(key: string) {
|
|
33
|
+
return this.data.has(key);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
delete(key: string) {
|
|
37
|
+
this.cancelTimeout(key);
|
|
38
|
+
this.data.delete(key);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
clear() {
|
|
42
|
+
this.data.clear();
|
|
43
|
+
this.timers.forEach(value => clearTimeout(value));
|
|
44
|
+
this.timers.clear();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private cancelTimeout(key: string) {
|
|
48
|
+
const id = this.timers.get(key);
|
|
49
|
+
if (id) {
|
|
50
|
+
clearTimeout(id);
|
|
51
|
+
this.timers.delete(key);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private setTimeout(key: string, ttl: number) {
|
|
56
|
+
this.cancelTimeout(key);
|
|
57
|
+
if (ttl < 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const id = setTimeout(() => this.delete(key), ttl);
|
|
62
|
+
this.timers.set(key, id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
import { Log } from '@servicetitan/log-service';
|
|
2
|
+
import { ExposedDependencies } from '../common';
|
|
2
3
|
import { BundleInfo } from './get-bundle-info';
|
|
3
4
|
|
|
4
5
|
export function validateBundleInfo(
|
|
5
6
|
{ mismatch }: BundleInfo,
|
|
6
|
-
{
|
|
7
|
+
{
|
|
8
|
+
exposedDependencies = {},
|
|
9
|
+
logService,
|
|
10
|
+
packageName,
|
|
11
|
+
}: { exposedDependencies?: ExposedDependencies; logService?: Log; packageName: string }
|
|
7
12
|
) {
|
|
8
|
-
if (mismatch) {
|
|
9
|
-
|
|
13
|
+
if (!mismatch || !logService) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const data = { package: packageName, dependencies: JSON.stringify(mismatch) };
|
|
18
|
+
if (Object.keys(exposedDependencies).length === 0) {
|
|
19
|
+
logService.warning({
|
|
20
|
+
category: 'MicroFrontends.NoExposedDependencies',
|
|
21
|
+
message:
|
|
22
|
+
'Cannot use light bundle because the host is not exposing dependencies or the list of shared dependencies is empty.',
|
|
23
|
+
data,
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
logService.warning({
|
|
10
27
|
category: 'MicroFrontends.DependenciesMismatch',
|
|
11
28
|
message: 'Some of the package dependencies have incompatible versions.',
|
|
12
|
-
data
|
|
29
|
+
data,
|
|
13
30
|
});
|
|
14
31
|
}
|
|
15
32
|
}
|