@backstage/plugin-app 0.4.3-next.1 → 0.4.3
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 +48 -0
- package/dist/apis/DefaultDialogApi.esm.js +69 -15
- package/dist/apis/DefaultDialogApi.esm.js.map +1 -1
- package/dist/extensions/AppLanguageApi.esm.js +4 -5
- package/dist/extensions/AppLanguageApi.esm.js.map +1 -1
- package/dist/extensions/DialogDisplay.esm.js +1 -15
- package/dist/extensions/DialogDisplay.esm.js.map +1 -1
- package/dist/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.esm.js +16 -0
- package/dist/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.esm.js.map +1 -1
- package/dist/plugins/app/package.json.esm.js +1 -1
- package/package.json +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @backstage/plugin-app
|
|
2
2
|
|
|
3
|
+
## 0.4.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- effa7bf: Migrated `AppLanguageApi` extension to use the new `configSchema` option.
|
|
8
|
+
- e5baa20: Added support for configuring URL redirects on the `app/routes` extension. Redirects can be configured through `app-config` as an array of `{from, to}` path pairs, which will cause navigation to the `from` path to be redirected to the `to` path.
|
|
9
|
+
|
|
10
|
+
For example:
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
app:
|
|
14
|
+
extensions:
|
|
15
|
+
- app/routes:
|
|
16
|
+
config:
|
|
17
|
+
redirects:
|
|
18
|
+
- from: /old-path
|
|
19
|
+
to: /new-path
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- 9244b70: The default auth implementation now checks for a `logoutUrl` in the logout response body. If the auth provider returns one (e.g. Auth0 federated logout), the browser is redirected to that URL to clear the provider's session cookies. This is backward compatible — providers that return an empty response are unaffected.
|
|
23
|
+
- e4804ab: Updated the default `DialogApi` implementation to support the new `open` method. The dialog display layer no longer renders any dialog chrome — callers provide their own dialog component. The deprecated `show` and `showModal` methods now use `open` internally with a Material UI dialog wrapper for backward compatibility.
|
|
24
|
+
- d66a3ec: Updated the `PageLayout` swap to pass a clickable `titleLink` on the `PluginHeader`, resolved from the plugin's root route ref.
|
|
25
|
+
- Updated dependencies
|
|
26
|
+
- @backstage/ui@0.14.0
|
|
27
|
+
- @backstage/theme@0.7.3
|
|
28
|
+
- @backstage/frontend-plugin-api@0.16.0
|
|
29
|
+
- @backstage/core-components@0.18.9
|
|
30
|
+
- @backstage/filter-predicates@0.1.2
|
|
31
|
+
- @backstage/plugin-permission-react@0.5.0
|
|
32
|
+
- @backstage/core-plugin-api@1.12.5
|
|
33
|
+
- @backstage/integration-react@1.2.17
|
|
34
|
+
- @backstage/plugin-app-react@0.2.2
|
|
35
|
+
|
|
36
|
+
## 0.4.3-next.2
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- 9244b70: The default auth implementation now checks for a `logoutUrl` in the logout response body. If the auth provider returns one (e.g. Auth0 federated logout), the browser is redirected to that URL to clear the provider's session cookies. This is backward compatible — providers that return an empty response are unaffected.
|
|
41
|
+
- Updated dependencies
|
|
42
|
+
- @backstage/ui@0.14.0-next.2
|
|
43
|
+
- @backstage/theme@0.7.3-next.0
|
|
44
|
+
- @backstage/core-components@0.18.9-next.1
|
|
45
|
+
- @backstage/core-plugin-api@1.12.5-next.2
|
|
46
|
+
- @backstage/filter-predicates@0.1.2-next.0
|
|
47
|
+
- @backstage/frontend-plugin-api@0.16.0-next.2
|
|
48
|
+
- @backstage/integration-react@1.2.17-next.1
|
|
49
|
+
- @backstage/plugin-permission-react@0.4.42-next.1
|
|
50
|
+
|
|
3
51
|
## 0.4.3-next.1
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
|
@@ -1,26 +1,80 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import Dialog from '@material-ui/core/Dialog';
|
|
3
|
+
|
|
1
4
|
class DefaultDialogApi {
|
|
2
|
-
#
|
|
3
|
-
|
|
4
|
-
if (!this.#
|
|
5
|
+
#onOpen;
|
|
6
|
+
open(elementOrComponent) {
|
|
7
|
+
if (!this.#onOpen) {
|
|
5
8
|
throw new Error("Dialog API has not been connected");
|
|
6
9
|
}
|
|
7
|
-
return this.#
|
|
8
|
-
component: typeof elementOrComponent === "function" ? elementOrComponent : () => elementOrComponent
|
|
9
|
-
modal: false
|
|
10
|
+
return this.#onOpen({
|
|
11
|
+
component: typeof elementOrComponent === "function" ? elementOrComponent : () => elementOrComponent
|
|
10
12
|
});
|
|
11
13
|
}
|
|
14
|
+
/** @deprecated Use {@link DefaultDialogApi.open} instead */
|
|
15
|
+
show(elementOrComponent) {
|
|
16
|
+
console.warn(
|
|
17
|
+
"DialogApi.show() is deprecated and will be removed in a future release. Use DialogApi.open() instead."
|
|
18
|
+
);
|
|
19
|
+
const innerDialog = this.open(({ dialog }) => /* @__PURE__ */ jsx(
|
|
20
|
+
DeprecatedMuiDialogWrapper,
|
|
21
|
+
{
|
|
22
|
+
dialog,
|
|
23
|
+
content: elementOrComponent,
|
|
24
|
+
modal: false
|
|
25
|
+
}
|
|
26
|
+
));
|
|
27
|
+
return wrapDialogHandle(innerDialog, false);
|
|
28
|
+
}
|
|
29
|
+
/** @deprecated Use {@link DefaultDialogApi.open} instead */
|
|
12
30
|
showModal(elementOrComponent) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
console.warn(
|
|
32
|
+
"DialogApi.showModal() is deprecated and will be removed in a future release. Use DialogApi.open() instead."
|
|
33
|
+
);
|
|
34
|
+
const innerDialog = this.open(({ dialog }) => /* @__PURE__ */ jsx(
|
|
35
|
+
DeprecatedMuiDialogWrapper,
|
|
36
|
+
{
|
|
37
|
+
dialog,
|
|
38
|
+
content: elementOrComponent,
|
|
39
|
+
modal: true
|
|
40
|
+
}
|
|
41
|
+
));
|
|
42
|
+
return wrapDialogHandle(innerDialog, true);
|
|
43
|
+
}
|
|
44
|
+
connect(onOpen) {
|
|
45
|
+
this.#onOpen = onOpen;
|
|
20
46
|
}
|
|
21
|
-
|
|
22
|
-
|
|
47
|
+
}
|
|
48
|
+
function DeprecatedMuiDialogWrapper({
|
|
49
|
+
dialog,
|
|
50
|
+
content,
|
|
51
|
+
modal
|
|
52
|
+
}) {
|
|
53
|
+
if (typeof content === "function") {
|
|
54
|
+
const Content = content;
|
|
55
|
+
return /* @__PURE__ */ jsx(Dialog, { open: true, onClose: modal ? void 0 : () => dialog.close(), children: /* @__PURE__ */ jsx(Content, { dialog }) });
|
|
23
56
|
}
|
|
57
|
+
return /* @__PURE__ */ jsx(Dialog, { open: true, onClose: modal ? void 0 : () => dialog.close(), children: content });
|
|
58
|
+
}
|
|
59
|
+
function wrapDialogHandle(innerDialog, modal) {
|
|
60
|
+
return {
|
|
61
|
+
close(...args) {
|
|
62
|
+
innerDialog.close(...args);
|
|
63
|
+
},
|
|
64
|
+
result() {
|
|
65
|
+
return innerDialog.result();
|
|
66
|
+
},
|
|
67
|
+
update(newContent) {
|
|
68
|
+
innerDialog.update(({ dialog }) => /* @__PURE__ */ jsx(
|
|
69
|
+
DeprecatedMuiDialogWrapper,
|
|
70
|
+
{
|
|
71
|
+
dialog,
|
|
72
|
+
content: newContent,
|
|
73
|
+
modal
|
|
74
|
+
}
|
|
75
|
+
));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
24
78
|
}
|
|
25
79
|
|
|
26
80
|
export { DefaultDialogApi };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultDialogApi.esm.js","sources":["../../src/apis/DefaultDialogApi.
|
|
1
|
+
{"version":3,"file":"DefaultDialogApi.esm.js","sources":["../../src/apis/DefaultDialogApi.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { DialogApi, DialogApiDialog } from '@backstage/frontend-plugin-api';\nimport Dialog from '@material-ui/core/Dialog';\n\nexport type OnOpenDialog = (options: {\n component: (props: { dialog: DialogApiDialog<any> }) => JSX.Element;\n}) => DialogApiDialog<unknown>;\n\n/**\n * Default implementation for the {@link DialogApi}.\n * @internal\n */\nexport class DefaultDialogApi implements DialogApi {\n #onOpen?: OnOpenDialog;\n\n open<TResult = void>(\n elementOrComponent:\n | JSX.Element\n | ((props: { dialog: DialogApiDialog<TResult> }) => JSX.Element),\n ): DialogApiDialog<TResult> {\n if (!this.#onOpen) {\n throw new Error('Dialog API has not been connected');\n }\n return this.#onOpen({\n component:\n typeof elementOrComponent === 'function'\n ? elementOrComponent\n : () => elementOrComponent,\n }) as DialogApiDialog<TResult>;\n }\n\n /** @deprecated Use {@link DefaultDialogApi.open} instead */\n show<TResult = void>(\n elementOrComponent:\n | JSX.Element\n | ((props: {\n dialog: DialogApiDialog<TResult | undefined>;\n }) => JSX.Element),\n ): DialogApiDialog<TResult | undefined> {\n // eslint-disable-next-line no-console\n console.warn(\n 'DialogApi.show() is deprecated and will be removed in a future release. Use DialogApi.open() instead.',\n );\n const innerDialog = this.open<TResult | undefined>(({ dialog }) => (\n <DeprecatedMuiDialogWrapper\n dialog={dialog}\n content={elementOrComponent}\n modal={false}\n />\n ));\n return wrapDialogHandle(innerDialog, false);\n }\n\n /** @deprecated Use {@link DefaultDialogApi.open} instead */\n showModal<TResult = void>(\n elementOrComponent:\n | JSX.Element\n | ((props: { dialog: DialogApiDialog<TResult> }) => JSX.Element),\n ): DialogApiDialog<TResult> {\n // eslint-disable-next-line no-console\n console.warn(\n 'DialogApi.showModal() is deprecated and will be removed in a future release. Use DialogApi.open() instead.',\n );\n const innerDialog = this.open<TResult>(({ dialog }) => (\n <DeprecatedMuiDialogWrapper\n dialog={dialog}\n content={elementOrComponent}\n modal\n />\n ));\n return wrapDialogHandle(innerDialog, true);\n }\n\n connect(onOpen: OnOpenDialog): void {\n this.#onOpen = onOpen;\n }\n}\n\nfunction DeprecatedMuiDialogWrapper({\n dialog,\n content,\n modal,\n}: {\n dialog: DialogApiDialog<any>;\n content:\n | JSX.Element\n | ((props: { dialog: DialogApiDialog<any> }) => JSX.Element);\n modal: boolean;\n}) {\n if (typeof content === 'function') {\n const Content = content;\n return (\n <Dialog open onClose={modal ? undefined : () => dialog.close()}>\n <Content dialog={dialog} />\n </Dialog>\n );\n }\n return (\n <Dialog open onClose={modal ? undefined : () => dialog.close()}>\n {content}\n </Dialog>\n );\n}\n\nfunction wrapDialogHandle<TResult>(\n innerDialog: DialogApiDialog<TResult>,\n modal: boolean,\n): DialogApiDialog<TResult> {\n return {\n close(...args: any[]) {\n (innerDialog.close as any)(...args);\n },\n result() {\n return innerDialog.result();\n },\n update(newContent: any) {\n innerDialog.update(({ dialog }: { dialog: DialogApiDialog<TResult> }) => (\n <DeprecatedMuiDialogWrapper\n dialog={dialog}\n content={newContent}\n modal={modal}\n />\n ));\n },\n };\n}\n"],"names":[],"mappings":";;;AA2BO,MAAM,gBAAA,CAAsC;AAAA,EACjD,OAAA;AAAA,EAEA,KACE,kBAAA,EAG0B;AAC1B,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AACjB,MAAA,MAAM,IAAI,MAAM,mCAAmC,CAAA;AAAA,IACrD;AACA,IAAA,OAAO,KAAK,OAAA,CAAQ;AAAA,MAClB,SAAA,EACE,OAAO,kBAAA,KAAuB,UAAA,GAC1B,qBACA,MAAM;AAAA,KACb,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,KACE,kBAAA,EAKsC;AAEtC,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KACF;AACA,IAAA,MAAM,cAAc,IAAA,CAAK,IAAA,CAA0B,CAAC,EAAE,QAAO,qBAC3D,GAAA;AAAA,MAAC,0BAAA;AAAA,MAAA;AAAA,QACC,MAAA;AAAA,QACA,OAAA,EAAS,kBAAA;AAAA,QACT,KAAA,EAAO;AAAA;AAAA,KAEV,CAAA;AACD,IAAA,OAAO,gBAAA,CAAiB,aAAa,KAAK,CAAA;AAAA,EAC5C;AAAA;AAAA,EAGA,UACE,kBAAA,EAG0B;AAE1B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KACF;AACA,IAAA,MAAM,cAAc,IAAA,CAAK,IAAA,CAAc,CAAC,EAAE,QAAO,qBAC/C,GAAA;AAAA,MAAC,0BAAA;AAAA,MAAA;AAAA,QACC,MAAA;AAAA,QACA,OAAA,EAAS,kBAAA;AAAA,QACT,KAAA,EAAK;AAAA;AAAA,KAER,CAAA;AACD,IAAA,OAAO,gBAAA,CAAiB,aAAa,IAAI,CAAA;AAAA,EAC3C;AAAA,EAEA,QAAQ,MAAA,EAA4B;AAClC,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAAA,EACjB;AACF;AAEA,SAAS,0BAAA,CAA2B;AAAA,EAClC,MAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAMG;AACD,EAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AACjC,IAAA,MAAM,OAAA,GAAU,OAAA;AAChB,IAAA,uBACE,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAI,IAAA,EAAC,SAAS,KAAA,GAAQ,MAAA,GAAY,MAAM,MAAA,CAAO,KAAA,EAAM,EAC3D,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,QAAgB,CAAA,EAC3B,CAAA;AAAA,EAEJ;AACA,EAAA,uBACE,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAI,IAAA,EAAC,OAAA,EAAS,KAAA,GAAQ,MAAA,GAAY,MAAM,MAAA,CAAO,KAAA,EAAM,EAC1D,QAAA,EAAA,OAAA,EACH,CAAA;AAEJ;AAEA,SAAS,gBAAA,CACP,aACA,KAAA,EAC0B;AAC1B,EAAA,OAAO;AAAA,IACL,SAAS,IAAA,EAAa;AACpB,MAAC,WAAA,CAAY,KAAA,CAAc,GAAG,IAAI,CAAA;AAAA,IACpC,CAAA;AAAA,IACA,MAAA,GAAS;AACP,MAAA,OAAO,YAAY,MAAA,EAAO;AAAA,IAC5B,CAAA;AAAA,IACA,OAAO,UAAA,EAAiB;AACtB,MAAA,WAAA,CAAY,MAAA,CAAO,CAAC,EAAE,MAAA,EAAO,qBAC3B,GAAA;AAAA,QAAC,0BAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,OAAA,EAAS,UAAA;AAAA,UACT;AAAA;AAAA,OAEH,CAAA;AAAA,IACH;AAAA,GACF;AACF;;;;"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { AppLanguageSelector } from '../packages/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js';
|
|
2
2
|
import { ApiBlueprint, appLanguageApiRef } from '@backstage/frontend-plugin-api';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
|
|
4
5
|
const AppLanguageApi = ApiBlueprint.makeWithOverrides({
|
|
5
6
|
name: "app-language",
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
availableLanguages: (z) => z.array(z.string()).optional()
|
|
10
|
-
}
|
|
7
|
+
configSchema: {
|
|
8
|
+
defaultLanguage: z.string().optional(),
|
|
9
|
+
availableLanguages: z.array(z.string()).optional()
|
|
11
10
|
},
|
|
12
11
|
factory(originalFactory, { config }) {
|
|
13
12
|
return originalFactory(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppLanguageApi.esm.js","sources":["../../src/extensions/AppLanguageApi.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\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageSelector } from '../../../../packages/core-app-api/src/apis/implementations/AppLanguageApi';\nimport { appLanguageApiRef } from '@backstage/frontend-plugin-api';\nimport { ApiBlueprint } from '@backstage/frontend-plugin-api';\n\nexport const AppLanguageApi = ApiBlueprint.makeWithOverrides({\n name: 'app-language',\n
|
|
1
|
+
{"version":3,"file":"AppLanguageApi.esm.js","sources":["../../src/extensions/AppLanguageApi.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\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageSelector } from '../../../../packages/core-app-api/src/apis/implementations/AppLanguageApi';\nimport { appLanguageApiRef } from '@backstage/frontend-plugin-api';\nimport { ApiBlueprint } from '@backstage/frontend-plugin-api';\nimport { z } from 'zod';\n\nexport const AppLanguageApi = ApiBlueprint.makeWithOverrides({\n name: 'app-language',\n configSchema: {\n defaultLanguage: z.string().optional(),\n availableLanguages: z.array(z.string()).optional(),\n },\n factory(originalFactory, { config }) {\n return originalFactory(defineParams =>\n defineParams({\n api: appLanguageApiRef,\n deps: {},\n factory: () =>\n AppLanguageSelector.createWithStorage({\n defaultLanguage: config.defaultLanguage,\n availableLanguages: config.availableLanguages,\n }),\n }),\n );\n },\n});\n"],"names":[],"mappings":";;;;AAsBO,MAAM,cAAA,GAAiB,aAAa,iBAAA,CAAkB;AAAA,EAC3D,IAAA,EAAM,cAAA;AAAA,EACN,YAAA,EAAc;AAAA,IACZ,eAAA,EAAiB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IACrC,oBAAoB,CAAA,CAAE,KAAA,CAAM,EAAE,MAAA,EAAQ,EAAE,QAAA;AAAS,GACnD;AAAA,EACA,OAAA,CAAQ,eAAA,EAAiB,EAAE,MAAA,EAAO,EAAG;AACnC,IAAA,OAAO,eAAA;AAAA,MAAgB,kBACrB,YAAA,CAAa;AAAA,QACX,GAAA,EAAK,iBAAA;AAAA,QACL,MAAM,EAAC;AAAA,QACP,OAAA,EAAS,MACP,mBAAA,CAAoB,iBAAA,CAAkB;AAAA,UACpC,iBAAiB,MAAA,CAAO,eAAA;AAAA,UACxB,oBAAoB,MAAA,CAAO;AAAA,SAC5B;AAAA,OACJ;AAAA,KACH;AAAA,EACF;AACF,CAAC;;;;"}
|
|
@@ -2,7 +2,6 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { Fragment, useState, useEffect } from 'react';
|
|
3
3
|
import { AppRootElementBlueprint, dialogApiRef } from '@backstage/frontend-plugin-api';
|
|
4
4
|
import { createDeferred } from '@backstage/types';
|
|
5
|
-
import Dialog from '@material-ui/core/Dialog';
|
|
6
5
|
|
|
7
6
|
let dialogId = 0;
|
|
8
7
|
function getDialogId() {
|
|
@@ -19,7 +18,6 @@ function DialogDisplay({
|
|
|
19
18
|
const deferred = createDeferred();
|
|
20
19
|
const dialog = {
|
|
21
20
|
id,
|
|
22
|
-
modal: options.modal,
|
|
23
21
|
close(result) {
|
|
24
22
|
deferred.resolve(result);
|
|
25
23
|
setDialogs((ds) => ds.filter((d) => d.dialog.id !== id));
|
|
@@ -40,19 +38,7 @@ function DialogDisplay({
|
|
|
40
38
|
});
|
|
41
39
|
}, [dialogApi]);
|
|
42
40
|
if (dialogs.length > 0) {
|
|
43
|
-
|
|
44
|
-
return /* @__PURE__ */ jsx(
|
|
45
|
-
Dialog,
|
|
46
|
-
{
|
|
47
|
-
open: true,
|
|
48
|
-
onClose: () => {
|
|
49
|
-
if (!lastDialog.dialog.modal) {
|
|
50
|
-
lastDialog.dialog.close();
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
children: lastDialog.element
|
|
54
|
-
}
|
|
55
|
-
);
|
|
41
|
+
return dialogs[dialogs.length - 1].element;
|
|
56
42
|
}
|
|
57
43
|
return null;
|
|
58
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DialogDisplay.esm.js","sources":["../../src/extensions/DialogDisplay.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { Fragment, useEffect, useState } from 'react';\nimport {\n AppRootElementBlueprint,\n DialogApi,\n DialogApiDialog,\n dialogApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { createDeferred } from '@backstage/types';\nimport {
|
|
1
|
+
{"version":3,"file":"DialogDisplay.esm.js","sources":["../../src/extensions/DialogDisplay.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { Fragment, useEffect, useState } from 'react';\nimport {\n AppRootElementBlueprint,\n DialogApi,\n DialogApiDialog,\n dialogApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { createDeferred } from '@backstage/types';\nimport { OnOpenDialog } from '../apis/DefaultDialogApi';\n\nlet dialogId = 0;\nfunction getDialogId() {\n dialogId += 1;\n return dialogId.toString(36);\n}\n\ntype DialogState = DialogApiDialog<unknown> & {\n id: string;\n};\n\n/**\n * The other half of the default implementation of the {@link DialogApi}.\n *\n * This component is responsible for rendering the dialogs in the React tree\n * and managing a stack of dialogs. It renders only the most recently opened\n * dialog, without any dialog chrome — the caller is expected to provide their\n * own dialog component (overlay, backdrop, surface, etc.).\n *\n * It expects the implementation of the {@link DialogApi} to be the\n * `DefaultDialogApi`. If one is replaced the other must be too.\n *\n * @internal\n */\nfunction DialogDisplay({\n dialogApi,\n}: {\n dialogApi: DialogApi & { connect(onOpen: OnOpenDialog): void };\n}) {\n const [dialogs, setDialogs] = useState<\n { dialog: DialogState; element: React.JSX.Element }[]\n >([]);\n\n useEffect(() => {\n dialogApi.connect(options => {\n const id = getDialogId();\n const deferred = createDeferred<unknown>();\n const dialog: DialogState = {\n id,\n close(result) {\n deferred.resolve(result);\n setDialogs(ds => ds.filter(d => d.dialog.id !== id));\n },\n update(ElementOrComponent) {\n const element =\n typeof ElementOrComponent === 'function' ? (\n <ElementOrComponent dialog={dialog} />\n ) : (\n ElementOrComponent\n );\n setDialogs(ds =>\n ds.map(d => (d.dialog.id === id ? { dialog, element } : d)),\n );\n },\n async result() {\n return deferred;\n },\n };\n const element = <options.component dialog={dialog} />;\n setDialogs(ds => [...ds, { dialog, element }]);\n return dialog;\n });\n }, [dialogApi]);\n\n if (dialogs.length > 0) {\n return dialogs[dialogs.length - 1].element;\n }\n\n return null;\n}\n\nexport const dialogDisplayAppRootElement =\n AppRootElementBlueprint.makeWithOverrides({\n name: 'dialog-display',\n factory(originalFactory, { apis }) {\n const dialogApi = apis.get(dialogApiRef);\n if (!isInternalDialogApi(dialogApi)) {\n return originalFactory({\n element: <Fragment />,\n });\n }\n return originalFactory({\n element: <DialogDisplay dialogApi={dialogApi} />,\n });\n },\n });\n\nfunction isInternalDialogApi(\n dialogApi?: DialogApi,\n): dialogApi is DialogApi & { connect(onOpen: OnOpenDialog): void } {\n if (!dialogApi) {\n return false;\n }\n return 'connect' in dialogApi;\n}\n"],"names":["element"],"mappings":";;;;;AA0BA,IAAI,QAAA,GAAW,CAAA;AACf,SAAS,WAAA,GAAc;AACrB,EAAA,QAAA,IAAY,CAAA;AACZ,EAAA,OAAO,QAAA,CAAS,SAAS,EAAE,CAAA;AAC7B;AAmBA,SAAS,aAAA,CAAc;AAAA,EACrB;AACF,CAAA,EAEG;AACD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAE5B,EAAE,CAAA;AAEJ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,QAAQ,CAAA,OAAA,KAAW;AAC3B,MAAA,MAAM,KAAK,WAAA,EAAY;AACvB,MAAA,MAAM,WAAW,cAAA,EAAwB;AACzC,MAAA,MAAM,MAAA,GAAsB;AAAA,QAC1B,EAAA;AAAA,QACA,MAAM,MAAA,EAAQ;AACZ,UAAA,QAAA,CAAS,QAAQ,MAAM,CAAA;AACvB,UAAA,UAAA,CAAW,CAAA,EAAA,KAAM,GAAG,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,MAAA,CAAO,EAAA,KAAO,EAAE,CAAC,CAAA;AAAA,QACrD,CAAA;AAAA,QACA,OAAO,kBAAA,EAAoB;AACzB,UAAA,MAAMA,WACJ,OAAO,kBAAA,KAAuB,6BAC5B,GAAA,CAAC,kBAAA,EAAA,EAAmB,QAAgB,CAAA,GAEpC,kBAAA;AAEJ,UAAA,UAAA;AAAA,YAAW,CAAA,EAAA,KACT,EAAA,CAAG,GAAA,CAAI,CAAA,CAAA,KAAM,CAAA,CAAE,MAAA,CAAO,EAAA,KAAO,EAAA,GAAK,EAAE,MAAA,EAAQ,OAAA,EAAAA,QAAAA,KAAY,CAAE;AAAA,WAC5D;AAAA,QACF,CAAA;AAAA,QACA,MAAM,MAAA,GAAS;AACb,UAAA,OAAO,QAAA;AAAA,QACT;AAAA,OACF;AACA,MAAA,MAAM,OAAA,mBAAU,GAAA,CAAC,OAAA,CAAQ,SAAA,EAAR,EAAkB,MAAA,EAAgB,CAAA;AACnD,MAAA,UAAA,CAAW,CAAA,EAAA,KAAM,CAAC,GAAG,EAAA,EAAI,EAAE,MAAA,EAAQ,OAAA,EAAS,CAAC,CAAA;AAC7C,MAAA,OAAO,MAAA;AAAA,IACT,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA,CAAE,OAAA;AAAA,EACrC;AAEA,EAAA,OAAO,IAAA;AACT;AAEO,MAAM,2BAAA,GACX,wBAAwB,iBAAA,CAAkB;AAAA,EACxC,IAAA,EAAM,gBAAA;AAAA,EACN,OAAA,CAAQ,eAAA,EAAiB,EAAE,IAAA,EAAK,EAAG;AACjC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA;AACvC,IAAA,IAAI,CAAC,mBAAA,CAAoB,SAAS,CAAA,EAAG;AACnC,MAAA,OAAO,eAAA,CAAgB;AAAA,QACrB,OAAA,sBAAU,QAAA,EAAA,EAAS;AAAA,OACpB,CAAA;AAAA,IACH;AACA,IAAA,OAAO,eAAA,CAAgB;AAAA,MACrB,OAAA,kBAAS,GAAA,CAAC,aAAA,EAAA,EAAc,SAAA,EAAsB;AAAA,KAC/C,CAAA;AAAA,EACH;AACF,CAAC;AAEH,SAAS,oBACP,SAAA,EACkE;AAClE,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,SAAA,IAAa,SAAA;AACtB;;;;"}
|
|
@@ -103,6 +103,22 @@ class DefaultAuthConnector {
|
|
|
103
103
|
error.status = res.status;
|
|
104
104
|
throw error;
|
|
105
105
|
}
|
|
106
|
+
try {
|
|
107
|
+
const contentType = res.headers.get("content-type");
|
|
108
|
+
if (contentType?.includes("application/json")) {
|
|
109
|
+
const body = await res.json();
|
|
110
|
+
if (body.logoutUrl) {
|
|
111
|
+
const url = new URL(body.logoutUrl);
|
|
112
|
+
if (url.protocol === "https:" || url.hostname === "localhost") {
|
|
113
|
+
window.location.href = body.logoutUrl;
|
|
114
|
+
return new Promise(() => {
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
return void 0;
|
|
106
122
|
}
|
|
107
123
|
async showPopup(scopes) {
|
|
108
124
|
const scope = this.joinScopesFunc(scopes);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultAuthConnector.esm.js","sources":["../../../../../../../../packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.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 */\nimport {\n AuthProviderInfo,\n ConfigApi,\n DiscoveryApi,\n OAuthRequestApi,\n OAuthRequester,\n} from '@backstage/core-plugin-api';\nimport { openLoginPopup } from '../loginPopup';\nimport {\n AuthConnector,\n AuthConnectorCreateSessionOptions,\n PopupOptions,\n AuthConnectorRefreshSessionOptions,\n} from './types';\n\nlet warned = false;\n\ntype Options<AuthSession> = {\n /**\n * DiscoveryApi instance used to locate the auth backend endpoint.\n */\n discoveryApi: DiscoveryApi;\n /**\n * Environment hint passed on to auth backend, for example 'production' or 'development'\n */\n environment: string;\n /**\n * Information about the auth provider to be shown to the user.\n * The ID Must match the backend auth plugin configuration, for example 'google'.\n */\n provider: AuthProviderInfo;\n /**\n * API used to instantiate an auth requester.\n */\n oauthRequestApi: OAuthRequestApi;\n /**\n * Function used to join together a set of scopes, defaults to joining with a space character.\n */\n joinScopes?: (scopes: Set<string>) => string;\n /**\n * Function used to transform an auth response into the session type.\n */\n sessionTransform?(response: any): AuthSession | Promise<AuthSession>;\n /**\n * ConfigApi instance used to configure authentication flow of pop-up or redirect.\n */\n configApi?: ConfigApi;\n /**\n * Options used to configure auth popup\n */\n popupOptions?: PopupOptions;\n};\n\nfunction defaultJoinScopes(scopes: Set<string>) {\n return [...scopes].join(' ');\n}\n\n/**\n * DefaultAuthConnector is the default auth connector in Backstage. It talks to the\n * backend auth plugin through the standardized API, and requests user permission\n * via the OAuthRequestApi.\n */\nexport class DefaultAuthConnector<AuthSession>\n implements AuthConnector<AuthSession>\n{\n private readonly discoveryApi: DiscoveryApi;\n private readonly environment: string;\n private readonly provider: AuthProviderInfo;\n private readonly joinScopesFunc: (scopes: Set<string>) => string;\n private readonly authRequester: OAuthRequester<AuthSession>;\n private readonly sessionTransform: (response: any) => Promise<AuthSession>;\n private readonly enableExperimentalRedirectFlow: boolean;\n private readonly popupOptions: PopupOptions | undefined;\n constructor(options: Options<AuthSession>) {\n const {\n configApi,\n discoveryApi,\n environment,\n provider,\n joinScopes = defaultJoinScopes,\n oauthRequestApi,\n sessionTransform = id => id,\n popupOptions,\n } = options;\n\n if (!warned && !configApi) {\n // eslint-disable-next-line no-console\n console.warn(\n 'DEPRECATION WARNING: Authentication providers require a configApi instance to configure the authentication flow. Please provide one to the authentication provider constructor.',\n );\n warned = true;\n }\n\n this.enableExperimentalRedirectFlow = configApi\n ? configApi.getOptionalBoolean('enableExperimentalRedirectFlow') ?? false\n : false;\n\n this.authRequester = oauthRequestApi.createAuthRequester({\n provider,\n onAuthRequest: async scopes => {\n if (!this.enableExperimentalRedirectFlow) {\n return this.showPopup(scopes);\n }\n return this.executeRedirect(scopes);\n },\n });\n\n this.discoveryApi = discoveryApi;\n this.environment = environment;\n this.provider = provider;\n this.joinScopesFunc = joinScopes;\n this.sessionTransform = sessionTransform;\n this.popupOptions = popupOptions;\n }\n\n async createSession(\n options: AuthConnectorCreateSessionOptions,\n ): Promise<AuthSession> {\n if (options.instantPopup) {\n if (this.enableExperimentalRedirectFlow) {\n return this.executeRedirect(options.scopes);\n }\n return this.showPopup(options.scopes);\n }\n return this.authRequester(options.scopes);\n }\n\n async refreshSession(\n options?: AuthConnectorRefreshSessionOptions,\n ): Promise<any> {\n const res = await fetch(\n await this.buildUrl('/refresh', {\n optional: true,\n ...(options && { scope: this.joinScopesFunc(options.scopes) }),\n }),\n {\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n },\n ).catch(error => {\n throw new Error(`Auth refresh request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(\n `Auth refresh request failed, ${res.statusText}`,\n );\n error.status = res.status;\n throw error;\n }\n\n const authInfo = await res.json();\n\n if (authInfo.error) {\n const error = new Error(authInfo.error.message);\n if (authInfo.error.name) {\n error.name = authInfo.error.name;\n }\n throw error;\n }\n return await this.sessionTransform(authInfo);\n }\n\n async removeSession(): Promise<void> {\n const res = await fetch(await this.buildUrl('/logout'), {\n method: 'POST',\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n }).catch(error => {\n throw new Error(`Logout request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(`Logout request failed, ${res.statusText}`);\n error.status = res.status;\n throw error;\n }\n }\n\n private async showPopup(scopes: Set<string>): Promise<AuthSession> {\n const scope = this.joinScopesFunc(scopes);\n const popupUrl = await this.buildUrl('/start', {\n scope,\n origin: window.location.origin,\n flow: 'popup',\n });\n\n const width = this.popupOptions?.size?.fullscreen\n ? window.screen.width\n : this.popupOptions?.size?.width || 450;\n\n const height = this.popupOptions?.size?.fullscreen\n ? window.screen.height\n : this.popupOptions?.size?.height || 730;\n\n const payload = await openLoginPopup({\n url: popupUrl,\n name: `${this.provider.title} Login`,\n width,\n height,\n });\n\n return await this.sessionTransform(payload);\n }\n\n private async executeRedirect(scopes: Set<string>): Promise<AuthSession> {\n const scope = this.joinScopesFunc(scopes);\n // redirect to auth api\n window.location.href = await this.buildUrl('/start', {\n scope,\n origin: window.location.origin,\n redirectUrl: window.location.href,\n flow: 'redirect',\n });\n // return a promise that never resolves\n return new Promise(() => {});\n }\n\n private async buildUrl(\n path: string,\n query?: { [key: string]: string | boolean | undefined },\n ): Promise<string> {\n const baseUrl = await this.discoveryApi.getBaseUrl('auth');\n const queryString = this.buildQueryString({\n ...query,\n env: this.environment,\n });\n\n return `${baseUrl}/${this.provider.id}${path}${queryString}`;\n }\n\n private buildQueryString(query?: {\n [key: string]: string | boolean | undefined;\n }): string {\n if (!query) {\n return '';\n }\n\n const queryString = Object.entries<string | boolean | undefined>(query)\n .map(([key, value]) => {\n if (typeof value === 'string') {\n return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n } else if (value) {\n return encodeURIComponent(key);\n }\n return undefined;\n })\n .filter(Boolean)\n .join('&');\n\n if (!queryString) {\n return '';\n }\n return `?${queryString}`;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultAuthConnector.esm.js","sources":["../../../../../../../../packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.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 */\nimport {\n AuthProviderInfo,\n ConfigApi,\n DiscoveryApi,\n OAuthRequestApi,\n OAuthRequester,\n} from '@backstage/core-plugin-api';\nimport { openLoginPopup } from '../loginPopup';\nimport {\n AuthConnector,\n AuthConnectorCreateSessionOptions,\n PopupOptions,\n AuthConnectorRefreshSessionOptions,\n} from './types';\n\nlet warned = false;\n\ntype Options<AuthSession> = {\n /**\n * DiscoveryApi instance used to locate the auth backend endpoint.\n */\n discoveryApi: DiscoveryApi;\n /**\n * Environment hint passed on to auth backend, for example 'production' or 'development'\n */\n environment: string;\n /**\n * Information about the auth provider to be shown to the user.\n * The ID Must match the backend auth plugin configuration, for example 'google'.\n */\n provider: AuthProviderInfo;\n /**\n * API used to instantiate an auth requester.\n */\n oauthRequestApi: OAuthRequestApi;\n /**\n * Function used to join together a set of scopes, defaults to joining with a space character.\n */\n joinScopes?: (scopes: Set<string>) => string;\n /**\n * Function used to transform an auth response into the session type.\n */\n sessionTransform?(response: any): AuthSession | Promise<AuthSession>;\n /**\n * ConfigApi instance used to configure authentication flow of pop-up or redirect.\n */\n configApi?: ConfigApi;\n /**\n * Options used to configure auth popup\n */\n popupOptions?: PopupOptions;\n};\n\nfunction defaultJoinScopes(scopes: Set<string>) {\n return [...scopes].join(' ');\n}\n\n/**\n * DefaultAuthConnector is the default auth connector in Backstage. It talks to the\n * backend auth plugin through the standardized API, and requests user permission\n * via the OAuthRequestApi.\n */\nexport class DefaultAuthConnector<AuthSession>\n implements AuthConnector<AuthSession>\n{\n private readonly discoveryApi: DiscoveryApi;\n private readonly environment: string;\n private readonly provider: AuthProviderInfo;\n private readonly joinScopesFunc: (scopes: Set<string>) => string;\n private readonly authRequester: OAuthRequester<AuthSession>;\n private readonly sessionTransform: (response: any) => Promise<AuthSession>;\n private readonly enableExperimentalRedirectFlow: boolean;\n private readonly popupOptions: PopupOptions | undefined;\n constructor(options: Options<AuthSession>) {\n const {\n configApi,\n discoveryApi,\n environment,\n provider,\n joinScopes = defaultJoinScopes,\n oauthRequestApi,\n sessionTransform = id => id,\n popupOptions,\n } = options;\n\n if (!warned && !configApi) {\n // eslint-disable-next-line no-console\n console.warn(\n 'DEPRECATION WARNING: Authentication providers require a configApi instance to configure the authentication flow. Please provide one to the authentication provider constructor.',\n );\n warned = true;\n }\n\n this.enableExperimentalRedirectFlow = configApi\n ? configApi.getOptionalBoolean('enableExperimentalRedirectFlow') ?? false\n : false;\n\n this.authRequester = oauthRequestApi.createAuthRequester({\n provider,\n onAuthRequest: async scopes => {\n if (!this.enableExperimentalRedirectFlow) {\n return this.showPopup(scopes);\n }\n return this.executeRedirect(scopes);\n },\n });\n\n this.discoveryApi = discoveryApi;\n this.environment = environment;\n this.provider = provider;\n this.joinScopesFunc = joinScopes;\n this.sessionTransform = sessionTransform;\n this.popupOptions = popupOptions;\n }\n\n async createSession(\n options: AuthConnectorCreateSessionOptions,\n ): Promise<AuthSession> {\n if (options.instantPopup) {\n if (this.enableExperimentalRedirectFlow) {\n return this.executeRedirect(options.scopes);\n }\n return this.showPopup(options.scopes);\n }\n return this.authRequester(options.scopes);\n }\n\n async refreshSession(\n options?: AuthConnectorRefreshSessionOptions,\n ): Promise<any> {\n const res = await fetch(\n await this.buildUrl('/refresh', {\n optional: true,\n ...(options && { scope: this.joinScopesFunc(options.scopes) }),\n }),\n {\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n },\n ).catch(error => {\n throw new Error(`Auth refresh request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(\n `Auth refresh request failed, ${res.statusText}`,\n );\n error.status = res.status;\n throw error;\n }\n\n const authInfo = await res.json();\n\n if (authInfo.error) {\n const error = new Error(authInfo.error.message);\n if (authInfo.error.name) {\n error.name = authInfo.error.name;\n }\n throw error;\n }\n return await this.sessionTransform(authInfo);\n }\n\n async removeSession(): Promise<void> {\n const res = await fetch(await this.buildUrl('/logout'), {\n method: 'POST',\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n }).catch(error => {\n throw new Error(`Logout request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(`Logout request failed, ${res.statusText}`);\n error.status = res.status;\n throw error;\n }\n\n // If the auth provider returned a logout URL (e.g. for Auth0 federated\n // logout), redirect the browser to clear the provider's session cookies.\n try {\n const contentType = res.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n const body = await res.json();\n if (body.logoutUrl) {\n const url = new URL(body.logoutUrl);\n if (url.protocol === 'https:' || url.hostname === 'localhost') {\n window.location.href = body.logoutUrl;\n return new Promise(() => {});\n }\n }\n }\n } catch {\n // Provider logout redirect is best-effort - the backend session\n // (refresh token cookie and persisted scopes) is already cleared,\n // so we degrade gracefully.\n }\n\n return undefined;\n }\n\n private async showPopup(scopes: Set<string>): Promise<AuthSession> {\n const scope = this.joinScopesFunc(scopes);\n const popupUrl = await this.buildUrl('/start', {\n scope,\n origin: window.location.origin,\n flow: 'popup',\n });\n\n const width = this.popupOptions?.size?.fullscreen\n ? window.screen.width\n : this.popupOptions?.size?.width || 450;\n\n const height = this.popupOptions?.size?.fullscreen\n ? window.screen.height\n : this.popupOptions?.size?.height || 730;\n\n const payload = await openLoginPopup({\n url: popupUrl,\n name: `${this.provider.title} Login`,\n width,\n height,\n });\n\n return await this.sessionTransform(payload);\n }\n\n private async executeRedirect(scopes: Set<string>): Promise<AuthSession> {\n const scope = this.joinScopesFunc(scopes);\n // redirect to auth api\n window.location.href = await this.buildUrl('/start', {\n scope,\n origin: window.location.origin,\n redirectUrl: window.location.href,\n flow: 'redirect',\n });\n // return a promise that never resolves\n return new Promise(() => {});\n }\n\n private async buildUrl(\n path: string,\n query?: { [key: string]: string | boolean | undefined },\n ): Promise<string> {\n const baseUrl = await this.discoveryApi.getBaseUrl('auth');\n const queryString = this.buildQueryString({\n ...query,\n env: this.environment,\n });\n\n return `${baseUrl}/${this.provider.id}${path}${queryString}`;\n }\n\n private buildQueryString(query?: {\n [key: string]: string | boolean | undefined;\n }): string {\n if (!query) {\n return '';\n }\n\n const queryString = Object.entries<string | boolean | undefined>(query)\n .map(([key, value]) => {\n if (typeof value === 'string') {\n return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n } else if (value) {\n return encodeURIComponent(key);\n }\n return undefined;\n })\n .filter(Boolean)\n .join('&');\n\n if (!queryString) {\n return '';\n }\n return `?${queryString}`;\n }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-app",
|
|
3
|
-
"version": "0.4.3
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "frontend-plugin",
|
|
6
6
|
"pluginId": "app",
|
|
@@ -63,17 +63,17 @@
|
|
|
63
63
|
"test": "backstage-cli package test"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@backstage/core-components": "0.18.9
|
|
67
|
-
"@backstage/core-plugin-api": "1.12.5
|
|
68
|
-
"@backstage/filter-predicates": "0.1.
|
|
69
|
-
"@backstage/frontend-plugin-api": "0.16.0
|
|
70
|
-
"@backstage/integration-react": "1.2.17
|
|
71
|
-
"@backstage/plugin-app-react": "0.2.2
|
|
72
|
-
"@backstage/plugin-permission-react": "0.
|
|
73
|
-
"@backstage/theme": "0.7.
|
|
74
|
-
"@backstage/types": "1.2.2",
|
|
75
|
-
"@backstage/ui": "0.14.0
|
|
76
|
-
"@backstage/version-bridge": "1.0.12",
|
|
66
|
+
"@backstage/core-components": "^0.18.9",
|
|
67
|
+
"@backstage/core-plugin-api": "^1.12.5",
|
|
68
|
+
"@backstage/filter-predicates": "^0.1.2",
|
|
69
|
+
"@backstage/frontend-plugin-api": "^0.16.0",
|
|
70
|
+
"@backstage/integration-react": "^1.2.17",
|
|
71
|
+
"@backstage/plugin-app-react": "^0.2.2",
|
|
72
|
+
"@backstage/plugin-permission-react": "^0.5.0",
|
|
73
|
+
"@backstage/theme": "^0.7.3",
|
|
74
|
+
"@backstage/types": "^1.2.2",
|
|
75
|
+
"@backstage/ui": "^0.14.0",
|
|
76
|
+
"@backstage/version-bridge": "^1.0.12",
|
|
77
77
|
"@material-ui/core": "^4.9.13",
|
|
78
78
|
"@material-ui/icons": "^4.9.1",
|
|
79
79
|
"@material-ui/lab": "^4.0.0-alpha.61",
|
|
@@ -88,11 +88,11 @@
|
|
|
88
88
|
"zod": "^3.25.76 || ^4.0.0"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@backstage/cli": "0.36.1
|
|
92
|
-
"@backstage/dev-utils": "1.1.22
|
|
93
|
-
"@backstage/frontend-defaults": "0.5.1
|
|
94
|
-
"@backstage/frontend-test-utils": "0.5.2
|
|
95
|
-
"@backstage/test-utils": "1.7.17
|
|
91
|
+
"@backstage/cli": "^0.36.1",
|
|
92
|
+
"@backstage/dev-utils": "^1.1.22",
|
|
93
|
+
"@backstage/frontend-defaults": "^0.5.1",
|
|
94
|
+
"@backstage/frontend-test-utils": "^0.5.2",
|
|
95
|
+
"@backstage/test-utils": "^1.7.17",
|
|
96
96
|
"@testing-library/jest-dom": "^6.0.0",
|
|
97
97
|
"@testing-library/react": "^16.0.0",
|
|
98
98
|
"@testing-library/user-event": "^14.0.0",
|