@backstage/core-app-api 1.19.2 → 1.19.3-next.1
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 +22 -0
- package/dist/apis/implementations/AlertApi/AlertApiForwarder.esm.js +13 -1
- package/dist/apis/implementations/AlertApi/AlertApiForwarder.esm.js.map +1 -1
- package/dist/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js +4 -4
- package/dist/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/translation/TranslationRef.esm.js.map +1 -0
- package/dist/frontend-plugin-api/src/translation/TranslationResource.esm.js.map +1 -0
- package/dist/index.d.ts +11 -5
- package/dist/routing/types.esm.js +1 -1
- package/dist/routing/types.esm.js.map +1 -1
- package/package.json +7 -7
- package/dist/core-plugin-api/src/translation/TranslationRef.esm.js.map +0 -1
- package/dist/core-plugin-api/src/translation/TranslationResource.esm.js.map +0 -1
- /package/dist/{core-plugin-api → frontend-plugin-api}/src/translation/TranslationRef.esm.js +0 -0
- /package/dist/{core-plugin-api → frontend-plugin-api}/src/translation/TranslationResource.esm.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @backstage/core-app-api
|
|
2
2
|
|
|
3
|
+
## 1.19.3-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 75683ed: Added replay functionality to `AlertApiForwarder` to buffer and replay recent alerts to new subscribers, preventing missed alerts that were posted before subscription.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/config@1.3.6
|
|
10
|
+
- @backstage/core-plugin-api@1.12.1-next.0
|
|
11
|
+
- @backstage/types@1.2.2
|
|
12
|
+
- @backstage/version-bridge@1.0.11
|
|
13
|
+
|
|
14
|
+
## 1.19.3-next.0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 97cd16f: Internal update of translation imports.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @backstage/core-plugin-api@1.12.1-next.0
|
|
21
|
+
- @backstage/config@1.3.6
|
|
22
|
+
- @backstage/types@1.2.2
|
|
23
|
+
- @backstage/version-bridge@1.0.11
|
|
24
|
+
|
|
3
25
|
## 1.19.2
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { PublishSubject } from '../../../lib/subjects.esm.js';
|
|
2
|
+
import ObservableImpl from 'zen-observable';
|
|
2
3
|
|
|
3
4
|
class AlertApiForwarder {
|
|
4
5
|
subject = new PublishSubject();
|
|
6
|
+
recentAlerts = [];
|
|
7
|
+
maxBufferSize = 10;
|
|
5
8
|
post(alert) {
|
|
9
|
+
this.recentAlerts.push(alert);
|
|
10
|
+
if (this.recentAlerts.length > this.maxBufferSize) {
|
|
11
|
+
this.recentAlerts.shift();
|
|
12
|
+
}
|
|
6
13
|
this.subject.next(alert);
|
|
7
14
|
}
|
|
8
15
|
alert$() {
|
|
9
|
-
return
|
|
16
|
+
return new ObservableImpl((subscriber) => {
|
|
17
|
+
for (const alert of this.recentAlerts) {
|
|
18
|
+
subscriber.next(alert);
|
|
19
|
+
}
|
|
20
|
+
return this.subject.subscribe(subscriber);
|
|
21
|
+
});
|
|
10
22
|
}
|
|
11
23
|
}
|
|
12
24
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlertApiForwarder.esm.js","sources":["../../../../src/apis/implementations/AlertApi/AlertApiForwarder.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 { AlertApi, AlertMessage } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { PublishSubject } from '../../../lib/subjects';\n\n/**\n * Base implementation for the AlertApi that simply forwards alerts to consumers.\n *\n * @public\n */\nexport class AlertApiForwarder implements AlertApi {\n private readonly subject = new PublishSubject<AlertMessage>();\n\n post(alert: AlertMessage) {\n this.subject.next(alert);\n }\n\n alert$(): Observable<AlertMessage> {\n return this.subject;\n }\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AlertApiForwarder.esm.js","sources":["../../../../src/apis/implementations/AlertApi/AlertApiForwarder.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 { AlertApi, AlertMessage } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { PublishSubject } from '../../../lib/subjects';\nimport ObservableImpl from 'zen-observable';\n\n/**\n * Base implementation for the AlertApi that simply forwards alerts to consumers.\n *\n * Recent alerts are buffered and replayed to new subscribers to prevent\n * missing alerts that were posted before subscription.\n *\n * @public\n */\nexport class AlertApiForwarder implements AlertApi {\n private readonly subject = new PublishSubject<AlertMessage>();\n private readonly recentAlerts: AlertMessage[] = [];\n private readonly maxBufferSize = 10;\n\n post(alert: AlertMessage) {\n this.recentAlerts.push(alert);\n if (this.recentAlerts.length > this.maxBufferSize) {\n this.recentAlerts.shift();\n }\n this.subject.next(alert);\n }\n\n alert$(): Observable<AlertMessage> {\n return new ObservableImpl<AlertMessage>(subscriber => {\n for (const alert of this.recentAlerts) {\n subscriber.next(alert);\n }\n return this.subject.subscribe(subscriber);\n });\n }\n}\n"],"names":[],"mappings":";;;AA6BO,MAAM,iBAAA,CAAsC;AAAA,EAChC,OAAA,GAAU,IAAI,cAAA,EAA6B;AAAA,EAC3C,eAA+B,EAAC;AAAA,EAChC,aAAA,GAAgB,EAAA;AAAA,EAEjC,KAAK,KAAA,EAAqB;AACxB,IAAA,IAAA,CAAK,YAAA,CAAa,KAAK,KAAK,CAAA;AAC5B,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,MAAA,GAAS,IAAA,CAAK,aAAA,EAAe;AACjD,MAAA,IAAA,CAAK,aAAa,KAAA,EAAM;AAAA,IAC1B;AACA,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,EACzB;AAAA,EAEA,MAAA,GAAmC;AACjC,IAAA,OAAO,IAAI,eAA6B,CAAA,UAAA,KAAc;AACpD,MAAA,KAAA,MAAW,KAAA,IAAS,KAAK,YAAA,EAAc;AACrC,QAAA,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,MACvB;AACA,MAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,UAAU,CAAA;AAAA,IAC1C,CAAC,CAAA;AAAA,EACH;AACF;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInstance } from 'i18next';
|
|
2
2
|
import ObservableImpl from 'zen-observable';
|
|
3
|
-
import { toInternalTranslationResource } from '../../../
|
|
4
|
-
import { toInternalTranslationRef } from '../../../
|
|
3
|
+
import { toInternalTranslationResource } from '../../../frontend-plugin-api/src/translation/TranslationResource.esm.js';
|
|
4
|
+
import { toInternalTranslationRef } from '../../../frontend-plugin-api/src/translation/TranslationRef.esm.js';
|
|
5
5
|
import { DEFAULT_LANGUAGE } from '../AppLanguageApi/AppLanguageSelector.esm.js';
|
|
6
6
|
import { isValidElement, createElement, Fragment } from 'react';
|
|
7
7
|
|
|
@@ -98,7 +98,7 @@ class JsxInterpolator {
|
|
|
98
98
|
this.#pattern = new RegExp(`\\$${marker}\\(([^)]+)\\)`);
|
|
99
99
|
}
|
|
100
100
|
wrapT(originalT) {
|
|
101
|
-
return (key, options) => {
|
|
101
|
+
return ((key, options) => {
|
|
102
102
|
let elementsMap = void 0;
|
|
103
103
|
this.#setFormatHook((value) => {
|
|
104
104
|
if (isValidElement(value)) {
|
|
@@ -126,7 +126,7 @@ class JsxInterpolator {
|
|
|
126
126
|
return elementsMap?.get(part);
|
|
127
127
|
}).filter(Boolean)
|
|
128
128
|
);
|
|
129
|
-
};
|
|
129
|
+
});
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
class I18nextTranslationApi {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"I18nextTranslationApi.esm.js","sources":["../../../../src/apis/implementations/TranslationApi/I18nextTranslationApi.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\nimport {\n AppLanguageApi,\n TranslationApi,\n TranslationFunction,\n TranslationMessages,\n TranslationRef,\n TranslationResource,\n TranslationSnapshot,\n} from '@backstage/core-plugin-api/alpha';\nimport {\n createInstance as createI18n,\n FormatFunction,\n Interpolator,\n TFunction,\n type i18n as I18n,\n} from 'i18next';\nimport ObservableImpl from 'zen-observable';\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 {\n toInternalTranslationResource,\n InternalTranslationResourceLoader,\n} from '../../../../../core-plugin-api/src/translation/TranslationResource';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalTranslationRef,\n InternalTranslationRef,\n} from '../../../../../core-plugin-api/src/translation/TranslationRef';\nimport { Observable } from '@backstage/types';\nimport { DEFAULT_LANGUAGE } from '../AppLanguageApi/AppLanguageSelector';\nimport { createElement, Fragment, ReactNode, isValidElement } from 'react';\n\n/** @alpha */\nexport interface I18nextTranslationApiOptions {\n languageApi: AppLanguageApi;\n resources?: Array<TranslationMessages | TranslationResource>;\n}\n\nfunction removeNulls(\n messages: Record<string, string | null>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(messages).filter(\n (e): e is [string, string] => e[1] !== null,\n ),\n );\n}\n\n/**\n * The built-in i18next backend loading logic doesn't handle on the fly switches\n * of language very well. It gets a bit confused about whether resources are actually\n * loaded or not, so instead we implement our own resource loader.\n */\nclass ResourceLoader {\n /** Loaded resources by loader key */\n #loaded = new Set<string>();\n /** Resource loading promises by loader key */\n #loading = new Map<string, Promise<void>>();\n /** Loaders for each resource language */\n #loaders = new Map<string, InternalTranslationResourceLoader>();\n\n constructor(\n private readonly onLoad: (loaded: {\n language: string;\n namespace: string;\n messages: Record<string, string | null>;\n }) => void,\n ) {}\n\n addTranslationResource(resource: TranslationResource) {\n const internalResource = toInternalTranslationResource(resource);\n for (const entry of internalResource.resources) {\n const key = this.#getLoaderKey(entry.language, internalResource.id);\n\n // First loader to register wins, this means that resources registered in the app\n // have priority over default resource from translation refs\n if (!this.#loaders.has(key)) {\n this.#loaders.set(key, entry.loader);\n }\n }\n }\n\n #getLoaderKey(language: string, namespace: string) {\n return `${language}/${namespace}`;\n }\n\n needsLoading(language: string, namespace: string) {\n const key = this.#getLoaderKey(language, namespace);\n const loader = this.#loaders.get(key);\n if (!loader) {\n return false;\n }\n\n return !this.#loaded.has(key);\n }\n\n async load(language: string, namespace: string): Promise<void> {\n const key = this.#getLoaderKey(language, namespace);\n\n const loader = this.#loaders.get(key);\n if (!loader) {\n return;\n }\n\n if (this.#loaded.has(key)) {\n return;\n }\n\n const loading = this.#loading.get(key);\n if (loading) {\n await loading;\n return;\n }\n\n const load = loader().then(\n result => {\n this.onLoad({ language, namespace, messages: result.messages });\n this.#loaded.add(key);\n },\n error => {\n this.#loaded.add(key); // Do not try to load failed resources again\n throw error;\n },\n );\n this.#loading.set(key, load);\n await load;\n }\n}\n\n/**\n * A helper for implementing JSX interpolation\n */\nexport class JsxInterpolator {\n readonly #setFormatHook: (hook: FormatFunction) => void;\n readonly #marker: string;\n readonly #pattern: RegExp;\n\n static fromI18n(i18n: I18n) {\n const interpolator = i18n.services.interpolator as Interpolator & {\n format: FormatFunction;\n };\n const originalFormat = interpolator.format;\n\n let formatHook: FormatFunction | undefined;\n\n // This is the only way to override the format function of the interpolator\n // without overriding the default formatters. See the behavior here:\n // https://github.com/i18next/i18next/blob/c633121e57e2b6024080142d78027842bf2a6e5e/src/i18next.js#L120-L125\n interpolator.format = (value, format, lng, formatOpts) => {\n if (format) {\n return originalFormat(value, format, lng, formatOpts);\n }\n return formatHook?.(value, format, lng, formatOpts) ?? value;\n };\n\n return new JsxInterpolator(\n // Using a random marker to ensure it can't be misused\n Math.random().toString(36).substring(2, 8),\n hook => {\n formatHook = hook;\n },\n );\n }\n\n private constructor(\n marker: string,\n setFormatHook: (hook: FormatFunction) => void,\n ) {\n this.#setFormatHook = setFormatHook;\n this.#marker = marker;\n this.#pattern = new RegExp(`\\\\$${marker}\\\\(([^)]+)\\\\)`);\n }\n\n wrapT<TMessages extends { [key in string]: string }>(\n originalT: TFunction,\n ): TranslationFunction<TMessages> {\n return ((key, options) => {\n let elementsMap: Map<string, ReactNode> | undefined = undefined;\n\n // There's no way to override the format hook via the translation function\n // options, event though types indicate that it might be possible.\n // Instead, override the format function hook before every invocation and\n // rely on synchronous execution.\n this.#setFormatHook(value => {\n if (isValidElement(value)) {\n if (!elementsMap) {\n elementsMap = new Map();\n }\n const elementKey = elementsMap.size.toString();\n elementsMap.set(elementKey, value);\n\n return `$${this.#marker}(${elementKey})`;\n }\n return value;\n });\n\n // Overriding the return options is not allowed via TranslationFunction,\n // so this will always be a string\n const result = originalT(key, options as any) as unknown as string;\n if (!elementsMap) {\n return result;\n }\n\n const split = result.split(this.#pattern);\n\n return createElement(\n Fragment,\n null,\n ...split\n .map((part, index) => {\n if (index % 2 === 0) {\n return part;\n }\n return elementsMap?.get(part);\n })\n .filter(Boolean),\n );\n }) as TranslationFunction<TMessages>;\n }\n}\n\n/** @alpha */\nexport class I18nextTranslationApi implements TranslationApi {\n static create(options: I18nextTranslationApiOptions) {\n const { languages } = options.languageApi.getAvailableLanguages();\n\n const i18n = createI18n({\n fallbackLng: DEFAULT_LANGUAGE,\n supportedLngs: languages,\n interpolation: {\n escapeValue: false,\n // Used for the JsxInterpolator format hook\n alwaysFormat: true,\n },\n ns: [],\n defaultNS: false,\n fallbackNS: false,\n\n // Disable resource loading on init, meaning i18n will be ready to use immediately\n initImmediate: false,\n });\n\n i18n.init();\n if (!i18n.isInitialized) {\n throw new Error('i18next was unexpectedly not initialized');\n }\n\n const interpolator = JsxInterpolator.fromI18n(i18n);\n\n const { language: initialLanguage } = options.languageApi.getLanguage();\n if (initialLanguage !== DEFAULT_LANGUAGE) {\n i18n.changeLanguage(initialLanguage);\n }\n\n const loader = new ResourceLoader(loaded => {\n i18n.addResourceBundle(\n loaded.language,\n loaded.namespace,\n removeNulls(loaded.messages),\n false, // do not merge with existing translations\n true, // overwrite translations\n );\n });\n\n const resources = options?.resources || [];\n // Iterate in reverse, giving higher priority to resources registered later\n for (let i = resources.length - 1; i >= 0; i--) {\n const resource = resources[i];\n if (resource.$$type === '@backstage/TranslationResource') {\n loader.addTranslationResource(resource);\n } else if (resource.$$type === '@backstage/TranslationMessages') {\n // Overrides for default messages, created with createTranslationMessages and installed via app\n i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n resource.id,\n removeNulls(resource.messages),\n true, // merge with existing translations\n false, // do not overwrite translations\n );\n }\n }\n\n const instance = new I18nextTranslationApi(\n i18n,\n loader,\n options.languageApi.getLanguage().language,\n interpolator,\n );\n\n options.languageApi.language$().subscribe(({ language }) => {\n instance.#changeLanguage(language);\n });\n\n return instance;\n }\n\n #i18n: I18n;\n #loader: ResourceLoader;\n #language: string;\n #jsxInterpolator: JsxInterpolator;\n\n /** Keep track of which refs we have registered default resources for */\n #registeredRefs = new Set<string>();\n /** Notify observers when language changes */\n #languageChangeListeners = new Set<() => void>();\n\n private constructor(\n i18n: I18n,\n loader: ResourceLoader,\n language: string,\n jsxInterpolator: JsxInterpolator,\n ) {\n this.#i18n = i18n;\n this.#loader = loader;\n this.#language = language;\n this.#jsxInterpolator = jsxInterpolator;\n }\n\n getTranslation<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n this.#registerDefaults(internalRef);\n\n return this.#createSnapshot(internalRef);\n }\n\n translation$<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): Observable<TranslationSnapshot<TMessages>> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n this.#registerDefaults(internalRef);\n\n return new ObservableImpl<TranslationSnapshot<TMessages>>(subscriber => {\n let loadTicket = {}; // To check for stale loads\n\n const loadResource = () => {\n loadTicket = {};\n const ticket = loadTicket;\n this.#loader.load(this.#language, internalRef.id).then(\n () => {\n if (ticket === loadTicket) {\n const snapshot = this.#createSnapshot(internalRef);\n if (snapshot.ready) {\n subscriber.next(snapshot);\n }\n }\n },\n error => {\n if (ticket === loadTicket) {\n subscriber.error(Array.isArray(error) ? error[0] : error);\n }\n },\n );\n };\n\n const onChange = () => {\n const snapshot = this.#createSnapshot(internalRef);\n if (snapshot.ready) {\n subscriber.next(snapshot);\n } else {\n loadResource();\n }\n };\n\n if (this.#loader.needsLoading(this.#language, internalRef.id)) {\n loadResource();\n }\n\n this.#languageChangeListeners.add(onChange);\n return () => {\n this.#languageChangeListeners.delete(onChange);\n };\n });\n }\n\n #changeLanguage(language: string): void {\n if (this.#language !== language) {\n this.#language = language;\n this.#i18n.changeLanguage(language);\n this.#languageChangeListeners.forEach(listener => listener());\n }\n }\n\n #createSnapshot<TMessages extends { [key in string]: string }>(\n internalRef: InternalTranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n if (this.#loader.needsLoading(this.#language, internalRef.id)) {\n return { ready: false };\n }\n\n const unwrappedT = this.#i18n.getFixedT(null, internalRef.id);\n const t = this.#jsxInterpolator.wrapT<TMessages>(unwrappedT);\n\n return {\n ready: true,\n t,\n };\n }\n\n #registerDefaults(internalRef: InternalTranslationRef): void {\n if (this.#registeredRefs.has(internalRef.id)) {\n return;\n }\n this.#registeredRefs.add(internalRef.id);\n\n const defaultMessages = internalRef.getDefaultMessages();\n this.#i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n internalRef.id,\n defaultMessages,\n true, // merge with existing translations\n false, // do not overwrite translations\n );\n\n const defaultResource = internalRef.getDefaultResource();\n if (defaultResource) {\n this.#loader.addTranslationResource(defaultResource);\n }\n }\n}\n"],"names":["createI18n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"I18nextTranslationApi.esm.js","sources":["../../../../src/apis/implementations/TranslationApi/I18nextTranslationApi.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\nimport {\n AppLanguageApi,\n TranslationApi,\n TranslationFunction,\n TranslationMessages,\n TranslationRef,\n TranslationResource,\n TranslationSnapshot,\n} from '@backstage/core-plugin-api/alpha';\nimport {\n createInstance as createI18n,\n FormatFunction,\n Interpolator,\n TFunction,\n type i18n as I18n,\n} from 'i18next';\nimport ObservableImpl from 'zen-observable';\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 {\n toInternalTranslationResource,\n InternalTranslationResourceLoader,\n} from '../../../../../frontend-plugin-api/src/translation/TranslationResource';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalTranslationRef,\n InternalTranslationRef,\n} from '../../../../../frontend-plugin-api/src/translation/TranslationRef';\nimport { Observable } from '@backstage/types';\nimport { DEFAULT_LANGUAGE } from '../AppLanguageApi/AppLanguageSelector';\nimport { createElement, Fragment, ReactNode, isValidElement } from 'react';\n\n/** @alpha */\nexport interface I18nextTranslationApiOptions {\n languageApi: AppLanguageApi;\n resources?: Array<TranslationMessages | TranslationResource>;\n}\n\nfunction removeNulls(\n messages: Record<string, string | null>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(messages).filter(\n (e): e is [string, string] => e[1] !== null,\n ),\n );\n}\n\n/**\n * The built-in i18next backend loading logic doesn't handle on the fly switches\n * of language very well. It gets a bit confused about whether resources are actually\n * loaded or not, so instead we implement our own resource loader.\n */\nclass ResourceLoader {\n /** Loaded resources by loader key */\n #loaded = new Set<string>();\n /** Resource loading promises by loader key */\n #loading = new Map<string, Promise<void>>();\n /** Loaders for each resource language */\n #loaders = new Map<string, InternalTranslationResourceLoader>();\n\n constructor(\n private readonly onLoad: (loaded: {\n language: string;\n namespace: string;\n messages: Record<string, string | null>;\n }) => void,\n ) {}\n\n addTranslationResource(resource: TranslationResource) {\n const internalResource = toInternalTranslationResource(resource);\n for (const entry of internalResource.resources) {\n const key = this.#getLoaderKey(entry.language, internalResource.id);\n\n // First loader to register wins, this means that resources registered in the app\n // have priority over default resource from translation refs\n if (!this.#loaders.has(key)) {\n this.#loaders.set(key, entry.loader);\n }\n }\n }\n\n #getLoaderKey(language: string, namespace: string) {\n return `${language}/${namespace}`;\n }\n\n needsLoading(language: string, namespace: string) {\n const key = this.#getLoaderKey(language, namespace);\n const loader = this.#loaders.get(key);\n if (!loader) {\n return false;\n }\n\n return !this.#loaded.has(key);\n }\n\n async load(language: string, namespace: string): Promise<void> {\n const key = this.#getLoaderKey(language, namespace);\n\n const loader = this.#loaders.get(key);\n if (!loader) {\n return;\n }\n\n if (this.#loaded.has(key)) {\n return;\n }\n\n const loading = this.#loading.get(key);\n if (loading) {\n await loading;\n return;\n }\n\n const load = loader().then(\n result => {\n this.onLoad({ language, namespace, messages: result.messages });\n this.#loaded.add(key);\n },\n error => {\n this.#loaded.add(key); // Do not try to load failed resources again\n throw error;\n },\n );\n this.#loading.set(key, load);\n await load;\n }\n}\n\n/**\n * A helper for implementing JSX interpolation\n */\nexport class JsxInterpolator {\n readonly #setFormatHook: (hook: FormatFunction) => void;\n readonly #marker: string;\n readonly #pattern: RegExp;\n\n static fromI18n(i18n: I18n) {\n const interpolator = i18n.services.interpolator as Interpolator & {\n format: FormatFunction;\n };\n const originalFormat = interpolator.format;\n\n let formatHook: FormatFunction | undefined;\n\n // This is the only way to override the format function of the interpolator\n // without overriding the default formatters. See the behavior here:\n // https://github.com/i18next/i18next/blob/c633121e57e2b6024080142d78027842bf2a6e5e/src/i18next.js#L120-L125\n interpolator.format = (value, format, lng, formatOpts) => {\n if (format) {\n return originalFormat(value, format, lng, formatOpts);\n }\n return formatHook?.(value, format, lng, formatOpts) ?? value;\n };\n\n return new JsxInterpolator(\n // Using a random marker to ensure it can't be misused\n Math.random().toString(36).substring(2, 8),\n hook => {\n formatHook = hook;\n },\n );\n }\n\n private constructor(\n marker: string,\n setFormatHook: (hook: FormatFunction) => void,\n ) {\n this.#setFormatHook = setFormatHook;\n this.#marker = marker;\n this.#pattern = new RegExp(`\\\\$${marker}\\\\(([^)]+)\\\\)`);\n }\n\n wrapT<TMessages extends { [key in string]: string }>(\n originalT: TFunction,\n ): TranslationFunction<TMessages> {\n return ((key, options) => {\n let elementsMap: Map<string, ReactNode> | undefined = undefined;\n\n // There's no way to override the format hook via the translation function\n // options, event though types indicate that it might be possible.\n // Instead, override the format function hook before every invocation and\n // rely on synchronous execution.\n this.#setFormatHook(value => {\n if (isValidElement(value)) {\n if (!elementsMap) {\n elementsMap = new Map();\n }\n const elementKey = elementsMap.size.toString();\n elementsMap.set(elementKey, value);\n\n return `$${this.#marker}(${elementKey})`;\n }\n return value;\n });\n\n // Overriding the return options is not allowed via TranslationFunction,\n // so this will always be a string\n const result = originalT(key, options as any) as unknown as string;\n if (!elementsMap) {\n return result;\n }\n\n const split = result.split(this.#pattern);\n\n return createElement(\n Fragment,\n null,\n ...split\n .map((part, index) => {\n if (index % 2 === 0) {\n return part;\n }\n return elementsMap?.get(part);\n })\n .filter(Boolean),\n );\n }) as TranslationFunction<TMessages>;\n }\n}\n\n/** @alpha */\nexport class I18nextTranslationApi implements TranslationApi {\n static create(options: I18nextTranslationApiOptions) {\n const { languages } = options.languageApi.getAvailableLanguages();\n\n const i18n = createI18n({\n fallbackLng: DEFAULT_LANGUAGE,\n supportedLngs: languages,\n interpolation: {\n escapeValue: false,\n // Used for the JsxInterpolator format hook\n alwaysFormat: true,\n },\n ns: [],\n defaultNS: false,\n fallbackNS: false,\n\n // Disable resource loading on init, meaning i18n will be ready to use immediately\n initImmediate: false,\n });\n\n i18n.init();\n if (!i18n.isInitialized) {\n throw new Error('i18next was unexpectedly not initialized');\n }\n\n const interpolator = JsxInterpolator.fromI18n(i18n);\n\n const { language: initialLanguage } = options.languageApi.getLanguage();\n if (initialLanguage !== DEFAULT_LANGUAGE) {\n i18n.changeLanguage(initialLanguage);\n }\n\n const loader = new ResourceLoader(loaded => {\n i18n.addResourceBundle(\n loaded.language,\n loaded.namespace,\n removeNulls(loaded.messages),\n false, // do not merge with existing translations\n true, // overwrite translations\n );\n });\n\n const resources = options?.resources || [];\n // Iterate in reverse, giving higher priority to resources registered later\n for (let i = resources.length - 1; i >= 0; i--) {\n const resource = resources[i];\n if (resource.$$type === '@backstage/TranslationResource') {\n loader.addTranslationResource(resource);\n } else if (resource.$$type === '@backstage/TranslationMessages') {\n // Overrides for default messages, created with createTranslationMessages and installed via app\n i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n resource.id,\n removeNulls(resource.messages),\n true, // merge with existing translations\n false, // do not overwrite translations\n );\n }\n }\n\n const instance = new I18nextTranslationApi(\n i18n,\n loader,\n options.languageApi.getLanguage().language,\n interpolator,\n );\n\n options.languageApi.language$().subscribe(({ language }) => {\n instance.#changeLanguage(language);\n });\n\n return instance;\n }\n\n #i18n: I18n;\n #loader: ResourceLoader;\n #language: string;\n #jsxInterpolator: JsxInterpolator;\n\n /** Keep track of which refs we have registered default resources for */\n #registeredRefs = new Set<string>();\n /** Notify observers when language changes */\n #languageChangeListeners = new Set<() => void>();\n\n private constructor(\n i18n: I18n,\n loader: ResourceLoader,\n language: string,\n jsxInterpolator: JsxInterpolator,\n ) {\n this.#i18n = i18n;\n this.#loader = loader;\n this.#language = language;\n this.#jsxInterpolator = jsxInterpolator;\n }\n\n getTranslation<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n this.#registerDefaults(internalRef);\n\n return this.#createSnapshot(internalRef);\n }\n\n translation$<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): Observable<TranslationSnapshot<TMessages>> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n this.#registerDefaults(internalRef);\n\n return new ObservableImpl<TranslationSnapshot<TMessages>>(subscriber => {\n let loadTicket = {}; // To check for stale loads\n\n const loadResource = () => {\n loadTicket = {};\n const ticket = loadTicket;\n this.#loader.load(this.#language, internalRef.id).then(\n () => {\n if (ticket === loadTicket) {\n const snapshot = this.#createSnapshot(internalRef);\n if (snapshot.ready) {\n subscriber.next(snapshot);\n }\n }\n },\n error => {\n if (ticket === loadTicket) {\n subscriber.error(Array.isArray(error) ? error[0] : error);\n }\n },\n );\n };\n\n const onChange = () => {\n const snapshot = this.#createSnapshot(internalRef);\n if (snapshot.ready) {\n subscriber.next(snapshot);\n } else {\n loadResource();\n }\n };\n\n if (this.#loader.needsLoading(this.#language, internalRef.id)) {\n loadResource();\n }\n\n this.#languageChangeListeners.add(onChange);\n return () => {\n this.#languageChangeListeners.delete(onChange);\n };\n });\n }\n\n #changeLanguage(language: string): void {\n if (this.#language !== language) {\n this.#language = language;\n this.#i18n.changeLanguage(language);\n this.#languageChangeListeners.forEach(listener => listener());\n }\n }\n\n #createSnapshot<TMessages extends { [key in string]: string }>(\n internalRef: InternalTranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n if (this.#loader.needsLoading(this.#language, internalRef.id)) {\n return { ready: false };\n }\n\n const unwrappedT = this.#i18n.getFixedT(null, internalRef.id);\n const t = this.#jsxInterpolator.wrapT<TMessages>(unwrappedT);\n\n return {\n ready: true,\n t,\n };\n }\n\n #registerDefaults(internalRef: InternalTranslationRef): void {\n if (this.#registeredRefs.has(internalRef.id)) {\n return;\n }\n this.#registeredRefs.add(internalRef.id);\n\n const defaultMessages = internalRef.getDefaultMessages();\n this.#i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n internalRef.id,\n defaultMessages,\n true, // merge with existing translations\n false, // do not overwrite translations\n );\n\n const defaultResource = internalRef.getDefaultResource();\n if (defaultResource) {\n this.#loader.addTranslationResource(defaultResource);\n }\n }\n}\n"],"names":["createI18n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationRef.esm.js","sources":["../../../../../frontend-plugin-api/src/translation/TranslationRef.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\nimport {\n createTranslationResource,\n TranslationResource,\n} from './TranslationResource';\n\n/** @public */\nexport interface TranslationRef<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n> {\n $$type: '@backstage/TranslationRef';\n\n id: TId;\n\n T: TMessages;\n}\n\n/** @internal */\ntype AnyMessages = { [key in string]: string };\n\n/** @ignore */\ntype AnyNestedMessages = { [key in string]: AnyNestedMessages | string };\n\n/**\n * Flattens a nested message declaration into a flat object with dot-separated keys.\n *\n * @ignore\n */\ntype FlattenedMessages<TMessages extends AnyNestedMessages> =\n // Flatten out object keys into a union structure of objects, e.g. { a: 'a', b: 'b' } -> { a: 'a' } | { b: 'b' }\n // Any nested object will be flattened into the individual unions, e.g. { a: 'a', b: { x: 'x', y: 'y' } } -> { a: 'a' } | { 'b.x': 'x', 'b.y': 'y' }\n // We create this structure by first nesting the desired union types into the original object, and\n // then extract them by indexing with `keyof TMessages` to form the union.\n // Throughout this the objects are wrapped up in a function parameter, which allows us to have the\n // final step of flipping this unions around to an intersection by inferring the function parameter.\n {\n [TKey in keyof TMessages]: (\n _: TMessages[TKey] extends infer TValue // \"local variable\" for the value\n ? TValue extends AnyNestedMessages\n ? FlattenedMessages<TValue> extends infer TNested // Recurse into nested messages, \"local variable\" for the result\n ? {\n [TNestedKey in keyof TNested as `${TKey & string}.${TNestedKey &\n string}`]: TNested[TNestedKey];\n }\n : never\n : { [_ in TKey]: TValue } // Primitive object values are passed through with the same key\n : never,\n ) => void;\n // The `[keyof TMessages]` extracts the object values union from our flattened structure, still wrapped up in function parameters.\n // The `extends (_: infer TIntersection) => void` flips the union to an intersection, at which point we have the correct type.\n }[keyof TMessages] extends (_: infer TIntersection) => void\n ? // This object mapping just expands similar to the Expand<> utility type, providing nicer type hints\n {\n readonly [TExpandKey in keyof TIntersection]: TIntersection[TExpandKey];\n }\n : never;\n\n/** @internal */\nexport interface InternalTranslationRef<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n> extends TranslationRef<TId, TMessages> {\n version: 'v1';\n\n getDefaultMessages(): AnyMessages;\n\n getDefaultResource(): TranslationResource | undefined;\n}\n\n/** @public */\nexport interface TranslationRefOptions<\n TId extends string,\n TNestedMessages extends AnyNestedMessages,\n TTranslations extends {\n [language in string]: () => Promise<{\n default: {\n [key in keyof FlattenedMessages<TNestedMessages>]: string | null;\n };\n }>;\n },\n> {\n id: TId;\n messages: TNestedMessages;\n translations?: TTranslations;\n}\n\nfunction flattenMessages(nested: AnyNestedMessages): AnyMessages {\n const entries = new Array<[string, string]>();\n\n function visit(obj: AnyNestedMessages, prefix: string): void {\n for (const [key, value] of Object.entries(obj)) {\n if (typeof value === 'string') {\n entries.push([prefix + key, value]);\n } else {\n visit(value, `${prefix}${key}.`);\n }\n }\n }\n\n visit(nested, '');\n\n return Object.fromEntries(entries);\n}\n\n/** @internal */\nclass TranslationRefImpl<\n TId extends string,\n TNestedMessages extends AnyNestedMessages,\n> implements InternalTranslationRef<TId, FlattenedMessages<TNestedMessages>>\n{\n #id: TId;\n #messages: FlattenedMessages<TNestedMessages>;\n #resources: TranslationResource | undefined;\n\n constructor(options: TranslationRefOptions<TId, TNestedMessages, any>) {\n this.#id = options.id;\n this.#messages = flattenMessages(\n options.messages,\n ) as FlattenedMessages<TNestedMessages>;\n }\n\n $$type = '@backstage/TranslationRef' as const;\n\n version = 'v1' as const;\n\n get id(): TId {\n return this.#id;\n }\n\n get T(): never {\n throw new Error('Not implemented');\n }\n\n getDefaultMessages(): AnyMessages {\n return this.#messages;\n }\n\n setDefaultResource(resources: TranslationResource): void {\n this.#resources = resources;\n }\n\n getDefaultResource(): TranslationResource | undefined {\n return this.#resources;\n }\n\n toString() {\n return `TranslationRef{id=${this.id}}`;\n }\n}\n\n/** @public */\nexport function createTranslationRef<\n TId extends string,\n const TNestedMessages extends AnyNestedMessages,\n TTranslations extends {\n [language in string]: () => Promise<{\n default: {\n [key in keyof FlattenedMessages<TNestedMessages>]: string | null;\n };\n }>;\n },\n>(\n config: TranslationRefOptions<TId, TNestedMessages, TTranslations>,\n): TranslationRef<TId, FlattenedMessages<TNestedMessages>> {\n const ref = new TranslationRefImpl(config);\n if (config.translations) {\n ref.setDefaultResource(\n createTranslationResource({\n ref,\n translations: config.translations as any,\n }),\n );\n }\n return ref;\n}\n\n/** @internal */\nexport function toInternalTranslationRef<\n TId extends string,\n TMessages extends AnyMessages,\n>(ref: TranslationRef<TId, TMessages>): InternalTranslationRef<TId, TMessages> {\n const r = ref as InternalTranslationRef<TId, TMessages>;\n if (r.$$type !== '@backstage/TranslationRef') {\n throw new Error(`Invalid translation ref, bad type '${r.$$type}'`);\n }\n if (r.version !== 'v1') {\n throw new Error(`Invalid translation ref, bad version '${r.version}'`);\n }\n return r;\n}\n"],"names":[],"mappings":"AAiMO,SAAS,yBAGd,GAAA,EAA6E;AAC7E,EAAA,MAAM,CAAA,GAAI,GAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,2BAAA,EAA6B;AAC5C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACnE;AACA,EAAA,IAAI,CAAA,CAAE,YAAY,IAAA,EAAM;AACtB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sCAAA,EAAyC,CAAA,CAAE,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,CAAA;AACT;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationResource.esm.js","sources":["../../../../../frontend-plugin-api/src/translation/TranslationResource.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\nimport { TranslationMessages } from './TranslationMessages';\nimport { TranslationRef } from './TranslationRef';\n\n/** @public */\nexport interface TranslationResource<TId extends string = string> {\n $$type: '@backstage/TranslationResource';\n id: TId;\n}\n\n/** @internal */\nexport type InternalTranslationResourceLoader = () => Promise<{\n messages: { [key in string]: string | null };\n}>;\n\n/** @internal */\nexport interface InternalTranslationResource<TId extends string = string>\n extends TranslationResource<TId> {\n version: 'v1';\n resources: Array<{\n language: string;\n loader: InternalTranslationResourceLoader;\n }>;\n}\n\n/** @internal */\nexport function toInternalTranslationResource<TId extends string>(\n resource: TranslationResource<TId>,\n): InternalTranslationResource<TId> {\n const r = resource as InternalTranslationResource<TId>;\n if (r.$$type !== '@backstage/TranslationResource') {\n throw new Error(`Invalid translation resource, bad type '${r.$$type}'`);\n }\n if (r.version !== 'v1') {\n throw new Error(`Invalid translation resource, bad version '${r.version}'`);\n }\n\n return r;\n}\n\n/** @public */\nexport interface TranslationResourceOptions<\n TId extends string,\n TMessages extends { [key in string]: string },\n TTranslations extends {\n [language in string]: () => Promise<{\n default:\n | TranslationMessages<TId>\n | { [key in keyof TMessages]: string | null };\n }>;\n },\n> {\n ref: TranslationRef<TId, TMessages>;\n\n translations: TTranslations;\n}\n\n/** @public */\nexport function createTranslationResource<\n TId extends string,\n TMessages extends { [key in string]: string },\n TTranslations extends {\n [language in string]: () => Promise<{\n default:\n | TranslationMessages<TId>\n | { [key in keyof TMessages]: string | null };\n }>;\n },\n>(\n options: TranslationResourceOptions<TId, TMessages, TTranslations>,\n): TranslationResource<TId> {\n return {\n $$type: '@backstage/TranslationResource',\n version: 'v1',\n id: options.ref.id,\n resources: Object.entries(options.translations).map(\n ([language, loader]) => ({\n language,\n loader: () =>\n loader().then(m => {\n const value = m.default;\n return {\n messages:\n value?.$$type === '@backstage/TranslationMessages'\n ? value.messages\n : value,\n };\n }),\n }),\n ),\n } as InternalTranslationResource<TId>;\n}\n"],"names":[],"mappings":"AAyCO,SAAS,8BACd,QAAA,EACkC;AAClC,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,gCAAA,EAAkC;AACjD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wCAAA,EAA2C,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACxE;AACA,EAAA,IAAI,CAAA,CAAE,YAAY,IAAA,EAAM;AACtB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,2CAAA,EAA8C,CAAA,CAAE,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EAC5E;AAEA,EAAA,OAAO,CAAA;AACT;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { PropsWithChildren, ReactNode, ComponentType } from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
|
-
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
5
4
|
import { ApiHolder, ApiRef, ApiFactory, AnyApiRef, DiscoveryApi, AuthProviderInfo, ConfigApi, OAuthRequestApi, githubAuthApiRef, gitlabAuthApiRef, googleAuthApiRef, ProfileInfo, BackstageIdentityResponse, OAuthApi, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, SessionApi, SessionState, AuthRequestOptions, oktaAuthApiRef, microsoftAuthApiRef, oneloginAuthApiRef, bitbucketAuthApiRef, bitbucketServerAuthApiRef, atlassianAuthApiRef, vmwareCloudAuthApiRef, openshiftAuthApiRef, AlertApi, AlertMessage, AnalyticsApi, AnalyticsEvent, AppThemeApi, AppTheme, ErrorApi, ErrorApiError, ErrorApiErrorContext, FeatureFlagsApi, FeatureFlag, FeatureFlagsSaveOptions, FetchApi, IdentityApi, OAuthRequesterOptions, OAuthRequester, PendingOAuthRequest, StorageApi, StorageValueSnapshot, AnyApiFactory, IconComponent, BackstagePlugin, ExternalRouteRef, RouteRef, SubRouteRef } from '@backstage/core-plugin-api';
|
|
6
5
|
import * as _backstage_types from '@backstage/types';
|
|
7
6
|
import { Observable, JsonValue } from '@backstage/types';
|
|
7
|
+
import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
|
|
8
8
|
import { Config, AppConfig } from '@backstage/config';
|
|
9
9
|
export { ConfigReader } from '@backstage/config';
|
|
10
10
|
|
|
@@ -320,11 +320,11 @@ declare class MicrosoftAuth {
|
|
|
320
320
|
private static resourceForScope;
|
|
321
321
|
getAccessToken(scope?: string | string[], options?: AuthRequestOptions): Promise<string>;
|
|
322
322
|
getIdToken(options?: AuthRequestOptions): Promise<string>;
|
|
323
|
-
getProfile(options?: AuthRequestOptions): Promise<
|
|
324
|
-
getBackstageIdentity(options?: AuthRequestOptions): Promise<
|
|
323
|
+
getProfile(options?: AuthRequestOptions): Promise<_backstage_frontend_plugin_api.ProfileInfo | undefined>;
|
|
324
|
+
getBackstageIdentity(options?: AuthRequestOptions): Promise<_backstage_frontend_plugin_api.BackstageIdentityResponse | undefined>;
|
|
325
325
|
signIn(): Promise<void>;
|
|
326
326
|
signOut(): Promise<void>;
|
|
327
|
-
sessionState$(): _backstage_types.Observable<
|
|
327
|
+
sessionState$(): _backstage_types.Observable<_backstage_frontend_plugin_api.SessionState>;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/**
|
|
@@ -424,10 +424,15 @@ declare class OpenShiftAuth {
|
|
|
424
424
|
/**
|
|
425
425
|
* Base implementation for the AlertApi that simply forwards alerts to consumers.
|
|
426
426
|
*
|
|
427
|
+
* Recent alerts are buffered and replayed to new subscribers to prevent
|
|
428
|
+
* missing alerts that were posted before subscription.
|
|
429
|
+
*
|
|
427
430
|
* @public
|
|
428
431
|
*/
|
|
429
432
|
declare class AlertApiForwarder implements AlertApi {
|
|
430
433
|
private readonly subject;
|
|
434
|
+
private readonly recentAlerts;
|
|
435
|
+
private readonly maxBufferSize;
|
|
431
436
|
post(alert: AlertMessage): void;
|
|
432
437
|
alert$(): Observable<AlertMessage>;
|
|
433
438
|
}
|
|
@@ -1161,4 +1166,5 @@ type FeatureFlaggedProps = {
|
|
|
1161
1166
|
*/
|
|
1162
1167
|
declare const FeatureFlagged: (props: FeatureFlaggedProps) => react_jsx_runtime.JSX.Element;
|
|
1163
1168
|
|
|
1164
|
-
export { AlertApiForwarder,
|
|
1169
|
+
export { AlertApiForwarder, ApiFactoryRegistry, ApiProvider, ApiResolver, AppRouter, AppThemeSelector, AtlassianAuth, BitbucketAuth, BitbucketServerAuth, ErrorAlerter, ErrorApiForwarder, FeatureFlagged, FetchMiddlewares, FlatRoutes, FrontendHostDiscovery, GithubAuth, GitlabAuth, GoogleAuth, LocalStorageFeatureFlags, MicrosoftAuth, MultipleAnalyticsApi, NoOpAnalyticsApi, OAuth2, OAuthRequestManager, OktaAuth, OneLoginAuth, OpenShiftAuth, SamlAuth, UnhandledErrorForwarder, UrlPatternDiscovery, VMwareCloudAuth, WebStorage, createFetchApi, createSpecializedApp, defaultConfigLoader, openLoginPopup };
|
|
1170
|
+
export type { ApiFactoryHolder, ApiFactoryScope, ApiProviderProps, AppComponents, AppConfigLoader, AppContext, AppIcons, AppOptions, AppRouteBinder, AppRouterProps, AuthApiCreateOptions, AuthConnector, AuthConnectorCreateSessionOptions, AuthConnectorRefreshSessionOptions, BackstageApp, BitbucketServerSession, BitbucketSession, BootErrorPageProps, ErrorBoundaryFallbackProps, FeatureFlaggedProps, FetchMiddleware, FlatRoutesProps, OAuth2CreateOptions, OAuth2CreateOptionsWithAuthConnector, OAuth2Session, OAuthApiCreateOptions, OneLoginAuthCreateOptions, OpenLoginPopupOptions, PopupOptions, SignInPageProps };
|
|
@@ -2,7 +2,7 @@ import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
|
|
|
2
2
|
|
|
3
3
|
const routeRefType = getOrCreateGlobalSingleton(
|
|
4
4
|
"route-ref-type",
|
|
5
|
-
() => Symbol("route-ref-type")
|
|
5
|
+
() => /* @__PURE__ */ Symbol("route-ref-type")
|
|
6
6
|
);
|
|
7
7
|
function isRouteRef(routeRef) {
|
|
8
8
|
return routeRef[routeRefType] === "absolute";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.esm.js","sources":["../../src/routing/types.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 RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n BackstagePlugin,\n} from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\ntype RouteRefType = Exclude<\n keyof RouteRef,\n 'params' | 'path' | 'title' | 'icon'\n>;\nexport const routeRefType: RouteRefType = getOrCreateGlobalSingleton<any>(\n 'route-ref-type',\n () => Symbol('route-ref-type'),\n);\n\nexport type AnyParams = { [param in string]: string } | undefined;\n\nexport type AnyRouteRef =\n | RouteRef<any>\n | SubRouteRef<any>\n | ExternalRouteRef<any, any>;\n\n// The extra TS magic here is to require a single params argument if the RouteRef\n// had at least one param defined, but require 0 arguments if there are no params defined.\n// Without this we'd have to pass in empty object to all parameter-less RouteRefs\n// just to make TypeScript happy, or we would have to make the argument optional in\n// which case you might forget to pass it in when it is actually required.\nexport type RouteFunc<Params extends AnyParams> = (\n ...[params]: Params extends undefined ? readonly [] : readonly [Params]\n) => string;\n\n// A duplicate of the react-router RouteObject, but with routeRef added\nexport interface BackstageRouteObject {\n caseSensitive: boolean;\n children?: BackstageRouteObject[];\n element: React.ReactNode;\n path: string;\n routeRefs: Set<RouteRef>;\n plugins: Set<BackstagePlugin>;\n}\n\nexport function isRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is RouteRef<Params> {\n return routeRef[routeRefType] === 'absolute';\n}\n\nexport function isSubRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is SubRouteRef<Params> {\n return routeRef[routeRefType] === 'sub';\n}\n\nexport function isExternalRouteRef<\n Params extends AnyParams,\n Optional extends boolean,\n>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, Optional>,\n): routeRef is ExternalRouteRef<Params, Optional> {\n return routeRef[routeRefType] === 'external';\n}\n"],"names":[],"mappings":";;AA4BO,MAAM,YAAA,GAA6B,0BAAA;AAAA,EACxC,gBAAA;AAAA,EACA,
|
|
1
|
+
{"version":3,"file":"types.esm.js","sources":["../../src/routing/types.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 RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n BackstagePlugin,\n} from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\ntype RouteRefType = Exclude<\n keyof RouteRef,\n 'params' | 'path' | 'title' | 'icon'\n>;\nexport const routeRefType: RouteRefType = getOrCreateGlobalSingleton<any>(\n 'route-ref-type',\n () => Symbol('route-ref-type'),\n);\n\nexport type AnyParams = { [param in string]: string } | undefined;\n\nexport type AnyRouteRef =\n | RouteRef<any>\n | SubRouteRef<any>\n | ExternalRouteRef<any, any>;\n\n// The extra TS magic here is to require a single params argument if the RouteRef\n// had at least one param defined, but require 0 arguments if there are no params defined.\n// Without this we'd have to pass in empty object to all parameter-less RouteRefs\n// just to make TypeScript happy, or we would have to make the argument optional in\n// which case you might forget to pass it in when it is actually required.\nexport type RouteFunc<Params extends AnyParams> = (\n ...[params]: Params extends undefined ? readonly [] : readonly [Params]\n) => string;\n\n// A duplicate of the react-router RouteObject, but with routeRef added\nexport interface BackstageRouteObject {\n caseSensitive: boolean;\n children?: BackstageRouteObject[];\n element: React.ReactNode;\n path: string;\n routeRefs: Set<RouteRef>;\n plugins: Set<BackstagePlugin>;\n}\n\nexport function isRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is RouteRef<Params> {\n return routeRef[routeRefType] === 'absolute';\n}\n\nexport function isSubRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is SubRouteRef<Params> {\n return routeRef[routeRefType] === 'sub';\n}\n\nexport function isExternalRouteRef<\n Params extends AnyParams,\n Optional extends boolean,\n>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, Optional>,\n): routeRef is ExternalRouteRef<Params, Optional> {\n return routeRef[routeRefType] === 'external';\n}\n"],"names":[],"mappings":";;AA4BO,MAAM,YAAA,GAA6B,0BAAA;AAAA,EACxC,gBAAA;AAAA,EACA,6BAAa,gBAAgB;AAC/B;AA4BO,SAAS,WACd,QAAA,EAI8B;AAC9B,EAAA,OAAO,QAAA,CAAS,YAAY,CAAA,KAAM,UAAA;AACpC;AAEO,SAAS,cACd,QAAA,EAIiC;AACjC,EAAA,OAAO,QAAA,CAAS,YAAY,CAAA,KAAM,KAAA;AACpC;AAEO,SAAS,mBAId,QAAA,EAIgD;AAChD,EAAA,OAAO,QAAA,CAAS,YAAY,CAAA,KAAM,UAAA;AACpC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/core-app-api",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.3-next.1",
|
|
4
4
|
"description": "Core app API used by Backstage apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "web-library"
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"test": "backstage-cli package test"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@backstage/config": "
|
|
54
|
-
"@backstage/core-plugin-api": "
|
|
55
|
-
"@backstage/types": "
|
|
56
|
-
"@backstage/version-bridge": "
|
|
53
|
+
"@backstage/config": "1.3.6",
|
|
54
|
+
"@backstage/core-plugin-api": "1.12.1-next.0",
|
|
55
|
+
"@backstage/types": "1.2.2",
|
|
56
|
+
"@backstage/version-bridge": "1.0.11",
|
|
57
57
|
"@types/prop-types": "^15.7.3",
|
|
58
58
|
"history": "^5.0.0",
|
|
59
59
|
"i18next": "^22.4.15",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"zod": "^3.22.4"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@backstage/cli": "
|
|
68
|
-
"@backstage/test-utils": "
|
|
67
|
+
"@backstage/cli": "0.35.0-next.2",
|
|
68
|
+
"@backstage/test-utils": "1.7.14-next.0",
|
|
69
69
|
"@testing-library/dom": "^10.0.0",
|
|
70
70
|
"@testing-library/jest-dom": "^6.0.0",
|
|
71
71
|
"@testing-library/react": "^16.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationRef.esm.js","sources":["../../../../../core-plugin-api/src/translation/TranslationRef.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\nimport {\n createTranslationResource,\n TranslationResource,\n} from './TranslationResource';\n\n/** @alpha */\nexport interface TranslationRef<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n> {\n $$type: '@backstage/TranslationRef';\n\n id: TId;\n\n T: TMessages;\n}\n\n/** @internal */\ntype AnyMessages = { [key in string]: string };\n\n/** @ignore */\ntype AnyNestedMessages = { [key in string]: AnyNestedMessages | string };\n\n/**\n * Flattens a nested message declaration into a flat object with dot-separated keys.\n *\n * @ignore\n */\ntype FlattenedMessages<TMessages extends AnyNestedMessages> =\n // Flatten out object keys into a union structure of objects, e.g. { a: 'a', b: 'b' } -> { a: 'a' } | { b: 'b' }\n // Any nested object will be flattened into the individual unions, e.g. { a: 'a', b: { x: 'x', y: 'y' } } -> { a: 'a' } | { 'b.x': 'x', 'b.y': 'y' }\n // We create this structure by first nesting the desired union types into the original object, and\n // then extract them by indexing with `keyof TMessages` to form the union.\n // Throughout this the objects are wrapped up in a function parameter, which allows us to have the\n // final step of flipping this unions around to an intersection by inferring the function parameter.\n {\n [TKey in keyof TMessages]: (\n _: TMessages[TKey] extends infer TValue // \"local variable\" for the value\n ? TValue extends AnyNestedMessages\n ? FlattenedMessages<TValue> extends infer TNested // Recurse into nested messages, \"local variable\" for the result\n ? {\n [TNestedKey in keyof TNested as `${TKey & string}.${TNestedKey &\n string}`]: TNested[TNestedKey];\n }\n : never\n : { [_ in TKey]: TValue } // Primitive object values are passed through with the same key\n : never,\n ) => void;\n // The `[keyof TMessages]` extracts the object values union from our flattened structure, still wrapped up in function parameters.\n // The `extends (_: infer TIntersection) => void` flips the union to an intersection, at which point we have the correct type.\n }[keyof TMessages] extends (_: infer TIntersection) => void\n ? // This object mapping just expands similar to the Expand<> utility type, providing nicer type hints\n {\n readonly [TExpandKey in keyof TIntersection]: TIntersection[TExpandKey];\n }\n : never;\n\n/** @internal */\nexport interface InternalTranslationRef<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n> extends TranslationRef<TId, TMessages> {\n version: 'v1';\n\n getDefaultMessages(): AnyMessages;\n\n getDefaultResource(): TranslationResource | undefined;\n}\n\n/** @alpha */\nexport interface TranslationRefOptions<\n TId extends string,\n TNestedMessages extends AnyNestedMessages,\n TTranslations extends {\n [language in string]: () => Promise<{\n default: {\n [key in keyof FlattenedMessages<TNestedMessages>]: string | null;\n };\n }>;\n },\n> {\n id: TId;\n messages: TNestedMessages;\n translations?: TTranslations;\n}\n\nfunction flattenMessages(nested: AnyNestedMessages): AnyMessages {\n const entries = new Array<[string, string]>();\n\n function visit(obj: AnyNestedMessages, prefix: string): void {\n for (const [key, value] of Object.entries(obj)) {\n if (typeof value === 'string') {\n entries.push([prefix + key, value]);\n } else {\n visit(value, `${prefix}${key}.`);\n }\n }\n }\n\n visit(nested, '');\n\n return Object.fromEntries(entries);\n}\n\n/** @internal */\nclass TranslationRefImpl<\n TId extends string,\n TNestedMessages extends AnyNestedMessages,\n> implements InternalTranslationRef<TId, FlattenedMessages<TNestedMessages>>\n{\n #id: TId;\n #messages: FlattenedMessages<TNestedMessages>;\n #resources: TranslationResource | undefined;\n\n constructor(options: TranslationRefOptions<TId, TNestedMessages, any>) {\n this.#id = options.id;\n this.#messages = flattenMessages(\n options.messages,\n ) as FlattenedMessages<TNestedMessages>;\n }\n\n $$type = '@backstage/TranslationRef' as const;\n\n version = 'v1' as const;\n\n get id(): TId {\n return this.#id;\n }\n\n get T(): never {\n throw new Error('Not implemented');\n }\n\n getDefaultMessages(): AnyMessages {\n return this.#messages;\n }\n\n setDefaultResource(resources: TranslationResource): void {\n this.#resources = resources;\n }\n\n getDefaultResource(): TranslationResource | undefined {\n return this.#resources;\n }\n\n toString() {\n return `TranslationRef{id=${this.id}}`;\n }\n}\n\n/** @alpha */\nexport function createTranslationRef<\n TId extends string,\n const TNestedMessages extends AnyNestedMessages,\n TTranslations extends {\n [language in string]: () => Promise<{\n default: {\n [key in keyof FlattenedMessages<TNestedMessages>]: string | null;\n };\n }>;\n },\n>(\n config: TranslationRefOptions<TId, TNestedMessages, TTranslations>,\n): TranslationRef<TId, FlattenedMessages<TNestedMessages>> {\n const ref = new TranslationRefImpl(config);\n if (config.translations) {\n ref.setDefaultResource(\n createTranslationResource({\n ref,\n translations: config.translations as any,\n }),\n );\n }\n return ref;\n}\n\n/** @internal */\nexport function toInternalTranslationRef<\n TId extends string,\n TMessages extends AnyMessages,\n>(ref: TranslationRef<TId, TMessages>): InternalTranslationRef<TId, TMessages> {\n const r = ref as InternalTranslationRef<TId, TMessages>;\n if (r.$$type !== '@backstage/TranslationRef') {\n throw new Error(`Invalid translation ref, bad type '${r.$$type}'`);\n }\n if (r.version !== 'v1') {\n throw new Error(`Invalid translation ref, bad version '${r.version}'`);\n }\n return r;\n}\n"],"names":[],"mappings":"AAiMO,SAAS,yBAGd,GAAA,EAA6E;AAC7E,EAAA,MAAM,CAAA,GAAI,GAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,2BAAA,EAA6B;AAC5C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACnE;AACA,EAAA,IAAI,CAAA,CAAE,YAAY,IAAA,EAAM;AACtB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sCAAA,EAAyC,CAAA,CAAE,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,CAAA;AACT;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationResource.esm.js","sources":["../../../../../core-plugin-api/src/translation/TranslationResource.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\nimport {\n TranslationMessages,\n TranslationRef,\n} from '@backstage/core-plugin-api/alpha';\n\n/** @alpha */\nexport interface TranslationResource<TId extends string = string> {\n $$type: '@backstage/TranslationResource';\n id: TId;\n}\n\n/** @internal */\nexport type InternalTranslationResourceLoader = () => Promise<{\n messages: { [key in string]: string | null };\n}>;\n\n/** @internal */\nexport interface InternalTranslationResource<TId extends string = string>\n extends TranslationResource<TId> {\n version: 'v1';\n resources: Array<{\n language: string;\n loader: InternalTranslationResourceLoader;\n }>;\n}\n\n/** @internal */\nexport function toInternalTranslationResource<TId extends string>(\n resource: TranslationResource<TId>,\n): InternalTranslationResource<TId> {\n const r = resource as InternalTranslationResource<TId>;\n if (r.$$type !== '@backstage/TranslationResource') {\n throw new Error(`Invalid translation resource, bad type '${r.$$type}'`);\n }\n if (r.version !== 'v1') {\n throw new Error(`Invalid translation resource, bad version '${r.version}'`);\n }\n\n return r;\n}\n\n/** @alpha */\nexport interface TranslationResourceOptions<\n TId extends string,\n TMessages extends { [key in string]: string },\n TTranslations extends {\n [language in string]: () => Promise<{\n default:\n | TranslationMessages<TId>\n | { [key in keyof TMessages]: string | null };\n }>;\n },\n> {\n ref: TranslationRef<TId, TMessages>;\n\n translations: TTranslations;\n}\n\n/** @alpha */\nexport function createTranslationResource<\n TId extends string,\n TMessages extends { [key in string]: string },\n TTranslations extends {\n [language in string]: () => Promise<{\n default:\n | TranslationMessages<TId>\n | { [key in keyof TMessages]: string | null };\n }>;\n },\n>(\n options: TranslationResourceOptions<TId, TMessages, TTranslations>,\n): TranslationResource<TId> {\n return {\n $$type: '@backstage/TranslationResource',\n version: 'v1',\n id: options.ref.id,\n resources: Object.entries(options.translations).map(\n ([language, loader]) => ({\n language,\n loader: () =>\n loader().then(m => {\n const value = m.default;\n return {\n messages:\n value?.$$type === '@backstage/TranslationMessages'\n ? value.messages\n : value,\n };\n }),\n }),\n ),\n } as InternalTranslationResource<TId>;\n}\n"],"names":[],"mappings":"AA2CO,SAAS,8BACd,QAAA,EACkC;AAClC,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,gCAAA,EAAkC;AACjD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wCAAA,EAA2C,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACxE;AACA,EAAA,IAAI,CAAA,CAAE,YAAY,IAAA,EAAM;AACtB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,2CAAA,EAA8C,CAAA,CAAE,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EAC5E;AAEA,EAAA,OAAO,CAAA;AACT;;;;"}
|
|
File without changes
|
/package/dist/{core-plugin-api → frontend-plugin-api}/src/translation/TranslationResource.esm.js
RENAMED
|
File without changes
|