@backstage/core-plugin-api 1.9.2 → 1.9.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.esm.js +6 -194
  4. package/dist/alpha.esm.js.map +1 -1
  5. package/dist/analytics/AnalyticsContext.esm.js +32 -0
  6. package/dist/analytics/AnalyticsContext.esm.js.map +1 -0
  7. package/dist/analytics/Tracker.esm.js +94 -0
  8. package/dist/analytics/Tracker.esm.js.map +1 -0
  9. package/dist/analytics/useAnalytics.esm.js +42 -0
  10. package/dist/analytics/useAnalytics.esm.js.map +1 -0
  11. package/dist/apis/definitions/AlertApi.esm.js +11 -0
  12. package/dist/apis/definitions/AlertApi.esm.js.map +1 -0
  13. package/dist/apis/definitions/AnalyticsApi.esm.js +11 -0
  14. package/dist/apis/definitions/AnalyticsApi.esm.js.map +1 -0
  15. package/dist/apis/definitions/AppLanguageApi.esm.js +8 -0
  16. package/dist/apis/definitions/AppLanguageApi.esm.js.map +1 -0
  17. package/dist/apis/definitions/AppThemeApi.esm.js +11 -0
  18. package/dist/apis/definitions/AppThemeApi.esm.js.map +1 -0
  19. package/dist/apis/definitions/ConfigApi.esm.js +11 -0
  20. package/dist/apis/definitions/ConfigApi.esm.js.map +1 -0
  21. package/dist/apis/definitions/DiscoveryApi.esm.js +11 -0
  22. package/dist/apis/definitions/DiscoveryApi.esm.js.map +1 -0
  23. package/dist/apis/definitions/ErrorApi.esm.js +11 -0
  24. package/dist/apis/definitions/ErrorApi.esm.js.map +1 -0
  25. package/dist/apis/definitions/FeatureFlagsApi.esm.js +16 -0
  26. package/dist/apis/definitions/FeatureFlagsApi.esm.js.map +1 -0
  27. package/dist/apis/definitions/FetchApi.esm.js +11 -0
  28. package/dist/apis/definitions/FetchApi.esm.js.map +1 -0
  29. package/dist/apis/definitions/IdentityApi.esm.js +11 -0
  30. package/dist/apis/definitions/IdentityApi.esm.js.map +1 -0
  31. package/dist/apis/definitions/OAuthRequestApi.esm.js +11 -0
  32. package/dist/apis/definitions/OAuthRequestApi.esm.js.map +1 -0
  33. package/dist/apis/definitions/StorageApi.esm.js +11 -0
  34. package/dist/apis/definitions/StorageApi.esm.js.map +1 -0
  35. package/dist/apis/definitions/TranslationApi.esm.js +8 -0
  36. package/dist/apis/definitions/TranslationApi.esm.js.map +1 -0
  37. package/dist/apis/definitions/auth.esm.js +43 -0
  38. package/dist/apis/definitions/auth.esm.js.map +1 -0
  39. package/dist/apis/system/ApiRef.esm.js +27 -0
  40. package/dist/apis/system/ApiRef.esm.js.map +1 -0
  41. package/dist/apis/system/helpers.esm.js +13 -0
  42. package/dist/apis/system/helpers.esm.js.map +1 -0
  43. package/dist/apis/system/useApi.esm.js +50 -0
  44. package/dist/apis/system/useApi.esm.js.map +1 -0
  45. package/dist/app/useApp.esm.js +18 -0
  46. package/dist/app/useApp.esm.js.map +1 -0
  47. package/dist/extensions/PluginErrorBoundary.esm.js +30 -0
  48. package/dist/extensions/PluginErrorBoundary.esm.js.map +1 -0
  49. package/dist/extensions/componentData.esm.js +45 -0
  50. package/dist/extensions/componentData.esm.js.map +1 -0
  51. package/dist/extensions/extensions.esm.js +109 -0
  52. package/dist/extensions/extensions.esm.js.map +1 -0
  53. package/dist/extensions/useElementFilter.esm.js +90 -0
  54. package/dist/extensions/useElementFilter.esm.js.map +1 -0
  55. package/dist/index.d.ts +7 -0
  56. package/dist/index.esm.js +27 -595
  57. package/dist/index.esm.js.map +1 -1
  58. package/dist/plugin/Plugin.esm.js +32 -0
  59. package/dist/plugin/Plugin.esm.js.map +1 -0
  60. package/dist/routing/ExternalRouteRef.esm.js +28 -0
  61. package/dist/routing/ExternalRouteRef.esm.js.map +1 -0
  62. package/dist/routing/RouteRef.esm.js +24 -0
  63. package/dist/routing/RouteRef.esm.js.map +1 -0
  64. package/dist/routing/SubRouteRef.esm.js +46 -0
  65. package/dist/routing/SubRouteRef.esm.js.map +1 -0
  66. package/dist/routing/types.esm.js +9 -0
  67. package/dist/routing/types.esm.js.map +1 -0
  68. package/dist/routing/useRouteRef.esm.js +32 -0
  69. package/dist/routing/useRouteRef.esm.js.map +1 -0
  70. package/dist/routing/useRouteRefParams.esm.js +8 -0
  71. package/dist/routing/useRouteRefParams.esm.js.map +1 -0
  72. package/dist/translation/TranslationMessages.esm.js +11 -0
  73. package/dist/translation/TranslationMessages.esm.js.map +1 -0
  74. package/dist/translation/TranslationRef.esm.js +62 -0
  75. package/dist/translation/TranslationRef.esm.js.map +1 -0
  76. package/dist/translation/TranslationResource.esm.js +21 -0
  77. package/dist/translation/TranslationResource.esm.js.map +1 -0
  78. package/dist/translation/useTranslationRef.esm.js +84 -0
  79. package/dist/translation/useTranslationRef.esm.js.map +1 -0
  80. package/package.json +4 -4
  81. package/dist/esm/StorageApi-DgJQFdRp.esm.js +0 -160
  82. package/dist/esm/StorageApi-DgJQFdRp.esm.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @backstage/core-plugin-api
2
2
 
3
+ ## 1.9.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 35fbe09: A new `defaultTarget` option has been added to `createExternalRouteRef`. This allows one to specify a default target of the route by name, for example `'catalog.catalogIndex'`, which will be used if the target route is present in the app and there is no explicit route binding.
8
+ - Updated dependencies
9
+ - @backstage/config@1.2.0
10
+ - @backstage/errors@1.2.4
11
+ - @backstage/types@1.1.1
12
+ - @backstage/version-bridge@1.0.8
13
+
14
+ ## 1.9.3-next.0
15
+
16
+ ### Patch Changes
17
+
18
+ - 35fbe09: Added a new `defaultTarget` option to `createExternalRouteRef`. I lets you specify a default target of the route by name, for example `'catalog.catalogIndex'`, which will be used if the target route is present in the app and there is no explicit route binding.
19
+ - Updated dependencies
20
+ - @backstage/config@1.2.0
21
+ - @backstage/errors@1.2.4
22
+ - @backstage/types@1.1.1
23
+ - @backstage/version-bridge@1.0.8
24
+
3
25
  ## 1.9.2
4
26
 
5
27
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/core-plugin-api",
3
- "version": "1.9.2",
3
+ "version": "1.9.3",
4
4
  "main": "../dist/alpha.esm.js",
5
5
  "module": "../dist/alpha.esm.js",
6
6
  "types": "../dist/alpha.d.ts"
package/dist/alpha.esm.js CHANGED
@@ -1,195 +1,7 @@
1
- import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
2
- import { u as useApi, r as errorApiRef } from './esm/StorageApi-DgJQFdRp.esm.js';
3
- import { createApiRef } from '@backstage/core-plugin-api';
4
- import '@backstage/version-bridge';
5
- import '@backstage/errors';
6
-
7
- function createTranslationMessages(options) {
8
- return {
9
- $$type: "@backstage/TranslationMessages",
10
- id: options.ref.id,
11
- full: Boolean(options.full),
12
- messages: options.messages
13
- };
14
- }
15
-
16
- function createTranslationResource(options) {
17
- return {
18
- $$type: "@backstage/TranslationResource",
19
- version: "v1",
20
- id: options.ref.id,
21
- resources: Object.entries(options.translations).map(
22
- ([language, loader]) => ({
23
- language,
24
- loader: () => loader().then((m) => {
25
- const value = m.default;
26
- return {
27
- messages: (value == null ? void 0 : value.$$type) === "@backstage/TranslationMessages" ? value.messages : value
28
- };
29
- })
30
- })
31
- )
32
- };
33
- }
34
-
35
- var __defProp = Object.defineProperty;
36
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
37
- var __publicField = (obj, key, value) => {
38
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
39
- return value;
40
- };
41
- var __accessCheck = (obj, member, msg) => {
42
- if (!member.has(obj))
43
- throw TypeError("Cannot " + msg);
44
- };
45
- var __privateGet = (obj, member, getter) => {
46
- __accessCheck(obj, member, "read from private field");
47
- return getter ? getter.call(obj) : member.get(obj);
48
- };
49
- var __privateAdd = (obj, member, value) => {
50
- if (member.has(obj))
51
- throw TypeError("Cannot add the same private member more than once");
52
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
53
- };
54
- var __privateSet = (obj, member, value, setter) => {
55
- __accessCheck(obj, member, "write to private field");
56
- setter ? setter.call(obj, value) : member.set(obj, value);
57
- return value;
58
- };
59
- var _id, _messages, _resources;
60
- function flattenMessages(nested) {
61
- const entries = new Array();
62
- function visit(obj, prefix) {
63
- for (const [key, value] of Object.entries(obj)) {
64
- if (typeof value === "string") {
65
- entries.push([prefix + key, value]);
66
- } else {
67
- visit(value, `${prefix}${key}.`);
68
- }
69
- }
70
- }
71
- visit(nested, "");
72
- return Object.fromEntries(entries);
73
- }
74
- class TranslationRefImpl {
75
- constructor(options) {
76
- __privateAdd(this, _id, void 0);
77
- __privateAdd(this, _messages, void 0);
78
- __privateAdd(this, _resources, void 0);
79
- __publicField(this, "$$type", "@backstage/TranslationRef");
80
- __publicField(this, "version", "v1");
81
- __privateSet(this, _id, options.id);
82
- __privateSet(this, _messages, flattenMessages(
83
- options.messages
84
- ));
85
- }
86
- get id() {
87
- return __privateGet(this, _id);
88
- }
89
- get T() {
90
- throw new Error("Not implemented");
91
- }
92
- getDefaultMessages() {
93
- return __privateGet(this, _messages);
94
- }
95
- setDefaultResource(resources) {
96
- __privateSet(this, _resources, resources);
97
- }
98
- getDefaultResource() {
99
- return __privateGet(this, _resources);
100
- }
101
- toString() {
102
- return `TranslationRef{id=${this.id}}`;
103
- }
104
- }
105
- _id = new WeakMap();
106
- _messages = new WeakMap();
107
- _resources = new WeakMap();
108
- function createTranslationRef(config) {
109
- const ref = new TranslationRefImpl(config);
110
- if (config.translations) {
111
- ref.setDefaultResource(
112
- createTranslationResource({
113
- ref,
114
- translations: config.translations
115
- })
116
- );
117
- }
118
- return ref;
119
- }
120
-
121
- const translationApiRef = createApiRef({
122
- id: "core.translation"
123
- });
124
-
125
- const appLanguageApiRef = createApiRef({
126
- id: "core.applanguage"
127
- });
128
-
129
- const loggedRefs = /* @__PURE__ */ new WeakSet();
130
- const useTranslationRef = (translationRef) => {
131
- const errorApi = useApi(errorApiRef);
132
- const translationApi = useApi(translationApiRef);
133
- const [snapshot, setSnapshot] = useState(
134
- () => translationApi.getTranslation(translationRef)
135
- );
136
- const observable = useMemo(
137
- () => translationApi.translation$(translationRef),
138
- [translationApi, translationRef]
139
- );
140
- const onError = useCallback(
141
- (error) => {
142
- if (!loggedRefs.has(translationRef)) {
143
- const errMsg = `Failed to load translation resource '${translationRef.id}'; caused by ${error}`;
144
- console.error(errMsg);
145
- errorApi.post(new Error(errMsg));
146
- loggedRefs.add(translationRef);
147
- }
148
- },
149
- [errorApi, translationRef]
150
- );
151
- useEffect(() => {
152
- const subscription = observable.subscribe({
153
- next(next) {
154
- if (next.ready) {
155
- setSnapshot(next);
156
- }
157
- },
158
- error(error) {
159
- onError(error);
160
- }
161
- });
162
- return () => {
163
- subscription.unsubscribe();
164
- };
165
- }, [observable, onError]);
166
- const initialRenderRef = useRef(true);
167
- useEffect(() => {
168
- if (initialRenderRef.current) {
169
- initialRenderRef.current = false;
170
- } else {
171
- setSnapshot(translationApi.getTranslation(translationRef));
172
- }
173
- }, [translationApi, translationRef]);
174
- if (!snapshot.ready) {
175
- throw new Promise((resolve) => {
176
- const subscription = observable.subscribe({
177
- next(next) {
178
- if (next.ready) {
179
- subscription.unsubscribe();
180
- resolve();
181
- }
182
- },
183
- error(error) {
184
- subscription.unsubscribe();
185
- onError(error);
186
- resolve();
187
- }
188
- });
189
- });
190
- }
191
- return { t: snapshot.t };
192
- };
193
-
194
- export { appLanguageApiRef, createTranslationMessages, createTranslationRef, createTranslationResource, translationApiRef, useTranslationRef };
1
+ export { createTranslationMessages } from './translation/TranslationMessages.esm.js';
2
+ export { createTranslationResource } from './translation/TranslationResource.esm.js';
3
+ export { createTranslationRef } from './translation/TranslationRef.esm.js';
4
+ export { useTranslationRef } from './translation/useTranslationRef.esm.js';
5
+ export { translationApiRef } from './apis/definitions/TranslationApi.esm.js';
6
+ export { appLanguageApiRef } from './apis/definitions/AppLanguageApi.esm.js';
195
7
  //# sourceMappingURL=alpha.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.esm.js","sources":["../src/translation/TranslationMessages.ts","../src/translation/TranslationResource.ts","../src/translation/TranslationRef.ts","../src/apis/definitions/TranslationApi.ts","../src/apis/definitions/AppLanguageApi.ts","../src/translation/useTranslationRef.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 { TranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/**\n * Represents a collection of messages to be provided for a given translation ref.\n *\n * @alpha\n * @remarks\n *\n * This collection of messages can either be used directly as an override for the\n * default messages, or it can be used to provide translations for a language by\n * by being referenced by a {@link TranslationResource}.\n */\nexport interface TranslationMessages<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n TFull extends boolean = boolean,\n> {\n $$type: '@backstage/TranslationMessages';\n /** The ID of the translation ref that these messages are for */\n id: TId;\n /** Whether or not these messages override all known messages */\n full: TFull;\n /** The messages provided for the given translation ref */\n messages: TMessages;\n}\n\n/**\n * Options for {@link createTranslationMessages}.\n *\n * @alpha\n */\nexport interface TranslationMessagesOptions<\n TId extends string,\n TMessages extends { [key in string]: string },\n TFull extends boolean,\n> {\n ref: TranslationRef<TId, TMessages>;\n\n full?: TFull;\n\n messages: false extends TFull\n ? { [key in keyof TMessages]?: string | null }\n : { [key in keyof TMessages]: string | null };\n}\n\n/**\n * Creates a collection of messages for a given translation ref.\n *\n * @alpha\n */\nexport function createTranslationMessages<\n TId extends string,\n TMessages extends { [key in string]: string },\n TFull extends boolean,\n>(\n options: TranslationMessagesOptions<TId, TMessages, TFull>,\n): TranslationMessages<TId, TMessages, TFull> {\n return {\n $$type: '@backstage/TranslationMessages',\n id: options.ref.id,\n full: Boolean(options.full) as TFull,\n messages: options.messages as TMessages,\n };\n}\n","/*\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","/*\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","/*\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 { ApiRef, createApiRef } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { TranslationRef } from '../../translation';\n\n/**\n * Base translation options.\n *\n * @alpha\n */\ninterface BaseOptions {\n interpolation?: {\n /** Whether to HTML escape provided values, defaults to false */\n escapeValue?: boolean;\n };\n}\n\n/**\n * All pluralization suffixes supported by i18next\n *\n * @ignore\n */\ntype TranslationPlural = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';\n\n/**\n * A mapping of i18n formatting types to their corresponding types and options.\n * @ignore\n */\ntype I18nextFormatMap = {\n number: {\n type: number;\n options: Intl.NumberFormatOptions;\n };\n currency: {\n type: number;\n options: Intl.NumberFormatOptions;\n };\n datetime: {\n type: Date;\n options: Intl.DateTimeFormatOptions;\n };\n relativetime: {\n type: number;\n options: {\n range?: Intl.RelativeTimeFormatUnit;\n } & Intl.RelativeTimeFormatOptions;\n };\n list: {\n type: string[];\n options: Intl.ListFormatOptions;\n };\n};\n\n/**\n * Extracts all pluralized keys from the message map.\n *\n * @example\n * ```\n * { foo: 'foo', bar_one: 'bar', bar_other: 'bars' } -> 'bar'\n * ```\n *\n * @ignore\n */\ntype PluralKeys<TMessages extends { [key in string]: string }> = {\n [Key in keyof TMessages]: Key extends `${infer K}_${TranslationPlural}`\n ? K\n : never;\n}[keyof TMessages];\n\n/**\n * Collapses a message map into normalized keys with union values.\n *\n * @example\n * ```\n * { foo_one: 'foo', foo_other: 'foos' } -> { foo: 'foo' | 'foos' }\n * ```\n *\n * @ignore\n */\ntype CollapsedMessages<TMessages extends { [key in string]: string }> = {\n [key in keyof TMessages as key extends `${infer K}_${TranslationPlural}`\n ? K\n : key]: TMessages[key];\n};\n\n/**\n * Helper type that expands type hints\n *\n * @ignore\n */\ntype Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;\n\n/**\n * Helper type that expands type hints recursively\n *\n * @ignore\n */\ntype ExpandRecursive<T> = T extends infer O\n ? { [K in keyof O]: ExpandRecursive<O[K]> }\n : never;\n\n/**\n * Trim away whitespace\n *\n * @ignore\n */\ntype Trim<T> = T extends ` ${infer U}`\n ? Trim<U>\n : T extends `${infer U} `\n ? Trim<U>\n : T;\n\n/**\n * Extracts the key and format from a replacement string.\n *\n * @example\n * ```\n * 'foo, number' -> { foo: number }, 'foo' -> { foo: undefined }\n * ```\n */\ntype ExtractFormat<Replacement extends string> =\n Replacement extends `${infer Key},${infer FullFormat}`\n ? {\n [key in Trim<Key>]: Lowercase<\n Trim<\n FullFormat extends `${infer Format}(${string})${string}`\n ? Format\n : FullFormat\n >\n >;\n }\n : { [key in Trim<Replacement>]: undefined };\n\n/**\n * Expand the keys in a flat map to nested objects.\n *\n * @example\n * ```\n * { 'a.b': 'foo', 'a.c': 'bar' } -> { a: { b: 'foo', c: 'bar' }\n * ```\n *\n * @ignore\n */\ntype ExpandKeys<TMap extends {}> = {\n [Key in keyof TMap as Key extends `${infer Prefix}.${string}`\n ? Prefix\n : Key]: Key extends `${string}.${infer Rest}`\n ? ExpandKeys<{ [key in Rest]: TMap[Key] }>\n : TMap[Key];\n};\n\n/**\n * Extracts all option keys and their format from a message string.\n *\n * @example\n * ```\n * 'foo {{bar}} {{baz, number}}' -> { 'bar': undefined, 'baz': 'number' }\n * ```\n *\n * @ignore\n */\ntype ReplaceFormatsFromMessage<TMessage> =\n TMessage extends `${string}{{${infer Replacement}}}${infer Tail}` // no formatting, e.g. {{foo}}\n ? ExpandKeys<ExtractFormat<Replacement>> & ReplaceFormatsFromMessage<Tail>\n : {};\n\n/**\n * Generates the replace options structure\n *\n * @ignore\n */\ntype ReplaceOptionsFromFormats<TFormats extends {}> = {\n [Key in keyof TFormats]: TFormats[Key] extends keyof I18nextFormatMap\n ? I18nextFormatMap[TFormats[Key]]['type']\n : TFormats[Key] extends {}\n ? Expand<ReplaceOptionsFromFormats<TFormats[Key]>>\n : string;\n};\n\n/**\n * Generates the formatParams options structure\n *\n * @ignore\n */\ntype ReplaceFormatParamsFromFormats<TFormats extends {}> = {\n [Key in keyof TFormats]?: TFormats[Key] extends keyof I18nextFormatMap\n ? I18nextFormatMap[TFormats[Key]]['options']\n : TFormats[Key] extends {}\n ? Expand<ReplaceFormatParamsFromFormats<TFormats[Key]>>\n : undefined;\n};\n\n/**\n * Extracts all nesting keys from a message string.\n *\n * @example\n * ```\n * 'foo $t(bar) $t(baz)' -> 'bar' | 'baz'\n * ```\n *\n * @ignore\n */\ntype NestingKeysFromMessage<TMessage extends string> =\n TMessage extends `${string}$t(${infer Key})${infer Tail}` // nesting options are not supported\n ? Trim<Key> | NestingKeysFromMessage<Tail>\n : never;\n\n/**\n * Find all referenced keys, given a starting key and the full set of messages.\n *\n * This will only discover keys up to 3 levels deep.\n *\n * @example\n * ```\n * <'x', { x: '$t(y) $t(z)', y: 'y', z: '$t(w)', w: 'w', foo: 'foo' }> -> 'x' | 'y' | 'z' | 'w'\n * ```\n *\n * @ignore\n */\ntype NestedMessageKeys<\n TKey extends keyof TMessages,\n TMessages extends { [key in string]: string },\n> =\n | TKey\n | NestedMessageKeys2<NestingKeysFromMessage<TMessages[TKey]>, TMessages>;\n// Can't recursively reference ourself, so instead we got this beauty\ntype NestedMessageKeys2<\n TKey extends keyof TMessages,\n TMessages extends { [key in string]: string },\n> =\n | TKey\n | NestedMessageKeys3<NestingKeysFromMessage<TMessages[TKey]>, TMessages>;\n// Only support 3 levels of nesting\ntype NestedMessageKeys3<\n TKey extends keyof TMessages,\n TMessages extends { [key in string]: string },\n> = TKey | NestingKeysFromMessage<TMessages[TKey]>;\n\n/**\n * Converts a union type to an intersection type.\n *\n * @example\n * ```\n * { foo: 'foo' } | { bar: 'bar' } -> { foo: 'foo' } & { bar: 'bar' }\n * ```\n *\n * @ignore\n */\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\n/**\n * Collects different types of options into a single object\n *\n * @ignore\n */\ntype CollectOptions<\n TCount extends { count?: number },\n TFormats extends {},\n> = TCount &\n // count is special, omit it from the replacements\n (keyof Omit<TFormats, 'count'> extends never\n ? {}\n : (\n | Expand<Omit<ReplaceOptionsFromFormats<TFormats>, 'count'>>\n | {\n replace: Expand<Omit<ReplaceOptionsFromFormats<TFormats>, 'count'>>;\n }\n ) & {\n formatParams?: Expand<ReplaceFormatParamsFromFormats<TFormats>>;\n });\n\n/**\n * Helper type to only require options argument if needed\n *\n * @ignore\n */\ntype OptionArgs<TOptions extends {}> = keyof TOptions extends never\n ? [options?: BaseOptions]\n : [options: BaseOptions & TOptions];\n\n/**\n * @ignore\n */\ntype TranslationFunctionOptions<\n TKeys extends keyof TMessages, // All normalized message keys to be considered, i.e. included nested ones\n TPluralKeys extends keyof TMessages, // All keys in the message map that are pluralized\n TMessages extends { [key in string]: string }, // Collapsed message map with normalized keys and union values\n> = OptionArgs<\n Expand<\n CollectOptions<\n TKeys & TPluralKeys extends never ? {} : { count: number },\n ExpandRecursive<\n UnionToIntersection<ReplaceFormatsFromMessage<TMessages[TKeys]>>\n >\n >\n >\n>;\n\n/** @alpha */\nexport interface TranslationFunction<\n TMessages extends { [key in string]: string },\n> {\n <TKey extends keyof CollapsedMessages<TMessages>>(\n key: TKey,\n ...[args]: TranslationFunctionOptions<\n NestedMessageKeys<TKey, CollapsedMessages<TMessages>>,\n PluralKeys<TMessages>,\n CollapsedMessages<TMessages>\n >\n ): CollapsedMessages<TMessages>[TKey];\n}\n\n/** @alpha */\nexport type TranslationSnapshot<TMessages extends { [key in string]: string }> =\n { ready: false } | { ready: true; t: TranslationFunction<TMessages> };\n\n/** @alpha */\nexport type TranslationApi = {\n getTranslation<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages>;\n\n translation$<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): Observable<TranslationSnapshot<TMessages>>;\n};\n\n/**\n * @alpha\n */\nexport const translationApiRef: ApiRef<TranslationApi> = createApiRef({\n id: 'core.translation',\n});\n","/*\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 { ApiRef, createApiRef } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\n\n/** @alpha */\nexport type AppLanguageApi = {\n getAvailableLanguages(): { languages: string[] };\n\n setLanguage(language?: string): void;\n\n getLanguage(): { language: string };\n\n language$(): Observable<{ language: string }>;\n};\n\n/**\n * @alpha\n */\nexport const appLanguageApiRef: ApiRef<AppLanguageApi> = createApiRef({\n id: 'core.applanguage',\n});\n","/*\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 { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { errorApiRef, useApi } from '../apis';\nimport {\n translationApiRef,\n TranslationFunction,\n TranslationSnapshot,\n} from '../apis/alpha';\nimport { TranslationRef } from './TranslationRef';\n\n// Make sure we don't fill the logs with loading errors for the same ref\nconst loggedRefs = new WeakSet<TranslationRef<string, {}>>();\n\n/** @alpha */\nexport const useTranslationRef = <\n TMessages extends { [key in string]: string },\n>(\n translationRef: TranslationRef<string, TMessages>,\n): { t: TranslationFunction<TMessages> } => {\n const errorApi = useApi(errorApiRef);\n const translationApi = useApi(translationApiRef);\n\n const [snapshot, setSnapshot] = useState<TranslationSnapshot<TMessages>>(() =>\n translationApi.getTranslation(translationRef),\n );\n const observable = useMemo(\n () => translationApi.translation$(translationRef),\n [translationApi, translationRef],\n );\n\n const onError = useCallback(\n (error: Error) => {\n if (!loggedRefs.has(translationRef)) {\n const errMsg = `Failed to load translation resource '${translationRef.id}'; caused by ${error}`;\n // eslint-disable-next-line no-console\n console.error(errMsg);\n errorApi.post(new Error(errMsg));\n loggedRefs.add(translationRef);\n }\n },\n [errorApi, translationRef],\n );\n\n useEffect(() => {\n const subscription = observable.subscribe({\n next(next) {\n if (next.ready) {\n setSnapshot(next);\n }\n },\n error(error) {\n onError(error);\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [observable, onError]);\n\n // Keep track of if the provided translation ref changes, and in that case update the snapshot\n const initialRenderRef = useRef(true);\n useEffect(() => {\n if (initialRenderRef.current) {\n initialRenderRef.current = false;\n } else {\n setSnapshot(translationApi.getTranslation(translationRef));\n }\n }, [translationApi, translationRef]);\n\n if (!snapshot.ready) {\n throw new Promise<void>(resolve => {\n const subscription = observable.subscribe({\n next(next) {\n if (next.ready) {\n subscription.unsubscribe();\n resolve();\n }\n },\n error(error) {\n subscription.unsubscribe();\n onError(error);\n resolve();\n },\n });\n });\n }\n\n return { t: snapshot.t };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"alpha.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
@@ -0,0 +1,32 @@
1
+ import { createVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
2
+ import React, { useContext } from 'react';
3
+
4
+ const AnalyticsReactContext = createVersionedContext("analytics-context");
5
+ const useAnalyticsContext = () => {
6
+ const theContext = useContext(AnalyticsReactContext);
7
+ if (theContext === void 0) {
8
+ return {
9
+ routeRef: "unknown",
10
+ pluginId: "root",
11
+ extension: "App"
12
+ };
13
+ }
14
+ const theValue = theContext.atVersion(1);
15
+ if (theValue === void 0) {
16
+ throw new Error("No context found for version 1.");
17
+ }
18
+ return theValue;
19
+ };
20
+ const AnalyticsContext = (options) => {
21
+ const { attributes, children } = options;
22
+ const parentValues = useAnalyticsContext();
23
+ const combinedValue = {
24
+ ...parentValues,
25
+ ...attributes
26
+ };
27
+ const versionedCombinedValue = createVersionedValueMap({ 1: combinedValue });
28
+ return /* @__PURE__ */ React.createElement(AnalyticsReactContext.Provider, { value: versionedCombinedValue }, children);
29
+ };
30
+
31
+ export { AnalyticsContext, useAnalyticsContext };
32
+ //# sourceMappingURL=AnalyticsContext.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsContext.esm.js","sources":["../../src/analytics/AnalyticsContext.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 createVersionedContext,\n createVersionedValueMap,\n} from '@backstage/version-bridge';\nimport React, { ReactNode, useContext } from 'react';\nimport { AnalyticsContextValue } from './types';\n\nconst AnalyticsReactContext = createVersionedContext<{\n 1: AnalyticsContextValue;\n}>('analytics-context');\n\n/**\n * A \"private\" (to this package) hook that enables context inheritance and a\n * way to read Analytics Context values at event capture-time.\n *\n * @internal\n */\nexport const useAnalyticsContext = (): AnalyticsContextValue => {\n const theContext = useContext(AnalyticsReactContext);\n\n // Provide a default value if no value exists.\n if (theContext === undefined) {\n return {\n routeRef: 'unknown',\n pluginId: 'root',\n extension: 'App',\n };\n }\n\n // This should probably never happen, but check for it.\n const theValue = theContext.atVersion(1);\n if (theValue === undefined) {\n throw new Error('No context found for version 1.');\n }\n\n return theValue;\n};\n\n/**\n * Provides components in the child react tree an Analytics Context, ensuring\n * all analytics events captured within the context have relevant attributes.\n *\n * @remarks\n *\n * Analytics contexts are additive, meaning the context ultimately emitted with\n * an event is the combination of all contexts in the parent tree.\n *\n * @public\n */\nexport const AnalyticsContext = (options: {\n attributes: Partial<AnalyticsContextValue>;\n children: ReactNode;\n}) => {\n const { attributes, children } = options;\n\n const parentValues = useAnalyticsContext();\n const combinedValue = {\n ...parentValues,\n ...attributes,\n };\n\n const versionedCombinedValue = createVersionedValueMap({ 1: combinedValue });\n return (\n <AnalyticsReactContext.Provider value={versionedCombinedValue}>\n {children}\n </AnalyticsReactContext.Provider>\n );\n};\n\n/**\n * Returns an HOC wrapping the provided component in an Analytics context with\n * the given values.\n *\n * @param Component - Component to be wrapped with analytics context attributes\n * @param values - Analytics context key/value pairs.\n * @internal\n */\nexport function withAnalyticsContext<TProps extends {}>(\n Component: React.ComponentType<TProps>,\n values: AnalyticsContextValue,\n) {\n const ComponentWithAnalyticsContext = (props: TProps) => {\n return (\n <AnalyticsContext attributes={values}>\n <Component {...props} />\n </AnalyticsContext>\n );\n };\n ComponentWithAnalyticsContext.displayName = `WithAnalyticsContext(${\n Component.displayName || Component.name || 'Component'\n })`;\n return ComponentWithAnalyticsContext;\n}\n"],"names":[],"mappings":";;;AAuBA,MAAM,qBAAA,GAAwB,uBAE3B,mBAAmB,CAAA,CAAA;AAQf,MAAM,sBAAsB,MAA6B;AAC9D,EAAM,MAAA,UAAA,GAAa,WAAW,qBAAqB,CAAA,CAAA;AAGnD,EAAA,IAAI,eAAe,KAAW,CAAA,EAAA;AAC5B,IAAO,OAAA;AAAA,MACL,QAAU,EAAA,SAAA;AAAA,MACV,QAAU,EAAA,MAAA;AAAA,MACV,SAAW,EAAA,KAAA;AAAA,KACb,CAAA;AAAA,GACF;AAGA,EAAM,MAAA,QAAA,GAAW,UAAW,CAAA,SAAA,CAAU,CAAC,CAAA,CAAA;AACvC,EAAA,IAAI,aAAa,KAAW,CAAA,EAAA;AAC1B,IAAM,MAAA,IAAI,MAAM,iCAAiC,CAAA,CAAA;AAAA,GACnD;AAEA,EAAO,OAAA,QAAA,CAAA;AACT,EAAA;AAaa,MAAA,gBAAA,GAAmB,CAAC,OAG3B,KAAA;AACJ,EAAM,MAAA,EAAE,UAAY,EAAA,QAAA,EAAa,GAAA,OAAA,CAAA;AAEjC,EAAA,MAAM,eAAe,mBAAoB,EAAA,CAAA;AACzC,EAAA,MAAM,aAAgB,GAAA;AAAA,IACpB,GAAG,YAAA;AAAA,IACH,GAAG,UAAA;AAAA,GACL,CAAA;AAEA,EAAA,MAAM,sBAAyB,GAAA,uBAAA,CAAwB,EAAE,CAAA,EAAG,eAAe,CAAA,CAAA;AAC3E,EAAA,2CACG,qBAAsB,CAAA,QAAA,EAAtB,EAA+B,KAAA,EAAO,0BACpC,QACH,CAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,94 @@
1
+ import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
2
+
3
+ const globalEvents = getOrCreateGlobalSingleton(
4
+ "core-plugin-api:analytics-tracker-events",
5
+ () => ({
6
+ mostRecentGatheredNavigation: void 0,
7
+ mostRecentRoutableExtensionRender: void 0,
8
+ beforeUnloadRegistered: false
9
+ })
10
+ );
11
+ const routableExtensionRenderedEvent = "_ROUTABLE-EXTENSION-RENDERED";
12
+ class Tracker {
13
+ constructor(analyticsApi, context = {
14
+ routeRef: "unknown",
15
+ pluginId: "root",
16
+ extension: "App"
17
+ }) {
18
+ this.analyticsApi = analyticsApi;
19
+ this.context = context;
20
+ if (!globalEvents.beforeUnloadRegistered) {
21
+ addEventListener(
22
+ "beforeunload",
23
+ () => {
24
+ if (globalEvents.mostRecentGatheredNavigation) {
25
+ this.analyticsApi.captureEvent({
26
+ ...globalEvents.mostRecentGatheredNavigation,
27
+ ...globalEvents.mostRecentRoutableExtensionRender
28
+ });
29
+ globalEvents.mostRecentGatheredNavigation = void 0;
30
+ globalEvents.mostRecentRoutableExtensionRender = void 0;
31
+ }
32
+ },
33
+ { once: true, passive: true }
34
+ );
35
+ globalEvents.beforeUnloadRegistered = true;
36
+ }
37
+ }
38
+ setContext(context) {
39
+ this.context = context;
40
+ }
41
+ captureEvent(action, subject, {
42
+ value,
43
+ attributes
44
+ } = {}) {
45
+ const { _routeNodeType, ...context } = this.context;
46
+ if (action === routableExtensionRenderedEvent) {
47
+ if (globalEvents.mostRecentGatheredNavigation) {
48
+ globalEvents.mostRecentRoutableExtensionRender = {
49
+ context: {
50
+ ...context,
51
+ extension: "App"
52
+ }
53
+ };
54
+ }
55
+ return;
56
+ }
57
+ if (globalEvents.mostRecentGatheredNavigation) {
58
+ try {
59
+ this.analyticsApi.captureEvent({
60
+ ...globalEvents.mostRecentGatheredNavigation,
61
+ ...globalEvents.mostRecentRoutableExtensionRender
62
+ });
63
+ } catch (e) {
64
+ console.warn("Error during analytics event capture. %o", e);
65
+ }
66
+ globalEvents.mostRecentGatheredNavigation = void 0;
67
+ globalEvents.mostRecentRoutableExtensionRender = void 0;
68
+ }
69
+ if (action === "navigate" && _routeNodeType === "gathered" && context.pluginId === "root") {
70
+ globalEvents.mostRecentGatheredNavigation = {
71
+ action,
72
+ subject,
73
+ value,
74
+ attributes,
75
+ context
76
+ };
77
+ return;
78
+ }
79
+ try {
80
+ this.analyticsApi.captureEvent({
81
+ action,
82
+ subject,
83
+ value,
84
+ attributes,
85
+ context
86
+ });
87
+ } catch (e) {
88
+ console.warn("Error during analytics event capture. %o", e);
89
+ }
90
+ }
91
+ }
92
+
93
+ export { Tracker, routableExtensionRenderedEvent };
94
+ //# sourceMappingURL=Tracker.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tracker.esm.js","sources":["../../src/analytics/Tracker.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\nimport {\n AnalyticsApi,\n AnalyticsEventAttributes,\n AnalyticsTracker,\n} from '../apis';\nimport { AnalyticsContextValue } from './';\n\ntype TempGlobalEvents = {\n /**\n * Stores the most recent \"gathered\" mountpoint navigation.\n */\n mostRecentGatheredNavigation?: {\n action: string;\n subject: string;\n value?: number;\n attributes?: AnalyticsEventAttributes;\n context: AnalyticsContextValue;\n };\n /**\n * Stores the most recent routable extension render.\n */\n mostRecentRoutableExtensionRender?: {\n context: AnalyticsContextValue;\n };\n /**\n * Tracks whether or not a beforeunload event listener has already been\n * registered.\n */\n beforeUnloadRegistered: boolean;\n};\n\n/**\n * Temporary global store for select event data. Used to make `navigate` events\n * more accurate when gathered mountpoints are used.\n */\nconst globalEvents = getOrCreateGlobalSingleton<TempGlobalEvents>(\n 'core-plugin-api:analytics-tracker-events',\n () => ({\n mostRecentGatheredNavigation: undefined,\n mostRecentRoutableExtensionRender: undefined,\n beforeUnloadRegistered: false,\n }),\n);\n\n/**\n * Internal-only event representing when a routable extension is rendered.\n */\nexport const routableExtensionRenderedEvent = '_ROUTABLE-EXTENSION-RENDERED';\n\nexport class Tracker implements AnalyticsTracker {\n constructor(\n private readonly analyticsApi: AnalyticsApi,\n private context: AnalyticsContextValue = {\n routeRef: 'unknown',\n pluginId: 'root',\n extension: 'App',\n },\n ) {\n // Only register a single beforeunload event across all trackers.\n if (!globalEvents.beforeUnloadRegistered) {\n // Before the page unloads, attempt to capture any deferred navigation\n // events that haven't yet been captured.\n addEventListener(\n 'beforeunload',\n () => {\n if (globalEvents.mostRecentGatheredNavigation) {\n this.analyticsApi.captureEvent({\n ...globalEvents.mostRecentGatheredNavigation,\n ...globalEvents.mostRecentRoutableExtensionRender,\n });\n globalEvents.mostRecentGatheredNavigation = undefined;\n globalEvents.mostRecentRoutableExtensionRender = undefined;\n }\n },\n { once: true, passive: true },\n );\n\n // Prevent duplicate handlers from being registered.\n globalEvents.beforeUnloadRegistered = true;\n }\n }\n\n setContext(context: AnalyticsContextValue) {\n this.context = context;\n }\n\n captureEvent(\n action: string,\n subject: string,\n {\n value,\n attributes,\n }: { value?: number; attributes?: AnalyticsEventAttributes } = {},\n ) {\n // Never pass internal \"_routeNodeType\" context value.\n const { _routeNodeType, ...context } = this.context;\n\n // Never fire the special \"_routable-extension-rendered\" internal event.\n if (action === routableExtensionRenderedEvent) {\n // But keep track of it if we're delaying a `navigate` event for a\n // a gathered route node type.\n if (globalEvents.mostRecentGatheredNavigation) {\n globalEvents.mostRecentRoutableExtensionRender = {\n context: {\n ...context,\n extension: 'App',\n },\n };\n }\n return;\n }\n\n // If we are about to fire a real event, and we have an un-fired gathered\n // mountpoint navigation on the global store, we need to fire the navigate\n // event first, so this real event happens accurately after the navigation.\n if (globalEvents.mostRecentGatheredNavigation) {\n try {\n this.analyticsApi.captureEvent({\n ...globalEvents.mostRecentGatheredNavigation,\n ...globalEvents.mostRecentRoutableExtensionRender,\n });\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn('Error during analytics event capture. %o', e);\n }\n\n // Clear the global stores.\n globalEvents.mostRecentGatheredNavigation = undefined;\n globalEvents.mostRecentRoutableExtensionRender = undefined;\n }\n\n // Never directly fire a navigation event on a gathered route with default\n // contextual details.\n if (\n action === 'navigate' &&\n _routeNodeType === 'gathered' &&\n context.pluginId === 'root'\n ) {\n // Instead, set it on the global store.\n globalEvents.mostRecentGatheredNavigation = {\n action,\n subject,\n value,\n attributes,\n context,\n };\n return;\n }\n\n try {\n this.analyticsApi.captureEvent({\n action,\n subject,\n value,\n attributes,\n context,\n });\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn('Error during analytics event capture. %o', e);\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { useAnalyticsContext } from './AnalyticsContext.esm.js';
2
+ import { useApi } from '../apis/system/useApi.esm.js';
3
+ import '../apis/definitions/auth.esm.js';
4
+ import '../apis/definitions/AlertApi.esm.js';
5
+ import { analyticsApiRef } from '../apis/definitions/AnalyticsApi.esm.js';
6
+ import '../apis/definitions/AppThemeApi.esm.js';
7
+ import '../apis/definitions/ConfigApi.esm.js';
8
+ import '../apis/definitions/DiscoveryApi.esm.js';
9
+ import '../apis/definitions/ErrorApi.esm.js';
10
+ import '../apis/definitions/FeatureFlagsApi.esm.js';
11
+ import '../apis/definitions/FetchApi.esm.js';
12
+ import '../apis/definitions/IdentityApi.esm.js';
13
+ import '../apis/definitions/OAuthRequestApi.esm.js';
14
+ import '../apis/definitions/StorageApi.esm.js';
15
+ import { useRef } from 'react';
16
+ import { Tracker } from './Tracker.esm.js';
17
+
18
+ function useAnalyticsApi() {
19
+ try {
20
+ return useApi(analyticsApiRef);
21
+ } catch {
22
+ return { captureEvent: () => {
23
+ } };
24
+ }
25
+ }
26
+ function useAnalytics() {
27
+ const trackerRef = useRef(null);
28
+ const context = useAnalyticsContext();
29
+ const analyticsApi = useAnalyticsApi();
30
+ function getTracker() {
31
+ if (trackerRef.current === null) {
32
+ trackerRef.current = new Tracker(analyticsApi);
33
+ }
34
+ return trackerRef.current;
35
+ }
36
+ const tracker = getTracker();
37
+ tracker.setContext(context);
38
+ return tracker;
39
+ }
40
+
41
+ export { useAnalytics };
42
+ //# sourceMappingURL=useAnalytics.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnalytics.esm.js","sources":["../../src/analytics/useAnalytics.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { useAnalyticsContext } from './AnalyticsContext';\nimport {\n analyticsApiRef,\n AnalyticsTracker,\n AnalyticsApi,\n useApi,\n} from '../apis';\nimport { useRef } from 'react';\nimport { Tracker } from './Tracker';\n\nfunction useAnalyticsApi(): AnalyticsApi {\n try {\n return useApi(analyticsApiRef);\n } catch {\n return { captureEvent: () => {} };\n }\n}\n\n/**\n * Gets a pre-configured analytics tracker.\n *\n * @public\n */\nexport function useAnalytics(): AnalyticsTracker {\n const trackerRef = useRef<Tracker | null>(null);\n const context = useAnalyticsContext();\n // Our goal is to make this API truly optional for any/all consuming code\n // (including tests). This hook runs last to ensure hook order is, as much as\n // possible, maintained.\n const analyticsApi = useAnalyticsApi();\n\n function getTracker(): Tracker {\n if (trackerRef.current === null) {\n trackerRef.current = new Tracker(analyticsApi);\n }\n return trackerRef.current;\n }\n\n const tracker = getTracker();\n // this is not ideal, but it allows to memoize the tracker\n // without explicitly set the context as dependency.\n tracker.setContext(context);\n\n return tracker;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AA0BA,SAAS,eAAgC,GAAA;AACvC,EAAI,IAAA;AACF,IAAA,OAAO,OAAO,eAAe,CAAA,CAAA;AAAA,GACvB,CAAA,MAAA;AACN,IAAO,OAAA,EAAE,cAAc,MAAM;AAAA,KAAG,EAAA,CAAA;AAAA,GAClC;AACF,CAAA;AAOO,SAAS,YAAiC,GAAA;AAC/C,EAAM,MAAA,UAAA,GAAa,OAAuB,IAAI,CAAA,CAAA;AAC9C,EAAA,MAAM,UAAU,mBAAoB,EAAA,CAAA;AAIpC,EAAA,MAAM,eAAe,eAAgB,EAAA,CAAA;AAErC,EAAA,SAAS,UAAsB,GAAA;AAC7B,IAAI,IAAA,UAAA,CAAW,YAAY,IAAM,EAAA;AAC/B,MAAW,UAAA,CAAA,OAAA,GAAU,IAAI,OAAA,CAAQ,YAAY,CAAA,CAAA;AAAA,KAC/C;AACA,IAAA,OAAO,UAAW,CAAA,OAAA,CAAA;AAAA,GACpB;AAEA,EAAA,MAAM,UAAU,UAAW,EAAA,CAAA;AAG3B,EAAA,OAAA,CAAQ,WAAW,OAAO,CAAA,CAAA;AAE1B,EAAO,OAAA,OAAA,CAAA;AACT;;;;"}
@@ -0,0 +1,11 @@
1
+ import 'react';
2
+ import '@backstage/version-bridge';
3
+ import '@backstage/errors';
4
+ import { createApiRef } from '../system/ApiRef.esm.js';
5
+
6
+ const alertApiRef = createApiRef({
7
+ id: "core.alert"
8
+ });
9
+
10
+ export { alertApiRef };
11
+ //# sourceMappingURL=AlertApi.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlertApi.esm.js","sources":["../../../src/apis/definitions/AlertApi.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 { createApiRef, ApiRef } from '../system';\nimport { Observable } from '@backstage/types';\n\n/**\n * Message handled by the {@link AlertApi}.\n *\n * @public\n */\nexport type AlertMessage = {\n message: string;\n // Severity will default to success since that is what material ui defaults the value to.\n severity?: 'success' | 'info' | 'warning' | 'error';\n display?: 'permanent' | 'transient';\n};\n\n/**\n * The alert API is used to report alerts to the app, and display them to the user.\n *\n * @public\n */\nexport type AlertApi = {\n /**\n * Post an alert for handling by the application.\n */\n post(alert: AlertMessage): void;\n\n /**\n * Observe alerts posted by other parts of the application.\n */\n alert$(): Observable<AlertMessage>;\n};\n\n/**\n * The {@link ApiRef} of {@link AlertApi}.\n *\n * @public\n */\nexport const alertApiRef: ApiRef<AlertApi> = createApiRef({\n id: 'core.alert',\n});\n"],"names":[],"mappings":";;;;;AAqDO,MAAM,cAAgC,YAAa,CAAA;AAAA,EACxD,EAAI,EAAA,YAAA;AACN,CAAC;;;;"}
@@ -0,0 +1,11 @@
1
+ import 'react';
2
+ import '@backstage/version-bridge';
3
+ import '@backstage/errors';
4
+ import { createApiRef } from '../system/ApiRef.esm.js';
5
+
6
+ const analyticsApiRef = createApiRef({
7
+ id: "core.analytics"
8
+ });
9
+
10
+ export { analyticsApiRef };
11
+ //# sourceMappingURL=AnalyticsApi.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsApi.esm.js","sources":["../../../src/apis/definitions/AnalyticsApi.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { ApiRef, createApiRef } from '../system';\nimport { AnalyticsContextValue } from '../../analytics/types';\n\n/**\n * Represents an event worth tracking in an analytics system that could inform\n * how users of a Backstage instance are using its features.\n *\n * @public\n */\nexport type AnalyticsEvent = {\n /**\n * A string that identifies the event being tracked by the type of action the\n * event represents. Be careful not to encode extra metadata in this string\n * that should instead be placed in the Analytics Context or attributes.\n * Examples include:\n *\n * - view\n * - click\n * - filter\n * - search\n * - hover\n * - scroll\n */\n action: string;\n\n /**\n * A string that uniquely identifies the object that the action is being\n * taken on. Examples include:\n *\n * - The path of the page viewed\n * - The url of the link clicked\n * - The value that was filtered by\n * - The text that was searched for\n */\n subject: string;\n\n /**\n * An optional numeric value relevant to the event that could be aggregated\n * by analytics tools. Examples include:\n *\n * - The index or position of the clicked element in an ordered list\n * - The percentage of an element that has been scrolled through\n * - The amount of time that has elapsed since a fixed point\n * - A satisfaction score on a fixed scale\n */\n value?: number;\n\n /**\n * Optional, additional attributes (representing dimensions or metrics)\n * specific to the event that could be forwarded on to analytics systems.\n */\n attributes?: AnalyticsEventAttributes;\n\n /**\n * Contextual metadata relating to where the event was captured and by whom.\n * This could include information about the route, plugin, or extension in\n * which an event was captured.\n */\n context: AnalyticsContextValue;\n};\n\n/**\n * A structure allowing other arbitrary metadata to be provided by analytics\n * event emitters.\n *\n * @public\n */\nexport type AnalyticsEventAttributes = {\n [attribute in string]: string | boolean | number;\n};\n\n/**\n * Represents a tracker with methods that can be called to track events in a\n * configured analytics service.\n *\n * @public\n */\nexport type AnalyticsTracker = {\n captureEvent: (\n action: string,\n subject: string,\n options?: {\n value?: number;\n attributes?: AnalyticsEventAttributes;\n },\n ) => void;\n};\n\n/**\n * The Analytics API is used to track user behavior in a Backstage instance.\n *\n * @remarks\n *\n * To instrument your App or Plugin, retrieve an analytics tracker using the\n * useAnalytics() hook. This will return a pre-configured AnalyticsTracker\n * with relevant methods for instrumentation.\n *\n * @public\n */\nexport type AnalyticsApi = {\n /**\n * Primary event handler responsible for compiling and forwarding events to\n * an analytics system.\n */\n captureEvent(event: AnalyticsEvent): void;\n};\n\n/**\n * The {@link ApiRef} of {@link AnalyticsApi}.\n *\n * @public\n */\nexport const analyticsApiRef: ApiRef<AnalyticsApi> = createApiRef({\n id: 'core.analytics',\n});\n"],"names":[],"mappings":";;;;;AAgIO,MAAM,kBAAwC,YAAa,CAAA;AAAA,EAChE,EAAI,EAAA,gBAAA;AACN,CAAC;;;;"}
@@ -0,0 +1,8 @@
1
+ import { createApiRef } from '@backstage/core-plugin-api';
2
+
3
+ const appLanguageApiRef = createApiRef({
4
+ id: "core.applanguage"
5
+ });
6
+
7
+ export { appLanguageApiRef };
8
+ //# sourceMappingURL=AppLanguageApi.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppLanguageApi.esm.js","sources":["../../../src/apis/definitions/AppLanguageApi.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 { ApiRef, createApiRef } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\n\n/** @alpha */\nexport type AppLanguageApi = {\n getAvailableLanguages(): { languages: string[] };\n\n setLanguage(language?: string): void;\n\n getLanguage(): { language: string };\n\n language$(): Observable<{ language: string }>;\n};\n\n/**\n * @alpha\n */\nexport const appLanguageApiRef: ApiRef<AppLanguageApi> = createApiRef({\n id: 'core.applanguage',\n});\n"],"names":[],"mappings":";;AAiCO,MAAM,oBAA4C,YAAa,CAAA;AAAA,EACpE,EAAI,EAAA,kBAAA;AACN,CAAC;;;;"}
@@ -0,0 +1,11 @@
1
+ import 'react';
2
+ import '@backstage/version-bridge';
3
+ import '@backstage/errors';
4
+ import { createApiRef } from '../system/ApiRef.esm.js';
5
+
6
+ const appThemeApiRef = createApiRef({
7
+ id: "core.apptheme"
8
+ });
9
+
10
+ export { appThemeApiRef };
11
+ //# sourceMappingURL=AppThemeApi.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppThemeApi.esm.js","sources":["../../../src/apis/definitions/AppThemeApi.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 { ReactNode } from 'react';\nimport { ApiRef, createApiRef } from '../system';\nimport { Observable } from '@backstage/types';\n\n/**\n * Describes a theme provided by the app.\n *\n * @public\n */\nexport type AppTheme = {\n /**\n * ID used to remember theme selections.\n */\n id: string;\n\n /**\n * Title of the theme\n */\n title: string;\n\n /**\n * Theme variant\n */\n variant: 'light' | 'dark';\n\n /**\n * An Icon for the theme mode setting.\n */\n icon?: React.ReactElement;\n\n Provider(props: { children: ReactNode }): JSX.Element | null;\n};\n\n/**\n * The AppThemeApi gives access to the current app theme, and allows switching\n * to other options that have been registered as a part of the App.\n *\n * @public\n */\nexport type AppThemeApi = {\n /**\n * Get a list of available themes.\n */\n getInstalledThemes(): AppTheme[];\n\n /**\n * Observe the currently selected theme. A value of undefined means no specific theme has been selected.\n */\n activeThemeId$(): Observable<string | undefined>;\n\n /**\n * Get the current theme ID. Returns undefined if no specific theme is selected.\n */\n getActiveThemeId(): string | undefined;\n\n /**\n * Set a specific theme to use in the app, overriding the default theme selection.\n *\n * Clear the selection by passing in undefined.\n */\n setActiveThemeId(themeId?: string): void;\n};\n\n/**\n * The {@link ApiRef} of {@link AppThemeApi}.\n *\n * @public\n */\nexport const appThemeApiRef: ApiRef<AppThemeApi> = createApiRef({\n id: 'core.apptheme',\n});\n"],"names":[],"mappings":";;;;;AAoFO,MAAM,iBAAsC,YAAa,CAAA;AAAA,EAC9D,EAAI,EAAA,eAAA;AACN,CAAC;;;;"}
@@ -0,0 +1,11 @@
1
+ import 'react';
2
+ import '@backstage/version-bridge';
3
+ import '@backstage/errors';
4
+ import { createApiRef } from '../system/ApiRef.esm.js';
5
+
6
+ const configApiRef = createApiRef({
7
+ id: "core.config"
8
+ });
9
+
10
+ export { configApiRef };
11
+ //# sourceMappingURL=ConfigApi.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigApi.esm.js","sources":["../../../src/apis/definitions/ConfigApi.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 { ApiRef, createApiRef } from '../system';\nimport { Config } from '@backstage/config';\n\n/**\n * The Config API is used to provide a mechanism to access the\n * runtime configuration of the system.\n *\n * @public\n */\nexport type ConfigApi = Config;\n\n/**\n * The {@link ApiRef} of {@link ConfigApi}.\n *\n * @public\n */\nexport const configApiRef: ApiRef<ConfigApi> = createApiRef({\n id: 'core.config',\n});\n"],"names":[],"mappings":";;;;;AA+BO,MAAM,eAAkC,YAAa,CAAA;AAAA,EAC1D,EAAI,EAAA,aAAA;AACN,CAAC;;;;"}
@@ -0,0 +1,11 @@
1
+ import 'react';
2
+ import '@backstage/version-bridge';
3
+ import '@backstage/errors';
4
+ import { createApiRef } from '../system/ApiRef.esm.js';
5
+
6
+ const discoveryApiRef = createApiRef({
7
+ id: "core.discovery"
8
+ });
9
+
10
+ export { discoveryApiRef };
11
+ //# sourceMappingURL=DiscoveryApi.esm.js.map