@backstage/frontend-app-api 0.6.4 → 0.6.5-next.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/CHANGELOG.md +15 -0
- package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js +53 -0
- package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js.map +1 -0
- package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js +35 -0
- package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js.map +1 -0
- package/dist/app/src/components/Root/LogoFull.esm.js +33 -0
- package/dist/app/src/components/Root/LogoFull.esm.js.map +1 -0
- package/dist/app/src/components/Root/LogoIcon.esm.js +33 -0
- package/dist/app/src/components/Root/LogoIcon.esm.js.map +1 -0
- package/dist/app-defaults/src/defaults/apis.esm.js +221 -0
- package/dist/app-defaults/src/defaults/apis.esm.js.map +1 -0
- package/dist/app-defaults/src/defaults/components.esm.js +46 -0
- package/dist/app-defaults/src/defaults/components.esm.js.map +1 -0
- package/dist/app-defaults/src/defaults/icons.esm.js +51 -0
- package/dist/app-defaults/src/defaults/icons.esm.js.map +1 -0
- package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js +114 -0
- package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js.map +1 -0
- package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js +84 -0
- package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js.map +1 -0
- package/dist/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js +128 -0
- package/dist/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js.map +1 -0
- package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js +105 -0
- package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js.map +1 -0
- package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js +275 -0
- package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js.map +1 -0
- package/dist/core-app-api/src/app/AppThemeProvider.esm.js +60 -0
- package/dist/core-app-api/src/app/AppThemeProvider.esm.js.map +1 -0
- package/dist/core-app-api/src/app/defaultConfigLoader.esm.js +33 -0
- package/dist/core-app-api/src/app/defaultConfigLoader.esm.js.map +1 -0
- package/dist/core-app-api/src/app/isProtectedApp.esm.js +9 -0
- package/dist/core-app-api/src/app/isProtectedApp.esm.js.map +1 -0
- package/dist/core-app-api/src/app/overrideBaseUrlConfigs.esm.js +50 -0
- package/dist/core-app-api/src/app/overrideBaseUrlConfigs.esm.js.map +1 -0
- package/dist/core-app-api/src/lib/subjects.esm.js +75 -0
- package/dist/core-app-api/src/lib/subjects.esm.js.map +1 -0
- package/dist/core-plugin-api/src/translation/TranslationRef.esm.js +13 -0
- package/dist/core-plugin-api/src/translation/TranslationRef.esm.js.map +1 -0
- package/dist/core-plugin-api/src/translation/TranslationResource.esm.js +13 -0
- package/dist/core-plugin-api/src/translation/TranslationResource.esm.js.map +1 -0
- package/dist/extensions/App.esm.js +39 -0
- package/dist/extensions/App.esm.js.map +1 -0
- package/dist/extensions/AppLayout.esm.js +34 -0
- package/dist/extensions/AppLayout.esm.js.map +1 -0
- package/dist/extensions/AppNav.esm.js +63 -0
- package/dist/extensions/AppNav.esm.js.map +1 -0
- package/dist/extensions/AppRoot.esm.js +125 -0
- package/dist/extensions/AppRoot.esm.js.map +1 -0
- package/dist/extensions/AppRoutes.esm.js +43 -0
- package/dist/extensions/AppRoutes.esm.js.map +1 -0
- package/dist/extensions/components.esm.js +52 -0
- package/dist/extensions/components.esm.js.map +1 -0
- package/dist/extensions/elements.esm.js +26 -0
- package/dist/extensions/elements.esm.js.map +1 -0
- package/dist/extensions/themes.esm.js +23 -0
- package/dist/extensions/themes.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/routing/ExternalRouteRef.esm.js +13 -0
- package/dist/frontend-plugin-api/src/routing/ExternalRouteRef.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/routing/RouteRef.esm.js +13 -0
- package/dist/frontend-plugin-api/src/routing/RouteRef.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/routing/SubRouteRef.esm.js +13 -0
- package/dist/frontend-plugin-api/src/routing/SubRouteRef.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/wiring/createExtension.esm.js +17 -0
- package/dist/frontend-plugin-api/src/wiring/createExtension.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/wiring/createExtensionOverrides.esm.js +17 -0
- package/dist/frontend-plugin-api/src/wiring/createExtensionOverrides.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/wiring/createPlugin.esm.js +15 -0
- package/dist/frontend-plugin-api/src/wiring/createPlugin.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js +41 -0
- package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -0
- package/dist/index.esm.js +1 -3030
- package/dist/index.esm.js.map +1 -1
- package/dist/routing/RouteResolver.esm.js +133 -0
- package/dist/routing/RouteResolver.esm.js.map +1 -0
- package/dist/routing/RouteTracker.esm.js +73 -0
- package/dist/routing/RouteTracker.esm.js.map +1 -0
- package/dist/routing/collectRouteIds.esm.js +40 -0
- package/dist/routing/collectRouteIds.esm.js.map +1 -0
- package/dist/routing/extractRouteInfoFromAppNode.esm.js +85 -0
- package/dist/routing/extractRouteInfoFromAppNode.esm.js.map +1 -0
- package/dist/routing/getBasePath.esm.js +13 -0
- package/dist/routing/getBasePath.esm.js.map +1 -0
- package/dist/routing/resolveRouteBindings.esm.js +66 -0
- package/dist/routing/resolveRouteBindings.esm.js.map +1 -0
- package/dist/routing/toLegacyPlugin.esm.js +35 -0
- package/dist/routing/toLegacyPlugin.esm.js.map +1 -0
- package/dist/tree/createAppTree.esm.js +21 -0
- package/dist/tree/createAppTree.esm.js.map +1 -0
- package/dist/tree/instantiateAppNodeTree.esm.js +146 -0
- package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -0
- package/dist/tree/readAppExtensionsConfig.esm.js +106 -0
- package/dist/tree/readAppExtensionsConfig.esm.js.map +1 -0
- package/dist/tree/resolveAppNodeSpecs.esm.js +168 -0
- package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -0
- package/dist/tree/resolveAppTree.esm.js +97 -0
- package/dist/tree/resolveAppTree.esm.js.map +1 -0
- package/dist/wiring/InternalAppContext.esm.js +6 -0
- package/dist/wiring/InternalAppContext.esm.js.map +1 -0
- package/dist/wiring/createApp.esm.js +285 -0
- package/dist/wiring/createApp.esm.js.map +1 -0
- package/dist/wiring/discovery.esm.js +56 -0
- package/dist/wiring/discovery.esm.js.map +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { BehaviorSubject } from '../../../lib/subjects.esm.js';
|
|
2
|
+
import '@backstage/core-plugin-api';
|
|
3
|
+
|
|
4
|
+
var __accessCheck = (obj, member, msg) => {
|
|
5
|
+
if (!member.has(obj))
|
|
6
|
+
throw TypeError("Cannot " + msg);
|
|
7
|
+
};
|
|
8
|
+
var __privateGet = (obj, member, getter) => {
|
|
9
|
+
__accessCheck(obj, member, "read from private field");
|
|
10
|
+
return member.get(obj);
|
|
11
|
+
};
|
|
12
|
+
var __privateAdd = (obj, member, value) => {
|
|
13
|
+
if (member.has(obj))
|
|
14
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
15
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
16
|
+
};
|
|
17
|
+
var __privateSet = (obj, member, value, setter) => {
|
|
18
|
+
__accessCheck(obj, member, "write to private field");
|
|
19
|
+
member.set(obj, value);
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
var _languages, _language, _subject;
|
|
23
|
+
const STORAGE_KEY = "language";
|
|
24
|
+
const DEFAULT_LANGUAGE = "en";
|
|
25
|
+
const _AppLanguageSelector = class _AppLanguageSelector {
|
|
26
|
+
constructor(languages, initialLanguage) {
|
|
27
|
+
__privateAdd(this, _languages, void 0);
|
|
28
|
+
__privateAdd(this, _language, void 0);
|
|
29
|
+
__privateAdd(this, _subject, void 0);
|
|
30
|
+
__privateSet(this, _languages, languages);
|
|
31
|
+
__privateSet(this, _language, initialLanguage);
|
|
32
|
+
__privateSet(this, _subject, new BehaviorSubject({
|
|
33
|
+
language: __privateGet(this, _language)
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
static create(options) {
|
|
37
|
+
var _a, _b;
|
|
38
|
+
const languages = (_a = options == null ? void 0 : options.availableLanguages) != null ? _a : [DEFAULT_LANGUAGE];
|
|
39
|
+
if (languages.length !== new Set(languages).size) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Supported languages may not contain duplicates, got '${languages.join(
|
|
42
|
+
"', '"
|
|
43
|
+
)}'`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (!languages.includes(DEFAULT_LANGUAGE)) {
|
|
47
|
+
throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);
|
|
48
|
+
}
|
|
49
|
+
const initialLanguage = (_b = options == null ? void 0 : options.defaultLanguage) != null ? _b : DEFAULT_LANGUAGE;
|
|
50
|
+
if (!languages.includes(initialLanguage)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Initial language must be one of the supported languages, got '${initialLanguage}'`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return new _AppLanguageSelector(languages, initialLanguage);
|
|
56
|
+
}
|
|
57
|
+
static createWithStorage(options) {
|
|
58
|
+
const selector = _AppLanguageSelector.create(options);
|
|
59
|
+
if (!window.localStorage) {
|
|
60
|
+
return selector;
|
|
61
|
+
}
|
|
62
|
+
const storedLanguage = window.localStorage.getItem(STORAGE_KEY);
|
|
63
|
+
const { languages } = selector.getAvailableLanguages();
|
|
64
|
+
if (storedLanguage && languages.includes(storedLanguage)) {
|
|
65
|
+
selector.setLanguage(storedLanguage);
|
|
66
|
+
}
|
|
67
|
+
selector.language$().subscribe(({ language }) => {
|
|
68
|
+
if (language !== window.localStorage.getItem(STORAGE_KEY)) {
|
|
69
|
+
window.localStorage.setItem(STORAGE_KEY, language);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
window.addEventListener("storage", (event) => {
|
|
73
|
+
var _a;
|
|
74
|
+
if (event.key === STORAGE_KEY) {
|
|
75
|
+
const language = (_a = localStorage.getItem(STORAGE_KEY)) != null ? _a : void 0;
|
|
76
|
+
if (language) {
|
|
77
|
+
selector.setLanguage(language);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return selector;
|
|
82
|
+
}
|
|
83
|
+
getAvailableLanguages() {
|
|
84
|
+
return { languages: __privateGet(this, _languages).slice() };
|
|
85
|
+
}
|
|
86
|
+
setLanguage(language) {
|
|
87
|
+
const lng = language != null ? language : DEFAULT_LANGUAGE;
|
|
88
|
+
if (lng === __privateGet(this, _language)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (lng && !__privateGet(this, _languages).includes(lng)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Failed to change language to '${lng}', available languages are '${__privateGet(this, _languages).join(
|
|
94
|
+
"', '"
|
|
95
|
+
)}'`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
__privateSet(this, _language, lng);
|
|
99
|
+
__privateGet(this, _subject).next({ language: lng });
|
|
100
|
+
}
|
|
101
|
+
getLanguage() {
|
|
102
|
+
return { language: __privateGet(this, _language) };
|
|
103
|
+
}
|
|
104
|
+
language$() {
|
|
105
|
+
return __privateGet(this, _subject);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
_languages = new WeakMap();
|
|
109
|
+
_language = new WeakMap();
|
|
110
|
+
_subject = new WeakMap();
|
|
111
|
+
let AppLanguageSelector = _AppLanguageSelector;
|
|
112
|
+
|
|
113
|
+
export { AppLanguageSelector, DEFAULT_LANGUAGE };
|
|
114
|
+
//# sourceMappingURL=AppLanguageSelector.esm.js.map
|
package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLanguageSelector.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageApi } from '@backstage/core-plugin-api/alpha';\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib';\n\nconst STORAGE_KEY = 'language';\nexport const DEFAULT_LANGUAGE = 'en';\n\n/** @alpha */\nexport interface AppLanguageSelectorOptions {\n defaultLanguage?: string;\n availableLanguages?: string[];\n}\n\n/**\n * Exposes the available languages in the app and allows for switching of the active language.\n *\n * @alpha\n */\nexport class AppLanguageSelector implements AppLanguageApi {\n static create(options?: AppLanguageSelectorOptions) {\n const languages = options?.availableLanguages ?? [DEFAULT_LANGUAGE];\n if (languages.length !== new Set(languages).size) {\n throw new Error(\n `Supported languages may not contain duplicates, got '${languages.join(\n \"', '\",\n )}'`,\n );\n }\n if (!languages.includes(DEFAULT_LANGUAGE)) {\n throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);\n }\n\n const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE;\n if (!languages.includes(initialLanguage)) {\n throw new Error(\n `Initial language must be one of the supported languages, got '${initialLanguage}'`,\n );\n }\n\n return new AppLanguageSelector(languages, initialLanguage);\n }\n\n static createWithStorage(options?: AppLanguageSelectorOptions) {\n const selector = AppLanguageSelector.create(options);\n\n if (!window.localStorage) {\n return selector;\n }\n\n const storedLanguage = window.localStorage.getItem(STORAGE_KEY);\n const { languages } = selector.getAvailableLanguages();\n if (storedLanguage && languages.includes(storedLanguage)) {\n selector.setLanguage(storedLanguage);\n }\n\n selector.language$().subscribe(({ language }) => {\n if (language !== window.localStorage.getItem(STORAGE_KEY)) {\n window.localStorage.setItem(STORAGE_KEY, language);\n }\n });\n\n window.addEventListener('storage', event => {\n if (event.key === STORAGE_KEY) {\n const language = localStorage.getItem(STORAGE_KEY) ?? undefined;\n if (language) {\n selector.setLanguage(language);\n }\n }\n });\n\n return selector;\n }\n\n #languages: string[];\n #language: string;\n #subject: BehaviorSubject<{ language: string }>;\n\n private constructor(languages: string[], initialLanguage: string) {\n this.#languages = languages;\n this.#language = initialLanguage;\n this.#subject = new BehaviorSubject<{ language: string }>({\n language: this.#language,\n });\n }\n\n getAvailableLanguages(): { languages: string[] } {\n return { languages: this.#languages.slice() };\n }\n\n setLanguage(language?: string | undefined): void {\n const lng = language ?? DEFAULT_LANGUAGE;\n if (lng === this.#language) {\n return;\n }\n if (lng && !this.#languages.includes(lng)) {\n throw new Error(\n `Failed to change language to '${lng}', available languages are '${this.#languages.join(\n \"', '\",\n )}'`,\n );\n }\n this.#language = lng;\n this.#subject.next({ language: lng });\n }\n\n getLanguage(): { language: string } {\n return { language: this.#language };\n }\n\n language$(): Observable<{ language: string }> {\n return this.#subject;\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { FeatureFlagState } from '@backstage/core-plugin-api';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField = (obj, key, value) => {
|
|
6
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
+
return value;
|
|
8
|
+
};
|
|
9
|
+
function validateFlagName(name) {
|
|
10
|
+
if (name.length < 3) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`The '${name}' feature flag must have a minimum length of three characters.`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
if (name.length > 150) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`The '${name}' feature flag must not exceed 150 characters.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. Examples: feature-flag-one, alpha, release-2020`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
class LocalStorageFeatureFlags {
|
|
27
|
+
constructor() {
|
|
28
|
+
__publicField(this, "registeredFeatureFlags", []);
|
|
29
|
+
__publicField(this, "flags");
|
|
30
|
+
}
|
|
31
|
+
registerFlag(flag) {
|
|
32
|
+
validateFlagName(flag.name);
|
|
33
|
+
this.registeredFeatureFlags.push(flag);
|
|
34
|
+
}
|
|
35
|
+
getRegisteredFlags() {
|
|
36
|
+
return this.registeredFeatureFlags.slice();
|
|
37
|
+
}
|
|
38
|
+
isActive(name) {
|
|
39
|
+
if (!this.flags) {
|
|
40
|
+
this.flags = this.load();
|
|
41
|
+
}
|
|
42
|
+
return this.flags.get(name) === FeatureFlagState.Active;
|
|
43
|
+
}
|
|
44
|
+
save(options) {
|
|
45
|
+
if (!this.flags) {
|
|
46
|
+
this.flags = this.load();
|
|
47
|
+
}
|
|
48
|
+
if (!options.merge) {
|
|
49
|
+
this.flags.clear();
|
|
50
|
+
}
|
|
51
|
+
for (const [name, state] of Object.entries(options.states)) {
|
|
52
|
+
this.flags.set(name, state);
|
|
53
|
+
}
|
|
54
|
+
const enabled = Array.from(this.flags.entries()).filter(
|
|
55
|
+
([, state]) => state === FeatureFlagState.Active
|
|
56
|
+
);
|
|
57
|
+
window.localStorage.setItem(
|
|
58
|
+
"featureFlags",
|
|
59
|
+
JSON.stringify(Object.fromEntries(enabled))
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
load() {
|
|
63
|
+
try {
|
|
64
|
+
const jsonStr = window.localStorage.getItem("featureFlags");
|
|
65
|
+
if (!jsonStr) {
|
|
66
|
+
return /* @__PURE__ */ new Map();
|
|
67
|
+
}
|
|
68
|
+
const json = JSON.parse(jsonStr);
|
|
69
|
+
if (typeof json !== "object" || json === null || Array.isArray(json)) {
|
|
70
|
+
return /* @__PURE__ */ new Map();
|
|
71
|
+
}
|
|
72
|
+
const entries = Object.entries(json).filter(([name, value]) => {
|
|
73
|
+
validateFlagName(name);
|
|
74
|
+
return value === FeatureFlagState.Active;
|
|
75
|
+
});
|
|
76
|
+
return new Map(entries);
|
|
77
|
+
} catch {
|
|
78
|
+
return /* @__PURE__ */ new Map();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { LocalStorageFeatureFlags, validateFlagName };
|
|
84
|
+
//# sourceMappingURL=LocalStorageFeatureFlags.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalStorageFeatureFlags.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FeatureFlagState,\n FeatureFlagsApi,\n FeatureFlag,\n FeatureFlagsSaveOptions,\n} from '@backstage/core-plugin-api';\n\nexport function validateFlagName(name: string): void {\n if (name.length < 3) {\n throw new Error(\n `The '${name}' feature flag must have a minimum length of three characters.`,\n );\n }\n\n if (name.length > 150) {\n throw new Error(\n `The '${name}' feature flag must not exceed 150 characters.`,\n );\n }\n\n if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {\n throw new Error(\n `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ` +\n 'Examples: feature-flag-one, alpha, release-2020',\n );\n }\n}\n\n/**\n * A feature flags implementation that stores the flags in the browser's local\n * storage.\n *\n * @public\n */\nexport class LocalStorageFeatureFlags implements FeatureFlagsApi {\n private registeredFeatureFlags: FeatureFlag[] = [];\n private flags?: Map<string, FeatureFlagState>;\n\n registerFlag(flag: FeatureFlag) {\n validateFlagName(flag.name);\n this.registeredFeatureFlags.push(flag);\n }\n\n getRegisteredFlags(): FeatureFlag[] {\n return this.registeredFeatureFlags.slice();\n }\n\n isActive(name: string): boolean {\n if (!this.flags) {\n this.flags = this.load();\n }\n return this.flags.get(name) === FeatureFlagState.Active;\n }\n\n save(options: FeatureFlagsSaveOptions): void {\n if (!this.flags) {\n this.flags = this.load();\n }\n if (!options.merge) {\n this.flags.clear();\n }\n for (const [name, state] of Object.entries(options.states)) {\n this.flags.set(name, state);\n }\n\n const enabled = Array.from(this.flags.entries()).filter(\n ([, state]) => state === FeatureFlagState.Active,\n );\n window.localStorage.setItem(\n 'featureFlags',\n JSON.stringify(Object.fromEntries(enabled)),\n );\n }\n\n private load(): Map<string, FeatureFlagState> {\n try {\n const jsonStr = window.localStorage.getItem('featureFlags');\n if (!jsonStr) {\n return new Map();\n }\n const json = JSON.parse(jsonStr) as unknown;\n if (typeof json !== 'object' || json === null || Array.isArray(json)) {\n return new Map();\n }\n\n const entries = Object.entries(json).filter(([name, value]) => {\n validateFlagName(name);\n return value === FeatureFlagState.Active;\n });\n\n return new Map(entries);\n } catch {\n return new Map();\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { startCookieAuthRefresh } from './startCookieAuthRefresh.esm.js';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField = (obj, key, value) => {
|
|
6
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
+
return value;
|
|
8
|
+
};
|
|
9
|
+
var __accessCheck = (obj, member, msg) => {
|
|
10
|
+
if (!member.has(obj))
|
|
11
|
+
throw TypeError("Cannot " + msg);
|
|
12
|
+
};
|
|
13
|
+
var __privateGet = (obj, member, getter) => {
|
|
14
|
+
__accessCheck(obj, member, "read from private field");
|
|
15
|
+
return member.get(obj);
|
|
16
|
+
};
|
|
17
|
+
var __privateAdd = (obj, member, value) => {
|
|
18
|
+
if (member.has(obj))
|
|
19
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
20
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
21
|
+
};
|
|
22
|
+
var __privateSet = (obj, member, value, setter) => {
|
|
23
|
+
__accessCheck(obj, member, "write to private field");
|
|
24
|
+
member.set(obj, value);
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
var _cookieAuthSignOut;
|
|
28
|
+
function mkError(thing) {
|
|
29
|
+
return new Error(
|
|
30
|
+
`Tried to access IdentityApi ${thing} before app was loaded`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
function logDeprecation(thing) {
|
|
34
|
+
console.warn(
|
|
35
|
+
`WARNING: Call to ${thing} is deprecated and will break in the future`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
class AppIdentityProxy {
|
|
39
|
+
constructor() {
|
|
40
|
+
__publicField(this, "target");
|
|
41
|
+
__publicField(this, "waitForTarget");
|
|
42
|
+
__publicField(this, "resolveTarget", () => {
|
|
43
|
+
});
|
|
44
|
+
__publicField(this, "signOutTargetUrl", "/");
|
|
45
|
+
__privateAdd(this, _cookieAuthSignOut, void 0);
|
|
46
|
+
this.waitForTarget = new Promise((resolve) => {
|
|
47
|
+
this.resolveTarget = resolve;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// This is called by the app manager once the sign-in page provides us with an implementation
|
|
51
|
+
setTarget(identityApi, targetOptions) {
|
|
52
|
+
this.target = identityApi;
|
|
53
|
+
this.signOutTargetUrl = targetOptions.signOutTargetUrl;
|
|
54
|
+
this.resolveTarget(identityApi);
|
|
55
|
+
}
|
|
56
|
+
getUserId() {
|
|
57
|
+
if (!this.target) {
|
|
58
|
+
throw mkError("getUserId");
|
|
59
|
+
}
|
|
60
|
+
if (!this.target.getUserId) {
|
|
61
|
+
throw new Error("IdentityApi does not implement getUserId");
|
|
62
|
+
}
|
|
63
|
+
logDeprecation("getUserId");
|
|
64
|
+
return this.target.getUserId();
|
|
65
|
+
}
|
|
66
|
+
getProfile() {
|
|
67
|
+
if (!this.target) {
|
|
68
|
+
throw mkError("getProfile");
|
|
69
|
+
}
|
|
70
|
+
if (!this.target.getProfile) {
|
|
71
|
+
throw new Error("IdentityApi does not implement getProfile");
|
|
72
|
+
}
|
|
73
|
+
logDeprecation("getProfile");
|
|
74
|
+
return this.target.getProfile();
|
|
75
|
+
}
|
|
76
|
+
async getProfileInfo() {
|
|
77
|
+
return this.waitForTarget.then((target) => target.getProfileInfo());
|
|
78
|
+
}
|
|
79
|
+
async getBackstageIdentity() {
|
|
80
|
+
const identity = await this.waitForTarget.then(
|
|
81
|
+
(target) => target.getBackstageIdentity()
|
|
82
|
+
);
|
|
83
|
+
if (!identity.userEntityRef.match(/^.*:.*\/.*$/)) {
|
|
84
|
+
console.warn(
|
|
85
|
+
`WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return identity;
|
|
89
|
+
}
|
|
90
|
+
async getCredentials() {
|
|
91
|
+
return this.waitForTarget.then((target) => target.getCredentials());
|
|
92
|
+
}
|
|
93
|
+
async getIdToken() {
|
|
94
|
+
return this.waitForTarget.then((target) => {
|
|
95
|
+
if (!target.getIdToken) {
|
|
96
|
+
throw new Error("IdentityApi does not implement getIdToken");
|
|
97
|
+
}
|
|
98
|
+
logDeprecation("getIdToken");
|
|
99
|
+
return target.getIdToken();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async signOut() {
|
|
103
|
+
var _a;
|
|
104
|
+
await this.waitForTarget.then((target) => target.signOut());
|
|
105
|
+
await ((_a = __privateGet(this, _cookieAuthSignOut)) == null ? void 0 : _a.call(this));
|
|
106
|
+
window.location.href = this.signOutTargetUrl;
|
|
107
|
+
}
|
|
108
|
+
enableCookieAuth(ctx) {
|
|
109
|
+
if (__privateGet(this, _cookieAuthSignOut)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const stopRefresh = startCookieAuthRefresh(ctx);
|
|
113
|
+
__privateSet(this, _cookieAuthSignOut, async () => {
|
|
114
|
+
stopRefresh();
|
|
115
|
+
const appBaseUrl = await ctx.discoveryApi.getBaseUrl("app");
|
|
116
|
+
try {
|
|
117
|
+
await ctx.fetchApi.fetch(`${appBaseUrl}/.backstage/auth/v1/cookie`, {
|
|
118
|
+
method: "DELETE"
|
|
119
|
+
});
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
_cookieAuthSignOut = new WeakMap();
|
|
126
|
+
|
|
127
|
+
export { AppIdentityProxy };
|
|
128
|
+
//# sourceMappingURL=AppIdentityProxy.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppIdentityProxy.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n ErrorApi,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { startCookieAuthRefresh } from './startCookieAuthRefresh';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\nfunction logDeprecation(thing: string) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: Call to ${thing} is deprecated and will break in the future`,\n );\n}\n\n// We use this for a period of backwards compatibility. It is a hidden\n// compatibility that will allow old plugins to continue working for a limited time.\ntype CompatibilityIdentityApi = IdentityApi & {\n getUserId?(): string;\n getIdToken?(): Promise<string | undefined>;\n getProfile?(): ProfileInfo;\n};\n\n/**\n * Implementation of the connection between the App-wide IdentityApi\n * and sign-in page.\n */\nexport class AppIdentityProxy implements IdentityApi {\n private target?: CompatibilityIdentityApi;\n private waitForTarget: Promise<CompatibilityIdentityApi>;\n private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};\n private signOutTargetUrl = '/';\n\n #cookieAuthSignOut?: () => Promise<void>;\n\n constructor() {\n this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {\n this.resolveTarget = resolve;\n });\n }\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(\n identityApi: CompatibilityIdentityApi,\n targetOptions: { signOutTargetUrl: string },\n ) {\n this.target = identityApi;\n this.signOutTargetUrl = targetOptions.signOutTargetUrl;\n this.resolveTarget(identityApi);\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n if (!this.target.getUserId) {\n throw new Error('IdentityApi does not implement getUserId');\n }\n logDeprecation('getUserId');\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n if (!this.target.getProfile) {\n throw new Error('IdentityApi does not implement getProfile');\n }\n logDeprecation('getProfile');\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n return this.waitForTarget.then(target => target.getProfileInfo());\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n const identity = await this.waitForTarget.then(target =>\n target.getBackstageIdentity(),\n );\n if (!identity.userEntityRef.match(/^.*:.*\\/.*$/)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. ` +\n `It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`,\n );\n }\n\n return identity;\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n return this.waitForTarget.then(target => target.getCredentials());\n }\n\n async getIdToken(): Promise<string | undefined> {\n return this.waitForTarget.then(target => {\n if (!target.getIdToken) {\n throw new Error('IdentityApi does not implement getIdToken');\n }\n logDeprecation('getIdToken');\n return target.getIdToken();\n });\n }\n\n async signOut(): Promise<void> {\n await this.waitForTarget.then(target => target.signOut());\n\n await this.#cookieAuthSignOut?.();\n\n window.location.href = this.signOutTargetUrl;\n }\n\n enableCookieAuth(ctx: {\n errorApi: ErrorApi;\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n }) {\n if (this.#cookieAuthSignOut) {\n return;\n }\n\n const stopRefresh = startCookieAuthRefresh(ctx);\n\n this.#cookieAuthSignOut = async () => {\n stopRefresh();\n\n // It is fine if we do NOT worry yet about deleting cookies for OTHER backends like techdocs\n const appBaseUrl = await ctx.discoveryApi.getBaseUrl('app');\n try {\n await ctx.fetchApi.fetch(`${appBaseUrl}/.backstage/auth/v1/cookie`, {\n method: 'DELETE',\n });\n } catch {\n // Ignore the error for those who use static serving of the frontend\n }\n };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const PLUGIN_ID = "app";
|
|
2
|
+
const CHANNEL_ID = `${PLUGIN_ID}-auth-cookie-expires-at`;
|
|
3
|
+
const MIN_BASE_DELAY_MS = 5 * 6e4;
|
|
4
|
+
const ERROR_BACKOFF_START = 5e3;
|
|
5
|
+
const ERROR_BACKOFF_FACTOR = 2;
|
|
6
|
+
const ERROR_BACKOFF_MAX = 5 * 6e4;
|
|
7
|
+
function startCookieAuthRefresh({
|
|
8
|
+
discoveryApi,
|
|
9
|
+
fetchApi,
|
|
10
|
+
errorApi
|
|
11
|
+
}) {
|
|
12
|
+
let stopped = false;
|
|
13
|
+
let timeout;
|
|
14
|
+
let firstError = true;
|
|
15
|
+
let errorBackoff = ERROR_BACKOFF_START;
|
|
16
|
+
const channel = "BroadcastChannel" in window ? new BroadcastChannel(CHANNEL_ID) : void 0;
|
|
17
|
+
const getDelay = (expiresAt) => {
|
|
18
|
+
const margin = (1 + 3 * Math.random()) * 6e4;
|
|
19
|
+
const delay = Math.max(expiresAt - Date.now(), MIN_BASE_DELAY_MS) - margin;
|
|
20
|
+
return delay;
|
|
21
|
+
};
|
|
22
|
+
const refresh = async () => {
|
|
23
|
+
try {
|
|
24
|
+
const baseUrl = await discoveryApi.getBaseUrl(PLUGIN_ID);
|
|
25
|
+
const requestUrl = `${baseUrl}/.backstage/auth/v1/cookie`;
|
|
26
|
+
const res = await fetchApi.fetch(requestUrl, {
|
|
27
|
+
credentials: "include"
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Request failed with status ${res.status} ${res.statusText}, see request towards ${requestUrl} for more details`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const data = await res.json();
|
|
35
|
+
if (!data.expiresAt) {
|
|
36
|
+
throw new Error("No expiration date in response");
|
|
37
|
+
}
|
|
38
|
+
const expiresAt = Date.parse(data.expiresAt);
|
|
39
|
+
if (Number.isNaN(expiresAt)) {
|
|
40
|
+
throw new Error("Invalid expiration date in response");
|
|
41
|
+
}
|
|
42
|
+
firstError = true;
|
|
43
|
+
channel == null ? void 0 : channel.postMessage({
|
|
44
|
+
action: "COOKIE_REFRESH_SUCCESS",
|
|
45
|
+
payload: { expiresAt: new Date(expiresAt).toISOString() }
|
|
46
|
+
});
|
|
47
|
+
scheduleRefresh(getDelay(expiresAt));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (firstError) {
|
|
50
|
+
firstError = false;
|
|
51
|
+
errorBackoff = ERROR_BACKOFF_START;
|
|
52
|
+
} else {
|
|
53
|
+
errorBackoff = Math.min(
|
|
54
|
+
ERROR_BACKOFF_MAX,
|
|
55
|
+
errorBackoff * ERROR_BACKOFF_FACTOR
|
|
56
|
+
);
|
|
57
|
+
console.error("Session cookie refresh failed", error);
|
|
58
|
+
errorApi.post(
|
|
59
|
+
new Error(
|
|
60
|
+
`Session refresh failed, see developer console for details`
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
scheduleRefresh(errorBackoff);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const onMessage = (event) => {
|
|
68
|
+
const { data } = event;
|
|
69
|
+
if (data === null || typeof data !== "object") {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if ("action" in data && data.action === "COOKIE_REFRESH_SUCCESS") {
|
|
73
|
+
const expiresAt = Date.parse(data.payload.expiresAt);
|
|
74
|
+
if (Number.isNaN(expiresAt)) {
|
|
75
|
+
console.warn(
|
|
76
|
+
"Received invalid expiration from session refresh channel"
|
|
77
|
+
);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
scheduleRefresh(getDelay(expiresAt));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
function scheduleRefresh(delayMs) {
|
|
84
|
+
if (stopped) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (timeout) {
|
|
88
|
+
clearTimeout(timeout);
|
|
89
|
+
}
|
|
90
|
+
timeout = setTimeout(refresh, delayMs);
|
|
91
|
+
}
|
|
92
|
+
channel == null ? void 0 : channel.addEventListener("message", onMessage);
|
|
93
|
+
refresh();
|
|
94
|
+
return () => {
|
|
95
|
+
stopped = true;
|
|
96
|
+
if (timeout) {
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
}
|
|
99
|
+
channel == null ? void 0 : channel.removeEventListener("message", onMessage);
|
|
100
|
+
channel == null ? void 0 : channel.close();
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { startCookieAuthRefresh };
|
|
105
|
+
//# sourceMappingURL=startCookieAuthRefresh.esm.js.map
|
package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startCookieAuthRefresh.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryApi, ErrorApi, FetchApi } from '@backstage/core-plugin-api';\n\nconst PLUGIN_ID = 'app';\nconst CHANNEL_ID = `${PLUGIN_ID}-auth-cookie-expires-at`;\n\nconst MIN_BASE_DELAY_MS = 5 * 60_000;\n\nconst ERROR_BACKOFF_START = 5_000;\nconst ERROR_BACKOFF_FACTOR = 2;\nconst ERROR_BACKOFF_MAX = 5 * 60_000;\n\n// Messaging implementation and IDs must match\n// plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx\nexport function startCookieAuthRefresh({\n discoveryApi,\n fetchApi,\n errorApi,\n}: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n errorApi: ErrorApi;\n}) {\n let stopped = false;\n let timeout: NodeJS.Timeout | undefined;\n let firstError = true;\n let errorBackoff = ERROR_BACKOFF_START;\n\n const channel =\n 'BroadcastChannel' in window ? new BroadcastChannel(CHANNEL_ID) : undefined;\n\n // Randomize the refreshing margin with a margin of 1-4 minutes to avoid all tabs refreshing at the same time\n const getDelay = (expiresAt: number) => {\n const margin = (1 + 3 * Math.random()) * 60000;\n const delay = Math.max(expiresAt - Date.now(), MIN_BASE_DELAY_MS) - margin;\n return delay;\n };\n\n const refresh = async () => {\n try {\n const baseUrl = await discoveryApi.getBaseUrl(PLUGIN_ID);\n const requestUrl = `${baseUrl}/.backstage/auth/v1/cookie`;\n const res = await fetchApi.fetch(requestUrl, {\n credentials: 'include',\n });\n\n if (!res.ok) {\n throw new Error(\n `Request failed with status ${res.status} ${res.statusText}, see request towards ${requestUrl} for more details`,\n );\n }\n\n const data = await res.json();\n if (!data.expiresAt) {\n throw new Error('No expiration date in response');\n }\n\n const expiresAt = Date.parse(data.expiresAt);\n if (Number.isNaN(expiresAt)) {\n throw new Error('Invalid expiration date in response');\n }\n\n firstError = true;\n\n channel?.postMessage({\n action: 'COOKIE_REFRESH_SUCCESS',\n payload: { expiresAt: new Date(expiresAt).toISOString() },\n });\n\n scheduleRefresh(getDelay(expiresAt));\n } catch (error) {\n // Ignore the first error after successful requests\n if (firstError) {\n firstError = false;\n errorBackoff = ERROR_BACKOFF_START;\n } else {\n errorBackoff = Math.min(\n ERROR_BACKOFF_MAX,\n errorBackoff * ERROR_BACKOFF_FACTOR,\n );\n // eslint-disable-next-line no-console\n console.error('Session cookie refresh failed', error);\n errorApi.post(\n new Error(\n `Session refresh failed, see developer console for details`,\n ),\n );\n }\n\n scheduleRefresh(errorBackoff);\n }\n };\n\n const onMessage = (\n event: MessageEvent<\n | {\n action: 'COOKIE_REFRESH_SUCCESS';\n payload: { expiresAt: string };\n }\n | object\n >,\n ) => {\n const { data } = event;\n if (data === null || typeof data !== 'object') {\n return;\n }\n if ('action' in data && data.action === 'COOKIE_REFRESH_SUCCESS') {\n const expiresAt = Date.parse(data.payload.expiresAt);\n if (Number.isNaN(expiresAt)) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Received invalid expiration from session refresh channel',\n );\n return;\n }\n\n scheduleRefresh(getDelay(expiresAt));\n }\n };\n\n function scheduleRefresh(delayMs: number) {\n if (stopped) {\n return;\n }\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(refresh, delayMs);\n }\n\n channel?.addEventListener('message', onMessage);\n refresh();\n\n return () => {\n stopped = true;\n if (timeout) {\n clearTimeout(timeout);\n }\n channel?.removeEventListener('message', onMessage);\n channel?.close();\n };\n}\n"],"names":[],"mappings":"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"}
|